Skip to content

Commit

Permalink
Merge pull request #59 from IndexCoop/feat/add-0x-swap-data
Browse files Browse the repository at this point in the history
feat: add 0x swap data
  • Loading branch information
janndriessen authored May 24, 2024
2 parents e877433 + 1bc68e5 commit 0b9f943
Show file tree
Hide file tree
Showing 64 changed files with 1,172 additions and 1,300 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- run: npm ci
- run: npm run lint
- run: npm run build --if-present
# - run: npm run hardhat:arbitrum & npm run test:arbitrum
- run: npm run hardhat:arbitrum & npm run test:arbitrum
- run: npm run hardhat & npm run test:utils
- run: npm run hardhat & npm run test:builders
- run: npm run hardhat & npm run test:quote
Expand All @@ -45,4 +45,5 @@ jobs:
- run: npm run hardhat & npm run test:iceth
# - run: npm run hardhat & npm run test:icreth
# run last - as it alters the block number
- run: npm run hardhat & npm run test:eth2xfli
# skip as it can't be minted or redeemed with 0x
# - run: npm run hardhat & npm run test:eth2xfli
75 changes: 24 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Flash Mint SDK v2
# Flash Mint SDK v3

The Flash Mint SDK provides easy to use functions to integrate flashminting for
The Flash Mint SDK provides easy to use functions to integrate flash minting for
Index's products.

## The Contracts
Expand Down Expand Up @@ -29,27 +29,35 @@ specific FlashMint contracts.

### Quotes

In v2 we made it way easier to fetch a quote. Meet the `FlashMintQuoteProvider`.
This provider will now return the appropriate quotes for any Index token, automatically
selecting the correct FlashMint contract for you.
With v3, while you could still use other quote providers individually, we recommend
solely using the `FlashMintQuoteProvider` which will do most of the guess work for you.
This provider will return the appropriate quotes for any Index token, automatically
selecting the correct FlashMint contract for you - as well as preparing the call data.

```typescript
import { FlashMintQuoteProvider } from '@indexcoop/flash-mint-sdk'
import { FlashMintQuoteProvider, QuoteToken } from '@indexcoop/flash-mint-sdk'

// Input/output token should be of type QuoteToken with the following properties
const inputToken = {
const inputToken: QuoteToken = {
symbol: 'ETH',
decimals: 18,
address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
}
const outputToken = {
const outputToken: QuoteToken = {
symbol: 'icETH',
decimals: 18,
address: '0x7C07F7aBe10CE8e33DC6C5aD68FE033085256A84',
}

const rpcProvider = new JsonRpcProvider('')
const quoteProvider = new FlashMintQuoteProvider(rpcProvider)
// Add a RPC URL e.g. from Alchemy.
const rpcUrl = ''
// Use the 0x swap quote provider configured to your needs to provide your own
// adapter implementing `SwapQuoteProvider`.
const zeroexSwapQuoteProvider = new ZeroExSwapQuoteProvider()
const quoteProvider = new FlashMintQuoteProvider(
rpcUrl,
zeroexSwapQuoteProvider
)
const quote = await quoteProvider.getQuote({
isMinting: true,
inputToken,
Expand Down Expand Up @@ -78,52 +86,17 @@ interface FlashMintQuote {
}
```

You can still fetch quotes for individual FlashMint contracts e.g. using the `ZeroExQuoteProvider`.

```typescript
import { QuoteToken, ZeroExQuoteProvider } from '@indexcoop/flash-mint-sdk'

// Input/output token should be of type QuoteToken with the following properties
const inputToken: QuoteToken = {
symbol: 'ETH',
decimals: 18,
address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
}
const outputToken: QuoteToken = {
symbol: 'dsETH',
decimals: 18,
address: '0x341c05c0E9b33C0E38d64de76516b2Ce970bB3BE',
}
const rpcProvider = new JsonRpcProvider('')
const quoteProvider = new ZeroExQuoteProvider(rpcProvider, zeroExApiV1)
const quote = await quoteProvider.getQuote({
isMinting: true,
inputToken,
outputToken,
indexTokenAmount,
slippage: 0.1,
})
```

The quote providers for the individual FlashMint contracts will return not just
the `inputOutputAmount` but also the `swap data/component quotes`.

```typescript
interface FlashMintZeroExQuote {
componentQuotes: string[]
indexTokenAmount: BigNumber
inputOutputTokenAmount: BigNumber
}
```

## Flashmint

To execute the flashminting of an Index for convenience use the `tx` object
To execute the flash minting of an Index token for convenience use the `tx` object
returned by the `FlashMintQuoteProvider`.

```typescript
...
const quoteProvider = new FlashMintQuoteProvider(rpcProvider)
const quoteProvider = new FlashMintQuoteProvider(
rpcUrl,
zeroexSwapQuoteProvider
)
const quote = await quoteProvider.getQuote({...})
let tx = quote.tx
const gasEstimate = await provider.estimateGas(tx)
Expand All @@ -150,7 +123,7 @@ When adding new .env vars do not forget to update the [publish.yml](.github/work
1. add a test for determining the correct contract [here](./src/utils/contracts.test.ts)
2. if there is a new FlashMint contract, add it as described [below](#adding-a-new-contract)
3. additionally, add a test in [tests](./src/tests/)
4. add symbol to `function getContractType(token: string)` in `FlashMintQuoteProvider`
4. add symbol to `function getContractType(token: string)` in [src/quote/provider/utils.ts](./src/quote//provider/utils.ts) and add a test

### Adding a new contract

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"test": "jest",
"test:arbitrum": "npm test src/flashmint/builders/leveraged-extended.test.ts src/quote/leveraged-extended",
"test:builders": "npm test src/flashmint/builders/leveraged.test.ts src/flashmint/builders/zeroex.test.ts",
"test:quote": "npm test src/quote/leveraged/provider.test.ts src/quote/zeroex src/quote/indexQuoteProvider.test.ts ",
"test:quote": "npm test src/quote/flashmint/leveraged/provider.test.ts src/quote/flashmint/zeroex src/quote/provider/ ",
"test:utils": "npm test src/utils",
"test:btc2x": "npm test src/tests/btc2x.test.ts",
"test:cdeti": "npm test src/tests/cdeti",
Expand Down
2 changes: 1 addition & 1 deletion src/constants/swapdata.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Exchange } from '../utils/swapData'
import { Exchange } from 'utils'
import { ETH, InterestCompoundingETHIndex, stETH } from './tokens'

export const noopSwapData: {
Expand Down
32 changes: 18 additions & 14 deletions src/flashmint/builders/leveraged-extended.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { ChainId } from 'constants/chains'
import { FlashMintLeveragedExtendedAddress } from 'constants/contracts'
import { IndexCoopEthereum2xIndex, USDC, WETH } from 'constants/tokens'
import { noopSwapData } from 'constants/swapdata'
import { LocalhostProviderArbitrum } from 'tests/utils'
import {
LocalhostProviderArbitrum,
LocalhostProviderUrlArbitrum,
} from 'tests/utils'
import { getFlashMintLeveragedContractForToken } from 'utils/contracts'
import { wei } from 'utils/numbers'
import { Exchange } from 'utils'
Expand All @@ -17,6 +20,7 @@ import {

const chainId = ChainId.Arbitrum
const provider = LocalhostProviderArbitrum
const rpcUrl = LocalhostProviderUrlArbitrum

const eth = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
const usdcAddress = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831'
Expand All @@ -36,31 +40,31 @@ describe('LeveragedTransactionBuilder()', () => {
const buildRequest = createBuildRequest()
buildRequest.isMinting = true
buildRequest.outputToken = ''
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

test('returns null for invalid request (no input/output token)', async () => {
const buildRequest = createBuildRequest()
buildRequest.inputToken = ''
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

test('returns null for invalid request (inputTokenAmount = 0)', async () => {
const buildRequest = createBuildRequest()
buildRequest.inputTokenAmount = BigNumber.from(0)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

test('returns null for invalid request (outputTokenAmount = 0)', async () => {
const buildRequest = createBuildRequest()
buildRequest.outputTokenAmount = BigNumber.from(0)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})
Expand All @@ -73,7 +77,7 @@ describe('LeveragedTransactionBuilder()', () => {
fees: [],
pool: '',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})
Expand All @@ -86,7 +90,7 @@ describe('LeveragedTransactionBuilder()', () => {
fees: [],
pool: '0x0000000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})
Expand All @@ -100,7 +104,7 @@ describe('LeveragedTransactionBuilder()', () => {
fees: [3000],
pool: '0x0000000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})
Expand All @@ -113,7 +117,7 @@ describe('LeveragedTransactionBuilder()', () => {
fees: [500],
pool: '0x00000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})
Expand All @@ -126,7 +130,7 @@ describe('LeveragedTransactionBuilder()', () => {
fees: [500],
pool: '0x0000000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
expect(tx).not.toBeNull()
})
Expand All @@ -143,7 +147,7 @@ describe('LeveragedTransactionBuilder()', () => {
buildRequest.swapDataDebtCollateral,
buildRequest.swapDataInputOutputToken
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
Expand All @@ -160,7 +164,7 @@ describe('LeveragedTransactionBuilder()', () => {
buildRequest.swapDataInputOutputToken,
{ value: buildRequest.inputTokenAmount }
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
Expand All @@ -184,7 +188,7 @@ describe('LeveragedTransactionBuilder()', () => {
buildRequest.swapDataDebtCollateral,
buildRequest.swapDataInputOutputToken
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
Expand All @@ -206,7 +210,7 @@ describe('LeveragedTransactionBuilder()', () => {
buildRequest.swapDataDebtCollateral,
buildRequest.swapDataInputOutputToken
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const builder = new LeveragedExtendedTransactionBuilder(rpcUrl)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
Expand Down
13 changes: 7 additions & 6 deletions src/flashmint/builders/leveraged-extended.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { TransactionRequest } from '@ethersproject/abstract-provider'
import { BigNumber } from '@ethersproject/bignumber'
import { JsonRpcProvider } from '@ethersproject/providers'

import { getFlashMintLeveragedContractForToken } from '../../utils/contracts'
import { Exchange, SwapData } from '../../utils/swapData'
import { getFlashMintLeveragedContractForToken } from 'utils/contracts'
import { getRpcProvider } from 'utils/rpc-provider'
import { Exchange, SwapData } from 'utils'

import { TransactionBuilder } from './interface'
import { isEmptyString, isInvalidAmount } from './utils'
Expand All @@ -30,13 +30,14 @@ export class LeveragedExtendedTransactionBuilder
TransactionRequest
>
{
constructor(private readonly provider: JsonRpcProvider) {}
constructor(private readonly rpcUrl: string) {}

async build(
request: FlashMintLeveragedExtendedBuildRequest
): Promise<TransactionRequest | null> {
const isValidRequest = this.isValidRequest(request)
if (!isValidRequest) return null
const provider = getRpcProvider(this.rpcUrl)
const {
inputToken,
inputTokenAmount,
Expand All @@ -50,13 +51,13 @@ export class LeveragedExtendedTransactionBuilder
// priceEstimateInflator,
// maxDust,
} = request
const network = await this.provider.getNetwork()
const network = await provider.getNetwork()
const chainId = network.chainId
const indexToken = isMinting ? outputToken : inputToken
const indexTokenSymbol = isMinting ? outputTokenSymbol : inputTokenSymbol
const contract = getFlashMintLeveragedContractForToken(
indexTokenSymbol,
this.provider,
provider,
chainId
)
if (isMinting) {
Expand Down
Loading

0 comments on commit 0b9f943

Please sign in to comment.