diff --git a/packages/sdk-router/src/abi/SynapseIntentPreviewer.json b/packages/sdk-router/src/abi/SynapseIntentPreviewer.json index 9cf2f9b574..92dcb08707 100644 --- a/packages/sdk-router/src/abi/SynapseIntentPreviewer.json +++ b/packages/sdk-router/src/abi/SynapseIntentPreviewer.json @@ -44,7 +44,7 @@ { "type": "error", "name": "SIP__TokenNotNative", "inputs": [] }, { "type": "error", - "name": "ZapDataV1__ForwardParamsIncorrect", + "name": "ZapDataV1__FinalTokenNotSpecified", "inputs": [] }, { "type": "error", "name": "ZapDataV1__InvalidEncoding", "inputs": [] }, diff --git a/packages/sdk-router/src/abi/SynapseIntentRouter.json b/packages/sdk-router/src/abi/SynapseIntentRouter.json index bd77b99e30..ddee817997 100644 --- a/packages/sdk-router/src/abi/SynapseIntentRouter.json +++ b/packages/sdk-router/src/abi/SynapseIntentRouter.json @@ -16,11 +16,6 @@ "internalType": "address" }, { "name": "amountIn", "type": "uint256", "internalType": "uint256" }, - { - "name": "minLastStepAmountIn", - "type": "uint256", - "internalType": "uint256" - }, { "name": "deadline", "type": "uint256", "internalType": "uint256" }, { "name": "steps", @@ -51,11 +46,6 @@ "internalType": "address" }, { "name": "amountIn", "type": "uint256", "internalType": "uint256" }, - { - "name": "minLastStepAmountIn", - "type": "uint256", - "internalType": "uint256" - }, { "name": "deadline", "type": "uint256", "internalType": "uint256" }, { "name": "steps", @@ -91,7 +81,6 @@ ] }, { "type": "error", "name": "FailedInnerCall", "inputs": [] }, - { "type": "error", "name": "SIR__AmountInsufficient", "inputs": [] }, { "type": "error", "name": "SIR__DeadlineExceeded", "inputs": [] }, { "type": "error", "name": "SIR__MsgValueIncorrect", "inputs": [] }, { "type": "error", "name": "SIR__StepsNotProvided", "inputs": [] }, diff --git a/packages/sdk-router/src/constants/addresses.ts b/packages/sdk-router/src/constants/addresses.ts index 05c40e5933..ed0f86e734 100644 --- a/packages/sdk-router/src/constants/addresses.ts +++ b/packages/sdk-router/src/constants/addresses.ts @@ -82,7 +82,7 @@ export const FAST_BRIDGE_V2_ADDRESS_MAP: AddressMap = generateAddressMap( * TokenZapV1 contract address for all chains except ones from TOKEN_ZAP_V1_EXCEPTION_MAP. * TODO: this is a staging TokenZapV1 deployment, update to the production deployment when ready. */ -const TOKEN_ZAP_V1_ADDRESS = '0xAFc26Ec01223Bea0665F48b92De3823Ee17E0550' +const TOKEN_ZAP_V1_ADDRESS = '0x6C6FA1cE8160bb680f7a1dd2068c7302bA2a9eaB' const TOKEN_ZAP_V1_EXCEPTION_MAP: AddressMap = {} export const TOKEN_ZAP_V1_ADDRESS_MAP: AddressMap = generateAddressMap( INTENTS_SUPPORTED_CHAIN_IDS, @@ -95,7 +95,7 @@ export const TOKEN_ZAP_V1_ADDRESS_MAP: AddressMap = generateAddressMap( * TODO: this is a staging SynapseIntentRouter deployment, update to the production deployment when ready. */ const SYNAPSE_INTENT_ROUTER_ADDRESS = - '0x57203c65DeA2ded4EE4E303a9494bee04df030BF' + '0x018396706193B16F8a1b20B87B2dcC840979D7EA' const SYNAPSE_INTENT_ROUTER_EXCEPTION_MAP: AddressMap = {} export const SYNAPSE_INTENT_ROUTER_ADDRESS_MAP: AddressMap = generateAddressMap( INTENTS_SUPPORTED_CHAIN_IDS, @@ -108,7 +108,7 @@ export const SYNAPSE_INTENT_ROUTER_ADDRESS_MAP: AddressMap = generateAddressMap( * TODO: this is a staging SynapseIntentPreviewer deployment, update to the production deployment when ready. */ const SYNAPSE_INTENT_PREVIEWER_ADDRESS = - '0x731EDF984E2A0d7657Fc451C0586F42939DC2010' + '0xc542Df6aA3813b49EE8A5A07f82eA0EaAa006bFa' const SYNAPSE_INTENT_PREVIEWER_EXCEPTION_MAP: AddressMap = {} export const SYNAPSE_INTENT_PREVIEWER_ADDRESS_MAP: AddressMap = generateAddressMap( diff --git a/packages/sdk-router/src/rfq/sir.ts b/packages/sdk-router/src/rfq/sir.ts index 4eb9982e8f..af48fb0ccd 100644 --- a/packages/sdk-router/src/rfq/sir.ts +++ b/packages/sdk-router/src/rfq/sir.ts @@ -93,7 +93,7 @@ export class SynapseIntentRouter implements SynapseModule { ): Promise { // Merge the preparation and final steps const steps: StepParams[] = [ - ...decodeStepParams(originQuery.rawParams), + ...this.getPrepSteps(originQuery), await this.getFinalStep(to, destChainId, originQuery.tokenOut, destQuery), ] if (isNativeToken(token)) { @@ -104,7 +104,6 @@ export class SynapseIntentRouter implements SynapseModule { await this.sirContract.populateTransaction.completeIntentWithBalanceChecks( this.tokenZapAddress, amount, - originQuery.minAmountOut, originQuery.deadline, steps ) @@ -153,6 +152,30 @@ export class SynapseIntentRouter implements SynapseModule { // ═════════════════════════════════════════════════ SIR TOOLS ═════════════════════════════════════════════════════ + private getPrepSteps(originQuery: CCTPRouterQuery): StepParams[] { + const prepSteps = decodeStepParams(originQuery.rawParams) + // Check that the minAmountOut matches the last step + if (prepSteps.length > 0) { + const decodedLastStep = decodeZapData( + hexlify(prepSteps[prepSteps.length - 1].zapData) + ) + if ( + !decodedLastStep.minFinalAmount || + !decodedLastStep.minFinalAmount.eq(originQuery.minAmountOut) + ) { + console.error( + { + decodedLastStep, + originQuery, + }, + 'Mismatch in minAmountOut in last step' + ) + throw new Error('Mismatch in minAmountOut in last step') + } + } + return prepSteps + } + private async getFinalStep( to: string, dstChainId: number, diff --git a/packages/sdk-router/src/rfq/sirSet.ts b/packages/sdk-router/src/rfq/sirSet.ts index 6ca9915d6f..082be11ec4 100644 --- a/packages/sdk-router/src/rfq/sirSet.ts +++ b/packages/sdk-router/src/rfq/sirSet.ts @@ -50,8 +50,12 @@ import { encodeSavedBridgeParams, SavedParamsV1, } from './paramsV2' -import { decodeZapData, encodeZapData } from './zapData' -import { extractSingleZapData } from './steps' +import { modifyMinFinalAmount } from './zapData' +import { + decodeStepParams, + encodeStepParams, + extractSingleZapData, +} from './steps' type OriginIntent = { ticker: Ticker @@ -323,6 +327,17 @@ export class SynapseIntentRouterSet extends SynapseModuleSet { if (originQuery.minAmountOut.lt(minAmountFinalAmount)) { originQuery.minAmountOut = minAmountFinalAmount } + // Adjust the slippage in the last origin step. + const originSteps = decodeStepParams(originQueryPrecise.rawParams) + if (originSteps.length === 0) { + console.error({ originQueryPrecise }, 'No steps in originQueryPrecise') + return originQuery + } + originSteps[originSteps.length - 1].zapData = modifyMinFinalAmount( + hexlify(originSteps[originSteps.length - 1].zapData), + minAmountFinalAmount + ) + originQuery.rawParams = encodeStepParams(originSteps) return originQuery } @@ -336,17 +351,14 @@ export class SynapseIntentRouterSet extends SynapseModuleSet { if (!validateEngineID(paramsV1.destEngineID)) { throw new Error(`Invalid engineID: ${paramsV1.destEngineID}`) } - const decodedZapData = decodeZapData(hexlify(paramsV2.zapData)) + const oldZapData = hexlify(paramsV2.zapData) // Do nothing if there is no Zap on the destination chain. - if (!decodedZapData.target) { + if (hexDataLength(oldZapData) === 0) { return destQueryPrecise } // Regenarate ZapData with the new minAmountOut const minAmountOut = applySlippage(destQueryPrecise.minAmountOut, slippage) - const zapData = encodeZapData({ - ...decodedZapData, - minFwdAmount: minAmountOut, - }) + const zapData = modifyMinFinalAmount(oldZapData, minAmountOut) return this.getRFQDestinationQuery({ tokenOut: destQueryPrecise.tokenOut, minAmountOut, diff --git a/packages/sdk-router/src/rfq/zapData.test.ts b/packages/sdk-router/src/rfq/zapData.test.ts index 448d6d45ed..fbb705dc3b 100644 --- a/packages/sdk-router/src/rfq/zapData.test.ts +++ b/packages/sdk-router/src/rfq/zapData.test.ts @@ -4,6 +4,7 @@ import { ETH_USDC, ETH_USDT, ETH_DAI } from '../constants/testValues' import { encodeZapData, decodeZapData, + modifyMinFinalAmount, ZapDataV1, AMOUNT_NOT_PRESENT, } from './zapData' @@ -15,7 +16,16 @@ describe('zapData', () => { amountPosition: 1, finalToken: ETH_USDT.toLowerCase(), forwardTo: ETH_DAI.toLowerCase(), - minFwdAmount: BigNumber.from('1234567890123456789012345678901234567890'), + minFinalAmount: BigNumber.from('1234567890123456789012345678901234567890'), + } + + const zapDataNewFinalAmount: ZapDataV1 = { + target: ETH_USDC.toLowerCase(), + payload: '0xdeadbeef', + amountPosition: 1, + finalToken: ETH_USDT.toLowerCase(), + forwardTo: ETH_DAI.toLowerCase(), + minFinalAmount: BigNumber.from('1234567890'), } const zapDataEmptyPayload: ZapDataV1 = { @@ -24,7 +34,7 @@ describe('zapData', () => { amountPosition: AMOUNT_NOT_PRESENT, finalToken: ETH_USDT.toLowerCase(), forwardTo: ETH_DAI.toLowerCase(), - minFwdAmount: BigNumber.from('1234567890'), + minFinalAmount: BigNumber.from('1234567890'), } it('roundtrip encoding', () => { @@ -38,4 +48,14 @@ describe('zapData', () => { const decoded = decodeZapData(encoded) expect(decoded).toEqual(zapDataEmptyPayload) }) + + it('roundtrip encoding with final amount modified', () => { + const encoded = encodeZapData(zapData) + const encodedModified = modifyMinFinalAmount( + encoded, + zapDataNewFinalAmount.minFinalAmount + ) + const decoded = decodeZapData(encodedModified) + expect(decoded).toEqual(zapDataNewFinalAmount) + }) }) diff --git a/packages/sdk-router/src/rfq/zapData.ts b/packages/sdk-router/src/rfq/zapData.ts index 7dde624f20..ee83cbd528 100644 --- a/packages/sdk-router/src/rfq/zapData.ts +++ b/packages/sdk-router/src/rfq/zapData.ts @@ -23,7 +23,7 @@ export type ZapDataV1 = { amountPosition: number finalToken: string forwardTo: string - minFwdAmount: BigNumber + minFinalAmount: BigNumber } export const encodeZapData = (zapData: Partial): string => { @@ -36,14 +36,14 @@ export const encodeZapData = (zapData: Partial): string => { amountPosition, finalToken, forwardTo, - minFwdAmount, + minFinalAmount, } = applyDefaultValues(zapData) return hexConcat([ encodeUint16(ZAP_DATA_VERSION), encodeUint16(amountPosition), finalToken, forwardTo, - encodeUint256(minFwdAmount), + encodeUint256(minFinalAmount), target, payload, ]) @@ -61,7 +61,7 @@ export const decodeZapData = (zapData: string): Partial => { // uint16 amountPosition [002 .. 004) // address finalToken [004 .. 024) // address forwardTo [024 .. 044) - // uint256 minFwdAmount [044 .. 076) + // uint256 minFinalAmount [044 .. 076) // address target [076 .. 096) // bytes payload [096 .. ***) const version = parseInt(hexDataSlice(zapData, 0, 2), 16) @@ -75,7 +75,7 @@ export const decodeZapData = (zapData: string): Partial => { ), finalToken: hexDataSlice(zapData, OFFSET_FINAL_TOKEN, OFFSET_FORWARD_TO), forwardTo: hexDataSlice(zapData, OFFSET_FORWARD_TO, OFFSET_MIN_FWD_AMOUNT), - minFwdAmount: BigNumber.from( + minFinalAmount: BigNumber.from( hexDataSlice(zapData, OFFSET_MIN_FWD_AMOUNT, OFFSET_TARGET) ), target: hexDataSlice(zapData, OFFSET_TARGET, OFFSET_PAYLOAD), @@ -83,6 +83,17 @@ export const decodeZapData = (zapData: string): Partial => { } } +export const modifyMinFinalAmount = ( + zapData: string, + newMinFinalAmount: BigNumber +): string => { + const decoded = decodeZapData(zapData) + return encodeZapData({ + ...decoded, + minFinalAmount: newMinFinalAmount, + }) +} + export const applyDefaultValues = (zapData: Partial): ZapDataV1 => { return { target: zapData.target || AddressZero, @@ -90,7 +101,7 @@ export const applyDefaultValues = (zapData: Partial): ZapDataV1 => { amountPosition: zapData.amountPosition || AMOUNT_NOT_PRESENT, finalToken: zapData.finalToken || AddressZero, forwardTo: zapData.forwardTo || AddressZero, - minFwdAmount: zapData.minFwdAmount || Zero, + minFinalAmount: zapData.minFinalAmount || Zero, } } diff --git a/packages/sdk-router/src/typechain/SynapseIntentRouter.ts b/packages/sdk-router/src/typechain/SynapseIntentRouter.ts index 00fdd62974..d4ecd9113d 100644 --- a/packages/sdk-router/src/typechain/SynapseIntentRouter.ts +++ b/packages/sdk-router/src/typechain/SynapseIntentRouter.ts @@ -46,8 +46,8 @@ export declare namespace ISynapseIntentRouter { export interface SynapseIntentRouterInterface extends utils.Interface { functions: { 'NATIVE_GAS_TOKEN()': FunctionFragment - 'completeIntent(address,uint256,uint256,uint256,(address,uint256,uint256,bytes)[])': FunctionFragment - 'completeIntentWithBalanceChecks(address,uint256,uint256,uint256,(address,uint256,uint256,bytes)[])': FunctionFragment + 'completeIntent(address,uint256,uint256,(address,uint256,uint256,bytes)[])': FunctionFragment + 'completeIntentWithBalanceChecks(address,uint256,uint256,(address,uint256,uint256,bytes)[])': FunctionFragment } getFunction( @@ -67,7 +67,6 @@ export interface SynapseIntentRouterInterface extends utils.Interface { string, BigNumberish, BigNumberish, - BigNumberish, ISynapseIntentRouter.StepParamsStruct[] ] ): string @@ -77,7 +76,6 @@ export interface SynapseIntentRouterInterface extends utils.Interface { string, BigNumberish, BigNumberish, - BigNumberish, ISynapseIntentRouter.StepParamsStruct[] ] ): string @@ -130,7 +128,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntent( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string } @@ -139,7 +136,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntentWithBalanceChecks( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string } @@ -151,7 +147,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntent( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string } @@ -160,7 +155,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntentWithBalanceChecks( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string } @@ -172,7 +166,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntent( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: CallOverrides @@ -181,7 +174,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntentWithBalanceChecks( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: CallOverrides @@ -196,7 +188,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntent( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string } @@ -205,7 +196,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntentWithBalanceChecks( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string } @@ -218,7 +208,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntent( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string } @@ -227,7 +216,6 @@ export interface SynapseIntentRouter extends BaseContract { completeIntentWithBalanceChecks( zapRecipient: string, amountIn: BigNumberish, - minLastStepAmountIn: BigNumberish, deadline: BigNumberish, steps: ISynapseIntentRouter.StepParamsStruct[], overrides?: PayableOverrides & { from?: string }