Skip to content

Commit

Permalink
Merge branch 'feat/sdk-syn-intents' into staging/syn-intents
Browse files Browse the repository at this point in the history
  • Loading branch information
ChiTimesChi committed Jan 2, 2025
2 parents b62687c + 1eaf140 commit 6068604
Show file tree
Hide file tree
Showing 13 changed files with 164 additions and 91 deletions.
2 changes: 1 addition & 1 deletion packages/sdk-router/src/abi/SynapseIntentPreviewer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [] },
Expand Down
11 changes: 0 additions & 11 deletions packages/sdk-router/src/abi/SynapseIntentRouter.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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": [] },
Expand Down
6 changes: 3 additions & 3 deletions packages/sdk-router/src/constants/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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(
Expand Down
1 change: 1 addition & 0 deletions packages/sdk-router/src/rfq/engine/defaultEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class DefaultEngine implements SwapEngine {
}

public async getQuote(input: RouteInput): Promise<SwapEngineRoute> {
// TODO: timeout
const { chainId, tokenIn, tokenOut, amountIn, finalRecipient } = input
const { previewer, swapQuoter } = this.contracts[chainId]
if (
Expand Down
31 changes: 23 additions & 8 deletions packages/sdk-router/src/rfq/engine/engineSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ import {
RouteInput,
SwapEngineQuote,
sanitizeMultiStepQuote,
sanitizeMultiStepRoute,
} from './swapEngine'
import { CCTPRouterQuery } from '../../module'
import { encodeStepParams } from '../steps'
import { OdosEngine } from './odosEngine'

export enum EngineTimeout {
Short = 1000,
Long = 3000,
}

export class EngineSet {
private engines: {
[engineID: number]: SwapEngine
Expand Down Expand Up @@ -43,7 +49,7 @@ export class EngineSet {

public async getQuotes(
inputs: RouteInput[],
options: { allowMultiStep: boolean }
options: { allowMultiStep: boolean; timeout?: number }
): Promise<SwapEngineQuote[]> {
// Find the quote for each input and each engine.
const allQuotes = await Promise.all(
Expand All @@ -62,16 +68,23 @@ export class EngineSet {
public async getQuote(
engineID: number,
input: RouteInput,
options: { allowMultiStep: boolean }
options: { allowMultiStep: boolean; timeout?: number }
): Promise<SwapEngineQuote> {
return this._getQuote(this._getEngine(engineID), input, options)
}

public async generateRoute(
input: RouteInput,
quote: SwapEngineQuote
quote: SwapEngineQuote,
options: { allowMultiStep: boolean; timeout?: number }
): Promise<SwapEngineRoute> {
return this._getEngine(quote.engineID).generateRoute(input, quote)
// Use longer timeout for route generation by default.
const route = await this._getEngine(quote.engineID).generateRoute(
input,
quote,
options.timeout ?? EngineTimeout.Long
)
return options.allowMultiStep ? route : sanitizeMultiStepRoute(route)
}

public getOriginQuery(
Expand Down Expand Up @@ -115,11 +128,13 @@ export class EngineSet {
private async _getQuote(
engine: SwapEngine,
input: RouteInput,
options: {
allowMultiStep: boolean
}
options: { allowMultiStep: boolean; timeout?: number }
): Promise<SwapEngineQuote> {
const quote = await engine.getQuote(input)
// Use shorter timeout for quote fetching by default.
const quote = await engine.getQuote(
input,
options.timeout ?? EngineTimeout.Short
)
return options.allowMultiStep ? quote : sanitizeMultiStepQuote(quote)
}

Expand Down
30 changes: 18 additions & 12 deletions packages/sdk-router/src/rfq/engine/odosEngine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,28 @@ global.fetch = require('node-fetch')
describe('Integration test: ParaSwapEngine', () => {
it('Ethereum USDC -> USDT', async () => {
const odosEngine = new OdosEngine({})
let response = await odosEngine.getQuoteResponse({
chainId: 1,
inputTokens: [{ amount: '1000000000', tokenAddress: ETH_USDC }],
outputTokens: [{ proportion: 1, tokenAddress: ETH_USDT }],
userAddr: USER_SIMULATED_ADDRESS,
slippageLimitPercent: 1,
simple: true,
})
let response = await odosEngine.getQuoteResponse(
{
chainId: 1,
inputTokens: [{ amount: '1000000000', tokenAddress: ETH_USDC }],
outputTokens: [{ proportion: 1, tokenAddress: ETH_USDT }],
userAddr: USER_SIMULATED_ADDRESS,
slippageLimitPercent: 1,
simple: true,
},
2000
)
expect(response).not.toBeNull()
const quoteResponse: OdosQuoteResponse = await response?.json()
console.log(JSON.stringify(quoteResponse, null, 2))

response = await odosEngine.getAssembleResponse({
userAddr: USER_SIMULATED_ADDRESS,
pathId: quoteResponse.pathId,
})
response = await odosEngine.getAssembleResponse(
{
userAddr: USER_SIMULATED_ADDRESS,
pathId: quoteResponse.pathId,
},
2000
)
expect(response).not.toBeNull()
const assembleResponse = await response?.json()
console.log(JSON.stringify(assembleResponse, null, 2))
Expand Down
36 changes: 17 additions & 19 deletions packages/sdk-router/src/rfq/engine/odosEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { logger } from '../../utils/logger'
import { isNativeToken } from '../../utils/handleNativeToken'

const ODOS_API_URL = 'https://api.odos.xyz/sor'
const ODOS_API_TIMEOUT = 2000

type OdosQuoteRequest = {
chainId: number
Expand Down Expand Up @@ -82,7 +81,10 @@ export class OdosEngine implements SwapEngine {
this.tokenZapAddressMap = tokenZapAddressMap
}

public async getQuote(input: RouteInput): Promise<OdosQuote> {
public async getQuote(
input: RouteInput,
timeout: number
): Promise<OdosQuote> {
const { chainId, tokenIn, tokenOut, amountIn } = input
const tokenZap = this.tokenZapAddressMap[chainId]
if (
Expand Down Expand Up @@ -111,7 +113,7 @@ export class OdosEngine implements SwapEngine {
slippageLimitPercent: toPercentFloat(SlippageMax),
simple: true,
}
const response = await this.getQuoteResponse(request)
const response = await this.getQuoteResponse(request, timeout)
if (!response) {
return EmptyOdosQuote
}
Expand Down Expand Up @@ -144,13 +146,17 @@ export class OdosEngine implements SwapEngine {

public async generateRoute(
input: RouteInput,
quote: OdosQuote
quote: OdosQuote,
timeout: number
): Promise<SwapEngineRoute> {
if (quote.engineID !== this.id || !quote.assembleRequest) {
logger.error({ quote }, 'Odos: unexpected quote')
return getEmptyRoute(this.id)
}
const response = await this.getAssembleResponse(quote.assembleRequest)
const response = await this.getAssembleResponse(
quote.assembleRequest,
timeout
)
if (!response) {
return getEmptyRoute(this.id)
}
Expand All @@ -169,25 +175,17 @@ export class OdosEngine implements SwapEngine {
}

public async getAssembleResponse(
params: OdosAssembleRequest
params: OdosAssembleRequest,
timeout: number
): Promise<Response | null> {
return postWithTimeout(
'Odos',
`${ODOS_API_URL}/assemble`,
ODOS_API_TIMEOUT,
params
)
return postWithTimeout('Odos', `${ODOS_API_URL}/assemble`, timeout, params)
}

public async getQuoteResponse(
params: OdosQuoteRequest
params: OdosQuoteRequest,
timeout: number
): Promise<Response | null> {
return postWithTimeout(
'Odos',
`${ODOS_API_URL}/quote/v2`,
ODOS_API_TIMEOUT,
params
)
return postWithTimeout('Odos', `${ODOS_API_URL}/quote/v2`, timeout, params)
}

private handleNativeToken(token: string): string {
Expand Down
5 changes: 3 additions & 2 deletions packages/sdk-router/src/rfq/engine/swapEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ export interface SwapEngine {
* Some of the engines may not be able to generate the route steps at the same time,
* use the `generateRoute` method to generate the steps.
*/
getQuote(input: RouteInput): Promise<SwapEngineQuote>
getQuote(input: RouteInput, timeout: number): Promise<SwapEngineQuote>

/**
* Generates the route steps from the quote obtained from the `getQuote` method.
*/
generateRoute(
input: RouteInput,
quote: SwapEngineQuote
quote: SwapEngineQuote,
timeout: number
): Promise<SwapEngineRoute>
}

Expand Down
27 changes: 25 additions & 2 deletions packages/sdk-router/src/rfq/sir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class SynapseIntentRouter implements SynapseModule {
): Promise<PopulatedTransaction> {
// 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)) {
Expand All @@ -104,7 +104,6 @@ export class SynapseIntentRouter implements SynapseModule {
await this.sirContract.populateTransaction.completeIntentWithBalanceChecks(
this.tokenZapAddress,
amount,
originQuery.minAmountOut,
originQuery.deadline,
steps
)
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 6068604

Please sign in to comment.