-
Notifications
You must be signed in to change notification settings - Fork 7
/
bridge_nft_from_evm.cdc
113 lines (103 loc) · 5.86 KB
/
bridge_nft_from_evm.cdc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import "FungibleToken"
import "NonFungibleToken"
import "ViewResolver"
import "MetadataViews"
import "FlowToken"
import "ScopedFTProviders"
import "EVM"
import "FlowEVMBridge"
import "FlowEVMBridgeConfig"
import "FlowEVMBridgeUtils"
/// This transaction bridges an NFT from EVM to Cadence assuming it has already been onboarded to the FlowEVMBridge
/// NOTE: The ERC721 must have first been onboarded to the bridge. This can be checked via the method
/// FlowEVMBridge.evmAddressRequiresOnboarding(address: self.evmContractAddress)
///
/// @param nftIdentifier: The Cadence type identifier of the NFT to bridge - e.g. nft.getType().identifier
/// @param id: The ERC721 id of the NFT to bridge to Cadence from EVM
///
transaction(nftIdentifier: String, id: UInt256) {
let nftType: Type
let collection: &{NonFungibleToken.Collection}
let scopedProvider: @ScopedFTProviders.ScopedFTProvider
let coa: auth(EVM.Bridge) &EVM.CadenceOwnedAccount
prepare(signer: auth(BorrowValue, CopyValue, IssueStorageCapabilityController, PublishCapability, SaveValue, UnpublishCapability) &Account) {
/* --- Reference the signer's CadenceOwnedAccount --- */
//
// Borrow a reference to the signer's COA
self.coa = signer.storage.borrow<auth(EVM.Bridge) &EVM.CadenceOwnedAccount>(from: /storage/evm)
?? panic("Could not borrow COA signer's account at path /storage/evm")
/* --- Construct the NFT type --- */
//
// Construct the NFT type from the provided identifier
self.nftType = CompositeType(nftIdentifier)
?? panic("Could not construct NFT type from identifier: ".concat(nftIdentifier))
// Parse the NFT identifier into its components
let nftContractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: self.nftType)
?? panic("Could not get contract address from identifier: ".concat(nftIdentifier))
let nftContractName = FlowEVMBridgeUtils.getContractName(fromType: self.nftType)
?? panic("Could not get contract name from identifier: ".concat(nftIdentifier))
/* --- Reference the signer's NFT Collection --- */
//
// Borrow a reference to the NFT collection, configuring if necessary
let viewResolver = getAccount(nftContractAddress).contracts.borrow<&{ViewResolver}>(name: nftContractName)
?? panic("Could not borrow ViewResolver from NFT contract with name "
.concat(nftContractName).concat(" and address ")
.concat(nftContractAddress.toString()))
let collectionData = viewResolver.resolveContractView(
resourceType: self.nftType,
viewType: Type<MetadataViews.NFTCollectionData>()
) as! MetadataViews.NFTCollectionData?
?? panic("Could not resolve NFTCollectionData view for NFT type ".concat(self.nftType.identifier))
if signer.storage.borrow<&{NonFungibleToken.Collection}>(from: collectionData.storagePath) == nil {
signer.storage.save(<-collectionData.createEmptyCollection(), to: collectionData.storagePath)
signer.capabilities.unpublish(collectionData.publicPath)
let collectionCap = signer.capabilities.storage.issue<&{NonFungibleToken.Collection}>(collectionData.storagePath)
signer.capabilities.publish(collectionCap, at: collectionData.publicPath)
}
self.collection = signer.storage.borrow<&{NonFungibleToken.Collection}>(from: collectionData.storagePath)
?? panic("Could not borrow a NonFungibleToken Collection from the signer's storage path "
.concat(collectionData.storagePath.toString()))
/* --- Configure a ScopedFTProvider --- */
//
// Set a cap on the withdrawable bridge fee
var approxFee = FlowEVMBridgeUtils.calculateBridgeFee(
bytes: 400_000 // 400 kB as upper bound on movable storage used in a single transaction
)
// Issue and store bridge-dedicated Provider Capability in storage if necessary
if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil {
let providerCap = signer.capabilities.storage.issue<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>(
/storage/flowTokenVault
)
signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath)
}
// Copy the stored Provider capability and create a ScopedFTProvider
let providerCapCopy = signer.storage.copy<Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>>(
from: FlowEVMBridgeConfig.providerCapabilityStoragePath
) ?? panic("Invalid FungibleToken Provider Capability found in storage at path "
.concat(FlowEVMBridgeConfig.providerCapabilityStoragePath.toString()))
let providerFilter = ScopedFTProviders.AllowanceFilter(approxFee)
self.scopedProvider <- ScopedFTProviders.createScopedFTProvider(
provider: providerCapCopy,
filters: [ providerFilter ],
expiration: getCurrentBlock().timestamp + 1.0
)
}
execute {
// Execute the bridge
let nft: @{NonFungibleToken.NFT} <- self.coa.withdrawNFT(
type: self.nftType,
id: id,
feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
)
// Ensure the bridged nft is the correct type
assert(
nft.getType() == self.nftType,
message: "Bridged nft type mismatch - requested: ".concat(self.nftType.identifier)
.concat(", received: ").concat(nft.getType().identifier)
)
// Deposit the bridged NFT into the signer's collection
self.collection.deposit(token: <-nft)
// Destroy the ScopedFTProvider
destroy self.scopedProvider
}
}