Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@
test:
flow test --cover --covercode="contracts" --coverprofile="coverage.lcov" ./cadence/tests/*_test.cdc

.PHONY: lint
lint:
find cadence -name "*.cdc" | xargs flow cadence lint \
| tee /dev/stderr | tail -n2 | grep -q "Lint passed"

.PHONY: ci
ci: test
ci: lint test
6 changes: 2 additions & 4 deletions cadence/contracts/connectors/SwapConnectors.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ access(all) contract SwapConnectors {
init(swapper: {DeFiActions.Swapper}, sink: {DeFiActions.Sink}, uniqueID: DeFiActions.UniqueIdentifier?) {
pre {
swapper.outType() == sink.getSinkType():
"Swapper outputs \(swapper.outType().identifier) but Sink takes \(sink.getSinkType().identifier) - "
.concat("Ensure the provided Swapper outputs a Vault Type compatible with the provided Sink")
"Swapper outputs \(swapper.outType().identifier) but Sink takes \(sink.getSinkType().identifier) - Ensure the provided Swapper outputs a Vault Type compatible with the provided Sink"
}
self.swapper = swapper
self.sink = sink
Expand Down Expand Up @@ -539,8 +538,7 @@ access(all) contract SwapConnectors {
init(swapper: {DeFiActions.Swapper}, source: {DeFiActions.Source}, uniqueID: DeFiActions.UniqueIdentifier?) {
pre {
source.getSourceType() == swapper.inType():
"Source outputs \(source.getSourceType().identifier) but Swapper takes \(swapper.inType().identifier) - "
.concat("Ensure the provided Source outputs a Vault Type compatible with the provided Swapper")
"Source outputs \(source.getSourceType().identifier) but Swapper takes \(swapper.inType().identifier) - Ensure the provided Source outputs a Vault Type compatible with the provided Swapper"
}
self.swapper = swapper
self.source = source
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,11 @@ access(all) contract BandOracleConnectors {

// check price data has not gone stale based on last updated timestamp
let now = UInt64(getCurrentBlock().timestamp)
if self.staleThreshold != nil {
assert(now < priceData.baseTimestamp + self.staleThreshold!,
message: "Price data's base timestamp \(priceData.baseTimestamp) exceeds the staleThreshold "
.concat("\(priceData.baseTimestamp + self.staleThreshold!) at current timestamp \(now)"))
assert(now < priceData.quoteTimestamp + self.staleThreshold!,
message: "Price data's quote timestamp \(priceData.quoteTimestamp) exceeds the staleThreshold "
.concat("\(priceData.quoteTimestamp + self.staleThreshold!) at current timestamp \(now)"))
if let threshold = self.staleThreshold {
assert(now < priceData.baseTimestamp + threshold,
message: "Price data's base timestamp \(priceData.baseTimestamp) exceeds the staleThreshold \(priceData.baseTimestamp + threshold) at current timestamp \(now)")
assert(now < priceData.quoteTimestamp + threshold,
message: "Price data's quote timestamp \(priceData.quoteTimestamp) exceeds the staleThreshold \(priceData.quoteTimestamp + threshold) at current timestamp \(now)")
}

return priceData.fixedPointRate
Expand Down
2 changes: 1 addition & 1 deletion cadence/contracts/connectors/evm/ERC4626PriceOracles.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ access(all) contract ERC4626PriceOracles {
}
let totalAssets = ERC4626Utils.totalAssets(vault: self.vault)
let totalShares = ERC4626Utils.totalShares(vault: self.vault)
if totalAssets == nil || totalShares == nil || totalShares == UInt256(0) {
if totalAssets == nil || totalShares == nil || totalShares == 0 {
return nil
}

Expand Down
8 changes: 2 additions & 6 deletions cadence/contracts/connectors/evm/ERC4626SinkConnectors.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,7 @@ access(all) contract ERC4626SinkConnectors {
///
access(self)
fun _approveErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, approveRes: EVM.Result): String {
return "Failed to approve ERC4626 vault \(self.vault.toString()) to spend \(ufixAmount) assets \(self.assetEVMAddress.toString()). "
.concat("approvee: \(self.vault.toString()), amount: \(uintAmount). ")
.concat("Error code: \(approveRes.errorCode) Error message: \(approveRes.errorMessage)")
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 @@ -224,9 +222,7 @@ access(all) contract ERC4626SinkConnectors {
access(self)
fun _depositErrorMessage(ufixAmount: UFix64, uintAmount: UInt256, depositRes: EVM.Result): String {
let coaHex = self.coa.borrow()!.address().toString()
return "Failed to deposit \(ufixAmount) assets \(self.assetEVMAddress.toString()) to ERC4626 vault \(self.vault.toString()). "
.concat("amount: \(uintAmount), to: \(coaHex). ")
.concat("Error code: \(depositRes.errorCode) Error message: \(depositRes.errorMessage)")
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)"
}
}
}
6 changes: 3 additions & 3 deletions cadence/contracts/connectors/evm/EVMAmountUtils.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ access(all) contract EVMAmountUtils {
}

let quantumExp: UInt8 = decimals - 8
let quantum: UInt256 = FlowEVMBridgeUtils.pow(base: 10, exponent: quantumExp)
let quantum = FlowEVMBridgeUtils.pow(base: 10, exponent: quantumExp)
let remainder: UInt256 = amt % quantum
let floored: UInt256 = amt - remainder

Expand All @@ -47,10 +47,10 @@ access(all) contract EVMAmountUtils {
}

let quantumExp: UInt8 = decimals - 8
let quantum: UInt256 = FlowEVMBridgeUtils.pow(base: 10, exponent: quantumExp)
let quantum = FlowEVMBridgeUtils.pow(base: 10, exponent: quantumExp)

let remainder: UInt256 = amt % quantum
var padded: UInt256 = amt
var padded = amt
if remainder != 0 {
padded = amt + (quantum - remainder)
}
Expand Down
6 changes: 2 additions & 4 deletions cadence/contracts/connectors/evm/EVMTokenConnectors.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ access(all) contract EVMTokenConnectors {
) {
pre {
FlowEVMBridgeConfig.getEVMAddressAssociated(with: depositVaultType) != nil:
"Provided type \(depositVaultType.identifier) has not been onboarded to the VM bridge - "
.concat("Ensure the type & ERC20 contracts are associated via the VM bridge")
"Provided type \(depositVaultType.identifier) has not been onboarded to the VM bridge - Ensure the type & ERC20 contracts are associated via the VM bridge"
feeSource.getSinkType() == Type<@FlowToken.Vault>() && feeSource.getSourceType() == Type<@FlowToken.Vault>():
"Provided feeSource must provide FlowToken.Vault but provides \(feeSource.getSourceType().identifier)"
}
Expand Down Expand Up @@ -180,8 +179,7 @@ access(all) contract EVMTokenConnectors {
) {
pre {
FlowEVMBridgeConfig.getEVMAddressAssociated(with: withdrawVaultType) != nil:
"Provided type \(withdrawVaultType.identifier) has not been onboarded to the VM bridge - "
.concat("Ensure the type & ERC20 contracts are associated via the VM bridge")
"Provided type \(withdrawVaultType.identifier) has not been onboarded to the VM bridge - Ensure the type & ERC20 contracts are associated via the VM bridge"
DeFiActionsUtils.definingContractIsFungibleToken(withdrawVaultType):
"The contract defining Vault \(withdrawVaultType.identifier) does not conform to FungibleToken contract interface"
coa.check():
Expand Down
23 changes: 8 additions & 15 deletions cadence/contracts/connectors/evm/UniswapV2SwapConnectors.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,9 @@ access(all) contract UniswapV2SwapConnectors {
pre {
path.length >= 2: "Provided path with length of \(path.length) - path must contain at least two EVM addresses)"
FlowEVMBridgeConfig.getTypeAssociated(with: path[0]) == inVault:
"Provided inVault \(inVault.identifier) is not associated with ERC20 at path[0] \(path[0].toString()) - "
.concat("Ensure the type & ERC20 contracts are associated via the VM bridge")
FlowEVMBridgeConfig.getTypeAssociated(with: path[path.length - 1]) == outVault:
"Provided outVault \(outVault.identifier) is not associated with ERC20 at path[\(path.length - 1)] \(path[path.length - 1].toString()) - "
.concat("Ensure the type & ERC20 contracts are associated via the VM bridge")
"Provided inVault \(inVault.identifier) is not associated with ERC20 at path[0] \(path[0].toString()) - Ensure the type & ERC20 contracts are associated via the VM bridge"
FlowEVMBridgeConfig.getTypeAssociated(with: path[path.length - 1]) == outVault:
"Provided outVault \(outVault.identifier) is not associated with ERC20 at path[\(path.length - 1)] \(path[path.length - 1].toString()) - Ensure the type & ERC20 contracts are associated via the VM bridge"
coaCapability.check():
"Provided COA Capability is invalid - provided an active, unrevoked Capability<auth(EVM.Call) &EVM.CadenceOwnedAccount>"
}
Expand Down Expand Up @@ -209,8 +207,7 @@ access(all) contract UniswapV2SwapConnectors {
let id = self.uniqueID?.id?.toString() ?? "UNASSIGNED"
let idType = self.uniqueID?.getType()?.identifier ?? "UNASSIGNED"
let coa = self.borrowCOA()
?? panic("The COA Capability contained by Swapper \(self.getType().identifier) with UniqueIdentifier "
.concat("\(idType) ID \(id) is invalid - cannot perform an EVM swap without a valid COA Capability"))
?? panic("The COA Capability contained by Swapper \(self.getType().identifier) with UniqueIdentifier \(idType) ID \(id) is invalid - cannot perform an EVM swap without a valid COA Capability")

// withdraw FLOW from the COA to cover the VM bridge fee
let bridgeFeeBalance = EVM.Balance(attoflow: 0)
Expand Down Expand Up @@ -241,7 +238,7 @@ access(all) contract UniswapV2SwapConnectors {
// perform the swap
res = self.call(to: self.routerAddress,
signature: "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)", // amountIn, amountOutMin, path, to, deadline (timestamp)
args: [evmAmountIn, UInt256(0), (reverse ? self.addressPath.reverse() : self.addressPath), coa.address(), UInt256(getCurrentBlock().timestamp)],
args: [evmAmountIn, 0, (reverse ? self.addressPath.reverse() : self.addressPath), coa.address(), UInt256(getCurrentBlock().timestamp)],
Comment thread
mts1715 marked this conversation as resolved.
Outdated
Comment thread
mts1715 marked this conversation as resolved.
Outdated
gasLimit: 1_000_000,
value: 0,
dryCall: false
Expand Down Expand Up @@ -285,7 +282,7 @@ access(all) contract UniswapV2SwapConnectors {
signature: out ? "getAmountsOut(uint,address[])" : "getAmountsIn(uint,address[])",
args: [amount, path],
gasLimit: 1_000_000,
value: UInt(0),
value: 0,
dryCall: true
)
if callRes == nil || callRes!.status != EVM.Status.successful {
Expand Down Expand Up @@ -330,7 +327,7 @@ access(all) contract UniswapV2SwapConnectors {
let calldata = EVM.encodeABIWithSignature(signature, args)
let valueBalance = EVM.Balance(attoflow: value)
if let coa = self.borrowCOA() {
let res: EVM.Result = dryCall
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
Expand All @@ -342,10 +339,6 @@ access(all) contract UniswapV2SwapConnectors {
/// Reverts with a message constructed from the provided args. Used in the event of a coa.call() error
access(self)
fun _callError(_ signature: String, _ res: EVM.Result,_ target: EVM.EVMAddress, _ uniqueIDType: String, _ id: String, _ swapperType: Type) {
panic("Call to \(target.toString()).\(signature) from Swapper \(swapperType.identifier) "
.concat("with UniqueIdentifier \(uniqueIDType) ID \(id) failed: \n\t"
.concat("Status value: \(res.status.rawValue)\n\t"))
.concat("Error code: \(res.errorCode)\n\t")
.concat("ErrorMessage: \(res.errorMessage)\n"))
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")
}
}
44 changes: 22 additions & 22 deletions cadence/contracts/connectors/evm/UniswapV3SwapConnectors.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ access(all) contract UniswapV3SwapConnectors {
continue
}

var translatedMaxIn: UInt256 = hopMaxIn
var translatedMaxIn = hopMaxIn

// If not the first hop, translate back to initial input token
if hopIdx > 0 {
Expand Down Expand Up @@ -358,7 +358,7 @@ access(all) contract UniswapV3SwapConnectors {

// helper to append address bytes
fun appendAddr(_ a: EVM.EVMAddress) {
let fixed: [UInt8; 20] = a.bytes
let fixed = a.bytes
var i = 0
while i < 20 {
out.append(fixed[i])
Expand Down Expand Up @@ -484,7 +484,7 @@ access(all) contract UniswapV3SwapConnectors {
if word.length < 32 { panic("getPool: invalid ABI word length") }

let addrSlice = word.slice(from: 12, upTo: 32) // 20 bytes
let addrBytes: [UInt8; 20] = addrSlice.toConstantSized<[UInt8; 20]>()!
let addrBytes = addrSlice.toConstantSized<[UInt8; 20]>()!

return EVM.EVMAddress(bytes: addrBytes)
}
Expand Down Expand Up @@ -516,7 +516,7 @@ access(all) contract UniswapV3SwapConnectors {
fun wordToUIntN(_ w: [UInt8], _ nBits: Int): UInt {
let full = wordToUInt(w)
if nBits >= 256 { return full }
let mask: UInt = (UInt(1) << UInt(nBits)) - UInt(1)
let mask = (1 as UInt << UInt(nBits)) - 1
Comment thread
SupunS marked this conversation as resolved.
Outdated
return full & mask
}
fun words(_ data: [UInt8]): [[UInt8]] {
Expand All @@ -535,7 +535,7 @@ access(all) contract UniswapV3SwapConnectors {
let SEL_LIQUIDITY: [UInt8] = [0x1a, 0x68, 0x65, 0x02]

// Get slot0 (sqrtPriceX96, tick, etc.)
let s0Res: EVM.Result? = self._dryCallRaw(
let s0Res = self._dryCallRaw(
to: poolEVMAddress,
calldata: EVMAbiHelpers.buildCalldata(selector: SEL_SLOT0, args: []),
gasLimit: 1_000_000,
Expand All @@ -544,7 +544,7 @@ access(all) contract UniswapV3SwapConnectors {
let sqrtPriceX96 = wordToUIntN(s0w[0], 160)

// Get current active liquidity
let liqRes: EVM.Result? = self._dryCallRaw(
let liqRes = self._dryCallRaw(
to: poolEVMAddress,
calldata: EVMAbiHelpers.buildCalldata(selector: SEL_LIQUIDITY, args: []),
gasLimit: 300_000,
Expand All @@ -553,10 +553,10 @@ access(all) contract UniswapV3SwapConnectors {

// Calculate price multiplier based on 6% price impact (600 bps)
// Use UInt256 throughout to prevent overflow in multiplication operations
let bps: UInt256 = 600
let Q96: UInt256 = 0x1000000000000000000000000
let sqrtPriceX96_256: UInt256 = UInt256(sqrtPriceX96)
let L_256: UInt256 = UInt256(L)
let bps = 600 as UInt256
let Q96 = 0x1000000000000000000000000 as UInt256
Comment thread
mts1715 marked this conversation as resolved.
Outdated
let sqrtPriceX96_256 = UInt256(sqrtPriceX96)
let L_256 = UInt256(L)

var maxAmount: UInt256 = 0
if zeroForOne {
Expand All @@ -570,17 +570,17 @@ access(all) contract UniswapV3SwapConnectors {
// Δx = L * (√P - √P') / (√P * √P')
// Since sqrt prices are in Q96 format: (L * ΔsqrtP * Q96) / (sqrtP * sqrtP')
// This gives us native token0 units after the two Q96 divisions cancel with one Q96 multiplication
let num1: UInt256 = L_256 * bps
let num2: UInt256 = num1 * Q96
let den: UInt256 = UInt256(20000) * sqrtPriceNew
maxAmount = den == 0 ? UInt256(0) : num2 / den
let num1 = L_256 * bps
let num2 = num1 * Q96
let den = 20000 as UInt256 * sqrtPriceNew
Comment thread
mts1715 marked this conversation as resolved.
Outdated
maxAmount = den == 0 ? 0 : num2 / den
} else {
// Swapping token1 -> token0 (price increases by maxPriceImpactBps)
// Formula: Δy = L * (√P' - √P)
// Approximation: √P' ≈ √P * (1 + priceImpact/2)
let sqrtMultiplier: UInt256 = 10000 + (bps / 2)
let sqrtPriceNew: UInt256 = (sqrtPriceX96_256 * sqrtMultiplier) / 10000
let deltaSqrt: UInt256 = sqrtPriceNew - sqrtPriceX96_256
let sqrtMultiplier = 10000 as UInt256 + (bps / 2)
Comment thread
mts1715 marked this conversation as resolved.
Outdated
let sqrtPriceNew = (sqrtPriceX96_256 * sqrtMultiplier) / 10000
let deltaSqrt = sqrtPriceNew - sqrtPriceX96_256

// Uniswap V3 spec: getAmount1Delta
// Δy = L * (√P' - √P)
Expand Down Expand Up @@ -624,7 +624,7 @@ access(all) contract UniswapV3SwapConnectors {
? "quoteExactInput(bytes,uint256)"
: "quoteExactOutput(bytes,uint256)"

let args: [AnyStruct] = [pathBytes, amount]
let args = [pathBytes, amount]

let res = self._dryCall(self.quoterAddress, callSig, args, 10_000_000)
if res == nil || res!.status != EVM.Status.successful { return nil }
Expand Down Expand Up @@ -678,7 +678,7 @@ access(all) contract UniswapV3SwapConnectors {
)

let coaRef = self.borrowCOA()!
let recipient: EVM.EVMAddress = coaRef.address()
let recipient = coaRef.address()

// optional dev guards
let _chkIn = EVMAbiHelpers.abiUInt256(evmAmountIn)
Expand All @@ -694,7 +694,7 @@ access(all) contract UniswapV3SwapConnectors {
amountOutMinimum: minOutUint
)

let calldata: [UInt8] = EVM.encodeABIWithSignature(
let calldata = EVM.encodeABIWithSignature(
"exactInput((bytes,address,uint256,uint256))",
[exactInputParams]
)
Expand All @@ -713,7 +713,7 @@ access(all) contract UniswapV3SwapConnectors {
)
}
let decoded = EVM.decodeABI(types: [Type<UInt256>()], data: swapRes.data)
let amountOut: UInt256 = decoded.length > 0 ? decoded[0] as! UInt256 : UInt256(0)
let amountOut: UInt256 = decoded.length > 0 ? decoded[0] as! UInt256 : 0

let outVaultType = reverse ? self.inType() : self.outType()
let outTokenEVMAddress =
Expand Down Expand Up @@ -833,7 +833,7 @@ access(all) contract UniswapV3SwapConnectors {
if word.length < 32 { panic("getPoolToken0: invalid ABI word length") }

let addrSlice = word.slice(from: 12, upTo: 32)
let addrBytes: [UInt8; 20] = addrSlice.toConstantSized<[UInt8; 20]>()!
let addrBytes = addrSlice.toConstantSized<[UInt8; 20]>()!
return EVM.EVMAddress(bytes: addrBytes)
}

Expand Down
Loading
Loading