Smart Contract
EVMTokenRegistry
A.17ae3b1b0b0d50db.EVMTokenRegistry
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