UNPKG

@melonproject/protocol

Version:

Technology Regulated and Operated Investment Funds

405 lines (404 loc) 22 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const order_utils_1 = require("@0x/order-utils"); const token_math_1 = require("@melonproject/token-math"); const orderSignatures_1 = require("../../utils/constants/orderSignatures"); const createOrder_1 = require("../../contracts/exchanges/third-party/0x/utils/createOrder"); const signOrder_1 = require("../../contracts/exchanges/third-party/0x/utils/signOrder"); const getAssetProxy_1 = require("../../contracts/exchanges/third-party/0x/calls/getAssetProxy"); const updateTestingPriceFeed_1 = require("../utils/updateTestingPriceFeed"); const getAllBalances_1 = require("../utils/getAllBalances"); const initTestEnvironment_1 = require("../utils/initTestEnvironment"); const deployAndGetSystem_1 = require("../utils/deployAndGetSystem"); const getToken_1 = require("../../contracts/dependencies/token/calls/getToken"); const beginSetup_1 = require("../../contracts/factory/transactions/beginSetup"); const completeSetup_1 = require("../../contracts/factory/transactions/completeSetup"); const createAccounting_1 = require("../../contracts/factory/transactions/createAccounting"); const createFeeManager_1 = require("../../contracts/factory/transactions/createFeeManager"); const createParticipation_1 = require("../../contracts/factory/transactions/createParticipation"); const createPolicyManager_1 = require("../../contracts/factory/transactions/createPolicyManager"); const createShares_1 = require("../../contracts/factory/transactions/createShares"); const createTrading_1 = require("../../contracts/factory/transactions/createTrading"); const createVault_1 = require("../../contracts/factory/transactions/createVault"); const getFundComponents_1 = require("../../utils/getFundComponents"); const randomHexOfSize_1 = require("../../utils/helpers/randomHexOfSize"); const Contracts_1 = require("../../Contracts"); const withDifferentAccount_1 = require("../../utils/environment/withDifferentAccount"); const increaseTime_1 = require("../../utils/evm/increaseTime"); const fillOrder_1 = require("../../contracts/exchanges/third-party/0x/transactions/fillOrder"); // mock data const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; let s = {}; beforeAll(() => __awaiter(this, void 0, void 0, function* () { s.environment = yield initTestEnvironment_1.initTestEnvironment(); s.accounts = yield s.environment.eth.getAccounts(); const { addresses, contracts } = yield deployAndGetSystem_1.deployAndGetSystem(s.environment); s.addresses = addresses; s = Object.assign(s, contracts); [s.deployer, s.manager, s.investor] = s.accounts; s.exchanges = [s.matchingMarket]; // , matchingMarket2]; s.gas = 8000000; s.opts = { from: s.deployer, gas: s.gas }; s.numberofExchanges = 1; s.exchanges = [s.matchingMarket]; s.erc20ProxyAddress = yield getAssetProxy_1.getAssetProxy(s.environment, s.zeroExExchange.options.address); s.mlnTokenInterface = yield getToken_1.getToken(s.environment, s.mln.options.address); s.wethTokenInterface = yield getToken_1.getToken(s.environment, s.weth.options.address); const exchangeConfigs = { [Contracts_1.Exchanges.ZeroEx]: { adapter: s.zeroExAdapter.options.address, exchange: s.zeroExExchange.options.address, takesCustody: false, }, }; const envManager = withDifferentAccount_1.withDifferentAccount(s.environment, s.manager); yield beginSetup_1.beginSetup(envManager, s.version.options.address, { defaultTokens: [s.wethTokenInterface, s.mlnTokenInterface], exchangeConfigs, fees: [], fundName: 'Test fund', quoteToken: s.wethTokenInterface, }); yield createAccounting_1.createAccounting(envManager, s.version.options.address); yield createFeeManager_1.createFeeManager(envManager, s.version.options.address); yield createParticipation_1.createParticipation(envManager, s.version.options.address); yield createPolicyManager_1.createPolicyManager(envManager, s.version.options.address); yield createShares_1.createShares(envManager, s.version.options.address); yield createTrading_1.createTrading(envManager, s.version.options.address); yield createVault_1.createVault(envManager, s.version.options.address); const hubAddress = yield completeSetup_1.completeSetup(envManager, s.version.options.address); s.fund = yield getFundComponents_1.getFundComponents(envManager, hubAddress); yield updateTestingPriceFeed_1.updateTestingPriceFeed(s, s.environment); })); const initialTokenAmount = new token_math_1.BigInteger(Math.pow(10, 19)); test('investor gets initial ethToken for testing)', () => __awaiter(this, void 0, void 0, function* () { const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); yield s.weth.methods .transfer(s.investor, `${initialTokenAmount}`) .send(s.opts); const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); expect(post.investor.weth).toEqual(token_math_1.add(pre.investor.weth, initialTokenAmount)); })); // tslint:disable-next-line:max-line-length test('fund receives ETH from investment, and gets ZRX from direct transfer', () => __awaiter(this, void 0, void 0, function* () { const offeredValue = new token_math_1.BigInteger(Math.pow(10, 18)); const wantedShares = new token_math_1.BigInteger(Math.pow(10, 18)); const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); yield s.weth.methods .approve(s.fund.participation.options.address, `${offeredValue}`) .send({ from: s.investor, gas: s.gas }); yield s.fund.participation.methods .requestInvestment(`${offeredValue}`, `${wantedShares}`, s.weth.options.address) .send({ from: s.investor, gas: s.gas, value: '10000000000000000' }); yield s.fund.participation.methods .executeRequestFor(s.investor) .send({ from: s.investor, gas: s.gas }); yield s.zrx.methods .transfer(s.fund.vault.options.address, `${initialTokenAmount}`) .send({ from: s.deployer, gas: s.gas }); const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); expect(post.investor.weth).toEqual(token_math_1.subtract(pre.investor.weth, offeredValue)); expect(post.fund.weth).toEqual(token_math_1.add(pre.fund.weth, offeredValue)); })); test('third party makes and validates an off-chain order', () => __awaiter(this, void 0, void 0, function* () { const makerAddress = s.deployer.toLowerCase(); const makerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 1); const takerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05); const unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, { makerAddress, makerQuantity, takerQuantity, }); yield createOrder_1.approveOrder(s.environment, s.zeroExExchange.options.address, unsignedOrder); s.signedOrder = yield signOrder_1.signOrder(s.environment, unsignedOrder); const signatureValid = yield createOrder_1.isValidSignatureOffChain(s.environment, unsignedOrder, s.signedOrder.signature); expect(signatureValid).toBeTruthy(); })); // tslint:disable-next-line:max-line-length test('manager takes order (half the total quantity) through 0x adapter', () => __awaiter(this, void 0, void 0, function* () { const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); const fillQuantity = s.signedOrder.takerAssetAmount; yield s.fund.trading.methods .callOnExchange(0, orderSignatures_1.takeOrderSignature, [ s.deployer, NULL_ADDRESS, s.mln.options.address, s.weth.options.address, s.signedOrder.feeRecipientAddress, NULL_ADDRESS, ], [ s.signedOrder.makerAssetAmount.toFixed(), s.signedOrder.takerAssetAmount.toFixed(), s.signedOrder.makerFee.toFixed(), s.signedOrder.takerFee.toFixed(), s.signedOrder.expirationTimeSeconds.toFixed(), s.signedOrder.salt.toFixed(), `${fillQuantity}`, 0, ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature) .send({ from: s.manager, gas: s.gas }); const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); const heldInExchange = yield s.fund.trading.methods .updateAndGetQuantityHeldInExchange(s.weth.options.address) .call(); expect(heldInExchange).toBe('0'); expect(post.deployer.mln).toEqual(token_math_1.subtract(pre.deployer.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount))); expect(post.fund.weth).toEqual(token_math_1.subtract(pre.fund.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount))); expect(post.fund.mln).toEqual(token_math_1.add(pre.fund.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount))); expect(post.deployer.weth).toEqual(token_math_1.add(pre.deployer.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount))); })); test('third party makes and validates an off-chain order', () => __awaiter(this, void 0, void 0, function* () { const makerAddress = s.deployer.toLowerCase(); const makerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 1); const takerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05); const takerFee = new token_math_1.BigInteger(Math.pow(10, 14)); const unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, { feeRecipientAddress: s.investor, makerAddress, makerQuantity, takerFee, takerQuantity, }); yield createOrder_1.approveOrder(s.environment, s.zeroExExchange.options.address, unsignedOrder); s.signedOrder = yield signOrder_1.signOrder(s.environment, unsignedOrder); const signatureValid = yield createOrder_1.isValidSignatureOffChain(s.environment, unsignedOrder, s.signedOrder.signature); expect(signatureValid).toBeTruthy(); })); test('fund with enough ZRX takes the above order', () => __awaiter(this, void 0, void 0, function* () { const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); const preFundZrx = new token_math_1.BigInteger(yield s.zrx.methods.balanceOf(s.fund.vault.options.address).call()); const fillQuantity = s.signedOrder.takerAssetAmount; yield s.fund.trading.methods .callOnExchange(0, orderSignatures_1.takeOrderSignature, [ s.deployer, NULL_ADDRESS, s.mln.options.address, s.weth.options.address, s.signedOrder.feeRecipientAddress, NULL_ADDRESS, ], [ s.signedOrder.makerAssetAmount.toFixed(), s.signedOrder.takerAssetAmount.toFixed(), s.signedOrder.makerFee.toFixed(), s.signedOrder.takerFee.toFixed(), s.signedOrder.expirationTimeSeconds.toFixed(), s.signedOrder.salt.toFixed(), `${fillQuantity}`, 0, ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature) .send({ from: s.manager, gas: s.gas }); const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); const postFundZrx = new token_math_1.BigInteger(yield s.zrx.methods.balanceOf(s.fund.vault.options.address).call()); const heldInExchange = yield s.fund.trading.methods .updateAndGetQuantityHeldInExchange(s.weth.options.address) .call(); expect(heldInExchange).toBe('0'); expect(postFundZrx).toEqual(token_math_1.subtract(preFundZrx, token_math_1.toBI(s.signedOrder.takerFee))); expect(post.deployer.mln).toEqual(token_math_1.subtract(pre.deployer.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount))); expect(post.fund.weth).toEqual(token_math_1.subtract(pre.fund.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount))); expect(post.fund.mln).toEqual(token_math_1.add(pre.fund.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount))); expect(post.deployer.weth).toEqual(token_math_1.add(pre.deployer.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount))); })); test('Make order through the fund', () => __awaiter(this, void 0, void 0, function* () { const makerAddress = s.fund.trading.options.address.toLowerCase(); const makerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 0.5); const takerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05); const unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, { makerAddress, makerQuantity, takerQuantity, }); s.signedOrder = yield signOrder_1.signOrder(s.environment, unsignedOrder, s.manager); yield s.fund.trading.methods .callOnExchange(0, orderSignatures_1.makeOrderSignature, [ makerAddress, NULL_ADDRESS, s.mln.options.address, s.weth.options.address, s.signedOrder.feeRecipientAddress, NULL_ADDRESS, ], [ s.signedOrder.makerAssetAmount.toFixed(), s.signedOrder.takerAssetAmount.toFixed(), s.signedOrder.makerFee.toFixed(), s.signedOrder.takerFee.toFixed(), s.signedOrder.expirationTimeSeconds.toFixed(), s.signedOrder.salt.toFixed(), 0, 0, ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature) .send({ from: s.manager, gas: s.gas }); const makerAssetAllowance = new token_math_1.BigInteger(yield s.mln.methods .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase()) .call()); expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(s.signedOrder.makerAssetAmount)); })); // test.serial( // 'Fund cannot make multiple orders for same asset unless fulfilled', // async t => { // await t.throws( // fund.trading.methods // .callOnExchange( // 0, // makeOrderSignature, // [ // fund.trading.options.address.toLowerCase(), // NULL_ADDRESS, // mlnToken.options.address, // ethToken.options.address, // order.feeRecipientAddress, // NULL_ADDRESS, // ], // [ // order.makerAssetAmount.toFixed(), // order.takerAssetAmount.toFixed(), // order.makerFee.toFixed(), // order.takerFee.toFixed(), // order.expirationTimeSeconds.toFixed(), // 559, // 0, // 0, // ], // web3.utils.padLeft('0x0', 64), // order.makerAssetData, // order.takerAssetData, // orderSignature, // ) // .send({ from: manager, gas: config.gas }), // ); // }, // ); test('Third party takes the order made by the fund', () => __awaiter(this, void 0, void 0, function* () { const pre = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); const result = yield fillOrder_1.fillOrder(s.environment, s.zeroExExchange.options.address, { signedOrder: s.signedOrder, }); const post = yield getAllBalances_1.getAllBalances(s, s.accounts, s.fund, s.environment); expect(result).toBeTruthy(); expect(post.fund.weth).toEqual(token_math_1.add(pre.fund.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount))); expect(post.fund.mln).toEqual(token_math_1.subtract(pre.fund.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount))); expect(post.deployer.weth).toEqual(token_math_1.subtract(pre.deployer.weth, token_math_1.toBI(s.signedOrder.takerAssetAmount))); expect(post.deployer.mln).toEqual(token_math_1.add(pre.deployer.mln, token_math_1.toBI(s.signedOrder.makerAssetAmount))); })); // tslint:disable-next-line:max-line-length test("Fund can make another make order for same asset (After it's inactive)", () => __awaiter(this, void 0, void 0, function* () { const makerAddress = s.fund.trading.options.address.toLowerCase(); const makerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05); const takerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 0.5); s.unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, { feeRecipientAddress: s.investor, makerAddress, makerQuantity, takerQuantity, }); s.signedOrder = yield signOrder_1.signOrder(s.environment, s.unsignedOrder, s.manager); yield s.fund.trading.methods .callOnExchange(0, orderSignatures_1.makeOrderSignature, [ makerAddress, NULL_ADDRESS, s.weth.options.address, s.mln.options.address, s.signedOrder.feeRecipientAddress, NULL_ADDRESS, ], [ s.signedOrder.makerAssetAmount.toFixed(), s.signedOrder.takerAssetAmount.toFixed(), s.signedOrder.makerFee.toFixed(), s.signedOrder.takerFee.toFixed(), s.signedOrder.expirationTimeSeconds.toFixed(), s.signedOrder.salt.toFixed(), 0, 0, ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature) .send({ from: s.manager, gas: s.gas }); const makerAssetAllowance = new token_math_1.BigInteger(yield s.weth.methods .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase()) .call()); expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(s.signedOrder.makerAssetAmount)); })); test('Fund can cancel the order using just the orderId', () => __awaiter(this, void 0, void 0, function* () { const orderHashHex = order_utils_1.orderHashUtils.getOrderHashHex(s.unsignedOrder); yield s.fund.trading.methods .callOnExchange(0, orderSignatures_1.cancelOrderSignature, [ NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, ], [0, 0, 0, 0, 0, 0, 0, 0], orderHashHex, '0x0', '0x0', '0x0') .send({ from: s.manager, gas: s.gas }); const isOrderCancelled = yield s.zeroExExchange.methods .cancelled(orderHashHex) .call(); const makerAssetAllowance = new token_math_1.BigInteger(yield s.mln.methods .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase()) .call()); expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(0)); expect(isOrderCancelled).toBeTruthy(); })); test('Expired order is removed from open maker order', () => __awaiter(this, void 0, void 0, function* () { yield increaseTime_1.increaseTime(s.environment, 60 * 30); const makerAddress = s.fund.trading.options.address.toLowerCase(); const makerQuantity = token_math_1.createQuantity(s.wethTokenInterface, 0.05); const takerQuantity = token_math_1.createQuantity(s.mlnTokenInterface, 0.5); const duration = 50; s.unsignedOrder = yield createOrder_1.createOrder(s.environment, s.zeroExExchange.options.address, { duration, feeRecipientAddress: s.investor, makerAddress, makerQuantity, takerQuantity, }); s.signedOrder = yield signOrder_1.signOrder(s.environment, s.unsignedOrder, s.manager); yield s.fund.trading.methods .callOnExchange(0, orderSignatures_1.makeOrderSignature, [ makerAddress, NULL_ADDRESS, s.weth.options.address, s.mln.options.address, s.signedOrder.feeRecipientAddress, NULL_ADDRESS, ], [ s.signedOrder.makerAssetAmount.toFixed(), s.signedOrder.takerAssetAmount.toFixed(), s.signedOrder.makerFee.toFixed(), s.signedOrder.takerFee.toFixed(), s.signedOrder.expirationTimeSeconds.toFixed(), s.signedOrder.salt.toFixed(), 0, 0, ], randomHexOfSize_1.randomHexOfSize(20), s.signedOrder.makerAssetData, s.signedOrder.takerAssetData, s.signedOrder.signature) .send({ from: s.manager, gas: s.gas }); const makerAssetAllowance = new token_math_1.BigInteger(yield s.weth.methods .allowance(s.fund.trading.options.address, s.erc20ProxyAddress.toLowerCase()) .call()); expect(makerAssetAllowance).toEqual(new token_math_1.BigInteger(s.signedOrder.makerAssetAmount)); const orderHashHex = order_utils_1.orderHashUtils.getOrderHashHex(s.unsignedOrder); yield increaseTime_1.increaseTime(s.environment, duration); yield s.fund.trading.methods .callOnExchange(0, orderSignatures_1.cancelOrderSignature, [ NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, NULL_ADDRESS, ], [0, 0, 0, 0, 0, 0, 0, 0], orderHashHex, '0x0', '0x0', '0x0') .send({ from: s.manager, gas: s.gas }); yield s.fund.trading.methods .updateAndGetQuantityBeingTraded(s.weth.options.address) .send({ from: s.manager, gas: s.gas }); const isInOpenMakeOrder = yield s.fund.trading.methods .isInOpenMakeOrder(s.weth.options.address) .call(); expect(isInOpenMakeOrder).toBeFalsy(); }));