1
0
forked from mico/idle_moloch
idle_moloch/lib/swap-router-contracts/test/TokenValidator.spec.ts

159 lines
5.4 KiB
TypeScript

import { expect } from 'chai'
import { constants } from 'ethers'
import hre, { ethers } from 'hardhat'
import { TokenValidator, TestERC20, IUniswapV2Pair__factory } from '../typechain'
describe('TokenValidator', function () {
let tokenValidator: TokenValidator
let testToken: TestERC20
this.timeout(100000)
enum Status {
UNKN = 0,
FOT = 1,
STF = 2,
}
// WETH9 and USDC
const BASE_TOKENS = ['0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48']
// Arbitrary amount to flash loan.
const AMOUNT_TO_BORROW = 1000
const FOT_TOKENS = [
'0xa68dd8cb83097765263adad881af6eed479c4a33', // WTF
'0x8B3192f5eEBD8579568A2Ed41E6FEB402f93f73F', // SAITAMA
'0xA2b4C0Af19cC16a6CfAcCe81F192B024d625817D', // KISHU
]
const BROKEN_TOKENS = [
'0xd233d1f6fd11640081abb8db125f722b5dc729dc', // USD
]
const NON_FOT_TOKENS = [
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
'0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', // UNI
'0xc00e94Cb662C3520282E6f5717214004A7f26888', // COMP
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH9
]
before(async function () {
// Easiest to test FOT using real world data, so these tests require a hardhat fork.
if (!process.env.ARCHIVE_RPC_URL) {
this.skip()
}
await hre.network.provider.request({
method: 'hardhat_reset',
params: [
{
forking: {
jsonRpcUrl: process.env.ARCHIVE_RPC_URL,
blockNumber: 14024832,
},
},
],
})
const factory = await ethers.getContractFactory('TokenValidator')
tokenValidator = (await factory.deploy(
'0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f', // V2 Factory
'0xC36442b4a4522E871399CD717aBDD847Ab11FE88' // V3 NFT position manager
)) as TokenValidator
// Deploy a new token for testing.
const tokenFactory = await ethers.getContractFactory('TestERC20')
testToken = (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20
})
after(async () => {
// Disable mainnet forking to avoid effecting other tests.
await hre.network.provider.request({
method: 'hardhat_reset',
params: [],
})
})
it('succeeds for tokens that cant be transferred', async () => {
for (const token of BROKEN_TOKENS) {
const isFot = await tokenValidator.callStatic.validate(token, BASE_TOKENS, AMOUNT_TO_BORROW)
expect(isFot).to.equal(Status.STF)
}
})
it('succeeds to detect fot tokens', async () => {
for (const token of FOT_TOKENS) {
const isFot = await tokenValidator.callStatic.validate(token, [BASE_TOKENS[0]!], AMOUNT_TO_BORROW)
expect(isFot).to.equal(Status.FOT)
}
})
it('succeeds to detect fot token when token doesnt have pair with first base token', async () => {
const isFot = await tokenValidator.callStatic.validate(
FOT_TOKENS[0],
[testToken.address, ...BASE_TOKENS],
AMOUNT_TO_BORROW
)
expect(isFot).to.equal(Status.FOT)
})
it('succeeds to return unknown when flash loaning full reserves', async () => {
const pairAddress = '0xab293dce330b92aa52bc2a7cd3816edaa75f890b' // WTF/ETH pair
const pair = IUniswapV2Pair__factory.connect(pairAddress, ethers.provider)
const { reserve0: wtfReserve } = await pair.callStatic.getReserves()
const isFot1 = await tokenValidator.callStatic.validate(
'0xa68dd8cb83097765263adad881af6eed479c4a33', // WTF
['0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'], // WETH
wtfReserve.sub(1).toString()
)
expect(isFot1).to.equal(Status.FOT)
const isFot2 = await tokenValidator.callStatic.validate(
'0xa68dd8cb83097765263adad881af6eed479c4a33', // WTF
['0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'], // WETH
wtfReserve.toString()
)
expect(isFot2).to.equal(Status.UNKN)
})
it('succeeds to batch detect fot tokens', async () => {
const isFots = await tokenValidator.callStatic.batchValidate(FOT_TOKENS, BASE_TOKENS, AMOUNT_TO_BORROW)
expect(isFots.every((isFot: Status) => isFot == Status.FOT)).to.be.true
})
it('succeeds to batch detect fot tokens when dont have pair with first base token', async () => {
const isFots = await tokenValidator.callStatic.batchValidate(
FOT_TOKENS,
[testToken.address, ...BASE_TOKENS],
AMOUNT_TO_BORROW
)
expect(isFots.every((isFot: Status) => isFot == Status.FOT)).to.be.true
})
it('succeeds to detect non fot tokens', async () => {
for (const token of NON_FOT_TOKENS) {
const isFot = await tokenValidator.callStatic.validate(token, BASE_TOKENS, AMOUNT_TO_BORROW)
expect(isFot).to.equal(Status.UNKN)
}
})
it('succeeds to batch detect non fot tokens', async () => {
const isFots = await tokenValidator.callStatic.batchValidate(NON_FOT_TOKENS, BASE_TOKENS, AMOUNT_TO_BORROW)
expect(isFots.every((isFot: Status) => isFot == Status.UNKN)).to.be.true
})
it('succeeds to batch detect mix of fot tokens and non fot tokens', async () => {
const isFots = await tokenValidator.callStatic.batchValidate(
[NON_FOT_TOKENS[0], FOT_TOKENS[0], BROKEN_TOKENS[0]],
BASE_TOKENS,
1000
)
expect(isFots).to.deep.equal([Status.UNKN, Status.FOT, Status.STF])
})
it('succeeds to return false if token doesnt have a pool with any of the base tokens', async () => {
await tokenValidator.callStatic.validate(testToken.address, BASE_TOKENS, AMOUNT_TO_BORROW)
})
})