From 446dcb2464452eddca0428184849914c7aae1f61 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 2 May 2024 12:30:57 +0100 Subject: [PATCH 01/31] chore: initial commit --- src/dex/sdai/config.ts | 24 ++++ src/dex/sdai/constants.ts | 1 + src/dex/sdai/sdai-e2e.test.ts | 93 ++++++++++++++++ src/dex/sdai/sdai-integration.test.ts | 23 ++++ src/dex/sdai/sdai.ts | 152 ++++++++++++++++++++++++++ src/dex/sdai/types.ts | 13 +++ 6 files changed, 306 insertions(+) create mode 100644 src/dex/sdai/config.ts create mode 100644 src/dex/sdai/constants.ts create mode 100644 src/dex/sdai/sdai-e2e.test.ts create mode 100644 src/dex/sdai/sdai-integration.test.ts create mode 100644 src/dex/sdai/sdai.ts create mode 100644 src/dex/sdai/types.ts diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts new file mode 100644 index 000000000..f39f84eca --- /dev/null +++ b/src/dex/sdai/config.ts @@ -0,0 +1,24 @@ +import { DexParams } from './types'; +import { DexConfigMap } from '../../types'; +import { Network } from '../../constants'; +import { SwapSide } from '@paraswap/core'; + +export const SDaiConfig: DexConfigMap = { + SDai: { + [Network.MAINNET]: { + sdaiAddress: '0x83f20f44975d03b1b09e64809b757c47f942beea', + daiAddress: '0x0', + }, + }, +}; + +export const Adapters: { + [chainId: number]: { + [side: string]: { name: string; index: number }[]; + }; +} = { + [Network.MAINNET]: { + [SwapSide.SELL]: [{ name: 'Adapter04', index: 4 }], + [SwapSide.BUY]: [{ name: 'BuyAdapter', index: 11 }], + }, +}; diff --git a/src/dex/sdai/constants.ts b/src/dex/sdai/constants.ts new file mode 100644 index 000000000..070069152 --- /dev/null +++ b/src/dex/sdai/constants.ts @@ -0,0 +1 @@ +export const SDAI_GAS_COST = 90_000; diff --git a/src/dex/sdai/sdai-e2e.test.ts b/src/dex/sdai/sdai-e2e.test.ts new file mode 100644 index 000000000..e9232f376 --- /dev/null +++ b/src/dex/sdai/sdai-e2e.test.ts @@ -0,0 +1,93 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +import { ContractMethod, Network, SwapSide } from '../../constants'; +import { StaticJsonRpcProvider } from '@ethersproject/providers'; +import { generateConfig } from '../../config'; +import { Holders, Tokens } from '../../../tests/constants-e2e'; +import { testE2E } from '../../../tests/utils-e2e'; + +function testForNetwork( + network: Network, + dexKey: string, + tokenASymbol: string, + tokenBSymbol: string, + tokenAAmount: string, + tokenBAmount: string, +) { + const provider = new StaticJsonRpcProvider( + generateConfig(network).privateHttpProvider, + network, + ); + const tokens = Tokens[network]; + const holders = Holders[network]; + + const sideToContractMethods = new Map([ + [ + SwapSide.SELL, + [ + ContractMethod.simpleSwap, + ContractMethod.multiSwap, + ContractMethod.megaSwap, + ], + ], + [SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]], + ]); + + describe(`${network}`, () => { + sideToContractMethods.forEach((contractMethods, side) => + describe(`${side}`, () => { + contractMethods.forEach((contractMethod: ContractMethod) => { + describe(`${contractMethod}`, () => { + it(`${tokenASymbol} -> ${tokenBSymbol}`, async () => { + await testE2E( + tokens[tokenASymbol], + tokens[tokenBSymbol], + holders[tokenASymbol], + side === SwapSide.SELL ? tokenAAmount : tokenBAmount, + side, + dexKey, + contractMethod, + network, + provider, + ); + }); + it(`${tokenBSymbol} -> ${tokenASymbol}`, async () => { + await testE2E( + tokens[tokenBSymbol], + tokens[tokenASymbol], + holders[tokenBSymbol], + side === SwapSide.SELL ? tokenBAmount : tokenAAmount, + side, + dexKey, + contractMethod, + network, + provider, + ); + }); + }); + }); + }), + ); + }); +} + +describe('SDai E2E', () => { + const dexKey = 'SDai'; + + describe('Mainnet', () => { + // const network = Network.MAINNET; + // const tokenASymbol: string = 'POL'; + // const tokenBSymbol: string = 'MATIC'; + // const tokenAAmount: string = '10000000000000000000'; + // const tokenBAmount: string = '20000000000000000000000'; + // testForNetwork( + // network, + // dexKey, + // tokenASymbol, + // tokenBSymbol, + // tokenAAmount, + // tokenBAmount, + // ); + }); +}); diff --git a/src/dex/sdai/sdai-integration.test.ts b/src/dex/sdai/sdai-integration.test.ts new file mode 100644 index 000000000..96af640e5 --- /dev/null +++ b/src/dex/sdai/sdai-integration.test.ts @@ -0,0 +1,23 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +import { DummyDexHelper } from '../../dex-helper/index'; +import { Network, SwapSide } from '../../constants'; +import { checkConstantPoolPrices } from '../../../tests/utils'; +import { Tokens } from '../../../tests/constants-e2e'; +import { BI_POWS } from '../../bigint-constants'; +import { SDai } from './sdai'; + +const network = Network.MAINNET; + +const SDaiSymbol = 'sDAI'; +const SDaiToken = Tokens[network][SDaiSymbol]; + +const DaiSymbol = 'DAI'; +const DaiToken = Tokens[network][DaiSymbol]; + +const amounts = [0n, BI_POWS[18], 2000000000000000000n]; + +const dexKey = 'SDai'; + +describe('SDai', function () {}); diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts new file mode 100644 index 000000000..ecd8806e1 --- /dev/null +++ b/src/dex/sdai/sdai.ts @@ -0,0 +1,152 @@ +import { SimpleExchange } from '../simple-exchange'; +import { IDex } from '../idex'; +import { DexParams, SDaiData, SDaiFunctions } from './types'; +import { Network, SwapSide } from '../../constants'; +import { getDexKeysWithNetwork } from '../../utils'; +import { Adapters, SDaiConfig } from './config'; +import { + AdapterExchangeParam, + Address, + ExchangePrices, + Logger, + PoolLiquidity, + PoolPrices, + SimpleExchangeParam, + Token, +} from '../../types'; +import { IDexHelper } from '../../dex-helper'; +import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; +import { BI_POWS } from '../../bigint-constants'; +import { SDAI_GAS_COST } from './constants'; +import PolygonMigrationAbi from '../../abi/polygon-migration/PolygonMigration.abi.json'; +import { Interface } from 'ethers/lib/utils'; + +export class SDai extends SimpleExchange implements IDex { + readonly hasConstantPriceLargeAmounts = true; + readonly isFeeOnTransferSupported = false; + + public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = + getDexKeysWithNetwork(SDaiConfig); + + logger: Logger; + + constructor( + protected network: Network, + dexKey: string, + protected dexHelper: IDexHelper, + readonly sdaiAddress: string = SDaiConfig[dexKey][network].sdaiAddress, + readonly daiAddress: string = SDaiConfig[dexKey][network].daiAddress, + protected unitPrice = BI_POWS[18], + protected adapters = Adapters[network] || {}, + protected migratorInterface = new Interface(PolygonMigrationAbi), + ) { + super(dexHelper, dexKey); + this.logger = dexHelper.getLogger(dexKey); + } + + getAdapters(side: SwapSide): { name: string; index: number }[] | null { + return this.adapters[side] || null; + } + + isSDai(tokenAddress: Address) { + return this.sdaiAddress.toLowerCase() === tokenAddress.toLowerCase(); + } + + isDai(tokenAddress: Address) { + return this.daiAddress.toLowerCase() === tokenAddress.toLowerCase(); + } + + isAppropriatePair(srcToken: Token, destToken: Token) { + return ( + (this.isDai(srcToken.address) && this.isSDai(destToken.address)) || + (this.isDai(destToken.address) && this.isSDai(srcToken.address)) + ); + } + + async getPoolIdentifiers( + srcToken: Token, + destToken: Token, + side: SwapSide, + blockNumber: number, + ): Promise { + if (this.isAppropriatePair(srcToken, destToken)) { + return [`${this.dexKey}_${srcToken.address}_${destToken.address}`]; + } + + return []; + } + + async getPricesVolume( + srcToken: Token, + destToken: Token, + amounts: bigint[], + side: SwapSide, + blockNumber: number, + limitPools?: string[], + ): Promise> { + if (!this.isAppropriatePair(srcToken, destToken)) { + return null; + } + + return [ + { + prices: amounts, + unit: this.unitPrice, + gasCost: SDAI_GAS_COST, + exchange: this.dexKey, + poolAddresses: [this.sdaiAddress], + data: null, + }, + ]; + } + + // Returns estimated gas cost of calldata for this DEX in multiSwap + getCalldataGasCost(poolPrices: PoolPrices): number | number[] { + return CALLDATA_GAS_COST.DEX_NO_PAYLOAD; + } + + getAdapterParam( + srcToken: string, + destToken: string, + srcAmount: string, + destAmount: string, + data: SDaiData, + side: SwapSide, + ): AdapterExchangeParam { + return { + targetExchange: this.sdaiAddress, + payload: '0x', + networkFee: '0', + }; + } + + async getSimpleParam( + srcToken: string, + destToken: string, + srcAmount: string, + destAmount: string, + data: SDaiData, + side: SwapSide, + ): Promise { + const swapData = this.migratorInterface.encodeFunctionData( + this.isDai(srcToken) ? SDaiFunctions.deposit : SDaiFunctions.redeem, + [srcAmount], + ); + + return this.buildSimpleParamWithoutWETHConversion( + srcToken, + srcAmount, + destToken, + destAmount, + swapData, + this.sdaiAddress, + ); + } + + async getTopPoolsForToken( + tokenAddress: Address, + limit: number, + ): Promise { + return []; + } +} diff --git a/src/dex/sdai/types.ts b/src/dex/sdai/types.ts new file mode 100644 index 000000000..45b9a037b --- /dev/null +++ b/src/dex/sdai/types.ts @@ -0,0 +1,13 @@ +import { Address } from '../../types'; + +export type SDaiData = null; + +export type DexParams = { + sdaiAddress: Address; + daiAddress: Address; +}; + +export enum SDaiFunctions { + deposit = 'deposit', + redeem = 'redeem', +} From a55fd9d37168a95c85be0d725bb8445ab5ddf016 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 2 May 2024 18:11:03 +0100 Subject: [PATCH 02/31] feat: added SavingsDai abi, test setup --- src/abi/sdai/SavingsDai.abi.json | 877 ++++++++++++++++++++++++++ src/dex/sdai/config.ts | 4 +- src/dex/sdai/constants.ts | 3 +- src/dex/sdai/sdai-e2e.test.ts | 27 +- src/dex/sdai/sdai-integration.test.ts | 118 +++- src/dex/sdai/sdai.ts | 54 +- 6 files changed, 1042 insertions(+), 41 deletions(-) create mode 100644 src/abi/sdai/SavingsDai.abi.json diff --git a/src/abi/sdai/SavingsDai.abi.json b/src/abi/sdai/SavingsDai.abi.json new file mode 100644 index 000000000..94a9a46e0 --- /dev/null +++ b/src/abi/sdai/SavingsDai.abi.json @@ -0,0 +1,877 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_daiJoin", + "type": "address" + }, + { + "internalType": "address", + "name": "_pot", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "asset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "convertToAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "name": "convertToShares", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "dai", + "outputs": [ + { + "internalType": "contract DaiLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "daiJoin", + "outputs": [ + { + "internalType": "contract DaiJoinLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "deploymentChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "deposit", + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "maxDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "maxMint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "maxRedeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "maxWithdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pot", + "outputs": [ + { + "internalType": "contract PotLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "name": "previewDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "previewMint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "previewRedeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "name": "previewWithdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "redeem", + "outputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vat", + "outputs": [ + { + "internalType": "contract VatLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index f39f84eca..6fccb654e 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -6,8 +6,8 @@ import { SwapSide } from '@paraswap/core'; export const SDaiConfig: DexConfigMap = { SDai: { [Network.MAINNET]: { - sdaiAddress: '0x83f20f44975d03b1b09e64809b757c47f942beea', - daiAddress: '0x0', + sdaiAddress: '0x83F20F44975D03b1b09e64809B757c47f942BEeA', + daiAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F', }, }, }; diff --git a/src/dex/sdai/constants.ts b/src/dex/sdai/constants.ts index 070069152..44dc895f5 100644 --- a/src/dex/sdai/constants.ts +++ b/src/dex/sdai/constants.ts @@ -1 +1,2 @@ -export const SDAI_GAS_COST = 90_000; +export const SDAI_DEPOSIT_GAS_COST = 90_000; +export const SDAI_REDEEM_GAS_COST = 90_000; diff --git a/src/dex/sdai/sdai-e2e.test.ts b/src/dex/sdai/sdai-e2e.test.ts index e9232f376..9f913afb3 100644 --- a/src/dex/sdai/sdai-e2e.test.ts +++ b/src/dex/sdai/sdai-e2e.test.ts @@ -76,18 +76,19 @@ describe('SDai E2E', () => { const dexKey = 'SDai'; describe('Mainnet', () => { - // const network = Network.MAINNET; - // const tokenASymbol: string = 'POL'; - // const tokenBSymbol: string = 'MATIC'; - // const tokenAAmount: string = '10000000000000000000'; - // const tokenBAmount: string = '20000000000000000000000'; - // testForNetwork( - // network, - // dexKey, - // tokenASymbol, - // tokenBSymbol, - // tokenAAmount, - // tokenBAmount, - // ); + const network = Network.MAINNET; + const tokenASymbol: string = 'sDAI'; + const tokenBSymbol: string = 'DAI'; + const tokenAAmount: string = BigInt(1.2345e18).toString(); + const tokenBAmount: string = BigInt(5.4321e18).toString(); + + testForNetwork( + network, + dexKey, + tokenASymbol, + tokenBSymbol, + tokenAAmount, + tokenBAmount, + ); }); }); diff --git a/src/dex/sdai/sdai-integration.test.ts b/src/dex/sdai/sdai-integration.test.ts index 96af640e5..1fddb7cf8 100644 --- a/src/dex/sdai/sdai-integration.test.ts +++ b/src/dex/sdai/sdai-integration.test.ts @@ -20,4 +20,120 @@ const amounts = [0n, BI_POWS[18], 2000000000000000000n]; const dexKey = 'SDai'; -describe('SDai', function () {}); +describe('SDai', function () { + it('getPoolIdentifiers and getPricesVolume DAI -> sDAI SELL', async function () { + const dexHelper = new DummyDexHelper(network); + const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); + const sdai = new SDai(network, dexKey, dexHelper); + + const pools = await sdai.getPoolIdentifiers( + DaiToken, + SDaiToken, + SwapSide.SELL, + blocknumber, + ); + console.log(`${DaiToken} <> ${SDaiToken} Pool Identifiers: `, pools); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await sdai.getPricesVolume( + DaiToken, + SDaiToken, + amounts, + SwapSide.SELL, + blocknumber, + pools, + ); + console.log(`${DaiToken} <> ${SDaiToken} Pool Prices: `, poolPrices); + + expect(poolPrices).not.toBeNull(); + checkConstantPoolPrices(poolPrices!, amounts, dexKey); + }); + + it('getPoolIdentifiers and getPricesVolume sDAI -> DAI SELL', async function () { + const dexHelper = new DummyDexHelper(network); + const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); + const sdai = new SDai(network, dexKey, dexHelper); + + const pools = await sdai.getPoolIdentifiers( + SDaiToken, + DaiToken, + SwapSide.SELL, + blocknumber, + ); + console.log(`${SDaiToken} <> ${DaiToken} Pool Identifiers: `, pools); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await sdai.getPricesVolume( + SDaiToken, + DaiToken, + amounts, + SwapSide.SELL, + blocknumber, + pools, + ); + console.log(`${SDaiToken} <> ${DaiToken} Pool Prices: `, poolPrices); + + expect(poolPrices).not.toBeNull(); + checkConstantPoolPrices(poolPrices!, amounts, dexKey); + }); + + it('getPoolIdentifiers and getPricesVolume DAI -> sDAI BUY', async function () { + const dexHelper = new DummyDexHelper(network); + const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); + const sdai = new SDai(network, dexKey, dexHelper); + + const pools = await sdai.getPoolIdentifiers( + DaiToken, + SDaiToken, + SwapSide.BUY, + blocknumber, + ); + console.log(`${DaiToken} <> ${SDaiToken} Pool Identifiers: `, pools); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await sdai.getPricesVolume( + DaiToken, + SDaiToken, + amounts, + SwapSide.BUY, + blocknumber, + pools, + ); + console.log(`${DaiToken} <> ${SDaiToken} Pool Prices: `, poolPrices); + + expect(poolPrices).not.toBeNull(); + checkConstantPoolPrices(poolPrices!, amounts, dexKey); + }); + + it('getPoolIdentifiers and getPricesVolume sDAI -> DAI BUY', async function () { + const dexHelper = new DummyDexHelper(network); + const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); + const sdai = new SDai(network, dexKey, dexHelper); + + const pools = await sdai.getPoolIdentifiers( + SDaiToken, + DaiToken, + SwapSide.BUY, + blocknumber, + ); + console.log(`${SDaiToken} <> ${DaiToken} Pool Identifiers: `, pools); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await sdai.getPricesVolume( + SDaiToken, + DaiToken, + amounts, + SwapSide.BUY, + blocknumber, + pools, + ); + console.log(`${SDaiToken} <> ${DaiToken} Pool Prices: `, poolPrices); + + expect(poolPrices).not.toBeNull(); + checkConstantPoolPrices(poolPrices!, amounts, dexKey); + }); +}); diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index ecd8806e1..972b8dbe0 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -1,7 +1,7 @@ import { SimpleExchange } from '../simple-exchange'; import { IDex } from '../idex'; import { DexParams, SDaiData, SDaiFunctions } from './types'; -import { Network, SwapSide } from '../../constants'; +import { NULL_ADDRESS, Network, SwapSide } from '../../constants'; import { getDexKeysWithNetwork } from '../../utils'; import { Adapters, SDaiConfig } from './config'; import { @@ -17,10 +17,11 @@ import { import { IDexHelper } from '../../dex-helper'; import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; import { BI_POWS } from '../../bigint-constants'; -import { SDAI_GAS_COST } from './constants'; -import PolygonMigrationAbi from '../../abi/polygon-migration/PolygonMigration.abi.json'; +import SavingsDaiAbi from '../../abi/sdai/SavingsDai.abi.json'; import { Interface } from 'ethers/lib/utils'; +const SDAI_GAS_COST = 1000000; + export class SDai extends SimpleExchange implements IDex { readonly hasConstantPriceLargeAmounts = true; readonly isFeeOnTransferSupported = false; @@ -34,16 +35,20 @@ export class SDai extends SimpleExchange implements IDex { protected network: Network, dexKey: string, protected dexHelper: IDexHelper, + readonly sdaiAddress: string = SDaiConfig[dexKey][network].sdaiAddress, readonly daiAddress: string = SDaiConfig[dexKey][network].daiAddress, + protected unitPrice = BI_POWS[18], protected adapters = Adapters[network] || {}, - protected migratorInterface = new Interface(PolygonMigrationAbi), + + protected sdaiInterface = new Interface(SavingsDaiAbi), ) { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); } + // TODO: getAdapters(side: SwapSide): { name: string; index: number }[] | null { return this.adapters[side] || null; } @@ -88,6 +93,7 @@ export class SDai extends SimpleExchange implements IDex { return null; } + // TODO: wtf is unit??? return [ { prices: amounts, @@ -105,19 +111,11 @@ export class SDai extends SimpleExchange implements IDex { return CALLDATA_GAS_COST.DEX_NO_PAYLOAD; } - getAdapterParam( - srcToken: string, - destToken: string, - srcAmount: string, - destAmount: string, - data: SDaiData, - side: SwapSide, - ): AdapterExchangeParam { - return { - targetExchange: this.sdaiAddress, - payload: '0x', - networkFee: '0', - }; + async getTopPoolsForToken( + tokenAddress: Address, + limit: number, + ): Promise { + return []; } async getSimpleParam( @@ -128,9 +126,9 @@ export class SDai extends SimpleExchange implements IDex { data: SDaiData, side: SwapSide, ): Promise { - const swapData = this.migratorInterface.encodeFunctionData( + const swapData = this.sdaiInterface.encodeFunctionData( this.isDai(srcToken) ? SDaiFunctions.deposit : SDaiFunctions.redeem, - [srcAmount], + [srcAmount, this.augustusAddress], ); return this.buildSimpleParamWithoutWETHConversion( @@ -143,10 +141,18 @@ export class SDai extends SimpleExchange implements IDex { ); } - async getTopPoolsForToken( - tokenAddress: Address, - limit: number, - ): Promise { - return []; + getAdapterParam( + srcToken: string, + destToken: string, + srcAmount: string, + destAmount: string, + data: SDaiData, + side: SwapSide, + ): AdapterExchangeParam { + return { + targetExchange: NULL_ADDRESS, + payload: '0x', + networkFee: '0', + }; } } From d7dbf6423dbf1e8241099ee3f7de7d3d863194e5 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 2 May 2024 18:54:11 +0100 Subject: [PATCH 03/31] chore: typo --- src/dex/sdai/sdai-integration.test.ts | 16 ++++++++-------- src/dex/sdai/sdai.ts | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/dex/sdai/sdai-integration.test.ts b/src/dex/sdai/sdai-integration.test.ts index 1fddb7cf8..5db1e5e40 100644 --- a/src/dex/sdai/sdai-integration.test.ts +++ b/src/dex/sdai/sdai-integration.test.ts @@ -32,7 +32,7 @@ describe('SDai', function () { SwapSide.SELL, blocknumber, ); - console.log(`${DaiToken} <> ${SDaiToken} Pool Identifiers: `, pools); + console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Identifiers: `, pools); expect(pools.length).toBeGreaterThan(0); @@ -44,7 +44,7 @@ describe('SDai', function () { blocknumber, pools, ); - console.log(`${DaiToken} <> ${SDaiToken} Pool Prices: `, poolPrices); + console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); checkConstantPoolPrices(poolPrices!, amounts, dexKey); @@ -61,7 +61,7 @@ describe('SDai', function () { SwapSide.SELL, blocknumber, ); - console.log(`${SDaiToken} <> ${DaiToken} Pool Identifiers: `, pools); + console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Identifiers: `, pools); expect(pools.length).toBeGreaterThan(0); @@ -73,7 +73,7 @@ describe('SDai', function () { blocknumber, pools, ); - console.log(`${SDaiToken} <> ${DaiToken} Pool Prices: `, poolPrices); + console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); checkConstantPoolPrices(poolPrices!, amounts, dexKey); @@ -90,7 +90,7 @@ describe('SDai', function () { SwapSide.BUY, blocknumber, ); - console.log(`${DaiToken} <> ${SDaiToken} Pool Identifiers: `, pools); + console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Identifiers: `, pools); expect(pools.length).toBeGreaterThan(0); @@ -102,7 +102,7 @@ describe('SDai', function () { blocknumber, pools, ); - console.log(`${DaiToken} <> ${SDaiToken} Pool Prices: `, poolPrices); + console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); checkConstantPoolPrices(poolPrices!, amounts, dexKey); @@ -119,7 +119,7 @@ describe('SDai', function () { SwapSide.BUY, blocknumber, ); - console.log(`${SDaiToken} <> ${DaiToken} Pool Identifiers: `, pools); + console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Identifiers: `, pools); expect(pools.length).toBeGreaterThan(0); @@ -131,7 +131,7 @@ describe('SDai', function () { blocknumber, pools, ); - console.log(`${SDaiToken} <> ${DaiToken} Pool Prices: `, poolPrices); + console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); checkConstantPoolPrices(poolPrices!, amounts, dexKey); diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index 972b8dbe0..0d0aa60c2 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -2,7 +2,7 @@ import { SimpleExchange } from '../simple-exchange'; import { IDex } from '../idex'; import { DexParams, SDaiData, SDaiFunctions } from './types'; import { NULL_ADDRESS, Network, SwapSide } from '../../constants'; -import { getDexKeysWithNetwork } from '../../utils'; +import { getBigIntPow, getDexKeysWithNetwork } from '../../utils'; import { Adapters, SDaiConfig } from './config'; import { AdapterExchangeParam, @@ -39,9 +39,7 @@ export class SDai extends SimpleExchange implements IDex { readonly sdaiAddress: string = SDaiConfig[dexKey][network].sdaiAddress, readonly daiAddress: string = SDaiConfig[dexKey][network].daiAddress, - protected unitPrice = BI_POWS[18], protected adapters = Adapters[network] || {}, - protected sdaiInterface = new Interface(SavingsDaiAbi), ) { super(dexHelper, dexKey); @@ -50,7 +48,8 @@ export class SDai extends SimpleExchange implements IDex { // TODO: getAdapters(side: SwapSide): { name: string; index: number }[] | null { - return this.adapters[side] || null; + // return this.adapters[side] || null; + return null; } isSDai(tokenAddress: Address) { @@ -75,10 +74,10 @@ export class SDai extends SimpleExchange implements IDex { blockNumber: number, ): Promise { if (this.isAppropriatePair(srcToken, destToken)) { - return [`${this.dexKey}_${srcToken.address}_${destToken.address}`]; + return []; } - return []; + return [`${this.dexKey}_${srcToken.address}_${destToken.address}`]; } async getPricesVolume( @@ -93,11 +92,12 @@ export class SDai extends SimpleExchange implements IDex { return null; } - // TODO: wtf is unit??? return [ { prices: amounts, - unit: this.unitPrice, + unit: getBigIntPow( + (side === SwapSide.SELL ? destToken : srcToken).decimals, + ), gasCost: SDAI_GAS_COST, exchange: this.dexKey, poolAddresses: [this.sdaiAddress], From b757d64bb5cf594c432ebb844d17531f8f82b1d3 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 6 May 2024 07:04:39 +0100 Subject: [PATCH 04/31] chore: pot contract ABI --- src/abi/maker-psm/pot.json | 322 +++++++++++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 src/abi/maker-psm/pot.json diff --git a/src/abi/maker-psm/pot.json b/src/abi/maker-psm/pot.json new file mode 100644 index 000000000..fac1f623f --- /dev/null +++ b/src/abi/maker-psm/pot.json @@ -0,0 +1,322 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vat_", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": true, + "internalType": "bytes4", + "name": "sig", + "type": "bytes4" + }, + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "arg1", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "arg2", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "LogNote", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "Pie", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "cage", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "chi", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "guy", + "type": "address" + } + ], + "name": "deny", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "drip", + "outputs": [ + { + "internalType": "uint256", + "name": "tmp", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "dsr", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "exit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "data", + "type": "uint256" + } + ], + "name": "file", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "file", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "join", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "live", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "pie", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "guy", + "type": "address" + } + ], + "name": "rely", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "rho", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "vat", + "outputs": [ + { + "internalType": "contract VatLike", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "vow", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "wards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] From 4ba9c31c840f5162b1452cb88a798cc4c70ef9ce Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 6 May 2024 07:05:41 +0100 Subject: [PATCH 05/31] fix: sdai pool, get prices, get top pools --- src/dex/sdai/config.ts | 5 +- src/dex/sdai/sdai-integration.test.ts | 36 +++++++-- src/dex/sdai/sdai-pool.ts | 101 ++++++++++++++++++++++++++ src/dex/sdai/sdai.ts | 86 +++++++++++++++++----- src/dex/sdai/types.ts | 9 ++- src/dex/sdai/utils.ts | 43 +++++++++++ 6 files changed, 254 insertions(+), 26 deletions(-) create mode 100644 src/dex/sdai/sdai-pool.ts create mode 100644 src/dex/sdai/utils.ts diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index 6fccb654e..7da7626ff 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -1,13 +1,14 @@ -import { DexParams } from './types'; +import { SDaiParams } from './types'; import { DexConfigMap } from '../../types'; import { Network } from '../../constants'; import { SwapSide } from '@paraswap/core'; -export const SDaiConfig: DexConfigMap = { +export const SDaiConfig: DexConfigMap = { SDai: { [Network.MAINNET]: { sdaiAddress: '0x83F20F44975D03b1b09e64809B757c47f942BEeA', daiAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + potAddress: '0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7', }, }, }; diff --git a/src/dex/sdai/sdai-integration.test.ts b/src/dex/sdai/sdai-integration.test.ts index 5db1e5e40..5089ace68 100644 --- a/src/dex/sdai/sdai-integration.test.ts +++ b/src/dex/sdai/sdai-integration.test.ts @@ -3,7 +3,7 @@ dotenv.config(); import { DummyDexHelper } from '../../dex-helper/index'; import { Network, SwapSide } from '../../constants'; -import { checkConstantPoolPrices } from '../../../tests/utils'; +import { checkPoolPrices, checkPoolsLiquidity } from '../../../tests/utils'; import { Tokens } from '../../../tests/constants-e2e'; import { BI_POWS } from '../../bigint-constants'; import { SDai } from './sdai'; @@ -47,7 +47,7 @@ describe('SDai', function () { console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); - checkConstantPoolPrices(poolPrices!, amounts, dexKey); + checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey); }); it('getPoolIdentifiers and getPricesVolume sDAI -> DAI SELL', async function () { @@ -76,7 +76,7 @@ describe('SDai', function () { console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); - checkConstantPoolPrices(poolPrices!, amounts, dexKey); + checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey); }); it('getPoolIdentifiers and getPricesVolume DAI -> sDAI BUY', async function () { @@ -105,7 +105,7 @@ describe('SDai', function () { console.log(`${DaiSymbol} <> ${SDaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); - checkConstantPoolPrices(poolPrices!, amounts, dexKey); + checkPoolPrices(poolPrices!, amounts, SwapSide.BUY, dexKey); }); it('getPoolIdentifiers and getPricesVolume sDAI -> DAI BUY', async function () { @@ -134,6 +134,32 @@ describe('SDai', function () { console.log(`${SDaiSymbol} <> ${DaiSymbol} Pool Prices: `, poolPrices); expect(poolPrices).not.toBeNull(); - checkConstantPoolPrices(poolPrices!, amounts, dexKey); + checkPoolPrices(poolPrices!, amounts, SwapSide.BUY, dexKey); + }); + + it('Dai getTopPoolsForToken', async function () { + const dexHelper = new DummyDexHelper(network); + const makerPsm = new SDai(network, dexKey, dexHelper); + + const poolLiquidity = await makerPsm.getTopPoolsForToken( + DaiToken.address, + 10, + ); + console.log(`${DaiSymbol} Top Pools:`, poolLiquidity); + + checkPoolsLiquidity(poolLiquidity, DaiToken.address, dexKey); + }); + + it('SDai getTopPoolsForToken', async function () { + const dexHelper = new DummyDexHelper(network); + const makerPsm = new SDai(network, dexKey, dexHelper); + + const poolLiquidity = await makerPsm.getTopPoolsForToken( + SDaiToken.address, + 10, + ); + console.log(`${SDaiSymbol} Top Pools:`, poolLiquidity); + + checkPoolsLiquidity(poolLiquidity, SDaiToken.address, dexKey); }); }); diff --git a/src/dex/sdai/sdai-pool.ts b/src/dex/sdai/sdai-pool.ts new file mode 100644 index 000000000..f5fcf07f0 --- /dev/null +++ b/src/dex/sdai/sdai-pool.ts @@ -0,0 +1,101 @@ +import { BI_POWS } from '../../bigint-constants'; +import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; +import { Interface } from '@ethersproject/abi'; + +import type { IDexHelper } from '../../dex-helper'; +import type { AsyncOrSync, DeepReadonly } from 'ts-essentials'; +import type { Address, Log, Logger } from '../../types'; +import type { SDaiPoolState } from './types'; +import { getOnChainState } from './utils'; +import { currentBigIntTimestampInS } from '../../utils'; + +const RAY = BI_POWS[27]; +const ZERO = BigInt(0); +const TWO = BigInt(2); +const HALF = RAY / TWO; + +const rpow = (x: bigint, n: bigint): bigint => { + // REF: https://etherscan.io/address/0x83f20f44975d03b1b09e64809b757c47f942beea#code#L122 + let z = RAY; + if (!x && !n) return z; + if (!x) return ZERO; + if (n % TWO > ZERO) { + z = x; + } + + for (n = n / TWO; n > ZERO; n /= TWO) { + x = (x * x + HALF) / RAY; + if (n % TWO > ZERO) continue; + z = (z * x + HALF) / RAY; + } + + return z; +}; + +export class SDaiPool extends StatefulEventSubscriber { + decoder = (log: Log) => this.potInterface.parseLog(log); + + constructor( + parentName: string, + protected dexHelper: IDexHelper, + private potAddress: Address, + private potInterface: Interface, + logger: Logger, + ) { + super(parentName, 'sdai', dexHelper, logger); + this.addressesSubscribed = [potAddress]; + } + + protected processLog( + state: DeepReadonly, + log: Readonly, + ): AsyncOrSync | null> { + const event = this.decoder(log); + // if (event.name === 'Reprice') + // return { + // dsr: BigInt(event.args.newSwETHToETHRate), + // rho: BigInt(event.args.newSwETHToETHRate), + // chi: BigInt(event.args.newSwETHToETHRate), + // timestamp: BigInt(0), + // // timestamp: BigInt(event.b); + // }; + + return null; + } + + async generateState( + blockNumber: number | 'latest' = 'latest', + ): Promise> { + const state = await getOnChainState( + this.dexHelper.multiContract, + this.potAddress, + this.potInterface, + blockNumber, + ); + + return state; + } + + chi(blockNumber: number): bigint { + const state = this.getState(blockNumber); + if (!state) throw new Error('Cannot compute price'); + + const { rho, chi, dsr } = state; + + const timestamp = currentBigIntTimestampInS(); + const multiplier = rpow(BigInt(dsr), timestamp - BigInt(rho)); + return timestamp > BigInt(rho) + ? (multiplier * BigInt(chi)) / RAY + : BigInt(chi); + } + + convertToSDai(blockNumber: number, daiAmount: bigint): bigint { + const chi = this.chi(blockNumber); + return (daiAmount * RAY) / chi; + } + + convertToDai(blockNumber: number, sdaiAmount: bigint): bigint { + const chi = this.chi(blockNumber); + return (sdaiAmount * chi) / RAY; + } +} diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index 0d0aa60c2..c6c32e950 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -1,6 +1,6 @@ import { SimpleExchange } from '../simple-exchange'; import { IDex } from '../idex'; -import { DexParams, SDaiData, SDaiFunctions } from './types'; +import { SDaiParams, SDaiData, SDaiFunctions } from './types'; import { NULL_ADDRESS, Network, SwapSide } from '../../constants'; import { getBigIntPow, getDexKeysWithNetwork } from '../../utils'; import { Adapters, SDaiConfig } from './config'; @@ -16,19 +16,23 @@ import { } from '../../types'; import { IDexHelper } from '../../dex-helper'; import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; -import { BI_POWS } from '../../bigint-constants'; +import PotAbi from '../../abi/maker-psm/pot.json'; import SavingsDaiAbi from '../../abi/sdai/SavingsDai.abi.json'; import { Interface } from 'ethers/lib/utils'; +import { getOnChainState } from './utils'; +import { SDaiPool } from './sdai-pool'; +import { BI_POWS } from '../../bigint-constants'; const SDAI_GAS_COST = 1000000; -export class SDai extends SimpleExchange implements IDex { +export class SDai extends SimpleExchange implements IDex { readonly hasConstantPriceLargeAmounts = true; readonly isFeeOnTransferSupported = false; public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = getDexKeysWithNetwork(SDaiConfig); + protected eventPool: SDaiPool; logger: Logger; constructor( @@ -36,20 +40,28 @@ export class SDai extends SimpleExchange implements IDex { dexKey: string, protected dexHelper: IDexHelper, - readonly sdaiAddress: string = SDaiConfig[dexKey][network].sdaiAddress, readonly daiAddress: string = SDaiConfig[dexKey][network].daiAddress, + readonly sdaiAddress: string = SDaiConfig[dexKey][network].sdaiAddress, + readonly potAddress: string = SDaiConfig[dexKey][network].potAddress, protected adapters = Adapters[network] || {}, protected sdaiInterface = new Interface(SavingsDaiAbi), + protected potInterface = new Interface(PotAbi), ) { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); + this.eventPool = new SDaiPool( + this.dexKey, + dexHelper, + this.potAddress, + this.potInterface, + this.logger, + ); } // TODO: getAdapters(side: SwapSide): { name: string; index: number }[] | null { - // return this.adapters[side] || null; - return null; + return this.adapters[side] || null; } isSDai(tokenAddress: Address) { @@ -67,17 +79,31 @@ export class SDai extends SimpleExchange implements IDex { ); } + async initializePricing(blockNumber: number) { + const poolState = await getOnChainState( + this.dexHelper.multiContract, + this.potAddress, + this.potInterface, + blockNumber, + ); + + await this.eventPool.initialize(blockNumber, { + state: poolState, + }); + } + async getPoolIdentifiers( srcToken: Token, destToken: Token, side: SwapSide, blockNumber: number, ): Promise { - if (this.isAppropriatePair(srcToken, destToken)) { - return []; - } + // TODO: remove this fn call + await this.initializePricing(blockNumber); - return [`${this.dexKey}_${srcToken.address}_${destToken.address}`]; + return this.isAppropriatePair(srcToken, destToken) + ? [`${this.dexKey}_${srcToken.address}_${destToken.address}`] + : []; } async getPricesVolume( @@ -88,16 +114,24 @@ export class SDai extends SimpleExchange implements IDex { blockNumber: number, limitPools?: string[], ): Promise> { - if (!this.isAppropriatePair(srcToken, destToken)) { - return null; - } + if (!this.isAppropriatePair(srcToken, destToken)) return null; + if (this.eventPool.getState(blockNumber) === null) return null; + + const convertFn = (blockNumber: number, amountIn: bigint) => + this.isDai(srcToken.address) + ? this.eventPool.convertToSDai(blockNumber, amountIn) + : this.eventPool.convertToDai(blockNumber, amountIn); + + const unitIn = BI_POWS[18]; + const unitOut = convertFn(blockNumber, unitIn); + const amountsOut = amounts.map(amountIn => + convertFn(blockNumber, amountIn), + ); return [ { - prices: amounts, - unit: getBigIntPow( - (side === SwapSide.SELL ? destToken : srcToken).decimals, - ), + prices: amountsOut, + unit: unitOut, gasCost: SDAI_GAS_COST, exchange: this.dexKey, poolAddresses: [this.sdaiAddress], @@ -115,7 +149,23 @@ export class SDai extends SimpleExchange implements IDex { tokenAddress: Address, limit: number, ): Promise { - return []; + if (!this.isDai(tokenAddress) && !this.isSDai(tokenAddress)) return []; + + return [ + { + exchange: this.dexKey, + address: this.sdaiAddress, + connectorTokens: [ + { + decimals: 18, + address: this.isDai(tokenAddress) + ? this.sdaiAddress + : this.daiAddress, + }, + ], + liquidityUSD: 1000000000, // Just returning a big number so this DEX will be preferred + }, + ]; } async getSimpleParam( diff --git a/src/dex/sdai/types.ts b/src/dex/sdai/types.ts index 45b9a037b..cb6385c04 100644 --- a/src/dex/sdai/types.ts +++ b/src/dex/sdai/types.ts @@ -2,12 +2,19 @@ import { Address } from '../../types'; export type SDaiData = null; -export type DexParams = { +export type SDaiParams = { sdaiAddress: Address; daiAddress: Address; + potAddress: Address; }; export enum SDaiFunctions { deposit = 'deposit', redeem = 'redeem', } + +export type SDaiPoolState = { + rho: string; + chi: string; + dsr: string; +}; diff --git a/src/dex/sdai/utils.ts b/src/dex/sdai/utils.ts new file mode 100644 index 000000000..00b749fab --- /dev/null +++ b/src/dex/sdai/utils.ts @@ -0,0 +1,43 @@ +import { Contract } from 'web3-eth-contract'; +import { SDaiPoolState } from './types'; +import { Interface, AbiCoder } from '@ethersproject/abi'; +import { currentBigIntTimestampInS } from '../../utils'; + +const coder = new AbiCoder(); + +// - `dsr`: the Dai Savings Rate +// - `chi`: the Rate Accumulator +// - `rho`: time of last drip +export async function getOnChainState( + multiContract: Contract, + potAddress: string, + potInterface: Interface, + blockNumber: number | 'latest', +): Promise { + const data: { returnData: any[] } = await multiContract.methods + .aggregate([ + { + target: potAddress, + callData: potInterface.encodeFunctionData('dsr', []), + }, + { + target: potAddress, + callData: potInterface.encodeFunctionData('chi', []), + }, + { + target: potAddress, + callData: potInterface.encodeFunctionData('rho', []), + }, + ]) + .call({}, blockNumber); + + const [dsr, chi, rho] = data.returnData.map(item => + coder.decode(['uint256'], item)[0].toString(), + ); + + return { + dsr, + chi, + rho, + }; +} From 18f606955ba7e40ecf5de0d1d6438ee3bfb7c849 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 6 May 2024 08:37:43 +0100 Subject: [PATCH 06/31] fix: add adapters, temporary remove buy e2e tests --- src/dex/index.ts | 2 ++ src/dex/sdai/config.ts | 4 ++-- src/dex/sdai/constants.ts | 2 +- src/dex/sdai/sdai-e2e.test.ts | 2 +- src/dex/sdai/sdai.ts | 12 ++++++------ 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/dex/index.ts b/src/dex/index.ts index 2868b6d06..94511893b 100644 --- a/src/dex/index.ts +++ b/src/dex/index.ts @@ -87,6 +87,7 @@ import { Wombat } from './wombat/wombat'; import { Swell } from './swell/swell'; import { PharaohV1 } from './solidly/forks-override/pharaohV1'; import { EtherFi } from './etherfi'; +import { SDai } from './sdai/sdai'; const LegacyDexes = [ CurveV2, @@ -170,6 +171,7 @@ const Dexes = [ Wombat, Swell, PharaohV1, + SDai, ]; export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder< diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index 7da7626ff..5bdfe0095 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -19,7 +19,7 @@ export const Adapters: { }; } = { [Network.MAINNET]: { - [SwapSide.SELL]: [{ name: 'Adapter04', index: 4 }], - [SwapSide.BUY]: [{ name: 'BuyAdapter', index: 11 }], + [SwapSide.SELL]: [{ name: 'Adapter06', index: 2 }], + [SwapSide.BUY]: [], }, }; diff --git a/src/dex/sdai/constants.ts b/src/dex/sdai/constants.ts index 44dc895f5..059203087 100644 --- a/src/dex/sdai/constants.ts +++ b/src/dex/sdai/constants.ts @@ -1,2 +1,2 @@ -export const SDAI_DEPOSIT_GAS_COST = 90_000; +export const SDAI_DEPOSIT_GAS_COST = 165_000; export const SDAI_REDEEM_GAS_COST = 90_000; diff --git a/src/dex/sdai/sdai-e2e.test.ts b/src/dex/sdai/sdai-e2e.test.ts index 9f913afb3..2a46caec8 100644 --- a/src/dex/sdai/sdai-e2e.test.ts +++ b/src/dex/sdai/sdai-e2e.test.ts @@ -31,7 +31,7 @@ function testForNetwork( ContractMethod.megaSwap, ], ], - [SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]], + // [SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]], ]); describe(`${network}`, () => { diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index c6c32e950..149015648 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -2,7 +2,7 @@ import { SimpleExchange } from '../simple-exchange'; import { IDex } from '../idex'; import { SDaiParams, SDaiData, SDaiFunctions } from './types'; import { NULL_ADDRESS, Network, SwapSide } from '../../constants'; -import { getBigIntPow, getDexKeysWithNetwork } from '../../utils'; +import { getDexKeysWithNetwork } from '../../utils'; import { Adapters, SDaiConfig } from './config'; import { AdapterExchangeParam, @@ -22,8 +22,7 @@ import { Interface } from 'ethers/lib/utils'; import { getOnChainState } from './utils'; import { SDaiPool } from './sdai-pool'; import { BI_POWS } from '../../bigint-constants'; - -const SDAI_GAS_COST = 1000000; +import { SDAI_DEPOSIT_GAS_COST } from './constants'; export class SDai extends SimpleExchange implements IDex { readonly hasConstantPriceLargeAmounts = true; @@ -59,7 +58,6 @@ export class SDai extends SimpleExchange implements IDex { ); } - // TODO: getAdapters(side: SwapSide): { name: string; index: number }[] | null { return this.adapters[side] || null; } @@ -132,7 +130,7 @@ export class SDai extends SimpleExchange implements IDex { { prices: amountsOut, unit: unitOut, - gasCost: SDAI_GAS_COST, + gasCost: SDAI_DEPOSIT_GAS_COST, exchange: this.dexKey, poolAddresses: [this.sdaiAddress], data: null, @@ -178,7 +176,9 @@ export class SDai extends SimpleExchange implements IDex { ): Promise { const swapData = this.sdaiInterface.encodeFunctionData( this.isDai(srcToken) ? SDaiFunctions.deposit : SDaiFunctions.redeem, - [srcAmount, this.augustusAddress], + this.isDai(srcToken) + ? [srcAmount, this.augustusAddress] + : [srcAmount, this.augustusAddress, this.augustusAddress], ); return this.buildSimpleParamWithoutWETHConversion( From e67093c59a57203575bea061c5e3d460dc7782ab Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 6 May 2024 09:21:20 +0100 Subject: [PATCH 07/31] fix: add adapter06 to base configs --- src/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.ts b/src/config.ts index 53390f2dd..4d1076f32 100644 --- a/src/config.ts +++ b/src/config.ts @@ -55,6 +55,7 @@ const baseConfigs: { [network: number]: BaseConfig } = { Adapter03: '0xBAEeb4540f59d30E567a5B563CC0c4587eDd9366', Adapter04: '0x369A2FDb910d432f0a07381a5E3d27572c876713', Adapter05: '0x3329dfa55A40B450952FBE0203167Ae6908E656d', + Adapter06: '0x476e0515eA63B5c3008014Fe5e22Ed126e81f289', BuyAdapter: '0x84bEF12C9931cE12662cc9F2366b6a5029E4BD29', BuyAdapter02: '0xe53d24CD81cC81bbf271AD7B02D0d67f851D727c', }, From ade1e90cbbfd0ef5753f64167a4a97467dc965b7 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Tue, 7 May 2024 08:47:58 +0100 Subject: [PATCH 08/31] fix: add buy adapter for sdai --- src/dex/sdai/config.ts | 2 +- src/dex/sdai/sdai-e2e.test.ts | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index 5bdfe0095..a30547c1d 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -20,6 +20,6 @@ export const Adapters: { } = { [Network.MAINNET]: { [SwapSide.SELL]: [{ name: 'Adapter06', index: 2 }], - [SwapSide.BUY]: [], + [SwapSide.BUY]: [{ name: 'BuyAdapter02', index: 5 }], }, }; diff --git a/src/dex/sdai/sdai-e2e.test.ts b/src/dex/sdai/sdai-e2e.test.ts index 2a46caec8..83a1b9182 100644 --- a/src/dex/sdai/sdai-e2e.test.ts +++ b/src/dex/sdai/sdai-e2e.test.ts @@ -1,11 +1,11 @@ import dotenv from 'dotenv'; dotenv.config(); -import { ContractMethod, Network, SwapSide } from '../../constants'; +import { testE2E } from '../../../tests/utils-e2e'; +import { Tokens, Holders } from '../../../tests/constants-e2e'; +import { Network, ContractMethod, SwapSide } from '../../constants'; import { StaticJsonRpcProvider } from '@ethersproject/providers'; import { generateConfig } from '../../config'; -import { Holders, Tokens } from '../../../tests/constants-e2e'; -import { testE2E } from '../../../tests/utils-e2e'; function testForNetwork( network: Network, @@ -31,7 +31,7 @@ function testForNetwork( ContractMethod.megaSwap, ], ], - // [SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]], + [SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]], ]); describe(`${network}`, () => { @@ -72,15 +72,17 @@ function testForNetwork( }); } -describe('SDai E2E', () => { - const dexKey = 'SDai'; +describe('sDAI E2E', () => { + const dexKey = 'sdai'; describe('Mainnet', () => { const network = Network.MAINNET; - const tokenASymbol: string = 'sDAI'; - const tokenBSymbol: string = 'DAI'; - const tokenAAmount: string = BigInt(1.2345e18).toString(); - const tokenBAmount: string = BigInt(5.4321e18).toString(); + + const tokenASymbol: string = 'DAI'; + const tokenBSymbol: string = 'sDAI'; + + const tokenAAmount: string = '1000000000000000000'; + const tokenBAmount: string = '1000000000000000000'; testForNetwork( network, From 960a1bb457dfba3172524b05b7cd9c287c7bcb72 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Tue, 7 May 2024 10:12:52 +0100 Subject: [PATCH 09/31] fix: use angle adapter for sdai integration --- src/dex/sdai/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index a30547c1d..758471621 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -19,7 +19,7 @@ export const Adapters: { }; } = { [Network.MAINNET]: { - [SwapSide.SELL]: [{ name: 'Adapter06', index: 2 }], - [SwapSide.BUY]: [{ name: 'BuyAdapter02', index: 5 }], + [SwapSide.SELL]: [{ name: 'Adapter06', index: 1 }], + [SwapSide.BUY]: [{ name: 'BuyAdapter02', index: 4 }], }, }; From 2ac795d8a47e51797bb9f892d15bdc7d500f7cb2 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Wed, 8 May 2024 13:28:41 +0100 Subject: [PATCH 10/31] feat: event processing from maker protocol --- src/dex/sdai/sdai-pool.ts | 84 ++++++++++++++++++++++++--------------- src/dex/sdai/sdai.ts | 7 +--- src/dex/sdai/types.ts | 1 + src/dex/sdai/utils.ts | 7 +++- 4 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/dex/sdai/sdai-pool.ts b/src/dex/sdai/sdai-pool.ts index f5fcf07f0..ab43eed82 100644 --- a/src/dex/sdai/sdai-pool.ts +++ b/src/dex/sdai/sdai-pool.ts @@ -4,7 +4,7 @@ import { Interface } from '@ethersproject/abi'; import type { IDexHelper } from '../../dex-helper'; import type { AsyncOrSync, DeepReadonly } from 'ts-essentials'; -import type { Address, Log, Logger } from '../../types'; +import type { Address, BlockHeader, Log, Logger } from '../../types'; import type { SDaiPoolState } from './types'; import { getOnChainState } from './utils'; import { currentBigIntTimestampInS } from '../../utils'; @@ -32,6 +32,19 @@ const rpow = (x: bigint, n: bigint): bigint => { return z; }; +const calcChi = (state: SDaiPoolState, currentTimestamp?: number) => { + currentTimestamp ||= Math.floor(Date.now() / 1000); + if (!state.live) return RAY; + + const { dsr: dsr_, chi: chi_, rho: rho_ } = state; + const now = BigInt(currentTimestamp); + const rho = BigInt(rho_); + const dsr = BigInt(dsr_); + const chi = BigInt(chi_); + + return now > rho ? (rpow(dsr, now - rho) * chi) / RAY : chi; +}; + export class SDaiPool extends StatefulEventSubscriber { decoder = (log: Log) => this.potInterface.parseLog(log); @@ -49,16 +62,32 @@ export class SDaiPool extends StatefulEventSubscriber { protected processLog( state: DeepReadonly, log: Readonly, + blockHeader: Readonly, ): AsyncOrSync | null> { const event = this.decoder(log); - // if (event.name === 'Reprice') - // return { - // dsr: BigInt(event.args.newSwETHToETHRate), - // rho: BigInt(event.args.newSwETHToETHRate), - // chi: BigInt(event.args.newSwETHToETHRate), - // timestamp: BigInt(0), - // // timestamp: BigInt(event.b); - // }; + if (event.name === 'cage') { + return { + dsr: RAY.toString(), + chi: RAY.toString(), + rho: RAY.toString(), + live: false, + }; + } + + if (event.name === 'file' && event.args.what === 'dsr') { + return { + ...state, + dsr: BigInt(event.args.data).toString(), + }; + } + + if (event.name === 'drip') { + return { + ...state, + rho: blockHeader.timestamp.toString(), + chi: calcChi(state, +blockHeader.timestamp).toString(), + }; + } return null; } @@ -66,36 +95,25 @@ export class SDaiPool extends StatefulEventSubscriber { async generateState( blockNumber: number | 'latest' = 'latest', ): Promise> { - const state = await getOnChainState( - this.dexHelper.multiContract, - this.potAddress, - this.potInterface, - blockNumber, - ); - - return state; + return { + dsr: RAY.toString(), + chi: RAY.toString(), + rho: RAY.toString(), + live: true, + }; } - chi(blockNumber: number): bigint { + convertToSDai(daiAmount: bigint, blockNumber: number): bigint { const state = this.getState(blockNumber); - if (!state) throw new Error('Cannot compute price'); + if (!state) throw new Error('Unable to fetch state for SDAI'); - const { rho, chi, dsr } = state; - - const timestamp = currentBigIntTimestampInS(); - const multiplier = rpow(BigInt(dsr), timestamp - BigInt(rho)); - return timestamp > BigInt(rho) - ? (multiplier * BigInt(chi)) / RAY - : BigInt(chi); + return (daiAmount * RAY) / calcChi(state); } - convertToSDai(blockNumber: number, daiAmount: bigint): bigint { - const chi = this.chi(blockNumber); - return (daiAmount * RAY) / chi; - } + convertToDai(sdaiAmount: bigint, blockNumber: number): bigint { + const state = this.getState(blockNumber); + if (!state) throw new Error('Unable to fetch state for SDAI'); - convertToDai(blockNumber: number, sdaiAmount: bigint): bigint { - const chi = this.chi(blockNumber); - return (sdaiAmount * chi) / RAY; + return (sdaiAmount * calcChi(state)) / RAY; } } diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index 149015648..ba552f94a 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -96,9 +96,6 @@ export class SDai extends SimpleExchange implements IDex { side: SwapSide, blockNumber: number, ): Promise { - // TODO: remove this fn call - await this.initializePricing(blockNumber); - return this.isAppropriatePair(srcToken, destToken) ? [`${this.dexKey}_${srcToken.address}_${destToken.address}`] : []; @@ -117,8 +114,8 @@ export class SDai extends SimpleExchange implements IDex { const convertFn = (blockNumber: number, amountIn: bigint) => this.isDai(srcToken.address) - ? this.eventPool.convertToSDai(blockNumber, amountIn) - : this.eventPool.convertToDai(blockNumber, amountIn); + ? this.eventPool.convertToSDai(amountIn, blockNumber) + : this.eventPool.convertToDai(amountIn, blockNumber); const unitIn = BI_POWS[18]; const unitOut = convertFn(blockNumber, unitIn); diff --git a/src/dex/sdai/types.ts b/src/dex/sdai/types.ts index cb6385c04..ebb0f3145 100644 --- a/src/dex/sdai/types.ts +++ b/src/dex/sdai/types.ts @@ -17,4 +17,5 @@ export type SDaiPoolState = { rho: string; chi: string; dsr: string; + live: boolean; }; diff --git a/src/dex/sdai/utils.ts b/src/dex/sdai/utils.ts index 00b749fab..82ed53104 100644 --- a/src/dex/sdai/utils.ts +++ b/src/dex/sdai/utils.ts @@ -28,14 +28,19 @@ export async function getOnChainState( target: potAddress, callData: potInterface.encodeFunctionData('rho', []), }, + { + target: potAddress, + callData: potInterface.encodeFunctionData('live', []), + }, ]) .call({}, blockNumber); - const [dsr, chi, rho] = data.returnData.map(item => + const [dsr, chi, rho, live] = data.returnData.map(item => coder.decode(['uint256'], item)[0].toString(), ); return { + live: !!live, dsr, chi, rho, From c2322428e60748b0c1685eeed3401604fa9a9675 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Wed, 8 May 2024 21:41:46 +0100 Subject: [PATCH 11/31] feat: event state update support, fix adapter params --- src/dex/sdai/sdai-events.test.ts | 105 +++++++++++++++++++++++++++++++ src/dex/sdai/sdai-pool.ts | 41 ++++++------ src/dex/sdai/sdai.ts | 43 +++++++------ src/dex/sdai/types.ts | 2 +- 4 files changed, 151 insertions(+), 40 deletions(-) create mode 100644 src/dex/sdai/sdai-events.test.ts diff --git a/src/dex/sdai/sdai-events.test.ts b/src/dex/sdai/sdai-events.test.ts new file mode 100644 index 000000000..f3022f44d --- /dev/null +++ b/src/dex/sdai/sdai-events.test.ts @@ -0,0 +1,105 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +import { SDaiEventPool } from './sdai-pool'; +import { SDaiConfig } from './config'; +import { Network } from '../../constants'; +import { DummyDexHelper } from '../../dex-helper/index'; +import { testEventSubscriber } from '../../../tests/utils-events'; +import { SDaiPoolState } from './types'; + +import PotAbi from '../../abi/maker-psm/pot.json'; +import { Interface } from '@ethersproject/abi'; +import _ from 'lodash'; + +/* + README + ====== + + This test script adds unit tests for SDai event based + system. This is done by fetching the state on-chain before the + event block, manually pushing the block logs to the event-subscriber, + comparing the local state with on-chain state. + + Most of the logic for testing is abstracted by `testEventSubscriber`. + You need to do two things to make the tests work: + + 1. Fetch the block numbers where certain events were released. You + can modify the `./scripts/fetch-event-blocknumber.ts` to get the + block numbers for different events. Make sure to get sufficient + number of blockNumbers to cover all possible cases for the event + mutations. + + 2. Complete the implementation for fetchPoolState function. The + function should fetch the on-chain state of the event subscriber + using just the blocknumber. + + The template tests only include the test for a single event + subscriber. There can be cases where multiple event subscribers + exist for a single DEX. In such cases additional tests should be + added. + + You can run this individual test script by running: + `npx jest src/dex//-events.test.ts` + + (This comment should be removed from the final implementation) +*/ + +jest.setTimeout(50 * 1000); +const dexKey = 'SDai'; +const network = Network.MAINNET; + +async function fetchPoolState( + sdaiPool: SDaiEventPool, + blockNumber: number, +): Promise { + return sdaiPool.generateState(blockNumber); +} + +describe('SDai Event', function () { + const blockNumbers: { [eventName: string]: number[] } = { + drip: [19827559, 19827524, 19827163, 19827124, 19827000, 19826892], + // TODO: no matching logs + // https://etherscan.io/advanced-filter?fadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&tadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&mtd=0x29ae8114%7eFile + file: [], + // TODO: no matching logs + // https://etherscan.io/advanced-filter?fadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&tadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&mtd=0x69245009%7eCage + cage: [], + }; + + const addresses: { [contract: string]: string } = { + potAddress: SDaiConfig[dexKey][network].potAddress, + }; + + describe('SDaiPool', function () { + Object.keys(blockNumbers).forEach((event: string) => { + blockNumbers[event].forEach((blockNumber: number) => { + it(`Should return the correct state after the ${blockNumber}:${event}`, async function () { + const dexHelper = new DummyDexHelper(network); + const logger = dexHelper.getLogger(dexKey); + + const sdaiPool = new SDaiEventPool( + dexKey, + dexHelper, + addresses.potAddress, + new Interface(PotAbi), + logger, + ); + + await testEventSubscriber( + sdaiPool, + sdaiPool.addressesSubscribed, + (_blockNumber: number) => fetchPoolState(sdaiPool, _blockNumber), + blockNumber, + `${dexKey}_${sdaiPool}`, + dexHelper.provider, + (state, expected) => { + // TODO: + expect(_.omit(state, ['chi'])).toEqual(_.omit(expected, ['chi'])); + }, + ); + }); + }); + }); + }); +}); diff --git a/src/dex/sdai/sdai-pool.ts b/src/dex/sdai/sdai-pool.ts index ab43eed82..13b200fa9 100644 --- a/src/dex/sdai/sdai-pool.ts +++ b/src/dex/sdai/sdai-pool.ts @@ -7,25 +7,31 @@ import type { AsyncOrSync, DeepReadonly } from 'ts-essentials'; import type { Address, BlockHeader, Log, Logger } from '../../types'; import type { SDaiPoolState } from './types'; import { getOnChainState } from './utils'; -import { currentBigIntTimestampInS } from '../../utils'; const RAY = BI_POWS[27]; const ZERO = BigInt(0); const TWO = BigInt(2); const HALF = RAY / TWO; +// function file(bytes32,uint256) +const FILE_TOPICHASH = `0x29ae811400000000000000000000000000000000000000000000000000000000`; +// function drip() +const DRIP_TOPICHASH = `0x9f678cca00000000000000000000000000000000000000000000000000000000`; +// function cage() +const CAGE_TOPICHASH = `0x6924500900000000000000000000000000000000000000000000000000000000`; +// bytes32 repr of "dsr" string +const DSR_TOPIC = `0x6473720000000000000000000000000000000000000000000000000000000000`; + const rpow = (x: bigint, n: bigint): bigint => { // REF: https://etherscan.io/address/0x83f20f44975d03b1b09e64809b757c47f942beea#code#L122 let z = RAY; if (!x && !n) return z; if (!x) return ZERO; - if (n % TWO > ZERO) { - z = x; - } + if (n % TWO) z = x; for (n = n / TWO; n > ZERO; n /= TWO) { x = (x * x + HALF) / RAY; - if (n % TWO > ZERO) continue; + if (n % TWO) continue; z = (z * x + HALF) / RAY; } @@ -45,9 +51,7 @@ const calcChi = (state: SDaiPoolState, currentTimestamp?: number) => { return now > rho ? (rpow(dsr, now - rho) * chi) / RAY : chi; }; -export class SDaiPool extends StatefulEventSubscriber { - decoder = (log: Log) => this.potInterface.parseLog(log); - +export class SDaiEventPool extends StatefulEventSubscriber { constructor( parentName: string, protected dexHelper: IDexHelper, @@ -64,8 +68,7 @@ export class SDaiPool extends StatefulEventSubscriber { log: Readonly, blockHeader: Readonly, ): AsyncOrSync | null> { - const event = this.decoder(log); - if (event.name === 'cage') { + if (log.topics[0] === CAGE_TOPICHASH) { return { dsr: RAY.toString(), chi: RAY.toString(), @@ -74,14 +77,14 @@ export class SDaiPool extends StatefulEventSubscriber { }; } - if (event.name === 'file' && event.args.what === 'dsr') { + if (log.topics[0] === FILE_TOPICHASH && log.topics[2] === DSR_TOPIC) { return { ...state, - dsr: BigInt(event.args.data).toString(), + dsr: BigInt(log.topics[2]).toString(), }; } - if (event.name === 'drip') { + if (log.topics[0] === DRIP_TOPICHASH) { return { ...state, rho: blockHeader.timestamp.toString(), @@ -95,12 +98,12 @@ export class SDaiPool extends StatefulEventSubscriber { async generateState( blockNumber: number | 'latest' = 'latest', ): Promise> { - return { - dsr: RAY.toString(), - chi: RAY.toString(), - rho: RAY.toString(), - live: true, - }; + return getOnChainState( + this.dexHelper.multiContract, + this.potAddress, + this.potInterface, + blockNumber, + ); } convertToSDai(daiAmount: bigint, blockNumber: number): bigint { diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index ba552f94a..743f111f1 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -1,7 +1,7 @@ import { SimpleExchange } from '../simple-exchange'; import { IDex } from '../idex'; import { SDaiParams, SDaiData, SDaiFunctions } from './types'; -import { NULL_ADDRESS, Network, SwapSide } from '../../constants'; +import { Network, SwapSide } from '../../constants'; import { getDexKeysWithNetwork } from '../../utils'; import { Adapters, SDaiConfig } from './config'; import { @@ -19,8 +19,7 @@ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; import PotAbi from '../../abi/maker-psm/pot.json'; import SavingsDaiAbi from '../../abi/sdai/SavingsDai.abi.json'; import { Interface } from 'ethers/lib/utils'; -import { getOnChainState } from './utils'; -import { SDaiPool } from './sdai-pool'; +import { SDaiEventPool } from './sdai-pool'; import { BI_POWS } from '../../bigint-constants'; import { SDAI_DEPOSIT_GAS_COST } from './constants'; @@ -31,7 +30,7 @@ export class SDai extends SimpleExchange implements IDex { public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = getDexKeysWithNetwork(SDaiConfig); - protected eventPool: SDaiPool; + protected eventPool: SDaiEventPool; logger: Logger; constructor( @@ -49,7 +48,7 @@ export class SDai extends SimpleExchange implements IDex { ) { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); - this.eventPool = new SDaiPool( + this.eventPool = new SDaiEventPool( this.dexKey, dexHelper, this.potAddress, @@ -78,16 +77,7 @@ export class SDai extends SimpleExchange implements IDex { } async initializePricing(blockNumber: number) { - const poolState = await getOnChainState( - this.dexHelper.multiContract, - this.potAddress, - this.potInterface, - blockNumber, - ); - - await this.eventPool.initialize(blockNumber, { - state: poolState, - }); + await this.eventPool.generateState(blockNumber); } async getPoolIdentifiers( @@ -97,7 +87,7 @@ export class SDai extends SimpleExchange implements IDex { blockNumber: number, ): Promise { return this.isAppropriatePair(srcToken, destToken) - ? [`${this.dexKey}_${srcToken.address}_${destToken.address}`] + ? [`${this.dexKey}_${this.sdaiAddress}`] : []; } @@ -110,7 +100,7 @@ export class SDai extends SimpleExchange implements IDex { limitPools?: string[], ): Promise> { if (!this.isAppropriatePair(srcToken, destToken)) return null; - if (this.eventPool.getState(blockNumber) === null) return null; + if (!this.eventPool.getState(blockNumber)) return null; const convertFn = (blockNumber: number, amountIn: bigint) => this.isDai(srcToken.address) @@ -130,7 +120,7 @@ export class SDai extends SimpleExchange implements IDex { gasCost: SDAI_DEPOSIT_GAS_COST, exchange: this.dexKey, poolAddresses: [this.sdaiAddress], - data: null, + data: { exchange: this.sdaiAddress }, }, ]; } @@ -196,9 +186,22 @@ export class SDai extends SimpleExchange implements IDex { data: SDaiData, side: SwapSide, ): AdapterExchangeParam { + const { exchange } = data; + + const payload = this.abiCoder.encodeParameter( + { + ParentStruct: { + toStaked: 'bool', + }, + }, + { + toStaked: this.isDai(srcToken), + }, + ); + return { - targetExchange: NULL_ADDRESS, - payload: '0x', + targetExchange: exchange, + payload, networkFee: '0', }; } diff --git a/src/dex/sdai/types.ts b/src/dex/sdai/types.ts index ebb0f3145..ee6bc8604 100644 --- a/src/dex/sdai/types.ts +++ b/src/dex/sdai/types.ts @@ -1,6 +1,6 @@ import { Address } from '../../types'; -export type SDaiData = null; +export type SDaiData = { exchange: Address }; export type SDaiParams = { sdaiAddress: Address; From 236ed5fb644b87790bdeb7dd86dbdb6c9ae04142 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 9 May 2024 09:59:45 +0100 Subject: [PATCH 12/31] fix: minor changes --- src/dex/sdai/sdai-events.test.ts | 2 ++ src/dex/sdai/sdai-integration.test.ts | 43 ++++++++------------------- src/dex/sdai/sdai.ts | 2 +- 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/dex/sdai/sdai-events.test.ts b/src/dex/sdai/sdai-events.test.ts index f3022f44d..23c270c5c 100644 --- a/src/dex/sdai/sdai-events.test.ts +++ b/src/dex/sdai/sdai-events.test.ts @@ -86,6 +86,8 @@ describe('SDai Event', function () { logger, ); + await sdaiPool.initialize(blockNumber); + await testEventSubscriber( sdaiPool, sdaiPool.addressesSubscribed, diff --git a/src/dex/sdai/sdai-integration.test.ts b/src/dex/sdai/sdai-integration.test.ts index 5089ace68..deb363f4f 100644 --- a/src/dex/sdai/sdai-integration.test.ts +++ b/src/dex/sdai/sdai-integration.test.ts @@ -19,13 +19,20 @@ const DaiToken = Tokens[network][DaiSymbol]; const amounts = [0n, BI_POWS[18], 2000000000000000000n]; const dexKey = 'SDai'; +const dexHelper = new DummyDexHelper(network); +let blocknumber: number; +let sdai: SDai; describe('SDai', function () { - it('getPoolIdentifiers and getPricesVolume DAI -> sDAI SELL', async function () { - const dexHelper = new DummyDexHelper(network); - const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); - const sdai = new SDai(network, dexKey, dexHelper); + beforeAll(async () => { + blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); + sdai = new SDai(network, dexKey, dexHelper); + if (sdai.initializePricing) { + await sdai.initializePricing(blocknumber); + } + }); + it('getPoolIdentifiers and getPricesVolume DAI -> sDAI SELL', async function () { const pools = await sdai.getPoolIdentifiers( DaiToken, SDaiToken, @@ -51,10 +58,6 @@ describe('SDai', function () { }); it('getPoolIdentifiers and getPricesVolume sDAI -> DAI SELL', async function () { - const dexHelper = new DummyDexHelper(network); - const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); - const sdai = new SDai(network, dexKey, dexHelper); - const pools = await sdai.getPoolIdentifiers( SDaiToken, DaiToken, @@ -80,10 +83,6 @@ describe('SDai', function () { }); it('getPoolIdentifiers and getPricesVolume DAI -> sDAI BUY', async function () { - const dexHelper = new DummyDexHelper(network); - const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); - const sdai = new SDai(network, dexKey, dexHelper); - const pools = await sdai.getPoolIdentifiers( DaiToken, SDaiToken, @@ -109,10 +108,6 @@ describe('SDai', function () { }); it('getPoolIdentifiers and getPricesVolume sDAI -> DAI BUY', async function () { - const dexHelper = new DummyDexHelper(network); - const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); - const sdai = new SDai(network, dexKey, dexHelper); - const pools = await sdai.getPoolIdentifiers( SDaiToken, DaiToken, @@ -138,26 +133,14 @@ describe('SDai', function () { }); it('Dai getTopPoolsForToken', async function () { - const dexHelper = new DummyDexHelper(network); - const makerPsm = new SDai(network, dexKey, dexHelper); - - const poolLiquidity = await makerPsm.getTopPoolsForToken( - DaiToken.address, - 10, - ); + const poolLiquidity = await sdai.getTopPoolsForToken(DaiToken.address, 10); console.log(`${DaiSymbol} Top Pools:`, poolLiquidity); checkPoolsLiquidity(poolLiquidity, DaiToken.address, dexKey); }); it('SDai getTopPoolsForToken', async function () { - const dexHelper = new DummyDexHelper(network); - const makerPsm = new SDai(network, dexKey, dexHelper); - - const poolLiquidity = await makerPsm.getTopPoolsForToken( - SDaiToken.address, - 10, - ); + const poolLiquidity = await sdai.getTopPoolsForToken(SDaiToken.address, 10); console.log(`${SDaiSymbol} Top Pools:`, poolLiquidity); checkPoolsLiquidity(poolLiquidity, SDaiToken.address, dexKey); diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index 743f111f1..f4da19817 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -77,7 +77,7 @@ export class SDai extends SimpleExchange implements IDex { } async initializePricing(blockNumber: number) { - await this.eventPool.generateState(blockNumber); + await this.eventPool.initialize(blockNumber); } async getPoolIdentifiers( From 120a87aa4c65ebae245c4d35f3d8e32f9e637b29 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 9 May 2024 12:39:20 +0100 Subject: [PATCH 13/31] fix: replace sdai holder --- tests/constants-e2e.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index bc9ca6580..88fbd9fc1 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -1068,7 +1068,7 @@ export const Holders: { USDT: '0x8A446971dbB112f3be15bc38C14D44B94D9E94b9', XAUT: '0xc4e161e8d8a4bc4ac762ab33a28bbac5474203d7', R: '0xBfe4c9D3235475C138a61f62e9e72FaD94A3303b', - sDAI: '0x8846163Fedc6b881526A6B48321601b474D40923', + sDAI: '0x4C612E3B15b96Ff9A6faED838F8d07d479a8dD4c', CVX: '0x0aCA67Fa70B142A3b9bF2eD89A81B40ff85dACdC', MIM: '0xa046a8660e66d178ee07ec97c585eeb6aa18c26c', AnkETH: '0xF7260D4ADc48fEefd5a19a9Eb23f9747CeE15C92', From 0f91d5c929450867c12a056d3aebdf7577f2f3b4 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 9 May 2024 13:19:57 +0100 Subject: [PATCH 14/31] fix: buy price calculation --- src/config.ts | 4 +-- src/dex/sdai/sdai.ts | 83 ++++++++++++++++++++++++++++--------------- src/dex/sdai/types.ts | 2 ++ 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/config.ts b/src/config.ts index 4d1076f32..40015cf97 100644 --- a/src/config.ts +++ b/src/config.ts @@ -55,9 +55,9 @@ const baseConfigs: { [network: number]: BaseConfig } = { Adapter03: '0xBAEeb4540f59d30E567a5B563CC0c4587eDd9366', Adapter04: '0x369A2FDb910d432f0a07381a5E3d27572c876713', Adapter05: '0x3329dfa55A40B450952FBE0203167Ae6908E656d', - Adapter06: '0x476e0515eA63B5c3008014Fe5e22Ed126e81f289', + Adapter06: '0x28bb3069aaa8ff9a39480c8c096718d3b354108b', BuyAdapter: '0x84bEF12C9931cE12662cc9F2366b6a5029E4BD29', - BuyAdapter02: '0xe53d24CD81cC81bbf271AD7B02D0d67f851D727c', + BuyAdapter02: '0x40dB7541b0c655a3a70c4713aD6879Ec3861c1FE', }, uniswapV2ExchangeRouterAddress: '0xF9234CB08edb93c0d4a4d4c70cC3FfD070e78e07', diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index f4da19817..edcb20dac 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -21,7 +21,7 @@ import SavingsDaiAbi from '../../abi/sdai/SavingsDai.abi.json'; import { Interface } from 'ethers/lib/utils'; import { SDaiEventPool } from './sdai-pool'; import { BI_POWS } from '../../bigint-constants'; -import { SDAI_DEPOSIT_GAS_COST } from './constants'; +import { SDAI_DEPOSIT_GAS_COST, SDAI_REDEEM_GAS_COST } from './constants'; export class SDai extends SimpleExchange implements IDex { readonly hasConstantPriceLargeAmounts = true; @@ -102,27 +102,39 @@ export class SDai extends SimpleExchange implements IDex { if (!this.isAppropriatePair(srcToken, destToken)) return null; if (!this.eventPool.getState(blockNumber)) return null; - const convertFn = (blockNumber: number, amountIn: bigint) => - this.isDai(srcToken.address) - ? this.eventPool.convertToSDai(amountIn, blockNumber) - : this.eventPool.convertToDai(amountIn, blockNumber); - - const unitIn = BI_POWS[18]; - const unitOut = convertFn(blockNumber, unitIn); - const amountsOut = amounts.map(amountIn => - convertFn(blockNumber, amountIn), - ); - - return [ - { - prices: amountsOut, - unit: unitOut, - gasCost: SDAI_DEPOSIT_GAS_COST, - exchange: this.dexKey, - poolAddresses: [this.sdaiAddress], - data: { exchange: this.sdaiAddress }, - }, - ]; + if (side === SwapSide.SELL) { + const calcSellFn = (blockNumber: number, amountIn: bigint) => + this.isDai(srcToken.address) + ? this.eventPool.convertToSDai(amountIn, blockNumber) + : this.eventPool.convertToDai(amountIn, blockNumber); + + return [ + { + prices: amounts.map(amount => calcSellFn(blockNumber, amount)), + unit: calcSellFn(blockNumber, BI_POWS[18]), + gasCost: SDAI_DEPOSIT_GAS_COST, + exchange: this.dexKey, + data: { exchange: `${this.sdaiAddress}` }, + poolAddresses: [`${this.sdaiAddress}`], + }, + ]; + } else { + const calcBuyFn = (blockNumber: number, amountIn: bigint) => + this.isDai(srcToken.address) + ? this.eventPool.convertToDai(amountIn, blockNumber) + : this.eventPool.convertToSDai(amountIn, blockNumber); + + return [ + { + prices: amounts.map(amount => calcBuyFn(blockNumber, amount)), + unit: calcBuyFn(blockNumber, BI_POWS[18]), + gasCost: SDAI_REDEEM_GAS_COST, + exchange: this.dexKey, + data: { exchange: `${this.sdaiAddress}` }, + poolAddresses: [`${this.sdaiAddress}`], + }, + ]; + } } // Returns estimated gas cost of calldata for this DEX in multiSwap @@ -161,12 +173,25 @@ export class SDai extends SimpleExchange implements IDex { data: SDaiData, side: SwapSide, ): Promise { - const swapData = this.sdaiInterface.encodeFunctionData( - this.isDai(srcToken) ? SDaiFunctions.deposit : SDaiFunctions.redeem, - this.isDai(srcToken) - ? [srcAmount, this.augustusAddress] - : [srcAmount, this.augustusAddress, this.augustusAddress], - ); + const isSell = side === SwapSide.SELL; + const { exchange } = data; + + let swapData: string; + if (this.isDai(srcToken)) { + swapData = this.sdaiInterface.encodeFunctionData( + isSell ? SDaiFunctions.deposit : SDaiFunctions.mint, + [isSell ? srcAmount : destAmount, this.augustusAddress], + ); + } else { + swapData = this.sdaiInterface.encodeFunctionData( + isSell ? SDaiFunctions.redeem : SDaiFunctions.withdraw, + [ + isSell ? srcAmount : destAmount, + this.augustusAddress, + this.augustusAddress, + ], + ); + } return this.buildSimpleParamWithoutWETHConversion( srcToken, @@ -174,7 +199,7 @@ export class SDai extends SimpleExchange implements IDex { destToken, destAmount, swapData, - this.sdaiAddress, + exchange, ); } diff --git a/src/dex/sdai/types.ts b/src/dex/sdai/types.ts index ee6bc8604..169ca34df 100644 --- a/src/dex/sdai/types.ts +++ b/src/dex/sdai/types.ts @@ -11,6 +11,8 @@ export type SDaiParams = { export enum SDaiFunctions { deposit = 'deposit', redeem = 'redeem', + withdraw = 'withdraw', + mint = 'mint', } export type SDaiPoolState = { From 5f91f0e38197fe4a415d6d3de95ecfd51ec494ca Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 9 May 2024 15:01:21 +0100 Subject: [PATCH 15/31] fix: remove cage event --- src/dex/sdai/sdai-events.test.ts | 8 +++----- src/dex/sdai/sdai-pool.ts | 11 +---------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/dex/sdai/sdai-events.test.ts b/src/dex/sdai/sdai-events.test.ts index 23c270c5c..e2192aac2 100644 --- a/src/dex/sdai/sdai-events.test.ts +++ b/src/dex/sdai/sdai-events.test.ts @@ -59,12 +59,10 @@ async function fetchPoolState( describe('SDai Event', function () { const blockNumbers: { [eventName: string]: number[] } = { drip: [19827559, 19827524, 19827163, 19827124, 19827000, 19826892], - // TODO: no matching logs + // TODO: no matching logs, you have to manually call "file" + // from "0xbe8e3e3618f7474f8cb1d074a26affef007e98fb" address // https://etherscan.io/advanced-filter?fadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&tadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&mtd=0x29ae8114%7eFile - file: [], - // TODO: no matching logs - // https://etherscan.io/advanced-filter?fadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&tadd=0x197e90f9fad81970ba7976f33cbd77088e5d7cf7&mtd=0x69245009%7eCage - cage: [], + // file: [19831086] }; const addresses: { [contract: string]: string } = { diff --git a/src/dex/sdai/sdai-pool.ts b/src/dex/sdai/sdai-pool.ts index 13b200fa9..c6d697600 100644 --- a/src/dex/sdai/sdai-pool.ts +++ b/src/dex/sdai/sdai-pool.ts @@ -68,19 +68,10 @@ export class SDaiEventPool extends StatefulEventSubscriber { log: Readonly, blockHeader: Readonly, ): AsyncOrSync | null> { - if (log.topics[0] === CAGE_TOPICHASH) { - return { - dsr: RAY.toString(), - chi: RAY.toString(), - rho: RAY.toString(), - live: false, - }; - } - if (log.topics[0] === FILE_TOPICHASH && log.topics[2] === DSR_TOPIC) { return { ...state, - dsr: BigInt(log.topics[2]).toString(), + dsr: BigInt(log.topics[3]).toString(), }; } From 4ff9913872a8863a4849858c4e9b9d85c74839bd Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 9 May 2024 15:54:54 +0100 Subject: [PATCH 16/31] chore: wording in error msg --- src/dex/sdai/sdai-pool.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dex/sdai/sdai-pool.ts b/src/dex/sdai/sdai-pool.ts index c6d697600..89e7d2a1e 100644 --- a/src/dex/sdai/sdai-pool.ts +++ b/src/dex/sdai/sdai-pool.ts @@ -99,14 +99,14 @@ export class SDaiEventPool extends StatefulEventSubscriber { convertToSDai(daiAmount: bigint, blockNumber: number): bigint { const state = this.getState(blockNumber); - if (!state) throw new Error('Unable to fetch state for SDAI'); + if (!state) throw new Error(`SDai state at ${blockNumber} does not exists`); return (daiAmount * RAY) / calcChi(state); } convertToDai(sdaiAmount: bigint, blockNumber: number): bigint { const state = this.getState(blockNumber); - if (!state) throw new Error('Unable to fetch state for SDAI'); + if (!state) throw new Error(`SDai state at ${blockNumber} does not exists`); return (sdaiAmount * calcChi(state)) / RAY; } From 7c85b79ab2cca7ebcc47d9a4612716f71f947312 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 9 May 2024 17:45:41 +0100 Subject: [PATCH 17/31] feat: validate state script --- src/dex/sdai/scripts/validate-state.ts | 151 +++++++++++++++++++++++++ src/dex/sdai/sdai.ts | 2 +- 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/dex/sdai/scripts/validate-state.ts diff --git a/src/dex/sdai/scripts/validate-state.ts b/src/dex/sdai/scripts/validate-state.ts new file mode 100644 index 000000000..fc512cfd8 --- /dev/null +++ b/src/dex/sdai/scripts/validate-state.ts @@ -0,0 +1,151 @@ +/* eslint-disable no-console */ +import dotenv from 'dotenv'; +dotenv.config(); + +import { Network } from '../../../constants'; +import { DummyDexHelper } from '../../../dex-helper'; +import { SDaiPoolState } from '../types'; +import { BlockHeader } from 'web3-eth'; +import { ethers } from 'ethers'; +import { SDai } from '../sdai'; +import { SDaiConfig } from '../config'; +import { getOnChainState } from '../utils'; +import { Interface } from '@ethersproject/abi'; +import PotAbi from '../../../abi/maker-psm/pot.json'; +import multiABIV2 from '../../../abi/multi-v2.json'; +import Web3 from 'web3'; + +const web3Provider = new Web3(''); +const multiContract = new web3Provider.eth.Contract( + multiABIV2 as any, + '0x5ba1e12693dc8f9c48aad8770482f4739beed696', +); +const network = Network.MAINNET; +const dexKey = 'SDai'; + +const { potAddress } = SDaiConfig[dexKey][network]; +const blockHeaders: Record = {}; + +const dexHelper = new DummyDexHelper(network); +const logger = dexHelper.getLogger(dexKey); + +function preprocessField(value: any): string { + if ( + typeof value === 'bigint' || + typeof value === 'number' || + ethers.BigNumber.isBigNumber(value) + ) { + return value.toString(); + } + return value; +} + +function compareAndLogDifferences( + obj1: T, + obj2: Y, + keyNames: Array, + checkEachKey = false, +) { + let isValid = true; + let keys = keyNames; + + if (checkEachKey) { + // find common keys + const keys1 = Object.keys(obj1); + const keys2 = new Set(Object.keys(obj2)); + keys = keys1.filter(key => keys2.has(key)) as Array; + } + + for (let fieldName of keys) { + const value1 = preprocessField(obj1[fieldName]); + const value2 = preprocessField(obj2[fieldName]); + + if (value1 !== value2) { + console.log( + `${fieldName.toString()} mismatch: actual: ${value1} vs pool: ${value2}`, + ); + isValid = false; + } + } + + return isValid; +} + +async function isPoolStateEqualToReal( + state: SDaiPoolState, + blockNumber: number, +) { + const { rho, chi, dsr, live } = await getOnChainState( + multiContract, + potAddress, + new Interface(PotAbi), + blockNumber, + ); + + const isValidState = compareAndLogDifferences( + state, + { rho, chi, dsr, live }, + ['rho', 'chi', 'dsr', 'dsr'], + ); + + return isValidState; +} + +async function checkPoolStateForBlockRange( + startBlockNumber: number, + endBlockNumber: number, +): Promise { + const network = Network.MAINNET; + const dexKey = 'SDai'; + const dexHelper = new DummyDexHelper(network); + + const sdai = new SDai(network, dexKey, dexHelper); + const pool = await sdai.eventPool; + + const logsToDispatch = await dexHelper.provider.getLogs({ + fromBlock: startBlockNumber, + toBlock: endBlockNumber, + address: potAddress, + }); + + console.log(logsToDispatch.length); + + // group logs by block number + const logsByBlockNumber: Record = {}; + for (let log of logsToDispatch) { + if (!logsByBlockNumber[log.blockNumber]) { + logsByBlockNumber[log.blockNumber] = []; + } + logsByBlockNumber[log.blockNumber].push(log); + } + + const sortedBlocks = Object.keys(logsByBlockNumber) + .map(Number) + .sort((a, b) => Number(a) - Number(b)); + + for (let blockNumber of sortedBlocks) { + if (!blockHeaders[blockNumber]) { + blockHeaders[blockNumber] = await dexHelper.web3Provider.eth.getBlock( + blockNumber, + ); + } + + await pool?.update(logsByBlockNumber[blockNumber], { + [blockNumber]: blockHeaders[blockNumber], + }); + } + + const state = pool?.getState(startBlockNumber) as SDaiPoolState; + return isPoolStateEqualToReal(state, endBlockNumber); +} + +async function main() { + // use findBreakingBlock to find the block where the state is broken + // console.log(await findBreakingBlock(startBlockNumber, endBlockNumber)); + // previously broken block 150502863 + console.log(await checkPoolStateForBlockRange(8928160, 19199247)); +} + +main() + .then(() => console.log('Done')) + .catch(e => console.error(e)); diff --git a/src/dex/sdai/sdai.ts b/src/dex/sdai/sdai.ts index edcb20dac..d9d0d84c6 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/sdai/sdai.ts @@ -30,7 +30,7 @@ export class SDai extends SimpleExchange implements IDex { public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = getDexKeysWithNetwork(SDaiConfig); - protected eventPool: SDaiEventPool; + public readonly eventPool: SDaiEventPool; logger: Logger; constructor( From 33340fcdad4c8f0814d05799ebdff9aabd10917c Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Thu, 9 May 2024 17:53:16 +0100 Subject: [PATCH 18/31] fix: working example script --- src/dex/sdai/scripts/validate-state.ts | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/dex/sdai/scripts/validate-state.ts b/src/dex/sdai/scripts/validate-state.ts index fc512cfd8..2301071df 100644 --- a/src/dex/sdai/scripts/validate-state.ts +++ b/src/dex/sdai/scripts/validate-state.ts @@ -12,14 +12,7 @@ import { SDaiConfig } from '../config'; import { getOnChainState } from '../utils'; import { Interface } from '@ethersproject/abi'; import PotAbi from '../../../abi/maker-psm/pot.json'; -import multiABIV2 from '../../../abi/multi-v2.json'; -import Web3 from 'web3'; - -const web3Provider = new Web3(''); -const multiContract = new web3Provider.eth.Contract( - multiABIV2 as any, - '0x5ba1e12693dc8f9c48aad8770482f4739beed696', -); + const network = Network.MAINNET; const dexKey = 'SDai'; @@ -75,18 +68,18 @@ async function isPoolStateEqualToReal( state: SDaiPoolState, blockNumber: number, ) { - const { rho, chi, dsr, live } = await getOnChainState( - multiContract, + const expected = await getOnChainState( + dexHelper.multiContract, potAddress, new Interface(PotAbi), blockNumber, ); - const isValidState = compareAndLogDifferences( - state, - { rho, chi, dsr, live }, - ['rho', 'chi', 'dsr', 'dsr'], - ); + const isValidState = compareAndLogDifferences(state, expected, [ + 'rho', + 'dsr', + 'dsr', + ]); return isValidState; } @@ -143,7 +136,7 @@ async function main() { // use findBreakingBlock to find the block where the state is broken // console.log(await findBreakingBlock(startBlockNumber, endBlockNumber)); // previously broken block 150502863 - console.log(await checkPoolStateForBlockRange(8928160, 19199247)); + console.log(await checkPoolStateForBlockRange(19199247 - 10000, 19199247)); } main() From 8a83fdf4290e802855e3265cf7c64568f6ee1cd5 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Fri, 10 May 2024 15:04:20 +0100 Subject: [PATCH 19/31] fix: sdai rpow calculation --- src/dex/sdai/config.ts | 3 +++ src/dex/sdai/sdai-pool.ts | 14 +++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index 758471621..ffc3cec4f 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -19,6 +19,9 @@ export const Adapters: { }; } = { [Network.MAINNET]: { + // TODO: uncomment this when SDAI integration got deployed + // [SwapSide.SELL]: [{ name: 'Adapter06', index: 2 }], + // [SwapSide.BUY]: [{ name: 'BuyAdapter02', index: 5 }], [SwapSide.SELL]: [{ name: 'Adapter06', index: 1 }], [SwapSide.BUY]: [{ name: 'BuyAdapter02', index: 4 }], }, diff --git a/src/dex/sdai/sdai-pool.ts b/src/dex/sdai/sdai-pool.ts index 89e7d2a1e..52585a9b6 100644 --- a/src/dex/sdai/sdai-pool.ts +++ b/src/dex/sdai/sdai-pool.ts @@ -23,16 +23,16 @@ const CAGE_TOPICHASH = `0x692450090000000000000000000000000000000000000000000000 const DSR_TOPIC = `0x6473720000000000000000000000000000000000000000000000000000000000`; const rpow = (x: bigint, n: bigint): bigint => { - // REF: https://etherscan.io/address/0x83f20f44975d03b1b09e64809b757c47f942beea#code#L122 - let z = RAY; - if (!x && !n) return z; + if (!x && !n) return RAY; if (!x) return ZERO; - if (n % TWO) z = x; - for (n = n / TWO; n > ZERO; n /= TWO) { + let z = n % TWO > ZERO ? x : RAY; + + for (n = n / TWO; n; n /= TWO) { x = (x * x + HALF) / RAY; - if (n % TWO) continue; - z = (z * x + HALF) / RAY; + if (n % TWO > ZERO) { + z = (z * x + HALF) / RAY; + } } return z; From 87bb7b1603290f02f04e799de214c148ba94491a Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Fri, 10 May 2024 15:12:23 +0100 Subject: [PATCH 20/31] 2.50.6-sdai --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 931857b66..ece11fbdc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.50.2", + "version": "2.50.6-sdai", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From b6872caa3ee876e6dc53ddd97d611344f32d52ab Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Fri, 10 May 2024 16:04:03 +0100 Subject: [PATCH 21/31] fix: use new adapter06 --- src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 40015cf97..fde9ed668 100644 --- a/src/config.ts +++ b/src/config.ts @@ -55,7 +55,7 @@ const baseConfigs: { [network: number]: BaseConfig } = { Adapter03: '0xBAEeb4540f59d30E567a5B563CC0c4587eDd9366', Adapter04: '0x369A2FDb910d432f0a07381a5E3d27572c876713', Adapter05: '0x3329dfa55A40B450952FBE0203167Ae6908E656d', - Adapter06: '0x28bb3069aaa8ff9a39480c8c096718d3b354108b', + Adapter06: '0xe9166234DFB6d3ec05C82404109C02Ca82b16c22', BuyAdapter: '0x84bEF12C9931cE12662cc9F2366b6a5029E4BD29', BuyAdapter02: '0x40dB7541b0c655a3a70c4713aD6879Ec3861c1FE', }, From 611333977abd31b54fda650d6ee598e45546290c Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Fri, 10 May 2024 16:23:21 +0100 Subject: [PATCH 22/31] 2.50.7-sdai --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ece11fbdc..02a87275b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.50.6-sdai", + "version": "2.50.7-sdai", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 070f9dd223f753115da396d07fb490898a96bc19 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 13 May 2024 18:39:40 +0100 Subject: [PATCH 23/31] chore: fix dexkey --- src/dex/sdai/config.ts | 2 +- src/dex/sdai/scripts/validate-state.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index ffc3cec4f..db0814a31 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -4,7 +4,7 @@ import { Network } from '../../constants'; import { SwapSide } from '@paraswap/core'; export const SDaiConfig: DexConfigMap = { - SDai: { + sdai: { [Network.MAINNET]: { sdaiAddress: '0x83F20F44975D03b1b09e64809B757c47f942BEeA', daiAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F', diff --git a/src/dex/sdai/scripts/validate-state.ts b/src/dex/sdai/scripts/validate-state.ts index 2301071df..5e564966f 100644 --- a/src/dex/sdai/scripts/validate-state.ts +++ b/src/dex/sdai/scripts/validate-state.ts @@ -14,7 +14,7 @@ import { Interface } from '@ethersproject/abi'; import PotAbi from '../../../abi/maker-psm/pot.json'; const network = Network.MAINNET; -const dexKey = 'SDai'; +const dexKey = 'sdai'; const { potAddress } = SDaiConfig[dexKey][network]; const blockHeaders: Record = {}; @@ -89,7 +89,7 @@ async function checkPoolStateForBlockRange( endBlockNumber: number, ): Promise { const network = Network.MAINNET; - const dexKey = 'SDai'; + const dexKey = 'sdai'; const dexHelper = new DummyDexHelper(network); const sdai = new SDai(network, dexKey, dexHelper); From 749fc0e3fd4f4b80f283ae3d114598a158aa7090 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 13 May 2024 18:42:35 +0100 Subject: [PATCH 24/31] 2.50.7-sdai.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 02a87275b..b610a3e51 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.50.7-sdai", + "version": "2.50.7-sdai.1", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 7561d8bfc08ccad8937b8c4f5c4d0505a8010056 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 13 May 2024 19:25:54 +0100 Subject: [PATCH 25/31] fix: dex key mismatch --- src/dex/sdai/sdai-events.test.ts | 2 +- src/dex/sdai/sdai-integration.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dex/sdai/sdai-events.test.ts b/src/dex/sdai/sdai-events.test.ts index e2192aac2..1ea822f93 100644 --- a/src/dex/sdai/sdai-events.test.ts +++ b/src/dex/sdai/sdai-events.test.ts @@ -46,7 +46,7 @@ import _ from 'lodash'; */ jest.setTimeout(50 * 1000); -const dexKey = 'SDai'; +const dexKey = 'sdai'; const network = Network.MAINNET; async function fetchPoolState( diff --git a/src/dex/sdai/sdai-integration.test.ts b/src/dex/sdai/sdai-integration.test.ts index deb363f4f..dc0fb5a4e 100644 --- a/src/dex/sdai/sdai-integration.test.ts +++ b/src/dex/sdai/sdai-integration.test.ts @@ -18,7 +18,7 @@ const DaiToken = Tokens[network][DaiSymbol]; const amounts = [0n, BI_POWS[18], 2000000000000000000n]; -const dexKey = 'SDai'; +const dexKey = 'sdai'; const dexHelper = new DummyDexHelper(network); let blocknumber: number; let sdai: SDai; From 9735c3d03facbb60bb0e200b2eb09d2fb387f96b Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Mon, 13 May 2024 19:33:03 +0100 Subject: [PATCH 26/31] chore: remove old comment --- src/dex/sdai/config.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/dex/sdai/config.ts b/src/dex/sdai/config.ts index db0814a31..bbd066b74 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/sdai/config.ts @@ -19,9 +19,6 @@ export const Adapters: { }; } = { [Network.MAINNET]: { - // TODO: uncomment this when SDAI integration got deployed - // [SwapSide.SELL]: [{ name: 'Adapter06', index: 2 }], - // [SwapSide.BUY]: [{ name: 'BuyAdapter02', index: 5 }], [SwapSide.SELL]: [{ name: 'Adapter06', index: 1 }], [SwapSide.BUY]: [{ name: 'BuyAdapter02', index: 4 }], }, From 9666e350927d7e9c76caae4f3ec1c7ad060251b9 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Tue, 14 May 2024 13:35:41 +0100 Subject: [PATCH 27/31] chore: rename sdai -> spark --- src/dex/index.ts | 4 +- src/dex/{sdai => spark}/config.ts | 6 +-- src/dex/{sdai => spark}/constants.ts | 0 .../{sdai => spark}/scripts/validate-state.ts | 16 ++++---- .../spark-e2e.test.ts} | 2 +- .../spark-events.test.ts} | 12 +++--- .../spark-integration.test.ts} | 37 ++++++++++--------- .../sdai-pool.ts => spark/spark-sdai-pool.ts} | 14 +++---- src/dex/{sdai/sdai.ts => spark/spark.ts} | 25 +++++++------ src/dex/{sdai => spark}/types.ts | 8 ++-- src/dex/{sdai => spark}/utils.ts | 5 +-- 11 files changed, 67 insertions(+), 62 deletions(-) rename src/dex/{sdai => spark}/config.ts (85%) rename src/dex/{sdai => spark}/constants.ts (100%) rename src/dex/{sdai => spark}/scripts/validate-state.ts (91%) rename src/dex/{sdai/sdai-e2e.test.ts => spark/spark-e2e.test.ts} (99%) rename src/dex/{sdai/sdai-events.test.ts => spark/spark-events.test.ts} (93%) rename src/dex/{sdai/sdai-integration.test.ts => spark/spark-integration.test.ts} (81%) rename src/dex/{sdai/sdai-pool.ts => spark/spark-sdai-pool.ts} (87%) rename src/dex/{sdai/sdai.ts => spark/spark.ts} (90%) rename src/dex/{sdai => spark}/types.ts (65%) rename src/dex/{sdai => spark}/utils.ts (89%) diff --git a/src/dex/index.ts b/src/dex/index.ts index bb5af96a3..929e93544 100644 --- a/src/dex/index.ts +++ b/src/dex/index.ts @@ -87,7 +87,7 @@ import { Wombat } from './wombat/wombat'; import { Swell } from './swell/swell'; import { PharaohV1 } from './solidly/forks-override/pharaohV1'; import { EtherFi } from './etherfi'; -import { SDai } from './sdai/sdai'; +import { Spark } from './spark/spark'; const LegacyDexes = [ CurveV2, @@ -171,7 +171,7 @@ const Dexes = [ Wombat, Swell, PharaohV1, - SDai, + Spark, ]; export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder< diff --git a/src/dex/sdai/config.ts b/src/dex/spark/config.ts similarity index 85% rename from src/dex/sdai/config.ts rename to src/dex/spark/config.ts index bbd066b74..d92728ea8 100644 --- a/src/dex/sdai/config.ts +++ b/src/dex/spark/config.ts @@ -1,10 +1,10 @@ -import { SDaiParams } from './types'; +import { SparkParams } from './types'; import { DexConfigMap } from '../../types'; import { Network } from '../../constants'; import { SwapSide } from '@paraswap/core'; -export const SDaiConfig: DexConfigMap = { - sdai: { +export const SDaiConfig: DexConfigMap = { + Spark: { [Network.MAINNET]: { sdaiAddress: '0x83F20F44975D03b1b09e64809B757c47f942BEeA', daiAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F', diff --git a/src/dex/sdai/constants.ts b/src/dex/spark/constants.ts similarity index 100% rename from src/dex/sdai/constants.ts rename to src/dex/spark/constants.ts diff --git a/src/dex/sdai/scripts/validate-state.ts b/src/dex/spark/scripts/validate-state.ts similarity index 91% rename from src/dex/sdai/scripts/validate-state.ts rename to src/dex/spark/scripts/validate-state.ts index 5e564966f..3ef032cb7 100644 --- a/src/dex/sdai/scripts/validate-state.ts +++ b/src/dex/spark/scripts/validate-state.ts @@ -4,17 +4,17 @@ dotenv.config(); import { Network } from '../../../constants'; import { DummyDexHelper } from '../../../dex-helper'; -import { SDaiPoolState } from '../types'; +import { SparkSDaiPoolState } from '../types'; import { BlockHeader } from 'web3-eth'; import { ethers } from 'ethers'; -import { SDai } from '../sdai'; +import { Spark } from '../spark'; import { SDaiConfig } from '../config'; import { getOnChainState } from '../utils'; import { Interface } from '@ethersproject/abi'; import PotAbi from '../../../abi/maker-psm/pot.json'; const network = Network.MAINNET; -const dexKey = 'sdai'; +const dexKey = 'Spark'; const { potAddress } = SDaiConfig[dexKey][network]; const blockHeaders: Record = {}; @@ -65,7 +65,7 @@ function compareAndLogDifferences( } async function isPoolStateEqualToReal( - state: SDaiPoolState, + state: SparkSDaiPoolState, blockNumber: number, ) { const expected = await getOnChainState( @@ -89,11 +89,11 @@ async function checkPoolStateForBlockRange( endBlockNumber: number, ): Promise { const network = Network.MAINNET; - const dexKey = 'sdai'; + const dexKey = 'Spark'; const dexHelper = new DummyDexHelper(network); - const sdai = new SDai(network, dexKey, dexHelper); - const pool = await sdai.eventPool; + const spark = new Spark(network, dexKey, dexHelper); + const pool = await spark.eventPool; const logsToDispatch = await dexHelper.provider.getLogs({ fromBlock: startBlockNumber, @@ -128,7 +128,7 @@ async function checkPoolStateForBlockRange( }); } - const state = pool?.getState(startBlockNumber) as SDaiPoolState; + const state = pool?.getState(startBlockNumber) as SparkSDaiPoolState; return isPoolStateEqualToReal(state, endBlockNumber); } diff --git a/src/dex/sdai/sdai-e2e.test.ts b/src/dex/spark/spark-e2e.test.ts similarity index 99% rename from src/dex/sdai/sdai-e2e.test.ts rename to src/dex/spark/spark-e2e.test.ts index 83a1b9182..aba536593 100644 --- a/src/dex/sdai/sdai-e2e.test.ts +++ b/src/dex/spark/spark-e2e.test.ts @@ -73,7 +73,7 @@ function testForNetwork( } describe('sDAI E2E', () => { - const dexKey = 'sdai'; + const dexKey = 'Spark'; describe('Mainnet', () => { const network = Network.MAINNET; diff --git a/src/dex/sdai/sdai-events.test.ts b/src/dex/spark/spark-events.test.ts similarity index 93% rename from src/dex/sdai/sdai-events.test.ts rename to src/dex/spark/spark-events.test.ts index 1ea822f93..504cc8deb 100644 --- a/src/dex/sdai/sdai-events.test.ts +++ b/src/dex/spark/spark-events.test.ts @@ -1,12 +1,12 @@ import dotenv from 'dotenv'; dotenv.config(); -import { SDaiEventPool } from './sdai-pool'; +import { SparkSDaiEventPool } from './spark-sdai-pool'; import { SDaiConfig } from './config'; import { Network } from '../../constants'; import { DummyDexHelper } from '../../dex-helper/index'; import { testEventSubscriber } from '../../../tests/utils-events'; -import { SDaiPoolState } from './types'; +import { SparkSDaiPoolState } from './types'; import PotAbi from '../../abi/maker-psm/pot.json'; import { Interface } from '@ethersproject/abi'; @@ -46,13 +46,13 @@ import _ from 'lodash'; */ jest.setTimeout(50 * 1000); -const dexKey = 'sdai'; +const dexKey = 'Spark'; const network = Network.MAINNET; async function fetchPoolState( - sdaiPool: SDaiEventPool, + sdaiPool: SparkSDaiEventPool, blockNumber: number, -): Promise { +): Promise { return sdaiPool.generateState(blockNumber); } @@ -76,7 +76,7 @@ describe('SDai Event', function () { const dexHelper = new DummyDexHelper(network); const logger = dexHelper.getLogger(dexKey); - const sdaiPool = new SDaiEventPool( + const sdaiPool = new SparkSDaiEventPool( dexKey, dexHelper, addresses.potAddress, diff --git a/src/dex/sdai/sdai-integration.test.ts b/src/dex/spark/spark-integration.test.ts similarity index 81% rename from src/dex/sdai/sdai-integration.test.ts rename to src/dex/spark/spark-integration.test.ts index dc0fb5a4e..61c050dd8 100644 --- a/src/dex/sdai/sdai-integration.test.ts +++ b/src/dex/spark/spark-integration.test.ts @@ -6,7 +6,7 @@ import { Network, SwapSide } from '../../constants'; import { checkPoolPrices, checkPoolsLiquidity } from '../../../tests/utils'; import { Tokens } from '../../../tests/constants-e2e'; import { BI_POWS } from '../../bigint-constants'; -import { SDai } from './sdai'; +import { Spark } from './spark'; const network = Network.MAINNET; @@ -18,22 +18,22 @@ const DaiToken = Tokens[network][DaiSymbol]; const amounts = [0n, BI_POWS[18], 2000000000000000000n]; -const dexKey = 'sdai'; +const dexKey = 'Spark'; const dexHelper = new DummyDexHelper(network); let blocknumber: number; -let sdai: SDai; +let spark: Spark; -describe('SDai', function () { +describe('Spark', function () { beforeAll(async () => { blocknumber = await dexHelper.web3Provider.eth.getBlockNumber(); - sdai = new SDai(network, dexKey, dexHelper); - if (sdai.initializePricing) { - await sdai.initializePricing(blocknumber); + spark = new Spark(network, dexKey, dexHelper); + if (spark.initializePricing) { + await spark.initializePricing(blocknumber); } }); it('getPoolIdentifiers and getPricesVolume DAI -> sDAI SELL', async function () { - const pools = await sdai.getPoolIdentifiers( + const pools = await spark.getPoolIdentifiers( DaiToken, SDaiToken, SwapSide.SELL, @@ -43,7 +43,7 @@ describe('SDai', function () { expect(pools.length).toBeGreaterThan(0); - const poolPrices = await sdai.getPricesVolume( + const poolPrices = await spark.getPricesVolume( DaiToken, SDaiToken, amounts, @@ -58,7 +58,7 @@ describe('SDai', function () { }); it('getPoolIdentifiers and getPricesVolume sDAI -> DAI SELL', async function () { - const pools = await sdai.getPoolIdentifiers( + const pools = await spark.getPoolIdentifiers( SDaiToken, DaiToken, SwapSide.SELL, @@ -68,7 +68,7 @@ describe('SDai', function () { expect(pools.length).toBeGreaterThan(0); - const poolPrices = await sdai.getPricesVolume( + const poolPrices = await spark.getPricesVolume( SDaiToken, DaiToken, amounts, @@ -83,7 +83,7 @@ describe('SDai', function () { }); it('getPoolIdentifiers and getPricesVolume DAI -> sDAI BUY', async function () { - const pools = await sdai.getPoolIdentifiers( + const pools = await spark.getPoolIdentifiers( DaiToken, SDaiToken, SwapSide.BUY, @@ -93,7 +93,7 @@ describe('SDai', function () { expect(pools.length).toBeGreaterThan(0); - const poolPrices = await sdai.getPricesVolume( + const poolPrices = await spark.getPricesVolume( DaiToken, SDaiToken, amounts, @@ -108,7 +108,7 @@ describe('SDai', function () { }); it('getPoolIdentifiers and getPricesVolume sDAI -> DAI BUY', async function () { - const pools = await sdai.getPoolIdentifiers( + const pools = await spark.getPoolIdentifiers( SDaiToken, DaiToken, SwapSide.BUY, @@ -118,7 +118,7 @@ describe('SDai', function () { expect(pools.length).toBeGreaterThan(0); - const poolPrices = await sdai.getPricesVolume( + const poolPrices = await spark.getPricesVolume( SDaiToken, DaiToken, amounts, @@ -133,14 +133,17 @@ describe('SDai', function () { }); it('Dai getTopPoolsForToken', async function () { - const poolLiquidity = await sdai.getTopPoolsForToken(DaiToken.address, 10); + const poolLiquidity = await spark.getTopPoolsForToken(DaiToken.address, 10); console.log(`${DaiSymbol} Top Pools:`, poolLiquidity); checkPoolsLiquidity(poolLiquidity, DaiToken.address, dexKey); }); it('SDai getTopPoolsForToken', async function () { - const poolLiquidity = await sdai.getTopPoolsForToken(SDaiToken.address, 10); + const poolLiquidity = await spark.getTopPoolsForToken( + SDaiToken.address, + 10, + ); console.log(`${SDaiSymbol} Top Pools:`, poolLiquidity); checkPoolsLiquidity(poolLiquidity, SDaiToken.address, dexKey); diff --git a/src/dex/sdai/sdai-pool.ts b/src/dex/spark/spark-sdai-pool.ts similarity index 87% rename from src/dex/sdai/sdai-pool.ts rename to src/dex/spark/spark-sdai-pool.ts index 52585a9b6..a56ecbd98 100644 --- a/src/dex/sdai/sdai-pool.ts +++ b/src/dex/spark/spark-sdai-pool.ts @@ -5,7 +5,7 @@ import { Interface } from '@ethersproject/abi'; import type { IDexHelper } from '../../dex-helper'; import type { AsyncOrSync, DeepReadonly } from 'ts-essentials'; import type { Address, BlockHeader, Log, Logger } from '../../types'; -import type { SDaiPoolState } from './types'; +import type { SparkSDaiPoolState } from './types'; import { getOnChainState } from './utils'; const RAY = BI_POWS[27]; @@ -38,7 +38,7 @@ const rpow = (x: bigint, n: bigint): bigint => { return z; }; -const calcChi = (state: SDaiPoolState, currentTimestamp?: number) => { +const calcChi = (state: SparkSDaiPoolState, currentTimestamp?: number) => { currentTimestamp ||= Math.floor(Date.now() / 1000); if (!state.live) return RAY; @@ -51,7 +51,7 @@ const calcChi = (state: SDaiPoolState, currentTimestamp?: number) => { return now > rho ? (rpow(dsr, now - rho) * chi) / RAY : chi; }; -export class SDaiEventPool extends StatefulEventSubscriber { +export class SparkSDaiEventPool extends StatefulEventSubscriber { constructor( parentName: string, protected dexHelper: IDexHelper, @@ -59,15 +59,15 @@ export class SDaiEventPool extends StatefulEventSubscriber { private potInterface: Interface, logger: Logger, ) { - super(parentName, 'sdai', dexHelper, logger); + super(parentName, 'Spark', dexHelper, logger); this.addressesSubscribed = [potAddress]; } protected processLog( - state: DeepReadonly, + state: DeepReadonly, log: Readonly, blockHeader: Readonly, - ): AsyncOrSync | null> { + ): AsyncOrSync | null> { if (log.topics[0] === FILE_TOPICHASH && log.topics[2] === DSR_TOPIC) { return { ...state, @@ -88,7 +88,7 @@ export class SDaiEventPool extends StatefulEventSubscriber { async generateState( blockNumber: number | 'latest' = 'latest', - ): Promise> { + ): Promise> { return getOnChainState( this.dexHelper.multiContract, this.potAddress, diff --git a/src/dex/sdai/sdai.ts b/src/dex/spark/spark.ts similarity index 90% rename from src/dex/sdai/sdai.ts rename to src/dex/spark/spark.ts index d9d0d84c6..5335651f9 100644 --- a/src/dex/sdai/sdai.ts +++ b/src/dex/spark/spark.ts @@ -1,6 +1,6 @@ import { SimpleExchange } from '../simple-exchange'; import { IDex } from '../idex'; -import { SDaiParams, SDaiData, SDaiFunctions } from './types'; +import { SparkParams, SparkData, SparkSDaiFunctions } from './types'; import { Network, SwapSide } from '../../constants'; import { getDexKeysWithNetwork } from '../../utils'; import { Adapters, SDaiConfig } from './config'; @@ -19,18 +19,21 @@ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; import PotAbi from '../../abi/maker-psm/pot.json'; import SavingsDaiAbi from '../../abi/sdai/SavingsDai.abi.json'; import { Interface } from 'ethers/lib/utils'; -import { SDaiEventPool } from './sdai-pool'; +import { SparkSDaiEventPool } from './spark-sdai-pool'; import { BI_POWS } from '../../bigint-constants'; import { SDAI_DEPOSIT_GAS_COST, SDAI_REDEEM_GAS_COST } from './constants'; -export class SDai extends SimpleExchange implements IDex { +export class Spark + extends SimpleExchange + implements IDex +{ readonly hasConstantPriceLargeAmounts = true; readonly isFeeOnTransferSupported = false; public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = getDexKeysWithNetwork(SDaiConfig); - public readonly eventPool: SDaiEventPool; + public readonly eventPool: SparkSDaiEventPool; logger: Logger; constructor( @@ -48,7 +51,7 @@ export class SDai extends SimpleExchange implements IDex { ) { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); - this.eventPool = new SDaiEventPool( + this.eventPool = new SparkSDaiEventPool( this.dexKey, dexHelper, this.potAddress, @@ -98,7 +101,7 @@ export class SDai extends SimpleExchange implements IDex { side: SwapSide, blockNumber: number, limitPools?: string[], - ): Promise> { + ): Promise> { if (!this.isAppropriatePair(srcToken, destToken)) return null; if (!this.eventPool.getState(blockNumber)) return null; @@ -138,7 +141,7 @@ export class SDai extends SimpleExchange implements IDex { } // Returns estimated gas cost of calldata for this DEX in multiSwap - getCalldataGasCost(poolPrices: PoolPrices): number | number[] { + getCalldataGasCost(poolPrices: PoolPrices): number | number[] { return CALLDATA_GAS_COST.DEX_NO_PAYLOAD; } @@ -170,7 +173,7 @@ export class SDai extends SimpleExchange implements IDex { destToken: string, srcAmount: string, destAmount: string, - data: SDaiData, + data: SparkData, side: SwapSide, ): Promise { const isSell = side === SwapSide.SELL; @@ -179,12 +182,12 @@ export class SDai extends SimpleExchange implements IDex { let swapData: string; if (this.isDai(srcToken)) { swapData = this.sdaiInterface.encodeFunctionData( - isSell ? SDaiFunctions.deposit : SDaiFunctions.mint, + isSell ? SparkSDaiFunctions.deposit : SparkSDaiFunctions.mint, [isSell ? srcAmount : destAmount, this.augustusAddress], ); } else { swapData = this.sdaiInterface.encodeFunctionData( - isSell ? SDaiFunctions.redeem : SDaiFunctions.withdraw, + isSell ? SparkSDaiFunctions.redeem : SparkSDaiFunctions.withdraw, [ isSell ? srcAmount : destAmount, this.augustusAddress, @@ -208,7 +211,7 @@ export class SDai extends SimpleExchange implements IDex { destToken: string, srcAmount: string, destAmount: string, - data: SDaiData, + data: SparkData, side: SwapSide, ): AdapterExchangeParam { const { exchange } = data; diff --git a/src/dex/sdai/types.ts b/src/dex/spark/types.ts similarity index 65% rename from src/dex/sdai/types.ts rename to src/dex/spark/types.ts index 169ca34df..393c31357 100644 --- a/src/dex/sdai/types.ts +++ b/src/dex/spark/types.ts @@ -1,21 +1,21 @@ import { Address } from '../../types'; -export type SDaiData = { exchange: Address }; +export type SparkData = { exchange: Address }; -export type SDaiParams = { +export type SparkParams = { sdaiAddress: Address; daiAddress: Address; potAddress: Address; }; -export enum SDaiFunctions { +export enum SparkSDaiFunctions { deposit = 'deposit', redeem = 'redeem', withdraw = 'withdraw', mint = 'mint', } -export type SDaiPoolState = { +export type SparkSDaiPoolState = { rho: string; chi: string; dsr: string; diff --git a/src/dex/sdai/utils.ts b/src/dex/spark/utils.ts similarity index 89% rename from src/dex/sdai/utils.ts rename to src/dex/spark/utils.ts index 82ed53104..84cf9aea1 100644 --- a/src/dex/sdai/utils.ts +++ b/src/dex/spark/utils.ts @@ -1,7 +1,6 @@ import { Contract } from 'web3-eth-contract'; -import { SDaiPoolState } from './types'; +import { SparkSDaiPoolState } from './types'; import { Interface, AbiCoder } from '@ethersproject/abi'; -import { currentBigIntTimestampInS } from '../../utils'; const coder = new AbiCoder(); @@ -13,7 +12,7 @@ export async function getOnChainState( potAddress: string, potInterface: Interface, blockNumber: number | 'latest', -): Promise { +): Promise { const data: { returnData: any[] } = await multiContract.methods .aggregate([ { From a90976d6e7f92fafd95d7ac8e966aa506956f9d7 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Tue, 14 May 2024 13:39:53 +0100 Subject: [PATCH 28/31] 2.50.7-sdai.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b610a3e51..d629095b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.50.7-sdai.1", + "version": "2.50.7-sdai.2", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From f207e74ef0f4a42ab9a3fd4b95d7118650e4915d Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Tue, 14 May 2024 15:19:17 +0100 Subject: [PATCH 29/31] fix: update sdai pool name --- src/dex/spark/spark-events.test.ts | 5 +---- src/dex/spark/spark-sdai-pool.ts | 3 ++- src/dex/spark/spark.ts | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/dex/spark/spark-events.test.ts b/src/dex/spark/spark-events.test.ts index 504cc8deb..a73ff3a7f 100644 --- a/src/dex/spark/spark-events.test.ts +++ b/src/dex/spark/spark-events.test.ts @@ -78,6 +78,7 @@ describe('SDai Event', function () { const sdaiPool = new SparkSDaiEventPool( dexKey, + `dai-sdai-pool`, dexHelper, addresses.potAddress, new Interface(PotAbi), @@ -93,10 +94,6 @@ describe('SDai Event', function () { blockNumber, `${dexKey}_${sdaiPool}`, dexHelper.provider, - (state, expected) => { - // TODO: - expect(_.omit(state, ['chi'])).toEqual(_.omit(expected, ['chi'])); - }, ); }); }); diff --git a/src/dex/spark/spark-sdai-pool.ts b/src/dex/spark/spark-sdai-pool.ts index a56ecbd98..e03f9559b 100644 --- a/src/dex/spark/spark-sdai-pool.ts +++ b/src/dex/spark/spark-sdai-pool.ts @@ -54,12 +54,13 @@ const calcChi = (state: SparkSDaiPoolState, currentTimestamp?: number) => { export class SparkSDaiEventPool extends StatefulEventSubscriber { constructor( parentName: string, + poolName: string, protected dexHelper: IDexHelper, private potAddress: Address, private potInterface: Interface, logger: Logger, ) { - super(parentName, 'Spark', dexHelper, logger); + super(parentName, poolName, dexHelper, logger); this.addressesSubscribed = [potAddress]; } diff --git a/src/dex/spark/spark.ts b/src/dex/spark/spark.ts index 5335651f9..facc48f4c 100644 --- a/src/dex/spark/spark.ts +++ b/src/dex/spark/spark.ts @@ -53,6 +53,7 @@ export class Spark this.logger = dexHelper.getLogger(dexKey); this.eventPool = new SparkSDaiEventPool( this.dexKey, + `${this.daiAddress}_${this.sdaiAddress}`, dexHelper, this.potAddress, this.potInterface, From 78111372400908a6a12675f8528d921c8d205957 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Tue, 14 May 2024 15:29:06 +0100 Subject: [PATCH 30/31] 2.50.7-sdai.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d629095b7..d0e70fa38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.50.7-sdai.2", + "version": "2.50.7-sdai.3", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From f18a19ab044d786326c0f3eba5e907c08dc9aa83 Mon Sep 17 00:00:00 2001 From: George Molchanov Date: Tue, 14 May 2024 16:53:55 +0100 Subject: [PATCH 31/31] 2.50.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0e70fa38..0b2237b3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.50.7-sdai.3", + "version": "2.50.8", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib",