Smart Contract
EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0
A.1e4aa0b87d10b141.EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0
1import NonFungibleToken from 0x1d7e57aa55817448
2import MetadataViews from 0x1d7e57aa55817448
3import ViewResolver from 0x1d7e57aa55817448
4import FungibleToken from 0xf233dcee88fe0abe
5import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
6import FlowToken from 0x1654653399040a61
7
8import EVM from 0xe467b9dd11fa00df
9
10import ICrossVM from 0x1e4aa0b87d10b141
11import ICrossVMAsset from 0x1e4aa0b87d10b141
12import IEVMBridgeTokenMinter from 0x1e4aa0b87d10b141
13import FlowEVMBridgeTokenEscrow from 0x1e4aa0b87d10b141
14import FlowEVMBridgeConfig from 0x1e4aa0b87d10b141
15import FlowEVMBridgeUtils from 0x1e4aa0b87d10b141
16import FlowEVMBridge from 0x1e4aa0b87d10b141
17import CrossVMToken from 0x1e4aa0b87d10b141
18import FlowEVMBridgeResolver from 0x1e4aa0b87d10b141
19
20/// This contract is a template used by FlowEVMBridge to define EVM-native fungible tokens bridged from Flow EVM to
21/// Cadence. Upon deployment of this contract, the contract name is derived as a function of the asset type (here an
22/// ERC20) and the contract's EVM address. The derived contract name is then joined with this contract's code,
23/// prepared as chunks in FlowEVMBridgeTemplates before being deployed to the Flow EVM Bridge account.
24///
25/// On bridging, the ERC20 is transferred to the bridge's CadenceOwnedAccount EVM address and tokens are minted from
26/// this contract to the bridging caller. On return to Flow EVM, the reverse process is followed - the token is burned
27/// in this contract and the ERC20 is transferred to the defined recipient. In this way, the Cadence Vault acts as a
28/// representation of both the EVM tokens and thus ownership rights to it upon bridging back to Flow EVM.
29///
30/// To bridge between VMs, a caller can either use the interface exposed on CadenceOwnedAccount or use FlowEVMBridge
31/// public contract methods.
32///
33access(all) contract EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0 : ICrossVM, ICrossVMAsset, IEVMBridgeTokenMinter, FungibleToken {
34
35 /// Pointer to the Factory deployed Solidity contract address defining the bridged asset
36 access(all) let evmTokenContractAddress: EVM.EVMAddress
37 /// Name of the fungible token defined in the corresponding ERC20 contract
38 access(all) let name: String
39 /// Symbol of the fungible token defined in the corresponding ERC20 contract
40 access(all) let symbol: String
41 /// Decimal place value defined in the source ERC20 contract
42 access(all) let decimals: UInt8
43 /// URI of the contract, if available as a var in case the bridge enables cross-VM Metadata syncing in the future
44 access(all) var contractURI: String?
45 /// Total supply of this Cadence token in circulation
46 /// NOTE: This does not reflect the total supply of the source ERC20 in circulation within EVM
47 access(all) var totalSupply: UFix64
48 /// Retain a Vault to reference when resolving Vault Metadata
49 access(self) let vault: @Vault
50
51 /// The Vault resource representing the bridged ERC20 token
52 ///
53 access(all) resource Vault : ICrossVMAsset.AssetInfo, CrossVMToken.EVMTokenInfo, FungibleToken.Vault {
54 /// Balance of the tokens in a given Vault
55 access(all) var balance: UFix64
56
57 init(balance: UFix64) {
58 self.balance = balance
59 }
60
61 /* --- CrossVMToken.EVMFTVault conformance --- */
62 //
63 /// Gets the ERC20 name value
64 access(all) view fun getName(): String {
65 return EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.name
66 }
67 /// Gets the ERC20 symbol value
68 access(all) view fun getSymbol(): String {
69 return EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.symbol
70 }
71 /// Gets the ERC20 decimals value
72 access(all) view fun getDecimals(): UInt8 {
73 return EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.decimals
74 }
75 /// Returns the EVM contract address of the fungible token
76 access(all) view fun getEVMContractAddress(): EVM.EVMAddress {
77 return EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.getEVMContractAddress()
78 }
79
80 access(all) view fun getViews(): [Type] {
81 return EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.getContractViews(resourceType: nil)
82 }
83
84 access(all) fun resolveView(_ view: Type): AnyStruct? {
85 return EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.resolveContractView(resourceType: nil, viewType: view)
86 }
87
88 /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
89 access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
90 return { self.getType(): true }
91 }
92
93 access(all) view fun isSupportedVaultType(type: Type): Bool {
94 return self.getSupportedVaultTypes()[type] ?? false
95 }
96
97 /// Asks if the amount can be withdrawn from this vault
98 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
99 return amount <= self.balance
100 }
101
102 /// deposit
103 ///
104 /// Function that takes a Vault object as an argument and adds
105 /// its balance to the balance of the owners Vault.
106 ///
107 /// It is allowed to destroy the sent Vault because the Vault
108 /// was a temporary holder of the tokens. The Vault's balance has
109 /// been consumed and therefore can be destroyed.
110 ///
111 access(all) fun deposit(from: @{FungibleToken.Vault}) {
112 let vault <- from as! @Vault
113 self.balance = self.balance + vault.balance
114 vault.balance = 0.0
115 destroy vault
116 }
117
118 /// createEmptyVault
119 ///
120 /// Function that creates a new Vault with a balance of zero
121 /// and returns it to the calling context. A user must call this function
122 /// and store the returned Vault in their storage in order to allow their
123 /// account to be able to receive deposits of this token type.
124 ///
125 access(all) fun createEmptyVault(): @Vault {
126 return <-create Vault(balance: 0.0)
127 }
128
129 /// withdraw
130 ///
131 /// Function that takes an amount as an argument
132 /// and withdraws that amount from the Vault.
133 ///
134 /// It creates a new temporary Vault that is used to hold
135 /// the tokens that are being transferred. It returns the newly
136 /// created Vault to the context that called so it can be deposited
137 /// elsewhere.
138 ///
139 access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @Vault {
140 self.balance = self.balance - amount
141 return <-create Vault(balance: amount)
142 }
143
144 /// Called when a fungible token is burned via the `Burner.burn()` method
145 access(contract) fun burnCallback() {
146 if self.balance > 0.0 {
147 EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.totalSupply = EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.totalSupply - self.balance
148 }
149 self.balance = 0.0
150 }
151 }
152
153 /// createEmptyVault
154 ///
155 /// Function that creates a new Vault with a balance of zero and returns it to the calling context. A user must call
156 /// this function and store the returned Vault in their storage in order to allow their account to be able to
157 /// receive deposits of this token type.
158 ///
159 access(all) fun createEmptyVault(vaultType: Type): @EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.Vault {
160 return <- create Vault(balance: 0.0)
161 }
162
163 /**********************
164 Getters
165 ***********************/
166
167 /// Returns the name of the asset
168 ///
169 access(all) view fun getName(): String {
170 return self.name
171 }
172
173 /// Returns the symbol of the asset
174 ///
175 access(all) view fun getSymbol(): String {
176 return self.symbol
177 }
178
179 /// Returns the EVM contract address of the fungible token this contract represents
180 ///
181 access(all) view fun getEVMContractAddress(): EVM.EVMAddress {
182 return self.evmTokenContractAddress
183 }
184
185 /// Function that returns all the Metadata Views implemented by this fungible token contract.
186 ///
187 /// @return An array of Types defining the implemented views. This value will be used by developers to know which
188 /// parameter to pass to the resolveContractView() method.
189 ///
190 access(all) view fun getContractViews(resourceType: Type?): [Type] {
191 return [
192 Type<FungibleTokenMetadataViews.FTView>(),
193 Type<FungibleTokenMetadataViews.FTDisplay>(),
194 Type<FungibleTokenMetadataViews.FTVaultData>(),
195 Type<FungibleTokenMetadataViews.TotalSupply>(),
196 Type<MetadataViews.EVMBridgedMetadata>()
197 ]
198 }
199
200 /// Function that resolves a metadata view for this contract.
201 ///
202 /// @param view: The Type of the desired view.
203 ///
204 /// @return A structure representing the requested view.
205 ///
206 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
207 switch viewType {
208 case Type<FungibleTokenMetadataViews.FTView>():
209 return FungibleTokenMetadataViews.FTView(
210 ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
211 ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
212 )
213 case Type<FungibleTokenMetadataViews.FTDisplay>():
214 let contractRef = self.borrowThisContract()
215 return FlowEVMBridgeResolver.resolveBridgedView(bridgedContract: contractRef, view: Type<FungibleTokenMetadataViews.FTDisplay>())
216 case Type<FungibleTokenMetadataViews.FTVaultData>():
217 return FungibleTokenMetadataViews.FTVaultData(
218 storagePath: /storage/EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0Vault,
219 receiverPath: /public/EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0Receiver,
220 metadataPath: /public/EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0Vault,
221 receiverLinkedType: Type<&EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.Vault>(),
222 metadataLinkedType: Type<&EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.Vault>(),
223 createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
224 return <-self.createEmptyVault(vaultType: Type<@EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.Vault>())
225 })
226 )
227 case Type<FungibleTokenMetadataViews.TotalSupply>():
228 return FungibleTokenMetadataViews.TotalSupply(
229 totalSupply: self.totalSupply
230 )
231 case Type<MetadataViews.EVMBridgedMetadata>():
232 return MetadataViews.EVMBridgedMetadata(
233 name: self.name,
234 symbol: self.symbol,
235 uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "")
236 )
237 }
238 return nil
239 }
240
241 /**********************
242 Internal Methods
243 ***********************/
244
245 /// Allows the bridge to mint tokens from bridge-defined fungible token contracts
246 ///
247 access(account) fun mintTokens(amount: UFix64): @{FungibleToken.Vault} {
248 self.totalSupply = self.totalSupply + amount
249 return <- create Vault(balance: amount)
250 }
251
252 /// Returns a reference to this contract as an ICrossVMAsset contract
253 ///
254 access(self)
255 fun borrowThisContract(): &{ICrossVMAsset} {
256 let contractAddress = self.account.address
257 return getAccount(contractAddress).contracts.borrow<&{ICrossVMAsset}>(name: "EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0")!
258 }
259
260 init(name: String, symbol: String, decimals: UInt8, evmContractAddress: EVM.EVMAddress, contractURI: String?) {
261 self.evmTokenContractAddress = evmContractAddress
262 self.name = name
263 self.symbol = symbol
264 self.decimals = decimals
265 self.contractURI = contractURI
266 self.totalSupply = 0.0
267 self.vault <- create Vault(balance: 0.0)
268
269 FlowEVMBridgeConfig.associateType(Type<@EVMVMBridgedToken_d59d7c7f4d063a707a021c7c49c5cfb4faf94cb0.Vault>(), with: self.evmTokenContractAddress)
270 FlowEVMBridgeTokenEscrow.initializeEscrow(
271 with: <-create Vault(balance: 0.0),
272 name: name,
273 symbol: symbol,
274 decimals: decimals,
275 evmTokenAddress: self.evmTokenContractAddress
276 )
277 }
278}
279