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 CrossVM views #243

Merged
merged 9 commits into from
Feb 12, 2025
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.vscode
node_modules
coverage.json
*.pkey
*.pkey
imports/
63 changes: 63 additions & 0 deletions contracts/CrossVMMetadataViews.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import "EVM"

/// This contract implements views originally proposed in FLIP-318 supporting NFT collections
/// with project-defined implementations across both Cadence & EVM.
///
sisyphusSmiling marked this conversation as resolved.
Show resolved Hide resolved
access(all) contract CrossVMMetadataViews {

/// An enum denoting a VM. For now, there are only two VMs on Flow, but this enum could be
/// expanded in the event other VMs are supported on the network.
///
access(all) enum VM : UInt8 {
access(all) case Cadence
access(all) case EVM
}

/// View resolved at contract & resource level pointing to the associated EVM implementation.
/// NOTE: This view alone is not sufficient to validate an association across Cadence & EVM!
/// Both the Cadence Type/contract *and* the EVM contract should point to each other, with the
/// EVM pointer being facilitated by ICrossVM.sol contract interface methods. For more
/// information and context, see FLIP-318: https://github.com/onflow/flips/issues/318
///
access(all) struct EVMPointer {
/// The associated Cadence Type
sisyphusSmiling marked this conversation as resolved.
Show resolved Hide resolved
access(all) let cadenceType: Type
/// The defining Cadence contract address
access(all) let cadenceContractAddress: Address
/// The associated EVM contract address
sisyphusSmiling marked this conversation as resolved.
Show resolved Hide resolved
access(all) let evmContractAddress: EVM.EVMAddress
/// Whether the asset is Cadence- or EVM-native. Native here meaning the VM in which the
/// asset is distributed.
sisyphusSmiling marked this conversation as resolved.
Show resolved Hide resolved
access(all) let nativeVM: VM

init(
cadenceType: Type,
cadenceContractAddress: Address,
evmContractAddress: EVM.EVMAddress,
nativeVM: VM
) {
self.cadenceType = cadenceType
self.cadenceContractAddress = cadenceContractAddress
self.evmContractAddress = evmContractAddress
self.nativeVM = nativeVM
}
}

/// View resolved at resource level denoting any metadata to be passed to the associated EVM
/// contract at the time of / bridging. This optional view would allow EVM side metadata to be
/// updated based on current Cadence state. If the / view is not supported, no bytes will be
/// passed into EVM when bridging.
///
access(all) struct EVMBytesMetadata {
/// Returns the bytes to be passed to the EVM contract on `fulfillToEVM` call, allowing the
/// EVM contract to update / the metadata associated with the NFT. The corresponding Solidity
/// `bytes` type allows the implementer greater / flexibility by enabling them to pass
/// arbitrary data between VMs.
access(all) let bytes: EVM.EVMBytes?

init(bytes: EVM.EVMBytes?) {
self.bytes = bytes
}
}

}
94 changes: 66 additions & 28 deletions flow.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
{
"contracts": {
"BasicNFT": {
"source": "./contracts/BasicNFT.cdc",
"CrossVMMetadataViews": {
"source": "contracts/CrossVMMetadataViews.cdc",
"aliases": {
"testing": "0000000000000007"
}
},
"Burner": {
"source": "./contracts/utility/Burner.cdc",
"aliases": {
"emulator": "f8d6e0586b0a20c7",
"testing": "0000000000000001",
"testnet": "9a0766d93b6608b7"
"emulator": "f8d6e0586b0a20c7"
}
},
"ExampleNFT": {
Expand All @@ -20,15 +12,6 @@
"testing": "0000000000000007"
}
},
"FungibleToken": {
"source": "./contracts/utility/FungibleToken.cdc",
"aliases": {
"emulator": "ee82856bf20e2aa6",
"mainnet": "f233dcee88fe0abe",
"testing": "0000000000000002",
"testnet": "9a0766d93b6608b7"
}
},
"MaliciousNFT": {
"source": "./contracts/MaliciousNFT.cdc",
"aliases": {
Expand Down Expand Up @@ -57,12 +40,6 @@
"testnet": "631e88ae7f1d7c20"
}
},
"UniversalCollection": {
"source": "./contracts/UniversalCollection.cdc",
"aliases": {
"testing": "0000000000000007"
}
},
"ViewResolver": {
"source": "./contracts/ViewResolver.cdc",
"aliases": {
Expand All @@ -72,6 +49,68 @@
}
}
},
"dependencies": {
"Burner": {
"source": "mainnet://f233dcee88fe0abe.Burner",
"hash": "71af18e227984cd434a3ad00bb2f3618b76482842bae920ee55662c37c8bf331",
"aliases": {
"emulator": "f8d6e0586b0a20c7",
"mainnet": "f233dcee88fe0abe",
"testnet": "9a0766d93b6608b7"
}
},
"EVM": {
"source": "mainnet://e467b9dd11fa00df.EVM",
"hash": "5c69921fa06088b477e2758e122636b39d3d3eb5316807c206c5680d9ac74c7e",
"aliases": {
"emulator": "f8d6e0586b0a20c7",
"mainnet": "e467b9dd11fa00df",
"testnet": "8c5303eaa26202d6"
}
},
"FlowToken": {
"source": "mainnet://1654653399040a61.FlowToken",
"hash": "cefb25fd19d9fc80ce02896267eb6157a6b0df7b1935caa8641421fe34c0e67a",
"aliases": {
"emulator": "0ae53cb6e3f42a79",
"mainnet": "1654653399040a61",
"testnet": "7e60df042a9c0868"
}
},
"FungibleToken": {
"source": "mainnet://f233dcee88fe0abe.FungibleToken",
"hash": "050328d01c6cde307fbe14960632666848d9b7ea4fef03ca8c0bbfb0f2884068",
"aliases": {
"emulator": "ee82856bf20e2aa6",
"mainnet": "f233dcee88fe0abe",
"testnet": "9a0766d93b6608b7"
}
},
"FungibleTokenMetadataViews": {
"source": "mainnet://f233dcee88fe0abe.FungibleTokenMetadataViews",
"hash": "dff704a6e3da83997ed48bcd244aaa3eac0733156759a37c76a58ab08863016a",
"aliases": {
"emulator": "ee82856bf20e2aa6",
"mainnet": "f233dcee88fe0abe",
"testnet": "9a0766d93b6608b7"
}
},
"MetadataViews": {
"source": "mainnet://1d7e57aa55817448.MetadataViews",
"hash": "10a239cc26e825077de6c8b424409ae173e78e8391df62750b6ba19ffd048f51",
"aliases": {}
},
"NonFungibleToken": {
"source": "mainnet://1d7e57aa55817448.NonFungibleToken",
"hash": "b63f10e00d1a814492822652dac7c0574428a200e4c26cb3c832c4829e2778f0",
"aliases": {}
},
"ViewResolver": {
"source": "mainnet://1d7e57aa55817448.ViewResolver",
"hash": "374a1994046bac9f6228b4843cb32393ef40554df9bd9907a702d098a2987bde",
"aliases": {}
}
},
"networks": {
"emulator": "127.0.0.1:3569",
"mainnet": "access.mainnet.nodes.onflow.org:9000",
Expand Down Expand Up @@ -101,8 +140,7 @@
"MetadataViews",
"ExampleNFT",
"NFTForwarding",
"UniversalCollection",
"BasicNFT"
"CrossVMMetadataViews"
]
},
"mainnet": {
Expand Down
47 changes: 29 additions & 18 deletions lib/go/contracts/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,30 @@ import (
)

var (
placeholderNonFungibleToken = regexp.MustCompile(`"NonFungibleToken"`)
nonFungibleTokenImport = "NonFungibleToken from "
placeholderMetadataViews = regexp.MustCompile(`"MetadataViews"`)
metadataViewsImport = "MetadataViews from "
placeholderFungibleToken = regexp.MustCompile(`"FungibleToken"`)
fungibleTokenImport = "FungibleToken from "
placeholderResolver = regexp.MustCompile(`"ViewResolver"`)
viewResolverImport = "ViewResolver from "
placeholderUniversalCollection = regexp.MustCompile(`"UniversalCollection"`)
universalCollectionImport = "UniversalCollection from "
placeholderNonFungibleToken = regexp.MustCompile(`"NonFungibleToken"`)
nonFungibleTokenImport = "NonFungibleToken from "
placeholderMetadataViews = regexp.MustCompile(`"MetadataViews"`)
metadataViewsImport = "MetadataViews from "
placeholderFungibleToken = regexp.MustCompile(`"FungibleToken"`)
fungibleTokenImport = "FungibleToken from "
placeholderResolver = regexp.MustCompile(`"ViewResolver"`)
viewResolverImport = "ViewResolver from "
placeholderUniversalCollection = regexp.MustCompile(`"UniversalCollection"`)
universalCollectionImport = "UniversalCollection from "
placeholderCrossVMMetadataViews = regexp.MustCompile(`"CrossVMMetadataViews"`)
crossVMMetadataViewsImport = "CrossVMMetadataViews from "
)

const (
filenameNonFungibleToken = "NonFungibleToken.cdc"
filenameExampleNFT = "ExampleNFT.cdc"
filenameMetadataViews = "MetadataViews.cdc"
filenameNFTMetadataViews = "NFTMetadataViews.cdc"
filenameViewResolver = "ViewResolver.cdc"
filenameUniversalCollection = "UniversalCollection.cdc"
filenameBasicNFT = "BasicNFT.cdc"
filenameFungibleToken = "utility/FungibleToken.cdc"
filenameNonFungibleToken = "NonFungibleToken.cdc"
filenameExampleNFT = "ExampleNFT.cdc"
filenameMetadataViews = "MetadataViews.cdc"
filenameCrossVMMetadataViews = "CrossVMMetadataViews.cdc"
filenameNFTMetadataViews = "NFTMetadataViews.cdc"
filenameViewResolver = "ViewResolver.cdc"
filenameUniversalCollection = "UniversalCollection.cdc"
filenameBasicNFT = "BasicNFT.cdc"
filenameFungibleToken = "utility/FungibleToken.cdc"
)

func withHexPrefix(address string) string {
Expand Down Expand Up @@ -84,6 +87,14 @@ func ViewResolver() []byte {
return []byte(code)
}

func CrossVMMetadataViews(evmAddress string) []byte {
code := assets.MustAssetString(filenameCrossVMMetadataViews)

code = placeholderFungibleToken.ReplaceAllString(code, fungibleTokenImport+withHexPrefix(evmAddress))

return []byte(code)
}

func UniversalCollection(nftAddress, resolverAddress, metadataAddress flow.Address) []byte {
code := assets.MustAssetString(filenameUniversalCollection)
code = placeholderMetadataViews.ReplaceAllString(code, metadataViewsImport+withHexPrefix(metadataAddress.String()))
Expand Down
5 changes: 5 additions & 0 deletions lib/go/contracts/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ func TestMetadataViewsContract(t *testing.T) {
contract := contracts.MetadataViews(addrA, addrA, addrA)
assert.NotNil(t, contract)
}

func TestCrossVMMetadataViewsContract(t *testing.T) {
contract := contracts.CrossVMMetadataViews(addrA)
assert.NotNil(t, contract)
}
31 changes: 27 additions & 4 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

Loading