UNPKG

@gnosis.pm/dex-contracts

Version:

Contracts for dFusion multi-token batch auction exchange

80 lines (79 loc) 4.56 kB
import BN from "bn.js"; import { placeFeeTokenLiquidityOrders } from "../src/fee_token_liquidity"; import { getOrdersPaginated } from "../src/onchain_reading"; import { factory } from "../src/logging"; const log = factory.getLogger("scripts.owl_liquidity"); const BatchExchange = artifacts.require("BatchExchange"); const MAXU32 = new BN(2).pow(new BN(32)).sub(new BN(1)); const MIN_OWL_LIQUIDITY = new BN(10).pow(new BN(17)); const SELL_AMOUNT_OWL = new BN(10).pow(new BN(18)).mul(new BN(5)); // All orders provided by this liquidity script will sell OWL for a very high price: // At 1000 [token]/[OWL]. In most of the cases this will ensure that 1 [OWL] is valued // higher than 1 dollar. For tokens valued below 1/10000 USD, OWL can be extracted profitably // from these orders. However, since we only sell 5 OWL and 10 OWL have to be spent to add one token, // stealing OWL by adding new tokens is not profitable. const PRICE_FOR_PROVISION = new BN(10000); const containsSellOrderProvidingLiquidity = function (orders) { return orders.some((order) => order.sellTokenBalance.gt(MIN_OWL_LIQUIDITY) && order.remainingAmount.gt(MIN_OWL_LIQUIDITY)); }; // This function checks whether it is likely that Gnosis has already provided liquidity for this token // with a liquidity-order. The check depends on the match of two order criteria: SellAmount and validUntil. // Despite being just an heuristic check, it should be sufficient for now. const hasOWLLiquidityOrderAlreadyBeenPlaced = function (orders) { return orders.some((order) => order.priceDenominator.eq(SELL_AMOUNT_OWL) && new BN(order.validUntil).eq(MAXU32)); }; module.exports = async (callback) => { try { const exchange = await BatchExchange.deployed(); const owlTokenAddress = await exchange.tokenIdToAddressMap(0); const [liquidityEnsurer] = await web3.eth.getAccounts(); log.info(`Using account ${liquidityEnsurer}`); // check that liquidityEnsurer has sufficient OWL in the exchange: const owlBalance = await exchange.getBalance(liquidityEnsurer, owlTokenAddress); if (new BN(10).pow(new BN(18)).gt(owlBalance)) { callback("Error: OWL balance is below the 10 OWL threshold, please stock it up again"); } // Get the order data const numTokens = (await exchange.numTokens()).toNumber(); const batchId = (await exchange.getCurrentBatchId()).toNumber(); log.info("Retrieving orders from exchange. This may take a while..."); let orders = await getOrdersPaginated(exchange.contract, 100); orders = orders.filter((order) => order.validUntil >= batchId && order.validFrom <= batchId); // Ensure OWL-liquidity is given const tokensRequiringLiquidity = []; for (let tokenId = 1; tokenId < numTokens; tokenId++) { const tokenAddress = await exchange.tokenIdToAddressMap(tokenId); log.info(`Checking liquidity for token ${tokenId} - ${tokenAddress}`); const ordersForTokenId = orders.filter((order) => order.buyToken == tokenId && order.sellToken == 0); if (!containsSellOrderProvidingLiquidity(ordersForTokenId) && !hasOWLLiquidityOrderAlreadyBeenPlaced(ordersForTokenId)) { tokensRequiringLiquidity.push(tokenId); } else { log.info(` Liquidity for ${tokenAddress} is given or has been provided in the past`); } } if (tokensRequiringLiquidity) { log.info(`Attempting to place orders for tokens ${tokensRequiringLiquidity}`); const successTokens = await placeFeeTokenLiquidityOrders(exchange, tokensRequiringLiquidity, PRICE_FOR_PROVISION, SELL_AMOUNT_OWL, artifacts); if (successTokens && successTokens.length) { log.info(`Successfully placed fee token liquidity orders for tokens: ${successTokens}`); } // This scenario is actually quite common. In fact, always // happens once a non-ERC20 token has been registered on exchange. const failedTokens = tokensRequiringLiquidity.filter((x) => !successTokens.includes(x)); if (failedTokens && failedTokens.length) { log.warn(`No orders placed for ${failedTokens} (Likely not ERC20s on this network).`); } } else { log.info("Did not detect any tokens requiring liquidity"); } callback(); } catch (error) { callback(error); } };