Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cadence_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 25 additions & 13 deletions cadence/contracts/connectors/evm/ERC4626SinkConnectors.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -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()],
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)"
}
Expand Down
54 changes: 37 additions & 17 deletions cadence/contracts/connectors/evm/UniswapV2SwapConnectors.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -227,19 +227,22 @@ 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 {
UniswapV2SwapConnectors._callError("approve(address,uint256)",
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,
Expand All @@ -250,15 +253,16 @@ access(all) contract UniswapV2SwapConnectors {
],
gasLimit: 1_000_000,
value: 0,
resultTypes: [Type<[UInt256]>()],
dryCall: false
)!
if res.status != EVM.Status.successful {
// revert because the funds have already been deposited to the COA - a no-op would leave the funds in EVM
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(),
Expand Down Expand Up @@ -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 {
Expand All @@ -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")
}
}
Loading
Loading