Skip to content

Commit e5ea029

Browse files
committed
Optimize EVM calls in UniswapV3SwapConnectors
1 parent 61d605a commit e5ea029

1 file changed

Lines changed: 57 additions & 77 deletions

File tree

cadence/contracts/connectors/evm/UniswapV3SwapConnectors.cdc

Lines changed: 57 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -469,24 +469,19 @@ access(all) contract UniswapV3SwapConnectors {
469469
: self.feePath[hopIndex]
470470

471471
let res = self._dryCall(
472-
self.factoryAddress,
473-
"getPool(address,address,uint24)",
474-
[ tokenA, tokenB, UInt256(fee) ],
475-
120_000
472+
to: self.factoryAddress,
473+
signature: "getPool(address,address,uint24)",
474+
args: [tokenA, tokenB, UInt256(fee)],
475+
gasLimit: 120_000,
476+
resultTypes: [Type<EVM.EVMAddress>()]
476477
)!
477478
assert(
478479
res.status == EVM.Status.successful,
479480
message: "unable to get pool: tokenA \(tokenA.toString()), tokenB \(tokenB.toString()), fee: \(fee)"
480481
)
481482

482-
// ABI return is one 32-byte word; the last 20 bytes are the address
483-
let word = res.data
484-
if word.length < 32 { panic("getPool: invalid ABI word length") }
485-
486-
let addrSlice = word.slice(from: 12, upTo: 32) // 20 bytes
487-
let addrBytes = addrSlice.toConstantSized<[UInt8; 20]>()!
488-
489-
return EVM.EVMAddress(bytes: addrBytes)
483+
assert(res.results.length == 1, message: "getPool: invalid ABI-encoded return data")
484+
return res.results[0] as! EVM.EVMAddress
490485
}
491486

492487
/// Get max input amount for a specific hop
@@ -506,50 +501,29 @@ access(all) contract UniswapV3SwapConnectors {
506501
access(self) fun getMaxInAmount(hopIndex: Int, zeroForOne: Bool, reverse: Bool): UInt256 {
507502
let poolEVMAddress = self.getPoolAddress(hopIndex: hopIndex, reverse: reverse)
508503

509-
// Helper functions
510-
fun wordToUInt(_ w: [UInt8]): UInt {
511-
var acc: UInt = 0
512-
var i = 0
513-
while i < 32 { acc = (acc << 8) | UInt(w[i]); i = i + 1 }
514-
return acc
515-
}
516-
fun wordToUIntN(_ w: [UInt8], _ nBits: UInt): UInt {
517-
let full = wordToUInt(w)
518-
if nBits >= 256 { return full }
519-
let mask: UInt = (1 << nBits) - 1
520-
return full & mask
521-
}
522-
fun words(_ data: [UInt8]): [[UInt8]] {
523-
let n = data.length / 32
524-
var out: [[UInt8]] = []
525-
var i = 0
526-
while i < n {
527-
out.append(data.slice(from: i*32, upTo: (i+1)*32))
528-
i = i + 1
529-
}
530-
return out
531-
}
532-
533-
// Selectors
534-
let SEL_SLOT0: [UInt8] = [0x38, 0x50, 0xc7, 0xbd]
535-
let SEL_LIQUIDITY: [UInt8] = [0x1a, 0x68, 0x65, 0x02]
536-
537504
// Get slot0 (sqrtPriceX96, tick, etc.)
538-
let s0Res = self._dryCallRaw(
505+
// slot0 (uint160 sqrtPriceX96, int24, uint16, uint16, uint16, uint8, bool)
506+
let s0Res = self._dryCall(
539507
to: poolEVMAddress,
540-
calldata: EVMAbiHelpers.buildCalldata(selector: SEL_SLOT0, args: []),
508+
signature: "slot0()",
509+
args: [],
541510
gasLimit: 1_000_000,
511+
resultTypes: [Type<UInt>(), Type<Int32>(), Type<UInt16>(), Type<UInt16>(), Type<UInt16>(), Type<UInt8>(), Type<Bool>()]
542512
)
543-
let s0w = words(s0Res!.data)
544-
let sqrtPriceX96 = wordToUIntN(s0w[0], 160)
513+
assert(s0Res.results.length == 7, message: "slot0: invalid ABI-encoded return data")
514+
let sqrtPriceX96 = s0Res.results[0] as! UInt
545515

546516
// Get current active liquidity
547-
let liqRes = self._dryCallRaw(
517+
// liquidity() (uint128 liquidity)
518+
let liqRes = self._dryCall(
548519
to: poolEVMAddress,
549-
calldata: EVMAbiHelpers.buildCalldata(selector: SEL_LIQUIDITY, args: []),
520+
signature: "liquidity()",
521+
args: [],
550522
gasLimit: 300_000,
523+
resultTypes: [Type<UInt>()]
551524
)
552-
let L = wordToUIntN(words(liqRes!.data)[0], 128)
525+
assert(liqRes.results.length == 1, message: "liquidity: invalid ABI-encoded return data")
526+
let L = liqRes.results[0] as! UInt
553527

554528
// Calculate price multiplier based on 6% price impact (600 bps)
555529
// Use UInt256 throughout to prevent overflow in multiplication operations
@@ -626,13 +600,18 @@ access(all) contract UniswapV3SwapConnectors {
626600

627601
let args = [pathBytes, amount]
628602

629-
let res = self._dryCall(self.quoterAddress, callSig, args, 10_000_000)
603+
let res = self._dryCall(
604+
to: self.quoterAddress,
605+
signature: callSig,
606+
args: args,
607+
gasLimit: 10_000_000,
608+
resultTypes: [Type<UInt256>()]
609+
)
630610
if res == nil || res!.status != EVM.Status.successful { return nil }
631611

632-
let decoded = EVM.decodeABI(types: [Type<UInt256>()], data: res!.data)
633-
if decoded.length == 0 { return nil }
612+
if res!.results.length == 0 { return nil }
634613

635-
return decoded[0] as! UInt256
614+
return res!.results[0] as! UInt256
636615
}
637616

638617
/// Executes exact input swap via router
@@ -701,7 +680,7 @@ access(all) contract UniswapV3SwapConnectors {
701680
args: [exactInputParams],
702681
gasLimit: 10_000_000,
703682
value: 0,
704-
resultTypes: nil
683+
resultTypes: [Type<UInt256>()]
705684
)!
706685
if swapRes.status != EVM.Status.successful {
707686
UniswapV3SwapConnectors._callError(
@@ -717,13 +696,14 @@ access(all) contract UniswapV3SwapConnectors {
717696
args: [self.routerAddress, 0 as UInt256],
718697
gasLimit: 60_000,
719698
value: 0,
720-
resultTypes: [Type<UInt256>()]
699+
resultTypes: nil
721700
)!
722701
if resetAllowanceRes.status != EVM.Status.successful {
723702
UniswapV3SwapConnectors._callError("approve(address,uint256)", resetAllowanceRes, inToken, idType, id, self.getType())
724703
}
725-
assert(resetAllowanceRes.results.length == 1, message: "invalid swap return data")
726-
let amountOut = resetAllowanceRes.results[0] as! UInt256
704+
705+
assert(swapRes.results.length == 1, message: "invalid swap return data")
706+
let amountOut = swapRes.results[0] as! UInt256
727707

728708
let outVaultType = reverse ? self.inType() : self.outType()
729709
let outTokenEVMAddress =
@@ -759,19 +739,23 @@ access(all) contract UniswapV3SwapConnectors {
759739

760740
access(self) view fun borrowCOA(): auth(EVM.Owner) &EVM.CadenceOwnedAccount? { return self.coaCapability.borrow() }
761741

762-
access(self) fun _dryCall(_ to: EVM.EVMAddress, _ signature: String, _ args: [AnyStruct], _ gas: UInt64): EVM.Result? {
763-
let calldata = EVM.encodeABIWithSignature(signature, args)
764-
let valueBalance = EVM.Balance(attoflow: 0)
765-
if let coa = self.borrowCOA() {
766-
return coa.dryCall(to: to, data: calldata, gasLimit: gas, value: valueBalance)
767-
}
768-
return nil
769-
}
770-
771-
access(self) fun _dryCallRaw(to: EVM.EVMAddress, calldata: [UInt8], gasLimit: UInt64): EVM.Result? {
772-
let valueBalance = EVM.Balance(attoflow: 0)
742+
access(self)
743+
fun _dryCall(
744+
to: EVM.EVMAddress,
745+
signature: String,
746+
args: [AnyStruct],
747+
gasLimit: UInt64,
748+
resultTypes: [Type]?
749+
): EVM.ResultDecoded? {
773750
if let coa = self.borrowCOA() {
774-
return coa.dryCall(to: to, data: calldata, gasLimit: gasLimit, value: valueBalance)
751+
return coa.dryCallWithSigAndArgs(
752+
to: to,
753+
signature: signature,
754+
args: args,
755+
gasLimit: gasLimit,
756+
value: EVM.Balance(attoflow: 0),
757+
resultTypes: resultTypes
758+
)
775759
}
776760
return nil
777761
}
@@ -818,21 +802,17 @@ access(all) contract UniswapV3SwapConnectors {
818802
}
819803

820804
access(self) fun getPoolToken0(_ pool: EVM.EVMAddress): EVM.EVMAddress {
821-
// token0() selector = 0x0dfe1681
822-
let SEL_TOKEN0: [UInt8] = [0x0d, 0xfe, 0x16, 0x81]
823-
let res = self._dryCallRaw(
805+
let res = self._dryCall(
824806
to: pool,
825-
calldata: EVMAbiHelpers.buildCalldata(selector: SEL_TOKEN0, args: []),
807+
signature: "token0()",
808+
args: [],
826809
gasLimit: 150_000,
810+
resultTypes: [Type<EVM.EVMAddress>()]
827811
)!
828812
assert(res.status == EVM.Status.successful, message: "token0() call failed")
829813

830-
let word = res.data
831-
if word.length < 32 { panic("getPoolToken0: invalid ABI word length") }
832-
833-
let addrSlice = word.slice(from: 12, upTo: 32)
834-
let addrBytes = addrSlice.toConstantSized<[UInt8; 20]>()!
835-
return EVM.EVMAddress(bytes: addrBytes)
814+
assert(res.results.length == 1, message: "token0: invalid ABI-encoded return data")
815+
return res.results[0] as! EVM.EVMAddress
836816
}
837817

838818
access(self) fun isZeroForOne(hopIndex: Int, reverse: Bool): Bool {

0 commit comments

Comments
 (0)