Smart Contract

EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3

A.1e4aa0b87d10b141.EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3

Deployed

1w ago
Feb 16, 2026, 08:14:21 PM UTC

Dependents

0 imports
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_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3 : 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_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.name
66        }
67        /// Gets the ERC20 symbol value
68        access(all) view fun getSymbol(): String {
69            return EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.symbol
70        }
71        /// Gets the ERC20 decimals value
72        access(all) view fun getDecimals(): UInt8 {
73            return EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.decimals
74        }
75        /// Returns the EVM contract address of the fungible token
76        access(all) view fun getEVMContractAddress(): EVM.EVMAddress {
77            return EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.getEVMContractAddress()
78        }
79
80        access(all) view fun getViews(): [Type] {
81            return EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.getContractViews(resourceType: nil)
82        }
83
84        access(all) fun resolveView(_ view: Type): AnyStruct? {
85            return EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.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_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.totalSupply = EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.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_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.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_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3Vault,
219                    receiverPath: /public/EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3Receiver,
220                    metadataPath: /public/EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3Vault,
221                    receiverLinkedType: Type<&EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.Vault>(),
222                    metadataLinkedType: Type<&EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.Vault>(),
223                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
224                        return <-self.createEmptyVault(vaultType: Type<@EVMVMBridgedToken_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.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_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3")!
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_8c6387781fca4ac8613f02b5ca7cc4888dccbfe3.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