Uniswap V4通过Universal Router代币兑换持续回滚问题求助
Uniswap V4 LINK转USDC交易回滚排查
我尝试通过Uniswap V4完成LINK到USDC的代币兑换,但交易始终回滚。已完成ERC20授权Permit2、Permit2授权Universal Router的操作,且此前使用相同授权逻辑在Uniswap V3进行兑换可成功。以下是我的Swift实现代码:
public func executeV4( fromToken: TokenWallet, toToken: TokenWallet, sender: EthereumAddress, routerAddress: String, amountIn: BigUInt, amountOutMin: BigUInt, fee: UInt32 = 2, tickSpacing: Int32 = 1, payerIsUser: Bool = true, rpcUrl: String, gasLimit: BigUInt ) async throws -> String { GKLogDebug("==============================================") GKLogDebug("[executeV4] START") GKLogDebug("[executeV4] fromToken: \(fromToken.contractAddress ?? "")") GKLogDebug("[executeV4] toToken: \(toToken.contractAddress ?? "")") GKLogDebug("[executeV4] router: \(routerAddress)") GKLogDebug("[executeV4] amountIn: \(amountIn)") GKLogDebug("[executeV4] amountOutMin: \(amountOutMin)") GKLogDebug("[executeV4] rpc: \(rpcUrl)") GKLogDebug("==============================================") guard let url = URL(string: rpcUrl) else { GKLogDebug("[executeV4] ERROR: Invalid RPC URL") throw APIError.statusMessage(message: "Error - RPC URL") } let web3 = try await Web3.new(url) GKLogDebug("[executeV4] RPC OK") // Commands let command = Data([0x10]) GKLogDebug("[executeV4] command: \(command.toHexString())") // Actions let actions = Data([0x06, 0x0C, 0x0F]) GKLogDebug("[executeV4] actions: \(actions.toHexString())") guard let fromTokenAddress = fromToken.contractAddress, let toTokenAddress = toToken.contractAddress, let fromTokenAddressEth = EthereumAddress(fromTokenAddress), let toTokenAddressEth = EthereumAddress(toTokenAddress) else { GKLogDebug("[executeV4] ERROR: Token address missing") throw APIError.statusMessage(message: "Missing token addresses") } GKLogDebug("[executeV4] Token addr OK") guard let hooksAddress = EthereumAddress("0x0000000000000000000000000000000000000000") else { throw APIError.statusMessage(message: "Missing hooks") } let token0 = fromTokenAddressEth.address.lowercased() < toTokenAddressEth.address.lowercased() ? fromTokenAddressEth : toTokenAddressEth let token1 = fromTokenAddressEth.address.lowercased() < toTokenAddressEth.address.lowercased() ? toTokenAddressEth : fromTokenAddressEth let poolKeyValues: [Any] = [ token0, token1, fee, tickSpacing, hooksAddress ] GKLogDebug("[executeV4] poolKey: \(poolKeyValues)") let zeroForOne = (fromTokenAddressEth.address.lowercased() == token0.address.lowercased()) let swapValues: [Any] = [ poolKeyValues, zeroForOne, amountIn, amountOutMin, Data() ] GKLogDebug("[executeV4] swapValues prepared") guard let swapParams = encodeInput( values: swapValues, abiTypes: [ .init(name: "poolKey", type: .tuple(types: [ .address, .address, .uint(bits: 24), .int(bits: 24), .address ])), .init(name: "zeroForOne", type: .bool), .init(name: "amountIn", type: .uint(bits: 128)), .init(name: "amountOutMinimum", type: .uint(bits: 128)), .init(name: "hookData", type: .dynamicBytes) ] ) else { GKLogDebug("[executeV4] ERROR: Failed to encode swapParams") throw APIError.statusMessage(message: "Fail to encode swapParams") } GKLogDebug("[executeV4] swapParams hex: \(swapParams.toHexString())") // SETTLE_ALL let settleAllValues: [Any] = [fromTokenAddressEth, amountIn] guard let settleAllParams = encodeSimpleTypes( values: settleAllValues, types: [.address, .uint(bits: 256)] ) else { GKLogDebug("[executeV4] ERROR: Fail to encode settleAll") throw APIError.statusMessage(message: "Fail to encode settleAllParams") } GKLogDebug("[executeV4] settleAllParams hex: \(settleAllParams.toHexString())") // TAKE_ALL let takeAllValues: [Any] = [toTokenAddressEth, amountOutMin] guard let takeAllParams = encodeSimpleTypes( values: takeAllValues, types: [.address, .uint(bits: 256)] ) else { GKLogDebug("[executeV4] ERROR: Fail to encode takeAll") throw APIError.statusMessage(message: "Fail to encode takeAllParams") } GKLogDebug("[executeV4] takeAllParams hex: \(takeAllParams.toHexString())") // Build params[] let params: [Data] = [swapParams, settleAllParams, takeAllParams] GKLogDebug("[executeV4] params count: \(params.count)") // Encode V4 input let v4InputValues: [Any] = [actions, params] let v4InputTypes: [ABI.Element.InOut] = [ .init(name: "actions", type: .dynamicBytes), .init(name: "params", type: .array(type: .dynamicBytes, length: 0)) ] guard let encodedV4Input = encodeInput(values: v4InputValues, abiTypes: v4InputTypes) else { GKLogDebug("[executeV4] ERROR: Failed to encode V4 input") throw APIError.statusMessage(message: "Failed to encode V4 input tuple") } GKLogDebug("[executeV4] encodedV4Input hex: \(encodedV4Input.toHexString())") let inputs = [encodedV4Input] let deadline = BigUInt(Date().timeIntervalSince1970 + 600) GKLogDebug("[executeV4] deadline: \(deadline)") let executeParams: [Any] = [command, inputs, deadline] GKLogDebug("[executeV4] executeParams OK") guard let walletAddress = LocalStorageManager.shared .get(LocalStorage.userWallet)? .walletAddressETH else { GKLogDebug("[executeV4] ERROR: missing local wallet") throw APIError.statusMessage(message: "Missing wallet address") } GKLogDebug("[executeV4] encodedV4Input hex: \(encodedV4Input.toHexString())") GKLogDebug("[executeV4] executeParams array count: \(executeParams.count)") for (index, param) in executeParams.enumerated() { if let dataParam = param as? Data { GKLogDebug("[executeV4] executeParams[\(index)] hex: \(dataParam.toHexString())") } else { GKLogDebug("[executeV4] executeParams[\(index)]: \(param)") } } let parameter = SwapUniTransParams( tokenIn: fromToken, sender: walletAddress, receiver: walletAddress, rpcURL: rpcUrl, value: 0 ) GKLogDebug("[executeV4] Preparing TX...") let rawTxBase64 = try await SwapUniHelper.createTransaction( parameter: parameter, contractAddress: routerAddress, abiName: "universal_execute", method: "execute", parameters: executeParams, value: 0, gasLimitEVM: gasLimit ) GKLogDebug("[executeV4] TX encoded (base64): \(rawTxBase64.prefix(60))...") GKLogDebug("[executeV4] DONE") GKLogDebug("==============================================") return rawTxBase64 }
排查方向
- Pool Key有效性:当前设置的
fee=2(对应0.002%费率)、tickSpacing=1,需确认链上是否存在该参数的池;同时检查fee的编码是否符合uint24要求,避免位数溢出。 - 参数编码匹配:
amountIn和amountOutMin使用uint128编码,需确认数值未超出uint128范围;验证SETTLE_ALL、TAKE_ALL的参数结构是否完全匹配Uniswap V4 Router的要求。 - Command与Input结构:确认
0x10命令对应的inputs数组结构是否正确,是否遗漏必要参数。 - Gas限制:Uniswap V4交易Gas消耗高于V3,尝试调高
gasLimit至300000以上测试。 - 池存在性验证:调用PoolManager合约的
getPool方法,传入构建的poolKey,确认池是否真实存在。
内容的提问来源于stack exchange,提问作者Shillwi2593
相关产品推荐
相关产品推荐

