diff --git a/.github/workflows/cadence_tests.yml b/.github/workflows/cadence_tests.yml index 71410810..e77cc895 100644 --- a/.github/workflows/cadence_tests.yml +++ b/.github/workflows/cadence_tests.yml @@ -23,7 +23,7 @@ jobs: path: ./imports key: flow-deps-${{ hashFiles('flow.json') }} - name: Install Flow CLI - run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v2.13.4 + run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v2.15.3 - name: Flow CLI Version run: flow version - name: Update PATH diff --git a/cadence/contracts/connectors/evm/ERC4626SinkConnectors.cdc b/cadence/contracts/connectors/evm/ERC4626SinkConnectors.cdc index 70c2832a..a599f975 100644 --- a/cadence/contracts/connectors/evm/ERC4626SinkConnectors.cdc +++ b/cadence/contracts/connectors/evm/ERC4626SinkConnectors.cdc @@ -131,19 +131,19 @@ access(all) contract ERC4626SinkConnectors { // approve the ERC4626 vault to spend the assets on deposit let uintAmount = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(amount, erc20Address: self.assetEVMAddress) - let approveRes = self._call( - to: self.assetEVMAddress, - signature: "approve(address,uint256)", - args: [self.vault, uintAmount], - gasLimit: 500_000 - )! + let approveRes = self._callWithSigAndArgs( + to: self.assetEVMAddress, + signature: "approve(address,uint256)", + args: [self.vault, uintAmount], + gasLimit: 500_000 + )! if approveRes.status != EVM.Status.successful { // Cadence panic reverts all EVM state changes in this transaction, so no need to bridge token back. panic(self._approveErrorMessage(ufixAmount: amount, uintAmount: uintAmount, approveRes: approveRes)) } // deposit the assets to the ERC4626 vault - let depositRes = self._call( + let depositRes = self._callWithSigAndArgs( to: self.vault, signature: "deposit(uint256,address)", args: [uintAmount, self.coa.borrow()!.address()], @@ -182,6 +182,7 @@ access(all) contract ERC4626SinkConnectors { access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) { self.uniqueID = id } + /// Performs a call to the ERC4626 vault /// /// @param to The address of the ERC4626 vault @@ -191,14 +192,25 @@ access(all) contract ERC4626SinkConnectors { /// /// @return The result of `nil` if the COA capability is invalid access(self) - fun _call(to: EVM.EVMAddress, signature: String, args: [AnyStruct], gasLimit: UInt64): EVM.Result? { - let calldata = EVM.encodeABIWithSignature(signature, args) - let valueBalance = EVM.Balance(attoflow: 0) + fun _callWithSigAndArgs( + to: EVM.EVMAddress, + signature: String, + args: [AnyStruct], + gasLimit: UInt64, + ): EVM.ResultDecoded? { if let coa = self.coa.borrow() { - return coa.call(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) + return coa.callWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: 0, + resultTypes: nil + ) } return nil } + /// Returns an error message for a failed approve call /// /// @param ufixAmount: the amount of assets to approve @@ -208,7 +220,7 @@ access(all) contract ERC4626SinkConnectors { /// @return an error message for a failed approve call /// access(self) - fun _approveErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, approveRes: EVM.Result): String { + fun _approveErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, approveRes: EVM.ResultDecoded): String { return "Failed to approve ERC4626 vault \(self.vault.toString()) to spend \(ufixAmount) assets \(self.assetEVMAddress.toString()). approvee: \(self.vault.toString()), amount: \(uintAmount). Error code: \(approveRes.errorCode) Error message: \(approveRes.errorMessage)" } /// Returns an error message for a failed deposit call @@ -220,7 +232,7 @@ access(all) contract ERC4626SinkConnectors { /// @return an error message for a failed deposit call /// access(self) - fun _depositErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, depositRes: EVM.Result): String { + fun _depositErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, depositRes: EVM.ResultDecoded): String { let coaHex = self.coa.borrow()!.address().toString() return "Failed to deposit \(ufixAmount) assets \(self.assetEVMAddress.toString()) to ERC4626 vault \(self.vault.toString()). amount: \(uintAmount), to: \(coaHex). Error code: \(depositRes.errorCode) Error message: \(depositRes.errorMessage)" } diff --git a/cadence/contracts/connectors/evm/UniswapV2SwapConnectors.cdc b/cadence/contracts/connectors/evm/UniswapV2SwapConnectors.cdc index a82d5850..0abc6814 100644 --- a/cadence/contracts/connectors/evm/UniswapV2SwapConnectors.cdc +++ b/cadence/contracts/connectors/evm/UniswapV2SwapConnectors.cdc @@ -227,11 +227,13 @@ access(all) contract UniswapV2SwapConnectors { coa.depositTokens(vault: <-exactVaultIn, feeProvider: feeVaultRef) // approve the router to swap tokens - var res = self.call(to: inTokenAddress, + var res = self.callWithSigAndArgs( + to: inTokenAddress, signature: "approve(address,uint256)", args: [self.routerAddress, evmAmountIn], gasLimit: 100_000, value: 0, + resultTypes: nil, dryCall: false )! if res.status != EVM.Status.successful { @@ -239,7 +241,8 @@ access(all) contract UniswapV2SwapConnectors { res, inTokenAddress, idType, id, self.getType()) } // perform the swap - res = self.call(to: self.routerAddress, + res = self.callWithSigAndArgs( + to: self.routerAddress, signature: "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)", // amountIn, amountOutMin, path, to, deadline (timestamp) args: [ evmAmountIn, @@ -250,6 +253,7 @@ access(all) contract UniswapV2SwapConnectors { ], gasLimit: 1_000_000, value: 0, + resultTypes: [Type<[UInt256]>()], dryCall: false )! if res.status != EVM.Status.successful { @@ -257,8 +261,8 @@ access(all) contract UniswapV2SwapConnectors { UniswapV2SwapConnectors._callError("swapExactTokensForTokens(uint256,uint256,address[],address,uint256)", res, self.routerAddress, idType, id, self.getType()) } - let decoded = EVM.decodeABI(types: [Type<[UInt256]>()], data: res.data) - let amountsOut = decoded[0] as! [UInt256] + assert(res.results.length == 1, message: "invalid swap return data") + let amountsOut = res.results[0] as! [UInt256] // withdraw tokens from EVM let outVault <- coa.withdrawTokens(type: self.outType(), @@ -287,18 +291,19 @@ access(all) contract UniswapV2SwapConnectors { /// the values in, otherwise the array contains the values out for each swap along the path /// access(self) fun getAmount(out: Bool, amount: UInt256, path: [EVM.EVMAddress]): UFix64? { - let callRes = self.call(to: self.routerAddress, + let callRes = self.callWithSigAndArgs( + to: self.routerAddress, signature: out ? "getAmountsOut(uint,address[])" : "getAmountsIn(uint,address[])", args: [amount, path], gasLimit: 1_000_000, value: 0, + resultTypes: [Type<[UInt256]>()], dryCall: true ) if callRes == nil || callRes!.status != EVM.Status.successful { return nil } - let decoded = EVM.decodeABI(types: [Type<[UInt256]>()], data: callRes!.data) // can revert if the type cannot be decoded - let uintAmounts: [UInt256] = decoded.length > 0 ? decoded[0] as! [UInt256] : [] + let uintAmounts: [UInt256] = callRes!.results.length > 0 ? callRes!.results[0] as! [UInt256] : [] if uintAmounts.length == 0 { return nil } else if out { @@ -325,29 +330,44 @@ access(all) contract UniswapV2SwapConnectors { /// Makes a call to the Swapper's routerEVMAddress via the contained COA Capability with the provided signature, /// args, and value. If flagged as dryCall, the more efficient and non-mutating COA.dryCall is used. A result is /// returned as long as the COA Capability is valid, otherwise `nil` is returned. - access(self) fun call( + access(self) + fun callWithSigAndArgs( to: EVM.EVMAddress, signature: String, args: [AnyStruct], gasLimit: UInt64, value: UInt, + resultTypes: [Type]?, dryCall: Bool - ): EVM.Result? { - let calldata = EVM.encodeABIWithSignature(signature, args) - let valueBalance = EVM.Balance(attoflow: value) + ): EVM.ResultDecoded? { if let coa = self.borrowCOA() { - let res = dryCall - ? coa.dryCall(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) - : coa.call(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) - return res + if dryCall { + return coa.dryCallWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: value, + resultTypes: resultTypes + ) + } + + return coa.callWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: value, + resultTypes: resultTypes + ) } return nil } } - /// Reverts with a message constructed from the provided args. Used in the event of a coa.call() error + /// Reverts with a message constructed from the provided args. Used in the event of a coa.callWithSigAndArgs() error access(self) - fun _callError(_ signature: String, _ res: EVM.Result,_ target: EVM.EVMAddress, _ uniqueIDType: String, _ id: String, _ swapperType: Type) { + fun _callError(_ signature: String, _ res: EVM.ResultDecoded,_ target: EVM.EVMAddress, _ uniqueIDType: String, _ id: String, _ swapperType: Type) { panic("Call to \(target.toString()).\(signature) from Swapper \(swapperType.identifier) with UniqueIdentifier \(uniqueIDType) ID \(id) failed: \n\tStatus value: \(res.status.rawValue)\n\tError code: \(res.errorCode)\n\tErrorMessage: \(res.errorMessage)\n") } } diff --git a/cadence/contracts/connectors/evm/UniswapV3SwapConnectors.cdc b/cadence/contracts/connectors/evm/UniswapV3SwapConnectors.cdc index 41ca8237..114036b9 100644 --- a/cadence/contracts/connectors/evm/UniswapV3SwapConnectors.cdc +++ b/cadence/contracts/connectors/evm/UniswapV3SwapConnectors.cdc @@ -469,24 +469,19 @@ access(all) contract UniswapV3SwapConnectors { : self.feePath[hopIndex] let res = self._dryCall( - self.factoryAddress, - "getPool(address,address,uint24)", - [ tokenA, tokenB, UInt256(fee) ], - 120_000 + to: self.factoryAddress, + signature: "getPool(address,address,uint24)", + args: [tokenA, tokenB, UInt256(fee)], + gasLimit: 120_000, + resultTypes: [Type()] )! assert( res.status == EVM.Status.successful, message: "unable to get pool: tokenA \(tokenA.toString()), tokenB \(tokenB.toString()), fee: \(fee)" ) - // ABI return is one 32-byte word; the last 20 bytes are the address - let word = res.data - if word.length < 32 { panic("getPool: invalid ABI word length") } - - let addrSlice = word.slice(from: 12, upTo: 32) // 20 bytes - let addrBytes = addrSlice.toConstantSized<[UInt8; 20]>()! - - return EVM.EVMAddress(bytes: addrBytes) + assert(res.results.length == 1, message: "getPool: invalid ABI-encoded return data") + return res.results[0] as! EVM.EVMAddress } /// Get max input amount for a specific hop @@ -506,50 +501,32 @@ access(all) contract UniswapV3SwapConnectors { access(self) fun getMaxInAmount(hopIndex: Int, zeroForOne: Bool, reverse: Bool): UInt256 { let poolEVMAddress = self.getPoolAddress(hopIndex: hopIndex, reverse: reverse) - // Helper functions - fun wordToUInt(_ w: [UInt8]): UInt { - var acc: UInt = 0 - var i = 0 - while i < 32 { acc = (acc << 8) | UInt(w[i]); i = i + 1 } - return acc - } - fun wordToUIntN(_ w: [UInt8], _ nBits: UInt): UInt { - let full = wordToUInt(w) - if nBits >= 256 { return full } - let mask: UInt = (1 << nBits) - 1 - return full & mask - } - fun words(_ data: [UInt8]): [[UInt8]] { - let n = data.length / 32 - var out: [[UInt8]] = [] - var i = 0 - while i < n { - out.append(data.slice(from: i*32, upTo: (i+1)*32)) - i = i + 1 - } - return out - } - - // Selectors - let SEL_SLOT0: [UInt8] = [0x38, 0x50, 0xc7, 0xbd] - let SEL_LIQUIDITY: [UInt8] = [0x1a, 0x68, 0x65, 0x02] - // Get slot0 (sqrtPriceX96, tick, etc.) - let s0Res = self._dryCallRaw( + // slot0() returns (uint160 sqrtPriceX96, int24, uint16, uint16, uint16, uint8, bool) + let types = [ + Type(), Type(), Type(), Type(), Type(), Type(), Type() + ] + let s0Res = self._dryCall( to: poolEVMAddress, - calldata: EVMAbiHelpers.buildCalldata(selector: SEL_SLOT0, args: []), + signature: "slot0()", + args: [], gasLimit: 1_000_000, + resultTypes: types ) - let s0w = words(s0Res!.data) - let sqrtPriceX96 = wordToUIntN(s0w[0], 160) + assert(s0Res!.results.length == types.length, message: "slot0: invalid ABI-encoded return data") + let sqrtPriceX96 = s0Res!.results[0] as! UInt // Get current active liquidity - let liqRes = self._dryCallRaw( + // liquidity() returns (uint128 liquidity) + let liqRes = self._dryCall( to: poolEVMAddress, - calldata: EVMAbiHelpers.buildCalldata(selector: SEL_LIQUIDITY, args: []), + signature: "liquidity()", + args: [], gasLimit: 300_000, + resultTypes: [Type()] ) - let L = wordToUIntN(words(liqRes!.data)[0], 128) + assert(liqRes!.results.length == 1, message: "liquidity: invalid ABI-encoded return data") + let L = liqRes!.results[0] as! UInt128 // Calculate price multiplier based on 6% price impact (600 bps) // Use UInt256 throughout to prevent overflow in multiplication operations @@ -626,13 +603,18 @@ access(all) contract UniswapV3SwapConnectors { let args = [pathBytes, amount] - let res = self._dryCall(self.quoterAddress, callSig, args, 10_000_000) + let res = self._dryCall( + to: self.quoterAddress, + signature: callSig, + args: args, + gasLimit: 10_000_000, + resultTypes: [Type()] + ) if res == nil || res!.status != EVM.Status.successful { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: res!.data) - if decoded.length == 0 { return nil } + if res!.results.length == 0 { return nil } - return decoded[0] as! UInt256 + return res!.results[0] as! UInt256 } /// Executes exact input swap via router @@ -660,12 +642,13 @@ access(all) contract UniswapV3SwapConnectors { let pathBytes = self._buildPathBytes(reverse: reverse, exactOutput: false, numHops: nil) // Approve - let allowanceRes = self._call( + let allowanceRes = self._callWithSigAndArgs( to: inToken, signature: "approve(address,uint256)", args: [self.routerAddress, evmAmountIn], gasLimit: 120_000, - value: 0 + value: 0, + resultTypes: nil )! if allowanceRes.status != EVM.Status.successful { UniswapV3SwapConnectors._callError("approve(address,uint256)", allowanceRes, inToken, idType, id, self.getType()) @@ -693,39 +676,37 @@ access(all) contract UniswapV3SwapConnectors { amountOutMinimum: minOutUint ) - let calldata = EVM.encodeABIWithSignature( - "exactInput((bytes,address,uint256,uint256))", - [exactInputParams] - ) - // Call the router with raw calldata - let swapRes = self._callRaw( + let swapRes = self._callWithSigAndArgs( to: self.routerAddress, - calldata: calldata, + signature: "exactInput((bytes,address,uint256,uint256))", + args: [exactInputParams], gasLimit: 10_000_000, - value: 0 + value: 0, + resultTypes: [Type()] )! if swapRes.status != EVM.Status.successful { UniswapV3SwapConnectors._callError( - EVMAbiHelpers.toHex(calldata), + "exactInput((bytes,address,uint256,uint256))", swapRes, self.routerAddress, idType, id, self.getType() ) } + // Reset allowance - let resetAllowanceRes = self._call( + let resetAllowanceRes = self._callWithSigAndArgs( to: inToken, signature: "approve(address,uint256)", args: [self.routerAddress, 0 as UInt256], gasLimit: 60_000, - value: 0 + value: 0, + resultTypes: nil )! - if resetAllowanceRes.status != EVM.Status.successful { UniswapV3SwapConnectors._callError("approve(address,uint256)", resetAllowanceRes, inToken, idType, id, self.getType()) } - let decoded = EVM.decodeABI(types: [Type()], data: swapRes.data) - assert(decoded.length == 1, message: "invalid swap return data") - let amountOut = decoded[0] as! UInt256 + + assert(swapRes.results.length == 1, message: "invalid swap return data") + let amountOut = swapRes.results[0] as! UInt256 let outVaultType = reverse ? self.inType() : self.outType() let outTokenEVMAddress = @@ -761,36 +742,45 @@ access(all) contract UniswapV3SwapConnectors { access(self) view fun borrowCOA(): auth(EVM.Owner) &EVM.CadenceOwnedAccount? { return self.coaCapability.borrow() } - access(self) fun _dryCall(_ to: EVM.EVMAddress, _ signature: String, _ args: [AnyStruct], _ gas: UInt64): EVM.Result? { - let calldata = EVM.encodeABIWithSignature(signature, args) - let valueBalance = EVM.Balance(attoflow: 0) + access(self) + fun _dryCall( + to: EVM.EVMAddress, + signature: String, + args: [AnyStruct], + gasLimit: UInt64, + resultTypes: [Type]? + ): EVM.ResultDecoded? { if let coa = self.borrowCOA() { - return coa.dryCall(to: to, data: calldata, gasLimit: gas, value: valueBalance) - } - return nil - } - - access(self) fun _dryCallRaw(to: EVM.EVMAddress, calldata: [UInt8], gasLimit: UInt64): EVM.Result? { - let valueBalance = EVM.Balance(attoflow: 0) - if let coa = self.borrowCOA() { - return coa.dryCall(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) - } - return nil - } - - access(self) fun _call(to: EVM.EVMAddress, signature: String, args: [AnyStruct], gasLimit: UInt64, value: UInt): EVM.Result? { - let calldata = EVM.encodeABIWithSignature(signature, args) - let valueBalance = EVM.Balance(attoflow: value) - if let coa = self.borrowCOA() { - return coa.call(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) + return coa.dryCallWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: 0, + resultTypes: resultTypes + ) } return nil } - access(self) fun _callRaw(to: EVM.EVMAddress, calldata: [UInt8], gasLimit: UInt64, value: UInt): EVM.Result? { - let valueBalance = EVM.Balance(attoflow: value) + access(self) + fun _callWithSigAndArgs( + to: EVM.EVMAddress, + signature: String, + args: [AnyStruct], + gasLimit: UInt64, + value: UInt, + resultTypes: [Type]? + ): EVM.ResultDecoded? { if let coa = self.borrowCOA() { - return coa.call(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) + return coa.callWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: value, + resultTypes: resultTypes + ) } return nil } @@ -815,21 +805,17 @@ access(all) contract UniswapV3SwapConnectors { } access(self) fun getPoolToken0(_ pool: EVM.EVMAddress): EVM.EVMAddress { - // token0() selector = 0x0dfe1681 - let SEL_TOKEN0: [UInt8] = [0x0d, 0xfe, 0x16, 0x81] - let res = self._dryCallRaw( + let res = self._dryCall( to: pool, - calldata: EVMAbiHelpers.buildCalldata(selector: SEL_TOKEN0, args: []), + signature: "token0()", + args: [], gasLimit: 150_000, + resultTypes: [Type()] )! assert(res.status == EVM.Status.successful, message: "token0() call failed") - let word = res.data - if word.length < 32 { panic("getPoolToken0: invalid ABI word length") } - - let addrSlice = word.slice(from: 12, upTo: 32) - let addrBytes = addrSlice.toConstantSized<[UInt8; 20]>()! - return EVM.EVMAddress(bytes: addrBytes) + assert(res.results.length == 1, message: "token0: invalid ABI-encoded return data") + return res.results[0] as! EVM.EVMAddress } access(self) fun isZeroForOne(hopIndex: Int, reverse: Bool): Bool { @@ -849,7 +835,7 @@ access(all) contract UniswapV3SwapConnectors { access(self) fun _callError( _ signature: String, - _ res: EVM.Result, + _ res: EVM.ResultDecoded, _ target: EVM.EVMAddress, _ uniqueIDType: String, _ id: String, diff --git a/cadence/contracts/connectors/evm/morpho/MorphoERC4626SinkConnectors.cdc b/cadence/contracts/connectors/evm/morpho/MorphoERC4626SinkConnectors.cdc index 8b2bd881..184fda1b 100644 --- a/cadence/contracts/connectors/evm/morpho/MorphoERC4626SinkConnectors.cdc +++ b/cadence/contracts/connectors/evm/morpho/MorphoERC4626SinkConnectors.cdc @@ -114,20 +114,20 @@ access(all) contract MorphoERC4626SinkConnectors { // approve the ERC4626 vault to spend the assets on deposit let uintAmount = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(amount, erc20Address: self.assetEVMAddress) - let approveRes = self._call( - dry: false, - to: self.assetEVMAddress, - signature: "approve(address,uint256)", - args: [self.vaultEVMAddress, uintAmount], - gasLimit: 500_000 - )! + let approveRes = self._callWithSigAndArgs( + dry: false, + to: self.assetEVMAddress, + signature: "approve(address,uint256)", + args: [self.vaultEVMAddress, uintAmount], + gasLimit: 500_000 + )! if approveRes.status != EVM.Status.successful { // TODO: consider more graceful handling of this error panic(self._approveErrorMessage(ufixAmount: amount, uintAmount: uintAmount, approveRes: approveRes)) } // deposit the assets to the ERC4626 vault - let depositRes = self._call( + let depositRes = self._callWithSigAndArgs( dry: false, to: self.vaultEVMAddress, signature: "deposit(uint256,address)", @@ -165,25 +165,48 @@ access(all) contract MorphoERC4626SinkConnectors { access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) { self.uniqueID = id } - /// Performs a dry call to the ERC4626 vault + + /// Performs a call to the ERC4626 vault /// + /// @param dry Whether to commit any state changes or not /// @param to The address of the ERC4626 vault /// @param signature The signature of the function to call /// @param args The arguments to pass to the function /// @param gasLimit The gas limit to use for the call /// - /// @return The result of the dry call or `nil` if the COA capability is invalid + /// @return The result of the call or `nil` if the COA capability is invalid access(self) - fun _call(dry: Bool, to: EVM.EVMAddress, signature: String, args: [AnyStruct], gasLimit: UInt64): EVM.Result? { - let calldata = EVM.encodeABIWithSignature(signature, args) - let valueBalance = EVM.Balance(attoflow: 0) + fun _callWithSigAndArgs( + dry: Bool, + to: EVM.EVMAddress, + signature: String, + args: [AnyStruct], + gasLimit: UInt64, + ): EVM.ResultDecoded? { if let coa = self.coa.borrow() { - return dry - ? coa.dryCall(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) - : coa.call(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) + if dry { + return coa.dryCallWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: 0, + resultTypes: nil + ) + } + + return coa.callWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: 0, + resultTypes: nil + ) } return nil } + /// Returns an error message for a failed approve call /// /// @param ufixAmount: the amount of assets to approve @@ -193,7 +216,7 @@ access(all) contract MorphoERC4626SinkConnectors { /// @return an error message for a failed approve call /// access(self) - fun _approveErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, approveRes: EVM.Result): String { + fun _approveErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, approveRes: EVM.ResultDecoded): String { return "Failed to approve ERC4626 vault \(self.vaultEVMAddress.toString()) to spend \(ufixAmount) assets \(self.assetEVMAddress.toString()), approvee: \(self.vaultEVMAddress.toString()), amount: \(uintAmount). Error code: \(approveRes.errorCode) Error message: \(approveRes.errorMessage)" } /// Returns an error message for a failed deposit call @@ -205,7 +228,7 @@ access(all) contract MorphoERC4626SinkConnectors { /// @return an error message for a failed deposit call /// access(self) - fun _depositErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, depositRes: EVM.Result): String { + fun _depositErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, depositRes: EVM.ResultDecoded): String { let coaHex = self.coa.borrow()!.address().toString() return "Failed to deposit \(ufixAmount) assets \(self.assetEVMAddress.toString()) to ERC4626 vault \(self.vaultEVMAddress.toString()), amount: \(uintAmount), to: \(coaHex). Error code: \(depositRes.errorCode) Error message: \(depositRes.errorMessage)" } @@ -314,7 +337,7 @@ access(all) contract MorphoERC4626SinkConnectors { let coa = self.coa.borrow() ?? panic("can't borrow COA") // redeem the shares from the ERC4626 vault - let redeemRes = self._call( + let redeemRes = self._callWithSigAndArgs( dry: false, to: self.vaultEVMAddress, signature: "redeem(uint256,address,address)", @@ -354,25 +377,48 @@ access(all) contract MorphoERC4626SinkConnectors { access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) { self.uniqueID = id } - /// Performs a dry call to the ERC4626 vault + + /// Performs a call to the ERC4626 vault /// + /// @param dry Whether to commit any state changes or not /// @param to The address of the ERC4626 vault /// @param signature The signature of the function to call /// @param args The arguments to pass to the function /// @param gasLimit The gas limit to use for the call /// - /// @return The result of the dry call or `nil` if the COA capability is invalid + /// @return The result of the call or `nil` if the COA capability is invalid access(self) - fun _call(dry: Bool, to: EVM.EVMAddress, signature: String, args: [AnyStruct], gasLimit: UInt64): EVM.Result? { - let calldata = EVM.encodeABIWithSignature(signature, args) - let valueBalance = EVM.Balance(attoflow: 0) + fun _callWithSigAndArgs( + dry: Bool, + to: EVM.EVMAddress, + signature: String, + args: [AnyStruct], + gasLimit: UInt64, + ): EVM.ResultDecoded? { if let coa = self.coa.borrow() { - return dry - ? coa.dryCall(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) - : coa.call(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance) + if dry { + return coa.dryCallWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: 0, + resultTypes: nil + ) + } + + return coa.callWithSigAndArgs( + to: to, + signature: signature, + args: args, + gasLimit: gasLimit, + value: 0, + resultTypes: nil + ) } return nil } + /// Returns an error message for a failed redeem call /// /// @param ufixShares: the amount of shares to redeem @@ -382,7 +428,7 @@ access(all) contract MorphoERC4626SinkConnectors { /// @return an error message for a failed redeem call /// access(self) - fun _redeemErrorMessage(ufixShares: UFix64, uintShares: UInt256, redeemRes: EVM.Result): String { + fun _redeemErrorMessage(ufixShares: UFix64, uintShares: UInt256, redeemRes: EVM.ResultDecoded): String { let coaHex = self.coa.borrow()!.address().toString() return "Failed to redeem \(ufixShares) shares \(self.vaultEVMAddress.toString()) from ERC4626 vault for \(self.assetEVMAddress.toString()), amount: \(uintShares), to: \(coaHex). Error code: \(redeemRes.errorCode) Error message: \(redeemRes.errorMessage)" } diff --git a/cadence/contracts/utils/ERC4626Utils.cdc b/cadence/contracts/utils/ERC4626Utils.cdc index 8128bcab..fea98959 100644 --- a/cadence/contracts/utils/ERC4626Utils.cdc +++ b/cadence/contracts/utils/ERC4626Utils.cdc @@ -42,12 +42,17 @@ access(all) contract ERC4626Utils { /// @return The EVM address of the underlying asset for the given ERC4626 vault access(all) fun underlyingAssetEVMAddress(vault: EVM.EVMAddress): EVM.EVMAddress? { - let callRes = self._dryCall(to: vault, signature: "asset()", args: [], gasLimit: 5_000_000) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + let callRes = self._dryCall( + to: vault, + signature: "asset()", + args: [], + gasLimit: 5_000_000, + resultTypes: [Type()] + ) + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: callRes.data) - return decoded[0] as! EVM.EVMAddress + return callRes.results[0] as! EVM.EVMAddress } /// Returns the total assets managed by the ERC4626 vault @@ -57,12 +62,17 @@ access(all) contract ERC4626Utils { /// @return The total assets managed by the ERC4626 vault. Callers should anticipate the address of the asset and /// the decimals of the asset being returned. access(all) fun totalAssets(vault: EVM.EVMAddress): UInt256? { - let callRes = self._dryCall(to: vault, signature: "totalAssets()", args: [], gasLimit: 5_000_000) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + let callRes = self._dryCall( + to: vault, + signature: "totalAssets()", + args: [], + gasLimit: 5_000_000, + resultTypes: [Type()] + ) + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let totalAssets = EVM.decodeABI(types: [Type()], data: callRes.data) - return totalAssets[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the total shares issued by the ERC4626 vault @@ -72,12 +82,17 @@ access(all) contract ERC4626Utils { /// @return The total shares issued by the ERC4626 vault. Callers should anticipate the address of the asset and /// the decimals of the asset being returned. access(all) fun totalShares(vault: EVM.EVMAddress): UInt256? { - let callRes = self._dryCall(to: vault, signature: "totalSupply()", args: [], gasLimit: 5_000_000) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + let callRes = self._dryCall( + to: vault, + signature: "totalSupply()", + args: [], + gasLimit: 5_000_000, + resultTypes: [Type()] + ) + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let totalAssets = EVM.decodeABI(types: [Type()], data: callRes.data) - return totalAssets[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the maximum amount of shares that can be redeemed from the given owner's balance in the ERC4626 vault @@ -89,12 +104,17 @@ access(all) contract ERC4626Utils { /// Callers should anticipate the address of the shares and the decimals of the shares being returned. access(all) fun maxRedeem(vault: EVM.EVMAddress, owner: EVM.EVMAddress): UInt256? { - let callRes = self._dryCall(to: vault, signature: "maxRedeem(address)", args: [owner], gasLimit: 5_000_000) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + let callRes = self._dryCall( + to: vault, + signature: "maxRedeem(address)", + args: [owner], + gasLimit: 5_000_000, + resultTypes: [Type()] + ) + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let maxRedeem = EVM.decodeABI(types: [Type()], data: callRes.data) - return maxRedeem[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the maximum amount of assets that can be deposited into the ERC4626 vault @@ -106,12 +126,17 @@ access(all) contract ERC4626Utils { /// the asset's decimals. access(all) fun maxDeposit(vault: EVM.EVMAddress, receiver: EVM.EVMAddress): UInt256? { - let callRes = self._dryCall(to: vault, signature: "maxDeposit(address)", args: [receiver], gasLimit: 5_000_000) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + let callRes = self._dryCall( + to: vault, + signature: "maxDeposit(address)", + args: [receiver], + gasLimit: 5_000_000, + resultTypes: [Type()] + ) + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let maxDeposit = EVM.decodeABI(types: [Type()], data: callRes.data) - return maxDeposit[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the amount of assets that would be required to mint the given amount of shares @@ -128,13 +153,13 @@ access(all) contract ERC4626Utils { to: vault, signature: "previewMint(uint256)", args: [shares], - gasLimit: 5_000_000 + gasLimit: 5_000_000, + resultTypes: [Type()] ) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: callRes.data) - return decoded[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the amount of shares that would be minted for depositing the given amount of assets @@ -151,13 +176,13 @@ access(all) contract ERC4626Utils { to: vault, signature: "previewDeposit(uint256)", args: [assets], - gasLimit: 5_000_000 + gasLimit: 5_000_000, + resultTypes: [Type()] ) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: callRes.data) - return decoded[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the amount of underlying assets that would be redeemed for the given amount of shares @@ -174,13 +199,13 @@ access(all) contract ERC4626Utils { to: vault, signature: "previewRedeem(uint256)", args: [shares], - gasLimit: 5_000_000 + gasLimit: 5_000_000, + resultTypes: [Type()] ) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: callRes.data) - return decoded[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the amount of shares that would be required to withdraw the given amount of assets @@ -197,13 +222,13 @@ access(all) contract ERC4626Utils { to: vault, signature: "previewWithdraw(uint256)", args: [assets], - gasLimit: 5_000_000 + gasLimit: 5_000_000, + resultTypes: [Type()] ) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: callRes.data) - return decoded[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the amount of underlying assets that the given amount of shares would be worth, @@ -221,13 +246,13 @@ access(all) contract ERC4626Utils { to: vault, signature: "convertToAssets(uint256)", args: [shares], - gasLimit: 5_000_000 + gasLimit: 5_000_000, + resultTypes: [Type()] ) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: callRes.data) - return decoded[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Returns the amount of shares that the given amount of underlying assets would be worth, @@ -245,13 +270,13 @@ access(all) contract ERC4626Utils { to: vault, signature: "convertToShares(uint256)", args: [assets], - gasLimit: 5_000_000 + gasLimit: 5_000_000, + resultTypes: [Type()] ) - if callRes.status != EVM.Status.successful || callRes.data.length == 0 { + if callRes.status != EVM.Status.successful || callRes.results.length == 0 { return nil } - let decoded = EVM.decodeABI(types: [Type()], data: callRes.data) - return decoded[0] as! UInt256 + return callRes.results[0] as! UInt256 } /// Performs a dry call using the calling COA @@ -260,14 +285,24 @@ access(all) contract ERC4626Utils { /// @param signature The signature of the function to dry call /// @param args The arguments to pass to the function /// @param gasLimit The gas limit for the dry call + /// @param resultTypes The Cadence types into which to ABI decode the dry call result data /// /// @return The result of the dry call - access(self) fun _dryCall(to: EVM.EVMAddress, signature: String, args: [AnyStruct], gasLimit: UInt64): EVM.Result { - return self.callingCOA.dryCall( + access(self) + fun _dryCall( + to: EVM.EVMAddress, + signature: String, + args: [AnyStruct], + gasLimit: UInt64, + resultTypes: [Type]? + ): EVM.ResultDecoded { + return self.callingCOA.dryCallWithSigAndArgs( to: to, - data: EVM.encodeABIWithSignature(signature, args), + signature: signature, + args: args, gasLimit: gasLimit, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: resultTypes ) } diff --git a/cadence/tests/transactions/bridge/setup/add_deployer.cdc b/cadence/tests/transactions/bridge/setup/add_deployer.cdc index a4be41f0..91c118ff 100644 --- a/cadence/tests/transactions/bridge/setup/add_deployer.cdc +++ b/cadence/tests/transactions/bridge/setup/add_deployer.cdc @@ -23,35 +23,29 @@ transaction(deployerTag: String, deployerEVMAddressHex: String) { execute { // Execute the call - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "addDeployer(string,address)", - [deployerTag, self.targetDeployerEVMAddress] - ), + signature: "addDeployer(string,address)", + args: [deployerTag, self.targetDeployerEVMAddress], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to add deployer") // Confirm the deployer was added under the tag - let postDeployerResult = self.coa.call( + let postDeployerResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "getDeployer(string)", - [deployerTag] - ), + signature: "getDeployer(string)", + args: [deployerTag], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert(postDeployerResult.status == EVM.Status.successful, message: "Failed to get deployer") - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postDeployerResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from getDeployer call") - self.postDeployer = decodedResult[0] as! EVM.EVMAddress + assert(postDeployerResult.results.length == 1, message: "Invalid response from getDeployer call") + self.postDeployer = postDeployerResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/tests/transactions/bridge/setup/set_delegated_deployer.cdc b/cadence/tests/transactions/bridge/setup/set_delegated_deployer.cdc index 5064168a..af52c69f 100644 --- a/cadence/tests/transactions/bridge/setup/set_delegated_deployer.cdc +++ b/cadence/tests/transactions/bridge/setup/set_delegated_deployer.cdc @@ -22,32 +22,29 @@ transaction(deployerEVMAddressHex: String) { execute { // Execute the call - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: self.targetDeployerEVMAddress, - data: EVM.encodeABIWithSignature( - "setDelegatedDeployer(address)", - [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()] - ), + signature: "setDelegatedDeployer(address)", + args: [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to set delegated deployer") // Confirm the delegated deployer was set - let postDelegatedDeployerResult = self.coa.call( + let postDelegatedDeployerResult = self.coa.callWithSigAndArgs( to: self.targetDeployerEVMAddress, - data: EVM.encodeABIWithSignature("delegatedDeployer()", []), + signature: "delegatedDeployer()", + args: [], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert(postDelegatedDeployerResult.status == EVM.Status.successful, message: "Failed to get delegated deployer") - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postDelegatedDeployerResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from delegatedDeployer() call") - self.postDelegatedDeployer = decodedResult[0] as! EVM.EVMAddress + assert(postDelegatedDeployerResult.results.length == 1, message: "Invalid response from delegatedDeployer() call") + self.postDelegatedDeployer = postDelegatedDeployerResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/tests/transactions/bridge/setup/set_deployment_registry.cdc b/cadence/tests/transactions/bridge/setup/set_deployment_registry.cdc index 68bd94aa..4197f382 100644 --- a/cadence/tests/transactions/bridge/setup/set_deployment_registry.cdc +++ b/cadence/tests/transactions/bridge/setup/set_deployment_registry.cdc @@ -26,35 +26,32 @@ transaction(registryEVMAddressHex: String) { execute { // Execute call - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature( - "setDeploymentRegistry(address)", - [self.targetRegistryEVMAddress] - ), + signature: "setDeploymentRegistry(address)", + args: [self.targetRegistryEVMAddress], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to set registry in FlowBridgeFactory contract") // Confirm the registry address was set - let postRegistryResult = self.coa.call( + let postRegistryResult = self.coa.callWithSigAndArgs( to: FlowEVMBridgeUtils.getBridgeFactoryEVMAddress(), - data: EVM.encodeABIWithSignature("getRegistry()", []), + signature: "getRegistry()", + args: [], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert( postRegistryResult.status == EVM.Status.successful, message: "Failed to get registry address from FlowBridgeFactory contract" ) - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postRegistryResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from getRegistry() call to FlowBridgeFactory contract") - self.postRegistry = decodedResult[0] as! EVM.EVMAddress + assert(postRegistryResult.results.length == 1, message: "Invalid response from getRegistry() call to FlowBridgeFactory contract") + self.postRegistry = postRegistryResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/tests/transactions/bridge/setup/set_registrar.cdc b/cadence/tests/transactions/bridge/setup/set_registrar.cdc index 4b8c2e33..aef96926 100644 --- a/cadence/tests/transactions/bridge/setup/set_registrar.cdc +++ b/cadence/tests/transactions/bridge/setup/set_registrar.cdc @@ -21,32 +21,29 @@ transaction(registryEVMAddressHex: String) { } execute { - let callResult = self.coa.call( + let callResult = self.coa.callWithSigAndArgs( to: self.targetRegistryEVMAddress, - data: EVM.encodeABIWithSignature( - "setRegistrar(address)", - [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()] - ), + signature: "setRegistrar(address)", + args: [FlowEVMBridgeUtils.getBridgeFactoryEVMAddress()], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callResult.status == EVM.Status.successful, message: "Failed to set registrar") // Confirm the registrar was set - let postRegistrarResult = self.coa.call( + let postRegistrarResult = self.coa.callWithSigAndArgs( to: self.targetRegistryEVMAddress, - data: EVM.encodeABIWithSignature("registrar()", []), + signature: "registrar()", + args: [], gasLimit: 15_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: [Type()] ) assert(postRegistrarResult.status == EVM.Status.successful, message: "Failed to get registrar") - let decodedResult = EVM.decodeABI( - types: [Type()], - data: postRegistrarResult.data - ) - assert(decodedResult.length == 1, message: "Invalid response from registrar() call to registry contract") - self.postRegistrar = decodedResult[0] as! EVM.EVMAddress + assert(postRegistrarResult.results.length == 1, message: "Invalid response from registrar() call to registry contract") + self.postRegistrar = postRegistrarResult.results[0] as! EVM.EVMAddress } post { diff --git a/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_dust_test.cdc b/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_dust_test.cdc index 6f5cc694..e5288e96 100644 --- a/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_dust_test.cdc +++ b/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_dust_test.cdc @@ -65,24 +65,25 @@ transaction( coa.depositTokens(vault: <-flowVault, feeProvider: feeRef) // Approve V2 Router for WFLOW - var callRes = coa.call( + var callRes = coa.callWithSigAndArgs( to: wflow, - data: EVM.encodeABIWithSignature("approve(address,uint256)", [v2Router, wflowAmount]), + signature: "approve(address,uint256)", + args: [v2Router, wflowAmount], gasLimit: 100_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callRes.status == EVM.Status.successful, message: "WFLOW approve for V2 failed") // Swap WFLOW -> tokenIn via V2 swapExactTokensForTokens let v2Path = [wflow, tokenIn] - callRes = coa.call( + callRes = coa.callWithSigAndArgs( to: v2Router, - data: EVM.encodeABIWithSignature( - "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)", - [wflowAmount, 0, v2Path, coaAddr, 99999999999] - ), + signature: "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)", + args: [wflowAmount, 0, v2Path, coaAddr, 99999999999], gasLimit: 1_000_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(callRes.status == EVM.Status.successful, message: "V2 provision swap failed: \(callRes.errorMessage)") diff --git a/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_overshoot_test.cdc b/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_overshoot_test.cdc index b9119b3d..14cf7929 100644 --- a/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_overshoot_test.cdc +++ b/cadence/tests/transactions/uniswap-v3-swap-connectors/uniswap_v3_swap_overshoot_test.cdc @@ -67,11 +67,13 @@ transaction( ) log("Transferring \(provisionAmount.toString()) tokens from holder") - let provisionRes = coa.call( + let provisionRes = coa.callWithSigAndArgs( to: tokenIn, - data: EVM.encodeABIWithSignature("transfer(address,uint256)", [coaAddr, provisionAmountEVM]), + signature: "transfer(address,uint256)", + args: [coaAddr, provisionAmountEVM], gasLimit: 500_000, - value: EVM.Balance(attoflow: 0) + value: 0, + resultTypes: nil ) assert(provisionRes.status == EVM.Status.successful,