Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- let NetworkProxy = artifacts.require("./KyberNetworkProxy.sol");
- let ConversionRates = artifacts.require("./mockContracts/MockConversionRate.sol");
- let TestToken = artifacts.require("./mockContracts/TestToken.sol");
- let Reserve = artifacts.require("./KyberReserve.sol");
- let Network = artifacts.require("./KyberNetwork.sol");
- let WhiteList = artifacts.require("./WhiteList.sol");
- let ExpectedRate = artifacts.require("./ExpectedRate.sol");
- let FeeBurner = artifacts.require("./FeeBurner.sol");
- let Helper = require("./helper.js");
- let BigNumber = require('bignumber.js');
- // Global Variables
- let precisionUnits = (new BigNumber(10).pow(18));
- let ethAddress = '0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
- let gasPrice = (new BigNumber(10).pow(9).mul(50));
- let negligibleRateDiff = 11;
- // Balances
- let expectedReserveBalanceWei = 0;
- let reserveTokenBalance = [];
- let reserveTokenImbalance = [];
- let reserveStartTokenBalance = [];
- //permission groups
- let admin;
- let operator;
- let user;
- let walletId;
- // Contracts
- let conversionRates; // Previously pricing2
- let reserve; // Previously reserve2
- let whiteList;
- let expectedRate;
- let network;
- let networkProxy;
- let feeBurner;
- // Block data
- let priceUpdateBlock;
- let currentBlock;
- let validRateDurationInBlocks = 5100;
- // Token data
- let numTokens = 2;
- let tokens = [];
- let tokenAddress = [];
- let tokenDecimals = [];
- // imbalance data
- let minimalRecordResolution = 2; // low resolution so I don't lose too much data. then easier to compare calculated imbalance values.
- let maxPerBlockImbalance = 4000;
- let maxTotalImbalance = maxPerBlockImbalance * 12;
- // all price steps in bps (basic price steps).
- // 100 bps means rate change will be: price * (100 + 10000) / 10000 == raise rate in 1%
- // higher rate is better for user. will get more dst quantity for his tokens.
- // all x values represent token imbalance. y values represent equivalent steps in bps.
- // buyImbalance represents coin shortage. higher buy imbalance = more tokens were bought.
- // generally. speaking, if imbalance is higher we want to have:
- // - smaller buy bps (negative) to lower rate when buying token with ether.
- // - bigger sell bps to have higher rate when buying ether with token.
- ////////////////////
- //base buy and sell rates (prices)
- let baseBuyRate1 = [];
- let baseBuyRate2 = [];
- let baseSellRate1 = [];
- let baseSellRate2 = [];
- //quantity buy steps
- let qtyBuyStepX = [0, 150, 350, 700, 1400];
- let qtyBuyStepY = [0, 0, -70, -160, -3000];
- //imbalance buy steps
- let imbalanceBuyStepX = [-8500, -2800, -1500, 0, 1500, 2800, 4500];
- let imbalanceBuyStepY = [ 1300, 130, 43, 0, 0, -110, -1600];
- //sell
- //sell price will be 1 / buy (assuming no spread) so sell is actually buy price in other direction
- let qtySellStepX = [0, 150, 350, 700, 1400];
- let qtySellStepY = [0, 0, 120, 170, 3000];
- //sell imbalance step
- let imbalanceSellStepX = [-8500, -2800, -1500, 0, 1500, 2800, 4500];
- let imbalanceSellStepY = [-1500, -320, -75, 0, 0, 110, 650];
- //compact data.
- let sells = [];
- let buys = [];
- let indices = [];
- let compactBuyArr = [];
- let compactSellArr = [];
- contract('KyberNetworkProxy', function(accounts) {
- it("Set Accounts, deploy ConversionRates, Tokens, and enable tokens for trade with basic data per token.", async function () {
- // set account addresses
- admin = accounts[0];
- operator = accounts[1];
- user = accounts[3];
- walletId = accounts[4];
- currentBlock = priceUpdateBlock = await Helper.getCurrentBlock();
- // Initialize ConversionRates contract
- conversionRates = await ConversionRates.new(admin, {});
- // Deploy tokens and add to ConversionRates
- for (let i = 0; i < numTokens; ++i) {
- tokenDecimals[i] = 15 * 1 + 1 * i;
- token = await TestToken.new("Token " + i, "TOKEN_ " + i, tokenDecimals[i]);
- tokens[i] = token;
- tokenAddress[i] = token.address;
- await conversionRates.addToken(token.address);
- await conversionRates.setTokenControlInfo(
- token.address,
- minimalRecordResolution,
- maxPerBlockImbalance,
- maxTotalImbalance
- );
- await conversionRates.enableTokenTrade(token.address);
- }
- assert.equal(tokens.length, numTokens, "Invalid number of tokens");
- await conversionRates.addOperator(operator);
- });
- it("Set base rates, compact data rate factor, and step function for all tokens.", async function () {
- // buy is ether to token rate. sale is token to ether rate. so sell == 1 / buy. assuming we have no spread.
- let tokensPerEther;
- let ethersPerToken;
- for (i = 0; i < numTokens; ++i) {
- tokensPerEther = (new BigNumber(precisionUnits.mul((i + 1) * 3)).floor());
- ethersPerToken = (new BigNumber(precisionUnits.div((i + 1) * 3)).floor());
- baseBuyRate1.push(tokensPerEther.valueOf());
- baseBuyRate2.push(tokensPerEther.valueOf() * 10100 / 10000);
- baseSellRate1.push(ethersPerToken.valueOf());
- baseSellRate2.push(ethersPerToken.div(1000).mul(980));
- }
- assert.equal(baseBuyRate1.length, tokens.length);
- assert.equal(baseBuyRate2.length, tokens.length);
- assert.equal(baseSellRate1.length, tokens.length);
- assert.equal(baseSellRate2.length, tokens.length);
- buys.length = sells.length = indices.length = 0;
- await conversionRates.setBaseRate(tokenAddress, baseBuyRate2, baseSellRate2, buys, sells, currentBlock, indices, {from: operator});
- // Set compact data
- compactBuyArr = [0, 0, 0, 0, 0, 06, 07, 08, 09, 1, 0, 11, 12, 13, 14];
- let compactBuyHex = Helper.bytesToHex(compactBuyArr);
- buys.push(compactBuyHex);
- compactSellArr = [0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34];
- let compactSellHex = Helper.bytesToHex(compactSellArr);
- sells.push(compactSellHex);
- indices[0] = 0;
- assert.equal(indices.length, sells.length, "bad sells array size");
- assert.equal(indices.length, buys.length, "bad buys array size");
- await conversionRates.setCompactData(buys, sells, currentBlock, indices, {from: operator});
- // Set pricing step functions
- let zeroArr = [0];
- for (let i = 0; i < numTokens; ++i) {
- await conversionRates.setQtyStepFunction(
- tokenAddress[i],
- qtyBuyStepX,
- qtyBuyStepY,
- qtySellStepX,
- qtySellStepY,
- { from: operator }
- );
- await conversionRates.setImbalanceStepFunction(
- tokenAddress[i],
- imbalanceBuyStepX,
- imbalanceBuyStepY,
- imbalanceSellStepX,
- imbalanceSellStepY,
- { from: operator }
- );
- }
- });
- it("Deploy Network and Reserve Contract and set Reserve data including balances.", async function () {
- network = await Network.new(admin);
- await network.addOperator(operator);
- reserve = await Reserve.new(network.address, conversionRates.address, admin);
- await conversionRates.setReserveAddress(reserve.address);
- for (i = 0; i < numTokens; ++i) {
- await reserve.approveWithdrawAddress(tokenAddress[i], accounts[0], true);
- }
- // Set reserve balance: 10 ** 18 wei ether + per token 10**18 wei ether value according to base rate.
- let reserveEtherInit = (new BigNumber(10)).pow(19);
- await Helper.sendEtherWithPromise(accounts[9], reserve.address, reserveEtherInit);
- // Transfer tokens to the reserve, each token same wei balance
- for (let i = 0; i < numTokens; ++i) {
- token = tokens[i];
- let amount2 = (new BigNumber(reserveEtherInit)).div(precisionUnits).mul(baseBuyRate2[i]).floor();
- reserveStartTokenBalance[i] = amount2
- await token.transfer(reserve.address, amount2.valueOf());
- reserveTokenBalance.push(amount2);
- reserveTokenImbalance.push(0);
- }
- });
- it("Deploy KyberNetworkProxy, Fee Burner, Genesis Token, Whitelist, Expected Rate, and list token pairs.", async function () {
- // Authorize reserves
- await network.addReserve(reserve.address, true);
- networkProxy = await NetworkProxy.new(admin);
- await network.setKyberProxy(networkProxy.address);
- await networkProxy.setKyberNetworkContract(network.address);
- // Set contracts
- feeBurner = await FeeBurner.new(admin, tokenAddress[0], network.address);
- let kgtToken = await TestToken.new("Kyber Genesis Token", "KGT", 0);
- whiteList = await WhiteList.new(admin, kgtToken.address);
- await whiteList.addOperator(operator);
- await whiteList.setCategoryCap(0, 1000, { from: operator });
- await whiteList.setSgdToEthRate(30000, { from: operator });
- expectedRate = await ExpectedRate.new(network.address, admin);
- await network.setWhiteList(whiteList.address);
- await network.setExpectedRate(expectedRate.address);
- await network.setFeeBurner(feeBurner.address);
- await network.setParams(gasPrice.valueOf(), negligibleRateDiff);
- await network.setEnable(true);
- let price = await network.maxGasPrice();
- assert.equal(price.valueOf(), gasPrice.valueOf());
- // List tokens per reserve
- for (let i = 0; i < numTokens; i++) {
- await network.listPairForReserve(
- reserve.address,
- tokenAddress[i],
- true,
- true,
- true
- );
- }
- });
- it("Swaps an ERC20 token for another ERC20 token.", async function () {
- let sourceTokenIndex = 1;
- let destinationTokenIndex = 0;
- let sourceToken = tokens[sourceTokenIndex];
- let destinationToken = tokens[destinationTokenIndex];
- let sourceAmountTwei = 1450 * 1;
- let maxDestAmount = (new BigNumber(10)).pow(18);
- // Reset max imbalance values for working with higher numbers
- buys.length = sells.length = indices.length = 0;
- // Set compact data
- compactBuyArr = [0, 0, 0, 0, 0, 06, 07, 08, 09, 10, 11, 12, 13, 14];
- let compactBuyHex = Helper.bytesToHex(compactBuyArr);
- buys.push(compactBuyHex);
- compactSellArr = [0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34];
- let compactSellHex = Helper.bytesToHex(compactSellArr);
- sells.push(compactSellHex);
- indices[0] = 0;
- currentBlock = await Helper.getCurrentBlock();
- await conversionRates.setBaseRate(
- tokenAddress,
- baseBuyRate2,
- baseSellRate2,
- buys,
- sells,
- currentBlock,
- indices,
- { from: operator }
- );
- priceUpdateBlock = currentBlock;
- maxPerBlockImbalance = 60000000;
- maxTotalImbalance = 12 * maxPerBlockImbalance;
- // Set higher imbalance values and set local imbalance values to 0 since we update compact data
- for (let i = 0; i < numTokens; ++i) {
- await conversionRates.setTokenControlInfo(
- tokenAddress[i],
- minimalRecordResolution,
- maxPerBlockImbalance,
- maxTotalImbalance
- );
- // Update balance in imbalance values
- reserveTokenBalance[i] = new BigNumber(await tokens[i].balanceOf(reserve.address));
- reserveTokenImbalance[i] = new BigNumber(0);
- }
- try {
- // Verify base rate
- let buyRate = await networkProxy.getExpectedRate(
- tokenAddress[sourceTokenIndex],
- tokenAddress[destinationTokenIndex],
- sourceAmountTwei
- );
- // First Token to Eth rate
- let expected = calculateRateAmount(false, sourceTokenIndex, sourceAmountTwei, 2);
- let expectedSellRate = expected[0];
- let expectedEthQtyWei = expected[1];
- // Eth to Token rate
- expected = calculateRateAmount(true, destinationTokenIndex, expectedEthQtyWei, 2);
- let expectedBuyRate = expected[0];
- expectedDestTokensTwei = expected[1];
- // Calculate combined rate
- let combinedRate = calcCombinedRate(
- sourceAmountTwei,
- expectedSellRate,
- expectedBuyRate,
- tokenDecimals[sourceTokenIndex],
- tokenDecimals[destinationTokenIndex],
- expectedDestTokensTwei
- );
- // Check correct rate calculated
- assert.equal(buyRate[0].valueOf(), combinedRate.valueOf(), "Unexpected Combined Rate.");
- //
- // Perform trade
- //
- // REQUIRED: Transfer funds to user (issuance order maker)
- await sourceToken.transfer(user, sourceAmountTwei);
- // REQUIRED: Approve funds to network for all trades
- // await sourceToken.approve(
- // networkProxy.address,
- // sourceAmountTwei,
- // { from: user }
- // );
- let startBalanceDestinationTokenUser = await destinationToken.balanceOf(user);
- let startBalanceSourceTokenUser = await sourceToken.balanceOf(user);
- let destinationTokenuserExistingBalance = await destinationToken.balanceOf(user);
- console.log(destinationTokenuserExistingBalance.valueOf());
- //
- // REQUIRED: Trade
- //
- // let result = await networkProxy.trade(
- // tokenAddress[sourceTokenIndex], // sourceToken
- // sourceAmountTwei, // sourceTokenAmount
- // tokenAddress[destinationTokenIndex], // destinationToken
- // user, // destinationAddress
- // maxDestAmount, // maxDestAmount
- // buyRate[1].valueOf(), // minConversionRate
- // walletId, // walletId
- // { from: user }
- // );
- // Update Balance and Imbalance
- reserveTokenBalance[sourceTokenIndex] = (reserveTokenBalance[sourceTokenIndex]).add(sourceAmountTwei);
- reserveTokenImbalance[sourceTokenIndex] = reserveTokenImbalance[sourceTokenIndex].sub(sourceAmountTwei);
- reserveTokenBalance[destinationTokenIndex] = reserveTokenBalance[destinationTokenIndex].sub(expectedDestTokensTwei);
- reserveTokenImbalance[destinationTokenIndex] = reserveTokenImbalance[destinationTokenIndex].add(expectedDestTokensTwei); //more missing tokens
- //
- // Check token balances
- //
- // Check higher destinationToken balance on user
- // let rate = new BigNumber(buyRate[0].valueOf());
- // let destinationTokenUserBalance = await destinationToken.balanceOf(user);
- // let expectedBalancedestinationTokenuser = startBalanceDestinationTokenUser.add(expectedDestTokensTwei);
- // assert.equal(expectedBalancedestinationTokenuser.valueOf(), destinationTokenUserBalance.valueOf(), "bad token balance");
- // Check lower sourceToken balance on user
- // let sourceTokenUserBalance = await sourceToken.balanceOf(user);
- // let expectedBalancesourceTokenUser = startBalanceSourceTokenUser.sub(sourceAmountTwei);
- // assert.equal(sourceTokenUserBalance.valueOf(), expectedBalancesourceTokenUser.valueOf(), "bad token balance");
- // Check source token balance on reserve
- // reportedBalance = await sourceToken.balanceOf(reserve.address);
- // assert.equal(reportedBalance.valueOf(), reserveTokenBalance[sourceTokenIndex].valueOf(), "Invalid source token balance on reserve");
- // Check destination token balance on reserve
- // reportedBalance = await destinationToken.balanceOf(reserve.address);
- // assert.equal(reportedBalance.valueOf(), reserveTokenBalance[destinationTokenIndex].valueOf(), "Invalid destination token balance on reserve");
- // Print out contract addresses
- console.log("KyberNetworkProxy Address: ", networkProxy.address);
- console.log("Source Token Address: ", tokenAddress[sourceTokenIndex]);
- console.log("Destination Token Address: ", tokenAddress[destinationTokenIndex]);
- console.log("Buy Rate: ", buyRate[1].valueOf());
- console.log("Source Amount: ", sourceAmountTwei);
- console.log("Max Destination Amount: ", maxDestAmount);
- } catch(e) {
- throw(e);
- }
- });
- });
- function convertRateToConversionRatesRate (baseRate) {
- // conversion rate in pricing is in precision units (10 ** 18) so
- // rate 1 to 50 is 50 * 10 ** 18
- // rate 50 to 1 is 1 / 50 * 10 ** 18 = 10 ** 18 / 50a
- return ((new BigNumber(10).pow(18)).mul(baseRate).floor());
- };
- function getExtraBpsForBuyQuantity(qty) {
- for (let i = 0; i < qtyBuyStepX.length; i++) {
- if (qty <= qtyBuyStepX[i]) return qtyBuyStepY[i];
- }
- return qtyBuyStepY[qtyBuyStepY.length - 1];
- };
- function getExtraBpsForSellQuantity(qty) {
- for (let i = 0; i < qtySellStepX.length; i++) {
- if (qty <= qtySellStepX[i]) return qtySellStepY[i];
- }
- return qtySellStepY[qtySellStepY.length - 1];
- };
- function getExtraBpsForImbalanceBuyQuantity(qty) {
- for (let i = 0; i < imbalanceBuyStepX.length; i++) {
- if (qty <= imbalanceBuyStepX[i]) return imbalanceBuyStepY[i];
- }
- return (imbalanceBuyStepY[imbalanceBuyStepY.length - 1]);
- };
- function getExtraBpsForImbalanceSellQuantity(qty) {
- for (let i = 0; i < imbalanceSellStepX.length; i++) {
- if (qty <= imbalanceSellStepX[i]) return imbalanceSellStepY[i];
- }
- return (imbalanceSellStepY[imbalanceSellStepY.length - 1]);
- };
- function addBps (rate, bps) {
- return (rate.mul(10000 + bps).div(10000));
- };
- function compareRates (receivedRate, expectedRate) {
- expectedRate = expectedRate - (expectedRate % 10);
- receivedRate = receivedRate - (receivedRate % 10);
- assert.equal(expectedRate, receivedRate, "different rates");
- };
- function calculateRateAmount(isBuy, tokenInd, srcQty, reserveIndex, maxDestAmount) {
- let expectedRate;
- let expectedAmount;
- let baseArray;
- let imbalanceArray;
- let expected = [];
- if (isBuy) {
- baseArray = baseBuyRate2;
- imbalanceArray = reserveTokenImbalance;
- } else {
- imbalanceArray = reserveTokenImbalance;
- baseArray = baseSellRate2;
- }
- if (isBuy) {
- expectedRate = (new BigNumber(baseArray[tokenInd]));
- let dstQty = calcDstQty(srcQty, 18, tokenDecimals[tokenInd], expectedRate);
- let extraBps = getExtraBpsForBuyQuantity(dstQty);
- expectedRate = addBps(expectedRate, extraBps);
- let relevantImbalance = imbalanceArray[tokenInd] * 1 + dstQty * 1;
- extraBps = getExtraBpsForImbalanceBuyQuantity(relevantImbalance);
- expectedRate = addBps(expectedRate, extraBps);
- expectedAmount = calcDstQty(srcQty, 18, tokenDecimals[tokenInd], expectedRate);
- } else {
- expectedRate = (new BigNumber(baseArray[tokenInd]));
- let extraBps = getExtraBpsForSellQuantity(srcQty);
- expectedRate = addBps(expectedRate, extraBps);
- let relevantImbalance = imbalanceArray[tokenInd] - srcQty;
- extraBps = getExtraBpsForImbalanceSellQuantity(relevantImbalance.valueOf());
- expectedRate = addBps(expectedRate, extraBps);
- expectedAmount = calcDstQty(srcQty, tokenDecimals[tokenInd], 18, expectedRate);
- }
- expectedAmount = expectedAmount.floor();
- expectedRate = expectedRate.floor();
- expected = [expectedRate, expectedAmount];
- return expected;
- }
- function calcDstQty(srcQty, srcDecimals, dstDecimals, rate) {
- rate = new BigNumber(rate);
- if (dstDecimals >= srcDecimals) {
- let decimalDiff = (new BigNumber(10)).pow(dstDecimals - srcDecimals);
- return (rate.mul(srcQty).mul(decimalDiff).div(precisionUnits)).floor();
- } else {
- let decimalDiff = (new BigNumber(10)).pow(srcDecimals - dstDecimals);
- return (rate.mul(srcQty).div(decimalDiff.mul(precisionUnits))).floor();
- }
- }
- function calcSrcQty(dstQty, srcDecimals, dstDecimals, rate) {
- //source quantity is rounded up. to avoid dest quantity being too low.
- let srcQty;
- let numerator;
- let denominator;
- if (srcDecimals >= dstDecimals) {
- numerator = precisionUnits.mul(dstQty).mul((new BigNumber(10)).pow(srcDecimals - dstDecimals));
- denominator = new BigNumber(rate);
- } else {
- numerator = precisionUnits.mul(dstQty);
- denominator = (new BigNumber(rate)).mul((new BigNumber(10)).pow(dstDecimals - srcDecimals));
- }
- srcQty = (numerator.add(denominator.sub(1))).div(denominator).floor(); //avoid rounding down errors
- return srcQty;
- }
- function calcCombinedRate(srcQty, sellRate, buyRate, srcDecimals, dstDecimals, destQty) {
- let rate;
- if (false) {
- rate = (sellRate.mul(srcQty).div(precisionUnits).floor()).mul(buyRate).div(srcQty).floor();
- } else {
- if (dstDecimals >= srcDecimals) {
- rate = (precisionUnits.mul(destQty)).div(((new BigNumber(10)).pow(dstDecimals - srcDecimals)).mul(srcQty));
- } else {
- rate = (precisionUnits.mul(destQty).mul((new BigNumber(10)).pow(srcDecimals - dstDecimals))).div(srcQty);
- }
- }
- return rate.floor();
- }
- function log (string) {
- console.log(string);
- };
Add Comment
Please, Sign In to add comment