You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
exchange-contracts/test/BridgeMigrationRouterRouter.ts

321 lines
20 KiB

import { expect } from "chai"
import { ethers } from "hardhat"
import {
getOwnerAccount,
makeAccountGenerator,
fundLiquidityToken,
getTokenContract,
getPairContract,
getWKLCContract,
getDeadline,
fundToken,
fundWKLC
} from "./utils"
import fixture from './fixture'
import {run} from "hardhat"
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"
import { Contract } from "@ethersproject/contracts"
import { BigNumber, ContractFactory } from "ethers"
describe("BridgeMigrationRouter", async function() {
let accountGenerator: ()=>SignerWithAddress
let owner: SignerWithAddress
let account: SignerWithAddress
let WKLC: Contract
let factory: ContractFactory
let migrationRouter: Contract
type MigratorTokenSymbol = keyof typeof fixture.Migrators
type TokenSymbol = keyof typeof fixture.Tokens
type KLCPairsTokenSymbol = keyof typeof fixture.Pairs.KLC
type KLCMigratedPairsTokenSymbol = keyof typeof fixture.Pairs.Migrated.KLC
type KSWAPPairsTokenSymbol = keyof typeof fixture.Pairs.KSWAP
type KSWAPMigratedPairsTokenSymbol = keyof typeof fixture.Pairs.Migrated.KSWAP
before(async () => {
await run("compile")
accountGenerator = await makeAccountGenerator()
const bridgeTokenFactory = await ethers.getContractFactory("BridgeToken")
owner = await getOwnerAccount()
WKLC = await getWKLCContract()
const factory = await ethers.getContractAt("KalyswapFactory", fixture.Factory)
const router = await ethers.getContractAt("KalyswapRouter", fixture.Router)
await fundWKLC(owner, BigNumber.from(10).pow(28))
await fundToken(owner, fixture.Tokens.KSWAP, BigNumber.from(10).pow(25))
await (await getTokenContract(fixture.Tokens.WKLC)).approve(router.address, ethers.constants.MaxUint256)
await (await getTokenContract(fixture.Tokens.KSWAP)).approve(router.address, ethers.constants.MaxUint256)
// in case we don't have the migrator deployed, it deploys migrators
for(let tokenSymbol of Object.keys(fixture.Migrators)) {
let tokenAddress = fixture.Tokens[tokenSymbol as TokenSymbol]
if (fixture.Migrators[tokenSymbol as MigratorTokenSymbol] !== "") {
continue
}
let bridgeToken = await bridgeTokenFactory.deploy()
await bridgeToken.deployed
await bridgeToken.addSwapToken(tokenAddress, ethers.constants.MaxUint256)
fixture.Migrators[tokenSymbol as MigratorTokenSymbol] = bridgeToken.address
await bridgeToken.connect(owner).mint(
owner.address,
BigNumber.from("1000000000000000000000000000000000"),
"0xc7198437980c041c805a1edcba50c1ce5db95118", 0,
ethers.utils.formatBytes32String("test")
)
await bridgeToken.connect(owner).approve(router.address, ethers.constants.MaxUint256)
}
// if there's no pairs in the migrators, create the pairs and add liquidity for KLC pairs
for(let tokenSymbol of Object.keys(fixture.Pairs.Migrated.KLC)) {
let tokenAddress = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
if (!tokenAddress) continue
if (fixture.Pairs.Migrated.KLC[tokenSymbol as KLCMigratedPairsTokenSymbol] !== "") {
continue
}
const price = BigNumber.from("1000000000000000000")
if (await factory.getPair(fixture.Tokens.WKLC, tokenAddress) == ethers.constants.AddressZero) {
await router.connect(owner).addLiquidity(
fixture.Tokens.WKLC, tokenAddress,
price, price.mul(2), price, price.mul(2),
owner.address, getDeadline()
)
}
fixture.Pairs.Migrated.KLC[tokenSymbol as KLCMigratedPairsTokenSymbol] = await factory.getPair(fixture.Tokens.WKLC, tokenAddress)
}
// if there's no pairs in the migrators, create the pairs and add liquidity for KSWAP pairs
for(let tokenSymbol of Object.keys(fixture.Pairs.Migrated.KSWAP)) {
let tokenAddress = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
if (!tokenAddress) continue
if (fixture.Pairs.Migrated.KSWAP[tokenSymbol as KSWAPMigratedPairsTokenSymbol] !== "") {
continue
}
const price = BigNumber.from("1000000000000000000")
await router.connect(owner).addLiquidity(
fixture.Tokens.KSWAP, tokenAddress,
price, price.mul(2), price, price.mul(2),
owner.address, getDeadline()
)
let bn0 = BigNumber.from(fixture.Tokens.KSWAP)
let bn1 = BigNumber.from(tokenAddress)
let [token0, token1] = bn0.gt(bn1) ? [bn1.toHexString(), bn0.toHexString()] : [bn0.toHexString(), bn1.toHexString()]
fixture.Pairs.Migrated.KSWAP[tokenSymbol as KSWAPMigratedPairsTokenSymbol] = await factory.getPair(token0, token1)
}
})
beforeEach(async () => {
account = accountGenerator()
//this is necessary, the asserts funds the account on the assumption it has 0 WKLC
await WKLC.connect(account).withdraw(await WKLC.balanceOf(account.address))
factory = await ethers.getContractFactory("KalyswapBridgeMigrationRouter")
migrationRouter = await factory.connect(owner).deploy()
await migrationRouter.deployed()
await fundWKLC(account, BigNumber.from(10).pow(26))
})
describe("Administration", async function() {
it("Can be deployed", async function() {
expect(await migrationRouter.connect(owner).isAdmin(owner.address)).to.be.true
})
it("Admin can add admin", async function() {
await migrationRouter.connect(owner).addAdmin(account.address)
expect(await migrationRouter.connect(owner).isAdmin(account.address)).to.be.true
})
it("Admin can remove admin", async function() {
await migrationRouter.connect(owner).addAdmin(account.address)
expect(await migrationRouter.connect(owner).isAdmin(account.address)).to.be.true
await migrationRouter.connect(owner).removeAdmin(account.address)
expect(await migrationRouter.connect(owner).isAdmin(account.address)).to.be.false
})
it("Others can't add admin", async function() {
expect(await migrationRouter.connect(owner).isAdmin(account.address)).to.be.false
await expect(migrationRouter.connect(account).addAdmin(account.address)).to.be.reverted
expect(await migrationRouter.isAdmin(account.address)).to.be.false
})
it("Others can't remove admin", async function() {
expect(await migrationRouter.connect(owner).isAdmin(account.address)).to.be.false
await expect(migrationRouter.connect(account).removeAdmin(owner.address)).to.be.reverted
expect(await migrationRouter.isAdmin(owner.address)).to.be.true
})
it("Others can't check admin", async function() {
expect(await migrationRouter.connect(owner).isAdmin(account.address)).to.be.false
expect(await migrationRouter.connect(account).isAdmin(owner.address)).to.be.true
})
it("Admin can't add migrator incompatible with the token", async function() {
await expect(migrationRouter.connect(owner).addMigrator(fixture.Tokens.WKLC, fixture.Migrators.WBTC)).to.be.reverted
})
for(let tokenSymbol of Object.keys(fixture.Migrators)) {
let tokenAddress = fixture.Tokens[tokenSymbol as TokenSymbol]
let migrator = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
it(`Others can't add migrator for ${tokenSymbol}`, async function() {
await expect(migrationRouter.connect(account).addMigrator(tokenAddress, migrator)).to.be.reverted
expect((await migrationRouter.bridgeMigrator(tokenAddress)).toString().toLowerCase()).to.equal(ethers.constants.AddressZero)
})
}
for(let tokenSymbol of Object.keys(fixture.Migrators)) {
let tokenAddress = fixture.Tokens[tokenSymbol as TokenSymbol]
it(`Admin can add migrator for ${tokenSymbol}`, async function() {
let migrator = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
await migrationRouter.connect(owner).addMigrator(tokenAddress, migrator)
expect((await migrationRouter.bridgeMigrator(tokenAddress)).toString().toLowerCase()).to.equal(migrator.toLowerCase())
})
}
})
describe("Token Migration", async function() {
for(let tokenSymbol of Object.keys(fixture.Migrators)) {
let tokenAddress = fixture.Tokens[tokenSymbol as TokenSymbol]
it(`Any can migrate for token ${tokenSymbol}`, async function() {
let migrator = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
await migrationRouter.connect(owner).addMigrator(tokenAddress, migrator)
expect((await migrationRouter.bridgeMigrator(tokenAddress)).toString().toLowerCase()).to.equal(migrator.toLowerCase())
//fund the token and allow the contract to spend on the "account"s behalf
let tokenAmount = await fundToken(account, tokenAddress, BigNumber.from(10).pow(18))
let tokenContract = await getTokenContract(tokenAddress)
await tokenContract.connect(account).approve(migrationRouter.address, ethers.constants.MaxInt256)
await migrationRouter.connect(account).migrateToken(tokenAddress, account.address, tokenAmount, getDeadline())
let migratedTokenAddress = await migrationRouter.bridgeMigrator(tokenAddress)
let migratedToken = await getTokenContract(migratedTokenAddress)
expect(await migratedToken.balanceOf(account.address)).to.be.equal(tokenAmount)
})
}
})
describe("Liquidity Migration", async function() {
describe("KLC", async function () {
for(let tokenSymbol of Object.keys(fixture.Migrators)) {
let tokenAddress = fixture.Tokens[tokenSymbol as TokenSymbol]
if (!((tokenSymbol as KLCPairsTokenSymbol) in fixture.Pairs.KLC)) continue
if (fixture.TokensWithoutFund.includes(tokenSymbol)) continue
it(`Can migrate liquidity from KLC-${tokenSymbol}`, async function() {
let pairAddress = fixture.Pairs.KLC[tokenSymbol as KLCPairsTokenSymbol]
let toPairAddress = fixture.Pairs.Migrated.KLC[tokenSymbol as KLCMigratedPairsTokenSymbol]
let migrator = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
let fromPairContract = await getTokenContract(pairAddress)
await migrationRouter.connect(owner).addMigrator(tokenAddress, migrator)
let liquidityAmount = await fundLiquidityToken(account, pairAddress, BigNumber.from(10).pow(20))
await fromPairContract.connect(account).approve(migrationRouter.address, ethers.constants.MaxUint256)
let migratedPair = await getPairContract(toPairAddress);
expect(await migratedPair.balanceOf(account.address)).to.equal(0)
await migrationRouter.connect(account).migrateLiquidity(pairAddress, toPairAddress, account.address, liquidityAmount, getDeadline())
expect(await migratedPair.balanceOf(account.address)).to.gt(0)
//makes sure there's no dust left in the migration router
let token0 = await getTokenContract(await migratedPair.token0())
let token1 = await getTokenContract(await migratedPair.token1())
expect(await token0.balanceOf(migrationRouter.address)).to.equal(0)
expect(await token1.balanceOf(migrationRouter.address)).to.equal(0)
expect(await migratedPair.balanceOf(migrationRouter.address)).to.equal(0)
expect(await fromPairContract.balanceOf(migrationRouter.address)).to.equal(0)
})
it(`Can compute accurately chargeback from KLC-${tokenSymbol}`, async function() {
let pairAddress = fixture.Pairs.KLC[tokenSymbol as KLCPairsTokenSymbol]
let toPairAddress = fixture.Pairs.Migrated.KLC[tokenSymbol as KLCMigratedPairsTokenSymbol]
let migrator = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
await migrationRouter.connect(owner).addMigrator(tokenAddress, migrator)
let liquidityAmount = await fundLiquidityToken(account, pairAddress, BigNumber.from(10).pow(20))
let toPairContract = await getPairContract(toPairAddress)
// reads the chargeback
let [chargeBack0, chargeBack1] = await migrationRouter.calculateChargeBack(pairAddress, toPairAddress, liquidityAmount)
// reads the previous balance to compute the real charge back
let token0 = await getTokenContract(await toPairContract.token0())
let token1 = await getTokenContract(await toPairContract.token1())
let previousBalance0 = await token0.balanceOf(account.address)
let previousBalance1 = await token1.balanceOf(account.address)
let tokenContract = await getTokenContract(pairAddress)
await tokenContract.connect(account).approve(migrationRouter.address, ethers.constants.MaxUint256)
expect(await tokenContract.balanceOf(account.address)).to.equal(liquidityAmount)
expect(await toPairContract.balanceOf(account.address)).to.equal(0)
await migrationRouter.connect(account).migrateLiquidity(pairAddress, toPairAddress, account.address, liquidityAmount, getDeadline())
expect(await toPairContract.balanceOf(account.address)).to.gt(0)
// computes the real charge back and verify that they match exactly
let balance0 = await token0.balanceOf(account.address)
let balance1 = await token1.balanceOf(account.address)
let expectedChargeBack0 = balance0.gte(previousBalance0) ? balance0.sub(previousBalance0) : previousBalance0.sub(balance0)
let expectedChargeBack1 = balance1.gte(previousBalance1) ? balance1.sub(previousBalance1) : previousBalance1.sub(balance1)
expect(chargeBack0).to.equal(expectedChargeBack0)
expect(chargeBack1).to.equal(expectedChargeBack1)
})
}
})
describe("KSWAP", async function() {
for(let tokenSymbol of Object.keys(fixture.Migrators)) {
let tokenAddress = fixture.Tokens[tokenSymbol as TokenSymbol]
if (!((tokenSymbol as KLCPairsTokenSymbol) in fixture.Pairs.KSWAP)) continue
if (fixture.TokensWithoutFund.includes(tokenSymbol)) continue
it(`Can migrate liquidity from KSWAP-${tokenSymbol}`, async function() {
let pairAddress = fixture.Pairs.KSWAP[tokenSymbol as KSWAPPairsTokenSymbol]
let toPairAddress = fixture.Pairs.Migrated.KSWAP[tokenSymbol as KSWAPPairsTokenSymbol]
let migrator = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
await migrationRouter.connect(owner).addMigrator(tokenAddress, migrator)
let liquidityAmount = await fundLiquidityToken(account, pairAddress, BigNumber.from(10).pow(20))
let fromPairContract = await getTokenContract(pairAddress)
await fromPairContract.connect(account).approve(migrationRouter.address, ethers.constants.MaxUint256)
let migratedPair = await getPairContract(toPairAddress);
expect(await migratedPair.balanceOf(account.address)).to.equal(0)
await migrationRouter.connect(account).migrateLiquidity(pairAddress, toPairAddress, account.address, liquidityAmount, getDeadline())
expect(await migratedPair.balanceOf(account.address)).to.gt(0)
//makes sure there's no dust left in the migration router
let token0 = await getTokenContract(await migratedPair.token0())
let token1 = await getTokenContract(await migratedPair.token1())
expect(await token0.balanceOf(migrationRouter.address)).to.equal(0)
expect(await token1.balanceOf(migrationRouter.address)).to.equal(0)
expect(await migratedPair.balanceOf(migrationRouter.address)).to.equal(0)
expect(await fromPairContract.balanceOf(migrationRouter.address)).to.equal(0)
})
it(`Can compute accurately chargeback from KSWAP-${tokenSymbol}`, async function() {
let pairAddress = fixture.Pairs.KLC[tokenSymbol as KLCPairsTokenSymbol]
let toPairAddress = fixture.Pairs.Migrated.KLC[tokenSymbol as KLCMigratedPairsTokenSymbol]
let migrator = fixture.Migrators[tokenSymbol as MigratorTokenSymbol]
await migrationRouter.connect(owner).addMigrator(tokenAddress, migrator)
let liquidityAmount = await fundLiquidityToken(account, pairAddress, BigNumber.from(10).pow(20))
let toPairContract = await getPairContract(toPairAddress)
// reads the chargeback
let [chargeBack0, chargeBack1] = await migrationRouter.calculateChargeBack(pairAddress, toPairAddress, liquidityAmount)
// reads the previous balance to compute the real charge back
let token0 = await getTokenContract(await toPairContract.token0())
let token1 = await getTokenContract(await toPairContract.token1())
let previousBalance0 = await token0.balanceOf(account.address)
let previousBalance1 = await token1.balanceOf(account.address)
let tokenContract = await getTokenContract(pairAddress)
await tokenContract.connect(account).approve(migrationRouter.address, ethers.constants.MaxUint256)
expect(await tokenContract.balanceOf(account.address)).to.equal(liquidityAmount)
expect(await toPairContract.balanceOf(account.address)).to.equal(0)
await migrationRouter.connect(account).migrateLiquidity(pairAddress, toPairAddress, account.address, liquidityAmount, getDeadline())
expect(await toPairContract.balanceOf(account.address)).to.gt(0)
// computes the real charge back and verify that they match exactly
let balance0 = await token0.balanceOf(account.address)
let balance1 = await token1.balanceOf(account.address)
let expectedChargeBack0 = balance0.gte(previousBalance0) ? balance0.sub(previousBalance0) : previousBalance0.sub(balance0)
let expectedChargeBack1 = balance1.gte(previousBalance1) ? balance1.sub(previousBalance1) : previousBalance1.sub(balance1)
expect(chargeBack0).to.equal(expectedChargeBack0)
expect(chargeBack1).to.equal(expectedChargeBack1)
})
}
})
})
})