UNPKG

@fleupold/dex-contracts

Version:

Contracts for dFusion multi-token batch auction exchange

106 lines (99 loc) 3.53 kB
import BN from "bn.js"; import type Web3 from "web3"; import type { BatchExchangeInstance } from "../types/truffle-typings"; const WORD_DATA_LENGTH = 64; /** * Retrieves user's token balance as stored in the "balance" entry of the private exchange mapping balanceStates * Value is directly read from storage relying on Solidity's layout of storage variables * See https://solidity.readthedocs.io/en/develop/internals/layout_in_storage.html * @param userAddress - address of the user * @param tokenAddress - address of the token * @param batchExchangeAddress - address of the batch exchange * @param web3Provider - provider of Ethereum JavaScript API * @returns balance of the token for the given user as stored in balanceStates[userAddress][tokenAddress].balance */ export async function getBalanceState( userAddress: string, tokenAddress: string, batchExchangeAddress: string, web3Provider: Web3 = web3, ): Promise<BN> { // TODO when Truffle depends on web3-utils version ^1.2.5: // use soliditySha3Raw instead of soliditySha3 and remove type coercion const BALANCESTATES_STORAGE_SLOT = "0x0"; const userBalancestatesStorageSlot = web3Provider.utils.soliditySha3( { type: "bytes32", value: web3Provider.utils.padLeft(userAddress, WORD_DATA_LENGTH), }, { type: "bytes32", value: web3Provider.utils.padLeft( BALANCESTATES_STORAGE_SLOT, WORD_DATA_LENGTH, ), }, ) as string; const targetStorageSlot = web3Provider.utils.soliditySha3( { type: "bytes32", value: web3Provider.utils.padLeft(tokenAddress, WORD_DATA_LENGTH), }, { type: "bytes32", value: web3Provider.utils.padLeft( userBalancestatesStorageSlot, WORD_DATA_LENGTH, ), }, ) as string; const storageAtSlot = await web3Provider.eth.getStorageAt( batchExchangeAddress, targetStorageSlot, ); return web3Provider.utils.toBN(storageAtSlot); } /** * Computes amount of a token that a user can immediately withdraw from the exchange * It not only checks whether a withdrawal is pending, but also considers the balance available to the user, * pending deposits, and whether there were recent trades that would make withdrawing fail. * @param userAddress - address of the user * @param tokenAddress - address of the token * @param batchExchange - object representing the batch exchange smart contract * @param web3Provider - provider of Ethereum JavaScript API * @returns amount of token that the user would receive by calling batchExchange.withdraw */ export async function getWithdrawableAmount( userAddress: string, tokenAddress: string, batchExchange: BatchExchangeInstance, web3Provider: Web3 = web3, ): Promise<BN> { const [ balanceState, pendingDeposit, pendingWithdrawal, lastCreditBatchId, batchId, ] = await Promise.all([ getBalanceState( userAddress, tokenAddress, batchExchange.address, web3Provider, ), batchExchange.getPendingDeposit(userAddress, tokenAddress), batchExchange.getPendingWithdraw(userAddress, tokenAddress), batchExchange.lastCreditBatchId(userAddress, tokenAddress), batchExchange.getCurrentBatchId(), ]); let balance = balanceState; if (pendingDeposit[1].lt(batchId)) { balance = balance.add(pendingDeposit[0]); } if (pendingWithdrawal[1].gte(batchId) || lastCreditBatchId.gte(batchId)) { return new BN(0); } else { return BN.min(balance, pendingWithdrawal[0]); } }