123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- const { ethers } = require("hardhat")
- const { expect } = require("chai")
- const { DEBUG } = require("./constants")
- function print(msg) {
- if (DEBUG) print(msg)
- }
- async function deployStargateContracts(_chainId) {
- const lzEndpointContract = await ethers.getContractFactory("LZEndpointMock") // LayerZeroEndpointMock.sol
- const lzEndpoint = await lzEndpointContract.deploy(_chainId)
- await lzEndpoint.deployed()
- const routerContract = await ethers.getContractFactory("Router") // Router.sol
- const router = await routerContract.deploy()
- await router.deployed()
- const bridgeContract = await ethers.getContractFactory("Bridge") // Bridge.sol
- const bridge = await bridgeContract.deploy(lzEndpoint.address, router.address)
- await bridge.deployed()
- const factoryContract = await ethers.getContractFactory("Factory") // Factory.sol
- const factory = await factoryContract.deploy(router.address)
- await factory.deployed()
- const feeLibraryContract = await ethers.getContractFactory("StargateFeeLibraryV01") // MirrorgateFeeLibraryV01.sol
- const feeLibrary = await feeLibraryContract.deploy(factory.address)
- await feeLibrary.deployed()
- await factory.setDefaultFeeLibrary(feeLibrary.address)
- //set deploy params
- await (await router.setBridgeAndFactory(bridge.address, factory.address)).wait()
- return { factory, router, bridge, lzEndpoint, feeLibrary }
- }
- async function deployPool(_poolContract, _factory, _router, _poolId, _token, _sharedDecimals) {
- //create pool
- await _router.createPool(_poolId, _token.address, _sharedDecimals, await _token.decimals(), "x", "x*")
- return _poolContract.attach(await _factory.getPool(_poolId))
- }
- async function deployToken(_tokenContract, _name, _symbol, _decimals) {
- const token = await _tokenContract.deploy(_name, _symbol, _decimals)
- await token.deployed()
- return token
- }
- async function mintAndApproveFunds(token, signer, amount, approveRouterAddresses) {
- await token.mint(signer.address, amount)
- for (let address of approveRouterAddresses) {
- await token.connect(signer).approve(address, amount)
- }
- }
- async function bridgeStargateEndpoints(stargateEndpoints) {
- for (const i in stargateEndpoints) {
- for (const j in stargateEndpoints) {
- if (i === j) continue
- const stargateSrc = stargateEndpoints[i]
- const stargateDst = stargateEndpoints[j]
- const remoteBridge = await stargateSrc.bridge.bridgeLookup(stargateDst.chainId)
- if (remoteBridge === "0x") {
- // set it if its not set
- await stargateSrc.bridge.setBridge(stargateDst.chainId, stargateDst.bridge.address)
- }
- const destLzEndpoint = await stargateSrc.lzEndpoint.lzEndpointLookup(stargateDst.bridge.address)
- if (destLzEndpoint === "0x0000000000000000000000000000000000000000") {
- // set it if its not set
- await stargateSrc.lzEndpoint.setDestLzEndpoint(stargateDst.bridge.address, stargateDst.lzEndpoint.address)
- }
- }
- }
- }
- class GlobalBook {
- constructor() {
- this.stargateEndpoints = {} //chainId => stargateEndpoint
- this.sharedDecimals = 6
- this.tokenList = []
- }
- async newStargateEndpoint(_newChainId, _name, _poolsParams) {
- const { factory, router, bridge, lzEndpoint, feeLibrary } = await deployStargateContracts(_newChainId, _poolsParams)
- //create poolInfos
- const poolInfos = {} //id => poolInfo
- //contracts deployed per pool
- const poolContract = await ethers.getContractFactory("Pool") //Pool.sol
- const mockTokenContract = await ethers.getContractFactory("MockToken") // MockTokenWithDecimals.sol
- for (const poolParams of _poolsParams) {
- const { poolId: newPoolId, tokenInfo } = poolParams
- const token = await deployToken(mockTokenContract, tokenInfo.name, tokenInfo.symbol, tokenInfo.decimals)
- this.tokenList.push(token)
- const newPool = await deployPool(poolContract, factory, router, newPoolId, token, this.sharedDecimals)
- // connect exiting pools to the new pool
- // for each existing pool
- const chainPaths = []
- for (const dstChainId in this.stargateEndpoints) {
- const dstStargateEndpoint = this.stargateEndpoints[dstChainId]
- //for each dst pool, create chain path to new Pool
- for (const dstPoolId in dstStargateEndpoint.poolInfos) {
- await dstStargateEndpoint.router.createChainPath(dstPoolId, _newChainId, newPoolId, 1)
- await dstStargateEndpoint.router.activateChainPath(dstPoolId, _newChainId, newPoolId)
- dstStargateEndpoint.poolInfos[dstPoolId].chainPaths.push([_newChainId, newPoolId])
- // new -> dst
- await router.createChainPath(newPoolId, dstChainId, dstPoolId, 1)
- await router.activateChainPath(newPoolId, dstChainId, dstPoolId)
- chainPaths.push([dstChainId, dstPoolId])
- }
- }
- poolInfos[newPoolId] = {
- pool: newPool,
- token,
- chainPaths,
- lpProviders: {},
- }
- }
- //assemble and return stargateEndpoint
- const stargateEndpoint = {
- name: _name,
- chainId: _newChainId,
- router,
- bridge,
- lzEndpoint,
- poolInfos,
- feeLibrary,
- }
- this.stargateEndpoints[_newChainId] = stargateEndpoint
- //bridge new stargate with each other
- await bridgeStargateEndpoints(this.stargateEndpoints)
- return stargateEndpoint
- }
- async provisionLiquidity(_signer, _chainId, _poolId, _amountRaw) {
- const amount = this.amountToPoolLD(_amountRaw, _chainId, _poolId)
- const stargateEndpoint = this.stargateEndpoints[_chainId]
- const { chainPaths, lpProviders } = stargateEndpoint.poolInfos[_poolId]
- await stargateEndpoint.router.connect(_signer).addLiquidity(_poolId, amount, _signer.address)
- for (const [dstChainId, dstPoolId] of chainPaths) {
- await stargateEndpoint.router.connect(_signer).sendCredits(dstChainId, _poolId, dstPoolId, _signer.address)
- }
- if (!(_signer.address in lpProviders)) {
- this.stargateEndpoints[_chainId].poolInfos[_poolId].lpProviders[_signer.address] = _signer
- }
- await this.audit()
- }
- async amountLDtoLP(chainId, poolId, amountLD) {
- const stargateEndpoint = this.stargateEndpoints[chainId]
- const { pool } = stargateEndpoint.poolInfos[poolId]
- const totalLiq = await pool.totalLiquidity()
- const totalSup = await pool.totalSupply()
- const convertRate = await pool.convertRate()
- const amountSD = amountLD.div(convertRate)
- return amountSD.mul(totalSup).div(totalLiq)
- }
- async getChainPath(_poolId, _fromChainId, _toChainId, _toPoolId) {
- const stargateSrc = this.stargateEndpoints[_fromChainId]
- const { pool } = stargateSrc.poolInfos[_poolId]
- const cpIndex = await pool.chainPathIndexLookup(_toChainId, _toPoolId)
- return await pool.chainPaths(cpIndex)
- }
- async amountToPoolLD(_amountRaw, _chainId, _poolId) {
- const { token } = this.stargateEndpoints[_chainId].poolInfos[_poolId]
- const decimals = await token.decimals()
- return ethers.BigNumber.from(10).pow(decimals).mul(_amountRaw)
- }
- /*
- CONSTRAINT 1 - IFG for each pool. the total promised liquidity to chainPath is solvent
- => { asset - deltaCredit == sum_over_chainPath (transactionInflight + dst.Balance + src.Credit) }
- CONSTRAINT 2 - global solvency. all LPs can withdraw in full (considering all fees)
- => { sum_over_pool_of_all_chains (asset - nonLiquidityRelatedFees) == sum_over_pool_of_all_chains (totalLiquidity) }
- */
- async audit() {
- /*
- compute global metrics across all chains
- - globalBookedLiquidity = (pool.totalLiquidity) sum by chains
- - globalEstimatedLiquidity = (unallocated liquidity (deltaCredits)
- + allocated liquidity (lkb + credits)) sum by chains
- */
- //for each chain
- for (const srcChainId in this.stargateEndpoints) {
- const { poolInfos } = this.stargateEndpoints[srcChainId]
- // for each pool of the pool
- for (const srcPoolId in poolInfos) {
- //loop over chainpaths of pool
- const { pool, token, chainPaths } = poolInfos[srcPoolId]
- const tokenBalance = await token.balanceOf(pool.address)
- print(tokenBalance.toString())
- print(pool.address)
- print(token.address)
- print(`balance pre-pool-assets${(await token.balanceOf(pool.address)).toString()}`)
- const convertRate = await pool.convertRate()
- const eqFeePool = await pool.eqFeePool()
- const protocolFee = await pool.protocolFeeBalance()
- const mintFeeBalance = await pool.mintFeeBalance()
- const poolAssets = tokenBalance.div(convertRate).sub(eqFeePool).sub(protocolFee).sub(mintFeeBalance)
- // let poolAssets = tokenBalance
- // .div(await pool.convertRate())
- // .sub(await pool.eqFeePool())
- // .sub(await pool.protocolFeeBalance())
- // .sub(await pool.mintFeeBalance())
- print(`balance pre-delta${(await token.balanceOf(pool.address)).toString()}`)
- let totalQueryBalance = poolAssets.sub(await pool.deltaCredit())
- print(`balance post-delta ${(await token.balanceOf(pool.address)).toString()}`)
- let totalPromisedBalance = ethers.BigNumber.from(0)
- print(`balance start ${(await token.balanceOf(pool.address)).toString()}`)
- // for each iterate by destination chain
- for (const [dstChainId, dstPoolId] of chainPaths) {
- const dstPool = this.stargateEndpoints[dstChainId].poolInfos[dstPoolId].pool
- const dstCP = await dstPool.chainPaths(await dstPool.chainPathIndexLookup(srcChainId, srcPoolId))
- const srcCP = await pool.chainPaths(await pool.chainPathIndexLookup(dstChainId, dstPoolId))
- // if no msg inFlight, dstCp.lkb === srcCp.balance
- const transactionsInFlight = dstCP.lkb.sub(srcCP.balance)
- const promisedBalance = dstCP.balance.add(srcCP.credits)
- totalPromisedBalance = totalPromisedBalance.add(transactionsInFlight).add(promisedBalance)
- print(`balance ${dstChainId} ${(await token.balanceOf(pool.address)).toString()}`)
- }
- if (totalPromisedBalance.toString() !== totalQueryBalance.toString()) {
- print((await pool.deltaCredit()).toString())
- print((await pool.convertRate()).toString())
- const tokenBalance3 = await token.balanceOf(pool.address)
- print(tokenBalance3.toString())
- print(token.address)
- print((await token.balanceOf(pool.address)).toString())
- print("here")
- }
- /*
- ASSERT - IFG constraints
- */
- expect(totalPromisedBalance).to.equal(totalQueryBalance)
- }
- }
- }
- }
- async function toPowerOfDecimals(_amountRaw, _token) {
- return ethers.BigNumber.from(10)
- .pow(await _token.decimals())
- .mul(_amountRaw)
- }
- module.exports = {
- GlobalBook,
- mintAndApproveFunds,
- toPowerOfDecimals,
- }
|