@gnosis.pm/dex-contracts
Version:
Contracts for dFusion multi-token batch auction exchange
80 lines (79 loc) • 4.56 kB
JavaScript
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);
}
};