Smart Contract

EVMTokenRegistry

A.17ae3b1b0b0d50db.EVMTokenRegistry

Valid From

135,478,102

Deployed

6d ago
Feb 22, 2026, 02:17:12 AM UTC

Dependents

4 imports
1import EVM from 0xe467b9dd11fa00df
2import FungibleToken from 0xf233dcee88fe0abe
3import FlowToken from 0x1654653399040a61
4
5/// EVMTokenRegistry: Registry mapping Cadence fungible tokens to their EVM addresses
6///
7/// This contract maintains a registry of token mappings between Cadence and Flow EVM.
8/// It's used by the DCA system to resolve EVM addresses for cross-VM swaps.
9///
10/// Flow EVM Mainnet Chain ID: 747
11///
12access(all) contract EVMTokenRegistry {
13
14    /// Events
15    access(all) event TokenRegistered(
16        cadenceType: String,
17        evmAddress: String,
18        symbol: String,
19        decimals: UInt8
20    )
21    access(all) event TokenUpdated(
22        cadenceType: String,
23        oldEVMAddress: String,
24        newEVMAddress: String
25    )
26    access(all) event TokenRemoved(cadenceType: String, evmAddress: String)
27
28    /// Storage paths
29    access(all) let AdminStoragePath: StoragePath
30    access(all) let RegistryStoragePath: StoragePath
31
32    /// Token info structure
33    access(all) struct TokenInfo {
34        access(all) let cadenceType: String
35        access(all) let evmAddress: EVM.EVMAddress
36        access(all) let symbol: String
37        access(all) let decimals: UInt8
38        access(all) let isNativelyEVM: Bool  // True if token originated on EVM
39
40        init(
41            cadenceType: String,
42            evmAddress: EVM.EVMAddress,
43            symbol: String,
44            decimals: UInt8,
45            isNativelyEVM: Bool
46        ) {
47            self.cadenceType = cadenceType
48            self.evmAddress = evmAddress
49            self.symbol = symbol
50            self.decimals = decimals
51            self.isNativelyEVM = isNativelyEVM
52        }
53    }
54
55    /// Registry storage - maps Cadence type identifier to EVM address
56    access(self) var tokenRegistry: {String: TokenInfo}
57    
58    /// Reverse lookup - maps EVM address (as hex string) to Cadence type
59    access(self) var evmToCADence: {String: String}
60
61    /// Well-known token addresses on Flow EVM Mainnet (Chain ID: 747)
62    /// WFLOW - Wrapped FLOW
63    access(all) let WFLOW_ADDRESS: EVM.EVMAddress
64    /// Common stablecoin and token addresses
65    access(all) let WETH_ADDRESS: EVM.EVMAddress
66    access(all) let CBBTC_ADDRESS: EVM.EVMAddress
67    access(all) let USDF_ADDRESS: EVM.EVMAddress
68
69    /// Get EVM address for a Cadence token type
70    access(all) fun getEVMAddress(_ cadenceType: Type): EVM.EVMAddress? {
71        let typeId = cadenceType.identifier
72        if let info = self.tokenRegistry[typeId] {
73            return info.evmAddress
74        }
75        return nil
76    }
77
78    /// Get EVM address by type identifier string
79    access(all) fun getEVMAddressByTypeId(_ typeId: String): EVM.EVMAddress? {
80        if let info = self.tokenRegistry[typeId] {
81            return info.evmAddress
82        }
83        return nil
84    }
85
86    /// Get token info for a Cadence type
87    access(all) fun getTokenInfo(_ cadenceType: Type): TokenInfo? {
88        return self.tokenRegistry[cadenceType.identifier]
89    }
90
91    /// Get token info by type identifier string
92    access(all) fun getTokenInfoByTypeId(_ typeId: String): TokenInfo? {
93        return self.tokenRegistry[typeId]
94    }
95
96    /// Get Cadence type for an EVM address
97    access(all) fun getCadenceType(_ evmAddress: EVM.EVMAddress): String? {
98        return self.evmToCADence[evmAddress.toString()]
99    }
100
101    /// Check if a token is registered
102    access(all) fun isRegistered(_ cadenceType: Type): Bool {
103        return self.tokenRegistry[cadenceType.identifier] != nil
104    }
105
106    /// Check if a token is registered by type identifier
107    access(all) fun isRegisteredByTypeId(_ typeId: String): Bool {
108        return self.tokenRegistry[typeId] != nil
109    }
110
111    /// Get all registered tokens
112    access(all) fun getAllTokens(): [TokenInfo] {
113        return self.tokenRegistry.values
114    }
115
116    /// Get EVM address for FLOW/WFLOW
117    /// FLOW uses WFLOW on EVM side
118    access(all) fun getFlowEVMAddress(): EVM.EVMAddress {
119        return self.WFLOW_ADDRESS
120    }
121
122    /// Admin resource for managing the registry
123    access(all) resource Admin {
124        /// Register a new token mapping
125        access(all) fun registerToken(
126            cadenceType: String,
127            evmAddressHex: String,
128            symbol: String,
129            decimals: UInt8,
130            isNativelyEVM: Bool
131        ) {
132            pre {
133                EVMTokenRegistry.tokenRegistry[cadenceType] == nil: "Token already registered"
134            }
135
136            let evmAddress = EVM.addressFromString(evmAddressHex)
137            let info = TokenInfo(
138                cadenceType: cadenceType,
139                evmAddress: evmAddress,
140                symbol: symbol,
141                decimals: decimals,
142                isNativelyEVM: isNativelyEVM
143            )
144
145            EVMTokenRegistry.tokenRegistry[cadenceType] = info
146            EVMTokenRegistry.evmToCADence[evmAddressHex.toLower()] = cadenceType
147
148            emit TokenRegistered(
149                cadenceType: cadenceType,
150                evmAddress: evmAddressHex,
151                symbol: symbol,
152                decimals: decimals
153            )
154        }
155
156        /// Update an existing token's EVM address
157        access(all) fun updateToken(
158            cadenceType: String,
159            newEVMAddressHex: String
160        ) {
161            pre {
162                EVMTokenRegistry.tokenRegistry[cadenceType] != nil: "Token not registered"
163            }
164
165            let oldInfo = EVMTokenRegistry.tokenRegistry[cadenceType]!
166            let oldAddressHex = oldInfo.evmAddress.toString()
167
168            let newEvmAddress = EVM.addressFromString(newEVMAddressHex)
169            let newInfo = TokenInfo(
170                cadenceType: cadenceType,
171                evmAddress: newEvmAddress,
172                symbol: oldInfo.symbol,
173                decimals: oldInfo.decimals,
174                isNativelyEVM: oldInfo.isNativelyEVM
175            )
176
177            // Update registry
178            EVMTokenRegistry.tokenRegistry[cadenceType] = newInfo
179            
180            // Update reverse lookup
181            EVMTokenRegistry.evmToCADence.remove(key: oldAddressHex.toLower())
182            EVMTokenRegistry.evmToCADence[newEVMAddressHex.toLower()] = cadenceType
183
184            emit TokenUpdated(
185                cadenceType: cadenceType,
186                oldEVMAddress: oldAddressHex,
187                newEVMAddress: newEVMAddressHex
188            )
189        }
190
191        /// Remove a token from the registry
192        access(all) fun removeToken(cadenceType: String) {
193            pre {
194                EVMTokenRegistry.tokenRegistry[cadenceType] != nil: "Token not registered"
195            }
196
197            let info = EVMTokenRegistry.tokenRegistry[cadenceType]!
198            let evmAddressHex = info.evmAddress.toString()
199
200            EVMTokenRegistry.tokenRegistry.remove(key: cadenceType)
201            EVMTokenRegistry.evmToCADence.remove(key: evmAddressHex.toLower())
202
203            emit TokenRemoved(cadenceType: cadenceType, evmAddress: evmAddressHex)
204        }
205    }
206
207    /// Create admin resource
208    access(all) fun createAdmin(): @Admin {
209        return <- create Admin()
210    }
211
212    init() {
213        // Initialize storage paths
214        self.AdminStoragePath = /storage/EVMTokenRegistryAdmin
215        self.RegistryStoragePath = /storage/EVMTokenRegistry
216
217        // Initialize empty registry
218        self.tokenRegistry = {}
219        self.evmToCADence = {}
220
221        // Initialize well-known EVM token addresses on Flow EVM Mainnet
222        // WFLOW - Wrapped FLOW (used for native FLOW on EVM)
223        self.WFLOW_ADDRESS = EVM.addressFromString("0xd3bF53DAC106A0290B0483EcBC89d40FcC961f3e")
224        // WETH - Wrapped Ether (bridged)
225        self.WETH_ADDRESS = EVM.addressFromString("0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590")
226        // cbBTC - Coinbase Wrapped Bitcoin
227        self.CBBTC_ADDRESS = EVM.addressFromString("0xA0197b2044D28b08Be34d98b23c9312158Ea9A18")
228        // USDF - Flow native stablecoin
229        self.USDF_ADDRESS = EVM.addressFromString("0x2aabea2058b5ac2d339b163c6ab6f2b6d53aabed")
230
231        // Pre-register known tokens
232        // FLOW -> WFLOW
233        let flowTypeId = Type<@FlowToken.Vault>().identifier
234        self.tokenRegistry[flowTypeId] = TokenInfo(
235            cadenceType: flowTypeId,
236            evmAddress: self.WFLOW_ADDRESS,
237            symbol: "FLOW",
238            decimals: 18,
239            isNativelyEVM: false
240        )
241        self.evmToCADence["0xd3bf53dac106a0290b0483ecbc89d40fcc961f3e"] = flowTypeId
242    }
243}
244
245
246
247
248