From 483eb2f623d74f391526de47a296baad6e4eb896 Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 4 Sep 2024 14:28:32 +0100 Subject: [PATCH 01/58] feat: add wrap data --- src/utils/wrap-data.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/utils/wrap-data.ts diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts new file mode 100644 index 0000000..923c731 --- /dev/null +++ b/src/utils/wrap-data.ts @@ -0,0 +1,40 @@ +export interface ComponentWrapData { + // wrap adapter integration name as listed in the IntegrationRegistry for the wrapModule + integrationName: string + // optional wrapData passed to the wrapAdapter + wrapData: string +} + +// TODO: check that adapter names are static +const aaveV2WrapV2AdapterName = 'Aave_V2_Wrap_V2_Adapter' +const aaveV3WrapV2AdapterName = 'Aave_V3_Wrap_V2_Adapter' +const compoundV3WrapV2AdapterName = 'Compound_V3_USDC_Wrap_V2_Adapter' +const erc4626WrapV2AdapterName = 'ERC4626_Wrap_V2_Adapter' +const ZERO_BYTES = '0x0000000000000000000000000000000000000000' + +export function getWrapData(tokenSymbol: string): ComponentWrapData[] { + // TODO: add check once token is available + // if (tokenSymbol !== USDCY.symbol) return [] + return [ + { + integrationName: '', + wrapData: ZERO_BYTES, + }, + { + integrationName: aaveV3WrapV2AdapterName, + wrapData: ZERO_BYTES, + }, + { + integrationName: compoundV3WrapV2AdapterName, + wrapData: ZERO_BYTES, + }, + { + integrationName: aaveV2WrapV2AdapterName, + wrapData: ZERO_BYTES, + }, + { + integrationName: erc4626WrapV2AdapterName, + wrapData: ZERO_BYTES, + }, + ] +} From 9a90e28b0fefd3c05c261390236a9f6d8aaead60 Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 4 Sep 2024 14:34:20 +0100 Subject: [PATCH 02/58] test: add test for wrap data --- src/utils/wrap-data.test.ts | 21 +++++++++++++++++++++ src/utils/wrap-data.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/utils/wrap-data.test.ts diff --git a/src/utils/wrap-data.test.ts b/src/utils/wrap-data.test.ts new file mode 100644 index 0000000..47dc2f9 --- /dev/null +++ b/src/utils/wrap-data.test.ts @@ -0,0 +1,21 @@ +import { getWrapData } from './wrap-data' + +describe('getWrapData()', () => { + test('returns empty array for unsupported index', async () => { + const wrapData = getWrapData('DPI') + expect(wrapData.length).toBe(0) + }) + + test.only('returns correct wrap data for USDCY', async () => { + const wrapData = getWrapData('USDCY') + expect(wrapData.length).toBe(5) + expect(wrapData[0].integrationName).toBe('') + expect(wrapData[1].integrationName).toBe('Aave_V3_Wrap_V2_Adapter') + expect(wrapData[2].integrationName).toBe('Compound_V3_USDC_Wrap_V2_Adapter') + expect(wrapData[3].integrationName).toBe('Aave_V2_Wrap_V2_Adapter') + expect(wrapData[4].integrationName).toBe('ERC4626_Wrap_V2_Adapter') + wrapData.forEach((data) => { + expect(data.wrapData).toBe('0x0000000000000000000000000000000000000000') + }) + }) +}) diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts index 923c731..907cd66 100644 --- a/src/utils/wrap-data.ts +++ b/src/utils/wrap-data.ts @@ -14,7 +14,7 @@ const ZERO_BYTES = '0x0000000000000000000000000000000000000000' export function getWrapData(tokenSymbol: string): ComponentWrapData[] { // TODO: add check once token is available - // if (tokenSymbol !== USDCY.symbol) return [] + if (tokenSymbol !== 'USDCY') return [] return [ { integrationName: '', From 271abee98100f2f1b85c23bbce2f03a304b0aefc Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 4 Sep 2024 14:44:16 +0100 Subject: [PATCH 03/58] feat: add issuance --- src/utils/issuanceModules.test.ts | 8 ++++++++ src/utils/issuanceModules.ts | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/utils/issuanceModules.test.ts b/src/utils/issuanceModules.test.ts index b72faca..f6cc8ed 100644 --- a/src/utils/issuanceModules.test.ts +++ b/src/utils/issuanceModules.test.ts @@ -59,6 +59,14 @@ describe('getIssuanceModule() - Mainnet - IndexProtocol', () => { expect(issuanceModule.isDebtIssuance).toBe(true) }) + test('returns debt issuance module v2 for USDCY', () => { + const expectedModule = IndexDebtIssuanceModuleV2Address + // FIXME: change to USDCY once available + const issuanceModule = getIssuanceModule('USDCY') + expect(issuanceModule.address).toEqual(expectedModule) + expect(issuanceModule.isDebtIssuance).toBe(true) + }) + test('returns debt issuance module v2 for wsETH2', async () => { const expectedModule = IndexDebtIssuanceModuleV2Address const issuanceModule = getIssuanceModule(wsETH2.symbol) diff --git a/src/utils/issuanceModules.ts b/src/utils/issuanceModules.ts index d6c45db..28dbe40 100644 --- a/src/utils/issuanceModules.ts +++ b/src/utils/issuanceModules.ts @@ -64,6 +64,8 @@ export function getIssuanceModule( isDebtIssuance: true, } case GitcoinStakedETHIndex.symbol: + // TODO: add index token + case 'USDCY': case wsETH2.symbol: return { address: IndexDebtIssuanceModuleV2Address, isDebtIssuance: true } case InterestCompoundingETHIndex.symbol: From 0e3a2b20e3cd57c232bd76c4a072793f461262bd Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 4 Sep 2024 14:45:19 +0100 Subject: [PATCH 04/58] build: export wrap data utils --- src/utils/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/index.ts b/src/utils/index.ts index 6dde6d9..bb00abb 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,3 +7,4 @@ export * from './slippage' export * from './swap-data' export * from './tokens' export * from './UniswapPath' +export * from './wrap-data' From ae58da0f5c77b6f468240af1520fe05e5aa8c5ca Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 4 Sep 2024 14:55:46 +0100 Subject: [PATCH 05/58] feat: add flash mint wrapped contract --- src/constants/abis/FlashMintWrapped.json | 1 + src/constants/contracts.ts | 3 +++ src/utils/contracts.test.ts | 15 +++++++++++++++ src/utils/contracts.ts | 16 ++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 src/constants/abis/FlashMintWrapped.json diff --git a/src/constants/abis/FlashMintWrapped.json b/src/constants/abis/FlashMintWrapped.json new file mode 100644 index 0000000..0fa132b --- /dev/null +++ b/src/constants/abis/FlashMintWrapped.json @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"address","name":"quickRouter","type":"address"},{"internalType":"address","name":"sushiRouter","type":"address"},{"internalType":"address","name":"uniV3Router","type":"address"},{"internalType":"address","name":"uniV3Quoter","type":"address"},{"internalType":"address","name":"curveAddressProvider","type":"address"},{"internalType":"address","name":"curveCalculator","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"internalType":"struct DEXAdapter.Addresses","name":"_dexAddresses","type":"tuple"},{"internalType":"contract IController","name":"_setController","type":"address"},{"internalType":"contract IDebtIssuanceModule","name":"_issuanceModule","type":"address"},{"internalType":"address","name":"_wrapModule","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"_inputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountInputToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountSetIssued","type":"uint256"}],"name":"FlashMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"_outputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountSetRedeemed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountOutputToken","type":"uint256"}],"name":"FlashRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"}],"name":"approveSetToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dexAdapter","outputs":[{"internalType":"address","name":"quickRouter","type":"address"},{"internalType":"address","name":"sushiRouter","type":"address"},{"internalType":"address","name":"uniV3Router","type":"address"},{"internalType":"address","name":"uniV3Quoter","type":"address"},{"internalType":"address","name":"curveAddressProvider","type":"address"},{"internalType":"address","name":"curveCalculator","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"address","name":"_inputToken","type":"address"},{"internalType":"uint256","name":"_setAmount","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"}],"name":"getIssueExactSet","outputs":[{"internalType":"uint256","name":"amountInputNeeded","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"address","name":"_outputToken","type":"address"},{"internalType":"uint256","name":"_setAmount","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"}],"name":"getRedeemExactSet","outputs":[{"internalType":"uint256","name":"amountOutputReceived","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"issuanceModule","outputs":[{"internalType":"contract IDebtIssuanceModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"contract IERC20","name":"_inputToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_maxAmountInputToken","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_wrapData","type":"tuple[]"}],"name":"issueExactSetFromERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_wrapData","type":"tuple[]"}],"name":"issueExactSetFromETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"contract IERC20","name":"_outputToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_minOutputReceive","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_unwrapData","type":"tuple[]"}],"name":"redeemExactSetForERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_minOutputReceive","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_unwrapData","type":"tuple[]"}],"name":"redeemExactSetForETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setController","outputs":[{"internalType":"contract IController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_tokens","type":"address[]"},{"internalType":"address payable","name":"_to","type":"address"}],"name":"withdrawTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"wrapModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/src/constants/contracts.ts b/src/constants/contracts.ts index 6ec420c..4451646 100644 --- a/src/constants/contracts.ts +++ b/src/constants/contracts.ts @@ -10,6 +10,9 @@ export const Contracts: { [key: number]: any } = { DebtIssuanceModuleV3: '0xa30E87311407dDcF1741901A8F359b6005252F22', FlashMintLeveragedExtended: '0xE6c18c4C9FC6909EDa546649EBE33A8159256CBE', }, + [ChainId.Mainnet]: { + FlashMintWrapped: '0x5C0D0a9a0c3A0a5B591496fF894686893b69FaA2', + }, } // Index Protocol diff --git a/src/utils/contracts.test.ts b/src/utils/contracts.test.ts index e7d2c0b..a703276 100644 --- a/src/utils/contracts.test.ts +++ b/src/utils/contracts.test.ts @@ -32,6 +32,7 @@ import { getFlashMintZeroExContractForToken, getFlashMintLeveragedContractForToken, getFlashMintHyEthContract, + getFlashMintWrappedContract, } from './contracts' describe('getExchangeIssuanceLeveragedContractAddress()', () => { @@ -140,6 +141,20 @@ describe('getFlashMintLeveragedContractForToken()', () => { }) }) +describe('getFlashMintWrappedContract()', () => { + test('returns correct contract', async () => { + const expectedAddress = Contracts[ChainId.Mainnet].FlashMintWrapped + const contract = getFlashMintWrappedContract(undefined) + expect(contract.address).toEqual(expectedAddress) + expect(contract.functions.getIssueExactSet).toBeDefined() + expect(contract.functions.getRedeemExactSet).toBeDefined() + expect(contract.functions.issueExactSetFromERC20).toBeDefined() + expect(contract.functions.issueExactSetFromETH).toBeDefined() + expect(contract.functions.redeemExactSetForERC20).toBeDefined() + expect(contract.functions.redeemExactSetForETH).toBeDefined() + }) +}) + describe('getExchangeIssuanceZeroExContractAddress()', () => { test('return correct address for polygon', async () => { const expectedAddress = ExchangeIssuanceZeroExPolygonAddress diff --git a/src/utils/contracts.ts b/src/utils/contracts.ts index bb35431..604e5d9 100644 --- a/src/utils/contracts.ts +++ b/src/utils/contracts.ts @@ -7,6 +7,7 @@ import EXCHANGE_ISSUANCE_ZERO_EX_ABI from '../constants/abis/ExchangeIssuanceZer import FLASHMINT_HYETH_ABI from '../constants/abis/FlashMintHyEth.json' import FLASHMINT_LEVERAGED_COMPOUND from '../constants/abis/FlashMintLeveragedForCompound.json' import FLASHMINT_LEVERAGED_EXTENDED_ABI from '../constants/abis/FlashMintLeveragedExtended.json' +import FLASHMINT_WRAPPED_ABI from '../constants/abis/FlashMintWrapped.json' import FLASHMINT_ZEROEX_ABI from '../constants/abis/FlashMintZeroEx.json' import { ChainId } from '../constants/chains' @@ -185,6 +186,21 @@ export const getFlashMintLeveragedContractForToken = ( } } +/** + * Returns an instance of a FlasthMintWrapped contract (mainnet only). + * @param signerOrProvider A signer or provider. + * @returns An instance of a FlasthMintWrapped contract. + */ +export const getFlashMintWrappedContract = ( + signerOrProvider: Signer | Provider | undefined +): Contract => { + return new Contract( + Contracts[ChainId.Mainnet].FlashMintWrapped, + FLASHMINT_WRAPPED_ABI, + signerOrProvider + ) +} + export function getExchangeIssuanceZeroExContractAddress( chainId: number = ChainId.Mainnet ): string { From 67938c50abd9756db9a6bf6a28cdd0f1517b26ef Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 4 Sep 2024 14:57:23 +0100 Subject: [PATCH 06/58] fix: switch case --- src/utils/issuanceModules.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/issuanceModules.ts b/src/utils/issuanceModules.ts index 28dbe40..d98a4e1 100644 --- a/src/utils/issuanceModules.ts +++ b/src/utils/issuanceModules.ts @@ -64,8 +64,7 @@ export function getIssuanceModule( isDebtIssuance: true, } case GitcoinStakedETHIndex.symbol: - // TODO: add index token - case 'USDCY': + case 'USDCY': // TODO: add index token case wsETH2.symbol: return { address: IndexDebtIssuanceModuleV2Address, isDebtIssuance: true } case InterestCompoundingETHIndex.symbol: From c488503890a6ddf31167b34755dfd47a7d209d7e Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 5 Sep 2024 10:23:09 +0100 Subject: [PATCH 07/58] feat: add empty component swap data --- src/utils/component-swap-data.test.ts | 174 +++++++++++++++ src/utils/component-swap-data.ts | 301 ++++++++++++++++++++++++++ 2 files changed, 475 insertions(+) create mode 100644 src/utils/component-swap-data.test.ts create mode 100644 src/utils/component-swap-data.ts diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts new file mode 100644 index 0000000..e9221a5 --- /dev/null +++ b/src/utils/component-swap-data.test.ts @@ -0,0 +1,174 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +// import { USDC, USDT, WETH } from 'constants/tokens' +// import { LocalhostProvider, ZeroExApiSwapQuote } from 'tests/utils' +// import { wei } from 'utils/numbers' + +// import { +// getIssuanceComponentSwapData, +// getRedemptionComponentSwapData, +// } from './componentSwapData' +// import { Exchange } from './swapData' + +// const provider = LocalhostProvider +// const zeroExApi = ZeroExApiSwapQuote + +// const dai = DAI.address!.toLowerCase() +// const usdc = USDC.address!.toLowerCase() +// const usdt = USDT.address!.toLowerCase() +// const weth = WETH.address!.toLowerCase() +// const zeroAddress = '0x0000000000000000000000000000000000000000' + +describe.skip('getIssuanceComponentSwapData()', () => { + test('returns correct swap data based on input token (USDC)', async () => { + expect(true).toBe(true) + // const inputToken = usdc + // const componentSwapData = await getIssuanceComponentSwapData( + // MoneyMarketIndexToken.symbol, + // MoneyMarketIndexToken.address!, + // inputToken, + // wei(1), + // provider, + // zeroExApi + // ) + // expect(componentSwapData.length).toBe(6) + // expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) + // expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) + // expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) + // expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) + // expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) + // expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) + // // Should be empty as input token is equal to output token + // expect(componentSwapData[0].dexData.exchange).toEqual(Exchange.None) + // expect(componentSwapData[0].dexData.path).toEqual([ + // zeroAddress, + // zeroAddress, + // ]) + // expect(componentSwapData[1].dexData.path).toEqual([inputToken, dai]) + // expect(componentSwapData[2].dexData.path).toEqual([inputToken, usdt]) + // expect(componentSwapData[3].dexData.path).toEqual([inputToken, usdt]) + // expect(componentSwapData[4].dexData.path).toEqual([inputToken, dai]) + // // Should be empty as input token is equal to output token + // expect(componentSwapData[5].dexData.exchange).toEqual(Exchange.None) + // expect(componentSwapData[5].dexData.path).toEqual([ + // zeroAddress, + // zeroAddress, + // ]) + // componentSwapData.forEach((swapData, index) => { + // expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) + // if (index > 0 && index < 5) { + // expect(swapData.dexData.exchange).toEqual(Exchange.UniV3) + // expect(swapData.dexData.fees.length).toBeGreaterThan(0) + // } + // expect(swapData.dexData.pool).toBe(zeroAddress) + // }) + }) + + // test('returns correct swap data based when input token is WETH', async () => { + // const inputToken = weth + // const componentSwapData = await getIssuanceComponentSwapData( + // MoneyMarketIndexToken.symbol, + // MoneyMarketIndexToken.address!, + // inputToken, + // wei(1), + // provider, + // zeroExApi + // ) + // expect(componentSwapData.length).toBe(6) + // expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) + // expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) + // expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) + // expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) + // expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) + // expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) + // expect(componentSwapData[0].dexData.path).toEqual([weth, usdc]) + // expect(componentSwapData[1].dexData.path).toEqual([weth, dai]) + // expect(componentSwapData[2].dexData.path).toEqual([weth, usdt]) + // expect(componentSwapData[3].dexData.path).toEqual([weth, usdt]) + // expect(componentSwapData[4].dexData.path).toEqual([weth, dai]) + // expect(componentSwapData[5].dexData.path).toEqual([weth, usdc]) + // componentSwapData.forEach((swapData) => { + // expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) + // expect(swapData.dexData.exchange).toBe(Exchange.UniV3) + // // Not great but atm there could be varying pools/fees returned + // expect(swapData.dexData.fees.length).toBeGreaterThan(0) + // expect(swapData.dexData.pool).toBe(zeroAddress) + // }) + // }) +}) + +// describe.skip('getRedemptionComponentSwapData()', () => { +// test('returns correct swap data based on output token (USDC)', async () => { +// const outputToken = usdc +// const componentSwapData = await getRedemptionComponentSwapData( +// MoneyMarketIndexToken.symbol, +// MoneyMarketIndexToken.address!, +// outputToken, +// wei(1), +// provider, +// zeroExApi +// ) +// expect(componentSwapData.length).toBe(6) +// expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) +// expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) +// expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) +// expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) +// expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) +// expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) +// // Should be empty as input token is equal to output token +// expect(componentSwapData[0].dexData.exchange).toEqual(Exchange.None) +// expect(componentSwapData[0].dexData.path).toEqual([ +// zeroAddress, +// zeroAddress, +// ]) +// expect(componentSwapData[1].dexData.path).toEqual([dai, usdc]) +// expect(componentSwapData[2].dexData.path).toEqual([usdt, usdc]) +// expect(componentSwapData[3].dexData.path).toEqual([usdt, usdc]) +// expect(componentSwapData[4].dexData.path).toEqual([dai, usdc]) +// // Should be empty as input token is equal to output token +// expect(componentSwapData[5].dexData.exchange).toEqual(Exchange.None) +// expect(componentSwapData[5].dexData.path).toEqual([ +// zeroAddress, +// zeroAddress, +// ]) +// componentSwapData.forEach((swapData, index) => { +// expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) +// if (index > 0 && index < 5) { +// expect(swapData.dexData.exchange).toEqual(Exchange.UniV3) +// expect(swapData.dexData.fees.length).toBeGreaterThan(0) +// } +// expect(swapData.dexData.pool).toBe(zeroAddress) +// }) +// }) + +// test('returns correct swap data when output token is WETH', async () => { +// const outputToken = weth +// const componentSwapData = await getRedemptionComponentSwapData( +// MoneyMarketIndexToken.symbol, +// MoneyMarketIndexToken.address!, +// outputToken, +// wei(1), +// provider, +// zeroExApi +// ) +// expect(componentSwapData.length).toBe(6) +// expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) +// expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) +// expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) +// expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) +// expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) +// expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) +// expect(componentSwapData[0].dexData.path).toEqual([usdc, weth]) +// expect(componentSwapData[1].dexData.path).toEqual([dai, weth]) +// expect(componentSwapData[2].dexData.path).toEqual([usdt, weth]) +// expect(componentSwapData[3].dexData.path).toEqual([usdt, weth]) +// expect(componentSwapData[4].dexData.path).toEqual([dai, weth]) +// expect(componentSwapData[5].dexData.path).toEqual([usdc, weth]) +// componentSwapData.forEach((swapData) => { +// expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) +// expect(swapData.dexData.exchange).toBe(Exchange.UniV3) +// // Not great but atm there could be varying pools/fees returned +// expect(swapData.dexData.fees.length).toBeGreaterThan(0) +// expect(swapData.dexData.pool).toBe(zeroAddress) +// }) +// }) +// }) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts new file mode 100644 index 0000000..d937036 --- /dev/null +++ b/src/utils/component-swap-data.ts @@ -0,0 +1,301 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { Contract } from '@ethersproject/contracts' +import { JsonRpcProvider } from '@ethersproject/providers' + +import { DAI, USDC, USDT } from 'constants/tokens' +import { Exchange, SwapData } from 'utils/swap-data' + +// import { ZeroExApi } from './0x' + +// FIXME: +export interface ComponentSwapData { + underlyingERC20: string + dexData: SwapData + // ONLY relevant for issue, not used for redeem: + // amount that has to be bought of the unwrapped token version (to cover required wrapped component amounts for issuance) + // this amount has to be computed beforehand through the exchange rate of wrapped Component <> unwrappedComponent + // i.e. getRequiredComponentIssuanceUnits() on the IssuanceModule and then convert units through exchange rate to unwrapped component units + // e.g. 300 cDAI needed for issuance of 1 Set token. exchange rate 1cDAI = 0.05 DAI. -> buyUnderlyingAmount = 0.05 DAI * 300 = 15 DAI + buyUnderlyingAmount: BigNumber +} + +interface WrappedToken { + address: string + decimals: number + underlyingErc20: { + address: string + decimals: number + symbol: string + } +} + +// const IssuanceAbi = [ +// 'function getRequiredComponentIssuanceUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', +// 'function getRequiredComponentRedemptionUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', +// ] + +// const erc4626Abi = [ +// 'constructor(address _morpho, address _morphoToken, address _lens, address _recipient)', +// 'error ZeroAddress()', +// 'event Accrued(address indexed user, uint256 index, uint256 unclaimed)', +// 'event Approval(address indexed owner, address indexed spender, uint256 value)', +// 'event Claimed(address indexed user, uint256 claimed)', +// 'event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares)', +// 'event Initialized(uint8 version)', +// 'event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)', +// 'event RewardsTransferred(address recipient, uint256 amount)', +// 'event Transfer(address indexed from, address indexed to, uint256 value)', +// 'event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)', +// 'function allowance(address owner, address spender) view returns (uint256)', +// 'function approve(address spender, uint256 amount) returns (bool)', +// 'function asset() view returns (address)', +// 'function balanceOf(address account) view returns (uint256)', +// 'function claimRewards(address _user) returns (uint256 rewardsAmount)', +// 'function comp() view returns (address)', +// 'function convertToAssets(uint256 shares) view returns (uint256 assets)', +// 'function convertToShares(uint256 assets) view returns (uint256 shares)', +// 'function decimals() view returns (uint8)', +// 'function decreaseAllowance(address spender, uint256 subtractedValue) returns (bool)', +// 'function deposit(uint256 assets, address receiver) returns (uint256)', +// 'function increaseAllowance(address spender, uint256 addedValue) returns (bool)', +// 'function initialize(address _poolToken, string _name, string _symbol, uint256 _initialDeposit)', +// 'function lens() view returns (address)', +// 'function maxDeposit(address) view returns (uint256)', +// 'function maxMint(address) view returns (uint256)', +// 'function maxRedeem(address owner) view returns (uint256)', +// 'function maxWithdraw(address owner) view returns (uint256)', +// 'function mint(uint256 shares, address receiver) returns (uint256)', +// 'function morpho() view returns (address)', +// 'function morphoToken() view returns (address)', +// 'function name() view returns (string)', +// 'function owner() view returns (address)', +// 'function poolToken() view returns (address)', +// 'function previewDeposit(uint256 assets) view returns (uint256)', +// 'function previewMint(uint256 shares) view returns (uint256)', +// 'function previewRedeem(uint256 shares) view returns (uint256)', +// 'function previewWithdraw(uint256 assets) view returns (uint256)', +// 'function recipient() view returns (address)', +// 'function redeem(uint256 shares, address receiver, address owner) returns (uint256)', +// 'function renounceOwnership()', +// 'function rewardsIndex() view returns (uint256)', +// 'function symbol() view returns (string)', +// 'function totalAssets() view returns (uint256)', +// 'function totalSupply() view returns (uint256)', +// 'function transfer(address to, uint256 amount) returns (bool)', +// 'function transferFrom(address from, address to, uint256 amount) returns (bool)', +// 'function transferOwnership(address newOwner)', +// 'function transferRewards()', +// 'function userRewards(address) view returns (uint128 index, uint128 unclaimed)', +// 'function wEth() view returns (address)', +// 'function withdraw(uint256 assets, address receiver, address owner) returns (uint256)', +// ] + +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +const dai = DAI.address! +const usdc = USDC.address! +const usdt = USDT.address! +/* eslint-enable @typescript-eslint/no-non-null-assertion */ +// const DEFAULT_SLIPPAGE = 0.0015 + +const emptySwapData: SwapData = { + exchange: Exchange.None, + path: [ + '0x0000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000', + ], + fees: [], + pool: '0x0000000000000000000000000000000000000000', +} + +// const isFCASH = (address: string) => +// [ +// '0x278039398A5eb29b6c2FB43789a38A84C6085266', +// '0xe09B1968851478f20a43959d8a212051367dF01A', +// ].includes(address) + +// const getAmountOfAssetToObtainShares = async () => +// component: string, +// shares: BigNumber, +// provider: JsonRpcProvider +// slippage = DEFAULT_SLIPPAGE // 1 = 1% +// { +// const componentContract = new Contract(component, erc4626Abi, provider) +// // Convert slippage to a BigNumber, do rounding to avoid weird JS precision loss +// const defaultSlippageBN = BigNumber.from(Math.round(slippage * 10000)) +// // if FCASH, increase slippage x3 +// const slippageBigNumber = isFCASH(component) +// ? defaultSlippageBN.mul(3) +// : defaultSlippageBN + +// // Calculate the multiplier (1 + slippage) +// const multiplier = BigNumber.from(10000).add(slippageBigNumber) + +// const buyUnderlyingAmount: BigNumber = +// await componentContract.convertToAssets(shares) +// return buyUnderlyingAmount.mul(multiplier).div(10000) +// } + +export async function getIssuanceComponentSwapData(): Promise< + ComponentSwapData[] +> { + // const issuanceModule = getIssuanceModule(indexTokenSymbol) + // const issuance = new Contract(issuanceModule.address, IssuanceAbi, provider) + // const [issuanceComponents, issuanceUnits] = + // await issuance.getRequiredComponentIssuanceUnits( + // indexToken, + // indexTokenAmount + // ) + // const underlyingERC20sPromises: Promise[] = + // issuanceComponents.map((component: string) => + // getUnderlyingErc20(component, provider) + // ) + // const buyAmountsPromises = issuanceComponents.map( + // (component: string, index: number) => + // getAmountOfAssetToObtainShares(component, issuanceUnits[index], provider) + // ) + // const buyAmounts = await Promise.all(buyAmountsPromises) + // const wrappedTokens = await Promise.all(underlyingERC20sPromises) + // const swaps: Promise<{ swapData: SwapData } | null>[] = + // issuanceComponents.map((_: string, index: number) => { + // const wrappedToken = wrappedTokens[index] + // const underlyingERC20 = wrappedToken.underlyingErc20 + // const buyUnderlyingAmount = buyAmounts[index] + // const mintParams = { + // buyToken: underlyingERC20.address, + // buyAmount: buyUnderlyingAmount, + // sellToken: inputToken, + // includedSources: 'Uniswap_V3', + // } + // return getSwapData(mintParams, 0.1, 1, zeroExApi) + // }) + // const swapDataResults = await Promise.all(swaps) + // const swapData = issuanceComponents.map((_: string, index: number) => { + // const wrappedToken = wrappedTokens[index] + // const underlyingERC20 = wrappedToken.underlyingErc20 + // const buyUnderlyingAmount = buyAmounts[index] + // const dexData = swapDataResults[index]?.swapData ?? emptySwapData + // return { + // underlyingERC20: underlyingERC20.address, + // buyUnderlyingAmount, + // dexData, + // } + // }) + // return swapData + + const swapData: ComponentSwapData = { + underlyingERC20: usdc, + buyUnderlyingAmount: BigNumber.from(0), + dexData: emptySwapData, + } + return [swapData] +} + +export async function getRedemptionComponentSwapData(): Promise< + // indexTokenSymbol: string, + // indexToken: string, + // outputToken: string, + // indexTokenAmount: BigNumber, + // provider: JsonRpcProvider + // zeroExApi: ZeroExApi + ComponentSwapData[] +> { + // const issuanceModule = getIssuanceModule(indexTokenSymbol) + // const issuance = new Contract(issuanceModule.address, IssuanceAbi, provider) + // const [issuanceComponents, issuanceUnits] = + // await issuance.getRequiredComponentRedemptionUnits( + // indexToken, + // indexTokenAmount + // ) + // const underlyingERC20sPromises: Promise[] = + // issuanceComponents.map((component: string) => + // getUnderlyingErc20(component, provider) + // ) + // const wrappedTokens = await Promise.all(underlyingERC20sPromises) + // const buyAmountsPromises = issuanceComponents.map( + // (component: string, index: number) => + // getAmountOfAssetToObtainShares( + // component, + // issuanceUnits[index], + // provider, + // -DEFAULT_SLIPPAGE + // ) + // ) + // const buyAmounts = await Promise.all(buyAmountsPromises) + // const swaps = issuanceComponents.map((_: string, index: number) => { + // const wrappedToken = wrappedTokens[index] + // const underlyingERC20 = wrappedToken.underlyingErc20 + // const buyUnderlyingAmount = buyAmounts[index] + // const redeemParams = { + // buyToken: outputToken, + // sellAmount: buyUnderlyingAmount, + // sellToken: underlyingERC20.address, + // includedSources: 'Uniswap_V3', + // } + // return getSwapData(redeemParams, 0.1, 1, zeroExApi) + // }) + // const swapDataResults = await Promise.all(swaps) + // const swapData = issuanceComponents.map((_: string, index: number) => { + // const wrappedToken = wrappedTokens[index] + // const underlyingERC20 = wrappedToken.underlyingErc20 + // const buyUnderlyingAmount = buyAmounts[index] + // const dexData = swapDataResults[index]?.swapData ?? emptySwapData + // return { + // underlyingERC20: underlyingERC20.address, + // buyUnderlyingAmount, + // dexData, + // } + // }) + const swapData: ComponentSwapData = { + underlyingERC20: usdc, + buyUnderlyingAmount: BigNumber.from(0), + dexData: emptySwapData, + } + return [swapData] +} + +async function getUnderlyingErc20( + token: string, + provider: JsonRpcProvider +): Promise { + const IERC4262_ABI = [ + 'function asset() public view returns (address)', + 'function decimals() public view returns (uint256)', + ] + const contract = new Contract(token, IERC4262_ABI, provider) + const underlyingERC20: string = await contract.asset() + const decimals: number = await contract.decimals() + switch (underlyingERC20.toLowerCase()) { + case dai.toLowerCase(): + return { + address: token, + decimals, + underlyingErc20: { + address: dai, + decimals: 18, + symbol: DAI.symbol, + }, + } + case usdc.toLowerCase(): + return { + address: token, + decimals, + underlyingErc20: { + address: usdc, + decimals: 6, + symbol: USDC.symbol, + }, + } + case usdt.toLowerCase(): + return { + address: token, + decimals, + underlyingErc20: { + address: usdt, + decimals: 6, + symbol: USDT.symbol, + }, + } + default: + return null + } +} From 9f71b441db1fb2769fbbda20f1b804338ba402e6 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 5 Sep 2024 10:36:46 +0100 Subject: [PATCH 08/58] feat: add get underlying erc20 --- src/utils/component-swap-data.ts | 122 ++++++++++++------------------- 1 file changed, 48 insertions(+), 74 deletions(-) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index d937036..d171d5d 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -1,12 +1,27 @@ import { BigNumber } from '@ethersproject/bignumber' -import { Contract } from '@ethersproject/contracts' -import { JsonRpcProvider } from '@ethersproject/providers' import { DAI, USDC, USDT } from 'constants/tokens' import { Exchange, SwapData } from 'utils/swap-data' +import { Address, createPublicClient, http, parseAbi } from 'viem' +import { mainnet } from 'viem/chains' // import { ZeroExApi } from './0x' +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +const usdc = USDC.address! +/* eslint-enable @typescript-eslint/no-non-null-assertion */ +// const DEFAULT_SLIPPAGE = 0.0015 + +const emptySwapData: SwapData = { + exchange: Exchange.None, + path: [ + '0x0000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000', + ], + fees: [], + pool: '0x0000000000000000000000000000000000000000', +} + // FIXME: export interface ComponentSwapData { underlyingERC20: string @@ -90,23 +105,6 @@ interface WrappedToken { // 'function withdraw(uint256 assets, address receiver, address owner) returns (uint256)', // ] -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -const dai = DAI.address! -const usdc = USDC.address! -const usdt = USDT.address! -/* eslint-enable @typescript-eslint/no-non-null-assertion */ -// const DEFAULT_SLIPPAGE = 0.0015 - -const emptySwapData: SwapData = { - exchange: Exchange.None, - path: [ - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], - fees: [], - pool: '0x0000000000000000000000000000000000000000', -} - // const isFCASH = (address: string) => // [ // '0x278039398A5eb29b6c2FB43789a38A84C6085266', @@ -145,10 +143,9 @@ export async function getIssuanceComponentSwapData(): Promise< // indexToken, // indexTokenAmount // ) - // const underlyingERC20sPromises: Promise[] = - // issuanceComponents.map((component: string) => - // getUnderlyingErc20(component, provider) - // ) + const issuanceComponents = ['0x', '0x', '0x'] + const underlyingERC20sPromises: Promise[] = + issuanceComponents.map((component: string) => getUnderlyingErc20(component)) // const buyAmountsPromises = issuanceComponents.map( // (component: string, index: number) => // getAmountOfAssetToObtainShares(component, issuanceUnits[index], provider) @@ -181,13 +178,15 @@ export async function getIssuanceComponentSwapData(): Promise< // } // }) // return swapData - - const swapData: ComponentSwapData = { - underlyingERC20: usdc, - buyUnderlyingAmount: BigNumber.from(0), - dexData: emptySwapData, - } - return [swapData] + const wrappedTokens = underlyingERC20sPromises.map(() => { + const swapData: ComponentSwapData = { + underlyingERC20: usdc, + buyUnderlyingAmount: BigNumber.from(0), + dexData: emptySwapData, + } + return swapData + }) + return wrappedTokens } export async function getRedemptionComponentSwapData(): Promise< @@ -253,49 +252,24 @@ export async function getRedemptionComponentSwapData(): Promise< return [swapData] } -async function getUnderlyingErc20( - token: string, - provider: JsonRpcProvider -): Promise { - const IERC4262_ABI = [ - 'function asset() public view returns (address)', - 'function decimals() public view returns (uint256)', - ] - const contract = new Contract(token, IERC4262_ABI, provider) - const underlyingERC20: string = await contract.asset() - const decimals: number = await contract.decimals() - switch (underlyingERC20.toLowerCase()) { - case dai.toLowerCase(): - return { - address: token, - decimals, - underlyingErc20: { - address: dai, - decimals: 18, - symbol: DAI.symbol, - }, - } - case usdc.toLowerCase(): - return { - address: token, - decimals, - underlyingErc20: { - address: usdc, - decimals: 6, - symbol: USDC.symbol, - }, - } - case usdt.toLowerCase(): - return { - address: token, - decimals, - underlyingErc20: { - address: usdt, - decimals: 6, - symbol: USDT.symbol, - }, - } - default: - return null +async function getUnderlyingErc20(token: string): Promise { + // FIXME: pass in? or config externally? + const publicClient = createPublicClient({ + chain: mainnet, + transport: http(process.env.MAINNET_ALCHEMY_API!), + }) + const decimals: number = await publicClient.readContract({ + address: token as Address, + abi: parseAbi(['function decimals() view returns (uint8)']), + functionName: 'decimals', + }) + return { + address: token, + decimals, + underlyingErc20: { + address: usdc, + decimals: 6, + symbol: USDC.symbol, + }, } } From 8d778cb767d4b8abdecb59bbef6dc9de88fc4a83 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 5 Sep 2024 10:43:27 +0100 Subject: [PATCH 09/58] lint --- src/utils/component-swap-data.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index d171d5d..2a126e1 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@ethersproject/bignumber' -import { DAI, USDC, USDT } from 'constants/tokens' +import { USDC } from 'constants/tokens' import { Exchange, SwapData } from 'utils/swap-data' import { Address, createPublicClient, http, parseAbi } from 'viem' import { mainnet } from 'viem/chains' @@ -256,7 +256,7 @@ async function getUnderlyingErc20(token: string): Promise { // FIXME: pass in? or config externally? const publicClient = createPublicClient({ chain: mainnet, - transport: http(process.env.MAINNET_ALCHEMY_API!), + transport: http(process.env.MAINNET_ALCHEMY_API), }) const decimals: number = await publicClient.readContract({ address: token as Address, From 8de8c108d48f6387c601b580f6c670c1e2a7d670 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 5 Sep 2024 10:47:26 +0100 Subject: [PATCH 10/58] feat: add empty wrapped builder --- src/flashmint/builders/index.ts | 1 + src/flashmint/builders/wrapped.test.ts | 270 +++++++++++++++++++++++++ src/flashmint/builders/wrapped.ts | 108 ++++++++++ 3 files changed, 379 insertions(+) create mode 100644 src/flashmint/builders/wrapped.test.ts create mode 100644 src/flashmint/builders/wrapped.ts diff --git a/src/flashmint/builders/index.ts b/src/flashmint/builders/index.ts index d5b66f0..03c3132 100644 --- a/src/flashmint/builders/index.ts +++ b/src/flashmint/builders/index.ts @@ -2,4 +2,5 @@ export * from './hyeth' export * from './interface' export * from './leveraged' export * from './leveraged-extended' +export * from './wrapped' export * from './zeroex' diff --git a/src/flashmint/builders/wrapped.test.ts b/src/flashmint/builders/wrapped.test.ts new file mode 100644 index 0000000..8f9d118 --- /dev/null +++ b/src/flashmint/builders/wrapped.test.ts @@ -0,0 +1,270 @@ +import { BigNumber } from '@ethersproject/bignumber' + +import { ChainId } from 'constants/chains' +import { Contracts } from 'constants/contracts' +import { LocalhostProvider, QuoteTokens } from 'tests/utils' +import { getFlashMintWrappedContract } from 'utils/contracts' +import { wei } from 'utils/numbers' +import { ComponentWrapData } from 'utils/wrap-data' + +import { + FlashMintWrappedBuildRequest, + WrappedTransactionBuilder, +} from './wrapped' + +const provider = LocalhostProvider +const ZERO_BYTES = '0x0000000000000000000000000000000000000000' + +const { usdc } = QuoteTokens + +const FlashMintWrappedAddress = Contracts[ChainId.Mainnet].FlashMintWrapped +const eth = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' +// FIXME: add address once token is available +const indexToken = usdc.address +const usdcAddress = usdc.address + +describe('WrappedTransactionBuilder()', () => { + beforeEach((): void => { + jest.setTimeout(10000000) + }) + + test('returns null for invalid request (no index token)', async () => { + const buildRequest = getDefaultBuildRequest() + buildRequest.indexToken = '' + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + expect(tx).toBeNull() + }) + + test('returns null for invalid request (no input/output token)', async () => { + const buildRequest = getDefaultBuildRequest() + buildRequest.inputOutputToken = '' + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + expect(tx).toBeNull() + }) + + test('returns null for invalid request (indexTokenAmount = 0)', async () => { + const buildRequest = getDefaultBuildRequest() + buildRequest.indexTokenAmount = BigNumber.from(0) + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + expect(tx).toBeNull() + }) + + test('returns null for invalid request (inputOutputTokenAmount = 0)', async () => { + const buildRequest = getDefaultBuildRequest() + buildRequest.inputOutputTokenAmount = BigNumber.from(0) + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + expect(tx).toBeNull() + }) + + test('returns null for invalid request (no component swap data)', async () => { + const buildRequest = getDefaultBuildRequest() + buildRequest.componentSwapData = [] + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + expect(tx).toBeNull() + }) + + test('returns null for invalid request (no wrap data)', async () => { + const buildRequest = getDefaultBuildRequest() + buildRequest.componentWrapData = [] + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + expect(tx).toBeNull() + }) + + test('returns null for invalid request (wrap data and swap data length mismatch)', async () => { + const buildRequest = getDefaultBuildRequest() + buildRequest.componentWrapData = buildRequest.componentWrapData.slice(0, -1) + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + expect(tx).toBeNull() + }) + + test('returns a tx for minting MMI (ERC20)', async () => { + const buildRequest = getDefaultBuildRequest() + const contract = getFlashMintWrappedContract(provider) + const refTx = await contract.populateTransaction.issueExactSetFromERC20( + buildRequest.indexToken, + buildRequest.inputOutputToken, + buildRequest.indexTokenAmount, + buildRequest.inputOutputTokenAmount, + buildRequest.componentSwapData, + buildRequest.componentWrapData + ) + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + if (!tx) fail() + expect(tx.to).toBe(FlashMintWrappedAddress) + expect(tx.data).toEqual(refTx.data) + }) + + test('returns a tx for minting MMI (ETH)', async () => { + const buildRequest = getDefaultBuildRequest(true, eth, 'ETH') + const contract = getFlashMintWrappedContract(provider) + const refTx = await contract.populateTransaction.issueExactSetFromETH( + buildRequest.indexToken, + buildRequest.indexTokenAmount, + buildRequest.componentSwapData, + buildRequest.componentWrapData, + { value: buildRequest.inputOutputTokenAmount } + ) + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + if (!tx) fail() + expect(tx.to).toBe(FlashMintWrappedAddress) + expect(tx.data).toEqual(refTx.data) + expect(tx.value).toEqual(buildRequest.inputOutputTokenAmount) + }) + + test('returns a tx for redeeming MMI (ERC20)', async () => { + const buildRequest = getDefaultBuildRequest(false) + const contract = getFlashMintWrappedContract(provider) + const refTx = await contract.populateTransaction.redeemExactSetForERC20( + buildRequest.indexToken, + buildRequest.inputOutputToken, + buildRequest.indexTokenAmount, + buildRequest.inputOutputTokenAmount, + buildRequest.componentSwapData, + buildRequest.componentWrapData + ) + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + if (!tx) fail() + expect(tx.to).toBe(FlashMintWrappedAddress) + expect(tx.data).toEqual(refTx.data) + }) + + test('returns a tx for redeeming MMI (ETH)', async () => { + const buildRequest = getDefaultBuildRequest(false, eth, 'ETH') + const contract = getFlashMintWrappedContract(provider) + const refTx = await contract.populateTransaction.redeemExactSetForETH( + buildRequest.indexToken, + buildRequest.indexTokenAmount, + buildRequest.inputOutputTokenAmount, + buildRequest.componentSwapData, + buildRequest.componentWrapData + ) + const builder = new WrappedTransactionBuilder(provider) + const tx = await builder.build(buildRequest) + if (!tx) fail() + expect(tx.to).toBe(FlashMintWrappedAddress) + expect(tx.data).toEqual(refTx.data) + }) +}) + +function getDefaultBuildRequest( + isMinting = true, + inputOutputToken: string = usdcAddress, + inputOutputTokenSymbol = 'USDC' +): FlashMintWrappedBuildRequest { + const wrapData: ComponentWrapData = { + integrationName: '', + wrapData: ZERO_BYTES, + } + return { + isMinting, + indexToken: indexToken, + inputOutputToken, + indexTokenAmount: wei(1), + inputOutputTokenAmount: BigNumber.from('16583822409709138541'), + inputOutputTokenSymbol, + componentSwapData: [ + { + underlyingERC20: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + buyUnderlyingAmount: BigNumber.from('16666666666666666666'), + dexData: { + exchange: 3, + path: [ + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + ], + fees: [3000, 3000], + pool: '0x0000000000000000000000000000000000000000', + }, + }, + { + underlyingERC20: '0xdac17f958d2ee523a2206206994597c13d831ec7', + buyUnderlyingAmount: BigNumber.from('16666666666666666666'), + dexData: { + exchange: 3, + path: [ + '0xdac17f958d2ee523a2206206994597c13d831ec7', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + ], + fees: [3000, 3000], + pool: '0x0000000000000000000000000000000000000000', + }, + }, + { + underlyingERC20: '0xdac17f958d2ee523a2206206994597c13d831ec7', + buyUnderlyingAmount: BigNumber.from('16666666666666666666'), + dexData: { + exchange: 3, + path: [ + '0xdac17f958d2ee523a2206206994597c13d831ec7', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + ], + fees: [3000, 3000], + pool: '0x0000000000000000000000000000000000000000', + }, + }, + { + underlyingERC20: '0x6b175474e89094c44da98b954eedeac495271d0f', + buyUnderlyingAmount: BigNumber.from('1666666666'), + dexData: { + exchange: 3, + path: [ + '0x6b175474e89094c44da98b954eedeac495271d0f', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + ], + fees: [3000, 3000], + pool: '0x0000000000000000000000000000000000000000', + }, + }, + { + underlyingERC20: '0x6b175474e89094c44da98b954eedeac495271d0f', + buyUnderlyingAmount: BigNumber.from('1666666666'), + dexData: { + exchange: 3, + path: [ + '0x6b175474e89094c44da98b954eedeac495271d0f', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + ], + fees: [3000, 3000], + pool: '0x0000000000000000000000000000000000000000', + }, + }, + { + underlyingERC20: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + buyUnderlyingAmount: BigNumber.from('1666666666'), + dexData: { + exchange: 3, + path: [ + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + ], + fees: [3000, 3000], + pool: '0x0000000000000000000000000000000000000000', + }, + }, + ], + componentWrapData: [ + wrapData, + wrapData, + wrapData, + wrapData, + wrapData, + wrapData, + ], + } +} diff --git a/src/flashmint/builders/wrapped.ts b/src/flashmint/builders/wrapped.ts new file mode 100644 index 0000000..2557873 --- /dev/null +++ b/src/flashmint/builders/wrapped.ts @@ -0,0 +1,108 @@ +import { TransactionRequest } from '@ethersproject/abstract-provider' +import { BigNumber } from '@ethersproject/bignumber' +import { PopulatedTransaction } from '@ethersproject/contracts' +import { JsonRpcProvider } from '@ethersproject/providers' + +import { ComponentSwapData } from 'utils/component-swap-data' +import { getFlashMintWrappedContract } from 'utils/contracts' +import { ComponentWrapData } from 'utils/wrap-data' + +import { TransactionBuilder } from './interface' +import { isEmptyString, isInvalidAmount } from './utils' + +export interface FlashMintWrappedBuildRequest { + isMinting: boolean + indexToken: string + inputOutputToken: string + inputOutputTokenSymbol: string + indexTokenAmount: BigNumber + inputOutputTokenAmount: BigNumber + componentSwapData: ComponentSwapData[] + componentWrapData: ComponentWrapData[] +} + +export class WrappedTransactionBuilder + implements + TransactionBuilder +{ + constructor(private readonly provider: JsonRpcProvider) {} + + async build( + request: FlashMintWrappedBuildRequest + ): Promise { + const isValidRequest = this.isValidRequest(request) + if (!isValidRequest) return null + const { + componentSwapData, + componentWrapData, + indexToken, + indexTokenAmount, + inputOutputToken, + inputOutputTokenSymbol, + inputOutputTokenAmount, + isMinting, + } = request + const inputOutputTokenIsEth = inputOutputTokenSymbol === 'ETH' + const contract = getFlashMintWrappedContract(this.provider) + let tx: PopulatedTransaction | null = null + if (isMinting) { + if (inputOutputTokenIsEth) { + tx = await contract.populateTransaction.issueExactSetFromETH( + indexToken, + indexTokenAmount, + componentSwapData, + componentWrapData, + { value: inputOutputTokenAmount } + ) + } else { + tx = await contract.populateTransaction.issueExactSetFromERC20( + indexToken, + inputOutputToken, + indexTokenAmount, + inputOutputTokenAmount, // _maxAmountInputToken + componentSwapData, + componentWrapData + ) + } + } else { + if (inputOutputTokenIsEth) { + tx = await contract.populateTransaction.redeemExactSetForETH( + indexToken, + indexTokenAmount, + inputOutputTokenAmount, // _minOutputReceive + componentSwapData, + componentWrapData + ) + } else { + tx = await contract.populateTransaction.redeemExactSetForERC20( + indexToken, + inputOutputToken, + indexTokenAmount, + inputOutputTokenAmount, // _minOutputReceive + componentSwapData, + componentWrapData + ) + } + } + return tx + } + + private isValidRequest(request: FlashMintWrappedBuildRequest): boolean { + const { + componentSwapData, + componentWrapData, + indexToken, + indexTokenAmount, + inputOutputToken, + inputOutputTokenAmount, + } = request + if (isEmptyString(indexToken)) return false + if (isEmptyString(inputOutputToken)) return false + if (isInvalidAmount(indexTokenAmount)) return false + if (isInvalidAmount(inputOutputTokenAmount)) return false + if (componentSwapData.length === 0) return false + if (componentWrapData.length === 0) return false + if (componentSwapData.length !== componentWrapData.length) return false + return true + } +} From 8b0ae1857c13f5356ba2fdc13e6c9ef435c8106c Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 5 Sep 2024 10:51:30 +0100 Subject: [PATCH 11/58] refactor: use rpc url in constructor --- src/flashmint/builders/wrapped.test.ts | 31 +++++++++++++++----------- src/flashmint/builders/wrapped.ts | 7 +++--- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/flashmint/builders/wrapped.test.ts b/src/flashmint/builders/wrapped.test.ts index 8f9d118..690bf69 100644 --- a/src/flashmint/builders/wrapped.test.ts +++ b/src/flashmint/builders/wrapped.test.ts @@ -2,9 +2,10 @@ import { BigNumber } from '@ethersproject/bignumber' import { ChainId } from 'constants/chains' import { Contracts } from 'constants/contracts' -import { LocalhostProvider, QuoteTokens } from 'tests/utils' +import { LocalhostProviderUrl, QuoteTokens } from 'tests/utils' import { getFlashMintWrappedContract } from 'utils/contracts' import { wei } from 'utils/numbers' +import { getRpcProvider } from 'utils/rpc-provider' import { ComponentWrapData } from 'utils/wrap-data' import { @@ -12,7 +13,7 @@ import { WrappedTransactionBuilder, } from './wrapped' -const provider = LocalhostProvider +const rpcUrl = LocalhostProviderUrl const ZERO_BYTES = '0x0000000000000000000000000000000000000000' const { usdc } = QuoteTokens @@ -31,7 +32,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (no index token)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.indexToken = '' - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) expect(tx).toBeNull() }) @@ -39,7 +40,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (no input/output token)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.inputOutputToken = '' - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) expect(tx).toBeNull() }) @@ -47,7 +48,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (indexTokenAmount = 0)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.indexTokenAmount = BigNumber.from(0) - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) expect(tx).toBeNull() }) @@ -55,7 +56,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (inputOutputTokenAmount = 0)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.inputOutputTokenAmount = BigNumber.from(0) - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) expect(tx).toBeNull() }) @@ -63,7 +64,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (no component swap data)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.componentSwapData = [] - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) expect(tx).toBeNull() }) @@ -71,7 +72,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (no wrap data)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.componentWrapData = [] - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) expect(tx).toBeNull() }) @@ -79,13 +80,14 @@ describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (wrap data and swap data length mismatch)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.componentWrapData = buildRequest.componentWrapData.slice(0, -1) - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) expect(tx).toBeNull() }) test('returns a tx for minting MMI (ERC20)', async () => { const buildRequest = getDefaultBuildRequest() + const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) const refTx = await contract.populateTransaction.issueExactSetFromERC20( buildRequest.indexToken, @@ -95,7 +97,7 @@ describe('WrappedTransactionBuilder()', () => { buildRequest.componentSwapData, buildRequest.componentWrapData ) - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) if (!tx) fail() expect(tx.to).toBe(FlashMintWrappedAddress) @@ -104,6 +106,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns a tx for minting MMI (ETH)', async () => { const buildRequest = getDefaultBuildRequest(true, eth, 'ETH') + const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) const refTx = await contract.populateTransaction.issueExactSetFromETH( buildRequest.indexToken, @@ -112,7 +115,7 @@ describe('WrappedTransactionBuilder()', () => { buildRequest.componentWrapData, { value: buildRequest.inputOutputTokenAmount } ) - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) if (!tx) fail() expect(tx.to).toBe(FlashMintWrappedAddress) @@ -122,6 +125,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns a tx for redeeming MMI (ERC20)', async () => { const buildRequest = getDefaultBuildRequest(false) + const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) const refTx = await contract.populateTransaction.redeemExactSetForERC20( buildRequest.indexToken, @@ -131,7 +135,7 @@ describe('WrappedTransactionBuilder()', () => { buildRequest.componentSwapData, buildRequest.componentWrapData ) - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) if (!tx) fail() expect(tx.to).toBe(FlashMintWrappedAddress) @@ -140,6 +144,7 @@ describe('WrappedTransactionBuilder()', () => { test('returns a tx for redeeming MMI (ETH)', async () => { const buildRequest = getDefaultBuildRequest(false, eth, 'ETH') + const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) const refTx = await contract.populateTransaction.redeemExactSetForETH( buildRequest.indexToken, @@ -148,7 +153,7 @@ describe('WrappedTransactionBuilder()', () => { buildRequest.componentSwapData, buildRequest.componentWrapData ) - const builder = new WrappedTransactionBuilder(provider) + const builder = new WrappedTransactionBuilder(rpcUrl) const tx = await builder.build(buildRequest) if (!tx) fail() expect(tx.to).toBe(FlashMintWrappedAddress) diff --git a/src/flashmint/builders/wrapped.ts b/src/flashmint/builders/wrapped.ts index 2557873..1196303 100644 --- a/src/flashmint/builders/wrapped.ts +++ b/src/flashmint/builders/wrapped.ts @@ -1,10 +1,10 @@ import { TransactionRequest } from '@ethersproject/abstract-provider' import { BigNumber } from '@ethersproject/bignumber' import { PopulatedTransaction } from '@ethersproject/contracts' -import { JsonRpcProvider } from '@ethersproject/providers' import { ComponentSwapData } from 'utils/component-swap-data' import { getFlashMintWrappedContract } from 'utils/contracts' +import { getRpcProvider } from 'utils/rpc-provider' import { ComponentWrapData } from 'utils/wrap-data' import { TransactionBuilder } from './interface' @@ -25,13 +25,14 @@ export class WrappedTransactionBuilder implements TransactionBuilder { - constructor(private readonly provider: JsonRpcProvider) {} + constructor(private readonly rpcUrl: string) {} async build( request: FlashMintWrappedBuildRequest ): Promise { const isValidRequest = this.isValidRequest(request) if (!isValidRequest) return null + const provider = getRpcProvider(this.rpcUrl) const { componentSwapData, componentWrapData, @@ -43,7 +44,7 @@ export class WrappedTransactionBuilder isMinting, } = request const inputOutputTokenIsEth = inputOutputTokenSymbol === 'ETH' - const contract = getFlashMintWrappedContract(this.provider) + const contract = getFlashMintWrappedContract(provider) let tx: PopulatedTransaction | null = null if (isMinting) { if (inputOutputTokenIsEth) { From 3f813a5402a575b3d30fe318beeeb3437aabe78a Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 6 Sep 2024 15:32:54 +0100 Subject: [PATCH 12/58] feat: add empty wrapped provider --- src/quote/flashmint/wrapped/index.ts | 1 + src/quote/flashmint/wrapped/provider.test.ts | 115 +++++++++++++++++++ src/quote/flashmint/wrapped/provider.ts | 100 ++++++++++++++++ src/utils/index.ts | 1 + 4 files changed, 217 insertions(+) create mode 100644 src/quote/flashmint/wrapped/index.ts create mode 100644 src/quote/flashmint/wrapped/provider.test.ts create mode 100644 src/quote/flashmint/wrapped/provider.ts diff --git a/src/quote/flashmint/wrapped/index.ts b/src/quote/flashmint/wrapped/index.ts new file mode 100644 index 0000000..6f29423 --- /dev/null +++ b/src/quote/flashmint/wrapped/index.ts @@ -0,0 +1 @@ +export * from './provider' diff --git a/src/quote/flashmint/wrapped/provider.test.ts b/src/quote/flashmint/wrapped/provider.test.ts new file mode 100644 index 0000000..806cdd9 --- /dev/null +++ b/src/quote/flashmint/wrapped/provider.test.ts @@ -0,0 +1,115 @@ +import { LocalhostProviderUrl, QuoteTokens } from 'tests/utils' +import { wei } from 'utils/numbers' +import { FlashMintWrappedQuoteRequest, WrappedQuoteProvider } from '.' + +const { dai, usdc, weth } = QuoteTokens +const indexToken = usdc +const provider = LocalhostProviderUrl + +describe.skip('WrappedQuoteProvider()', () => { + beforeEach((): void => { + jest.setTimeout(100000000) + }) + + test('returns a quote for minting MMI w/ DAI', async () => { + const inputToken = dai + const request: FlashMintWrappedQuoteRequest = { + isMinting: true, + inputToken, + outputToken: indexToken, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new WrappedQuoteProvider(provider) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + expect(quote.componentSwapData.length).toEqual(6) + }) + + test('returns a quote for minting MMI w/ USDC', async () => { + const inputToken = usdc + const request: FlashMintWrappedQuoteRequest = { + isMinting: true, + inputToken, + outputToken: indexToken, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new WrappedQuoteProvider(provider) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + expect(quote.componentSwapData.length).toEqual(6) + }) + + test('returns a quote for minting MMI w/ WETH', async () => { + const inputToken = weth + const request: FlashMintWrappedQuoteRequest = { + isMinting: true, + inputToken, + outputToken: indexToken, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new WrappedQuoteProvider(provider) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + expect(quote.componentSwapData.length).toEqual(6) + }) + + test('returns a quote for redeeming MMI for DAI', async () => { + const outputToken = dai + const request: FlashMintWrappedQuoteRequest = { + isMinting: false, + inputToken: indexToken, + outputToken, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new WrappedQuoteProvider(provider) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + expect(quote.componentSwapData.length).toEqual(6) + }) + + test('returns a quote for redeeming MMI for USDC', async () => { + const outputToken = usdc + const request: FlashMintWrappedQuoteRequest = { + isMinting: false, + inputToken: indexToken, + outputToken, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new WrappedQuoteProvider(provider) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + expect(quote.componentSwapData.length).toEqual(6) + }) + + test('returns a quote for redeeming MMI for WETH', async () => { + const outputToken = weth + const request: FlashMintWrappedQuoteRequest = { + isMinting: false, + inputToken: indexToken, + outputToken, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new WrappedQuoteProvider(provider) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + expect(quote.componentSwapData.length).toEqual(6) + }) +}) diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts new file mode 100644 index 0000000..1641583 --- /dev/null +++ b/src/quote/flashmint/wrapped/provider.ts @@ -0,0 +1,100 @@ +import { BigNumber } from '@ethersproject/bignumber' + +import { ComponentSwapData, ComponentWrapData } from 'utils' + +import { QuoteProvider, QuoteToken } from '../../interfaces' +import { getRpcProvider } from 'utils/rpc-provider' + +export interface FlashMintWrappedQuoteRequest { + isMinting: boolean + inputToken: QuoteToken + outputToken: QuoteToken + indexTokenAmount: BigNumber + slippage: number +} + +export interface FlashMintWrappedQuote { + componentSwapData: ComponentSwapData[] + componentWrapData: ComponentWrapData[] + indexTokenAmount: BigNumber + inputOutputTokenAmount: BigNumber +} + +export class WrappedQuoteProvider + implements QuoteProvider +{ + constructor(private readonly rpcUrl: string) {} + + async getQuote( + request: FlashMintWrappedQuoteRequest + ): Promise { + const provider = getRpcProvider(this.rpcUrl) + console.log(provider.connection.url) + const { inputToken, indexTokenAmount, isMinting, outputToken, slippage } = + request + console.log( + inputToken.symbol, + outputToken.symbol, + indexTokenAmount.toString(), + isMinting, + slippage + ) + // const indexToken = isMinting ? outputToken : inputToken + // const indexTokenSymbol = indexToken.symbol + // const componentSwapData = isMinting + // ? await getIssuanceComponentSwapData( + // indexTokenSymbol, + // indexToken.address, + // inputToken.address, + // indexTokenAmount, + // provider, + // zeroExApi + // ) + // : await getRedemptionComponentSwapData( + // indexTokenSymbol, + // indexToken.address, + // outputToken.address, + // indexTokenAmount, + // provider, + // zeroExApi + // ) + // const componentWrapData = getWrapData(indexToken.symbol) + // if (componentSwapData.length !== componentSwapData.length) return null + // let estimatedInputOutputAmount: BigNumber = BigNumber.from(0) + // const contract = getFlashMintWrappedContract(provider) + // if (isMinting) { + // estimatedInputOutputAmount = await contract.callStatic.getIssueExactSet( + // indexToken.address, + // inputToken.address, + // indexTokenAmount, + // componentSwapData + // ) + // } else { + // estimatedInputOutputAmount = await contract.callStatic.getRedeemExactSet( + // indexToken.address, + // outputToken.address, + // indexTokenAmount, + // componentSwapData + // ) + // } + // const inputOutputTokenAmount = slippageAdjustedTokenAmount( + // estimatedInputOutputAmount, + // isMinting ? inputToken.decimals : outputToken.decimals, + // slippage, + // isMinting + // ) + // const quote: FlashMintWrappedQuote = { + // componentSwapData, + // componentWrapData, + // indexTokenAmount, + // inputOutputTokenAmount, + // } + const quote: FlashMintWrappedQuote = { + componentSwapData: [], + componentWrapData: [], + indexTokenAmount: BigNumber.from(0), + inputOutputTokenAmount: BigNumber.from(0), + } + return quote + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts index bb00abb..f3bbc49 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './addresses' +export * from './component-swap-data' export * from './contracts' export * from './issuanceModules' export * from './numbers' From 2efef081549fbf717919c282898a179530c78d98 Mon Sep 17 00:00:00 2001 From: JD Date: Tue, 10 Sep 2024 14:46:39 +0100 Subject: [PATCH 13/58] chore: clean up --- src/flashmint/builders/wrapped.test.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/flashmint/builders/wrapped.test.ts b/src/flashmint/builders/wrapped.test.ts index 690bf69..5b5e046 100644 --- a/src/flashmint/builders/wrapped.test.ts +++ b/src/flashmint/builders/wrapped.test.ts @@ -25,10 +25,6 @@ const indexToken = usdc.address const usdcAddress = usdc.address describe('WrappedTransactionBuilder()', () => { - beforeEach((): void => { - jest.setTimeout(10000000) - }) - test('returns null for invalid request (no index token)', async () => { const buildRequest = getDefaultBuildRequest() buildRequest.indexToken = '' @@ -85,7 +81,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx).toBeNull() }) - test('returns a tx for minting MMI (ERC20)', async () => { + test('returns a tx for USDCY MMI (ERC20)', async () => { const buildRequest = getDefaultBuildRequest() const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) @@ -104,7 +100,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx.data).toEqual(refTx.data) }) - test('returns a tx for minting MMI (ETH)', async () => { + test('returns a tx for minting USDCY (ETH)', async () => { const buildRequest = getDefaultBuildRequest(true, eth, 'ETH') const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) @@ -123,7 +119,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx.value).toEqual(buildRequest.inputOutputTokenAmount) }) - test('returns a tx for redeeming MMI (ERC20)', async () => { + test('returns a tx for redeeming USDCY (ERC20)', async () => { const buildRequest = getDefaultBuildRequest(false) const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) @@ -142,7 +138,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx.data).toEqual(refTx.data) }) - test('returns a tx for redeeming MMI (ETH)', async () => { + test('returns a tx for redeeming USDCY (ETH)', async () => { const buildRequest = getDefaultBuildRequest(false, eth, 'ETH') const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) @@ -172,7 +168,7 @@ function getDefaultBuildRequest( } return { isMinting, - indexToken: indexToken, + indexToken, inputOutputToken, indexTokenAmount: wei(1), inputOutputTokenAmount: BigNumber.from('16583822409709138541'), From 2c611e1a223793871edf8c18c5fd077cd8394954 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 13 Sep 2024 15:56:41 +0100 Subject: [PATCH 14/58] chore: add export --- src/quote/flashmint/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quote/flashmint/index.ts b/src/quote/flashmint/index.ts index 757ca68..190b82a 100644 --- a/src/quote/flashmint/index.ts +++ b/src/quote/flashmint/index.ts @@ -1,4 +1,5 @@ export * from './hyeth' export * from './leveraged' export * from './leveraged-extended' +export * from './wrapped' export * from './zeroEx' From ac1b3db01616440a2154de549dfe571bd0aad15d Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 13 Sep 2024 16:19:20 +0100 Subject: [PATCH 15/58] feat: add contract type wrapped --- src/constants/tokens.ts | 6 ++++++ src/quote/provider/index.ts | 1 + src/quote/provider/utils.test.ts | 6 ++++++ src/quote/provider/utils.ts | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index 5bcd85a..b03830b 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -1,3 +1,5 @@ +import { getIndexTokenData } from '@indexcoop/tokenlists' + export interface Token { symbol: string address?: string @@ -100,6 +102,10 @@ export const RealWorldAssetIndex: Token = { symbol: 'RWA', } +export const USDCY: Token = { + ...getIndexTokenData('USDCY', 1)!, +} + // Other export const DAI: Token = { diff --git a/src/quote/provider/index.ts b/src/quote/provider/index.ts index bb9bf7c..ef22ecf 100644 --- a/src/quote/provider/index.ts +++ b/src/quote/provider/index.ts @@ -26,6 +26,7 @@ export enum FlashMintContractType { hyeth, leveraged, leveragedExtended, + wrapped, zeroEx, } diff --git a/src/quote/provider/utils.test.ts b/src/quote/provider/utils.test.ts index 73c8da1..dbb9b1f 100644 --- a/src/quote/provider/utils.test.ts +++ b/src/quote/provider/utils.test.ts @@ -14,6 +14,7 @@ import { IndexCoopInverseEthereumIndex, InterestCompoundingETHIndex, MetaverseIndex, + USDCY, } from 'constants/tokens' import { FlashMintContractType } from './' @@ -126,4 +127,9 @@ describe('getContractType()', () => { ) expect(contractType).toBe(FlashMintContractType.leveraged) }) + + test('returns correct contract type for USDCY', async () => { + const contractType = getContractType(USDCY.symbol, ChainId.Mainnet) + expect(contractType).toBe(FlashMintContractType.wrapped) + }) }) diff --git a/src/quote/provider/utils.ts b/src/quote/provider/utils.ts index 0c63edd..82a9d31 100644 --- a/src/quote/provider/utils.ts +++ b/src/quote/provider/utils.ts @@ -17,6 +17,7 @@ import { InterestCompoundingETHIndex, MetaverseIndex, RealWorldAssetIndex, + USDCY, } from 'constants/tokens' import { FlashMintContractType } from './index' @@ -47,6 +48,9 @@ export function getContractType( if (token === HighYieldETHIndex.symbol) { return FlashMintContractType.hyeth } + if (token === USDCY.symbol) { + return FlashMintContractType.wrapped + } if ( token === BanklessBEDIndex.symbol || token === CoinDeskEthTrendIndex.symbol || From a9bb98f61e4f4167510bc7b7c605f67cd3ec9b48 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 13 Sep 2024 16:35:47 +0100 Subject: [PATCH 16/58] test: remove obsolete dseth tests --- src/quote/provider/index.test.ts | 82 +------------------------------- 1 file changed, 2 insertions(+), 80 deletions(-) diff --git a/src/quote/provider/index.test.ts b/src/quote/provider/index.test.ts index 8388cc5..9bf56ed 100644 --- a/src/quote/provider/index.test.ts +++ b/src/quote/provider/index.test.ts @@ -2,11 +2,7 @@ import { ChainId } from 'constants/chains' import { FlashMintHyEthAddress } from 'constants/contracts' import { IndexCoopEthereum2xIndex } from 'constants/tokens' -import { - getFlashMintLeveragedContractForToken, - getFlashMintZeroExContractForToken, - wei, -} from 'utils' +import { getFlashMintLeveragedContractForToken, wei } from 'utils' import { IndexZeroExSwapQuoteProvider, @@ -28,7 +24,7 @@ const rpcUrl = LocalhostProviderUrl const provider = LocalhostProvider const zeroexSwapQuoteProvider = IndexZeroExSwapQuoteProvider -const { dseth, eth, eth2x, hyeth, iceth, usdc } = QuoteTokens +const { eth, eth2x, hyeth, iceth, usdc } = QuoteTokens describe('FlashMintQuoteProvider()', () => { test('throws if token is unsupported', async () => { @@ -54,80 +50,6 @@ describe('FlashMintQuoteProvider()', () => { ) }) - test.skip('returns a quote for minting dsETH', async () => { - const inputToken = usdc - const outputToken = dseth - const contract = getFlashMintZeroExContractForToken( - outputToken.symbol, - undefined - ) - const request: FlashMintQuoteRequest = { - isMinting: true, - inputToken, - outputToken, - indexTokenAmount: wei(1), - slippage: 0.5, - } - const quoteProvider = new FlashMintQuoteProvider( - rpcUrl, - zeroexSwapQuoteProvider - ) - const quote = await quoteProvider.getQuote(request) - if (!quote) fail() - const chainId = (await provider.getNetwork()).chainId - expect(quote.chainId).toEqual(chainId) - expect(quote.contractType).toEqual(FlashMintContractType.zeroEx) - expect(quote.contract).toEqual(contract.address) - expect(quote.isMinting).toEqual(request.isMinting) - expect(quote.inputToken).toEqual(request.inputToken) - expect(quote.outputToken).toEqual(request.outputToken) - expect(quote.inputAmount).toEqual(quote.inputOutputAmount) - expect(quote.outputAmount).toEqual(request.indexTokenAmount) - expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) - expect(quote.inputOutputAmount.gt(0)).toBe(true) - expect(quote.slippage).toEqual(request.slippage) - expect(quote.tx).not.toBeNull() - expect(quote.tx.to).toBe(contract.address) - expect(quote.tx.data?.length).toBeGreaterThan(0) - }) - - test.skip('returns a quote for redeeming dsETH', async () => { - const inputToken = dseth - const outputToken = usdc - const contract = getFlashMintZeroExContractForToken( - inputToken.symbol, - undefined - ) - const request: FlashMintQuoteRequest = { - isMinting: false, - inputToken, - outputToken, - indexTokenAmount: wei(1), - slippage: 0.5, - } - const quoteProvider = new FlashMintQuoteProvider( - rpcUrl, - zeroexSwapQuoteProvider - ) - const quote = await quoteProvider.getQuote(request) - if (!quote) fail() - const chainId = (await provider.getNetwork()).chainId - expect(quote.chainId).toEqual(chainId) - expect(quote.contractType).toEqual(FlashMintContractType.zeroEx) - expect(quote.contract).toEqual(contract.address) - expect(quote.isMinting).toEqual(request.isMinting) - expect(quote.inputToken).toEqual(request.inputToken) - expect(quote.outputToken).toEqual(request.outputToken) - expect(quote.inputAmount).toEqual(request.indexTokenAmount) - expect(quote.outputAmount).toEqual(quote.inputOutputAmount) - expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) - expect(quote.inputOutputAmount.gt(0)).toBe(true) - expect(quote.slippage).toEqual(request.slippage) - expect(quote.tx).not.toBeNull() - expect(quote.tx.to).toBe(contract.address) - expect(quote.tx.data?.length).toBeGreaterThan(0) - }) - test('returns a quote for minting ETH2X', async () => { const arbitrumProvider = LocalhostProviderArbitrum const inputToken = usdc From 9178849f1fe395024b186f12f9c2cd5fe57a7c2d Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 13 Sep 2024 16:43:43 +0100 Subject: [PATCH 17/58] feat: integrate wrapped contract type into quote provider --- src/constants/swapdata.ts | 2 +- src/constants/tokens.ts | 1 + src/quote/provider/index.test.ts | 36 +++++++++++++++++++++++++++++-- src/quote/provider/index.ts | 37 ++++++++++++++++++++++++++++++++ src/tests/utils/quoteTokens.ts | 9 ++++++++ 5 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/constants/swapdata.ts b/src/constants/swapdata.ts index f8c003a..66c6fac 100644 --- a/src/constants/swapdata.ts +++ b/src/constants/swapdata.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Exchange } from 'utils' import { ETH, InterestCompoundingETHIndex, stETH } from './tokens' diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index b03830b..7cd59f7 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { getIndexTokenData } from '@indexcoop/tokenlists' export interface Token { diff --git a/src/quote/provider/index.test.ts b/src/quote/provider/index.test.ts index 9bf56ed..59e9b02 100644 --- a/src/quote/provider/index.test.ts +++ b/src/quote/provider/index.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { ChainId } from 'constants/chains' -import { FlashMintHyEthAddress } from 'constants/contracts' +import { Contracts, FlashMintHyEthAddress } from 'constants/contracts' import { IndexCoopEthereum2xIndex } from 'constants/tokens' import { getFlashMintLeveragedContractForToken, wei } from 'utils' @@ -24,7 +24,7 @@ const rpcUrl = LocalhostProviderUrl const provider = LocalhostProvider const zeroexSwapQuoteProvider = IndexZeroExSwapQuoteProvider -const { eth, eth2x, hyeth, iceth, usdc } = QuoteTokens +const { eth, eth2x, hyeth, iceth, usdc, usdcy } = QuoteTokens describe('FlashMintQuoteProvider()', () => { test('throws if token is unsupported', async () => { @@ -124,6 +124,38 @@ describe('FlashMintQuoteProvider()', () => { expect(quote.tx.data?.length).toBeGreaterThan(0) }) + test.only('returns a quote for minting USDCY', async () => { + const request: FlashMintQuoteRequest = { + isMinting: true, + inputToken: usdc, + outputToken: usdcy, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new FlashMintQuoteProvider( + LocalhostProviderUrl, + IndexZeroExSwapQuoteProvider + ) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + const FlashMintWrappedAddress = Contracts[ChainId.Mainnet].FlashMintWrapped + const chainId = (await provider.getNetwork()).chainId + expect(quote.chainId).toEqual(chainId) + expect(quote.contractType).toEqual(FlashMintContractType.wrapped) + expect(quote.contract).toEqual(FlashMintWrappedAddress) + expect(quote.isMinting).toEqual(request.isMinting) + expect(quote.inputToken).toEqual(request.inputToken) + expect(quote.outputToken).toEqual(request.outputToken) + expect(quote.outputToken).toEqual(request.outputToken) + expect(quote.inputAmount).toEqual(quote.inputOutputAmount) + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputAmount.gt(0)).toBe(true) + expect(quote.slippage).toEqual(request.slippage) + expect(quote.tx).not.toBeNull() + expect(quote.tx.to).toBe(FlashMintWrappedAddress) + expect(quote.tx.data?.length).toBeGreaterThan(0) + }) + test('returns a quote for redeeming ETH2X', async () => { const arbitrumProvider = LocalhostProviderArbitrum const inputToken = { diff --git a/src/quote/provider/index.ts b/src/quote/provider/index.ts index ef22ecf..8c0d1d7 100644 --- a/src/quote/provider/index.ts +++ b/src/quote/provider/index.ts @@ -5,9 +5,11 @@ import { FlashMintHyEthTransactionBuilder, FlashMintLeveragedBuildRequest, FlashMintLeveragedExtendedBuildRequest, + FlashMintWrappedBuildRequest, FlashMintZeroExBuildRequest, LeveragedExtendedTransactionBuilder, LeveragedTransactionBuilder, + WrappedTransactionBuilder, ZeroExTransactionBuilder, } from 'flashmint' import { getRpcProvider } from 'utils/rpc-provider' @@ -16,6 +18,7 @@ import { wei } from 'utils' import { FlashMintHyEthQuoteProvider } from '../flashmint/hyeth' import { LeveragedQuoteProvider } from '../flashmint/leveraged' import { LeveragedExtendedQuoteProvider } from '../flashmint/leveraged-extended' +import { WrappedQuoteProvider } from '../flashmint/wrapped' import { ZeroExQuoteProvider } from '../flashmint/zeroEx' import { QuoteProvider, QuoteToken } from '../interfaces' import { SwapQuoteProvider } from '../swap' @@ -208,6 +211,40 @@ export class FlashMintQuoteProvider tx, } } + case FlashMintContractType.wrapped: { + const wrappedQuoteProvider = new WrappedQuoteProvider(rpcUrl) + const wrappedQuote = await wrappedQuoteProvider.getQuote(request) + if (!wrappedQuote) return null + const builder = new WrappedTransactionBuilder(rpcUrl) + const txRequest: FlashMintWrappedBuildRequest = { + isMinting, + indexToken: indexToken.address, + indexTokenAmount, + inputOutputToken: inputOutputToken.address, + inputOutputTokenSymbol: inputOutputToken.symbol, + inputOutputTokenAmount: wrappedQuote.inputOutputTokenAmount, + componentSwapData: wrappedQuote.componentSwapData, + componentWrapData: wrappedQuote.componentWrapData, + } + const tx = await builder.build(txRequest) + if (!tx) return null + const { inputOutputTokenAmount } = wrappedQuote + return { + chainId, + contractType, + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + contract: tx.to!, + isMinting, + inputToken, + outputToken, + inputAmount: isMinting ? inputOutputTokenAmount : indexTokenAmount, + outputAmount: isMinting ? indexTokenAmount : inputOutputTokenAmount, + indexTokenAmount, + inputOutputAmount: inputOutputTokenAmount, + slippage, + tx, + } + } case FlashMintContractType.zeroEx: { const zeroExQuoteProvider = new ZeroExQuoteProvider( rpcUrl, diff --git a/src/tests/utils/quoteTokens.ts b/src/tests/utils/quoteTokens.ts index 94b9795..251402c 100644 --- a/src/tests/utils/quoteTokens.ts +++ b/src/tests/utils/quoteTokens.ts @@ -12,6 +12,7 @@ import { MetaverseIndex, RETH, USDC, + USDCY, USDT, WETH, sETH2, @@ -104,6 +105,13 @@ const usdc: QuoteToken = { symbol: USDC.symbol, } +const usdcy: QuoteToken = { + address: USDCY.address!, + // FIXME: 18 or 6? + decimals: 18, + symbol: USDCY.symbol, +} + const usdt: QuoteToken = { address: USDT.address!, decimals: 6, @@ -137,6 +145,7 @@ export const QuoteTokens = { seth2, steth, usdc, + usdcy, usdt, weth, wseth, From ff937324b30121058aa46a46e4109cc136d65f57 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 13 Sep 2024 17:08:57 +0100 Subject: [PATCH 18/58] test: skip test for now --- src/quote/provider/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quote/provider/index.test.ts b/src/quote/provider/index.test.ts index 59e9b02..f3e6257 100644 --- a/src/quote/provider/index.test.ts +++ b/src/quote/provider/index.test.ts @@ -124,7 +124,7 @@ describe('FlashMintQuoteProvider()', () => { expect(quote.tx.data?.length).toBeGreaterThan(0) }) - test.only('returns a quote for minting USDCY', async () => { + test.skip('returns a quote for minting USDCY', async () => { const request: FlashMintQuoteRequest = { isMinting: true, inputToken: usdc, From 22f5b779731266ede0b86575fa4666e4424c8889 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 16 Sep 2024 10:05:26 +0100 Subject: [PATCH 19/58] refactor: quote response --- src/quote/provider/index.ts | 96 +++++++++----------------------- src/quote/provider/utils.test.ts | 47 +++++++++++++++- src/quote/provider/utils.ts | 35 +++++++++++- 3 files changed, 105 insertions(+), 73 deletions(-) diff --git a/src/quote/provider/index.ts b/src/quote/provider/index.ts index 8c0d1d7..d001a19 100644 --- a/src/quote/provider/index.ts +++ b/src/quote/provider/index.ts @@ -23,7 +23,7 @@ import { ZeroExQuoteProvider } from '../flashmint/zeroEx' import { QuoteProvider, QuoteToken } from '../interfaces' import { SwapQuoteProvider } from '../swap' -import { getContractType } from './utils' +import { buildQuoteResponse, getContractType } from './utils' export enum FlashMintContractType { hyeth, @@ -116,21 +116,13 @@ export class FlashMintQuoteProvider } const tx = await builder.build(txRequest) if (!tx) return null - return { + return buildQuoteResponse( + request, chainId, contractType, - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - contract: tx.to!, - isMinting, - inputToken, - outputToken, - inputAmount: isMinting ? inputOutputTokenAmount : indexTokenAmount, - outputAmount: isMinting ? indexTokenAmount : inputOutputTokenAmount, - indexTokenAmount, - inputOutputAmount: inputOutputTokenAmount, - slippage, - tx, - } + inputOutputTokenAmount, + tx + ) } case FlashMintContractType.leveraged: { const leveragedQuoteProvider = new LeveragedQuoteProvider( @@ -153,22 +145,13 @@ export class FlashMintQuoteProvider } const tx = await builder.build(txRequest) if (!tx) return null - const { inputOutputTokenAmount } = leveragedQuote - return { + return buildQuoteResponse( + request, chainId, contractType, - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - contract: tx.to!, - isMinting, - inputToken, - outputToken, - inputAmount: isMinting ? inputOutputTokenAmount : indexTokenAmount, - outputAmount: isMinting ? indexTokenAmount : inputOutputTokenAmount, - indexTokenAmount, - inputOutputAmount: inputOutputTokenAmount, - slippage, - tx, - } + leveragedQuote.inputOutputTokenAmount, + tx + ) } case FlashMintContractType.leveragedExtended: { const leverageExtendedQuoteProvider = @@ -194,22 +177,13 @@ export class FlashMintQuoteProvider } const tx = await builder.build(txRequest) if (!tx) return null - const { inputOutputTokenAmount } = leveragedExtendedQuote - return { + return buildQuoteResponse( + request, chainId, contractType, - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - contract: tx.to!, - isMinting, - inputToken, - outputToken, - inputAmount: isMinting ? inputOutputTokenAmount : indexTokenAmount, - outputAmount: isMinting ? indexTokenAmount : inputOutputTokenAmount, - indexTokenAmount, - inputOutputAmount: inputOutputTokenAmount, - slippage, - tx, - } + leveragedExtendedQuote.inputOutputTokenAmount, + tx + ) } case FlashMintContractType.wrapped: { const wrappedQuoteProvider = new WrappedQuoteProvider(rpcUrl) @@ -228,22 +202,13 @@ export class FlashMintQuoteProvider } const tx = await builder.build(txRequest) if (!tx) return null - const { inputOutputTokenAmount } = wrappedQuote - return { + return buildQuoteResponse( + request, chainId, contractType, - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - contract: tx.to!, - isMinting, - inputToken, - outputToken, - inputAmount: isMinting ? inputOutputTokenAmount : indexTokenAmount, - outputAmount: isMinting ? indexTokenAmount : inputOutputTokenAmount, - indexTokenAmount, - inputOutputAmount: inputOutputTokenAmount, - slippage, - tx, - } + wrappedQuote.inputOutputTokenAmount, + tx + ) } case FlashMintContractType.zeroEx: { const zeroExQuoteProvider = new ZeroExQuoteProvider( @@ -265,22 +230,13 @@ export class FlashMintQuoteProvider } const tx = await builder.build(txRequest) if (!tx) return null - const { inputOutputTokenAmount } = zeroExQuote - return { + return buildQuoteResponse( + request, chainId, contractType, - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - contract: tx.to!, - isMinting, - inputToken, - outputToken, - inputAmount: isMinting ? inputOutputTokenAmount : indexTokenAmount, - outputAmount: isMinting ? indexTokenAmount : inputOutputTokenAmount, - indexTokenAmount, - inputOutputAmount: inputOutputTokenAmount, - slippage, - tx, - } + zeroExQuote.inputOutputTokenAmount, + tx + ) } default: return null diff --git a/src/quote/provider/utils.test.ts b/src/quote/provider/utils.test.ts index dbb9b1f..2d75e09 100644 --- a/src/quote/provider/utils.test.ts +++ b/src/quote/provider/utils.test.ts @@ -1,4 +1,5 @@ import { ChainId } from 'constants/chains' +import { Contracts } from 'constants/contracts' import { BanklessBEDIndex, DefiPulseIndex, @@ -16,9 +17,51 @@ import { MetaverseIndex, USDCY, } from 'constants/tokens' +import { QuoteTokens } from 'tests/utils' +import { wei } from 'utils' -import { FlashMintContractType } from './' -import { getContractType } from './utils' +import { FlashMintContractType, FlashMintQuoteRequest } from './' +import { buildQuoteResponse, getContractType } from './utils' + +const { usdc, usdcy } = QuoteTokens + +describe('buildQuoteResponse()', () => { + test('returns correct quote response object', async () => { + const request: FlashMintQuoteRequest = { + isMinting: true, + inputToken: usdc, + outputToken: usdcy, + indexTokenAmount: wei(1), + slippage: 0.1, + } + const quoteAmount = wei(100, 6) + const tx = { + to: Contracts[ChainId.Mainnet].FlashMintWrapped, + value: wei(1), + } + const response = buildQuoteResponse( + request, + 1, + FlashMintContractType.wrapped, + quoteAmount, + tx + ) + expect(response).toEqual({ + chainId: 1, + contractType: FlashMintContractType.wrapped, + contract: Contracts[ChainId.Mainnet].FlashMintWrapped, + isMinting: true, + inputToken: usdc, + outputToken: usdcy, + inputAmount: quoteAmount, + outputAmount: request.indexTokenAmount, + indexTokenAmount: request.indexTokenAmount, + inputOutputAmount: quoteAmount, + slippage: 0.1, + tx, + }) + }) +}) describe('getContractType()', () => { test('returns correct contract type for leveraged arbitrum tokens', async () => { diff --git a/src/quote/provider/utils.ts b/src/quote/provider/utils.ts index 82a9d31..e5c44bb 100644 --- a/src/quote/provider/utils.ts +++ b/src/quote/provider/utils.ts @@ -1,3 +1,6 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { TransactionRequest } from '@ethersproject/abstract-provider' + import { ChainId } from 'constants/chains' import { BanklessBEDIndex, @@ -20,7 +23,37 @@ import { USDCY, } from 'constants/tokens' -import { FlashMintContractType } from './index' +import { + FlashMintContractType, + FlashMintQuote, + FlashMintQuoteRequest, +} from './index' + +export function buildQuoteResponse( + request: FlashMintQuoteRequest, + chainId: number, + contractType: FlashMintContractType, + inputOutputTokenAmount: BigNumber, // quote amount + tx: TransactionRequest +): FlashMintQuote { + const { isMinting, indexTokenAmount, inputToken, outputToken, slippage } = + request + return { + chainId, + contractType, + /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ + contract: tx.to!, + isMinting, + inputToken, + outputToken, + inputAmount: isMinting ? inputOutputTokenAmount : indexTokenAmount, + outputAmount: isMinting ? indexTokenAmount : inputOutputTokenAmount, + indexTokenAmount, + inputOutputAmount: inputOutputTokenAmount, + slippage, + tx, + } +} // Returns contract type for token or null if not supported export function getContractType( From d5fb71db1797a9c0e87ad140f1f0b55b7701f66f Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 16 Sep 2024 10:10:00 +0100 Subject: [PATCH 20/58] build: upgrade tokenlists --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2a4550..b9eab7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@ethersproject/contracts": "^5.6.2", "@ethersproject/providers": "^5.6.8", "@ethersproject/units": "^5.6.1", - "@indexcoop/tokenlists": "1.48.0", + "@indexcoop/tokenlists": "^1.56.0", "@lifi/sdk": "3.0.0-beta.1", "@uniswap/sdk-core": "^5.3.1", "@uniswap/v3-sdk": "^3.13.1", @@ -3097,9 +3097,9 @@ "dev": true }, "node_modules/@indexcoop/tokenlists": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.48.0.tgz", - "integrity": "sha512-T0moSlCH8Hohoo49pb3iVrFJ4KHud4Ejdu6OPtATTzAVJ2K2K177ErfrvZL+IPxgYXCNCz738iV+HUj9h79T0Q==", + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.56.0.tgz", + "integrity": "sha512-d7nb5ULnbVm3rxYe88nol89kBggOytw11SkbEzFcVwjZ2IbcpGq6n//TEGAsRcCEMvhQ7cln1BEZhc+xp9s9aA==", "engines": { "node": ">=16" }, @@ -20104,9 +20104,9 @@ "dev": true }, "@indexcoop/tokenlists": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.48.0.tgz", - "integrity": "sha512-T0moSlCH8Hohoo49pb3iVrFJ4KHud4Ejdu6OPtATTzAVJ2K2K177ErfrvZL+IPxgYXCNCz738iV+HUj9h79T0Q==", + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.56.0.tgz", + "integrity": "sha512-d7nb5ULnbVm3rxYe88nol89kBggOytw11SkbEzFcVwjZ2IbcpGq6n//TEGAsRcCEMvhQ7cln1BEZhc+xp9s9aA==", "requires": {} }, "@istanbuljs/load-nyc-config": { diff --git a/package.json b/package.json index bf858fa..77e3e5e 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@ethersproject/contracts": "^5.6.2", "@ethersproject/providers": "^5.6.8", "@ethersproject/units": "^5.6.1", - "@indexcoop/tokenlists": "1.48.0", + "@indexcoop/tokenlists": "^1.56.0", "@lifi/sdk": "3.0.0-beta.1", "@uniswap/sdk-core": "^5.3.1", "@uniswap/v3-sdk": "^3.13.1", From a8d0c9b43e6db88566a3377981eab2de7467b993 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 16 Sep 2024 10:12:22 +0100 Subject: [PATCH 21/58] chore: use usdcy symbol form index token --- src/utils/issuanceModules.ts | 3 ++- src/utils/wrap-data.ts | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/utils/issuanceModules.ts b/src/utils/issuanceModules.ts index d98a4e1..eb9ff1f 100644 --- a/src/utils/issuanceModules.ts +++ b/src/utils/issuanceModules.ts @@ -20,6 +20,7 @@ import { IndexCoopBitcoin2xIndex, HighYieldETHIndex, RealWorldAssetIndex, + USDCY, } from '../constants/tokens' export interface IssuanceModule { @@ -64,7 +65,7 @@ export function getIssuanceModule( isDebtIssuance: true, } case GitcoinStakedETHIndex.symbol: - case 'USDCY': // TODO: add index token + case USDCY.symbol: case wsETH2.symbol: return { address: IndexDebtIssuanceModuleV2Address, isDebtIssuance: true } case InterestCompoundingETHIndex.symbol: diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts index 907cd66..87f4d8b 100644 --- a/src/utils/wrap-data.ts +++ b/src/utils/wrap-data.ts @@ -1,3 +1,5 @@ +import { USDCY } from 'constants/tokens' + export interface ComponentWrapData { // wrap adapter integration name as listed in the IntegrationRegistry for the wrapModule integrationName: string @@ -5,7 +7,7 @@ export interface ComponentWrapData { wrapData: string } -// TODO: check that adapter names are static +// FIXME: check adapter names const aaveV2WrapV2AdapterName = 'Aave_V2_Wrap_V2_Adapter' const aaveV3WrapV2AdapterName = 'Aave_V3_Wrap_V2_Adapter' const compoundV3WrapV2AdapterName = 'Compound_V3_USDC_Wrap_V2_Adapter' @@ -13,8 +15,7 @@ const erc4626WrapV2AdapterName = 'ERC4626_Wrap_V2_Adapter' const ZERO_BYTES = '0x0000000000000000000000000000000000000000' export function getWrapData(tokenSymbol: string): ComponentWrapData[] { - // TODO: add check once token is available - if (tokenSymbol !== 'USDCY') return [] + if (tokenSymbol !== USDCY.symbol) return [] return [ { integrationName: '', From c576a3dc55ecbe7f6142f0fdff664f2c67604f11 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 16 Sep 2024 10:14:42 +0100 Subject: [PATCH 22/58] chore: use index token --- src/flashmint/builders/wrapped.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/flashmint/builders/wrapped.test.ts b/src/flashmint/builders/wrapped.test.ts index 5b5e046..ca1407d 100644 --- a/src/flashmint/builders/wrapped.test.ts +++ b/src/flashmint/builders/wrapped.test.ts @@ -16,12 +16,11 @@ import { const rpcUrl = LocalhostProviderUrl const ZERO_BYTES = '0x0000000000000000000000000000000000000000' -const { usdc } = QuoteTokens +const { usdc, usdcy } = QuoteTokens const FlashMintWrappedAddress = Contracts[ChainId.Mainnet].FlashMintWrapped const eth = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' -// FIXME: add address once token is available -const indexToken = usdc.address +const indexToken = usdcy.address const usdcAddress = usdc.address describe('WrappedTransactionBuilder()', () => { From e7cfff914b0616b9ad667e54842c697cea89a0b4 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 16 Sep 2024 10:31:51 +0100 Subject: [PATCH 23/58] chore: use usdcy --- src/utils/issuanceModules.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/issuanceModules.test.ts b/src/utils/issuanceModules.test.ts index f6cc8ed..16ff2c3 100644 --- a/src/utils/issuanceModules.test.ts +++ b/src/utils/issuanceModules.test.ts @@ -19,6 +19,7 @@ import { IndexCoopBitcoin2xIndex, IndexCoopBitcoin3xIndex, HighYieldETHIndex, + USDCY, } from 'constants/tokens' import { getIssuanceModule } from './issuanceModules' @@ -61,8 +62,7 @@ describe('getIssuanceModule() - Mainnet - IndexProtocol', () => { test('returns debt issuance module v2 for USDCY', () => { const expectedModule = IndexDebtIssuanceModuleV2Address - // FIXME: change to USDCY once available - const issuanceModule = getIssuanceModule('USDCY') + const issuanceModule = getIssuanceModule(USDCY.symbol) expect(issuanceModule.address).toEqual(expectedModule) expect(issuanceModule.isDebtIssuance).toBe(true) }) From 72103bb13512b526b4e19d793ae9501204b58fea Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 16 Sep 2024 13:32:49 +0100 Subject: [PATCH 24/58] feat: use wrap data in wrapped quote provider --- src/quote/flashmint/wrapped/provider.test.ts | 20 +++++++++----------- src/quote/flashmint/wrapped/provider.ts | 16 +++++----------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/quote/flashmint/wrapped/provider.test.ts b/src/quote/flashmint/wrapped/provider.test.ts index 806cdd9..72da256 100644 --- a/src/quote/flashmint/wrapped/provider.test.ts +++ b/src/quote/flashmint/wrapped/provider.test.ts @@ -2,17 +2,13 @@ import { LocalhostProviderUrl, QuoteTokens } from 'tests/utils' import { wei } from 'utils/numbers' import { FlashMintWrappedQuoteRequest, WrappedQuoteProvider } from '.' -const { dai, usdc, weth } = QuoteTokens -const indexToken = usdc +const { dai, usdc, usdcy, weth } = QuoteTokens +const indexToken = usdcy const provider = LocalhostProviderUrl -describe.skip('WrappedQuoteProvider()', () => { - beforeEach((): void => { - jest.setTimeout(100000000) - }) - - test('returns a quote for minting MMI w/ DAI', async () => { - const inputToken = dai +describe('WrappedQuoteProvider()', () => { + test.only('returns a quote for minting USDCY', async () => { + const inputToken = usdc const request: FlashMintWrappedQuoteRequest = { isMinting: true, inputToken, @@ -24,8 +20,10 @@ describe.skip('WrappedQuoteProvider()', () => { const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) - expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - expect(quote.componentSwapData.length).toEqual(6) + // FIXME: test + // expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + // expect(quote.componentSwapData.length).toEqual(6) + expect(quote.componentWrapData.length).toEqual(5) }) test('returns a quote for minting MMI w/ USDC', async () => { diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts index 1641583..3bf901e 100644 --- a/src/quote/flashmint/wrapped/provider.ts +++ b/src/quote/flashmint/wrapped/provider.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@ethersproject/bignumber' -import { ComponentSwapData, ComponentWrapData } from 'utils' +import { ComponentSwapData, ComponentWrapData, getWrapData } from 'utils' import { QuoteProvider, QuoteToken } from '../../interfaces' import { getRpcProvider } from 'utils/rpc-provider' @@ -39,7 +39,7 @@ export class WrappedQuoteProvider isMinting, slippage ) - // const indexToken = isMinting ? outputToken : inputToken + const indexToken = isMinting ? outputToken : inputToken // const indexTokenSymbol = indexToken.symbol // const componentSwapData = isMinting // ? await getIssuanceComponentSwapData( @@ -58,7 +58,7 @@ export class WrappedQuoteProvider // provider, // zeroExApi // ) - // const componentWrapData = getWrapData(indexToken.symbol) + const componentWrapData = getWrapData(indexToken.symbol) // if (componentSwapData.length !== componentSwapData.length) return null // let estimatedInputOutputAmount: BigNumber = BigNumber.from(0) // const contract = getFlashMintWrappedContract(provider) @@ -83,16 +83,10 @@ export class WrappedQuoteProvider // slippage, // isMinting // ) - // const quote: FlashMintWrappedQuote = { - // componentSwapData, - // componentWrapData, - // indexTokenAmount, - // inputOutputTokenAmount, - // } const quote: FlashMintWrappedQuote = { componentSwapData: [], - componentWrapData: [], - indexTokenAmount: BigNumber.from(0), + componentWrapData, + indexTokenAmount, inputOutputTokenAmount: BigNumber.from(0), } return quote From 9bf688d7442983f7d7b68a74ddfc195052d80419 Mon Sep 17 00:00:00 2001 From: JD Date: Tue, 17 Sep 2024 07:40:47 +0100 Subject: [PATCH 25/58] chore: remove fixme --- src/tests/utils/quoteTokens.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/utils/quoteTokens.ts b/src/tests/utils/quoteTokens.ts index 251402c..1ab8f69 100644 --- a/src/tests/utils/quoteTokens.ts +++ b/src/tests/utils/quoteTokens.ts @@ -107,7 +107,6 @@ const usdc: QuoteToken = { const usdcy: QuoteToken = { address: USDCY.address!, - // FIXME: 18 or 6? decimals: 18, symbol: USDCY.symbol, } From 47b276f5ea9e7dc496a914bf01ad99a79340a6d5 Mon Sep 17 00:00:00 2001 From: JD Date: Tue, 17 Sep 2024 15:02:05 +0100 Subject: [PATCH 26/58] feat: start assembling component swap data --- src/constants/tokens.ts | 2 + src/quote/flashmint/wrapped/provider.test.ts | 27 +--- src/quote/flashmint/wrapped/provider.ts | 51 +++--- src/utils/component-swap-data.test.ts | 157 ++++++++++--------- src/utils/component-swap-data.ts | 131 +++++++++------- 5 files changed, 195 insertions(+), 173 deletions(-) diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index 7cd59f7..b563ce5 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -105,6 +105,8 @@ export const RealWorldAssetIndex: Token = { export const USDCY: Token = { ...getIndexTokenData('USDCY', 1)!, + // FIXME: delete; just for testing + address: '0xc30FBa978743a43E736fc32FBeEd364b8A2039cD', } // Other diff --git a/src/quote/flashmint/wrapped/provider.test.ts b/src/quote/flashmint/wrapped/provider.test.ts index 72da256..c2eb34c 100644 --- a/src/quote/flashmint/wrapped/provider.test.ts +++ b/src/quote/flashmint/wrapped/provider.test.ts @@ -2,7 +2,7 @@ import { LocalhostProviderUrl, QuoteTokens } from 'tests/utils' import { wei } from 'utils/numbers' import { FlashMintWrappedQuoteRequest, WrappedQuoteProvider } from '.' -const { dai, usdc, usdcy, weth } = QuoteTokens +const { usdc, usdcy, weth } = QuoteTokens const indexToken = usdcy const provider = LocalhostProviderUrl @@ -22,7 +22,7 @@ describe('WrappedQuoteProvider()', () => { expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) // FIXME: test // expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - // expect(quote.componentSwapData.length).toEqual(6) + expect(quote.componentSwapData.length).toEqual(6) expect(quote.componentWrapData.length).toEqual(5) }) @@ -60,24 +60,7 @@ describe('WrappedQuoteProvider()', () => { expect(quote.componentSwapData.length).toEqual(6) }) - test('returns a quote for redeeming MMI for DAI', async () => { - const outputToken = dai - const request: FlashMintWrappedQuoteRequest = { - isMinting: false, - inputToken: indexToken, - outputToken, - indexTokenAmount: wei(1), - slippage: 0.5, - } - const quoteProvider = new WrappedQuoteProvider(provider) - const quote = await quoteProvider.getQuote(request) - if (!quote) fail() - expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) - expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - expect(quote.componentSwapData.length).toEqual(6) - }) - - test('returns a quote for redeeming MMI for USDC', async () => { + test.only('returns a quote redeeming USDCY for USDC', async () => { const outputToken = usdc const request: FlashMintWrappedQuoteRequest = { isMinting: false, @@ -90,8 +73,10 @@ describe('WrappedQuoteProvider()', () => { const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) - expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + // FIXME: + // expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) expect(quote.componentSwapData.length).toEqual(6) + expect(quote.componentWrapData.length).toEqual(5) }) test('returns a quote for redeeming MMI for WETH', async () => { diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts index 3bf901e..85c0f66 100644 --- a/src/quote/flashmint/wrapped/provider.ts +++ b/src/quote/flashmint/wrapped/provider.ts @@ -1,6 +1,12 @@ import { BigNumber } from '@ethersproject/bignumber' -import { ComponentSwapData, ComponentWrapData, getWrapData } from 'utils' +import { + ComponentSwapData, + ComponentWrapData, + getIssuanceComponentSwapData, + getRedemptionComponentSwapData, + getWrapData, +} from 'utils' import { QuoteProvider, QuoteToken } from '../../interfaces' import { getRpcProvider } from 'utils/rpc-provider' @@ -40,26 +46,29 @@ export class WrappedQuoteProvider slippage ) const indexToken = isMinting ? outputToken : inputToken - // const indexTokenSymbol = indexToken.symbol - // const componentSwapData = isMinting - // ? await getIssuanceComponentSwapData( - // indexTokenSymbol, - // indexToken.address, - // inputToken.address, - // indexTokenAmount, - // provider, - // zeroExApi - // ) - // : await getRedemptionComponentSwapData( - // indexTokenSymbol, - // indexToken.address, - // outputToken.address, - // indexTokenAmount, - // provider, - // zeroExApi - // ) + const indexTokenSymbol = indexToken.symbol + const componentSwapData = isMinting + ? await getIssuanceComponentSwapData( + { + indexTokenSymbol, + indexToken: indexToken.address, + inputToken: inputToken.address, + indexTokenAmount, + }, + this.rpcUrl + ) + : await getRedemptionComponentSwapData( + { + indexTokenSymbol, + indexToken: indexToken.address, + outputToken: outputToken.address, + indexTokenAmount, + }, + this.rpcUrl + ) const componentWrapData = getWrapData(indexToken.symbol) - // if (componentSwapData.length !== componentSwapData.length) return null + // FIXME: add check + // if (componentSwapData.length !== componentWrapData.length) return null // let estimatedInputOutputAmount: BigNumber = BigNumber.from(0) // const contract = getFlashMintWrappedContract(provider) // if (isMinting) { @@ -84,7 +93,7 @@ export class WrappedQuoteProvider // isMinting // ) const quote: FlashMintWrappedQuote = { - componentSwapData: [], + componentSwapData, componentWrapData, indexTokenAmount, inputOutputTokenAmount: BigNumber.from(0), diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index e9221a5..6130929 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -1,42 +1,38 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -// import { USDC, USDT, WETH } from 'constants/tokens' -// import { LocalhostProvider, ZeroExApiSwapQuote } from 'tests/utils' -// import { wei } from 'utils/numbers' +import { USDC, USDCY } from 'constants/tokens' +import { + getIssuanceComponentSwapData, + getRedemptionComponentSwapData, +} from 'utils/component-swap-data' +import { wei } from 'utils/numbers' +import { LocalhostProviderUrl } from 'tests/utils' +import { isSameAddress } from 'utils/addresses' -// import { -// getIssuanceComponentSwapData, -// getRedemptionComponentSwapData, -// } from './componentSwapData' -// import { Exchange } from './swapData' +const rpcUrl = LocalhostProviderUrl -// const provider = LocalhostProvider -// const zeroExApi = ZeroExApiSwapQuote - -// const dai = DAI.address!.toLowerCase() -// const usdc = USDC.address!.toLowerCase() -// const usdt = USDT.address!.toLowerCase() -// const weth = WETH.address!.toLowerCase() +const indexTokenSymbol = USDCY.symbol +const indexToken = USDCY.address! +const usdc = USDC.address! // const zeroAddress = '0x0000000000000000000000000000000000000000' -describe.skip('getIssuanceComponentSwapData()', () => { - test('returns correct swap data based on input token (USDC)', async () => { - expect(true).toBe(true) - // const inputToken = usdc - // const componentSwapData = await getIssuanceComponentSwapData( - // MoneyMarketIndexToken.symbol, - // MoneyMarketIndexToken.address!, - // inputToken, - // wei(1), - // provider, - // zeroExApi - // ) - // expect(componentSwapData.length).toBe(6) - // expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) - // expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) - // expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) - // expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) - // expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) - // expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) +describe('getIssuanceComponentSwapData()', () => { + test('returns correct swap data based on input token USDC', async () => { + const componentSwapData = await getIssuanceComponentSwapData( + { + indexTokenSymbol, + indexToken, + inputToken: usdc, + indexTokenAmount: wei(1), + }, + rpcUrl + ) + // FIXME: should be 5 for USDCY + expect(componentSwapData.length).toBe(6) + for (let i = 0; i < componentSwapData.length; i++) { + expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( + true + ) + } // // Should be empty as input token is equal to output token // expect(componentSwapData[0].dexData.exchange).toEqual(Exchange.None) // expect(componentSwapData[0].dexData.path).toEqual([ @@ -96,49 +92,56 @@ describe.skip('getIssuanceComponentSwapData()', () => { // }) }) -// describe.skip('getRedemptionComponentSwapData()', () => { -// test('returns correct swap data based on output token (USDC)', async () => { -// const outputToken = usdc -// const componentSwapData = await getRedemptionComponentSwapData( -// MoneyMarketIndexToken.symbol, -// MoneyMarketIndexToken.address!, -// outputToken, -// wei(1), -// provider, -// zeroExApi -// ) -// expect(componentSwapData.length).toBe(6) -// expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) -// expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) -// expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) -// expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) -// expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) -// expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) -// // Should be empty as input token is equal to output token -// expect(componentSwapData[0].dexData.exchange).toEqual(Exchange.None) -// expect(componentSwapData[0].dexData.path).toEqual([ -// zeroAddress, -// zeroAddress, -// ]) -// expect(componentSwapData[1].dexData.path).toEqual([dai, usdc]) -// expect(componentSwapData[2].dexData.path).toEqual([usdt, usdc]) -// expect(componentSwapData[3].dexData.path).toEqual([usdt, usdc]) -// expect(componentSwapData[4].dexData.path).toEqual([dai, usdc]) -// // Should be empty as input token is equal to output token -// expect(componentSwapData[5].dexData.exchange).toEqual(Exchange.None) -// expect(componentSwapData[5].dexData.path).toEqual([ -// zeroAddress, -// zeroAddress, -// ]) -// componentSwapData.forEach((swapData, index) => { -// expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) -// if (index > 0 && index < 5) { -// expect(swapData.dexData.exchange).toEqual(Exchange.UniV3) -// expect(swapData.dexData.fees.length).toBeGreaterThan(0) -// } -// expect(swapData.dexData.pool).toBe(zeroAddress) -// }) -// }) +describe('getRedemptionComponentSwapData()', () => { + test('returns correct swap data based for output token USDC', async () => { + const componentSwapData = await getRedemptionComponentSwapData( + { + indexTokenSymbol, + indexToken, + outputToken: usdc, + indexTokenAmount: wei(1), + }, + rpcUrl + ) + // FIXME: should be 5 for USDCY + expect(componentSwapData.length).toBe(6) + for (let i = 0; i < componentSwapData.length; i++) { + expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( + true + ) + } + // expect(componentSwapData.length).toBe(6) + // expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) + // expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) + // expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) + // expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) + // expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) + // expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) + // // Should be empty as input token is equal to output token + // expect(componentSwapData[0].dexData.exchange).toEqual(Exchange.None) + // expect(componentSwapData[0].dexData.path).toEqual([ + // zeroAddress, + // zeroAddress, + // ]) + // expect(componentSwapData[1].dexData.path).toEqual([dai, usdc]) + // expect(componentSwapData[2].dexData.path).toEqual([usdt, usdc]) + // expect(componentSwapData[3].dexData.path).toEqual([usdt, usdc]) + // expect(componentSwapData[4].dexData.path).toEqual([dai, usdc]) + // // Should be empty as input token is equal to output token + // expect(componentSwapData[5].dexData.exchange).toEqual(Exchange.None) + // expect(componentSwapData[5].dexData.path).toEqual([ + // zeroAddress, + // zeroAddress, + // ]) + // componentSwapData.forEach((swapData, index) => { + // expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) + // if (index > 0 && index < 5) { + // expect(swapData.dexData.exchange).toEqual(Exchange.UniV3) + // expect(swapData.dexData.fees.length).toBeGreaterThan(0) + // } + // expect(swapData.dexData.pool).toBe(zeroAddress) + }) +}) // test('returns correct swap data when output token is WETH', async () => { // const outputToken = weth diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 2a126e1..4f5962a 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -1,11 +1,12 @@ import { BigNumber } from '@ethersproject/bignumber' - -import { USDC } from 'constants/tokens' -import { Exchange, SwapData } from 'utils/swap-data' +import { Contract } from '@ethersproject/contracts' import { Address, createPublicClient, http, parseAbi } from 'viem' import { mainnet } from 'viem/chains' -// import { ZeroExApi } from './0x' +import { USDC } from 'constants/tokens' +import { getIssuanceModule } from 'utils/issuanceModules' +import { getRpcProvider } from 'utils/rpc-provider' +import { Exchange, SwapData } from 'utils/swap-data' /* eslint-disable @typescript-eslint/no-non-null-assertion */ const usdc = USDC.address! @@ -34,6 +35,12 @@ export interface ComponentSwapData { buyUnderlyingAmount: BigNumber } +interface ComponentSwapDataRequest { + indexTokenSymbol: string + indexToken: string + indexTokenAmount: BigNumber +} + interface WrappedToken { address: string decimals: number @@ -44,11 +51,6 @@ interface WrappedToken { } } -// const IssuanceAbi = [ -// 'function getRequiredComponentIssuanceUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', -// 'function getRequiredComponentRedemptionUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', -// ] - // const erc4626Abi = [ // 'constructor(address _morpho, address _morphoToken, address _lens, address _recipient)', // 'error ZeroAddress()', @@ -133,25 +135,44 @@ interface WrappedToken { // return buyUnderlyingAmount.mul(multiplier).div(10000) // } -export async function getIssuanceComponentSwapData(): Promise< - ComponentSwapData[] -> { - // const issuanceModule = getIssuanceModule(indexTokenSymbol) - // const issuance = new Contract(issuanceModule.address, IssuanceAbi, provider) - // const [issuanceComponents, issuanceUnits] = - // await issuance.getRequiredComponentIssuanceUnits( - // indexToken, - // indexTokenAmount - // ) - const issuanceComponents = ['0x', '0x', '0x'] +interface IssuanceRequest extends ComponentSwapDataRequest { + inputToken: string +} + +function getIssuanceContract( + indexTokenSymbol: string, + rpcUrl: string +): Contract { + const abi = [ + 'function getRequiredComponentIssuanceUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', + 'function getRequiredComponentRedemptionUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', + ] + const provider = getRpcProvider(rpcUrl) + const issuanceModule = getIssuanceModule(indexTokenSymbol) + return new Contract(issuanceModule.address, abi, provider) +} + +export async function getIssuanceComponentSwapData( + request: IssuanceRequest, + rpcUrl: string +): Promise { + const { indexTokenSymbol, indexToken, indexTokenAmount } = request + const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) + const [issuanceComponents] = await issuance.getRequiredComponentIssuanceUnits( + indexToken, + indexTokenAmount + ) const underlyingERC20sPromises: Promise[] = issuanceComponents.map((component: string) => getUnderlyingErc20(component)) - // const buyAmountsPromises = issuanceComponents.map( - // (component: string, index: number) => - // getAmountOfAssetToObtainShares(component, issuanceUnits[index], provider) - // ) + // TODO: + // const buyAmountsPromises = issuanceComponents.map( + // (component: string, index: number) => + // getAmountOfAssetToObtainShares(component, issuanceUnits[index], provider) + // ) // const buyAmounts = await Promise.all(buyAmountsPromises) - // const wrappedTokens = await Promise.all(underlyingERC20sPromises) + const wrappedTokens = await Promise.all(underlyingERC20sPromises) + console.log(wrappedTokens) + // TODO: get swap data // const swaps: Promise<{ swapData: SwapData } | null>[] = // issuanceComponents.map((_: string, index: number) => { // const wrappedToken = wrappedTokens[index] @@ -178,7 +199,7 @@ export async function getIssuanceComponentSwapData(): Promise< // } // }) // return swapData - const wrappedTokens = underlyingERC20sPromises.map(() => { + const componentSwapData = wrappedTokens.map(() => { const swapData: ComponentSwapData = { underlyingERC20: usdc, buyUnderlyingAmount: BigNumber.from(0), @@ -186,30 +207,29 @@ export async function getIssuanceComponentSwapData(): Promise< } return swapData }) - return wrappedTokens + return componentSwapData +} + +interface RedemptionRequest extends ComponentSwapDataRequest { + outputToken: string } -export async function getRedemptionComponentSwapData(): Promise< - // indexTokenSymbol: string, - // indexToken: string, - // outputToken: string, - // indexTokenAmount: BigNumber, - // provider: JsonRpcProvider - // zeroExApi: ZeroExApi - ComponentSwapData[] -> { - // const issuanceModule = getIssuanceModule(indexTokenSymbol) - // const issuance = new Contract(issuanceModule.address, IssuanceAbi, provider) - // const [issuanceComponents, issuanceUnits] = - // await issuance.getRequiredComponentRedemptionUnits( - // indexToken, - // indexTokenAmount - // ) - // const underlyingERC20sPromises: Promise[] = - // issuanceComponents.map((component: string) => - // getUnderlyingErc20(component, provider) - // ) - // const wrappedTokens = await Promise.all(underlyingERC20sPromises) +export async function getRedemptionComponentSwapData( + request: RedemptionRequest, + rpcUrl: string +): Promise { + const { indexTokenSymbol, indexToken, indexTokenAmount } = request + const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) + const [issuanceComponents] = + await issuance.getRequiredComponentRedemptionUnits( + indexToken, + indexTokenAmount + ) + const underlyingERC20sPromises: Promise[] = + issuanceComponents.map((component: string) => getUnderlyingErc20(component)) + const wrappedTokens = await Promise.all(underlyingERC20sPromises) + console.log(wrappedTokens) + // TODO: // const buyAmountsPromises = issuanceComponents.map( // (component: string, index: number) => // getAmountOfAssetToObtainShares( @@ -244,12 +264,15 @@ export async function getRedemptionComponentSwapData(): Promise< // dexData, // } // }) - const swapData: ComponentSwapData = { - underlyingERC20: usdc, - buyUnderlyingAmount: BigNumber.from(0), - dexData: emptySwapData, - } - return [swapData] + const componentSwapData = wrappedTokens.map(() => { + const swapData: ComponentSwapData = { + underlyingERC20: usdc, + buyUnderlyingAmount: BigNumber.from(0), + dexData: emptySwapData, + } + return swapData + }) + return componentSwapData } async function getUnderlyingErc20(token: string): Promise { From db6804b63dd433e044ce6687c933fee5ae96080a Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 18 Sep 2024 10:49:58 +0100 Subject: [PATCH 27/58] add calling get issue/redeem exact set --- src/quote/flashmint/wrapped/provider.ts | 57 +++++++++++++------------ 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts index 85c0f66..f436ccc 100644 --- a/src/quote/flashmint/wrapped/provider.ts +++ b/src/quote/flashmint/wrapped/provider.ts @@ -3,13 +3,15 @@ import { BigNumber } from '@ethersproject/bignumber' import { ComponentSwapData, ComponentWrapData, + getFlashMintWrappedContract, getIssuanceComponentSwapData, getRedemptionComponentSwapData, getWrapData, + slippageAdjustedTokenAmount, } from 'utils' +import { getRpcProvider } from 'utils/rpc-provider' import { QuoteProvider, QuoteToken } from '../../interfaces' -import { getRpcProvider } from 'utils/rpc-provider' export interface FlashMintWrappedQuoteRequest { isMinting: boolean @@ -34,8 +36,6 @@ export class WrappedQuoteProvider async getQuote( request: FlashMintWrappedQuoteRequest ): Promise { - const provider = getRpcProvider(this.rpcUrl) - console.log(provider.connection.url) const { inputToken, indexTokenAmount, isMinting, outputToken, slippage } = request console.log( @@ -66,37 +66,40 @@ export class WrappedQuoteProvider }, this.rpcUrl ) + console.log(componentSwapData) const componentWrapData = getWrapData(indexToken.symbol) // FIXME: add check // if (componentSwapData.length !== componentWrapData.length) return null - // let estimatedInputOutputAmount: BigNumber = BigNumber.from(0) - // const contract = getFlashMintWrappedContract(provider) - // if (isMinting) { - // estimatedInputOutputAmount = await contract.callStatic.getIssueExactSet( - // indexToken.address, - // inputToken.address, - // indexTokenAmount, - // componentSwapData - // ) - // } else { - // estimatedInputOutputAmount = await contract.callStatic.getRedeemExactSet( - // indexToken.address, - // outputToken.address, - // indexTokenAmount, - // componentSwapData - // ) - // } - // const inputOutputTokenAmount = slippageAdjustedTokenAmount( - // estimatedInputOutputAmount, - // isMinting ? inputToken.decimals : outputToken.decimals, - // slippage, - // isMinting - // ) + let estimatedInputOutputAmount: BigNumber = BigNumber.from(0) + const provider = getRpcProvider(this.rpcUrl) + const contract = getFlashMintWrappedContract(provider) + if (isMinting) { + estimatedInputOutputAmount = await contract.callStatic.getIssueExactSet( + indexToken.address, + inputToken.address, + indexTokenAmount, + componentSwapData + ) + } else { + estimatedInputOutputAmount = await contract.callStatic.getRedeemExactSet( + indexToken.address, + outputToken.address, + indexTokenAmount, + componentSwapData + ) + } + // FIXME: add slippage? + const inputOutputTokenAmount = slippageAdjustedTokenAmount( + estimatedInputOutputAmount, + isMinting ? inputToken.decimals : outputToken.decimals, + slippage, + isMinting + ) const quote: FlashMintWrappedQuote = { componentSwapData, componentWrapData, indexTokenAmount, - inputOutputTokenAmount: BigNumber.from(0), + inputOutputTokenAmount, } return quote } From ff02fff283c2665a2c2f62f705d953b7f3dfa102 Mon Sep 17 00:00:00 2001 From: JD Date: Wed, 18 Sep 2024 11:12:42 +0100 Subject: [PATCH 28/58] test: skip large amounts test --- src/tests/hyeth.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/hyeth.test.ts b/src/tests/hyeth.test.ts index d0e6065..4091b50 100644 --- a/src/tests/hyeth.test.ts +++ b/src/tests/hyeth.test.ts @@ -77,7 +77,7 @@ describe('hyETH', () => { await factory.executeTx() }) - test('can mint with ETH (large amout)', async () => { + test.skip('can mint with ETH (large amout)', async () => { await factory.fetchQuote({ isMinting: true, inputToken: eth, From 9992167401ee2afe9e769dfbfe049eb7954a8d1d Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 20 Sep 2024 14:40:39 +0100 Subject: [PATCH 29/58] feat: build component swap data for input token usdc --- src/utils/component-swap-data.test.ts | 71 ++------- src/utils/component-swap-data.ts | 205 +++++++++++++------------- 2 files changed, 119 insertions(+), 157 deletions(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 6130929..164ee6b 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { AddressZero } from 'constants/addresses' import { USDC, USDCY } from 'constants/tokens' import { getIssuanceComponentSwapData, @@ -7,13 +8,13 @@ import { import { wei } from 'utils/numbers' import { LocalhostProviderUrl } from 'tests/utils' import { isSameAddress } from 'utils/addresses' +import { Exchange } from 'utils/swap-data' const rpcUrl = LocalhostProviderUrl const indexTokenSymbol = USDCY.symbol const indexToken = USDCY.address! const usdc = USDC.address! -// const zeroAddress = '0x0000000000000000000000000000000000000000' describe('getIssuanceComponentSwapData()', () => { test('returns correct swap data based on input token USDC', async () => { @@ -32,31 +33,14 @@ describe('getIssuanceComponentSwapData()', () => { expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( true ) + // Should be empty as input token is equal to output token (underlying erc20) + const dexData = componentSwapData[i].dexData + expect(dexData.exchange).toEqual(Exchange.None) + expect(dexData.fees).toEqual([]) + expect(dexData.path).toEqual([]) + expect(dexData.pool).toEqual(AddressZero) } - // // Should be empty as input token is equal to output token - // expect(componentSwapData[0].dexData.exchange).toEqual(Exchange.None) - // expect(componentSwapData[0].dexData.path).toEqual([ - // zeroAddress, - // zeroAddress, - // ]) - // expect(componentSwapData[1].dexData.path).toEqual([inputToken, dai]) - // expect(componentSwapData[2].dexData.path).toEqual([inputToken, usdt]) - // expect(componentSwapData[3].dexData.path).toEqual([inputToken, usdt]) - // expect(componentSwapData[4].dexData.path).toEqual([inputToken, dai]) - // // Should be empty as input token is equal to output token - // expect(componentSwapData[5].dexData.exchange).toEqual(Exchange.None) - // expect(componentSwapData[5].dexData.path).toEqual([ - // zeroAddress, - // zeroAddress, - // ]) - // componentSwapData.forEach((swapData, index) => { - // expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) - // if (index > 0 && index < 5) { - // expect(swapData.dexData.exchange).toEqual(Exchange.UniV3) - // expect(swapData.dexData.fees.length).toBeGreaterThan(0) - // } - // expect(swapData.dexData.pool).toBe(zeroAddress) - // }) + // fIXME: test for buy amounts }) // test('returns correct swap data based when input token is WETH', async () => { @@ -109,37 +93,14 @@ describe('getRedemptionComponentSwapData()', () => { expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( true ) + // Should be empty as input token is equal to output token (underlying erc20) + const dexData = componentSwapData[i].dexData + expect(dexData.exchange).toEqual(Exchange.None) + expect(dexData.fees).toEqual([]) + expect(dexData.path).toEqual([]) + expect(dexData.pool).toEqual(AddressZero) } - // expect(componentSwapData.length).toBe(6) - // expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) - // expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) - // expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) - // expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) - // expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) - // expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) - // // Should be empty as input token is equal to output token - // expect(componentSwapData[0].dexData.exchange).toEqual(Exchange.None) - // expect(componentSwapData[0].dexData.path).toEqual([ - // zeroAddress, - // zeroAddress, - // ]) - // expect(componentSwapData[1].dexData.path).toEqual([dai, usdc]) - // expect(componentSwapData[2].dexData.path).toEqual([usdt, usdc]) - // expect(componentSwapData[3].dexData.path).toEqual([usdt, usdc]) - // expect(componentSwapData[4].dexData.path).toEqual([dai, usdc]) - // // Should be empty as input token is equal to output token - // expect(componentSwapData[5].dexData.exchange).toEqual(Exchange.None) - // expect(componentSwapData[5].dexData.path).toEqual([ - // zeroAddress, - // zeroAddress, - // ]) - // componentSwapData.forEach((swapData, index) => { - // expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) - // if (index > 0 && index < 5) { - // expect(swapData.dexData.exchange).toEqual(Exchange.UniV3) - // expect(swapData.dexData.fees.length).toBeGreaterThan(0) - // } - // expect(swapData.dexData.pool).toBe(zeroAddress) + // fIXME: test for buy amounts }) }) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 4f5962a..44ce7cf 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -3,24 +3,20 @@ import { Contract } from '@ethersproject/contracts' import { Address, createPublicClient, http, parseAbi } from 'viem' import { mainnet } from 'viem/chains' +import { AddressZero } from 'constants/addresses' import { USDC } from 'constants/tokens' +import { isSameAddress } from 'utils/addresses' import { getIssuanceModule } from 'utils/issuanceModules' import { getRpcProvider } from 'utils/rpc-provider' import { Exchange, SwapData } from 'utils/swap-data' -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -const usdc = USDC.address! -/* eslint-enable @typescript-eslint/no-non-null-assertion */ // const DEFAULT_SLIPPAGE = 0.0015 const emptySwapData: SwapData = { exchange: Exchange.None, - path: [ - '0x0000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000', - ], + path: [], fees: [], - pool: '0x0000000000000000000000000000000000000000', + pool: AddressZero, } // FIXME: @@ -139,24 +135,11 @@ interface IssuanceRequest extends ComponentSwapDataRequest { inputToken: string } -function getIssuanceContract( - indexTokenSymbol: string, - rpcUrl: string -): Contract { - const abi = [ - 'function getRequiredComponentIssuanceUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', - 'function getRequiredComponentRedemptionUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', - ] - const provider = getRpcProvider(rpcUrl) - const issuanceModule = getIssuanceModule(indexTokenSymbol) - return new Contract(issuanceModule.address, abi, provider) -} - export async function getIssuanceComponentSwapData( request: IssuanceRequest, rpcUrl: string ): Promise { - const { indexTokenSymbol, indexToken, indexTokenAmount } = request + const { indexTokenSymbol, indexToken, indexTokenAmount, inputToken } = request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) const [issuanceComponents] = await issuance.getRequiredComponentIssuanceUnits( indexToken, @@ -170,44 +153,42 @@ export async function getIssuanceComponentSwapData( // getAmountOfAssetToObtainShares(component, issuanceUnits[index], provider) // ) // const buyAmounts = await Promise.all(buyAmountsPromises) + const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) const wrappedTokens = await Promise.all(underlyingERC20sPromises) console.log(wrappedTokens) - // TODO: get swap data - // const swaps: Promise<{ swapData: SwapData } | null>[] = - // issuanceComponents.map((_: string, index: number) => { - // const wrappedToken = wrappedTokens[index] - // const underlyingERC20 = wrappedToken.underlyingErc20 - // const buyUnderlyingAmount = buyAmounts[index] - // const mintParams = { - // buyToken: underlyingERC20.address, - // buyAmount: buyUnderlyingAmount, - // sellToken: inputToken, - // includedSources: 'Uniswap_V3', - // } - // return getSwapData(mintParams, 0.1, 1, zeroExApi) - // }) - // const swapDataResults = await Promise.all(swaps) - // const swapData = issuanceComponents.map((_: string, index: number) => { - // const wrappedToken = wrappedTokens[index] - // const underlyingERC20 = wrappedToken.underlyingErc20 - // const buyUnderlyingAmount = buyAmounts[index] - // const dexData = swapDataResults[index]?.swapData ?? emptySwapData - // return { - // underlyingERC20: underlyingERC20.address, - // buyUnderlyingAmount, - // dexData, - // } - // }) - // return swapData - const componentSwapData = wrappedTokens.map(() => { - const swapData: ComponentSwapData = { - underlyingERC20: usdc, - buyUnderlyingAmount: BigNumber.from(0), - dexData: emptySwapData, - } - return swapData - }) - return componentSwapData + // TODO: get swap data; add swap quote provider + const swaps: Promise<{ swapData: SwapData } | null>[] = + issuanceComponents.map((_: string, index: number) => { + const wrappedToken = wrappedTokens[index] + const underlyingERC20 = wrappedToken.underlyingErc20 + console.log( + underlyingERC20.symbol === USDC.symbol, + underlyingERC20.symbol, + USDC.symbol + ) + console.log( + isSameAddress(underlyingERC20.address, inputToken), + underlyingERC20.address, + inputToken + ) + if (isSameAddress(underlyingERC20.address, inputToken)) return null + // const buyUnderlyingAmount = buyAmounts[index] + // const mintParams = { + // buyToken: underlyingERC20.address, + // buyAmount: buyUnderlyingAmount, + // sellToken: inputToken, + // includedSources: 'Uniswap_V3', + // } + // TODO: add swap quote provider + return null + }) + const swapData = await Promise.all(swaps) + return buildComponentSwapData( + issuanceComponents, + wrappedTokens, + buyAmounts, + swapData + ) } interface RedemptionRequest extends ComponentSwapDataRequest { @@ -218,7 +199,8 @@ export async function getRedemptionComponentSwapData( request: RedemptionRequest, rpcUrl: string ): Promise { - const { indexTokenSymbol, indexToken, indexTokenAmount } = request + const { indexTokenSymbol, indexToken, indexTokenAmount, outputToken } = + request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) const [issuanceComponents] = await issuance.getRequiredComponentRedemptionUnits( @@ -229,50 +211,68 @@ export async function getRedemptionComponentSwapData( issuanceComponents.map((component: string) => getUnderlyingErc20(component)) const wrappedTokens = await Promise.all(underlyingERC20sPromises) console.log(wrappedTokens) - // TODO: - // const buyAmountsPromises = issuanceComponents.map( - // (component: string, index: number) => - // getAmountOfAssetToObtainShares( - // component, - // issuanceUnits[index], - // provider, - // -DEFAULT_SLIPPAGE - // ) - // ) - // const buyAmounts = await Promise.all(buyAmountsPromises) - // const swaps = issuanceComponents.map((_: string, index: number) => { - // const wrappedToken = wrappedTokens[index] - // const underlyingERC20 = wrappedToken.underlyingErc20 - // const buyUnderlyingAmount = buyAmounts[index] - // const redeemParams = { - // buyToken: outputToken, - // sellAmount: buyUnderlyingAmount, - // sellToken: underlyingERC20.address, - // includedSources: 'Uniswap_V3', - // } - // return getSwapData(redeemParams, 0.1, 1, zeroExApi) - // }) - // const swapDataResults = await Promise.all(swaps) - // const swapData = issuanceComponents.map((_: string, index: number) => { - // const wrappedToken = wrappedTokens[index] - // const underlyingERC20 = wrappedToken.underlyingErc20 - // const buyUnderlyingAmount = buyAmounts[index] - // const dexData = swapDataResults[index]?.swapData ?? emptySwapData - // return { - // underlyingERC20: underlyingERC20.address, - // buyUnderlyingAmount, - // dexData, - // } - // }) - const componentSwapData = wrappedTokens.map(() => { - const swapData: ComponentSwapData = { - underlyingERC20: usdc, - buyUnderlyingAmount: BigNumber.from(0), - dexData: emptySwapData, + // TODO: check google docs + // const buyAmountsPromises = issuanceComponents.map( + // (component: string, index: number) => + // getAmountOfAssetToObtainShares( + // component, + // issuanceUnits[index], + // provider, + // -DEFAULT_SLIPPAGE + // ) + // ) + // const buyAmounts = await Promise.all(buyAmountsPromises) + const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) + const swaps = issuanceComponents.map((_: string, index: number) => { + const wrappedToken = wrappedTokens[index] + const underlyingERC20 = wrappedToken.underlyingErc20 + if (isSameAddress(underlyingERC20.address, outputToken)) return null + // const buyUnderlyingAmount = buyAmounts[index] + // const redeemParams = { + // buyToken: outputToken, + // sellAmount: buyUnderlyingAmount, + // sellToken: underlyingERC20.address, + // includedSources: 'Uniswap_V3', + // } + return null + }) + return buildComponentSwapData( + issuanceComponents, + wrappedTokens, + buyAmounts, + swaps + ) +} + +function buildComponentSwapData( + issuanceComponents: string[], + wrappedTokens: WrappedToken[], + buyAmounts: BigNumber[], + swapDataResults: any +): ComponentSwapData[] { + return issuanceComponents.map((_: string, index: number) => { + const wrappedToken = wrappedTokens[index] + const buyUnderlyingAmount = buyAmounts[index] + const dexData = swapDataResults[index]?.swapData ?? emptySwapData + return { + underlyingERC20: wrappedToken.underlyingErc20.address, + buyUnderlyingAmount, + dexData, } - return swapData }) - return componentSwapData +} + +function getIssuanceContract( + indexTokenSymbol: string, + rpcUrl: string +): Contract { + const abi = [ + 'function getRequiredComponentIssuanceUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', + 'function getRequiredComponentRedemptionUnits(address _setToken, uint256 _quantity) external view returns (address[] memory, uint256[] memory, uint256[] memory)', + ] + const provider = getRpcProvider(rpcUrl) + const issuanceModule = getIssuanceModule(indexTokenSymbol) + return new Contract(issuanceModule.address, abi, provider) } async function getUnderlyingErc20(token: string): Promise { @@ -290,7 +290,8 @@ async function getUnderlyingErc20(token: string): Promise { address: token, decimals, underlyingErc20: { - address: usdc, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + address: USDC.address!, decimals: 6, symbol: USDC.symbol, }, From 8eab9aed0ad867ec2dc9ab39985c7799b1d9e6b4 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 20 Sep 2024 15:08:29 +0100 Subject: [PATCH 30/58] feat: add swap quote provider --- src/quote/flashmint/wrapped/provider.ts | 31 +++++--- src/quote/provider/index.ts | 10 ++- src/utils/component-swap-data.test.ts | 12 ++- src/utils/component-swap-data.ts | 99 +++++++++++++------------ 4 files changed, 88 insertions(+), 64 deletions(-) diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts index f436ccc..4411998 100644 --- a/src/quote/flashmint/wrapped/provider.ts +++ b/src/quote/flashmint/wrapped/provider.ts @@ -1,5 +1,6 @@ import { BigNumber } from '@ethersproject/bignumber' +import { SwapQuoteProvider } from 'quote/swap' import { ComponentSwapData, ComponentWrapData, @@ -14,6 +15,7 @@ import { getRpcProvider } from 'utils/rpc-provider' import { QuoteProvider, QuoteToken } from '../../interfaces' export interface FlashMintWrappedQuoteRequest { + chainId: number isMinting: boolean inputToken: QuoteToken outputToken: QuoteToken @@ -31,42 +33,47 @@ export interface FlashMintWrappedQuote { export class WrappedQuoteProvider implements QuoteProvider { - constructor(private readonly rpcUrl: string) {} + constructor( + private readonly rpcUrl: string, + private readonly swapQuoteProvider: SwapQuoteProvider + ) {} async getQuote( request: FlashMintWrappedQuoteRequest ): Promise { - const { inputToken, indexTokenAmount, isMinting, outputToken, slippage } = - request - console.log( - inputToken.symbol, - outputToken.symbol, - indexTokenAmount.toString(), + const { + chainId, + inputToken, + indexTokenAmount, isMinting, - slippage - ) + outputToken, + slippage, + } = request const indexToken = isMinting ? outputToken : inputToken const indexTokenSymbol = indexToken.symbol const componentSwapData = isMinting ? await getIssuanceComponentSwapData( { + chainId, indexTokenSymbol, indexToken: indexToken.address, inputToken: inputToken.address, indexTokenAmount, }, - this.rpcUrl + this.rpcUrl, + this.swapQuoteProvider ) : await getRedemptionComponentSwapData( { + chainId, indexTokenSymbol, indexToken: indexToken.address, outputToken: outputToken.address, indexTokenAmount, }, - this.rpcUrl + this.rpcUrl, + this.swapQuoteProvider ) - console.log(componentSwapData) const componentWrapData = getWrapData(indexToken.symbol) // FIXME: add check // if (componentSwapData.length !== componentWrapData.length) return null diff --git a/src/quote/provider/index.ts b/src/quote/provider/index.ts index d001a19..31f8991 100644 --- a/src/quote/provider/index.ts +++ b/src/quote/provider/index.ts @@ -186,8 +186,14 @@ export class FlashMintQuoteProvider ) } case FlashMintContractType.wrapped: { - const wrappedQuoteProvider = new WrappedQuoteProvider(rpcUrl) - const wrappedQuote = await wrappedQuoteProvider.getQuote(request) + const wrappedQuoteProvider = new WrappedQuoteProvider( + rpcUrl, + swapQuoteProvider + ) + const wrappedQuote = await wrappedQuoteProvider.getQuote({ + ...request, + chainId, + }) if (!wrappedQuote) return null const builder = new WrappedTransactionBuilder(rpcUrl) const txRequest: FlashMintWrappedBuildRequest = { diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 164ee6b..6268335 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -6,11 +6,13 @@ import { getRedemptionComponentSwapData, } from 'utils/component-swap-data' import { wei } from 'utils/numbers' -import { LocalhostProviderUrl } from 'tests/utils' +import { IndexZeroExSwapQuoteProvider, LocalhostProviderUrl } from 'tests/utils' import { isSameAddress } from 'utils/addresses' import { Exchange } from 'utils/swap-data' +const chainId = 1 const rpcUrl = LocalhostProviderUrl +const swapQuoteProvider = IndexZeroExSwapQuoteProvider const indexTokenSymbol = USDCY.symbol const indexToken = USDCY.address! @@ -20,12 +22,14 @@ describe('getIssuanceComponentSwapData()', () => { test('returns correct swap data based on input token USDC', async () => { const componentSwapData = await getIssuanceComponentSwapData( { + chainId, indexTokenSymbol, indexToken, inputToken: usdc, indexTokenAmount: wei(1), }, - rpcUrl + rpcUrl, + swapQuoteProvider ) // FIXME: should be 5 for USDCY expect(componentSwapData.length).toBe(6) @@ -80,12 +84,14 @@ describe('getRedemptionComponentSwapData()', () => { test('returns correct swap data based for output token USDC', async () => { const componentSwapData = await getRedemptionComponentSwapData( { + chainId, indexTokenSymbol, indexToken, outputToken: usdc, indexTokenAmount: wei(1), }, - rpcUrl + rpcUrl, + swapQuoteProvider ) // FIXME: should be 5 for USDCY expect(componentSwapData.length).toBe(6) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 44ce7cf..763c7bc 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -5,6 +5,7 @@ import { mainnet } from 'viem/chains' import { AddressZero } from 'constants/addresses' import { USDC } from 'constants/tokens' +import { SwapQuote, SwapQuoteProvider } from 'quote' import { isSameAddress } from 'utils/addresses' import { getIssuanceModule } from 'utils/issuanceModules' import { getRpcProvider } from 'utils/rpc-provider' @@ -32,6 +33,7 @@ export interface ComponentSwapData { } interface ComponentSwapDataRequest { + chainId: number indexTokenSymbol: string indexToken: string indexTokenAmount: BigNumber @@ -137,9 +139,16 @@ interface IssuanceRequest extends ComponentSwapDataRequest { export async function getIssuanceComponentSwapData( request: IssuanceRequest, - rpcUrl: string + rpcUrl: string, + swapQuoteProvider: SwapQuoteProvider ): Promise { - const { indexTokenSymbol, indexToken, indexTokenAmount, inputToken } = request + const { + chainId, + indexTokenSymbol, + indexToken, + indexTokenAmount, + inputToken, + } = request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) const [issuanceComponents] = await issuance.getRequiredComponentIssuanceUnits( indexToken, @@ -147,6 +156,7 @@ export async function getIssuanceComponentSwapData( ) const underlyingERC20sPromises: Promise[] = issuanceComponents.map((component: string) => getUnderlyingErc20(component)) + const wrappedTokens = await Promise.all(underlyingERC20sPromises) // TODO: // const buyAmountsPromises = issuanceComponents.map( // (component: string, index: number) => @@ -154,35 +164,22 @@ export async function getIssuanceComponentSwapData( // ) // const buyAmounts = await Promise.all(buyAmountsPromises) const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) - const wrappedTokens = await Promise.all(underlyingERC20sPromises) - console.log(wrappedTokens) - // TODO: get swap data; add swap quote provider - const swaps: Promise<{ swapData: SwapData } | null>[] = - issuanceComponents.map((_: string, index: number) => { + const swapPromises: Promise[] = issuanceComponents.map( + (_: string, index: number) => { const wrappedToken = wrappedTokens[index] const underlyingERC20 = wrappedToken.underlyingErc20 - console.log( - underlyingERC20.symbol === USDC.symbol, - underlyingERC20.symbol, - USDC.symbol - ) - console.log( - isSameAddress(underlyingERC20.address, inputToken), - underlyingERC20.address, - inputToken - ) if (isSameAddress(underlyingERC20.address, inputToken)) return null - // const buyUnderlyingAmount = buyAmounts[index] - // const mintParams = { - // buyToken: underlyingERC20.address, - // buyAmount: buyUnderlyingAmount, - // sellToken: inputToken, - // includedSources: 'Uniswap_V3', - // } - // TODO: add swap quote provider - return null - }) - const swapData = await Promise.all(swaps) + return swapQuoteProvider.getSwapQuote({ + chainId, + inputToken, + outputToken: underlyingERC20.address, + outputAmount: buyAmounts[index].toString(), + // TODO: needed for USDCY? + // includedSources: 'Uniswap_V3', + }) + } + ) + const swapData = await Promise.all(swapPromises) return buildComponentSwapData( issuanceComponents, wrappedTokens, @@ -197,10 +194,16 @@ interface RedemptionRequest extends ComponentSwapDataRequest { export async function getRedemptionComponentSwapData( request: RedemptionRequest, - rpcUrl: string + rpcUrl: string, + swapQuoteProvider: SwapQuoteProvider ): Promise { - const { indexTokenSymbol, indexToken, indexTokenAmount, outputToken } = - request + const { + chainId, + indexTokenSymbol, + indexToken, + indexTokenAmount, + outputToken, + } = request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) const [issuanceComponents] = await issuance.getRequiredComponentRedemptionUnits( @@ -210,7 +213,6 @@ export async function getRedemptionComponentSwapData( const underlyingERC20sPromises: Promise[] = issuanceComponents.map((component: string) => getUnderlyingErc20(component)) const wrappedTokens = await Promise.all(underlyingERC20sPromises) - console.log(wrappedTokens) // TODO: check google docs // const buyAmountsPromises = issuanceComponents.map( // (component: string, index: number) => @@ -223,24 +225,27 @@ export async function getRedemptionComponentSwapData( // ) // const buyAmounts = await Promise.all(buyAmountsPromises) const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) - const swaps = issuanceComponents.map((_: string, index: number) => { - const wrappedToken = wrappedTokens[index] - const underlyingERC20 = wrappedToken.underlyingErc20 - if (isSameAddress(underlyingERC20.address, outputToken)) return null - // const buyUnderlyingAmount = buyAmounts[index] - // const redeemParams = { - // buyToken: outputToken, - // sellAmount: buyUnderlyingAmount, - // sellToken: underlyingERC20.address, - // includedSources: 'Uniswap_V3', - // } - return null - }) + const swapPromises: Promise[] = issuanceComponents.map( + (_: string, index: number) => { + const wrappedToken = wrappedTokens[index] + const underlyingERC20 = wrappedToken.underlyingErc20 + if (isSameAddress(underlyingERC20.address, outputToken)) return null + return swapQuoteProvider.getSwapQuote({ + chainId, + inputToken: underlyingERC20.address, + inputAmount: buyAmounts[index].toString(), + outputToken, + // TODO: needed for USDCY? + // includedSources: 'Uniswap_V3', + }) + } + ) + const swapData = await Promise.all(swapPromises) return buildComponentSwapData( issuanceComponents, wrappedTokens, buyAmounts, - swaps + swapData ) } @@ -248,7 +253,7 @@ function buildComponentSwapData( issuanceComponents: string[], wrappedTokens: WrappedToken[], buyAmounts: BigNumber[], - swapDataResults: any + swapDataResults: (SwapQuote | null)[] ): ComponentSwapData[] { return issuanceComponents.map((_: string, index: number) => { const wrappedToken = wrappedTokens[index] From a0ca6790375fc41b9da261ae716871e07ae4619b Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 20 Sep 2024 15:11:06 +0100 Subject: [PATCH 31/58] test: update flash mint wrapped tests to use chainId and swap quote provider --- src/quote/flashmint/wrapped/provider.test.ts | 23 +++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/quote/flashmint/wrapped/provider.test.ts b/src/quote/flashmint/wrapped/provider.test.ts index c2eb34c..68dd84d 100644 --- a/src/quote/flashmint/wrapped/provider.test.ts +++ b/src/quote/flashmint/wrapped/provider.test.ts @@ -1,22 +1,29 @@ -import { LocalhostProviderUrl, QuoteTokens } from 'tests/utils' +import { + IndexZeroExSwapQuoteProvider, + LocalhostProviderUrl, + QuoteTokens, +} from 'tests/utils' import { wei } from 'utils/numbers' import { FlashMintWrappedQuoteRequest, WrappedQuoteProvider } from '.' const { usdc, usdcy, weth } = QuoteTokens const indexToken = usdcy +const chainId = 1 const provider = LocalhostProviderUrl +const swapQuoteProvider = IndexZeroExSwapQuoteProvider describe('WrappedQuoteProvider()', () => { test.only('returns a quote for minting USDCY', async () => { const inputToken = usdc const request: FlashMintWrappedQuoteRequest = { + chainId, isMinting: true, inputToken, outputToken: indexToken, indexTokenAmount: wei(1), slippage: 0.5, } - const quoteProvider = new WrappedQuoteProvider(provider) + const quoteProvider = new WrappedQuoteProvider(provider, swapQuoteProvider) const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) @@ -29,13 +36,14 @@ describe('WrappedQuoteProvider()', () => { test('returns a quote for minting MMI w/ USDC', async () => { const inputToken = usdc const request: FlashMintWrappedQuoteRequest = { + chainId, isMinting: true, inputToken, outputToken: indexToken, indexTokenAmount: wei(1), slippage: 0.5, } - const quoteProvider = new WrappedQuoteProvider(provider) + const quoteProvider = new WrappedQuoteProvider(provider, swapQuoteProvider) const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) @@ -46,13 +54,14 @@ describe('WrappedQuoteProvider()', () => { test('returns a quote for minting MMI w/ WETH', async () => { const inputToken = weth const request: FlashMintWrappedQuoteRequest = { + chainId, isMinting: true, inputToken, outputToken: indexToken, indexTokenAmount: wei(1), slippage: 0.5, } - const quoteProvider = new WrappedQuoteProvider(provider) + const quoteProvider = new WrappedQuoteProvider(provider, swapQuoteProvider) const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) @@ -63,13 +72,14 @@ describe('WrappedQuoteProvider()', () => { test.only('returns a quote redeeming USDCY for USDC', async () => { const outputToken = usdc const request: FlashMintWrappedQuoteRequest = { + chainId, isMinting: false, inputToken: indexToken, outputToken, indexTokenAmount: wei(1), slippage: 0.5, } - const quoteProvider = new WrappedQuoteProvider(provider) + const quoteProvider = new WrappedQuoteProvider(provider, swapQuoteProvider) const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) @@ -82,13 +92,14 @@ describe('WrappedQuoteProvider()', () => { test('returns a quote for redeeming MMI for WETH', async () => { const outputToken = weth const request: FlashMintWrappedQuoteRequest = { + chainId, isMinting: false, inputToken: indexToken, outputToken, indexTokenAmount: wei(1), slippage: 0.5, } - const quoteProvider = new WrappedQuoteProvider(provider) + const quoteProvider = new WrappedQuoteProvider(provider, swapQuoteProvider) const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) From 9eaeaeb6f121ed8825c23aa8d22d84446521c028 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 20 Sep 2024 15:17:11 +0100 Subject: [PATCH 32/58] feat: add create client util --- src/utils/clients.ts | 12 ++++++++++++ src/utils/component-swap-data.ts | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 src/utils/clients.ts diff --git a/src/utils/clients.ts b/src/utils/clients.ts new file mode 100644 index 0000000..00b0201 --- /dev/null +++ b/src/utils/clients.ts @@ -0,0 +1,12 @@ +import { createPublicClient, http } from 'viem' +import { mainnet } from 'viem/chains' + +import { ChainId } from 'constants/chains' + +export function createClient(chainId: number) { + if (chainId !== ChainId.Mainnet) return null + return createPublicClient({ + chain: mainnet, + transport: http(process.env.MAINNET_ALCHEMY_API), + }) +} diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 763c7bc..e41a042 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -7,6 +7,7 @@ import { AddressZero } from 'constants/addresses' import { USDC } from 'constants/tokens' import { SwapQuote, SwapQuoteProvider } from 'quote' import { isSameAddress } from 'utils/addresses' +import { createClient } from 'utils/clients' import { getIssuanceModule } from 'utils/issuanceModules' import { getRpcProvider } from 'utils/rpc-provider' import { Exchange, SwapData } from 'utils/swap-data' @@ -155,7 +156,9 @@ export async function getIssuanceComponentSwapData( indexTokenAmount ) const underlyingERC20sPromises: Promise[] = - issuanceComponents.map((component: string) => getUnderlyingErc20(component)) + issuanceComponents.map((component: string) => + getUnderlyingErc20(component, chainId) + ) const wrappedTokens = await Promise.all(underlyingERC20sPromises) // TODO: // const buyAmountsPromises = issuanceComponents.map( @@ -211,7 +214,9 @@ export async function getRedemptionComponentSwapData( indexTokenAmount ) const underlyingERC20sPromises: Promise[] = - issuanceComponents.map((component: string) => getUnderlyingErc20(component)) + issuanceComponents.map((component: string) => + getUnderlyingErc20(component, chainId) + ) const wrappedTokens = await Promise.all(underlyingERC20sPromises) // TODO: check google docs // const buyAmountsPromises = issuanceComponents.map( @@ -280,12 +285,11 @@ function getIssuanceContract( return new Contract(issuanceModule.address, abi, provider) } -async function getUnderlyingErc20(token: string): Promise { - // FIXME: pass in? or config externally? - const publicClient = createPublicClient({ - chain: mainnet, - transport: http(process.env.MAINNET_ALCHEMY_API), - }) +async function getUnderlyingErc20( + token: string, + chainId: number +): Promise { + const publicClient = createClient(chainId)! const decimals: number = await publicClient.readContract({ address: token as Address, abi: parseAbi(['function decimals() view returns (uint8)']), From 7d89343aeb6652328f26ec5e49088d22807ad2e9 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 20 Sep 2024 15:42:00 +0100 Subject: [PATCH 33/58] lint --- src/utils/component-swap-data.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index e41a042..3329d31 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -1,7 +1,6 @@ import { BigNumber } from '@ethersproject/bignumber' import { Contract } from '@ethersproject/contracts' -import { Address, createPublicClient, http, parseAbi } from 'viem' -import { mainnet } from 'viem/chains' +import { Address, parseAbi } from 'viem' import { AddressZero } from 'constants/addresses' import { USDC } from 'constants/tokens' @@ -289,6 +288,7 @@ async function getUnderlyingErc20( token: string, chainId: number ): Promise { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const publicClient = createClient(chainId)! const decimals: number = await publicClient.readContract({ address: token as Address, From 606e319e1c31ab7cbff49598983a8a94ba356596 Mon Sep 17 00:00:00 2001 From: JD Date: Tue, 24 Sep 2024 16:20:25 +0100 Subject: [PATCH 34/58] feat: add amounts for erc4262 --- src/utils/component-swap-data.ts | 195 +++++++++++++------------------ 1 file changed, 83 insertions(+), 112 deletions(-) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 3329d31..37c54a4 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -49,90 +49,6 @@ interface WrappedToken { } } -// const erc4626Abi = [ -// 'constructor(address _morpho, address _morphoToken, address _lens, address _recipient)', -// 'error ZeroAddress()', -// 'event Accrued(address indexed user, uint256 index, uint256 unclaimed)', -// 'event Approval(address indexed owner, address indexed spender, uint256 value)', -// 'event Claimed(address indexed user, uint256 claimed)', -// 'event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares)', -// 'event Initialized(uint8 version)', -// 'event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)', -// 'event RewardsTransferred(address recipient, uint256 amount)', -// 'event Transfer(address indexed from, address indexed to, uint256 value)', -// 'event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)', -// 'function allowance(address owner, address spender) view returns (uint256)', -// 'function approve(address spender, uint256 amount) returns (bool)', -// 'function asset() view returns (address)', -// 'function balanceOf(address account) view returns (uint256)', -// 'function claimRewards(address _user) returns (uint256 rewardsAmount)', -// 'function comp() view returns (address)', -// 'function convertToAssets(uint256 shares) view returns (uint256 assets)', -// 'function convertToShares(uint256 assets) view returns (uint256 shares)', -// 'function decimals() view returns (uint8)', -// 'function decreaseAllowance(address spender, uint256 subtractedValue) returns (bool)', -// 'function deposit(uint256 assets, address receiver) returns (uint256)', -// 'function increaseAllowance(address spender, uint256 addedValue) returns (bool)', -// 'function initialize(address _poolToken, string _name, string _symbol, uint256 _initialDeposit)', -// 'function lens() view returns (address)', -// 'function maxDeposit(address) view returns (uint256)', -// 'function maxMint(address) view returns (uint256)', -// 'function maxRedeem(address owner) view returns (uint256)', -// 'function maxWithdraw(address owner) view returns (uint256)', -// 'function mint(uint256 shares, address receiver) returns (uint256)', -// 'function morpho() view returns (address)', -// 'function morphoToken() view returns (address)', -// 'function name() view returns (string)', -// 'function owner() view returns (address)', -// 'function poolToken() view returns (address)', -// 'function previewDeposit(uint256 assets) view returns (uint256)', -// 'function previewMint(uint256 shares) view returns (uint256)', -// 'function previewRedeem(uint256 shares) view returns (uint256)', -// 'function previewWithdraw(uint256 assets) view returns (uint256)', -// 'function recipient() view returns (address)', -// 'function redeem(uint256 shares, address receiver, address owner) returns (uint256)', -// 'function renounceOwnership()', -// 'function rewardsIndex() view returns (uint256)', -// 'function symbol() view returns (string)', -// 'function totalAssets() view returns (uint256)', -// 'function totalSupply() view returns (uint256)', -// 'function transfer(address to, uint256 amount) returns (bool)', -// 'function transferFrom(address from, address to, uint256 amount) returns (bool)', -// 'function transferOwnership(address newOwner)', -// 'function transferRewards()', -// 'function userRewards(address) view returns (uint128 index, uint128 unclaimed)', -// 'function wEth() view returns (address)', -// 'function withdraw(uint256 assets, address receiver, address owner) returns (uint256)', -// ] - -// const isFCASH = (address: string) => -// [ -// '0x278039398A5eb29b6c2FB43789a38A84C6085266', -// '0xe09B1968851478f20a43959d8a212051367dF01A', -// ].includes(address) - -// const getAmountOfAssetToObtainShares = async () => -// component: string, -// shares: BigNumber, -// provider: JsonRpcProvider -// slippage = DEFAULT_SLIPPAGE // 1 = 1% -// { -// const componentContract = new Contract(component, erc4626Abi, provider) -// // Convert slippage to a BigNumber, do rounding to avoid weird JS precision loss -// const defaultSlippageBN = BigNumber.from(Math.round(slippage * 10000)) -// // if FCASH, increase slippage x3 -// const slippageBigNumber = isFCASH(component) -// ? defaultSlippageBN.mul(3) -// : defaultSlippageBN - -// // Calculate the multiplier (1 + slippage) -// const multiplier = BigNumber.from(10000).add(slippageBigNumber) - -// const buyUnderlyingAmount: BigNumber = -// await componentContract.convertToAssets(shares) -// return buyUnderlyingAmount.mul(multiplier).div(10000) -// } - interface IssuanceRequest extends ComponentSwapDataRequest { inputToken: string } @@ -150,32 +66,41 @@ export async function getIssuanceComponentSwapData( inputToken, } = request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) - const [issuanceComponents] = await issuance.getRequiredComponentIssuanceUnits( - indexToken, - indexTokenAmount - ) + const [issuanceComponents, issuanceUnits] = + await issuance.getRequiredComponentIssuanceUnits( + indexToken, + indexTokenAmount + ) const underlyingERC20sPromises: Promise[] = issuanceComponents.map((component: string) => getUnderlyingErc20(component, chainId) ) + const amountPromises = issuanceComponents.map( + (component: Address, index: number) => + getAmount(component, issuanceUnits[index], chainId) + ) const wrappedTokens = await Promise.all(underlyingERC20sPromises) - // TODO: - // const buyAmountsPromises = issuanceComponents.map( - // (component: string, index: number) => - // getAmountOfAssetToObtainShares(component, issuanceUnits[index], provider) - // ) - // const buyAmounts = await Promise.all(buyAmountsPromises) - const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) + const amounts = await Promise.all(amountPromises) const swapPromises: Promise[] = issuanceComponents.map( (_: string, index: number) => { const wrappedToken = wrappedTokens[index] const underlyingERC20 = wrappedToken.underlyingErc20 + console.log( + underlyingERC20.symbol === USDC.symbol, + underlyingERC20.symbol, + USDC.symbol + ) + console.log( + isSameAddress(underlyingERC20.address, inputToken), + underlyingERC20.address, + inputToken + ) if (isSameAddress(underlyingERC20.address, inputToken)) return null return swapQuoteProvider.getSwapQuote({ chainId, inputToken, outputToken: underlyingERC20.address, - outputAmount: buyAmounts[index].toString(), + outputAmount: amounts[index].toString(), // TODO: needed for USDCY? // includedSources: 'Uniswap_V3', }) @@ -185,7 +110,7 @@ export async function getIssuanceComponentSwapData( return buildComponentSwapData( issuanceComponents, wrappedTokens, - buyAmounts, + amounts, swapData ) } @@ -207,7 +132,7 @@ export async function getRedemptionComponentSwapData( outputToken, } = request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) - const [issuanceComponents] = + const [issuanceComponents, issuanceUnits] = await issuance.getRequiredComponentRedemptionUnits( indexToken, indexTokenAmount @@ -216,19 +141,13 @@ export async function getRedemptionComponentSwapData( issuanceComponents.map((component: string) => getUnderlyingErc20(component, chainId) ) + const amountPromises = issuanceComponents.map( + (component: Address, index: number) => + getAmount(component, issuanceUnits[index], chainId) + ) const wrappedTokens = await Promise.all(underlyingERC20sPromises) - // TODO: check google docs - // const buyAmountsPromises = issuanceComponents.map( - // (component: string, index: number) => - // getAmountOfAssetToObtainShares( - // component, - // issuanceUnits[index], - // provider, - // -DEFAULT_SLIPPAGE - // ) - // ) - // const buyAmounts = await Promise.all(buyAmountsPromises) - const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) + const amounts = await Promise.all(amountPromises) + console.log(wrappedTokens) const swapPromises: Promise[] = issuanceComponents.map( (_: string, index: number) => { const wrappedToken = wrappedTokens[index] @@ -237,7 +156,7 @@ export async function getRedemptionComponentSwapData( return swapQuoteProvider.getSwapQuote({ chainId, inputToken: underlyingERC20.address, - inputAmount: buyAmounts[index].toString(), + inputAmount: amounts[index].toString(), outputToken, // TODO: needed for USDCY? // includedSources: 'Uniswap_V3', @@ -248,7 +167,7 @@ export async function getRedemptionComponentSwapData( return buildComponentSwapData( issuanceComponents, wrappedTokens, - buyAmounts, + amounts, swapData ) } @@ -271,6 +190,58 @@ function buildComponentSwapData( }) } +async function getAmount( + component: Address, + issuanceUnits: bigint, + chainId: number +): Promise { + try { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const publicClient = createClient(chainId)! + const erc4626Abi = [ + 'function convertToAssets(uint256 shares) view returns (uint256 assets)', + 'function previewDeposit(uint256 assets) view returns (uint256)', + 'function previewMint(uint256 shares) view returns (uint256)', + 'function previewRedeem(uint256 shares) view returns (uint256)', + 'function previewWithdraw(uint256 assets) view returns (uint256)', + ] + const assets: bigint = (await publicClient.readContract({ + address: component as Address, + abi: erc4626Abi, + functionName: 'convertToAssets', + args: [issuanceUnits], + })) as bigint + return assets + } catch { + // TODO: apply slippage to issuance units amount (for all none erc4262) + return BigInt(0) + } + // const isFCASH = (address: string) => + // [ + // '0x278039398A5eb29b6c2FB43789a38A84C6085266', + // '0xe09B1968851478f20a43959d8a212051367dF01A', + // ].includes(address) + // const getAmountOfAssetToObtainShares = async () => + // component: string, + // shares: BigNumber, + // provider: JsonRpcProvider + // slippage = DEFAULT_SLIPPAGE // 1 = 1% + // { + // const componentContract = new Contract(component, erc4626Abi, provider) + // // Convert slippage to a BigNumber, do rounding to avoid weird JS precision loss + // const defaultSlippageBN = BigNumber.from(Math.round(slippage * 10000)) + // // if FCASH, increase slippage x3 + // const slippageBigNumber = isFCASH(component) + // ? defaultSlippageBN.mul(3) + // : defaultSlippageBN + // // Calculate the multiplier (1 + slippage) + // const multiplier = BigNumber.from(10000).add(slippageBigNumber) + // const buyUnderlyingAmount: BigNumber = + // await componentContract.convertToAssets(shares) + // return buyUnderlyingAmount.mul(multiplier).div(10000) + // } +} + function getIssuanceContract( indexTokenSymbol: string, rpcUrl: string From 2fc339ad65c743d2775a60d33751ba712b41f202 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 30 Sep 2024 11:16:26 +0100 Subject: [PATCH 35/58] build: upgrade tokenlists w/ updated token info --- package-lock.json | 14 +++++++------- package.json | 2 +- src/constants/tokens.ts | 6 ++---- src/flashmint/builders/wrapped.test.ts | 12 ++++++------ src/quote/flashmint/wrapped/provider.test.ts | 14 +++++++------- src/quote/provider/index.test.ts | 6 +++--- src/quote/provider/utils.test.ts | 15 +++++++++------ src/quote/provider/utils.ts | 4 ++-- src/tests/utils/quoteTokens.ts | 16 ++++++++-------- src/utils/component-swap-data.test.ts | 6 +++--- src/utils/issuanceModules.test.ts | 6 +++--- src/utils/issuanceModules.ts | 4 ++-- src/utils/wrap-data.test.ts | 4 ++-- src/utils/wrap-data.ts | 4 ++-- 14 files changed, 57 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9eab7d..9acbcdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@ethersproject/contracts": "^5.6.2", "@ethersproject/providers": "^5.6.8", "@ethersproject/units": "^5.6.1", - "@indexcoop/tokenlists": "^1.56.0", + "@indexcoop/tokenlists": "^1.57.0", "@lifi/sdk": "3.0.0-beta.1", "@uniswap/sdk-core": "^5.3.1", "@uniswap/v3-sdk": "^3.13.1", @@ -3097,9 +3097,9 @@ "dev": true }, "node_modules/@indexcoop/tokenlists": { - "version": "1.56.0", - "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.56.0.tgz", - "integrity": "sha512-d7nb5ULnbVm3rxYe88nol89kBggOytw11SkbEzFcVwjZ2IbcpGq6n//TEGAsRcCEMvhQ7cln1BEZhc+xp9s9aA==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.57.0.tgz", + "integrity": "sha512-K+1wVUk/0CVx97qN+xg8PC/S2/a+F+TDUPjKnXcsixYbKFMykhgndCvYYP0opGB/F+yQWobbhmK8RdjjCCVGRA==", "engines": { "node": ">=16" }, @@ -20104,9 +20104,9 @@ "dev": true }, "@indexcoop/tokenlists": { - "version": "1.56.0", - "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.56.0.tgz", - "integrity": "sha512-d7nb5ULnbVm3rxYe88nol89kBggOytw11SkbEzFcVwjZ2IbcpGq6n//TEGAsRcCEMvhQ7cln1BEZhc+xp9s9aA==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@indexcoop/tokenlists/-/tokenlists-1.57.0.tgz", + "integrity": "sha512-K+1wVUk/0CVx97qN+xg8PC/S2/a+F+TDUPjKnXcsixYbKFMykhgndCvYYP0opGB/F+yQWobbhmK8RdjjCCVGRA==", "requires": {} }, "@istanbuljs/load-nyc-config": { diff --git a/package.json b/package.json index 77e3e5e..ee82e9d 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "@ethersproject/contracts": "^5.6.2", "@ethersproject/providers": "^5.6.8", "@ethersproject/units": "^5.6.1", - "@indexcoop/tokenlists": "^1.56.0", + "@indexcoop/tokenlists": "^1.57.0", "@lifi/sdk": "3.0.0-beta.1", "@uniswap/sdk-core": "^5.3.1", "@uniswap/v3-sdk": "^3.13.1", diff --git a/src/constants/tokens.ts b/src/constants/tokens.ts index b563ce5..bc5b567 100644 --- a/src/constants/tokens.ts +++ b/src/constants/tokens.ts @@ -103,10 +103,8 @@ export const RealWorldAssetIndex: Token = { symbol: 'RWA', } -export const USDCY: Token = { - ...getIndexTokenData('USDCY', 1)!, - // FIXME: delete; just for testing - address: '0xc30FBa978743a43E736fc32FBeEd364b8A2039cD', +export const TheUSDCYieldIndex: Token = { + ...getIndexTokenData('icUSD', 1)!, } // Other diff --git a/src/flashmint/builders/wrapped.test.ts b/src/flashmint/builders/wrapped.test.ts index ca1407d..3ba9a21 100644 --- a/src/flashmint/builders/wrapped.test.ts +++ b/src/flashmint/builders/wrapped.test.ts @@ -16,11 +16,11 @@ import { const rpcUrl = LocalhostProviderUrl const ZERO_BYTES = '0x0000000000000000000000000000000000000000' -const { usdc, usdcy } = QuoteTokens +const { icusd, usdc } = QuoteTokens const FlashMintWrappedAddress = Contracts[ChainId.Mainnet].FlashMintWrapped const eth = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' -const indexToken = usdcy.address +const indexToken = icusd.address const usdcAddress = usdc.address describe('WrappedTransactionBuilder()', () => { @@ -80,7 +80,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx).toBeNull() }) - test('returns a tx for USDCY MMI (ERC20)', async () => { + test('returns a tx for icUSD MMI (ERC20)', async () => { const buildRequest = getDefaultBuildRequest() const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) @@ -99,7 +99,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx.data).toEqual(refTx.data) }) - test('returns a tx for minting USDCY (ETH)', async () => { + test('returns a tx for minting icUSD (ETH)', async () => { const buildRequest = getDefaultBuildRequest(true, eth, 'ETH') const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) @@ -118,7 +118,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx.value).toEqual(buildRequest.inputOutputTokenAmount) }) - test('returns a tx for redeeming USDCY (ERC20)', async () => { + test('returns a tx for redeeming icUSD (ERC20)', async () => { const buildRequest = getDefaultBuildRequest(false) const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) @@ -137,7 +137,7 @@ describe('WrappedTransactionBuilder()', () => { expect(tx.data).toEqual(refTx.data) }) - test('returns a tx for redeeming USDCY (ETH)', async () => { + test('returns a tx for redeeming icUSD (ETH)', async () => { const buildRequest = getDefaultBuildRequest(false, eth, 'ETH') const provider = getRpcProvider(rpcUrl) const contract = getFlashMintWrappedContract(provider) diff --git a/src/quote/flashmint/wrapped/provider.test.ts b/src/quote/flashmint/wrapped/provider.test.ts index 68dd84d..6d0581c 100644 --- a/src/quote/flashmint/wrapped/provider.test.ts +++ b/src/quote/flashmint/wrapped/provider.test.ts @@ -6,14 +6,14 @@ import { import { wei } from 'utils/numbers' import { FlashMintWrappedQuoteRequest, WrappedQuoteProvider } from '.' -const { usdc, usdcy, weth } = QuoteTokens -const indexToken = usdcy +const { icusd, usdc, weth } = QuoteTokens +const indexToken = icusd const chainId = 1 const provider = LocalhostProviderUrl const swapQuoteProvider = IndexZeroExSwapQuoteProvider describe('WrappedQuoteProvider()', () => { - test.only('returns a quote for minting USDCY', async () => { + test.only('returns a quote for minting icUSD', async () => { const inputToken = usdc const request: FlashMintWrappedQuoteRequest = { chainId, @@ -33,7 +33,7 @@ describe('WrappedQuoteProvider()', () => { expect(quote.componentWrapData.length).toEqual(5) }) - test('returns a quote for minting MMI w/ USDC', async () => { + test('returns a quote for minting MMI w/ icUSD', async () => { const inputToken = usdc const request: FlashMintWrappedQuoteRequest = { chainId, @@ -51,7 +51,7 @@ describe('WrappedQuoteProvider()', () => { expect(quote.componentSwapData.length).toEqual(6) }) - test('returns a quote for minting MMI w/ WETH', async () => { + test('returns a quote for minting icUSD w/ WETH', async () => { const inputToken = weth const request: FlashMintWrappedQuoteRequest = { chainId, @@ -69,7 +69,7 @@ describe('WrappedQuoteProvider()', () => { expect(quote.componentSwapData.length).toEqual(6) }) - test.only('returns a quote redeeming USDCY for USDC', async () => { + test.only('returns a quote redeeming icUSD for USDC', async () => { const outputToken = usdc const request: FlashMintWrappedQuoteRequest = { chainId, @@ -89,7 +89,7 @@ describe('WrappedQuoteProvider()', () => { expect(quote.componentWrapData.length).toEqual(5) }) - test('returns a quote for redeeming MMI for WETH', async () => { + test('returns a quote for redeeming icUSD for WETH', async () => { const outputToken = weth const request: FlashMintWrappedQuoteRequest = { chainId, diff --git a/src/quote/provider/index.test.ts b/src/quote/provider/index.test.ts index f3e6257..7036bb3 100644 --- a/src/quote/provider/index.test.ts +++ b/src/quote/provider/index.test.ts @@ -24,7 +24,7 @@ const rpcUrl = LocalhostProviderUrl const provider = LocalhostProvider const zeroexSwapQuoteProvider = IndexZeroExSwapQuoteProvider -const { eth, eth2x, hyeth, iceth, usdc, usdcy } = QuoteTokens +const { eth, eth2x, hyeth, iceth, icusd, usdc } = QuoteTokens describe('FlashMintQuoteProvider()', () => { test('throws if token is unsupported', async () => { @@ -124,11 +124,11 @@ describe('FlashMintQuoteProvider()', () => { expect(quote.tx.data?.length).toBeGreaterThan(0) }) - test.skip('returns a quote for minting USDCY', async () => { + test.skip('returns a quote for minting icUSD', async () => { const request: FlashMintQuoteRequest = { isMinting: true, inputToken: usdc, - outputToken: usdcy, + outputToken: icusd, indexTokenAmount: wei(1), slippage: 0.5, } diff --git a/src/quote/provider/utils.test.ts b/src/quote/provider/utils.test.ts index 2d75e09..14ceeaf 100644 --- a/src/quote/provider/utils.test.ts +++ b/src/quote/provider/utils.test.ts @@ -15,7 +15,7 @@ import { IndexCoopInverseEthereumIndex, InterestCompoundingETHIndex, MetaverseIndex, - USDCY, + TheUSDCYieldIndex, } from 'constants/tokens' import { QuoteTokens } from 'tests/utils' import { wei } from 'utils' @@ -23,14 +23,14 @@ import { wei } from 'utils' import { FlashMintContractType, FlashMintQuoteRequest } from './' import { buildQuoteResponse, getContractType } from './utils' -const { usdc, usdcy } = QuoteTokens +const { icusd, usdc } = QuoteTokens describe('buildQuoteResponse()', () => { test('returns correct quote response object', async () => { const request: FlashMintQuoteRequest = { isMinting: true, inputToken: usdc, - outputToken: usdcy, + outputToken: icusd, indexTokenAmount: wei(1), slippage: 0.1, } @@ -52,7 +52,7 @@ describe('buildQuoteResponse()', () => { contract: Contracts[ChainId.Mainnet].FlashMintWrapped, isMinting: true, inputToken: usdc, - outputToken: usdcy, + outputToken: icusd, inputAmount: quoteAmount, outputAmount: request.indexTokenAmount, indexTokenAmount: request.indexTokenAmount, @@ -171,8 +171,11 @@ describe('getContractType()', () => { expect(contractType).toBe(FlashMintContractType.leveraged) }) - test('returns correct contract type for USDCY', async () => { - const contractType = getContractType(USDCY.symbol, ChainId.Mainnet) + test('returns correct contract type for icUSD', async () => { + const contractType = getContractType( + TheUSDCYieldIndex.symbol, + ChainId.Mainnet + ) expect(contractType).toBe(FlashMintContractType.wrapped) }) }) diff --git a/src/quote/provider/utils.ts b/src/quote/provider/utils.ts index e5c44bb..15b72d9 100644 --- a/src/quote/provider/utils.ts +++ b/src/quote/provider/utils.ts @@ -20,7 +20,7 @@ import { InterestCompoundingETHIndex, MetaverseIndex, RealWorldAssetIndex, - USDCY, + TheUSDCYieldIndex, } from 'constants/tokens' import { @@ -81,7 +81,7 @@ export function getContractType( if (token === HighYieldETHIndex.symbol) { return FlashMintContractType.hyeth } - if (token === USDCY.symbol) { + if (token === TheUSDCYieldIndex.symbol) { return FlashMintContractType.wrapped } if ( diff --git a/src/tests/utils/quoteTokens.ts b/src/tests/utils/quoteTokens.ts index 1ab8f69..2addc1c 100644 --- a/src/tests/utils/quoteTokens.ts +++ b/src/tests/utils/quoteTokens.ts @@ -11,8 +11,8 @@ import { InterestCompoundingETHIndex, MetaverseIndex, RETH, + TheUSDCYieldIndex, USDC, - USDCY, USDT, WETH, sETH2, @@ -75,6 +75,12 @@ const iceth: QuoteToken = { address: InterestCompoundingETHIndex.address!, } +const icusd: QuoteToken = { + address: TheUSDCYieldIndex.address!, + decimals: 18, + symbol: TheUSDCYieldIndex.symbol, +} + const mvi: QuoteToken = { address: MetaverseIndex.address!, decimals: 18, @@ -105,12 +111,6 @@ const usdc: QuoteToken = { symbol: USDC.symbol, } -const usdcy: QuoteToken = { - address: USDCY.address!, - decimals: 18, - symbol: USDCY.symbol, -} - const usdt: QuoteToken = { address: USDT.address!, decimals: 6, @@ -139,12 +139,12 @@ export const QuoteTokens = { gtcETH, hyeth, iceth, + icusd, mvi, reth, seth2, steth, usdc, - usdcy, usdt, weth, wseth, diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 6268335..aa6adf8 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { AddressZero } from 'constants/addresses' -import { USDC, USDCY } from 'constants/tokens' +import { TheUSDCYieldIndex, USDC } from 'constants/tokens' import { getIssuanceComponentSwapData, getRedemptionComponentSwapData, @@ -14,8 +14,8 @@ const chainId = 1 const rpcUrl = LocalhostProviderUrl const swapQuoteProvider = IndexZeroExSwapQuoteProvider -const indexTokenSymbol = USDCY.symbol -const indexToken = USDCY.address! +const indexTokenSymbol = TheUSDCYieldIndex.symbol +const indexToken = TheUSDCYieldIndex.address! const usdc = USDC.address! describe('getIssuanceComponentSwapData()', () => { diff --git a/src/utils/issuanceModules.test.ts b/src/utils/issuanceModules.test.ts index 16ff2c3..6f5dd56 100644 --- a/src/utils/issuanceModules.test.ts +++ b/src/utils/issuanceModules.test.ts @@ -19,7 +19,7 @@ import { IndexCoopBitcoin2xIndex, IndexCoopBitcoin3xIndex, HighYieldETHIndex, - USDCY, + TheUSDCYieldIndex, } from 'constants/tokens' import { getIssuanceModule } from './issuanceModules' @@ -60,9 +60,9 @@ describe('getIssuanceModule() - Mainnet - IndexProtocol', () => { expect(issuanceModule.isDebtIssuance).toBe(true) }) - test('returns debt issuance module v2 for USDCY', () => { + test('returns debt issuance module v2 for icUSD', () => { const expectedModule = IndexDebtIssuanceModuleV2Address - const issuanceModule = getIssuanceModule(USDCY.symbol) + const issuanceModule = getIssuanceModule(TheUSDCYieldIndex.symbol) expect(issuanceModule.address).toEqual(expectedModule) expect(issuanceModule.isDebtIssuance).toBe(true) }) diff --git a/src/utils/issuanceModules.ts b/src/utils/issuanceModules.ts index eb9ff1f..9d2ced7 100644 --- a/src/utils/issuanceModules.ts +++ b/src/utils/issuanceModules.ts @@ -20,7 +20,7 @@ import { IndexCoopBitcoin2xIndex, HighYieldETHIndex, RealWorldAssetIndex, - USDCY, + TheUSDCYieldIndex, } from '../constants/tokens' export interface IssuanceModule { @@ -65,7 +65,7 @@ export function getIssuanceModule( isDebtIssuance: true, } case GitcoinStakedETHIndex.symbol: - case USDCY.symbol: + case TheUSDCYieldIndex.symbol: case wsETH2.symbol: return { address: IndexDebtIssuanceModuleV2Address, isDebtIssuance: true } case InterestCompoundingETHIndex.symbol: diff --git a/src/utils/wrap-data.test.ts b/src/utils/wrap-data.test.ts index 47dc2f9..76300ba 100644 --- a/src/utils/wrap-data.test.ts +++ b/src/utils/wrap-data.test.ts @@ -6,8 +6,8 @@ describe('getWrapData()', () => { expect(wrapData.length).toBe(0) }) - test.only('returns correct wrap data for USDCY', async () => { - const wrapData = getWrapData('USDCY') + test.only('returns correct wrap data for icUSD', async () => { + const wrapData = getWrapData('icUSD') expect(wrapData.length).toBe(5) expect(wrapData[0].integrationName).toBe('') expect(wrapData[1].integrationName).toBe('Aave_V3_Wrap_V2_Adapter') diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts index 87f4d8b..c78f512 100644 --- a/src/utils/wrap-data.ts +++ b/src/utils/wrap-data.ts @@ -1,4 +1,4 @@ -import { USDCY } from 'constants/tokens' +import { TheUSDCYieldIndex } from 'constants/tokens' export interface ComponentWrapData { // wrap adapter integration name as listed in the IntegrationRegistry for the wrapModule @@ -15,7 +15,7 @@ const erc4626WrapV2AdapterName = 'ERC4626_Wrap_V2_Adapter' const ZERO_BYTES = '0x0000000000000000000000000000000000000000' export function getWrapData(tokenSymbol: string): ComponentWrapData[] { - if (tokenSymbol !== USDCY.symbol) return [] + if (tokenSymbol !== TheUSDCYieldIndex.symbol) return [] return [ { integrationName: '', From ffa0c17dcd179c6b12a7cb71d03328d086813d42 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 30 Sep 2024 11:39:49 +0100 Subject: [PATCH 36/58] test: update component swap data tests --- src/utils/component-swap-data.test.ts | 15 +++++++++------ src/utils/component-swap-data.ts | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index aa6adf8..8dfdb8f 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -9,6 +9,7 @@ import { wei } from 'utils/numbers' import { IndexZeroExSwapQuoteProvider, LocalhostProviderUrl } from 'tests/utils' import { isSameAddress } from 'utils/addresses' import { Exchange } from 'utils/swap-data' +import { BigNumber } from '@ethersproject/bignumber' const chainId = 1 const rpcUrl = LocalhostProviderUrl @@ -31,8 +32,8 @@ describe('getIssuanceComponentSwapData()', () => { rpcUrl, swapQuoteProvider ) - // FIXME: should be 5 for USDCY - expect(componentSwapData.length).toBe(6) + // TODO: update once rebalanced into components + expect(componentSwapData.length).toBe(1) for (let i = 0; i < componentSwapData.length; i++) { expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( true @@ -44,7 +45,8 @@ describe('getIssuanceComponentSwapData()', () => { expect(dexData.path).toEqual([]) expect(dexData.pool).toEqual(AddressZero) } - // fIXME: test for buy amounts + // TODO: update once rebalanced into components + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') }) // test('returns correct swap data based when input token is WETH', async () => { @@ -93,8 +95,8 @@ describe('getRedemptionComponentSwapData()', () => { rpcUrl, swapQuoteProvider ) - // FIXME: should be 5 for USDCY - expect(componentSwapData.length).toBe(6) + // TODO: update once rebalanced into components + expect(componentSwapData.length).toBe(1) for (let i = 0; i < componentSwapData.length; i++) { expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( true @@ -106,7 +108,8 @@ describe('getRedemptionComponentSwapData()', () => { expect(dexData.path).toEqual([]) expect(dexData.pool).toEqual(AddressZero) } - // fIXME: test for buy amounts + // TODO: update once rebalanced into components + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') }) }) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 37c54a4..5ae8f5a 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -214,7 +214,7 @@ async function getAmount( return assets } catch { // TODO: apply slippage to issuance units amount (for all none erc4262) - return BigInt(0) + return issuanceUnits } // const isFCASH = (address: string) => // [ From 8c0a0efff5bc7c8c9a4d7ea459e531284daab2b8 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 30 Sep 2024 11:40:39 +0100 Subject: [PATCH 37/58] test: update tests for presale token --- src/quote/flashmint/wrapped/provider.test.ts | 40 ++++++-------------- src/quote/flashmint/wrapped/provider.ts | 3 +- src/quote/swap/adapters/uniswap/index.ts | 2 +- src/utils/component-swap-data.ts | 3 +- src/utils/wrap-data.ts | 33 ++++++++-------- 5 files changed, 31 insertions(+), 50 deletions(-) diff --git a/src/quote/flashmint/wrapped/provider.test.ts b/src/quote/flashmint/wrapped/provider.test.ts index 6d0581c..dfee220 100644 --- a/src/quote/flashmint/wrapped/provider.test.ts +++ b/src/quote/flashmint/wrapped/provider.test.ts @@ -13,27 +13,7 @@ const provider = LocalhostProviderUrl const swapQuoteProvider = IndexZeroExSwapQuoteProvider describe('WrappedQuoteProvider()', () => { - test.only('returns a quote for minting icUSD', async () => { - const inputToken = usdc - const request: FlashMintWrappedQuoteRequest = { - chainId, - isMinting: true, - inputToken, - outputToken: indexToken, - indexTokenAmount: wei(1), - slippage: 0.5, - } - const quoteProvider = new WrappedQuoteProvider(provider, swapQuoteProvider) - const quote = await quoteProvider.getQuote(request) - if (!quote) fail() - expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) - // FIXME: test - // expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - expect(quote.componentSwapData.length).toEqual(6) - expect(quote.componentWrapData.length).toEqual(5) - }) - - test('returns a quote for minting MMI w/ icUSD', async () => { + test('returns a quote for minting icUSD', async () => { const inputToken = usdc const request: FlashMintWrappedQuoteRequest = { chainId, @@ -48,7 +28,8 @@ describe('WrappedQuoteProvider()', () => { if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - expect(quote.componentSwapData.length).toEqual(6) + expect(quote.componentSwapData.length).toEqual(1) + expect(quote.componentWrapData.length).toEqual(1) }) test('returns a quote for minting icUSD w/ WETH', async () => { @@ -66,10 +47,11 @@ describe('WrappedQuoteProvider()', () => { if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - expect(quote.componentSwapData.length).toEqual(6) + expect(quote.componentSwapData.length).toEqual(1) + expect(quote.componentWrapData.length).toEqual(1) }) - test.only('returns a quote redeeming icUSD for USDC', async () => { + test('returns a quote redeeming icUSD for USDC', async () => { const outputToken = usdc const request: FlashMintWrappedQuoteRequest = { chainId, @@ -83,10 +65,9 @@ describe('WrappedQuoteProvider()', () => { const quote = await quoteProvider.getQuote(request) if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) - // FIXME: - // expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - expect(quote.componentSwapData.length).toEqual(6) - expect(quote.componentWrapData.length).toEqual(5) + expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) + expect(quote.componentSwapData.length).toEqual(1) + expect(quote.componentWrapData.length).toEqual(1) }) test('returns a quote for redeeming icUSD for WETH', async () => { @@ -104,6 +85,7 @@ describe('WrappedQuoteProvider()', () => { if (!quote) fail() expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) expect(quote.inputOutputTokenAmount.gt(0)).toEqual(true) - expect(quote.componentSwapData.length).toEqual(6) + expect(quote.componentSwapData.length).toEqual(1) + expect(quote.componentWrapData.length).toEqual(1) }) }) diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts index 4411998..27e42e4 100644 --- a/src/quote/flashmint/wrapped/provider.ts +++ b/src/quote/flashmint/wrapped/provider.ts @@ -75,8 +75,7 @@ export class WrappedQuoteProvider this.swapQuoteProvider ) const componentWrapData = getWrapData(indexToken.symbol) - // FIXME: add check - // if (componentSwapData.length !== componentWrapData.length) return null + if (componentSwapData.length !== componentWrapData.length) return null let estimatedInputOutputAmount: BigNumber = BigNumber.from(0) const provider = getRpcProvider(this.rpcUrl) const contract = getFlashMintWrappedContract(provider) diff --git a/src/quote/swap/adapters/uniswap/index.ts b/src/quote/swap/adapters/uniswap/index.ts index 5833ef9..fd92455 100644 --- a/src/quote/swap/adapters/uniswap/index.ts +++ b/src/quote/swap/adapters/uniswap/index.ts @@ -35,7 +35,7 @@ export class UniswapSwapQuoteProvider implements SwapQuoteProvider { isSameAddress(inputToken, EthAddress) || isSameAddress(outputToken, EthAddress) ) { - // FIXME: remove for production, just for runnint tests and catching any of these cases + // FIXME: remove for production, just for running tests and catching any of these cases throw new Error('Error - using ETH instead of WETH') } diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 5ae8f5a..12f178c 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -20,13 +20,12 @@ const emptySwapData: SwapData = { pool: AddressZero, } -// FIXME: export interface ComponentSwapData { underlyingERC20: string dexData: SwapData // ONLY relevant for issue, not used for redeem: // amount that has to be bought of the unwrapped token version (to cover required wrapped component amounts for issuance) - // this amount has to be computed beforehand through the exchange rate of wrapped Component <> unwrappedComponent + // this amount has to be computed beforehand through the exchange rate of wrapped component <> unwrapped component // i.e. getRequiredComponentIssuanceUnits() on the IssuanceModule and then convert units through exchange rate to unwrapped component units // e.g. 300 cDAI needed for issuance of 1 Set token. exchange rate 1cDAI = 0.05 DAI. -> buyUnderlyingAmount = 0.05 DAI * 300 = 15 DAI buyUnderlyingAmount: BigNumber diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts index c78f512..0efb1c6 100644 --- a/src/utils/wrap-data.ts +++ b/src/utils/wrap-data.ts @@ -21,21 +21,22 @@ export function getWrapData(tokenSymbol: string): ComponentWrapData[] { integrationName: '', wrapData: ZERO_BYTES, }, - { - integrationName: aaveV3WrapV2AdapterName, - wrapData: ZERO_BYTES, - }, - { - integrationName: compoundV3WrapV2AdapterName, - wrapData: ZERO_BYTES, - }, - { - integrationName: aaveV2WrapV2AdapterName, - wrapData: ZERO_BYTES, - }, - { - integrationName: erc4626WrapV2AdapterName, - wrapData: ZERO_BYTES, - }, + // TODO: update once rebalanced + // { + // integrationName: aaveV3WrapV2AdapterName, + // wrapData: ZERO_BYTES, + // }, + // { + // integrationName: compoundV3WrapV2AdapterName, + // wrapData: ZERO_BYTES, + // }, + // { + // integrationName: aaveV2WrapV2AdapterName, + // wrapData: ZERO_BYTES, + // }, + // { + // integrationName: erc4626WrapV2AdapterName, + // wrapData: ZERO_BYTES, + // }, ] } From 726f866f922ef6187625ebf48fa5ec0a891b5e1e Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 30 Sep 2024 13:16:15 +0100 Subject: [PATCH 38/58] lint --- src/utils/component-swap-data.test.ts | 1 - src/utils/wrap-data.ts | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 8dfdb8f..fa974fc 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -9,7 +9,6 @@ import { wei } from 'utils/numbers' import { IndexZeroExSwapQuoteProvider, LocalhostProviderUrl } from 'tests/utils' import { isSameAddress } from 'utils/addresses' import { Exchange } from 'utils/swap-data' -import { BigNumber } from '@ethersproject/bignumber' const chainId = 1 const rpcUrl = LocalhostProviderUrl diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts index 0efb1c6..128d546 100644 --- a/src/utils/wrap-data.ts +++ b/src/utils/wrap-data.ts @@ -8,10 +8,10 @@ export interface ComponentWrapData { } // FIXME: check adapter names -const aaveV2WrapV2AdapterName = 'Aave_V2_Wrap_V2_Adapter' -const aaveV3WrapV2AdapterName = 'Aave_V3_Wrap_V2_Adapter' -const compoundV3WrapV2AdapterName = 'Compound_V3_USDC_Wrap_V2_Adapter' -const erc4626WrapV2AdapterName = 'ERC4626_Wrap_V2_Adapter' +// const aaveV2WrapV2AdapterName = 'Aave_V2_Wrap_V2_Adapter' +// const aaveV3WrapV2AdapterName = 'Aave_V3_Wrap_V2_Adapter' +// const compoundV3WrapV2AdapterName = 'Compound_V3_USDC_Wrap_V2_Adapter' +// const erc4626WrapV2AdapterName = 'ERC4626_Wrap_V2_Adapter' const ZERO_BYTES = '0x0000000000000000000000000000000000000000' export function getWrapData(tokenSymbol: string): ComponentWrapData[] { From 5144a002ab10e0b6db82f960f8660f931386d499 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 30 Sep 2024 13:17:27 +0100 Subject: [PATCH 39/58] update wrap data test --- src/utils/wrap-data.test.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/utils/wrap-data.test.ts b/src/utils/wrap-data.test.ts index 76300ba..8d556a9 100644 --- a/src/utils/wrap-data.test.ts +++ b/src/utils/wrap-data.test.ts @@ -8,14 +8,15 @@ describe('getWrapData()', () => { test.only('returns correct wrap data for icUSD', async () => { const wrapData = getWrapData('icUSD') - expect(wrapData.length).toBe(5) - expect(wrapData[0].integrationName).toBe('') - expect(wrapData[1].integrationName).toBe('Aave_V3_Wrap_V2_Adapter') - expect(wrapData[2].integrationName).toBe('Compound_V3_USDC_Wrap_V2_Adapter') - expect(wrapData[3].integrationName).toBe('Aave_V2_Wrap_V2_Adapter') - expect(wrapData[4].integrationName).toBe('ERC4626_Wrap_V2_Adapter') - wrapData.forEach((data) => { - expect(data.wrapData).toBe('0x0000000000000000000000000000000000000000') - }) + // TODO: update for components (after presale/rebalance) + expect(wrapData.length).toBe(1) + // expect(wrapData[0].integrationName).toBe('') + // expect(wrapData[1].integrationName).toBe('Aave_V3_Wrap_V2_Adapter') + // expect(wrapData[2].integrationName).toBe('Compound_V3_USDC_Wrap_V2_Adapter') + // expect(wrapData[3].integrationName).toBe('Aave_V2_Wrap_V2_Adapter') + // expect(wrapData[4].integrationName).toBe('ERC4626_Wrap_V2_Adapter') + // wrapData.forEach((data) => { + // expect(data.wrapData).toBe('0x0000000000000000000000000000000000000000') + // }) }) }) From c7c3ecba3fbb3edc7698df63e38476dd11e068e9 Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 30 Sep 2024 13:38:00 +0100 Subject: [PATCH 40/58] test: add component swap data tests for weth --- src/utils/component-swap-data.test.ts | 130 +++++++++++++------------- src/utils/component-swap-data.ts | 6 +- 2 files changed, 67 insertions(+), 69 deletions(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index fa974fc..d96a89b 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { AddressZero } from 'constants/addresses' -import { TheUSDCYieldIndex, USDC } from 'constants/tokens' +import { TheUSDCYieldIndex, USDC, WETH } from 'constants/tokens' import { getIssuanceComponentSwapData, getRedemptionComponentSwapData, @@ -17,6 +17,7 @@ const swapQuoteProvider = IndexZeroExSwapQuoteProvider const indexTokenSymbol = TheUSDCYieldIndex.symbol const indexToken = TheUSDCYieldIndex.address! const usdc = USDC.address! +const weth = WETH.address! describe('getIssuanceComponentSwapData()', () => { test('returns correct swap data based on input token USDC', async () => { @@ -48,37 +49,37 @@ describe('getIssuanceComponentSwapData()', () => { expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') }) - // test('returns correct swap data based when input token is WETH', async () => { - // const inputToken = weth - // const componentSwapData = await getIssuanceComponentSwapData( - // MoneyMarketIndexToken.symbol, - // MoneyMarketIndexToken.address!, - // inputToken, - // wei(1), - // provider, - // zeroExApi - // ) - // expect(componentSwapData.length).toBe(6) - // expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) - // expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) - // expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) - // expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) - // expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) - // expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) - // expect(componentSwapData[0].dexData.path).toEqual([weth, usdc]) - // expect(componentSwapData[1].dexData.path).toEqual([weth, dai]) - // expect(componentSwapData[2].dexData.path).toEqual([weth, usdt]) - // expect(componentSwapData[3].dexData.path).toEqual([weth, usdt]) - // expect(componentSwapData[4].dexData.path).toEqual([weth, dai]) - // expect(componentSwapData[5].dexData.path).toEqual([weth, usdc]) - // componentSwapData.forEach((swapData) => { - // expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) - // expect(swapData.dexData.exchange).toBe(Exchange.UniV3) - // // Not great but atm there could be varying pools/fees returned - // expect(swapData.dexData.fees.length).toBeGreaterThan(0) - // expect(swapData.dexData.pool).toBe(zeroAddress) - // }) - // }) + test('returns correct swap data based when input token is WETH', async () => { + const componentSwapData = await getIssuanceComponentSwapData( + { + chainId, + indexTokenSymbol, + indexToken, + inputToken: weth, + indexTokenAmount: wei(1), + }, + rpcUrl, + swapQuoteProvider + ) + // TODO: update once rebalanced into components + expect(componentSwapData.length).toBe(1) + for (let i = 0; i < componentSwapData.length; i++) { + expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( + true + ) + // Should be empty as input token is equal to output token (underlying erc20) + const dexData = componentSwapData[i].dexData + expect(dexData.exchange).toEqual(Exchange.UniV3) + expect(dexData.fees.length).toBeGreaterThan(0) + expect(dexData.path).toEqual([ + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + ]) + expect(dexData.pool).toEqual(AddressZero) + } + // TODO: update once rebalanced into components + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') + }) }) describe('getRedemptionComponentSwapData()', () => { @@ -110,37 +111,36 @@ describe('getRedemptionComponentSwapData()', () => { // TODO: update once rebalanced into components expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') }) -}) -// test('returns correct swap data when output token is WETH', async () => { -// const outputToken = weth -// const componentSwapData = await getRedemptionComponentSwapData( -// MoneyMarketIndexToken.symbol, -// MoneyMarketIndexToken.address!, -// outputToken, -// wei(1), -// provider, -// zeroExApi -// ) -// expect(componentSwapData.length).toBe(6) -// expect(componentSwapData[0].underlyingERC20.toLowerCase()).toBe(usdc) -// expect(componentSwapData[1].underlyingERC20.toLowerCase()).toBe(dai) -// expect(componentSwapData[2].underlyingERC20.toLowerCase()).toBe(usdt) -// expect(componentSwapData[3].underlyingERC20.toLowerCase()).toBe(usdt) -// expect(componentSwapData[4].underlyingERC20.toLowerCase()).toBe(dai) -// expect(componentSwapData[5].underlyingERC20.toLowerCase()).toBe(usdc) -// expect(componentSwapData[0].dexData.path).toEqual([usdc, weth]) -// expect(componentSwapData[1].dexData.path).toEqual([dai, weth]) -// expect(componentSwapData[2].dexData.path).toEqual([usdt, weth]) -// expect(componentSwapData[3].dexData.path).toEqual([usdt, weth]) -// expect(componentSwapData[4].dexData.path).toEqual([dai, weth]) -// expect(componentSwapData[5].dexData.path).toEqual([usdc, weth]) -// componentSwapData.forEach((swapData) => { -// expect(swapData.buyUnderlyingAmount.gt(0)).toBe(true) -// expect(swapData.dexData.exchange).toBe(Exchange.UniV3) -// // Not great but atm there could be varying pools/fees returned -// expect(swapData.dexData.fees.length).toBeGreaterThan(0) -// expect(swapData.dexData.pool).toBe(zeroAddress) -// }) -// }) -// }) + test('returns correct swap data when output token is WETH', async () => { + const componentSwapData = await getRedemptionComponentSwapData( + { + chainId, + indexTokenSymbol, + indexToken, + outputToken: weth, + indexTokenAmount: wei(1), + }, + rpcUrl, + swapQuoteProvider + ) + // TODO: update once rebalanced into components + expect(componentSwapData.length).toBe(1) + for (let i = 0; i < componentSwapData.length; i++) { + expect(isSameAddress(componentSwapData[i].underlyingERC20, usdc)).toBe( + true + ) + // Should be empty as input token is equal to output token (underlying erc20) + const dexData = componentSwapData[i].dexData + expect(dexData.exchange).toEqual(Exchange.UniV3) + expect(dexData.fees.length).toBeGreaterThan(0) + expect(dexData.path).toEqual([ + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + ]) + expect(dexData.pool).toEqual(AddressZero) + } + // TODO: update once rebalanced into components + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') + }) +}) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 12f178c..0151506 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -100,8 +100,7 @@ export async function getIssuanceComponentSwapData( inputToken, outputToken: underlyingERC20.address, outputAmount: amounts[index].toString(), - // TODO: needed for USDCY? - // includedSources: 'Uniswap_V3', + sources: [Exchange.UniV3], }) } ) @@ -157,8 +156,7 @@ export async function getRedemptionComponentSwapData( inputToken: underlyingERC20.address, inputAmount: amounts[index].toString(), outputToken, - // TODO: needed for USDCY? - // includedSources: 'Uniswap_V3', + sources: [Exchange.UniV3], }) } ) From adda212ebed49df97ce82ff51ceafbe2eefca9ab Mon Sep 17 00:00:00 2001 From: JD Date: Mon, 30 Sep 2024 13:48:24 +0100 Subject: [PATCH 41/58] test: prepare tests for icUSD --- src/tests/icusd.test.ts | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/tests/icusd.test.ts diff --git a/src/tests/icusd.test.ts b/src/tests/icusd.test.ts new file mode 100644 index 0000000..831affa --- /dev/null +++ b/src/tests/icusd.test.ts @@ -0,0 +1,50 @@ +import { + getMainnetTestFactory, + QuoteTokens, + SignerAccount4, + TestFactory, + transferFromWhale, + wei, +} from './utils' + +const { eth, icusd, usdc } = QuoteTokens + +// TOOD: activate once FMWrapped contract is ready +describe.skip('icUSD (mainnet)', () => { + const indexToken = icusd + const signer = SignerAccount4 + let factory: TestFactory + beforeEach(async () => { + factory = getMainnetTestFactory(signer) + }) + + test('can mint with USDC', async () => { + const quote = await factory.fetchQuote({ + isMinting: true, + inputToken: usdc, + outputToken: indexToken, + indexTokenAmount: wei('1'), + slippage: 0.5, + }) + const usdcWhale = '0x7713974908Be4BEd47172370115e8b1219F4A5f0' + await transferFromWhale( + usdcWhale, + factory.getSigner().address, + wei('100000', quote.inputToken.decimals), + quote.inputToken.address, + factory.getProvider() + ) + await factory.executeTx() + }) + + test('can redeem to USDC', async () => { + await factory.fetchQuote({ + isMinting: false, + inputToken: indexToken, + outputToken: eth, + indexTokenAmount: wei('1'), + slippage: 0.5, + }) + await factory.executeTx() + }) +}) From a21e797d1ffc9f1ec5ad72124402a56349ba2416 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 08:36:25 +0100 Subject: [PATCH 42/58] fixes after merging in main --- src/constants/contracts.ts | 4 +--- src/quote/provider/index.test.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/constants/contracts.ts b/src/constants/contracts.ts index 8a5dafd..258394d 100644 --- a/src/constants/contracts.ts +++ b/src/constants/contracts.ts @@ -4,6 +4,7 @@ import { ChainId } from 'constants/chains' export const Contracts: { [key: number]: any } = { [ChainId.Mainnet]: { FlashMintHyEthV3: '0xCb1eEA349f25288627f008C5e2a69b684bddDf49', + FlashMintWrapped: '0x5C0D0a9a0c3A0a5B591496fF894686893b69FaA2', }, [ChainId.Arbitrum]: { DebtIssuanceModuleV3: '0x4ac26c26116fa976352b70700af58bc2442489d8', @@ -13,9 +14,6 @@ export const Contracts: { [key: number]: any } = { DebtIssuanceModuleV3: '0xa30E87311407dDcF1741901A8F359b6005252F22', FlashMintLeveragedExtended: '0xE6c18c4C9FC6909EDa546649EBE33A8159256CBE', }, - [ChainId.Mainnet]: { - FlashMintWrapped: '0x5C0D0a9a0c3A0a5B591496fF894686893b69FaA2', - }, } // Index Protocol diff --git a/src/quote/provider/index.test.ts b/src/quote/provider/index.test.ts index bfe202f..4ce7c8f 100644 --- a/src/quote/provider/index.test.ts +++ b/src/quote/provider/index.test.ts @@ -25,7 +25,7 @@ const provider = LocalhostProvider const zeroexSwapQuoteProvider = IndexZeroExSwapQuoteProvider const FlashMintHyEthAddress = Contracts[ChainId.Mainnet].FlashMintHyEthV3 -const { dseth, eth, eth2x, hyeth, iceth, usdc } = QuoteTokens +const { eth, eth2x, hyeth, iceth, icusd, usdc } = QuoteTokens describe('FlashMintQuoteProvider()', () => { test('throws if token is unsupported', async () => { From b87879cd48322a0193f070a6957891c46dc5a181 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 08:45:42 +0100 Subject: [PATCH 43/58] feat: add flash mint wrapped --- src/constants/abis/FlashMintWrapped.json | 2 +- src/constants/contracts.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constants/abis/FlashMintWrapped.json b/src/constants/abis/FlashMintWrapped.json index 0fa132b..c588211 100644 --- a/src/constants/abis/FlashMintWrapped.json +++ b/src/constants/abis/FlashMintWrapped.json @@ -1 +1 @@ -[{"inputs":[{"components":[{"internalType":"address","name":"quickRouter","type":"address"},{"internalType":"address","name":"sushiRouter","type":"address"},{"internalType":"address","name":"uniV3Router","type":"address"},{"internalType":"address","name":"uniV3Quoter","type":"address"},{"internalType":"address","name":"curveAddressProvider","type":"address"},{"internalType":"address","name":"curveCalculator","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"internalType":"struct DEXAdapter.Addresses","name":"_dexAddresses","type":"tuple"},{"internalType":"contract IController","name":"_setController","type":"address"},{"internalType":"contract IDebtIssuanceModule","name":"_issuanceModule","type":"address"},{"internalType":"address","name":"_wrapModule","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"_inputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountInputToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountSetIssued","type":"uint256"}],"name":"FlashMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"_outputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountSetRedeemed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountOutputToken","type":"uint256"}],"name":"FlashRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"}],"name":"approveSetToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dexAdapter","outputs":[{"internalType":"address","name":"quickRouter","type":"address"},{"internalType":"address","name":"sushiRouter","type":"address"},{"internalType":"address","name":"uniV3Router","type":"address"},{"internalType":"address","name":"uniV3Quoter","type":"address"},{"internalType":"address","name":"curveAddressProvider","type":"address"},{"internalType":"address","name":"curveCalculator","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"address","name":"_inputToken","type":"address"},{"internalType":"uint256","name":"_setAmount","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"}],"name":"getIssueExactSet","outputs":[{"internalType":"uint256","name":"amountInputNeeded","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"address","name":"_outputToken","type":"address"},{"internalType":"uint256","name":"_setAmount","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"}],"name":"getRedeemExactSet","outputs":[{"internalType":"uint256","name":"amountOutputReceived","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"issuanceModule","outputs":[{"internalType":"contract IDebtIssuanceModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"contract IERC20","name":"_inputToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_maxAmountInputToken","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_wrapData","type":"tuple[]"}],"name":"issueExactSetFromERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_wrapData","type":"tuple[]"}],"name":"issueExactSetFromETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"contract IERC20","name":"_outputToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_minOutputReceive","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_unwrapData","type":"tuple[]"}],"name":"redeemExactSetForERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_minOutputReceive","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"enum DEXAdapter.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapter.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_unwrapData","type":"tuple[]"}],"name":"redeemExactSetForETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setController","outputs":[{"internalType":"contract IController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_tokens","type":"address[]"},{"internalType":"address payable","name":"_to","type":"address"}],"name":"withdrawTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"wrapModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file +[{"inputs":[{"components":[{"internalType":"address","name":"quickRouter","type":"address"},{"internalType":"address","name":"sushiRouter","type":"address"},{"internalType":"address","name":"uniV3Router","type":"address"},{"internalType":"address","name":"uniV3Quoter","type":"address"},{"internalType":"address","name":"curveAddressProvider","type":"address"},{"internalType":"address","name":"curveCalculator","type":"address"},{"internalType":"address","name":"balV2Vault","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"internalType":"struct DEXAdapterV3.Addresses","name":"_dexAddresses","type":"tuple"},{"internalType":"contract IController","name":"_setController","type":"address"},{"internalType":"contract IDebtIssuanceModule","name":"_issuanceModule","type":"address"},{"internalType":"address","name":"_wrapModule","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"_inputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountInputToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountSetIssued","type":"uint256"}],"name":"FlashMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"_outputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountSetRedeemed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountOutputToken","type":"uint256"}],"name":"FlashRedeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"}],"name":"approveSetToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dexAdapter","outputs":[{"internalType":"address","name":"quickRouter","type":"address"},{"internalType":"address","name":"sushiRouter","type":"address"},{"internalType":"address","name":"uniV3Router","type":"address"},{"internalType":"address","name":"uniV3Quoter","type":"address"},{"internalType":"address","name":"curveAddressProvider","type":"address"},{"internalType":"address","name":"curveCalculator","type":"address"},{"internalType":"address","name":"balV2Vault","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"address","name":"_inputToken","type":"address"},{"internalType":"uint256","name":"_setAmount","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"bytes32[]","name":"poolIds","type":"bytes32[]"},{"internalType":"enum DEXAdapterV3.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapterV3.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"}],"name":"getIssueExactSet","outputs":[{"internalType":"uint256","name":"amountInputNeeded","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"address","name":"_outputToken","type":"address"},{"internalType":"uint256","name":"_setAmount","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"bytes32[]","name":"poolIds","type":"bytes32[]"},{"internalType":"enum DEXAdapterV3.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapterV3.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"}],"name":"getRedeemExactSet","outputs":[{"internalType":"uint256","name":"amountOutputReceived","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"issuanceModule","outputs":[{"internalType":"contract IDebtIssuanceModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"contract IERC20","name":"_inputToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_maxAmountInputToken","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"bytes32[]","name":"poolIds","type":"bytes32[]"},{"internalType":"enum DEXAdapterV3.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapterV3.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_wrapData","type":"tuple[]"}],"name":"issueExactSetFromERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"bytes32[]","name":"poolIds","type":"bytes32[]"},{"internalType":"enum DEXAdapterV3.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapterV3.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_wrapData","type":"tuple[]"}],"name":"issueExactSetFromETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"contract IERC20","name":"_outputToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_minOutputReceive","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"bytes32[]","name":"poolIds","type":"bytes32[]"},{"internalType":"enum DEXAdapterV3.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapterV3.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_unwrapData","type":"tuple[]"}],"name":"redeemExactSetForERC20","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISetToken","name":"_setToken","type":"address"},{"internalType":"uint256","name":"_amountSetToken","type":"uint256"},{"internalType":"uint256","name":"_minOutputReceive","type":"uint256"},{"components":[{"internalType":"address","name":"underlyingERC20","type":"address"},{"components":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint24[]","name":"fees","type":"uint24[]"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"bytes32[]","name":"poolIds","type":"bytes32[]"},{"internalType":"enum DEXAdapterV3.Exchange","name":"exchange","type":"uint8"}],"internalType":"struct DEXAdapterV3.SwapData","name":"dexData","type":"tuple"},{"internalType":"uint256","name":"buyUnderlyingAmount","type":"uint256"}],"internalType":"struct FlashMintWrapped.ComponentSwapData[]","name":"_swapData","type":"tuple[]"},{"components":[{"internalType":"string","name":"integrationName","type":"string"},{"internalType":"bytes","name":"wrapData","type":"bytes"}],"internalType":"struct FlashMintWrapped.ComponentWrapData[]","name":"_unwrapData","type":"tuple[]"}],"name":"redeemExactSetForETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setController","outputs":[{"internalType":"contract IController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"_tokens","type":"address[]"},{"internalType":"address payable","name":"_to","type":"address"}],"name":"withdrawTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"wrapModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/src/constants/contracts.ts b/src/constants/contracts.ts index 258394d..ef4bb29 100644 --- a/src/constants/contracts.ts +++ b/src/constants/contracts.ts @@ -4,7 +4,7 @@ import { ChainId } from 'constants/chains' export const Contracts: { [key: number]: any } = { [ChainId.Mainnet]: { FlashMintHyEthV3: '0xCb1eEA349f25288627f008C5e2a69b684bddDf49', - FlashMintWrapped: '0x5C0D0a9a0c3A0a5B591496fF894686893b69FaA2', + FlashMintWrapped: '0x7ddE626dE8CE73229838B5c2F9A71bc7ac207801', }, [ChainId.Arbitrum]: { DebtIssuanceModuleV3: '0x4ac26c26116fa976352b70700af58bc2442489d8', From 16927c050fa99f915417144bd70921efea1c3e8b Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 10:16:20 +0100 Subject: [PATCH 44/58] feat: add swap data v3 --- src/utils/swap-data.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/utils/swap-data.ts b/src/utils/swap-data.ts index 84206bf..6645104 100644 --- a/src/utils/swap-data.ts +++ b/src/utils/swap-data.ts @@ -1,5 +1,5 @@ -// The order here has to be exactly the same as in the `DEXAdapter`` -// https://github.com/IndexCoop/index-coop-smart-contracts/blob/317dfb677e9738fc990cf69d198358065e8cb595/contracts/exchangeIssuance/DEXAdapter.sol#L53 +// The order here has to be exactly the same as in the `DEXAdapter` +// https://github.com/IndexCoop/index-coop-smart-contracts/blob/master/contracts/exchangeIssuance/DEXAdapterV3.sol#L54 export enum Exchange { None, Quickswap, @@ -15,3 +15,11 @@ export interface SwapData { fees: number[] pool: string } + +export interface SwapDataV3 { + exchange: Exchange + path: string[] + fees: number[] + pool: string + poolIds: string[] +} From 2b758a01f1f8afb77cd7c2977b9dfb0c7dd63b1f Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 10:22:23 +0100 Subject: [PATCH 45/58] update component swap data to use swapdatav3 --- src/utils/component-swap-data.test.ts | 4 ++++ src/utils/component-swap-data.ts | 14 ++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index d96a89b..5672365 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -44,6 +44,7 @@ describe('getIssuanceComponentSwapData()', () => { expect(dexData.fees).toEqual([]) expect(dexData.path).toEqual([]) expect(dexData.pool).toEqual(AddressZero) + expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') @@ -76,6 +77,7 @@ describe('getIssuanceComponentSwapData()', () => { '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', ]) expect(dexData.pool).toEqual(AddressZero) + expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') @@ -107,6 +109,7 @@ describe('getRedemptionComponentSwapData()', () => { expect(dexData.fees).toEqual([]) expect(dexData.path).toEqual([]) expect(dexData.pool).toEqual(AddressZero) + expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') @@ -139,6 +142,7 @@ describe('getRedemptionComponentSwapData()', () => { '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', ]) expect(dexData.pool).toEqual(AddressZero) + expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 0151506..8c01c87 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -9,20 +9,21 @@ import { isSameAddress } from 'utils/addresses' import { createClient } from 'utils/clients' import { getIssuanceModule } from 'utils/issuanceModules' import { getRpcProvider } from 'utils/rpc-provider' -import { Exchange, SwapData } from 'utils/swap-data' +import { Exchange, SwapDataV3 } from 'utils/swap-data' // const DEFAULT_SLIPPAGE = 0.0015 -const emptySwapData: SwapData = { +const emptySwapData: SwapDataV3 = { exchange: Exchange.None, path: [], fees: [], pool: AddressZero, + poolIds: [], } export interface ComponentSwapData { underlyingERC20: string - dexData: SwapData + dexData: SwapDataV3 // ONLY relevant for issue, not used for redeem: // amount that has to be bought of the unwrapped token version (to cover required wrapped component amounts for issuance) // this amount has to be computed beforehand through the exchange rate of wrapped component <> unwrapped component @@ -178,7 +179,12 @@ function buildComponentSwapData( return issuanceComponents.map((_: string, index: number) => { const wrappedToken = wrappedTokens[index] const buyUnderlyingAmount = buyAmounts[index] - const dexData = swapDataResults[index]?.swapData ?? emptySwapData + const dexData = swapDataResults[index]?.swapData + ? { + ...swapDataResults[index]?.swapData, + poolIds: [], + } + : emptySwapData return { underlyingERC20: wrappedToken.underlyingErc20.address, buyUnderlyingAmount, From 4fe07d1d4aa00f12d35391e90e31fb8cb1a7ba1f Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 10:22:50 +0100 Subject: [PATCH 46/58] test: update wrapped builder tests --- src/flashmint/builders/wrapped.test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/flashmint/builders/wrapped.test.ts b/src/flashmint/builders/wrapped.test.ts index 3ba9a21..be0e2ed 100644 --- a/src/flashmint/builders/wrapped.test.ts +++ b/src/flashmint/builders/wrapped.test.ts @@ -16,12 +16,10 @@ import { const rpcUrl = LocalhostProviderUrl const ZERO_BYTES = '0x0000000000000000000000000000000000000000' -const { icusd, usdc } = QuoteTokens - const FlashMintWrappedAddress = Contracts[ChainId.Mainnet].FlashMintWrapped const eth = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' -const indexToken = icusd.address -const usdcAddress = usdc.address +const indexToken = QuoteTokens.icusd.address +const usdcAddress = QuoteTokens.usdc.address describe('WrappedTransactionBuilder()', () => { test('returns null for invalid request (no index token)', async () => { @@ -185,6 +183,7 @@ function getDefaultBuildRequest( ], fees: [3000, 3000], pool: '0x0000000000000000000000000000000000000000', + poolIds: [], }, }, { @@ -199,6 +198,7 @@ function getDefaultBuildRequest( ], fees: [3000, 3000], pool: '0x0000000000000000000000000000000000000000', + poolIds: [], }, }, { @@ -213,6 +213,7 @@ function getDefaultBuildRequest( ], fees: [3000, 3000], pool: '0x0000000000000000000000000000000000000000', + poolIds: [], }, }, { @@ -227,6 +228,7 @@ function getDefaultBuildRequest( ], fees: [3000, 3000], pool: '0x0000000000000000000000000000000000000000', + poolIds: [], }, }, { @@ -241,6 +243,7 @@ function getDefaultBuildRequest( ], fees: [3000, 3000], pool: '0x0000000000000000000000000000000000000000', + poolIds: [], }, }, { @@ -255,6 +258,7 @@ function getDefaultBuildRequest( ], fees: [3000, 3000], pool: '0x0000000000000000000000000000000000000000', + poolIds: [], }, }, ], From a9dc98dee81e9fcfa4ee93165d42dc190c5ffd1b Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 10:47:26 +0100 Subject: [PATCH 47/58] feat: add debt issuance module v3 --- src/constants/contracts.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/contracts.ts b/src/constants/contracts.ts index ef4bb29..73a9c38 100644 --- a/src/constants/contracts.ts +++ b/src/constants/contracts.ts @@ -3,6 +3,7 @@ import { ChainId } from 'constants/chains' // eslint-disable-next-line @typescript-eslint/no-explicit-any export const Contracts: { [key: number]: any } = { [ChainId.Mainnet]: { + DebtIssuanceModuleV3: '0x86B7C605C03B9bbb0F6A25FBBb63baF15d875193', FlashMintHyEthV3: '0xCb1eEA349f25288627f008C5e2a69b684bddDf49', FlashMintWrapped: '0x7ddE626dE8CE73229838B5c2F9A71bc7ac207801', }, From 961976e0c5fc37932ec21aca8a52c6ad8a0f3777 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 10:47:52 +0100 Subject: [PATCH 48/58] update issuance module for icusd --- src/utils/component-swap-data.test.ts | 8 ++++---- src/utils/issuanceModules.test.ts | 2 +- src/utils/issuanceModules.ts | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 5672365..5a2168d 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -47,7 +47,7 @@ describe('getIssuanceComponentSwapData()', () => { expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components - expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000010') }) test('returns correct swap data based when input token is WETH', async () => { @@ -80,7 +80,7 @@ describe('getIssuanceComponentSwapData()', () => { expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components - expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000010') }) }) @@ -112,7 +112,7 @@ describe('getRedemptionComponentSwapData()', () => { expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components - expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('999990') }) test('returns correct swap data when output token is WETH', async () => { @@ -145,6 +145,6 @@ describe('getRedemptionComponentSwapData()', () => { expect(dexData.poolIds).toEqual([]) } // TODO: update once rebalanced into components - expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('1000000') + expect(componentSwapData[0].buyUnderlyingAmount.toString()).toBe('999990') }) }) diff --git a/src/utils/issuanceModules.test.ts b/src/utils/issuanceModules.test.ts index 6f5dd56..497c244 100644 --- a/src/utils/issuanceModules.test.ts +++ b/src/utils/issuanceModules.test.ts @@ -61,7 +61,7 @@ describe('getIssuanceModule() - Mainnet - IndexProtocol', () => { }) test('returns debt issuance module v2 for icUSD', () => { - const expectedModule = IndexDebtIssuanceModuleV2Address + const expectedModule = Contracts[ChainId.Mainnet].DebtIssuanceModuleV3 const issuanceModule = getIssuanceModule(TheUSDCYieldIndex.symbol) expect(issuanceModule.address).toEqual(expectedModule) expect(issuanceModule.isDebtIssuance).toBe(true) diff --git a/src/utils/issuanceModules.ts b/src/utils/issuanceModules.ts index 9d2ced7..d792f06 100644 --- a/src/utils/issuanceModules.ts +++ b/src/utils/issuanceModules.ts @@ -65,11 +65,15 @@ export function getIssuanceModule( isDebtIssuance: true, } case GitcoinStakedETHIndex.symbol: - case TheUSDCYieldIndex.symbol: case wsETH2.symbol: return { address: IndexDebtIssuanceModuleV2Address, isDebtIssuance: true } case InterestCompoundingETHIndex.symbol: return { address: DebtIssuanceModuleV2Address, isDebtIssuance: true } + case TheUSDCYieldIndex.symbol: + return { + address: Contracts[ChainId.Mainnet].DebtIssuanceModuleV3, + isDebtIssuance: true, + } default: return { address: BasicIssuanceModuleAddress, isDebtIssuance: false } } From c15340eb1d7cd5683066dde5779bb60fec5d430e Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 11:05:29 +0100 Subject: [PATCH 49/58] test: turn on icUSD quote provider tests --- src/quote/provider/index.test.ts | 39 +++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/quote/provider/index.test.ts b/src/quote/provider/index.test.ts index 4ce7c8f..7dbbb8c 100644 --- a/src/quote/provider/index.test.ts +++ b/src/quote/provider/index.test.ts @@ -125,7 +125,7 @@ describe('FlashMintQuoteProvider()', () => { expect(quote.tx.data?.length).toBeGreaterThan(0) }) - test.skip('returns a quote for minting icUSD', async () => { + test('returns a quote for minting icUSD', async () => { const request: FlashMintQuoteRequest = { isMinting: true, inputToken: usdc, @@ -221,8 +221,9 @@ describe('FlashMintQuoteProvider()', () => { expect(quote.isMinting).toEqual(request.isMinting) expect(quote.inputToken).toEqual(request.inputToken) expect(quote.outputToken).toEqual(request.outputToken) - expect(quote.outputToken).toEqual(request.outputToken) - expect(quote.inputAmount).toEqual(quote.indexTokenAmount) + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputAmount).toEqual(request.indexTokenAmount) + expect(quote.outputAmount).toEqual(quote.inputOutputAmount) expect(quote.inputOutputAmount.gt(0)).toBe(true) expect(quote.slippage).toEqual(request.slippage) expect(quote.tx).not.toBeNull() @@ -267,4 +268,36 @@ describe('FlashMintQuoteProvider()', () => { expect(quote.tx.to).toBe(contract.address) expect(quote.tx.data?.length).toBeGreaterThan(0) }) + + test('returns a quote for redeeming icUSD', async () => { + const request: FlashMintQuoteRequest = { + isMinting: false, + inputToken: icusd, + outputToken: usdc, + indexTokenAmount: wei(1), + slippage: 0.5, + } + const quoteProvider = new FlashMintQuoteProvider( + LocalhostProviderUrl, + IndexZeroExSwapQuoteProvider + ) + const quote = await quoteProvider.getQuote(request) + if (!quote) fail() + const FlashMintWrappedAddress = Contracts[ChainId.Mainnet].FlashMintWrapped + const chainId = (await provider.getNetwork()).chainId + expect(quote.chainId).toEqual(chainId) + expect(quote.contractType).toEqual(FlashMintContractType.wrapped) + expect(quote.contract).toEqual(FlashMintWrappedAddress) + expect(quote.isMinting).toEqual(request.isMinting) + expect(quote.inputToken).toEqual(request.inputToken) + expect(quote.outputToken).toEqual(request.outputToken) + expect(quote.inputAmount).toEqual(request.indexTokenAmount) + expect(quote.outputAmount).toEqual(quote.inputOutputAmount) + expect(quote.indexTokenAmount).toEqual(request.indexTokenAmount) + expect(quote.inputOutputAmount.gt(0)).toBe(true) + expect(quote.slippage).toEqual(request.slippage) + expect(quote.tx).not.toBeNull() + expect(quote.tx.to).toBe(FlashMintWrappedAddress) + expect(quote.tx.data?.length).toBeGreaterThan(0) + }) }) From bdc7350df3c2a9cd904dc7c314c157c4226174fc Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 11:22:33 +0100 Subject: [PATCH 50/58] test: activate icUSD integration tests --- .github/workflows/publish.yml | 1 + package-lock.json | 4 ++-- package.json | 1 + src/tests/icusd.test.ts | 3 +-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e1744a4..dd70650 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,6 +43,7 @@ jobs: - run: npm run test:builders - run: npm run test:quotes - run: npm run test:base + - run: npm run test:icusd - run: npm run test:hyeth - run: npm run test:btc2x - run: npm run test:eth2x diff --git a/package-lock.json b/package-lock.json index 814f014..a9b602b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@indexcoop/flash-mint-sdk", - "version": "3.3.2", + "version": "3.7.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@indexcoop/flash-mint-sdk", - "version": "3.3.2", + "version": "3.7.1", "license": "MIT", "dependencies": { "@ethersproject/abstract-provider": "^5.6.1", diff --git a/package.json b/package.json index f15d3d9..bca0d21 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "test:eth2x": "npm test src/tests/eth2x.test.ts", "test:hyeth": "npm test src/tests/hyeth.test.ts", "test:iceth": "npm test src/tests/iceth", + "test:icusd": "npm test src/tests/icusd.test.ts", "test:watch": "jest --watch" }, "keywords": [ diff --git a/src/tests/icusd.test.ts b/src/tests/icusd.test.ts index 831affa..ff3f9cd 100644 --- a/src/tests/icusd.test.ts +++ b/src/tests/icusd.test.ts @@ -9,8 +9,7 @@ import { const { eth, icusd, usdc } = QuoteTokens -// TOOD: activate once FMWrapped contract is ready -describe.skip('icUSD (mainnet)', () => { +describe('icUSD (mainnet)', () => { const indexToken = icusd const signer = SignerAccount4 let factory: TestFactory From 72a9fc479b3ac7494a8a9f5dbf6bd73394366048 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 11:31:41 +0100 Subject: [PATCH 51/58] fix component swap data --- src/utils/component-swap-data.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 8c01c87..304dc5a 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -179,9 +179,13 @@ function buildComponentSwapData( return issuanceComponents.map((_: string, index: number) => { const wrappedToken = wrappedTokens[index] const buyUnderlyingAmount = buyAmounts[index] - const dexData = swapDataResults[index]?.swapData + const swapData = swapDataResults[index]?.swapData + const dexData: SwapDataV3 = swapData ? { - ...swapDataResults[index]?.swapData, + exchange: swapData.exchange, + path: swapData.path, + fees: swapData.fees, + pool: swapData.pool, poolIds: [], } : emptySwapData From ea75a814565ee1fca84001f8196db2530fa78abf Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 11:47:52 +0100 Subject: [PATCH 52/58] test: fix redeem test --- src/tests/icusd.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/icusd.test.ts b/src/tests/icusd.test.ts index ff3f9cd..dd029f8 100644 --- a/src/tests/icusd.test.ts +++ b/src/tests/icusd.test.ts @@ -7,7 +7,7 @@ import { wei, } from './utils' -const { eth, icusd, usdc } = QuoteTokens +const { icusd, usdc } = QuoteTokens describe('icUSD (mainnet)', () => { const indexToken = icusd @@ -40,7 +40,7 @@ describe('icUSD (mainnet)', () => { await factory.fetchQuote({ isMinting: false, inputToken: indexToken, - outputToken: eth, + outputToken: usdc, indexTokenAmount: wei('1'), slippage: 0.5, }) From 6eeff32f253205a75fc21fab64119dd5bf2c0976 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 12:00:52 +0100 Subject: [PATCH 53/58] test: temp skip component swap data --- src/utils/component-swap-data.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 5a2168d..308bc87 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -19,7 +19,7 @@ const indexToken = TheUSDCYieldIndex.address! const usdc = USDC.address! const weth = WETH.address! -describe('getIssuanceComponentSwapData()', () => { +describe.skip('getIssuanceComponentSwapData()', () => { test('returns correct swap data based on input token USDC', async () => { const componentSwapData = await getIssuanceComponentSwapData( { From e8d3b8636c4e564cb237dd90264a0031cb498d94 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 12:03:19 +0100 Subject: [PATCH 54/58] skip --- src/utils/component-swap-data.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 308bc87..e68858a 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -84,7 +84,7 @@ describe.skip('getIssuanceComponentSwapData()', () => { }) }) -describe('getRedemptionComponentSwapData()', () => { +describe.skip('getRedemptionComponentSwapData()', () => { test('returns correct swap data based for output token USDC', async () => { const componentSwapData = await getRedemptionComponentSwapData( { From 8972d42cc38fe34ebdaa67b466d36d8480768c63 Mon Sep 17 00:00:00 2001 From: JD Date: Thu, 17 Oct 2024 14:50:10 +0100 Subject: [PATCH 55/58] readd component swap data tests --- src/utils/component-swap-data.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index e68858a..5a2168d 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -19,7 +19,7 @@ const indexToken = TheUSDCYieldIndex.address! const usdc = USDC.address! const weth = WETH.address! -describe.skip('getIssuanceComponentSwapData()', () => { +describe('getIssuanceComponentSwapData()', () => { test('returns correct swap data based on input token USDC', async () => { const componentSwapData = await getIssuanceComponentSwapData( { @@ -84,7 +84,7 @@ describe.skip('getIssuanceComponentSwapData()', () => { }) }) -describe.skip('getRedemptionComponentSwapData()', () => { +describe('getRedemptionComponentSwapData()', () => { test('returns correct swap data based for output token USDC', async () => { const componentSwapData = await getRedemptionComponentSwapData( { From a2c3464afc656866e3b3d5a8a798e37e522e31e6 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 18 Oct 2024 08:58:55 +0100 Subject: [PATCH 56/58] remove fixme --- src/quote/flashmint/wrapped/provider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts index 27e42e4..35d4855 100644 --- a/src/quote/flashmint/wrapped/provider.ts +++ b/src/quote/flashmint/wrapped/provider.ts @@ -94,7 +94,6 @@ export class WrappedQuoteProvider componentSwapData ) } - // FIXME: add slippage? const inputOutputTokenAmount = slippageAdjustedTokenAmount( estimatedInputOutputAmount, isMinting ? inputToken.decimals : outputToken.decimals, From 6b2b74b27a9b8a37d8cfddd506718517c8d65742 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 18 Oct 2024 09:09:22 +0100 Subject: [PATCH 57/58] add integration names --- src/utils/wrap-data.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts index 128d546..11a47a3 100644 --- a/src/utils/wrap-data.ts +++ b/src/utils/wrap-data.ts @@ -1,17 +1,20 @@ import { TheUSDCYieldIndex } from 'constants/tokens' +enum IntegrationName { + aaveV2WrapV2AdapterName = 'AaveV2WrapV2Adapter', + aaveV3WrapV2AdapterName = 'AaveV3WrapV2Adapter', + compoundV3UsdcWrapV2AdapterName = 'CompoundV3WrapV2Adapter', + erc4626WrapV2AdapterName = 'ERC4626WrapV2Adapter', + wrapModuleV2ContractName = 'WrapModuleV2', +} + export interface ComponentWrapData { // wrap adapter integration name as listed in the IntegrationRegistry for the wrapModule - integrationName: string + integrationName: IntegrationName | '' // optional wrapData passed to the wrapAdapter wrapData: string } -// FIXME: check adapter names -// const aaveV2WrapV2AdapterName = 'Aave_V2_Wrap_V2_Adapter' -// const aaveV3WrapV2AdapterName = 'Aave_V3_Wrap_V2_Adapter' -// const compoundV3WrapV2AdapterName = 'Compound_V3_USDC_Wrap_V2_Adapter' -// const erc4626WrapV2AdapterName = 'ERC4626_Wrap_V2_Adapter' const ZERO_BYTES = '0x0000000000000000000000000000000000000000' export function getWrapData(tokenSymbol: string): ComponentWrapData[] { From f7f39e5a4ccd942a25ce2e599ebf110f13e8ed3d Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 18 Oct 2024 09:10:18 +0100 Subject: [PATCH 58/58] refactor: delete obsolete name --- src/utils/wrap-data.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/wrap-data.ts b/src/utils/wrap-data.ts index 11a47a3..dc6fdc5 100644 --- a/src/utils/wrap-data.ts +++ b/src/utils/wrap-data.ts @@ -1,11 +1,11 @@ import { TheUSDCYieldIndex } from 'constants/tokens' enum IntegrationName { - aaveV2WrapV2AdapterName = 'AaveV2WrapV2Adapter', - aaveV3WrapV2AdapterName = 'AaveV3WrapV2Adapter', - compoundV3UsdcWrapV2AdapterName = 'CompoundV3WrapV2Adapter', - erc4626WrapV2AdapterName = 'ERC4626WrapV2Adapter', - wrapModuleV2ContractName = 'WrapModuleV2', + aaveV2WrapV2Adapter = 'AaveV2WrapV2Adapter', + aaveV3WrapV2Adapter = 'AaveV3WrapV2Adapter', + compoundV3UsdcWrapV2Adapter = 'CompoundV3WrapV2Adapter', + erc4626WrapV2Adapter = 'ERC4626WrapV2Adapter', + wrapModuleV2Contract = 'WrapModuleV2', } export interface ComponentWrapData {