Skip to content

Nialexsan/univ3 connectors#47

Merged
nialexsan merged 15 commits intomainfrom
nialexsan/univ3-connectors
Oct 31, 2025
Merged

Nialexsan/univ3 connectors#47
nialexsan merged 15 commits intomainfrom
nialexsan/univ3-connectors

Conversation

@nialexsan
Copy link
Contributor

@nialexsan nialexsan commented Oct 6, 2025

Uniswap v3 SwapRouterV2 connector
EVM ABI helpers for tuple encoding

@nialexsan nialexsan marked this pull request as ready for review October 9, 2025 16:01
Copy link
Contributor

@sisyphusSmiling sisyphusSmiling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this connector needs some more testing and perhaps a different approach (EVM-side helper) until tuple encoding is natively available. The lack of testing and application-level byte packing creates potential security & bug surface area and likely lots of computation overhead we can optimize.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I think this contract will be low hanging fruit for optimization. Without the ability to encode a tuple from Cadence (see onflow/flow-go#8020), packing the bytes is quite inefficient and difficult to test.

Second to native encoding/decoding, I think having a helper contract in EVM to which we can pass/get natively encodable/decodable types will be preferred to this approach. But I suppose it's not a blocker for initial tracer deployment & setup.

Comment on lines +24 to +29
access(all) fun encodeTuple_bytes_addr_u256_u256(
path: [UInt8],
recipient: EVM.EVMAddress,
amountOne: UInt256,
amountTwo: UInt256
): [UInt8] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Param comments would be helpful here

// ===== Utilities: concat & padding =====
access(all) fun concat(_ parts: [[UInt8]]): [UInt8] {
var out: [UInt8] = []
for p in parts { out = out.concat(p) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

appendAll would probably be more performant here

Comment on lines +95 to +103
let hex: [String] = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
var out = ""
var i = 0
while i < bytes.length {
let b = bytes[i]
out = out.concat(hex[Int(b / 16)]).concat(hex[Int(b % 16)])
i = i + 1
}
return out
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't this just be return String.encodeHex(bytes)


// Represents one ABI argument chunk
access(all) struct ABIArg {
access(all) let isDynamic: Bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me what is meant by dynamic here

Comment on lines +657 to +658
// exactInput((bytes,address,uint256,uint256)) selector = 0xb858183f
let selector: [UInt8] = [0xb8, 0x58, 0x18, 0x3f]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not apparent that the selector values match the commented selector which on its own need to match the .sol contract selector. This seems to work:

Suggested change
// exactInput((bytes,address,uint256,uint256)) selector = 0xb858183f
let selector: [UInt8] = [0xb8, 0x58, 0x18, 0x3f]
// exactInput((bytes,address,uint256,uint256)) selector = 0xb858183f
let selector: [UInt8] = EVM.encodeABIWithSignature("exactInput((bytes,address,uint256,uint256))", [])

Comment on lines +708 to +712
access(self) fun _firstUint256(_ data: [UInt8]): UInt256 {
let decoded = EVM.decodeABI(types: [Type<UInt256>()], data: data)
if decoded.length > 0 { return decoded[0] as! UInt256 }
return UInt256(0)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this used anywhere

Suggested change
access(self) fun _firstUint256(_ data: [UInt8]): UInt256 {
let decoded = EVM.decodeABI(types: [Type<UInt256>()], data: data)
if decoded.length > 0 { return decoded[0] as! UInt256 }
return UInt256(0)
}

Comment on lines +525 to +530
let SEL_SLOT0: [UInt8] = [0x38, 0x50, 0xc7, 0xbd]
let SEL_LIQUIDITY: [UInt8] = [0x1a, 0x68, 0x65, 0x02]
let SEL_FEE: [UInt8] = [0xdd, 0xca, 0x3f, 0x43]
let SEL_TICK_BITMAP: [UInt8] = [0x53, 0x39, 0xc2, 0x96]
let SEL_TICKS: [UInt8] = [0xf3, 0x0d, 0xba, 0x93]
let SEL_TICK_SPACING: [UInt8] = [0xd0, 0xc9, 0x3a, 0x7c]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as elsewhere - it's difficult to catch any mismatch against the desired selector. Suggest decoding the signature for each using EVM.encodeABIWithSignature() using empty data array.

return EVM.EVMAddress(bytes: addrBytes)
}

access(self) fun getMaxAmount(zeroForOne: Bool): UInt256 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's going to be very difficult to test these nested function definitions directly. If we do continue down the path of Cadence-side byte packing, we'll want to move these to a scope that we can test and validate directly.

Comment on lines +221 to +229
access(self) fun to20(_ b: [UInt8]): [UInt8; 20] {
if b.length != 20 { panic("to20: need exactly 20 bytes") }
return [
b[0], b[1], b[2], b[3], b[4],
b[5], b[6], b[7], b[8], b[9],
b[10], b[11], b[12], b[13], b[14],
b[15], b[16], b[17], b[18], b[19]
]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can just use b.toConstantSized<[UInt8; 20]>()!.

ref

Co-authored-by: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com>
Copy link
Contributor

@sisyphusSmiling sisyphusSmiling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving to unblock. Once revisiting, I'm fairly confident the production version will require a redeployment

@nialexsan nialexsan merged commit ffc1645 into main Oct 31, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants