-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[JS->TS] Migrate ensure_owl_liquidity.js (#820)
* Migrate ensure_owl_liquidity.js * improve return type and capture all possible script outcomes
- Loading branch information
Showing
3 changed files
with
121 additions
and
76 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import BN from "bn.js"; | ||
import { placeFeeTokenLiquidityOrders } from "../src/fee_token_liquidity"; | ||
import { getOrdersPaginated } from "../src/onchain_reading"; | ||
import type { Order } from "../src/encoding"; | ||
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: Order<BN>[]) { | ||
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: Order<BN>[]) { | ||
return orders.some( | ||
(order) => | ||
order.priceDenominator.eq(SELL_AMOUNT_OWL) && | ||
new BN(order.validUntil).eq(MAXU32), | ||
); | ||
}; | ||
|
||
module.exports = async (callback: Truffle.ScriptCallback) => { | ||
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); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters