github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/stdlib/contract.cdc (about)

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