@flowtyio/flow-contracts 0.1.0-beta.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +1 -1
  2. package/contracts/FungibleTokenSwitchboard.cdc +360 -0
  3. package/contracts/MetadataViews.cdc +79 -6
  4. package/contracts/NonFungibleToken.cdc +17 -10
  5. package/contracts/capability-cache/CapabilityCache.cdc +97 -0
  6. package/contracts/dapper/TopShot.cdc +323 -259
  7. package/contracts/dapper/TopShotLocking.cdc +41 -15
  8. package/contracts/dapper/offers/DapperOffersV2.cdc +46 -43
  9. package/contracts/dapper/offers/OffersV2.cdc +40 -56
  10. package/contracts/dapper/offers/Resolver.cdc +20 -13
  11. package/contracts/emerald-city/FLOAT.cdc +259 -254
  12. package/contracts/evm/CrossVMNFT.cdc +50 -0
  13. package/contracts/evm/EVM.cdc +851 -0
  14. package/contracts/evm/FlowEVMBridgeConfig.cdc +454 -0
  15. package/contracts/evm/FlowEVMBridgeHandlerInterfaces.cdc +163 -0
  16. package/contracts/evm/FlowEVMBridgeHandlers.cdc +230 -0
  17. package/contracts/evm/FlowEVMBridgeUtils.cdc +1303 -0
  18. package/contracts/evm/IBridgePermissions.cdc +19 -0
  19. package/contracts/evm/ICrossVM.cdc +12 -0
  20. package/contracts/evm/ICrossVMAsset.cdc +19 -0
  21. package/contracts/evm/Serialize.cdc +141 -0
  22. package/contracts/evm/SerializeMetadata.cdc +221 -0
  23. package/contracts/example/ExampleNFT.cdc +2 -2
  24. package/contracts/find/FindViews.cdc +357 -353
  25. package/contracts/flow-utils/ScopedFTProviders.cdc +5 -2
  26. package/contracts/flow-utils/ScopedNFTProviders.cdc +6 -2
  27. package/contracts/flowty-drops/ContractManager.cdc +73 -0
  28. package/contracts/flowty-drops/DropFactory.cdc +75 -0
  29. package/contracts/flowty-drops/DropTypes.cdc +282 -0
  30. package/contracts/flowty-drops/FlowtyActiveCheckers.cdc +113 -0
  31. package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
  32. package/contracts/flowty-drops/FlowtyDrops.cdc +461 -0
  33. package/contracts/flowty-drops/FlowtyPricers.cdc +48 -0
  34. package/contracts/flowty-drops/initializers/ContractBorrower.cdc +14 -0
  35. package/contracts/flowty-drops/initializers/ContractInitializer.cdc +7 -0
  36. package/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +57 -0
  37. package/contracts/flowty-drops/nft/BaseCollection.cdc +97 -0
  38. package/contracts/flowty-drops/nft/BaseNFT.cdc +107 -0
  39. package/contracts/flowty-drops/nft/ContractFactory.cdc +13 -0
  40. package/contracts/flowty-drops/nft/ContractFactoryTemplate.cdc +48 -0
  41. package/contracts/flowty-drops/nft/NFTMetadata.cdc +140 -0
  42. package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +42 -0
  43. package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +54 -0
  44. package/contracts/flowty-drops/nft/UniversalCollection.cdc +29 -0
  45. package/contracts/fungible-token-router/FungibleTokenRouter.cdc +103 -0
  46. package/contracts/hybrid-custody/CapabilityDelegator.cdc +28 -26
  47. package/contracts/hybrid-custody/CapabilityFactory.cdc +20 -18
  48. package/contracts/hybrid-custody/CapabilityFilter.cdc +41 -24
  49. package/contracts/hybrid-custody/HybridCustody.cdc +303 -242
  50. package/contracts/hybrid-custody/factories/FTAllFactory.cdc +16 -4
  51. package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +16 -4
  52. package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +17 -5
  53. package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +16 -4
  54. package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +16 -4
  55. package/contracts/hybrid-custody/factories/FTVaultFactory.cdc +46 -0
  56. package/contracts/hybrid-custody/factories/NFTCollectionFactory.cdc +45 -0
  57. package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +16 -4
  58. package/contracts/hybrid-custody/factories/NFTProviderAndCollectionFactory.cdc +22 -0
  59. package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +16 -4
  60. package/contracts/lost-and-found/LostAndFound.cdc +21 -17
  61. package/contracts/tokens/USDCFlow.cdc +232 -0
  62. package/flow.json +278 -7
  63. package/package.json +1 -1
  64. package/contracts/hybrid-custody/factories/NFTProviderAndCollectionPublicFactory.cdc +0 -10
@@ -0,0 +1,851 @@
1
+ import Crypto
2
+ import "NonFungibleToken"
3
+ import "FungibleToken"
4
+ import "FlowToken"
5
+
6
+ access(all)
7
+ contract EVM {
8
+
9
+ // Entitlements enabling finer-grained access control on a CadenceOwnedAccount
10
+ access(all) entitlement Validate
11
+ access(all) entitlement Withdraw
12
+ access(all) entitlement Call
13
+ access(all) entitlement Deploy
14
+ access(all) entitlement Owner
15
+ access(all) entitlement Bridge
16
+
17
+ /// Block executed event is emitted when a new block is created,
18
+ /// which always happens when a transaction is executed.
19
+ access(all)
20
+ event BlockExecuted(
21
+ // height or number of the block
22
+ height: UInt64,
23
+ // hash of the block
24
+ hash: [UInt8; 32],
25
+ // timestamp of the block creation
26
+ timestamp: UInt64,
27
+ // total Flow supply
28
+ totalSupply: Int,
29
+ // all gas used in the block by transactions included
30
+ totalGasUsed: UInt64,
31
+ // parent block hash
32
+ parentHash: [UInt8; 32],
33
+ // root hash of all the transaction receipts
34
+ receiptRoot: [UInt8; 32],
35
+ // root hash of all the transaction hashes
36
+ transactionHashRoot: [UInt8; 32],
37
+ /// value returned for PREVRANDAO opcode
38
+ prevrandao: [UInt8; 32],
39
+ )
40
+
41
+ /// Transaction executed event is emitted every time a transaction
42
+ /// is executed by the EVM (even if failed).
43
+ access(all)
44
+ event TransactionExecuted(
45
+ // hash of the transaction
46
+ hash: [UInt8; 32],
47
+ // index of the transaction in a block
48
+ index: UInt16,
49
+ // type of the transaction
50
+ type: UInt8,
51
+ // RLP encoded transaction payload
52
+ payload: [UInt8],
53
+ // code indicating a specific validation (201-300) or execution (301-400) error
54
+ errorCode: UInt16,
55
+ // a human-readable message about the error (if any)
56
+ errorMessage: String,
57
+ // the amount of gas transaction used
58
+ gasConsumed: UInt64,
59
+ // if transaction was a deployment contains a newly deployed contract address
60
+ contractAddress: String,
61
+ // RLP encoded logs
62
+ logs: [UInt8],
63
+ // block height in which transaction was included
64
+ blockHeight: UInt64,
65
+ /// captures the hex encoded data that is returned from
66
+ /// the evm. For contract deployments
67
+ /// it returns the code deployed to
68
+ /// the address provided in the contractAddress field.
69
+ /// in case of revert, the smart contract custom error message
70
+ /// is also returned here (see EIP-140 for more details).
71
+ returnedData: [UInt8],
72
+ /// captures the input and output of the calls (rlp encoded) to the extra
73
+ /// precompiled contracts (e.g. Cadence Arch) during the transaction execution.
74
+ /// This data helps to replay the transactions without the need to
75
+ /// have access to the full cadence state data.
76
+ precompiledCalls: [UInt8]
77
+ )
78
+
79
+ access(all)
80
+ event CadenceOwnedAccountCreated(address: String)
81
+
82
+ /// FLOWTokensDeposited is emitted when FLOW tokens is bridged
83
+ /// into the EVM environment. Note that this event is not emitted
84
+ /// for transfer of flow tokens between two EVM addresses.
85
+ /// Similar to the FungibleToken.Deposited event
86
+ /// this event includes a depositedUUID that captures the
87
+ /// uuid of the source vault.
88
+ access(all)
89
+ event FLOWTokensDeposited(
90
+ address: String,
91
+ amount: UFix64,
92
+ depositedUUID: UInt64,
93
+ balanceAfterInAttoFlow: UInt
94
+ )
95
+
96
+ /// FLOWTokensWithdrawn is emitted when FLOW tokens are bridged
97
+ /// out of the EVM environment. Note that this event is not emitted
98
+ /// for transfer of flow tokens between two EVM addresses.
99
+ /// similar to the FungibleToken.Withdrawn events
100
+ /// this event includes a withdrawnUUID that captures the
101
+ /// uuid of the returning vault.
102
+ access(all)
103
+ event FLOWTokensWithdrawn(
104
+ address: String,
105
+ amount: UFix64,
106
+ withdrawnUUID: UInt64,
107
+ balanceAfterInAttoFlow: UInt
108
+ )
109
+
110
+ /// BridgeAccessorUpdated is emitted when the BridgeAccessor Capability
111
+ /// is updated in the stored BridgeRouter along with identifying
112
+ /// information about both.
113
+ access(all)
114
+ event BridgeAccessorUpdated(
115
+ routerType: Type,
116
+ routerUUID: UInt64,
117
+ routerAddress: Address,
118
+ accessorType: Type,
119
+ accessorUUID: UInt64,
120
+ accessorAddress: Address
121
+ )
122
+
123
+ /// EVMAddress is an EVM-compatible address
124
+ access(all)
125
+ struct EVMAddress {
126
+
127
+ /// Bytes of the address
128
+ access(all)
129
+ let bytes: [UInt8; 20]
130
+
131
+ /// Constructs a new EVM address from the given byte representation
132
+ view init(bytes: [UInt8; 20]) {
133
+ self.bytes = bytes
134
+ }
135
+
136
+ /// Balance of the address
137
+ access(all)
138
+ view fun balance(): Balance {
139
+ let balance = InternalEVM.balance(
140
+ address: self.bytes
141
+ )
142
+ return Balance(attoflow: balance)
143
+ }
144
+
145
+ /// Nonce of the address
146
+ access(all)
147
+ fun nonce(): UInt64 {
148
+ return InternalEVM.nonce(
149
+ address: self.bytes
150
+ )
151
+ }
152
+
153
+ /// Code of the address
154
+ access(all)
155
+ fun code(): [UInt8] {
156
+ return InternalEVM.code(
157
+ address: self.bytes
158
+ )
159
+ }
160
+
161
+ /// CodeHash of the address
162
+ access(all)
163
+ fun codeHash(): [UInt8] {
164
+ return InternalEVM.codeHash(
165
+ address: self.bytes
166
+ )
167
+ }
168
+
169
+ /// Deposits the given vault into the EVM account with the given address
170
+ access(all)
171
+ fun deposit(from: @FlowToken.Vault) {
172
+ let amount = from.balance
173
+ if amount == 0.0 {
174
+ panic("calling deposit function with an empty vault is not allowed")
175
+ }
176
+ let depositedUUID = from.uuid
177
+ InternalEVM.deposit(
178
+ from: <-from,
179
+ to: self.bytes
180
+ )
181
+ emit FLOWTokensDeposited(
182
+ address: self.toString(),
183
+ amount: amount,
184
+ depositedUUID: depositedUUID,
185
+ balanceAfterInAttoFlow: self.balance().attoflow
186
+ )
187
+ }
188
+
189
+ /// Serializes the address to a hex string without the 0x prefix
190
+ /// Future implementations should pass data to InternalEVM for native serialization
191
+ access(all)
192
+ view fun toString(): String {
193
+ return String.encodeHex(self.bytes.toVariableSized())
194
+ }
195
+
196
+ /// Compares the address with another address
197
+ access(all)
198
+ view fun equals(_ other: EVMAddress): Bool {
199
+ return self.bytes == other.bytes
200
+ }
201
+ }
202
+
203
+ /// Converts a hex string to an EVM address if the string is a valid hex string
204
+ /// Future implementations should pass data to InternalEVM for native deserialization
205
+ access(all)
206
+ fun addressFromString(_ asHex: String): EVMAddress {
207
+ pre {
208
+ asHex.length == 40 || asHex.length == 42: "Invalid hex string length for an EVM address"
209
+ }
210
+ // Strip the 0x prefix if it exists
211
+ var withoutPrefix = (asHex[1] == "x" ? asHex.slice(from: 2, upTo: asHex.length) : asHex).toLower()
212
+ let bytes = withoutPrefix.decodeHex().toConstantSized<[UInt8; 20]>()!
213
+ return EVMAddress(bytes: bytes)
214
+ }
215
+
216
+ access(all)
217
+ struct Balance {
218
+
219
+ /// The balance in atto-FLOW
220
+ /// Atto-FLOW is the smallest denomination of FLOW (1e18 FLOW)
221
+ /// that is used to store account balances inside EVM
222
+ /// similar to the way WEI is used to store ETH divisible to 18 decimal places.
223
+ access(all)
224
+ var attoflow: UInt
225
+
226
+ /// Constructs a new balance
227
+ access(all)
228
+ view init(attoflow: UInt) {
229
+ self.attoflow = attoflow
230
+ }
231
+
232
+ /// Sets the balance by a UFix64 (8 decimal points), the format
233
+ /// that is used in Cadence to store FLOW tokens.
234
+ access(all)
235
+ fun setFLOW(flow: UFix64){
236
+ self.attoflow = InternalEVM.castToAttoFLOW(balance: flow)
237
+ }
238
+
239
+ /// Casts the balance to a UFix64 (rounding down)
240
+ /// Warning! casting a balance to a UFix64 which supports a lower level of precision
241
+ /// (8 decimal points in compare to 18) might result in rounding down error.
242
+ /// Use the toAttoFlow function if you care need more accuracy.
243
+ access(all)
244
+ view fun inFLOW(): UFix64 {
245
+ return InternalEVM.castToFLOW(balance: self.attoflow)
246
+ }
247
+
248
+ /// Returns the balance in Atto-FLOW
249
+ access(all)
250
+ view fun inAttoFLOW(): UInt {
251
+ return self.attoflow
252
+ }
253
+
254
+ /// Returns true if the balance is zero
255
+ access(all)
256
+ fun isZero(): Bool {
257
+ return self.attoflow == 0
258
+ }
259
+ }
260
+
261
+ /// reports the status of evm execution.
262
+ access(all) enum Status: UInt8 {
263
+ /// is (rarely) returned when status is unknown
264
+ /// and something has gone very wrong.
265
+ access(all) case unknown
266
+
267
+ /// is returned when execution of an evm transaction/call
268
+ /// has failed at the validation step (e.g. nonce mismatch).
269
+ /// An invalid transaction/call is rejected to be executed
270
+ /// or be included in a block.
271
+ access(all) case invalid
272
+
273
+ /// is returned when execution of an evm transaction/call
274
+ /// has been successful but the vm has reported an error as
275
+ /// the outcome of execution (e.g. running out of gas).
276
+ /// A failed tx/call is included in a block.
277
+ /// Note that resubmission of a failed transaction would
278
+ /// result in invalid status in the second attempt, given
279
+ /// the nonce would be come invalid.
280
+ access(all) case failed
281
+
282
+ /// is returned when execution of an evm transaction/call
283
+ /// has been successful and no error is reported by the vm.
284
+ access(all) case successful
285
+ }
286
+
287
+ /// reports the outcome of evm transaction/call execution attempt
288
+ access(all) struct Result {
289
+ /// status of the execution
290
+ access(all)
291
+ let status: Status
292
+
293
+ /// error code (error code zero means no error)
294
+ access(all)
295
+ let errorCode: UInt64
296
+
297
+ /// error message
298
+ access(all)
299
+ let errorMessage: String
300
+
301
+ /// returns the amount of gas metered during
302
+ /// evm execution
303
+ access(all)
304
+ let gasUsed: UInt64
305
+
306
+ /// returns the data that is returned from
307
+ /// the evm for the call. For coa.deploy
308
+ /// calls it returns the code deployed to
309
+ /// the address provided in the contractAddress field.
310
+ /// in case of revert, the smart contract custom error message
311
+ /// is also returned here (see EIP-140 for more details).
312
+ access(all)
313
+ let data: [UInt8]
314
+
315
+ /// returns the newly deployed contract address
316
+ /// if the transaction caused such a deployment
317
+ /// otherwise the value is nil.
318
+ access(all)
319
+ let deployedContract: EVMAddress?
320
+
321
+ init(
322
+ status: Status,
323
+ errorCode: UInt64,
324
+ errorMessage: String,
325
+ gasUsed: UInt64,
326
+ data: [UInt8],
327
+ contractAddress: [UInt8; 20]?
328
+ ) {
329
+ self.status = status
330
+ self.errorCode = errorCode
331
+ self.errorMessage = errorMessage
332
+ self.gasUsed = gasUsed
333
+ self.data = data
334
+
335
+ if let addressBytes = contractAddress {
336
+ self.deployedContract = EVMAddress(bytes: addressBytes)
337
+ } else {
338
+ self.deployedContract = nil
339
+ }
340
+ }
341
+ }
342
+
343
+ access(all)
344
+ resource interface Addressable {
345
+ /// The EVM address
346
+ access(all)
347
+ view fun address(): EVMAddress
348
+ }
349
+
350
+ access(all)
351
+ resource CadenceOwnedAccount: Addressable {
352
+
353
+ access(self)
354
+ var addressBytes: [UInt8; 20]
355
+
356
+ init() {
357
+ // address is initially set to zero
358
+ // but updated through initAddress later
359
+ // we have to do this since we need resource id (uuid)
360
+ // to calculate the EVM address for this cadence owned account
361
+ self.addressBytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
362
+ }
363
+
364
+ access(contract)
365
+ fun initAddress(addressBytes: [UInt8; 20]) {
366
+ // only allow set address for the first time
367
+ // check address is empty
368
+ for item in self.addressBytes {
369
+ assert(item == 0, message: "address byte is not empty")
370
+ }
371
+ self.addressBytes = addressBytes
372
+ }
373
+
374
+ /// The EVM address of the cadence owned account
375
+ access(all)
376
+ view fun address(): EVMAddress {
377
+ // Always create a new EVMAddress instance
378
+ return EVMAddress(bytes: self.addressBytes)
379
+ }
380
+
381
+ /// Get balance of the cadence owned account
382
+ access(all)
383
+ view fun balance(): Balance {
384
+ return self.address().balance()
385
+ }
386
+
387
+ /// Deposits the given vault into the cadence owned account's balance
388
+ access(all)
389
+ fun deposit(from: @FlowToken.Vault) {
390
+ self.address().deposit(from: <-from)
391
+ }
392
+
393
+ /// The EVM address of the cadence owned account behind an entitlement, acting as proof of access
394
+ access(Owner | Validate)
395
+ view fun protectedAddress(): EVMAddress {
396
+ return self.address()
397
+ }
398
+
399
+ /// Withdraws the balance from the cadence owned account's balance
400
+ /// Note that amounts smaller than 10nF (10e-8) can't be withdrawn
401
+ /// given that Flow Token Vaults use UFix64s to store balances.
402
+ /// If the given balance conversion to UFix64 results in
403
+ /// rounding error, this function would fail.
404
+ access(Owner | Withdraw)
405
+ fun withdraw(balance: Balance): @FlowToken.Vault {
406
+ if balance.isZero() {
407
+ panic("calling withdraw function with zero balance is not allowed")
408
+ }
409
+ let vault <- InternalEVM.withdraw(
410
+ from: self.addressBytes,
411
+ amount: balance.attoflow
412
+ ) as! @FlowToken.Vault
413
+ emit FLOWTokensWithdrawn(
414
+ address: self.address().toString(),
415
+ amount: balance.inFLOW(),
416
+ withdrawnUUID: vault.uuid,
417
+ balanceAfterInAttoFlow: self.balance().attoflow
418
+ )
419
+ return <-vault
420
+ }
421
+
422
+ /// Deploys a contract to the EVM environment.
423
+ /// Returns the result which contains address of
424
+ /// the newly deployed contract
425
+ access(Owner | Deploy)
426
+ fun deploy(
427
+ code: [UInt8],
428
+ gasLimit: UInt64,
429
+ value: Balance
430
+ ): Result {
431
+ return InternalEVM.deploy(
432
+ from: self.addressBytes,
433
+ code: code,
434
+ gasLimit: gasLimit,
435
+ value: value.attoflow
436
+ ) as! Result
437
+ }
438
+
439
+ /// Calls a function with the given data.
440
+ /// The execution is limited by the given amount of gas
441
+ access(Owner | Call)
442
+ fun call(
443
+ to: EVMAddress,
444
+ data: [UInt8],
445
+ gasLimit: UInt64,
446
+ value: Balance
447
+ ): Result {
448
+ return InternalEVM.call(
449
+ from: self.addressBytes,
450
+ to: to.bytes,
451
+ data: data,
452
+ gasLimit: gasLimit,
453
+ value: value.attoflow
454
+ ) as! Result
455
+ }
456
+
457
+ /// Bridges the given NFT to the EVM environment, requiring a Provider from which to withdraw a fee to fulfill
458
+ /// the bridge request
459
+ access(all)
460
+ fun depositNFT(
461
+ nft: @{NonFungibleToken.NFT},
462
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
463
+ ) {
464
+ EVM.borrowBridgeAccessor().depositNFT(nft: <-nft, to: self.address(), feeProvider: feeProvider)
465
+ }
466
+
467
+ /// Bridges the given NFT from the EVM environment, requiring a Provider from which to withdraw a fee to fulfill
468
+ /// the bridge request. Note: the caller should own the requested NFT in EVM
469
+ access(Owner | Bridge)
470
+ fun withdrawNFT(
471
+ type: Type,
472
+ id: UInt256,
473
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
474
+ ): @{NonFungibleToken.NFT} {
475
+ return <- EVM.borrowBridgeAccessor().withdrawNFT(
476
+ caller: &self as auth(Call) &CadenceOwnedAccount,
477
+ type: type,
478
+ id: id,
479
+ feeProvider: feeProvider
480
+ )
481
+ }
482
+
483
+ /// Bridges the given Vault to the EVM environment, requiring a Provider from which to withdraw a fee to fulfill
484
+ /// the bridge request
485
+ access(all)
486
+ fun depositTokens(
487
+ vault: @{FungibleToken.Vault},
488
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
489
+ ) {
490
+ EVM.borrowBridgeAccessor().depositTokens(vault: <-vault, to: self.address(), feeProvider: feeProvider)
491
+ }
492
+
493
+ /// Bridges the given fungible tokens from the EVM environment, requiring a Provider from which to withdraw a
494
+ /// fee to fulfill the bridge request. Note: the caller should own the requested tokens & sufficient balance of
495
+ /// requested tokens in EVM
496
+ access(Owner | Bridge)
497
+ fun withdrawTokens(
498
+ type: Type,
499
+ amount: UInt256,
500
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
501
+ ): @{FungibleToken.Vault} {
502
+ return <- EVM.borrowBridgeAccessor().withdrawTokens(
503
+ caller: &self as auth(Call) &CadenceOwnedAccount,
504
+ type: type,
505
+ amount: amount,
506
+ feeProvider: feeProvider
507
+ )
508
+ }
509
+ }
510
+
511
+ /// Creates a new cadence owned account
512
+ access(all)
513
+ fun createCadenceOwnedAccount(): @CadenceOwnedAccount {
514
+ let acc <-create CadenceOwnedAccount()
515
+ let addr = InternalEVM.createCadenceOwnedAccount(uuid: acc.uuid)
516
+ acc.initAddress(addressBytes: addr)
517
+
518
+ emit CadenceOwnedAccountCreated(address: acc.address().toString())
519
+ return <-acc
520
+ }
521
+
522
+ /// Runs an a RLP-encoded EVM transaction, deducts the gas fees,
523
+ /// and deposits the gas fees into the provided coinbase address.
524
+ access(all)
525
+ fun run(tx: [UInt8], coinbase: EVMAddress): Result {
526
+ return InternalEVM.run(
527
+ tx: tx,
528
+ coinbase: coinbase.bytes
529
+ ) as! Result
530
+ }
531
+
532
+ /// mustRun runs the transaction using EVM.run yet it
533
+ /// rollback if the tx execution status is unknown or invalid.
534
+ /// Note that this method does not rollback if transaction
535
+ /// is executed but an vm error is reported as the outcome
536
+ /// of the execution (status: failed).
537
+ access(all)
538
+ fun mustRun(tx: [UInt8], coinbase: EVMAddress): Result {
539
+ let runResult = self.run(tx: tx, coinbase: coinbase)
540
+ assert(
541
+ runResult.status == Status.failed || runResult.status == Status.successful,
542
+ message: "tx is not valid for execution"
543
+ )
544
+ return runResult
545
+ }
546
+
547
+ /// Simulates running unsigned RLP-encoded transaction using
548
+ /// the from address as the signer.
549
+ /// The transaction state changes are not persisted.
550
+ /// This is useful for gas estimation or calling view contract functions.
551
+ access(all)
552
+ fun dryRun(tx: [UInt8], from: EVMAddress): Result {
553
+ return InternalEVM.dryRun(
554
+ tx: tx,
555
+ from: from.bytes,
556
+ ) as! Result
557
+ }
558
+
559
+ /// Runs a batch of RLP-encoded EVM transactions, deducts the gas fees,
560
+ /// and deposits the gas fees into the provided coinbase address.
561
+ /// An invalid transaction is not executed and not included in the block.
562
+ access(all)
563
+ fun batchRun(txs: [[UInt8]], coinbase: EVMAddress): [Result] {
564
+ return InternalEVM.batchRun(
565
+ txs: txs,
566
+ coinbase: coinbase.bytes,
567
+ ) as! [Result]
568
+ }
569
+
570
+ access(all)
571
+ fun encodeABI(_ values: [AnyStruct]): [UInt8] {
572
+ return InternalEVM.encodeABI(values)
573
+ }
574
+
575
+ access(all)
576
+ fun decodeABI(types: [Type], data: [UInt8]): [AnyStruct] {
577
+ return InternalEVM.decodeABI(types: types, data: data)
578
+ }
579
+
580
+ access(all)
581
+ fun encodeABIWithSignature(
582
+ _ signature: String,
583
+ _ values: [AnyStruct]
584
+ ): [UInt8] {
585
+ let methodID = HashAlgorithm.KECCAK_256.hash(
586
+ signature.utf8
587
+ ).slice(from: 0, upTo: 4)
588
+ let arguments = InternalEVM.encodeABI(values)
589
+
590
+ return methodID.concat(arguments)
591
+ }
592
+
593
+ access(all)
594
+ fun decodeABIWithSignature(
595
+ _ signature: String,
596
+ types: [Type],
597
+ data: [UInt8]
598
+ ): [AnyStruct] {
599
+ let methodID = HashAlgorithm.KECCAK_256.hash(
600
+ signature.utf8
601
+ ).slice(from: 0, upTo: 4)
602
+
603
+ for byte in methodID {
604
+ if byte != data.removeFirst() {
605
+ panic("signature mismatch")
606
+ }
607
+ }
608
+
609
+ return InternalEVM.decodeABI(types: types, data: data)
610
+ }
611
+
612
+ /// ValidationResult returns the result of COA ownership proof validation
613
+ access(all)
614
+ struct ValidationResult {
615
+ access(all)
616
+ let isValid: Bool
617
+
618
+ access(all)
619
+ let problem: String?
620
+
621
+ init(isValid: Bool, problem: String?) {
622
+ self.isValid = isValid
623
+ self.problem = problem
624
+ }
625
+ }
626
+
627
+ /// validateCOAOwnershipProof validates a COA ownership proof
628
+ access(all)
629
+ fun validateCOAOwnershipProof(
630
+ address: Address,
631
+ path: PublicPath,
632
+ signedData: [UInt8],
633
+ keyIndices: [UInt64],
634
+ signatures: [[UInt8]],
635
+ evmAddress: [UInt8; 20]
636
+ ): ValidationResult {
637
+
638
+ // make signature set first
639
+ // check number of signatures matches number of key indices
640
+ if keyIndices.length != signatures.length {
641
+ return ValidationResult(
642
+ isValid: false,
643
+ problem: "key indices size doesn't match the signatures"
644
+ )
645
+ }
646
+
647
+ var signatureSet: [Crypto.KeyListSignature] = []
648
+ for signatureIndex, signature in signatures{
649
+ signatureSet.append(Crypto.KeyListSignature(
650
+ keyIndex: Int(keyIndices[signatureIndex]),
651
+ signature: signature
652
+ ))
653
+ }
654
+
655
+ // fetch account
656
+ let acc = getAccount(address)
657
+
658
+ // constructing key list
659
+ let keyList = Crypto.KeyList()
660
+ for signature in signatureSet {
661
+ let keyRef = acc.keys.get(keyIndex: signature.keyIndex)
662
+ if keyRef == nil {
663
+ return ValidationResult(
664
+ isValid: false,
665
+ problem: "invalid key index"
666
+ )
667
+ }
668
+ let key = keyRef!
669
+ if key.isRevoked {
670
+ return ValidationResult(
671
+ isValid: false,
672
+ problem: "account key is revoked"
673
+ )
674
+ }
675
+ keyList.add(
676
+ key.publicKey,
677
+ hashAlgorithm: key.hashAlgorithm,
678
+ weight: key.weight,
679
+ )
680
+ }
681
+
682
+ let isValid = keyList.verify(
683
+ signatureSet: signatureSet,
684
+ signedData: signedData,
685
+ domainSeparationTag: "FLOW-V0.0-user"
686
+ )
687
+
688
+ if !isValid{
689
+ return ValidationResult(
690
+ isValid: false,
691
+ problem: "the given signatures are not valid or provide enough weight"
692
+ )
693
+ }
694
+
695
+ let coaRef = acc.capabilities.borrow<&EVM.CadenceOwnedAccount>(path)
696
+ if coaRef == nil {
697
+ return ValidationResult(
698
+ isValid: false,
699
+ problem: "could not borrow bridge account's resource"
700
+ )
701
+ }
702
+
703
+ // verify evm address matching
704
+ var addr = coaRef!.address()
705
+ for index, item in coaRef!.address().bytes {
706
+ if item != evmAddress[index] {
707
+ return ValidationResult(
708
+ isValid: false,
709
+ problem: "evm address mismatch"
710
+ )
711
+ }
712
+ }
713
+
714
+ return ValidationResult(
715
+ isValid: true,
716
+ problem: nil
717
+ )
718
+ }
719
+
720
+ /// Block returns information about the latest executed block.
721
+ access(all)
722
+ struct EVMBlock {
723
+ access(all)
724
+ let height: UInt64
725
+
726
+ access(all)
727
+ let hash: String
728
+
729
+ access(all)
730
+ let totalSupply: Int
731
+
732
+ access(all)
733
+ let timestamp: UInt64
734
+
735
+ init(height: UInt64, hash: String, totalSupply: Int, timestamp: UInt64) {
736
+ self.height = height
737
+ self.hash = hash
738
+ self.totalSupply = totalSupply
739
+ self.timestamp = timestamp
740
+ }
741
+ }
742
+
743
+ /// Returns the latest executed block.
744
+ access(all)
745
+ fun getLatestBlock(): EVMBlock {
746
+ return InternalEVM.getLatestBlock() as! EVMBlock
747
+ }
748
+
749
+ /// Interface for a resource which acts as an entrypoint to the VM bridge
750
+ access(all)
751
+ resource interface BridgeAccessor {
752
+
753
+ /// Endpoint enabling the bridging of an NFT to EVM
754
+ access(Bridge)
755
+ fun depositNFT(
756
+ nft: @{NonFungibleToken.NFT},
757
+ to: EVMAddress,
758
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
759
+ )
760
+
761
+ /// Endpoint enabling the bridging of an NFT from EVM
762
+ access(Bridge)
763
+ fun withdrawNFT(
764
+ caller: auth(Call) &CadenceOwnedAccount,
765
+ type: Type,
766
+ id: UInt256,
767
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
768
+ ): @{NonFungibleToken.NFT}
769
+
770
+ /// Endpoint enabling the bridging of a fungible token vault to EVM
771
+ access(Bridge)
772
+ fun depositTokens(
773
+ vault: @{FungibleToken.Vault},
774
+ to: EVMAddress,
775
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
776
+ )
777
+
778
+ /// Endpoint enabling the bridging of fungible tokens from EVM
779
+ access(Bridge)
780
+ fun withdrawTokens(
781
+ caller: auth(Call) &CadenceOwnedAccount,
782
+ type: Type,
783
+ amount: UInt256,
784
+ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
785
+ ): @{FungibleToken.Vault}
786
+ }
787
+
788
+ /// Interface which captures a Capability to the bridge Accessor, saving it within the BridgeRouter resource
789
+ access(all)
790
+ resource interface BridgeRouter {
791
+
792
+ /// Returns a reference to the BridgeAccessor designated for internal bridge requests
793
+ access(Bridge) view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor}
794
+
795
+ /// Sets the BridgeAccessor Capability in the BridgeRouter
796
+ access(Bridge) fun setBridgeAccessor(_ accessor: Capability<auth(Bridge) &{BridgeAccessor}>) {
797
+ pre {
798
+ accessor.check(): "Invalid BridgeAccessor Capability provided"
799
+ emit BridgeAccessorUpdated(
800
+ routerType: self.getType(),
801
+ routerUUID: self.uuid,
802
+ routerAddress: self.owner?.address ?? panic("Router must have an owner to be identified"),
803
+ accessorType: accessor.borrow()!.getType(),
804
+ accessorUUID: accessor.borrow()!.uuid,
805
+ accessorAddress: accessor.address
806
+ )
807
+ }
808
+ }
809
+ }
810
+
811
+ /// Returns a reference to the BridgeAccessor designated for internal bridge requests
812
+ access(self)
813
+ view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} {
814
+ return self.account.storage.borrow<auth(Bridge) &{BridgeRouter}>(from: /storage/evmBridgeRouter)
815
+ ?.borrowBridgeAccessor()
816
+ ?? panic("Could not borrow reference to the EVM bridge")
817
+ }
818
+
819
+ /// The Heartbeat resource controls the block production.
820
+ /// It is stored in the storage and used in the Flow protocol to call the heartbeat function once per block.
821
+ access(all)
822
+ resource Heartbeat {
823
+ /// heartbeat calls commit block proposals and forms new blocks including all the
824
+ /// recently executed transactions.
825
+ /// The Flow protocol makes sure to call this function once per block as a system call.
826
+ access(all)
827
+ fun heartbeat() {
828
+ InternalEVM.commitBlockProposal()
829
+ }
830
+ }
831
+
832
+ /// setupHeartbeat creates a heartbeat resource and saves it to storage.
833
+ /// The function is called once during the contract initialization.
834
+ ///
835
+ /// The heartbeat resource is used to control the block production,
836
+ /// and used in the Flow protocol to call the heartbeat function once per block.
837
+ ///
838
+ /// The function can be called by anyone, but only once:
839
+ /// the function will fail if the resource already exists.
840
+ ///
841
+ /// The resulting resource is stored in the account storage,
842
+ /// and is only accessible by the account, not the caller of the function.
843
+ access(all)
844
+ fun setupHeartbeat() {
845
+ self.account.storage.save(<-create Heartbeat(), to: /storage/EVMHeartbeat)
846
+ }
847
+
848
+ init() {
849
+ self.setupHeartbeat()
850
+ }
851
+ }