Skip to content

Commit

Permalink
feat: add leverage extended builder
Browse files Browse the repository at this point in the history
  • Loading branch information
janndriessen committed Apr 24, 2024
1 parent 29a4c05 commit f6a197c
Show file tree
Hide file tree
Showing 6 changed files with 426 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env.default
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
INDEX_0X_API=
INDEX_0X_API_KEY=
ARBITRUM_ALCHEMY_APIhttps://arb-mainnet.g.alchemy.com/v2/[key]
MAINNET_ALCHEMY_API=https://eth-mainnet.alchemyapi.io/v2/[key]
ZEROEX_API_KEY=
14 changes: 14 additions & 0 deletions hardhat.arbitrum.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require('dotenv').config()

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: '0.8.17',
networks: {
hardhat: {
chainId: 42161,
forking: {
url: process.env.ARBITRUM_ALCHEMY_API,
},
},
},
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
"build:watch": "npm run build -- --watch src",
"hardhat": "npx hardhat node",
"hardhat:arbitrum": "npx hardhat node --config hardhat.arbitrum.config.js",
"lint": "prettier -c . && eslint ./src",
"lint:fix": "prettier -w . && eslint ./src --fix",
"test": "jest",
Expand Down
1 change: 1 addition & 0 deletions src/flashmint/builders/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './interface'
export * from './leveraged'
export * from './leveraged-extended'
export * from './zeroex'
257 changes: 257 additions & 0 deletions src/flashmint/builders/leveraged-extended.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import { BigNumber } from '@ethersproject/bignumber'

import { ChainId } from 'constants/chains'
import { FlashMintLeveragedExtendedAddress } from 'constants/contracts'
import { IndexCoopEthereum2xIndex } from 'constants/tokens'
import {
collateralDebtSwapData,
debtCollateralSwapData,
inputSwapData,
outputSwapData,
} from 'constants/swapdata'
import { LocalhostProvider, QuoteTokens } from 'tests/utils'
import { getFlashMintLeveragedContractForToken } from 'utils/contracts'
import { wei } from 'utils/numbers'

import {
FlashMintLeveragedExtendedBuildRequest,
LeveragedExtendedTransactionBuilder,
} from './leveraged-extended'

const chainId = ChainId.Arbitrum
const provider = LocalhostProvider

const { eth2x, usdc } = QuoteTokens

Check warning on line 24 in src/flashmint/builders/leveraged-extended.test.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'eth2x' is assigned a value but never used

Check warning on line 24 in src/flashmint/builders/leveraged-extended.test.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'usdc' is assigned a value but never used

const eth = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
const usdcAddress = '0xaf88d065e77c8cC2239327C5EDb3A432268e5831'

describe('LeveragedTransactionBuilder()', () => {
const contract = getFlashMintLeveragedContractForToken(
IndexCoopEthereum2xIndex.symbol,
provider,
chainId
)

beforeEach((): void => {
jest.setTimeout(10000000)
})

test('returns null for invalid request (no index token)', async () => {
const buildRequest = createBuildRequest()
buildRequest.isMinting = true
buildRequest.outputToken = ''
const builder = new LeveragedExtendedTransactionBuilder(provider)
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 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 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 tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

test('returns null for invalid request (invalid swap data debt collateral - no pool)', async () => {
const buildRequest = createBuildRequest()
buildRequest.swapDataDebtCollateral = {
exchange: 1,
path: ['', ''],
fees: [],
pool: '',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

test('returns null for invalid request (invalid swap data input/output token - no paths)', async () => {
const buildRequest = createBuildRequest()
buildRequest.swapDataInputOutputToken = {
exchange: 1,
path: [],
fees: [],
pool: '0x0000000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

test('returns null for invalid request (invalid swap data input/output token - univ3 fees)', async () => {
const buildRequest = createBuildRequest()
buildRequest.swapDataInputOutputToken = {
exchange: 3,
path: ['', '', ''],
// For UniV3 fees.length has to be path.length - 1
fees: [3000],
pool: '0x0000000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

test('returns null for invalid request (invalid path - exchange type none)', async () => {
const buildRequest = createBuildRequest()
buildRequest.swapDataInputOutputToken = {
exchange: 0,
path: [],
fees: [500],
pool: '0x00000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
expect(tx).toBeNull()
})

// TODO: check
test.skip('returns tx for correct swap data with exchange type none', async () => {
const buildRequest = createBuildRequest()
buildRequest.swapDataInputOutputToken = {
exchange: 0,
path: [],
fees: [500],
pool: '0x0000000000000000000000000000000000000000',
}
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
expect(tx).not.toBeNull()
})

// FIXME:
test.skip('returns a tx for minting ETH2X (ERC20)', async () => {
const buildRequest = createBuildRequest()
buildRequest.isMinting = true
const indexToken = buildRequest.outputToken
// TODO: figure out
const swapDataInputTokenForETH = inputSwapData['icETH']['ETH']
const priceEstimateInflator = BigNumber.from(0)
const maxDust = BigNumber.from(0)
const refTx = await contract.populateTransaction.issueSetFromExactERC20(
indexToken,
buildRequest.outputTokenAmount,
buildRequest.inputToken,
buildRequest.inputTokenAmount,
buildRequest.swapDataDebtCollateral,
buildRequest.swapDataInputOutputToken,
swapDataInputTokenForETH,
priceEstimateInflator,
maxDust
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
expect(tx.data).toEqual(refTx.data)
})

test('returns a tx for minting ETH2X (ETH)', async () => {
const buildRequest = createBuildRequest(true, eth, 'ETH')
const indexToken = buildRequest.outputToken
const priceEstimateInflator = BigNumber.from(0)
const maxDust = BigNumber.from(0)
const refTx = await contract.populateTransaction.issueSetFromExactETH(
indexToken,
buildRequest.outputTokenAmount,
buildRequest.swapDataDebtCollateral,
buildRequest.swapDataInputOutputToken,
priceEstimateInflator,
maxDust,
{ value: buildRequest.inputTokenAmount }
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
expect(tx.data).toEqual(refTx.data)
expect(tx.value).toEqual(buildRequest.inputTokenAmount)
})

test('returns a tx for redeeming ETH2X (ERC20)', async () => {
const buildRequest = createBuildRequest(
false,
IndexCoopEthereum2xIndex.addressArbitrum!,

Check warning on line 192 in src/flashmint/builders/leveraged-extended.test.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Forbidden non-null assertion
IndexCoopEthereum2xIndex.symbol,
usdcAddress,
'USDC'
)
const refTx = await contract.populateTransaction.redeemExactSetForERC20(
buildRequest.inputToken,
buildRequest.inputTokenAmount,
buildRequest.outputToken,
buildRequest.outputTokenAmount,
buildRequest.swapDataDebtCollateral,
buildRequest.swapDataInputOutputToken
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
expect(tx.data).toEqual(refTx.data)
})

test('returns a tx for redeeming ETH2X (ETH)', async () => {
const buildRequest = createBuildRequest(
false,
IndexCoopEthereum2xIndex.addressArbitrum!,

Check warning on line 215 in src/flashmint/builders/leveraged-extended.test.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Forbidden non-null assertion
IndexCoopEthereum2xIndex.symbol,
eth,
'ETH'
)
const refTx = await contract.populateTransaction.redeemExactSetForETH(
buildRequest.inputToken,
buildRequest.inputTokenAmount,
buildRequest.outputTokenAmount,
buildRequest.swapDataDebtCollateral,
buildRequest.swapDataInputOutputToken
)
const builder = new LeveragedExtendedTransactionBuilder(provider)
const tx = await builder.build(buildRequest)
if (!tx) fail()
expect(tx.to).toBe(FlashMintLeveragedExtendedAddress)
expect(tx.data).toEqual(refTx.data)
})
})

function createBuildRequest(
isMinting = true,
inputToken: string = usdcAddress,
inputTokenSymbol = 'USDC',
outputToken: string = IndexCoopEthereum2xIndex.addressArbitrum!,

Check warning on line 239 in src/flashmint/builders/leveraged-extended.test.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Forbidden non-null assertion
outputTokenSymbol: string = IndexCoopEthereum2xIndex.symbol
): FlashMintLeveragedExtendedBuildRequest {
return {
isMinting,
inputToken,
inputTokenSymbol,
outputToken,
outputTokenSymbol,
inputTokenAmount: wei(1),
outputTokenAmount: BigNumber.from(194235680),
swapDataDebtCollateral: isMinting
? collateralDebtSwapData['icETH']
: debtCollateralSwapData['icETH'],
swapDataInputOutputToken: isMinting
? inputSwapData['icETH']['ETH']
: outputSwapData['icETH']['ETH'],
}
}
Loading

0 comments on commit f6a197c

Please sign in to comment.