-
Notifications
You must be signed in to change notification settings - Fork 7
/
bridge_tokens_to_evm.cdc
120 lines (108 loc) · 5.86 KB
/
bridge_tokens_to_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
114
115
116
117
118
119
120
import "FungibleToken"
import "ViewResolver"
import "FungibleTokenMetadataViews"
import "FlowToken"
import "ScopedFTProviders"
import "EVM"
import "FlowEVMBridge"
import "FlowEVMBridgeConfig"
import "FlowEVMBridgeUtils"
/// Bridges a Vault from the signer's storage to the signer's COA in EVM.Account.
///
/// NOTE: This transaction also onboards the Vault to the bridge if necessary which may incur additional fees
/// than bridging an asset that has already been onboarded.
///
/// @param vaultIdentifier: The Cadence type identifier of the FungibleToken Vault to bridge
/// - e.g. vault.getType().identifier
/// @param amount: The amount of tokens to bridge from EVM
///
transaction(vaultIdentifier: String, amount: UFix64) {
let sentVault: @{FungibleToken.Vault}
let coa: auth(EVM.Bridge) &EVM.CadenceOwnedAccount
let requiresOnboarding: Bool
let scopedProvider: @ScopedFTProviders.ScopedFTProvider
prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &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 Vault type --- */
//
// Construct the Vault type from the provided identifier
let vaultType = CompositeType(vaultIdentifier)
?? panic("Could not construct Vault type from identifier: ".concat(vaultIdentifier))
// Parse the Vault identifier into its components
let tokenContractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: vaultType)
?? panic("Could not get contract address from identifier: ".concat(vaultIdentifier))
let tokenContractName = FlowEVMBridgeUtils.getContractName(fromType: vaultType)
?? panic("Could not get contract name from identifier: ".concat(vaultIdentifier))
/* --- Retrieve the funds --- */
//
// Borrow a reference to the FungibleToken Vault
let viewResolver = getAccount(tokenContractAddress).contracts.borrow<&{ViewResolver}>(name: tokenContractName)
?? panic("Could not borrow ViewResolver from FungibleToken contract with name"
.concat(tokenContractName).concat(" and address ")
.concat(tokenContractAddress.toString()))
let vaultData = viewResolver.resolveContractView(
resourceType: vaultType,
viewType: Type<FungibleTokenMetadataViews.FTVaultData>()
) as! FungibleTokenMetadataViews.FTVaultData?
?? panic("Could not resolve FTVaultData view for Vault type ".concat(vaultType.identifier))
let vault = signer.storage.borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(
from: vaultData.storagePath
) ?? panic("Could not borrow FungibleToken Vault from storage path ".concat(vaultData.storagePath.toString()))
// Withdraw the requested balance & set a cap on the withdrawable bridge fee
self.sentVault <- vault.withdraw(amount: amount)
var approxFee = FlowEVMBridgeUtils.calculateBridgeFee(
bytes: 400_000 // 400 kB as upper bound on movable storage used in a single transaction
)
// Determine if the Vault requires onboarding - this impacts the fee required
self.requiresOnboarding = FlowEVMBridge.typeRequiresOnboarding(self.sentVault.getType())
?? panic("Bridge does not support the requested asset type ".concat(vaultIdentifier))
if self.requiresOnboarding {
approxFee = approxFee + FlowEVMBridgeConfig.onboardFee
}
/* --- Configure a ScopedFTProvider --- */
//
// 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
)
}
pre {
self.sentVault.getType().identifier == vaultIdentifier:
"Attempting to send invalid vault type - requested: ".concat(vaultIdentifier)
.concat(", sending: ").concat(self.sentVault.getType().identifier)
}
execute {
if self.requiresOnboarding {
// Onboard the Vault to the bridge
FlowEVMBridge.onboardByType(
self.sentVault.getType(),
feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
)
}
// Execute the bridge
self.coa.depositTokens(
vault: <-self.sentVault,
feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
)
// Destroy the ScopedFTProvider
destroy self.scopedProvider
}
}