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

Updates to EVM interface #39

Merged
merged 7 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ignore:
- "./cadence/contracts/example-assets/*"
- "./cadence/contracts/utils/ArrayUtils.cdc"
- "./cadence/contracts/utils/StringUtils.cdc"
- "./cadence/contracts/utils/ScopedFTProvider.cdc"
- "./cadence/contracts/standards/Burner.cdc"
- "./cadence/contracts/standards/FlowStorageFees.cdc"
- "./cadence/contracts/standards/FlowToken.cdc"
- "./cadence/contracts/standards/FungibleToken.cdc"
- "./cadence/contracts/standards/FungibleTokenMetadataViews.cdc"
- "./cadence/contracts/standards/NonFungibleToken.cdc"
- "./cadence/contracts/standards/ViewResolver.cdc"
- "./cadence/contracts/templates/emulator/*"
- "./cadence/contracts/templates/previewnet/*"
- "./cadence/contracts/test/*"
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ coverage.lcov

# Keys
*.pkey
*.pem
*.pem

# Local configs
local.flow.json
14 changes: 5 additions & 9 deletions cadence/contracts/bridge/FlowEVMBridgeAccessor.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -160,21 +160,17 @@ contract FlowEVMBridgeAccessor {
self.bridgeAccessorCap = nil
}

/// Sets the BridgeAccessor capability on the BridgeRouter
///
access(EVM.Bridge) fun setBridgeAccessorCap(_ cap: Capability<auth(EVM.Bridge) &{EVM.BridgeAccessor}>) {
pre {
cap.check(): "BridgeAccessor capability already set"
}
self.bridgeAccessorCap = cap
}

/// Returns an EVM.Bridge entitled reference to the underlying BridgeAccessor resource
///
access(EVM.Bridge) view fun borrowBridgeAccessor(): auth(EVM.Bridge) &{EVM.BridgeAccessor} {
let cap = self.bridgeAccessorCap ?? panic("BridgeAccessor Capabaility is not yet set")
return cap.borrow() ?? panic("Problem retrieving BridgeAccessor reference")
}

/// Sets the BridgeAccessor Capability in the BridgeRouter
access(EVM.Bridge) fun setBridgeAccessor(_ accessorCap: Capability<auth(EVM.Bridge) &{EVM.BridgeAccessor}>) {
self.bridgeAccessorCap = accessorCap
}
}

init(publishToEVMAccount: Address) {
Expand Down
4 changes: 2 additions & 2 deletions cadence/contracts/example-assets/ExampleNFT.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ access(all) contract ExampleNFT: NonFungibleToken {
access(all) resource Collection: NonFungibleToken.Collection {
/// dictionary of NFT conforming tokens
/// NFT is a resource type with an `UInt64` ID field
access(contract) var ownedNFTs: @{UInt64: ExampleNFT.NFT}
access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}

access(all) var storagePath: StoragePath
access(all) var publicPath: PublicPath
Expand Down Expand Up @@ -187,7 +187,7 @@ access(all) contract ExampleNFT: NonFungibleToken {

/// Borrow the view resolver for the specified NFT ID
access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
if let nft = &self.ownedNFTs[id] as &ExampleNFT.NFT? {
if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
return nft as &{ViewResolver.Resolver}
}
return nil
Expand Down
28 changes: 28 additions & 0 deletions cadence/contracts/standards/EVM.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ contract EVM {
access(all)
event FLOWTokensWithdrawn(addressBytes: [UInt8; 20], amount: UFix64)

/// BridgeAccessorUpdated is emitted when the BridgeAccessor Capability
/// is updated in the stored BridgeRouter along with identifying
/// information about both.
access(all)
event BridgeAccessorUpdated(
routerType: Type,
routerUUID: UInt64,
routerAddress: Address,
accessorType: Type,
accessorUUID: UInt64,
accessorAddress: Address
)

/// EVMAddress is an EVM-compatible address
access(all)
struct EVMAddress {
Expand Down Expand Up @@ -602,6 +615,21 @@ contract EVM {

/// Returns a reference to the BridgeAccessor designated for internal bridge requests
access(Bridge) view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor}

/// Sets the BridgeAccessor Capability in the BridgeRouter
access(Bridge) fun setBridgeAccessor(_ accessor: Capability<auth(Bridge) &{BridgeAccessor}>) {
pre {
accessor.check(): "Invalid BridgeAccessor Capability provided"
emit BridgeAccessorUpdated(
routerType: self.getType(),
routerUUID: self.uuid,
routerAddress: self.owner?.address ?? panic("Router must have an owner to be identified"),
accessorType: accessor.borrow()!.getType(),
accessorUUID: accessor.borrow()!.uuid,
accessorAddress: accessor.address
)
}
}
}

/// Returns a reference to the BridgeAccessor designated for internal bridge requests
Expand Down
26 changes: 19 additions & 7 deletions cadence/contracts/standards/NonFungibleToken.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Collection to complete the transfer.
*/

import ViewResolver from "ViewResolver"
import "ViewResolver"

/// The main NFT contract. Other NFT contracts will
/// import and implement the interfaces defined in this contract
Expand All @@ -63,7 +63,7 @@ access(all) contract interface NonFungibleToken: ViewResolver {
/// and query the updated metadata from the owners' collections.
///
access(all) event Updated(type: String, id: UInt64, uuid: UInt64, owner: Address?)
access(contract) view fun emitNFTUpdated(_ nftRef: auth(Update | Owner) &{NonFungibleToken.NFT})
access(all) view fun emitNFTUpdated(_ nftRef: auth(Update | Owner) &{NonFungibleToken.NFT})
{
emit Updated(type: nftRef.getType().identifier, id: nftRef.id, uuid: nftRef.uuid, owner: nftRef.owner?.address)
}
Expand All @@ -85,9 +85,6 @@ access(all) contract interface NonFungibleToken: ViewResolver {
///
access(all) event Deposited(type: String, id: UInt64, uuid: UInt64, to: Address?, collectionUUID: UInt64)

/// Included for backwards-compatibility
access(all) resource interface INFT: NFT {}

/// Interface that the NFTs must conform to
///
access(all) resource interface NFT: ViewResolver.Resolver {
Expand All @@ -105,6 +102,7 @@ access(all) contract interface NonFungibleToken: ViewResolver {
access(all) fun createEmptyCollection(): @{Collection} {
post {
result.getLength() == 0: "The created collection must be empty!"
result.isSupportedNFTType(type: self.getType()): "The created collection must support this NFT type"
}
}

Expand Down Expand Up @@ -174,6 +172,7 @@ access(all) contract interface NonFungibleToken: ViewResolver {
access(all) fun deposit(token: @{NFT})
access(all) view fun getLength(): Int
access(all) view fun getIDs(): [UInt64]
access(all) fun forEachID(_ f: fun (UInt64): Bool): Void
access(all) view fun borrowNFT(_ id: UInt64): &{NFT}?
}

Expand All @@ -182,6 +181,10 @@ access(all) contract interface NonFungibleToken: ViewResolver {
///
access(all) resource interface Collection: Provider, Receiver, CollectionPublic, ViewResolver.ResolverCollection {

/// Cadence allows implementing types to specify less restrictive access
/// so implementing contracts can have this as `access(all)` with no problem
access(contract) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}

/// deposit takes a NFT as an argument and stores it in the collection
/// @param token: The NFT to deposit into the collection
access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
Expand All @@ -196,7 +199,16 @@ access(all) contract interface NonFungibleToken: ViewResolver {

/// Gets the amount of NFTs stored in the collection
/// @return An integer indicating the size of the collection
access(all) view fun getLength(): Int
access(all) view fun getLength(): Int {
return self.ownedNFTs.length
}

/// Allows a given function to iterate through the list
/// of owned NFT IDs in a collection without first
/// having to load the entire list into memory
access(all) fun forEachID(_ f: fun (UInt64): Bool): Void {
self.ownedNFTs.forEachKey(f)
}

/// Borrows a reference to an NFT stored in the collection
/// If the NFT with the specified ID is not in the collection,
Expand Down Expand Up @@ -231,4 +243,4 @@ access(all) contract interface NonFungibleToken: ViewResolver {
result.getIDs().length == 0: "The created collection must be empty!"
}
}
}
}
2 changes: 1 addition & 1 deletion cadence/tests/test_helpers.cdc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,34 @@ import "FlowEVMBridgeAccessor"
///
transaction(name: String, provider: Address) {

prepare(signer: auth(Inbox, SaveValue) &Account) {
let accessorCap: Capability<auth(EVM.Bridge) &FlowEVMBridgeAccessor.BridgeAccessor>
let routerRef: auth(EVM.Bridge) &{EVM.BridgeRouter}

prepare(signer: auth(BorrowValue, ClaimInboxCapability, SaveValue) &Account) {
let routerStoragePath = /storage/evmBridgeRouter

// Claim the BridgeAccessor Capability
let accessorCap = signer.inbox.claim<auth(EVM.Bridge) &FlowEVMBridgeAccessor.BridgeAccessor>(name, provider: provider)
?? panic("BridgeAccessor Capability not found")
self.accessorCap = signer.inbox.claim<auth(EVM.Bridge) &FlowEVMBridgeAccessor.BridgeAccessor>(
name,
provider: provider
) ?? panic("BridgeAccessor Capability not found")

// Ensure the Capability is valid and nothing is stored where the BridgeRouter should be stored
assert(self.accessorCap.check() == true, message: "Invalid BridgeAccessor Capability")
assert(
signer.storage.type(at: routerStoragePath) == nil,
message: "Collision where BridgeRouter will be stored"
)

// Ensure the Capability is valid
assert(accessorCap.check() == true, message: "Invalid BridgeAccessor Capability")
let router <-self.accessorCap.borrow()!.createBridgeRouter()
signer.storage.save(<-router, to: routerStoragePath)

// Create a Router to store the Capability and set the BridgeAccessor Capability in the Router
let router <- accessorCap.borrow()!.createBridgeRouter()
router.setBridgeAccessorCap(accessorCap)
// Borrow the router from storage and set the BridgeAccessor Capability
self.routerRef = signer.storage.borrow<auth(EVM.Bridge) &{EVM.BridgeRouter}>(from: routerStoragePath)
?? panic("BridgeRouter not found in storage")
}

// Save the Router in storage
signer.storage.save(<-router, to: /storage/evmBridgeRouter)
execute {
self.routerRef.setBridgeAccessor(self.accessorCap)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "EVM"

import "FlowEVMBridgeAccessor"

/// This transaction is intended to be run by the EVM contract account after FlowEVMBridgeAccessor.BridgeAccessor in the
/// event the BridgeAccessor Capability needs to be reset within the store BridgeRouter. Within this transacion, the
/// prepare block assumes a BridgeRouter is already stored at /storage/evmBridgeRouter.
///
/// @param name: The name of the BridgeAccessor Capability to claim
/// @param provider: The address of the account that published the BridgeAccessor Capability
///
transaction(name: String, provider: Address) {

let accessorCap: Capability<auth(EVM.Bridge) &FlowEVMBridgeAccessor.BridgeAccessor>
let router: auth(EVM.Bridge) &{EVM.BridgeRouter}

prepare(signer: auth(BorrowValue, ClaimInboxCapability, SaveValue) &Account) {
// Claim the BridgeAccessor Capability
self.accessorCap = signer.inbox.claim<auth(EVM.Bridge) &FlowEVMBridgeAccessor.BridgeAccessor>(
name,
provider: provider
) ?? panic("BridgeAccessor Capability not found")

// Ensure the Capability is valid
assert(self.accessorCap.check() == true, message: "Invalid BridgeAccessor Capability")

// Borrow the router from storage and set the BridgeAccessor Capability
self.router = signer.storage.borrow<auth(EVM.Bridge) &{EVM.BridgeRouter}>(from: /storage/evmBridgeRouter)
?? panic("BridgeRouter not found in storage")
}

execute {
self.router.setBridgeAccessor(self.accessorCap)
}
}
11 changes: 10 additions & 1 deletion flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,16 @@
"IEVMBridgeTokenMinter",
"IFlowEVMNFTBridge",
"IFlowEVMTokenBridge",
"FlowEVMBridge"
"FlowEVMBridge",
{
"name": "FlowEVMBridgeAccessor",
"args": [
{
"type": "Address",
"value": "0xb6763b4399a888c8"
}
]
}
]
}
}
Expand Down
Loading