Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cross-VM transfer NFT/FT to and from EVM #90

Merged
merged 8 commits into from
Jun 27, 2024
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ coverage.lcov
local.flow.json

# Local
.DS_Store
.DS_Store
.vscode/
207 changes: 197 additions & 10 deletions cadence/tests/flow_evm_bridge_tests.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ access(all) let exampleNFTAccount = Test.getAccount(0x0000000000000008)
access(all) let exampleERCAccount = Test.getAccount(0x0000000000000009)
access(all) let exampleTokenAccount = Test.getAccount(0x0000000000000010)
access(all) let alice = Test.createAccount()
access(all) let bob = Test.createAccount()

// ExampleNFT values
access(all) let exampleNFTIdentifier = "A.0000000000000008.ExampleNFT.NFT"
Expand Down Expand Up @@ -181,7 +182,7 @@ fun setup() {
arguments: []
)
Test.expect(err, Test.beNil())

/* Integrate EVM bridge contract */

// Set factory as registrar in registry
Expand Down Expand Up @@ -334,7 +335,7 @@ fun testDeployERC721Succeeds() {
exampleERCAccount
)
Test.expect(erc721DeployResult, Test.beSucceeded())

// Get ERC721 & ERC20 deployed contract addresses
let evts = Test.eventsOfType(Type<EVM.TransactionExecuted>())
Test.assertEqual(21, evts.length)
Expand All @@ -349,20 +350,23 @@ fun testDeployERC20Succeeds() {
exampleERCAccount
)
Test.expect(erc20DeployResult, Test.beSucceeded())

// Get ERC721 & ERC20 deployed contract addresses
let evts = Test.eventsOfType(Type<EVM.TransactionExecuted>())
Test.assertEqual(22, evts.length)
erc20AddressHex = getEVMAddressHexFromEvents(evts, idx: 21)

}

access(all)
fun testCreateCOASucceeds() {
transferFlow(signer: serviceAccount, recipient: alice.address, amount: 1_000.0)
transferFlow(signer: serviceAccount, recipient: bob.address, amount: 1_000.0)
createCOA(signer: alice, fundingAmount: 100.0)
createCOA(signer: bob, fundingAmount: 100.0)

let coaAddressHex = getCOAAddressHex(atFlowAddress: alice.address)
let aliceCOAAddress = getCOAAddressHex(atFlowAddress: alice.address)
let bobCOAAddress = getCOAAddressHex(atFlowAddress: bob.address)
}

access(all)
Expand Down Expand Up @@ -590,15 +594,14 @@ fun testOnboardAndBridgeNFTToEVMSucceeds() {
Test.reset(to: snapshot)

var aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)
Test.assertEqual(40, aliceCOAAddressHex.length)
var aliceOwnedIDs = getIDs(ownerAddr: alice.address, storagePathIdentifier: "cadenceExampleNFTCollection")
Test.assertEqual(1, aliceOwnedIDs.length)
let aliceID = aliceOwnedIDs[0]

var requiresOnboarding = typeRequiresOnboardingByIdentifier(exampleNFTIdentifier)
?? panic("Problem getting onboarding status for type")
Test.assertEqual(true, requiresOnboarding)

// Execute bridge NFT to EVM - should also onboard the NFT type
bridgeNFTToEVM(
signer: alice,
Expand Down Expand Up @@ -636,6 +639,56 @@ fun testOnboardAndBridgeNFTToEVMSucceeds() {
Test.assertEqual(true, isOwnerResult.returnValue as! Bool? ?? panic("Problem getting owner status"))
}

access(all)
fun testOnboardAndCrossVMTransferNFTToEVMSucceeds() {
// Revert to state before ExampleNFT was onboarded
Test.reset(to: snapshot)

var aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)
var aliceOwnedIDs = getIDs(ownerAddr: alice.address, storagePathIdentifier: "cadenceExampleNFTCollection")
Test.assertEqual(1, aliceOwnedIDs.length)
let aliceID = aliceOwnedIDs[0]

let recipient = getCOAAddressHex(atFlowAddress: bob.address)

var requiresOnboarding = typeRequiresOnboardingByIdentifier(exampleNFTIdentifier)
?? panic("Problem getting onboarding status for type")
Test.assertEqual(true, requiresOnboarding)

// Execute bridge NFT to EVM recipient - should also onboard the NFT type
let crossVMTransferResult = executeTransaction(
"../transactions/bridge/nft/bridge_nft_to_any_evm_address.cdc",
[ exampleNFTAccount.address, "ExampleNFT", aliceID, recipient ],
alice
)
Test.expect(crossVMTransferResult, Test.beSucceeded())

requiresOnboarding = typeRequiresOnboardingByIdentifier(exampleNFTIdentifier)
?? panic("Problem getting onboarding status for type")
Test.assertEqual(false, requiresOnboarding)

let onboardingResult = executeTransaction(
"../transactions/bridge/onboarding/onboard_by_type_identifier.cdc",
[exampleNFTIdentifier],
alice
)
Test.expect(onboardingResult, Test.beFailed())

let associatedEVMAddressHex = getAssociatedEVMAddressHex(with: exampleNFTIdentifier)
Test.assertEqual(40, associatedEVMAddressHex.length)

// Confirm the NFT is no longer in Alice's Collection
aliceOwnedIDs = getIDs(ownerAddr: alice.address, storagePathIdentifier: "cadenceExampleNFTCollection")
Test.assertEqual(0, aliceOwnedIDs.length)

// Confirm ownership on EVM side with Alice COA as owner of ERC721 representation
let isOwnerResult = executeScript(
"../scripts/utils/is_owner.cdc",
[UInt256(mintedNFTID), recipient, associatedEVMAddressHex]
)
Test.expect(isOwnerResult, Test.beSucceeded())
Test.assertEqual(true, isOwnerResult.returnValue as! Bool? ?? panic("Problem getting owner status"))
}

access(all)
fun testOnboardTokenByTypeSucceeds() {
Expand Down Expand Up @@ -666,9 +719,8 @@ access(all)
fun testOnboardAndBridgeTokensToEVMSucceeds() {
// Revert to state before ExampleNFT was onboarded
Test.reset(to: snapshot)

var aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)
Test.assertEqual(40, aliceCOAAddressHex.length)
var cadenceBalance = getBalance(ownerAddr: alice.address, storagePathIdentifier: "exampleTokenVault")
?? panic("Could not get ExampleToken balance")

Expand Down Expand Up @@ -711,6 +763,54 @@ fun testOnboardAndBridgeTokensToEVMSucceeds() {
Test.assertEqual(expectedEVMBalance, evmBalance)
}

access(all)
fun testOnboardAndCrossVMTransferTokensToEVMSucceeds() {
// Revert to state before ExampleNFT was onboarded
Test.reset(to: snapshot)

var aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)
var cadenceBalance = getBalance(ownerAddr: alice.address, storagePathIdentifier: "exampleTokenVault")
?? panic("Could not get ExampleToken balance")
let recipient = getCOAAddressHex(atFlowAddress: bob.address)

var requiresOnboarding = typeRequiresOnboardingByIdentifier(exampleTokenIdentifier)
?? panic("Problem getting onboarding status for type")
Test.assertEqual(true, requiresOnboarding)

// Execute bridge to EVM - should also onboard the token type
let crossVMTransferResult = executeTransaction(
"../transactions/bridge/tokens/bridge_tokens_to_any_evm_address.cdc",
[ exampleTokenAccount.address, "ExampleToken", cadenceBalance, recipient ],
alice
)
Test.expect(crossVMTransferResult, Test.beSucceeded())

requiresOnboarding = typeRequiresOnboardingByIdentifier(exampleTokenIdentifier)
?? panic("Problem getting onboarding status for type")
Test.assertEqual(false, requiresOnboarding)

let onboardingResult = executeTransaction(
"../transactions/bridge/onboarding/onboard_by_type_identifier.cdc",
[exampleTokenIdentifier],
alice
)
Test.expect(onboardingResult, Test.beFailed())

let associatedEVMAddressHex = getAssociatedEVMAddressHex(with: exampleTokenIdentifier)
Test.assertEqual(40, associatedEVMAddressHex.length)

// Confirm Alice's token balance is now 0.0
cadenceBalance = getBalance(ownerAddr: alice.address, storagePathIdentifier: "exampleTokenVault")
?? panic("Problem getting ExampleToken balance")
Test.assertEqual(0.0, cadenceBalance)

// Confirm balance on EVM side has been updated
let decimals = getTokenDecimals(erc20AddressHex: associatedEVMAddressHex)
let expectedEVMBalance = ufix64ToUInt256(exampleTokenMintAmount, decimals: decimals)
let evmBalance = balanceOf(evmAddressHex: recipient, erc20AddressHex: associatedEVMAddressHex)
Test.assertEqual(expectedEVMBalance, evmBalance)
}

access(all)
fun testBatchOnboardByTypeSucceeds() {
Test.assert(snapshot != 0, message: "Expected snapshot to be taken before onboarding any types")
Expand Down Expand Up @@ -929,8 +1029,47 @@ fun testBridgeCadenceNativeNFTToEVMSucceeds() {
Test.assertEqual(true, isOwnerResult.returnValue as! Bool? ?? panic("Problem getting owner status"))
}

access(all)
fun testCrossVMTransferCadenceNativeNFTFromEVMSucceeds() {
snapshot = getCurrentBlockHeight()
// Configure recipient's Collection first, using generic setup transaction
let setupCollectionResult = executeTransaction(
"../transactions/example-assets/setup/setup_generic_nft_collection.cdc",
[exampleNFTIdentifier],
bob
)
Test.expect(setupCollectionResult, Test.beSucceeded())

let aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)

let associatedEVMAddressHex = getAssociatedEVMAddressHex(with: exampleNFTIdentifier)
Test.assertEqual(40, associatedEVMAddressHex.length)

// Assert ownership of the bridged NFT in EVM
var aliceIsOwner = isOwner(of: UInt256(mintedNFTID), ownerEVMAddrHex: aliceCOAAddressHex, erc721AddressHex: associatedEVMAddressHex)
Test.assertEqual(true, aliceIsOwner)

// Execute bridge NFT from EVM to Cadence recipient (Bob in this case)
let crossVMTransferResult = executeTransaction(
"../transactions/bridge/nft/bridge_nft_to_any_cadence_address.cdc",
[ exampleNFTAccount.address, "ExampleNFT", UInt256(mintedNFTID), bob.address ],
alice
)
Test.expect(crossVMTransferResult, Test.beSucceeded())

// Assert ownership of the bridged NFT in EVM has transferred
aliceIsOwner = isOwner(of: UInt256(mintedNFTID), ownerEVMAddrHex: aliceCOAAddressHex, erc721AddressHex: associatedEVMAddressHex)
Test.assertEqual(false, aliceIsOwner)

// Assert the NFT is now in Bob's Collection
let bobOwnedIDs = getIDs(ownerAddr: bob.address, storagePathIdentifier: "cadenceExampleNFTCollection")
Test.assertEqual(1, bobOwnedIDs.length)
Test.assertEqual(mintedNFTID, bobOwnedIDs[0])
}

access(all)
fun testBridgeCadenceNativeNFTFromEVMSucceeds() {
Test.reset(to: snapshot)
let aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)

let associatedEVMAddressHex = getAssociatedEVMAddressHex(with: exampleNFTIdentifier)
Expand Down Expand Up @@ -1096,11 +1235,59 @@ fun testBridgeCadenceNativeTokenToEVMSucceeds() {
Test.assertEqual(expectedEVMBalance, evmBalance)
}

access(all)
fun testCrossVMTransferCadenceNativeTokenFromEVMSucceeds() {
snapshot = getCurrentBlockHeight()
// Configure recipient's Vault first, using generic setup transaction
let setupVaultResult = executeTransaction(
"../transactions/example-assets/setup/setup_generic_vault.cdc",
[exampleTokenIdentifier],
bob
)
Test.expect(setupVaultResult, Test.beSucceeded())

let associatedEVMAddressHex = getAssociatedEVMAddressHex(with: exampleTokenIdentifier)
Test.assertEqual(40, associatedEVMAddressHex.length)

var aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)

// Confirm Alice is starting with 0.0 balance in their Cadence Vault
let preCadenceBalance = getBalance(ownerAddr: alice.address, storagePathIdentifier: "exampleTokenVault")
?? panic("Problem getting ExampleToken balance")
Test.assertEqual(0.0, preCadenceBalance)

// Get Alice's ERC20 balance & convert to UFix64
var evmBalance = balanceOf(evmAddressHex: aliceCOAAddressHex, erc20AddressHex: associatedEVMAddressHex)
let decimals = getTokenDecimals(erc20AddressHex: associatedEVMAddressHex)
let ufixValue = uint256ToUFix64(evmBalance, decimals: decimals)
// Assert the converted balance is equal to the originally minted amount that was bridged in the previous step
Test.assertEqual(exampleTokenMintAmount, ufixValue)

// Execute bridge tokens from EVM to Cadence recipient (Bob in this case)
let crossVMTransferResult = executeTransaction(
"../transactions/bridge/tokens/bridge_tokens_to_any_cadence_address.cdc",
[ exampleTokenAccount.address, "ExampleToken", evmBalance, bob.address ],
alice
)
Test.expect(crossVMTransferResult, Test.beSucceeded())

// Confirm ExampleToken balance has been bridged back to Alice's Cadence vault
let recipientCadenceBalance = getBalance(ownerAddr: bob.address, storagePathIdentifier: "exampleTokenVault")
?? panic("Problem getting ExampleToken balance")
Test.assertEqual(ufixValue, recipientCadenceBalance)

// Confirm ownership on EVM side with Alice COA as owner of ERC721 representation
evmBalance = balanceOf(evmAddressHex: aliceCOAAddressHex, erc20AddressHex: associatedEVMAddressHex)
Test.assertEqual(UInt256(0), evmBalance)
}

access(all)
fun testBridgeCadenceNativeTokenFromEVMSucceeds() {
Test.reset(to: snapshot)

let associatedEVMAddressHex = getAssociatedEVMAddressHex(with: exampleTokenIdentifier)
Test.assertEqual(40, associatedEVMAddressHex.length)

var aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address)

// Confirm Alice is starting with 0.0 balance in their Cadence Vault
Expand Down
Loading
Loading