From f20891af38a6185cc3d703eefc79332ea73827cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Brand=C3=A3o?= Date: Tue, 12 Dec 2023 21:54:41 +0000 Subject: [PATCH] Several adjustments for multichain --- core/src/asset-strategies/TokenStrategy.ts | 16 +++++++---- .../asset-strategies/asset-type-strategies.ts | 28 ++++++++----------- core/src/external-apis/paraswap.ts | 4 ++- core/src/external-apis/zero-x.ts | 10 ++++++- core/src/path/apis/paraswap-full.ts | 11 ++++++-- core/src/path/apis/zerox-full.ts | 11 +++++++- .../asset-type-strategies-helpers.ts | 7 ++--- core/src/transaction/generate-steps.ts | 2 +- core/tests/protocols/savingsDai.spec.ts | 15 ++++++---- core/tests/protocols/utils.ts | 5 +++- data/assets/1/savingsDaiDeposit.json | 2 +- 11 files changed, 72 insertions(+), 39 deletions(-) diff --git a/core/src/asset-strategies/TokenStrategy.ts b/core/src/asset-strategies/TokenStrategy.ts index e50ea1e..dbfa81a 100644 --- a/core/src/asset-strategies/TokenStrategy.ts +++ b/core/src/asset-strategies/TokenStrategy.ts @@ -1,9 +1,6 @@ import { getParaswapPrice } from "../external-apis/paraswap"; import { RequestTree } from "../transaction/get-prices-and-linked-assets"; -import { - USDC_ADDRESS, - SELL_AMOUNT, -} from "../transaction/asset-type-strategies-helpers"; +import { SELL_AMOUNT } from "../transaction/asset-type-strategies-helpers"; import { FetchPriceDataParams, GetPriceParams, @@ -13,7 +10,16 @@ import { InterfaceStrategy } from "./InterfaceStrategy"; export class TokenStrategy extends InterfaceStrategy { fetchPriceData({ provider, assetStore, asset }: FetchPriceDataParams) { - const sellToken = USDC_ADDRESS; + let usdcAddress; + if (asset.chainId === 1) { + usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; + } else if (asset.chainId === 137) { + usdcAddress = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; + } else { + throw new Error("Unsupported chainId"); + } + + const sellToken = usdcAddress; const buyToken = asset.address; const sellAmount = SELL_AMOUNT; diff --git a/core/src/asset-strategies/asset-type-strategies.ts b/core/src/asset-strategies/asset-type-strategies.ts index 792c368..982cf20 100644 --- a/core/src/asset-strategies/asset-type-strategies.ts +++ b/core/src/asset-strategies/asset-type-strategies.ts @@ -13,21 +13,17 @@ import { SavingsDaiDepositStrategy } from "./SavingsDaiDepositStrategy"; import { CompoundDepositStrategy } from "./CompoundV3DepositStrategy"; export const assetTypeStrategies: { - [chainId: number]: { - [interfaceName in AssetType]: InterfaceStrategy; - }; + [interfaceName in AssetType]: InterfaceStrategy; } = { - 137: { - token: new TokenStrategy(), - networkToken: new NetworkTokenStrategy(), - balancerDeposit: new BalancerDepositStrategy(), - beefyDeposit: new BeefyDepositStrategy(), - uniswapV2Liquidity: new UniswapV2LiquidityStrategy(), - gammaDeposit: new GammaDepositStrategy(), - aaveV2Deposit: new AaveV2DepositStrategy(), - aaveV3Deposit: new AaveV3DepositStrategy(), - compoundV3Deposit: new CompoundDepositStrategy(), - stargateDeposit: new StargateDepositStrategy(), - savingsDaiDeposit: new SavingsDaiDepositStrategy(), - }, + token: new TokenStrategy(), + networkToken: new NetworkTokenStrategy(), + balancerDeposit: new BalancerDepositStrategy(), + beefyDeposit: new BeefyDepositStrategy(), + uniswapV2Liquidity: new UniswapV2LiquidityStrategy(), + gammaDeposit: new GammaDepositStrategy(), + aaveV2Deposit: new AaveV2DepositStrategy(), + aaveV3Deposit: new AaveV3DepositStrategy(), + compoundV3Deposit: new CompoundDepositStrategy(), + stargateDeposit: new StargateDepositStrategy(), + savingsDaiDeposit: new SavingsDaiDepositStrategy(), }; diff --git a/core/src/external-apis/paraswap.ts b/core/src/external-apis/paraswap.ts index 9448398..bd12e3d 100644 --- a/core/src/external-apis/paraswap.ts +++ b/core/src/external-apis/paraswap.ts @@ -13,7 +13,9 @@ async function callParaswapFullAPI({ sellToken: string; sellAmount: string; }): Promise { - const url = `https://apiv5.paraswap.io/prices/?destDecimals=${buyToken.decimals}&srcToken=${sellToken}&destToken=${buyToken.address}&amount=${sellAmount}&side=SELL&network=137`; + const chainId = buyToken.chainId; + + const url = `https://apiv5.paraswap.io/prices/?destDecimals=${buyToken.decimals}&srcToken=${sellToken}&destToken=${buyToken.address}&amount=${sellAmount}&side=SELL&network=${chainId}`; const req = await fetch(url); const reqClone = req.clone(); // workaround to avoid response already read error diff --git a/core/src/external-apis/zero-x.ts b/core/src/external-apis/zero-x.ts index eaae61f..2c7a9cf 100644 --- a/core/src/external-apis/zero-x.ts +++ b/core/src/external-apis/zero-x.ts @@ -46,7 +46,15 @@ async function call0XAPIUnfiltered({ sellToken, sellAmount, }: ParamsAPI): Promise { - const url = `https://polygon.api.0x.org/swap/v1/quote?buyToken=${buyToken.address}&sellToken=${sellToken.address}&sellAmount=${sellAmount}`; + let baseUrl; + if (buyToken.chainId === 1) { + baseUrl = "https://api.0x.org"; + } else if (buyToken.chainId === 137) { + baseUrl = "https://polygon.api.0x.org"; + } else { + throw new Error("Unsupported chainId"); + } + const url = `${baseUrl}/swap/v1/quote?buyToken=${buyToken.address}&sellToken=${sellToken.address}&sellAmount=${sellAmount}`; const req = await fetch(url); const data = (await req.json()) as Order0X; console.debug( diff --git a/core/src/path/apis/paraswap-full.ts b/core/src/path/apis/paraswap-full.ts index ae1b777..f95e288 100644 --- a/core/src/path/apis/paraswap-full.ts +++ b/core/src/path/apis/paraswap-full.ts @@ -15,7 +15,9 @@ async function callParaswapFullAPI({ sellAmount, exchangeNames, }: ParamsAPI): Promise { - const url = `https://apiv5.paraswap.io/prices/?srcToken=${sellToken.address}&srcDecimals=${sellToken.decimals}&destToken=${buyToken.address}&destDecimals=${buyToken.decimals}&amount=${sellAmount}&side=SELL&network=137&includeContractMethods=MegaSwap,MultiSwap`; + const chainId = buyToken.chainId; + + const url = `https://apiv5.paraswap.io/prices/?srcToken=${sellToken.address}&srcDecimals=${sellToken.decimals}&destToken=${buyToken.address}&destDecimals=${buyToken.decimals}&amount=${sellAmount}&side=SELL&network=${chainId}&includeContractMethods=MegaSwap,MultiSwap`; const req = await fetch(url); @@ -30,10 +32,12 @@ async function callParaswapFullAPI({ // ParaswapTxAPIParams has one field, which is body of type any interface ParaswapTxAPIParams { body: any; + chainId: number; } async function callParaswapFullTxAPI({ body, + chainId, }: ParaswapTxAPIParams): Promise { const finalBody = { network: body.network, @@ -48,8 +52,7 @@ async function callParaswapFullTxAPI({ userAddress: "0xee13C86EE4eb1EC3a05E2cc3AB70576F31666b3b", }; - const transactionsUrl = - "https://apiv5.paraswap.io/transactions/137?ignoreChecks=true"; + const transactionsUrl = `https://apiv5.paraswap.io/transactions/${chainId}?ignoreChecks=true`; const req = await fetch(transactionsUrl, { method: "POST", @@ -73,11 +76,13 @@ export async function getParaswapFullData( throw new Error(`Paraswap API returned an error: ${JSON.stringify(data)}`); } + const chainId = buyToken.chainId; const paraswapAddress = data.priceRoute.contractAddress; const approveAddress = data.priceRoute.tokenTransferProxy; const txData = await useLimiter(limiterParaswap, callParaswapFullTxAPI, { body: data.priceRoute, + chainId, }); return [ diff --git a/core/src/path/apis/zerox-full.ts b/core/src/path/apis/zerox-full.ts index b4f3556..8958a91 100644 --- a/core/src/path/apis/zerox-full.ts +++ b/core/src/path/apis/zerox-full.ts @@ -16,8 +16,17 @@ async function callZeroXFullAPI({ sellAmount, exchangeNames, }: ParamsAPI): Promise { + let baseUrl; + if (buyToken.chainId === 1) { + baseUrl = "https://api.0x.org"; + } else if (buyToken.chainId === 137) { + baseUrl = "https://polygon.api.0x.org"; + } else { + throw new Error("Unsupported chainId"); + } + const adjustedSellAmount = adjustAmount(sellAmount); - const url = `https://polygon.api.0x.org/swap/v1/quote?buyToken=${buyToken.address}&sellToken=${sellToken.address}&sellAmount=${adjustedSellAmount}`; + const url = `${baseUrl}/swap/v1/quote?buyToken=${buyToken.address}&sellToken=${sellToken.address}&sellAmount=${adjustedSellAmount}`; const req = await fetch(url, { headers: { diff --git a/core/src/transaction/asset-type-strategies-helpers.ts b/core/src/transaction/asset-type-strategies-helpers.ts index d88e855..e8ab66d 100644 --- a/core/src/transaction/asset-type-strategies-helpers.ts +++ b/core/src/transaction/asset-type-strategies-helpers.ts @@ -4,7 +4,6 @@ import { Provider } from "ethers"; import { assetTypeStrategies } from "../asset-strategies/asset-type-strategies"; import { BigNumberish, formatUnits } from "ethers"; -export const USDC_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; export const SELL_AMOUNT = "25000000"; // 25 USD export function getAmount({ @@ -26,7 +25,7 @@ export function getPrice({ asset: Asset; requestTree: RequestTree; }): number { - const strategy = assetTypeStrategies[asset.chainId][asset.type]; + const strategy = assetTypeStrategies[asset.type]; if (strategy === null || strategy === undefined) { throw new Error( @@ -55,7 +54,7 @@ export function getLinkedAssets({ asset: Asset; requestTree: RequestTree; }): LinkedAsset[] { - const strategy = assetTypeStrategies[asset.chainId][asset.type]; + const strategy = assetTypeStrategies[asset.type]; if (strategy === null || strategy === undefined) { throw new Error( @@ -93,7 +92,7 @@ export function fetchPriceData({ `fetchData: invalid chainId or type for asset ID ${asset?.id}` ); } - const strategy = assetTypeStrategies[asset.chainId][asset.type]; + const strategy = assetTypeStrategies[asset.type]; if (strategy === null || strategy === undefined) { throw new Error( diff --git a/core/src/transaction/generate-steps.ts b/core/src/transaction/generate-steps.ts index 53c0923..cd428cc 100644 --- a/core/src/transaction/generate-steps.ts +++ b/core/src/transaction/generate-steps.ts @@ -64,7 +64,7 @@ async function processBridges({ const assetAllocation = currentLayer[assetId]; - output = await assetTypeStrategies[chainId][asset.type].generateStep({ + output = await assetTypeStrategies[asset.type].generateStep({ chainId, provider, walletAddress, diff --git a/core/tests/protocols/savingsDai.spec.ts b/core/tests/protocols/savingsDai.spec.ts index 41a871d..372acac 100644 --- a/core/tests/protocols/savingsDai.spec.ts +++ b/core/tests/protocols/savingsDai.spec.ts @@ -1,22 +1,26 @@ import { test } from "vitest"; import { simulateRouterOperationHelper } from "./utils"; +import { JsonRpcProvider } from "ethers"; -test.skip("generateTransaction: USDC to SDAI (savingsDai)", async () => { +test.skip("generateTransaction: DAI to SDAI (savingsDai)", async () => { + const provider = new JsonRpcProvider("<<>>"); await simulateRouterOperationHelper({ chainId: 1, inputAllocation: [ { - assetId: "ed46f991-d225-491d-b2b2-91f89da016d2", - amountStr: "1000000000", + assetId: "a80c67f9-5ba9-4c05-b5b1-511836b38454", + amountStr: "1000000000000000000000", }, ], + provider, outputAllocation: [ { assetId: "8bd1bd78-4938-4204-a945-fa63f57c642b", fraction: 1.0 }, ], }); }); -test.skip("generateTransaction: SDAI (savingsDai) to USDC", async () => { +test.skip("generateTransaction: SDAI (savingsDai) to DAI", async () => { + const provider = new JsonRpcProvider("<<>>"); await simulateRouterOperationHelper({ chainId: 1, inputAllocation: [ @@ -25,8 +29,9 @@ test.skip("generateTransaction: SDAI (savingsDai) to USDC", async () => { amountStr: "1000000000000000000000", }, ], + provider, outputAllocation: [ - { assetId: "ed46f991-d225-491d-b2b2-91f89da016d2", fraction: 1.0 }, + { assetId: "a80c67f9-5ba9-4c05-b5b1-511836b38454", fraction: 1.0 }, ], }); }); diff --git a/core/tests/protocols/utils.ts b/core/tests/protocols/utils.ts index 38ebaea..bbe1145 100644 --- a/core/tests/protocols/utils.ts +++ b/core/tests/protocols/utils.ts @@ -5,19 +5,22 @@ import { FractionAllocation, } from "core/src/transaction/types"; import { getProvider } from "core/src/utils/get-provider"; +import { Provider } from "ethers"; export async function simulateRouterOperationHelper({ chainId, + provider, inputAllocation, outputAllocation, }: { chainId: number; + provider?: Provider; inputAllocation: AbsoluteAllocation; outputAllocation: FractionAllocation; }) { const assetStore = new AssetStore(); const config = await loadConfig(); - const provider = await getProvider({ chainId: 137 }); + const actualProvider = provider ? provider : await getProvider({ chainId }); const routerOperation = await generateTransaction({ inputAllocation, diff --git a/data/assets/1/savingsDaiDeposit.json b/data/assets/1/savingsDaiDeposit.json index e272980..e5e0c78 100644 --- a/data/assets/1/savingsDaiDeposit.json +++ b/data/assets/1/savingsDaiDeposit.json @@ -15,7 +15,7 @@ "balanceSlot": 1, "linkedAssets": [ { - "assetId": "389d84c3-9095-4b00-b01a-18948b9ada39", + "assetId": "a80c67f9-5ba9-4c05-b5b1-511836b38454", "fraction": 1 } ]