Split 0x.js into contract-wrappers, order-watcher but keep 0x.js as a unifying library with the same interface
This commit is contained in:
39
packages/contract-wrappers/test/artifacts/AccountLevels.json
Normal file
39
packages/contract-wrappers/test/artifacts/AccountLevels.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"contract_name": "AccountLevels",
|
||||
"networks": {
|
||||
"50": {
|
||||
"solc_version": "0.4.23",
|
||||
"source_tree_hash": "0x48558bbda893aa15786836980a5fffb694db15163d462619c4dcb0f8ad97977b",
|
||||
"optimizer_enabled": false,
|
||||
"abi": [
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "user",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "accountLevel",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b5060ce8061001f6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631cbd0519146044575b600080fd5b348015604f57600080fd5b506082600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506098565b6040518082815260200191505060405180910390f35b60008090509190505600a165627a7a72305820ba8f0553b1bad01d6aebe1dfdcea170d83055523ddd0c848be8952e0494b9e230029",
|
||||
"runtime_bytecode": "0x608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631cbd0519146044575b600080fd5b348015604f57600080fd5b506082600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506098565b6040518082815260200191505060405180910390f35b60008090509190505600a165627a7a72305820ba8f0553b1bad01d6aebe1dfdcea170d83055523ddd0c848be8952e0494b9e230029",
|
||||
"updated_at": 1525776887441,
|
||||
"source_map": "26:388:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;26:388:0;;;;;;;",
|
||||
"source_map_runtime": "26:388:0:-;;;;;;;;;;;;;;;;;;;;;;;;328:84;;8:9:-1;5:2;;;30:1;27;20:12;5:2;328:84:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;381:4;404:1;397:8;;328:84;;;:::o",
|
||||
"sources": [
|
||||
"/Users/fabioberger/Documents/projects/0x_project/0x-monorepo/packages/contracts/src/contracts/current/tutorials/EtherDelta/AccountLevels.sol"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
121
packages/contract-wrappers/test/artifacts/Arbitrage.json
Normal file
121
packages/contract-wrappers/test/artifacts/Arbitrage.json
Normal file
File diff suppressed because one or more lines are too long
324
packages/contract-wrappers/test/artifacts/DummyToken.json
Normal file
324
packages/contract-wrappers/test/artifacts/DummyToken.json
Normal file
File diff suppressed because one or more lines are too long
875
packages/contract-wrappers/test/artifacts/EtherDelta.json
Normal file
875
packages/contract-wrappers/test/artifacts/EtherDelta.json
Normal file
File diff suppressed because one or more lines are too long
631
packages/contract-wrappers/test/artifacts/Exchange.json
Normal file
631
packages/contract-wrappers/test/artifacts/Exchange.json
Normal file
File diff suppressed because one or more lines are too long
195
packages/contract-wrappers/test/artifacts/MaliciousToken.json
Normal file
195
packages/contract-wrappers/test/artifacts/MaliciousToken.json
Normal file
File diff suppressed because one or more lines are too long
551
packages/contract-wrappers/test/artifacts/MultiSigWallet.json
Normal file
551
packages/contract-wrappers/test/artifacts/MultiSigWallet.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
179
packages/contract-wrappers/test/artifacts/Token.json
Normal file
179
packages/contract-wrappers/test/artifacts/Token.json
Normal file
@@ -0,0 +1,179 @@
|
||||
{
|
||||
"contract_name": "Token",
|
||||
"networks": {
|
||||
"50": {
|
||||
"solc_version": "0.4.23",
|
||||
"source_tree_hash": "0xe43382be55ddb9c7a28567b4cc59e35072da198e6c49a90ff1396aa8399fd61e",
|
||||
"optimizer_enabled": false,
|
||||
"abi": [
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "approve",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferFrom",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_spender",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "allowance",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Transfer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "_spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "_value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Approval",
|
||||
"type": "event"
|
||||
}
|
||||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b506102e3806100206000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063095ea7b31461007257806323b872dd146100d757806370a082311461015c578063a9059cbb146101b3578063dd62ed3e14610218575b600080fd5b34801561007e57600080fd5b506100bd600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061028f565b604051808215151515815260200191505060405180910390f35b3480156100e357600080fd5b50610142600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610297565b604051808215151515815260200191505060405180910390f35b34801561016857600080fd5b5061019d600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a0565b6040518082815260200191505060405180910390f35b3480156101bf57600080fd5b506101fe600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506102a7565b604051808215151515815260200191505060405180910390f35b34801561022457600080fd5b50610279600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102af565b6040518082815260200191505060405180910390f35b600092915050565b60009392505050565b6000919050565b600092915050565b6000929150505600a165627a7a72305820c0eb8c14c0b2d0b9f45639d053a22e400cf642410f155a793e5b2ea1e78925bb0029",
|
||||
"runtime_bytecode": "0x60806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063095ea7b31461007257806323b872dd146100d757806370a082311461015c578063a9059cbb146101b3578063dd62ed3e14610218575b600080fd5b34801561007e57600080fd5b506100bd600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061028f565b604051808215151515815260200191505060405180910390f35b3480156100e357600080fd5b50610142600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610297565b604051808215151515815260200191505060405180910390f35b34801561016857600080fd5b5061019d600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a0565b6040518082815260200191505060405180910390f35b3480156101bf57600080fd5b506101fe600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506102a7565b604051808215151515815260200191505060405180910390f35b34801561022457600080fd5b50610279600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102af565b6040518082815260200191505060405180910390f35b600092915050565b60009392505050565b6000919050565b600092915050565b6000929150505600a165627a7a72305820c0eb8c14c0b2d0b9f45639d053a22e400cf642410f155a793e5b2ea1e78925bb0029",
|
||||
"updated_at": 1525776876709,
|
||||
"source_map": "26:1709:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;26:1709:0;;;;;;;",
|
||||
"source_map_runtime": "26:1709:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1037:72;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1037:72:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;667:87;;8:9:-1;5:2;;;30:1;27;20:12;5:2;667:87:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1218:64;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1218:64:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;278:68;;8:9:-1;5:2;;;30:1;27;20:12;5:2;278:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1490:82;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1490:82:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1037:72;1101:4;1037:72;;;;:::o;667:87::-;746:4;667:87;;;;;:::o;1218:64::-;1274:4;1218:64;;;:::o;278:68::-;338:4;278:68;;;;:::o;1490:82::-;1564:4;1490:82;;;;:::o",
|
||||
"sources": [
|
||||
"/Users/fabioberger/Documents/projects/0x_project/0x-monorepo/packages/contracts/src/contracts/current/tokens/Token/Token.sol"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
562
packages/contract-wrappers/test/artifacts/TokenRegistry.json
Normal file
562
packages/contract-wrappers/test/artifacts/TokenRegistry.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
297
packages/contract-wrappers/test/artifacts/WETH9.json
Normal file
297
packages/contract-wrappers/test/artifacts/WETH9.json
Normal file
File diff suppressed because one or more lines are too long
244
packages/contract-wrappers/test/artifacts/ZRXToken.json
Normal file
244
packages/contract-wrappers/test/artifacts/ZRXToken.json
Normal file
File diff suppressed because one or more lines are too long
49
packages/contract-wrappers/test/artifacts_test.ts
Normal file
49
packages/contract-wrappers/test/artifacts_test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { web3Factory } from '@0xproject/dev-utils';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { ContractWrappers } from '../src';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
|
||||
chaiSetup.configure();
|
||||
|
||||
// Those tests are slower cause they're talking to a remote node
|
||||
const TIMEOUT = 10000;
|
||||
|
||||
describe('Artifacts', () => {
|
||||
describe('contracts are deployed on kovan', () => {
|
||||
const kovanRpcUrl = constants.KOVAN_RPC_URL;
|
||||
const provider = web3Factory.create({ rpcUrl: kovanRpcUrl }).currentProvider;
|
||||
const config = {
|
||||
networkId: constants.KOVAN_NETWORK_ID,
|
||||
};
|
||||
const contractWrappers = new ContractWrappers(provider, config);
|
||||
it('token registry contract is deployed', async () => {
|
||||
await (contractWrappers.tokenRegistry as any)._getTokenRegistryContractAsync();
|
||||
}).timeout(TIMEOUT);
|
||||
it('proxy contract is deployed', async () => {
|
||||
await (contractWrappers.proxy as any)._getTokenTransferProxyContractAsync();
|
||||
}).timeout(TIMEOUT);
|
||||
it('exchange contract is deployed', async () => {
|
||||
await (contractWrappers.exchange as any)._getExchangeContractAsync();
|
||||
}).timeout(TIMEOUT);
|
||||
});
|
||||
describe('contracts are deployed on ropsten', () => {
|
||||
const ropstenRpcUrl = constants.ROPSTEN_RPC_URL;
|
||||
const provider = web3Factory.create({ rpcUrl: ropstenRpcUrl }).currentProvider;
|
||||
const config = {
|
||||
networkId: constants.ROPSTEN_NETWORK_ID,
|
||||
};
|
||||
const contractWrappers = new ContractWrappers(provider, config);
|
||||
it('token registry contract is deployed', async () => {
|
||||
await (contractWrappers.tokenRegistry as any)._getTokenRegistryContractAsync();
|
||||
}).timeout(TIMEOUT);
|
||||
it('proxy contract is deployed', async () => {
|
||||
await (contractWrappers.proxy as any)._getTokenTransferProxyContractAsync();
|
||||
}).timeout(TIMEOUT);
|
||||
it('exchange contract is deployed', async () => {
|
||||
await (contractWrappers.exchange as any)._getExchangeContractAsync();
|
||||
}).timeout(TIMEOUT);
|
||||
});
|
||||
});
|
||||
417
packages/contract-wrappers/test/ether_token_wrapper_test.ts
Normal file
417
packages/contract-wrappers/test/ether_token_wrapper_test.ts
Normal file
@@ -0,0 +1,417 @@
|
||||
import { BlockchainLifecycle, callbackErrorReporter, devConstants, web3Factory } from '@0xproject/dev-utils';
|
||||
import { DoneCallback } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
import {
|
||||
ApprovalContractEventArgs,
|
||||
BlockParamLiteral,
|
||||
BlockRange,
|
||||
ContractWrappers,
|
||||
ContractWrappersError,
|
||||
DecodedLogEvent,
|
||||
DepositContractEventArgs,
|
||||
EtherTokenEvents,
|
||||
Token,
|
||||
TransferContractEventArgs,
|
||||
WithdrawalContractEventArgs,
|
||||
} from '../src';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
import { TokenUtils } from './utils/token_utils';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
// Since the address depositing/withdrawing ETH/WETH also needs to pay gas costs for the transaction,
|
||||
// a small amount of ETH will be used to pay this gas cost. We therefore check that the difference between
|
||||
// the expected balance and actual balance (given the amount of ETH deposited), only deviates by the amount
|
||||
// required to pay gas costs.
|
||||
const MAX_REASONABLE_GAS_COST_IN_WEI = 62517;
|
||||
|
||||
describe('EtherTokenWrapper', () => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
let tokens: Token[];
|
||||
let userAddresses: string[];
|
||||
let addressWithETH: string;
|
||||
let wethContractAddress: string;
|
||||
let depositWeiAmount: BigNumber;
|
||||
let decimalPlaces: number;
|
||||
let addressWithoutFunds: string;
|
||||
const gasPrice = new BigNumber(1);
|
||||
const zeroExConfig = {
|
||||
gasPrice,
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
};
|
||||
const transferAmount = new BigNumber(42);
|
||||
const allowanceAmount = new BigNumber(42);
|
||||
const depositAmount = new BigNumber(42);
|
||||
const withdrawalAmount = new BigNumber(42);
|
||||
before(async () => {
|
||||
contractWrappers = new ContractWrappers(provider, zeroExConfig);
|
||||
tokens = await contractWrappers.tokenRegistry.getTokensAsync();
|
||||
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
addressWithETH = userAddresses[0];
|
||||
wethContractAddress = contractWrappers.etherToken.getContractAddressIfExists() as string;
|
||||
depositWeiAmount = Web3Wrapper.toWei(new BigNumber(5));
|
||||
decimalPlaces = 7;
|
||||
addressWithoutFunds = userAddresses[1];
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('#getContractAddressIfExists', async () => {
|
||||
it('should return contract address if connected to a known network', () => {
|
||||
const contractAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists();
|
||||
expect(contractAddressIfExists).to.not.be.undefined();
|
||||
});
|
||||
it('should throw if connected to a private network and contract addresses are not specified', () => {
|
||||
const UNKNOWN_NETWORK_NETWORK_ID = 10;
|
||||
expect(
|
||||
() =>
|
||||
new ContractWrappers(provider, {
|
||||
networkId: UNKNOWN_NETWORK_NETWORK_ID,
|
||||
} as any),
|
||||
).to.throw();
|
||||
});
|
||||
});
|
||||
describe('#depositAsync', () => {
|
||||
it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => {
|
||||
const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
|
||||
const preWETHBalance = await contractWrappers.token.getBalanceAsync(wethContractAddress, addressWithETH);
|
||||
expect(preETHBalance).to.be.bignumber.gt(0);
|
||||
expect(preWETHBalance).to.be.bignumber.equal(0);
|
||||
|
||||
const txHash = await contractWrappers.etherToken.depositAsync(
|
||||
wethContractAddress,
|
||||
depositWeiAmount,
|
||||
addressWithETH,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const postETHBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
|
||||
const postWETHBalanceInBaseUnits = await contractWrappers.token.getBalanceAsync(
|
||||
wethContractAddress,
|
||||
addressWithETH,
|
||||
);
|
||||
|
||||
expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount);
|
||||
const remainingETHInWei = preETHBalance.minus(depositWeiAmount);
|
||||
const gasCost = remainingETHInWei.minus(postETHBalanceInWei);
|
||||
expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
|
||||
});
|
||||
it('should throw if user has insufficient ETH balance for deposit', async () => {
|
||||
const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
|
||||
|
||||
const extraETHBalance = Web3Wrapper.toWei(new BigNumber(5));
|
||||
const overETHBalanceinWei = preETHBalance.add(extraETHBalance);
|
||||
|
||||
return expect(
|
||||
contractWrappers.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH),
|
||||
).to.be.rejectedWith(ContractWrappersError.InsufficientEthBalanceForDeposit);
|
||||
});
|
||||
});
|
||||
describe('#withdrawAsync', () => {
|
||||
it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => {
|
||||
const ETHBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
|
||||
|
||||
await contractWrappers.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
|
||||
|
||||
const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount);
|
||||
const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
|
||||
const preWETHBalance = await contractWrappers.token.getBalanceAsync(wethContractAddress, addressWithETH);
|
||||
let gasCost = expectedPreETHBalance.minus(preETHBalance);
|
||||
expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
|
||||
expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount);
|
||||
|
||||
const txHash = await contractWrappers.etherToken.withdrawAsync(
|
||||
wethContractAddress,
|
||||
depositWeiAmount,
|
||||
addressWithETH,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const postETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
|
||||
const postWETHBalanceInBaseUnits = await contractWrappers.token.getBalanceAsync(
|
||||
wethContractAddress,
|
||||
addressWithETH,
|
||||
);
|
||||
|
||||
expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
|
||||
const expectedETHBalance = preETHBalance.add(depositWeiAmount).round(decimalPlaces);
|
||||
gasCost = expectedETHBalance.minus(postETHBalance);
|
||||
expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
|
||||
});
|
||||
it('should throw if user has insufficient WETH balance for withdrawal', async () => {
|
||||
const preWETHBalance = await contractWrappers.token.getBalanceAsync(wethContractAddress, addressWithETH);
|
||||
expect(preWETHBalance).to.be.bignumber.equal(0);
|
||||
|
||||
const overWETHBalance = preWETHBalance.add(999999999);
|
||||
|
||||
return expect(
|
||||
contractWrappers.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH),
|
||||
).to.be.rejectedWith(ContractWrappersError.InsufficientWEthBalanceForWithdrawal);
|
||||
});
|
||||
});
|
||||
describe('#subscribe', () => {
|
||||
const indexFilterValues = {};
|
||||
let etherTokenAddress: string;
|
||||
before(() => {
|
||||
const tokenUtils = new TokenUtils(tokens);
|
||||
const etherToken = tokenUtils.getWethTokenOrThrow();
|
||||
etherTokenAddress = etherToken.address;
|
||||
});
|
||||
afterEach(() => {
|
||||
contractWrappers.etherToken.unsubscribeAll();
|
||||
});
|
||||
// Hack: Mocha does not allow a test to be both async and have a `done` callback
|
||||
// Since we need to await the receipt of the event in the `subscribe` callback,
|
||||
// we do need both. A hack is to make the top-level async fn w/ a done callback and then
|
||||
// wrap the rest of the test in an async block
|
||||
// Source: https://github.com/mochajs/mocha/issues/2407
|
||||
it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
|
||||
expect(logEvent).to.not.be.undefined();
|
||||
expect(logEvent.isRemoved).to.be.false();
|
||||
expect(logEvent.log.logIndex).to.be.equal(0);
|
||||
expect(logEvent.log.transactionIndex).to.be.equal(0);
|
||||
expect(logEvent.log.blockNumber).to.be.a('number');
|
||||
const args = logEvent.log.args;
|
||||
expect(args._from).to.be.equal(addressWithETH);
|
||||
expect(args._to).to.be.equal(addressWithoutFunds);
|
||||
expect(args._value).to.be.bignumber.equal(transferAmount);
|
||||
},
|
||||
);
|
||||
await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
|
||||
contractWrappers.etherToken.subscribe(
|
||||
etherTokenAddress,
|
||||
EtherTokenEvents.Transfer,
|
||||
indexFilterValues,
|
||||
callback,
|
||||
);
|
||||
await contractWrappers.token.transferAsync(
|
||||
etherTokenAddress,
|
||||
addressWithETH,
|
||||
addressWithoutFunds,
|
||||
transferAmount,
|
||||
);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
|
||||
expect(logEvent).to.not.be.undefined();
|
||||
expect(logEvent.isRemoved).to.be.false();
|
||||
const args = logEvent.log.args;
|
||||
expect(args._owner).to.be.equal(addressWithETH);
|
||||
expect(args._spender).to.be.equal(addressWithoutFunds);
|
||||
expect(args._value).to.be.bignumber.equal(allowanceAmount);
|
||||
},
|
||||
);
|
||||
contractWrappers.etherToken.subscribe(
|
||||
etherTokenAddress,
|
||||
EtherTokenEvents.Approval,
|
||||
indexFilterValues,
|
||||
callback,
|
||||
);
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
etherTokenAddress,
|
||||
addressWithETH,
|
||||
addressWithoutFunds,
|
||||
allowanceAmount,
|
||||
);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<DepositContractEventArgs>) => {
|
||||
expect(logEvent).to.not.be.undefined();
|
||||
expect(logEvent.isRemoved).to.be.false();
|
||||
const args = logEvent.log.args;
|
||||
expect(args._owner).to.be.equal(addressWithETH);
|
||||
expect(args._value).to.be.bignumber.equal(depositAmount);
|
||||
},
|
||||
);
|
||||
contractWrappers.etherToken.subscribe(
|
||||
etherTokenAddress,
|
||||
EtherTokenEvents.Deposit,
|
||||
indexFilterValues,
|
||||
callback,
|
||||
);
|
||||
await contractWrappers.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<WithdrawalContractEventArgs>) => {
|
||||
expect(logEvent).to.not.be.undefined();
|
||||
expect(logEvent.isRemoved).to.be.false();
|
||||
const args = logEvent.log.args;
|
||||
expect(args._owner).to.be.equal(addressWithETH);
|
||||
expect(args._value).to.be.bignumber.equal(depositAmount);
|
||||
},
|
||||
);
|
||||
await contractWrappers.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
|
||||
contractWrappers.etherToken.subscribe(
|
||||
etherTokenAddress,
|
||||
EtherTokenEvents.Withdrawal,
|
||||
indexFilterValues,
|
||||
callback,
|
||||
);
|
||||
await contractWrappers.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
|
||||
})().catch(done);
|
||||
});
|
||||
it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
|
||||
done(new Error('Expected this subscription to have been cancelled'));
|
||||
},
|
||||
);
|
||||
contractWrappers.etherToken.subscribe(
|
||||
etherTokenAddress,
|
||||
EtherTokenEvents.Transfer,
|
||||
indexFilterValues,
|
||||
callbackNeverToBeCalled,
|
||||
);
|
||||
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
|
||||
contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID);
|
||||
await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
|
||||
contractWrappers.etherToken.subscribe(
|
||||
etherTokenAddress,
|
||||
EtherTokenEvents.Transfer,
|
||||
indexFilterValues,
|
||||
callbackToBeCalled,
|
||||
);
|
||||
await contractWrappers.token.transferAsync(
|
||||
etherTokenAddress,
|
||||
addressWithETH,
|
||||
addressWithoutFunds,
|
||||
transferAmount,
|
||||
);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
|
||||
done(new Error('Expected this subscription to have been cancelled'));
|
||||
},
|
||||
);
|
||||
await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
|
||||
const subscriptionToken = contractWrappers.etherToken.subscribe(
|
||||
etherTokenAddress,
|
||||
EtherTokenEvents.Transfer,
|
||||
indexFilterValues,
|
||||
callbackNeverToBeCalled,
|
||||
);
|
||||
contractWrappers.etherToken.unsubscribe(subscriptionToken);
|
||||
await contractWrappers.token.transferAsync(
|
||||
etherTokenAddress,
|
||||
addressWithETH,
|
||||
addressWithoutFunds,
|
||||
transferAmount,
|
||||
);
|
||||
done();
|
||||
})().catch(done);
|
||||
});
|
||||
});
|
||||
describe('#getLogsAsync', () => {
|
||||
let etherTokenAddress: string;
|
||||
let tokenTransferProxyAddress: string;
|
||||
const blockRange: BlockRange = {
|
||||
fromBlock: 0,
|
||||
toBlock: BlockParamLiteral.Latest,
|
||||
};
|
||||
let txHash: string;
|
||||
before(() => {
|
||||
addressWithETH = userAddresses[0];
|
||||
const tokenUtils = new TokenUtils(tokens);
|
||||
const etherToken = tokenUtils.getWethTokenOrThrow();
|
||||
etherTokenAddress = etherToken.address;
|
||||
tokenTransferProxyAddress = contractWrappers.proxy.getContractAddress();
|
||||
});
|
||||
it('should get logs with decoded args emitted by Approval', async () => {
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const eventName = EtherTokenEvents.Approval;
|
||||
const indexFilterValues = {};
|
||||
const logs = await contractWrappers.etherToken.getLogsAsync<ApprovalContractEventArgs>(
|
||||
etherTokenAddress,
|
||||
eventName,
|
||||
blockRange,
|
||||
indexFilterValues,
|
||||
);
|
||||
expect(logs).to.have.length(1);
|
||||
const args = logs[0].args;
|
||||
expect(logs[0].event).to.be.equal(eventName);
|
||||
expect(args._owner).to.be.equal(addressWithETH);
|
||||
expect(args._spender).to.be.equal(tokenTransferProxyAddress);
|
||||
expect(args._value).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
it('should get logs with decoded args emitted by Deposit', async () => {
|
||||
await contractWrappers.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
|
||||
const eventName = EtherTokenEvents.Deposit;
|
||||
const indexFilterValues = {};
|
||||
const logs = await contractWrappers.etherToken.getLogsAsync<DepositContractEventArgs>(
|
||||
etherTokenAddress,
|
||||
eventName,
|
||||
blockRange,
|
||||
indexFilterValues,
|
||||
);
|
||||
expect(logs).to.have.length(1);
|
||||
const args = logs[0].args;
|
||||
expect(logs[0].event).to.be.equal(eventName);
|
||||
expect(args._owner).to.be.equal(addressWithETH);
|
||||
expect(args._value).to.be.bignumber.equal(depositAmount);
|
||||
});
|
||||
it('should only get the logs with the correct event name', async () => {
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const differentEventName = EtherTokenEvents.Transfer;
|
||||
const indexFilterValues = {};
|
||||
const logs = await contractWrappers.etherToken.getLogsAsync(
|
||||
etherTokenAddress,
|
||||
differentEventName,
|
||||
blockRange,
|
||||
indexFilterValues,
|
||||
);
|
||||
expect(logs).to.have.length(0);
|
||||
});
|
||||
it('should only get the logs with the correct indexed fields', async () => {
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(
|
||||
etherTokenAddress,
|
||||
addressWithoutFunds,
|
||||
);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const eventName = EtherTokenEvents.Approval;
|
||||
const indexFilterValues = {
|
||||
_owner: addressWithETH,
|
||||
};
|
||||
const logs = await contractWrappers.etherToken.getLogsAsync<ApprovalContractEventArgs>(
|
||||
etherTokenAddress,
|
||||
eventName,
|
||||
blockRange,
|
||||
indexFilterValues,
|
||||
);
|
||||
expect(logs).to.have.length(1);
|
||||
const args = logs[0].args;
|
||||
expect(args._owner).to.be.equal(addressWithETH);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
import { BlockchainLifecycle, devConstants } from '@0xproject/dev-utils';
|
||||
import { BlockParamLiteral, Token } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { ContractWrappers, ExchangeContractErrs } from '../src';
|
||||
import { TradeSide, TransferType } from '../src/types';
|
||||
import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('ExchangeTransferSimulator', () => {
|
||||
const config = {
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
};
|
||||
const contractWrappers = new ContractWrappers(provider, config);
|
||||
const transferAmount = new BigNumber(5);
|
||||
let userAddresses: string[];
|
||||
let tokens: Token[];
|
||||
let coinbase: string;
|
||||
let sender: string;
|
||||
let recipient: string;
|
||||
let exampleTokenAddress: string;
|
||||
let exchangeTransferSimulator: ExchangeTransferSimulator;
|
||||
let txHash: string;
|
||||
before(async () => {
|
||||
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[coinbase, sender, recipient] = userAddresses;
|
||||
tokens = await contractWrappers.tokenRegistry.getTokensAsync();
|
||||
exampleTokenAddress = tokens[0].address;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('#transferFromAsync', () => {
|
||||
beforeEach(() => {
|
||||
exchangeTransferSimulator = new ExchangeTransferSimulator(contractWrappers.token, BlockParamLiteral.Latest);
|
||||
});
|
||||
it("throws if the user doesn't have enough allowance", async () => {
|
||||
return expect(
|
||||
exchangeTransferSimulator.transferFromAsync(
|
||||
exampleTokenAddress,
|
||||
sender,
|
||||
recipient,
|
||||
transferAmount,
|
||||
TradeSide.Taker,
|
||||
TransferType.Trade,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance);
|
||||
});
|
||||
it("throws if the user doesn't have enough balance", async () => {
|
||||
txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
return expect(
|
||||
exchangeTransferSimulator.transferFromAsync(
|
||||
exampleTokenAddress,
|
||||
sender,
|
||||
recipient,
|
||||
transferAmount,
|
||||
TradeSide.Maker,
|
||||
TransferType.Trade,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance);
|
||||
});
|
||||
it('updates balances and proxyAllowance after transfer', async () => {
|
||||
txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
await exchangeTransferSimulator.transferFromAsync(
|
||||
exampleTokenAddress,
|
||||
sender,
|
||||
recipient,
|
||||
transferAmount,
|
||||
TradeSide.Taker,
|
||||
TransferType.Trade,
|
||||
);
|
||||
const store = (exchangeTransferSimulator as any)._store;
|
||||
const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
|
||||
const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
|
||||
const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
|
||||
expect(senderBalance).to.be.bignumber.equal(0);
|
||||
expect(recipientBalance).to.be.bignumber.equal(transferAmount);
|
||||
expect(senderProxyAllowance).to.be.bignumber.equal(0);
|
||||
});
|
||||
it("doesn't update proxyAllowance after transfer if unlimited", async () => {
|
||||
txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
await exchangeTransferSimulator.transferFromAsync(
|
||||
exampleTokenAddress,
|
||||
sender,
|
||||
recipient,
|
||||
transferAmount,
|
||||
TradeSide.Taker,
|
||||
TransferType.Trade,
|
||||
);
|
||||
const store = (exchangeTransferSimulator as any)._store;
|
||||
const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender);
|
||||
const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient);
|
||||
const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender);
|
||||
expect(senderBalance).to.be.bignumber.equal(0);
|
||||
expect(recipientBalance).to.be.bignumber.equal(transferAmount);
|
||||
expect(senderProxyAllowance).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
});
|
||||
});
|
||||
1228
packages/contract-wrappers/test/exchange_wrapper_test.ts
Normal file
1228
packages/contract-wrappers/test/exchange_wrapper_test.ts
Normal file
File diff suppressed because it is too large
Load Diff
7
packages/contract-wrappers/test/global_hooks.ts
Normal file
7
packages/contract-wrappers/test/global_hooks.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { runMigrationsAsync } from '@0xproject/migrations';
|
||||
|
||||
import { deployer } from './utils/deployer';
|
||||
|
||||
before('migrate contracts', async () => {
|
||||
await runMigrationsAsync(deployer);
|
||||
});
|
||||
527
packages/contract-wrappers/test/order_validation_test.ts
Normal file
527
packages/contract-wrappers/test/order_validation_test.ts
Normal file
@@ -0,0 +1,527 @@
|
||||
import { BlockchainLifecycle, devConstants } from '@0xproject/dev-utils';
|
||||
import { FillScenarios } from '@0xproject/fill-scenarios';
|
||||
import { OrderError } from '@0xproject/order-utils';
|
||||
import { BlockParamLiteral } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
import { ContractWrappers, ContractWrappersError, ExchangeContractErrs, SignedOrder, Token } from '../src';
|
||||
import { TradeSide, TransferType } from '../src/types';
|
||||
import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator';
|
||||
import { OrderValidationUtils } from '../src/utils/order_validation_utils';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
import { TokenUtils } from './utils/token_utils';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('OrderValidation', () => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
let userAddresses: string[];
|
||||
let tokens: Token[];
|
||||
let tokenUtils: TokenUtils;
|
||||
let exchangeContractAddress: string;
|
||||
let zrxTokenAddress: string;
|
||||
let fillScenarios: FillScenarios;
|
||||
let makerTokenAddress: string;
|
||||
let takerTokenAddress: string;
|
||||
let coinbase: string;
|
||||
let makerAddress: string;
|
||||
let takerAddress: string;
|
||||
let feeRecipient: string;
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const fillTakerAmount = new BigNumber(5);
|
||||
const config = {
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
};
|
||||
before(async () => {
|
||||
contractWrappers = new ContractWrappers(provider, config);
|
||||
exchangeContractAddress = contractWrappers.exchange.getContractAddress();
|
||||
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
|
||||
tokens = await contractWrappers.tokenRegistry.getTokensAsync();
|
||||
tokenUtils = new TokenUtils(tokens);
|
||||
zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
|
||||
fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress);
|
||||
const [makerToken, takerToken] = tokenUtils.getDummyTokens();
|
||||
makerTokenAddress = makerToken.address;
|
||||
takerTokenAddress = takerToken.address;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('validateOrderFillableOrThrowAsync', () => {
|
||||
it('should succeed if the order is fillable', async () => {
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
|
||||
});
|
||||
it('should succeed if the maker is buying ZRX and has no ZRX balance', async () => {
|
||||
const makerFee = new BigNumber(2);
|
||||
const takerFee = new BigNumber(2);
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress,
|
||||
zrxTokenAddress,
|
||||
makerFee,
|
||||
takerFee,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
feeRecipient,
|
||||
);
|
||||
const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress);
|
||||
await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance);
|
||||
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
|
||||
});
|
||||
it('should succeed if the maker is buying ZRX and has no ZRX balance and there is no specified taker', async () => {
|
||||
const makerFee = new BigNumber(2);
|
||||
const takerFee = new BigNumber(2);
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress,
|
||||
zrxTokenAddress,
|
||||
makerFee,
|
||||
takerFee,
|
||||
makerAddress,
|
||||
constants.NULL_ADDRESS,
|
||||
fillableAmount,
|
||||
feeRecipient,
|
||||
);
|
||||
const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress);
|
||||
await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance);
|
||||
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
|
||||
});
|
||||
it('should succeed if the order is asymmetric and fillable', async () => {
|
||||
const makerFillableAmount = fillableAmount;
|
||||
const takerFillableAmount = fillableAmount.minus(4);
|
||||
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
makerFillableAmount,
|
||||
takerFillableAmount,
|
||||
);
|
||||
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
|
||||
});
|
||||
it('should throw when the order is fully filled or cancelled', async () => {
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount);
|
||||
return expect(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
|
||||
ExchangeContractErrs.OrderRemainingFillAmountZero,
|
||||
);
|
||||
});
|
||||
it('should throw when order is expired', async () => {
|
||||
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
expirationInPast,
|
||||
);
|
||||
return expect(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith(
|
||||
ExchangeContractErrs.OrderFillExpired,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('validateFillOrderAndThrowIfInvalidAsync', () => {
|
||||
it('should throw when the fill amount is zero', async () => {
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
const zeroFillAmount = new BigNumber(0);
|
||||
return expect(
|
||||
contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
|
||||
signedOrder,
|
||||
zeroFillAmount,
|
||||
takerAddress,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero);
|
||||
});
|
||||
it('should throw when the signature is invalid', async () => {
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
// 27 <--> 28
|
||||
signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
|
||||
return expect(
|
||||
contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
|
||||
signedOrder,
|
||||
fillableAmount,
|
||||
takerAddress,
|
||||
),
|
||||
).to.be.rejectedWith(OrderError.InvalidSignature);
|
||||
});
|
||||
it('should throw when the order is fully filled or cancelled', async () => {
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount);
|
||||
return expect(
|
||||
contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
|
||||
signedOrder,
|
||||
fillableAmount,
|
||||
takerAddress,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero);
|
||||
});
|
||||
it('should throw when sender is not a taker', async () => {
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
const nonTakerAddress = userAddresses[6];
|
||||
return expect(
|
||||
contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
|
||||
signedOrder,
|
||||
fillTakerAmount,
|
||||
nonTakerAddress,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
|
||||
});
|
||||
it('should throw when order is expired', async () => {
|
||||
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
expirationInPast,
|
||||
);
|
||||
return expect(
|
||||
contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
|
||||
signedOrder,
|
||||
fillTakerAmount,
|
||||
takerAddress,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired);
|
||||
});
|
||||
it('should throw when there a rounding error would have occurred', async () => {
|
||||
const makerAmount = new BigNumber(3);
|
||||
const takerAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
makerAmount,
|
||||
takerAmount,
|
||||
);
|
||||
const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
|
||||
return expect(
|
||||
contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync(
|
||||
signedOrder,
|
||||
fillTakerAmountThatCausesRoundingError,
|
||||
takerAddress,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError);
|
||||
});
|
||||
});
|
||||
describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => {
|
||||
it('should throw if remaining fillAmount is less then the desired fillAmount', async () => {
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
const tooLargeFillAmount = new BigNumber(7);
|
||||
const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount);
|
||||
await contractWrappers.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference);
|
||||
await contractWrappers.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount);
|
||||
await contractWrappers.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference);
|
||||
await contractWrappers.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount);
|
||||
|
||||
return expect(
|
||||
contractWrappers.exchange.validateFillOrKillOrderThrowIfInvalidAsync(
|
||||
signedOrder,
|
||||
tooLargeFillAmount,
|
||||
takerAddress,
|
||||
),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount);
|
||||
});
|
||||
});
|
||||
describe('validateCancelOrderAndThrowIfInvalidAsync', () => {
|
||||
let signedOrder: SignedOrder;
|
||||
const cancelAmount = new BigNumber(3);
|
||||
beforeEach(async () => {
|
||||
[coinbase, makerAddress, takerAddress] = userAddresses;
|
||||
const [makerToken, takerToken] = tokenUtils.getDummyTokens();
|
||||
makerTokenAddress = makerToken.address;
|
||||
takerTokenAddress = takerToken.address;
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
);
|
||||
});
|
||||
it('should throw when cancel amount is zero', async () => {
|
||||
const zeroCancelAmount = new BigNumber(0);
|
||||
return expect(
|
||||
contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero);
|
||||
});
|
||||
it('should throw when order is expired', async () => {
|
||||
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
|
||||
const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
expirationInPast,
|
||||
);
|
||||
return expect(
|
||||
contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired);
|
||||
});
|
||||
it('should throw when order is already cancelled or filled', async () => {
|
||||
await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount);
|
||||
return expect(
|
||||
contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount),
|
||||
).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
|
||||
});
|
||||
});
|
||||
describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => {
|
||||
let exchangeTransferSimulator: ExchangeTransferSimulator;
|
||||
let transferFromAsync: Sinon.SinonSpy;
|
||||
const bigNumberMatch = (expected: BigNumber) => {
|
||||
return Sinon.match((value: BigNumber) => value.eq(expected));
|
||||
};
|
||||
beforeEach('create exchangeTransferSimulator', async () => {
|
||||
exchangeTransferSimulator = new ExchangeTransferSimulator(contractWrappers.token, BlockParamLiteral.Latest);
|
||||
transferFromAsync = Sinon.spy();
|
||||
exchangeTransferSimulator.transferFromAsync = transferFromAsync as any;
|
||||
});
|
||||
it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => {
|
||||
const makerFee = new BigNumber(2);
|
||||
const takerFee = new BigNumber(2);
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerFee,
|
||||
takerFee,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
feeRecipient,
|
||||
);
|
||||
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
|
||||
exchangeTransferSimulator,
|
||||
signedOrder,
|
||||
fillableAmount,
|
||||
takerAddress,
|
||||
zrxTokenAddress,
|
||||
);
|
||||
expect(transferFromAsync.callCount).to.be.equal(4);
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(0)
|
||||
.calledWith(
|
||||
makerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
bigNumberMatch(fillableAmount),
|
||||
TradeSide.Maker,
|
||||
TransferType.Trade,
|
||||
),
|
||||
).to.be.true();
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(1)
|
||||
.calledWith(
|
||||
takerTokenAddress,
|
||||
takerAddress,
|
||||
makerAddress,
|
||||
bigNumberMatch(fillableAmount),
|
||||
TradeSide.Taker,
|
||||
TransferType.Trade,
|
||||
),
|
||||
).to.be.true();
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(2)
|
||||
.calledWith(
|
||||
zrxTokenAddress,
|
||||
makerAddress,
|
||||
feeRecipient,
|
||||
bigNumberMatch(makerFee),
|
||||
TradeSide.Maker,
|
||||
TransferType.Fee,
|
||||
),
|
||||
).to.be.true();
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(3)
|
||||
.calledWith(
|
||||
zrxTokenAddress,
|
||||
takerAddress,
|
||||
feeRecipient,
|
||||
bigNumberMatch(takerFee),
|
||||
TradeSide.Taker,
|
||||
TransferType.Fee,
|
||||
),
|
||||
).to.be.true();
|
||||
});
|
||||
it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => {
|
||||
const makerFee = new BigNumber(2);
|
||||
const takerFee = new BigNumber(2);
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerFee,
|
||||
takerFee,
|
||||
makerAddress,
|
||||
constants.NULL_ADDRESS,
|
||||
fillableAmount,
|
||||
feeRecipient,
|
||||
);
|
||||
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
|
||||
exchangeTransferSimulator,
|
||||
signedOrder,
|
||||
fillableAmount,
|
||||
takerAddress,
|
||||
zrxTokenAddress,
|
||||
);
|
||||
expect(transferFromAsync.callCount).to.be.equal(4);
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(0)
|
||||
.calledWith(
|
||||
makerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
bigNumberMatch(fillableAmount),
|
||||
TradeSide.Maker,
|
||||
TransferType.Trade,
|
||||
),
|
||||
).to.be.true();
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(1)
|
||||
.calledWith(
|
||||
takerTokenAddress,
|
||||
takerAddress,
|
||||
makerAddress,
|
||||
bigNumberMatch(fillableAmount),
|
||||
TradeSide.Taker,
|
||||
TransferType.Trade,
|
||||
),
|
||||
).to.be.true();
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(2)
|
||||
.calledWith(
|
||||
zrxTokenAddress,
|
||||
makerAddress,
|
||||
feeRecipient,
|
||||
bigNumberMatch(makerFee),
|
||||
TradeSide.Maker,
|
||||
TransferType.Fee,
|
||||
),
|
||||
).to.be.true();
|
||||
expect(
|
||||
transferFromAsync
|
||||
.getCall(3)
|
||||
.calledWith(
|
||||
zrxTokenAddress,
|
||||
takerAddress,
|
||||
feeRecipient,
|
||||
bigNumberMatch(takerFee),
|
||||
TradeSide.Taker,
|
||||
TransferType.Fee,
|
||||
),
|
||||
).to.be.true();
|
||||
});
|
||||
it('should correctly round the fillMakerTokenAmount', async () => {
|
||||
const makerTokenAmount = new BigNumber(3);
|
||||
const takerTokenAmount = new BigNumber(1);
|
||||
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
makerTokenAmount,
|
||||
takerTokenAmount,
|
||||
);
|
||||
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
|
||||
exchangeTransferSimulator,
|
||||
signedOrder,
|
||||
takerTokenAmount,
|
||||
takerAddress,
|
||||
zrxTokenAddress,
|
||||
);
|
||||
expect(transferFromAsync.callCount).to.be.equal(4);
|
||||
const makerFillAmount = transferFromAsync.getCall(0).args[3];
|
||||
expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount);
|
||||
});
|
||||
it('should correctly round the makerFeeAmount', async () => {
|
||||
const makerFee = new BigNumber(2);
|
||||
const takerFee = new BigNumber(4);
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
makerFee,
|
||||
takerFee,
|
||||
makerAddress,
|
||||
takerAddress,
|
||||
fillableAmount,
|
||||
constants.NULL_ADDRESS,
|
||||
);
|
||||
const fillTakerTokenAmount = fillableAmount.div(2).round(0);
|
||||
await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
|
||||
exchangeTransferSimulator,
|
||||
signedOrder,
|
||||
fillTakerTokenAmount,
|
||||
takerAddress,
|
||||
zrxTokenAddress,
|
||||
);
|
||||
const makerPartialFee = makerFee.div(2);
|
||||
const takerPartialFee = takerFee.div(2);
|
||||
expect(transferFromAsync.callCount).to.be.equal(4);
|
||||
const partialMakerFee = transferFromAsync.getCall(2).args[3];
|
||||
expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee);
|
||||
const partialTakerFee = transferFromAsync.getCall(3).args[3];
|
||||
expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee);
|
||||
});
|
||||
});
|
||||
}); // tslint:disable-line:max-file-line-count
|
||||
95
packages/contract-wrappers/test/subscription_test.ts
Normal file
95
packages/contract-wrappers/test/subscription_test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { BlockchainLifecycle, callbackErrorReporter, devConstants } from '@0xproject/dev-utils';
|
||||
import { DoneCallback } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as _ from 'lodash';
|
||||
import 'mocha';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
import { ApprovalContractEventArgs, ContractWrappers, DecodedLogEvent, Token, TokenEvents } from '../src';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('SubscriptionTest', () => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
let userAddresses: string[];
|
||||
let tokens: Token[];
|
||||
let coinbase: string;
|
||||
let addressWithoutFunds: string;
|
||||
const config = {
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
};
|
||||
before(async () => {
|
||||
contractWrappers = new ContractWrappers(provider, config);
|
||||
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
tokens = await contractWrappers.tokenRegistry.getTokensAsync();
|
||||
coinbase = userAddresses[0];
|
||||
addressWithoutFunds = userAddresses[1];
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('#subscribe', () => {
|
||||
const indexFilterValues = {};
|
||||
let tokenAddress: string;
|
||||
const allowanceAmount = new BigNumber(42);
|
||||
let stubs: Sinon.SinonStub[] = [];
|
||||
before(() => {
|
||||
const token = tokens[0];
|
||||
tokenAddress = token.address;
|
||||
});
|
||||
afterEach(() => {
|
||||
contractWrappers.token.unsubscribeAll();
|
||||
_.each(stubs, s => s.restore());
|
||||
stubs = [];
|
||||
});
|
||||
it('Should receive the Error when an error occurs while fetching the block', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const errMsg = 'Error fetching block';
|
||||
const callback = callbackErrorReporter.assertNodeCallbackError(done, errMsg);
|
||||
stubs = [Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))];
|
||||
contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
tokenAddress,
|
||||
coinbase,
|
||||
addressWithoutFunds,
|
||||
allowanceAmount,
|
||||
);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should receive the Error when an error occurs while reconciling the new block', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const errMsg = 'Error fetching logs';
|
||||
const callback = callbackErrorReporter.assertNodeCallbackError(done, errMsg);
|
||||
stubs = [Sinon.stub((contractWrappers as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))];
|
||||
contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
tokenAddress,
|
||||
coinbase,
|
||||
addressWithoutFunds,
|
||||
allowanceAmount,
|
||||
);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callback = (err: Error | null, logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop;
|
||||
contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
|
||||
stubs = [
|
||||
Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws(
|
||||
new Error('JSON RPC error'),
|
||||
),
|
||||
];
|
||||
contractWrappers.token.unsubscribeAll();
|
||||
done();
|
||||
})().catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
134
packages/contract-wrappers/test/token_registry_wrapper_test.ts
Normal file
134
packages/contract-wrappers/test/token_registry_wrapper_test.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { BlockchainLifecycle, devConstants } from '@0xproject/dev-utils';
|
||||
import { schemas, SchemaValidator } from '@0xproject/json-schemas';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
import 'mocha';
|
||||
|
||||
import { ContractWrappers, Token } from '../src';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7;
|
||||
|
||||
describe('TokenRegistryWrapper', () => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
let tokens: Token[];
|
||||
const tokenAddressBySymbol: { [symbol: string]: string } = {};
|
||||
const tokenAddressByName: { [symbol: string]: string } = {};
|
||||
const tokenBySymbol: { [symbol: string]: Token } = {};
|
||||
const tokenByName: { [symbol: string]: Token } = {};
|
||||
const registeredSymbol = 'ZRX';
|
||||
const registeredName = '0x Protocol Token';
|
||||
const unregisteredSymbol = 'MAL';
|
||||
const unregisteredName = 'Malicious Token';
|
||||
const config = {
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
};
|
||||
before(async () => {
|
||||
contractWrappers = new ContractWrappers(provider, config);
|
||||
tokens = await contractWrappers.tokenRegistry.getTokensAsync();
|
||||
_.map(tokens, token => {
|
||||
tokenAddressBySymbol[token.symbol] = token.address;
|
||||
tokenAddressByName[token.name] = token.address;
|
||||
tokenBySymbol[token.symbol] = token;
|
||||
tokenByName[token.name] = token;
|
||||
});
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('#getTokensAsync', () => {
|
||||
it('should return all the tokens added to the tokenRegistry during the migration', async () => {
|
||||
expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
|
||||
|
||||
const schemaValidator = new SchemaValidator();
|
||||
_.each(tokens, token => {
|
||||
const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
|
||||
expect(validationResult.errors).to.have.lengthOf(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('#getTokenAddressesAsync', () => {
|
||||
it('should return all the token addresses added to the tokenRegistry during the migration', async () => {
|
||||
const tokenAddresses = await contractWrappers.tokenRegistry.getTokenAddressesAsync();
|
||||
expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION);
|
||||
|
||||
const schemaValidator = new SchemaValidator();
|
||||
_.each(tokenAddresses, tokenAddress => {
|
||||
const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema);
|
||||
expect(validationResult.errors).to.have.lengthOf(0);
|
||||
expect(tokenAddress).to.not.be.equal(constants.NULL_ADDRESS);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('#getTokenAddressBySymbol', () => {
|
||||
it('should return correct address for a token in the registry', async () => {
|
||||
const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(
|
||||
registeredSymbol,
|
||||
);
|
||||
expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]);
|
||||
});
|
||||
it('should return undefined for a token out of registry', async () => {
|
||||
const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync(
|
||||
unregisteredSymbol,
|
||||
);
|
||||
expect(tokenAddress).to.be.undefined();
|
||||
});
|
||||
});
|
||||
describe('#getTokenAddressByName', () => {
|
||||
it('should return correct address for a token in the registry', async () => {
|
||||
const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync(registeredName);
|
||||
expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]);
|
||||
});
|
||||
it('should return undefined for a token out of registry', async () => {
|
||||
const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync(
|
||||
unregisteredName,
|
||||
);
|
||||
expect(tokenAddress).to.be.undefined();
|
||||
});
|
||||
});
|
||||
describe('#getTokenBySymbol', () => {
|
||||
it('should return correct token for a token in the registry', async () => {
|
||||
const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol);
|
||||
expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]);
|
||||
});
|
||||
it('should return undefined for a token out of registry', async () => {
|
||||
const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol);
|
||||
expect(token).to.be.undefined();
|
||||
});
|
||||
});
|
||||
describe('#getTokenByName', () => {
|
||||
it('should return correct token for a token in the registry', async () => {
|
||||
const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(registeredName);
|
||||
expect(token).to.be.deep.equal(tokenByName[registeredName]);
|
||||
});
|
||||
it('should return undefined for a token out of registry', async () => {
|
||||
const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName);
|
||||
expect(token).to.be.undefined();
|
||||
});
|
||||
});
|
||||
describe('#getTokenIfExistsAsync', () => {
|
||||
it('should return the token added to the tokenRegistry during the migration', async () => {
|
||||
const aToken = tokens[0];
|
||||
|
||||
const token = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(aToken.address);
|
||||
const schemaValidator = new SchemaValidator();
|
||||
const validationResult = schemaValidator.validate(token, schemas.tokenSchema);
|
||||
expect(validationResult.errors).to.have.lengthOf(0);
|
||||
});
|
||||
it('should return return undefined when passed a token address not in the tokenRegistry', async () => {
|
||||
const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
|
||||
const tokenIfExists = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress);
|
||||
expect(tokenIfExists).to.be.undefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { ContractWrappers } from '../src';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
import { provider } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('TokenTransferProxyWrapper', () => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
const config = {
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
};
|
||||
before(async () => {
|
||||
contractWrappers = new ContractWrappers(provider, config);
|
||||
});
|
||||
describe('#isAuthorizedAsync', () => {
|
||||
it('should return false if the address is not authorized', async () => {
|
||||
const isAuthorized = await contractWrappers.proxy.isAuthorizedAsync(constants.NULL_ADDRESS);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
});
|
||||
describe('#getAuthorizedAddressesAsync', () => {
|
||||
it('should return the list of authorized addresses', async () => {
|
||||
const authorizedAddresses = await contractWrappers.proxy.getAuthorizedAddressesAsync();
|
||||
for (const authorizedAddress of authorizedAddresses) {
|
||||
const isAuthorized = await contractWrappers.proxy.isAuthorizedAsync(authorizedAddress);
|
||||
expect(isAuthorized).to.be.true();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
605
packages/contract-wrappers/test/token_wrapper_test.ts
Normal file
605
packages/contract-wrappers/test/token_wrapper_test.ts
Normal file
@@ -0,0 +1,605 @@
|
||||
import { BlockchainLifecycle, callbackErrorReporter, devConstants } from '@0xproject/dev-utils';
|
||||
import { EmptyWalletSubprovider } from '@0xproject/subproviders';
|
||||
import { DoneCallback, Provider } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
import Web3ProviderEngine = require('web3-provider-engine');
|
||||
|
||||
import {
|
||||
ApprovalContractEventArgs,
|
||||
BlockParamLiteral,
|
||||
BlockRange,
|
||||
ContractWrappers,
|
||||
ContractWrappersError,
|
||||
DecodedLogEvent,
|
||||
Token,
|
||||
TokenEvents,
|
||||
TransferContractEventArgs,
|
||||
} from '../src';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
import { constants } from './utils/constants';
|
||||
import { TokenUtils } from './utils/token_utils';
|
||||
import { provider, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('TokenWrapper', () => {
|
||||
let contractWrappers: ContractWrappers;
|
||||
let userAddresses: string[];
|
||||
let tokens: Token[];
|
||||
let tokenUtils: TokenUtils;
|
||||
let coinbase: string;
|
||||
let addressWithoutFunds: string;
|
||||
const config = {
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
};
|
||||
before(async () => {
|
||||
contractWrappers = new ContractWrappers(provider, config);
|
||||
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||
tokens = await contractWrappers.tokenRegistry.getTokensAsync();
|
||||
tokenUtils = new TokenUtils(tokens);
|
||||
coinbase = userAddresses[0];
|
||||
addressWithoutFunds = userAddresses[1];
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('#transferAsync', () => {
|
||||
let token: Token;
|
||||
let transferAmount: BigNumber;
|
||||
before(() => {
|
||||
token = tokens[0];
|
||||
transferAmount = new BigNumber(42);
|
||||
});
|
||||
it('should successfully transfer tokens', async () => {
|
||||
const fromAddress = coinbase;
|
||||
const toAddress = addressWithoutFunds;
|
||||
const preBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress);
|
||||
expect(preBalance).to.be.bignumber.equal(0);
|
||||
await contractWrappers.token.transferAsync(token.address, fromAddress, toAddress, transferAmount);
|
||||
const postBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress);
|
||||
return expect(postBalance).to.be.bignumber.equal(transferAmount);
|
||||
});
|
||||
it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => {
|
||||
const fromAddress = addressWithoutFunds;
|
||||
const toAddress = coinbase;
|
||||
return expect(
|
||||
contractWrappers.token.transferAsync(token.address, fromAddress, toAddress, transferAmount),
|
||||
).to.be.rejectedWith(ContractWrappersError.InsufficientBalanceForTransfer);
|
||||
});
|
||||
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
|
||||
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
|
||||
const fromAddress = coinbase;
|
||||
const toAddress = coinbase;
|
||||
return expect(
|
||||
contractWrappers.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount),
|
||||
).to.be.rejectedWith(ContractWrappersError.TokenContractDoesNotExist);
|
||||
});
|
||||
});
|
||||
describe('#transferFromAsync', () => {
|
||||
let token: Token;
|
||||
let toAddress: string;
|
||||
let senderAddress: string;
|
||||
before(async () => {
|
||||
token = tokens[0];
|
||||
toAddress = addressWithoutFunds;
|
||||
senderAddress = userAddresses[2];
|
||||
});
|
||||
it('should fail to transfer tokens if fromAddress has insufficient allowance set', async () => {
|
||||
const fromAddress = coinbase;
|
||||
const transferAmount = new BigNumber(42);
|
||||
|
||||
const fromAddressBalance = await contractWrappers.token.getBalanceAsync(token.address, fromAddress);
|
||||
expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
|
||||
|
||||
const fromAddressAllowance = await contractWrappers.token.getAllowanceAsync(
|
||||
token.address,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
);
|
||||
expect(fromAddressAllowance).to.be.bignumber.equal(0);
|
||||
|
||||
return expect(
|
||||
contractWrappers.token.transferFromAsync(
|
||||
token.address,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
senderAddress,
|
||||
transferAmount,
|
||||
),
|
||||
).to.be.rejectedWith(ContractWrappersError.InsufficientAllowanceForTransfer);
|
||||
});
|
||||
it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => {
|
||||
const fromAddress = coinbase;
|
||||
const transferAmount = new BigNumber(42);
|
||||
|
||||
await contractWrappers.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount);
|
||||
|
||||
return expect(
|
||||
contractWrappers.token.transferFromAsync(
|
||||
token.address,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
senderAddress,
|
||||
transferAmount,
|
||||
),
|
||||
).to.be.rejectedWith(ContractWrappersError.InsufficientAllowanceForTransfer);
|
||||
});
|
||||
it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
|
||||
const fromAddress = addressWithoutFunds;
|
||||
const transferAmount = new BigNumber(42);
|
||||
|
||||
const fromAddressBalance = await contractWrappers.token.getBalanceAsync(token.address, fromAddress);
|
||||
expect(fromAddressBalance).to.be.bignumber.equal(0);
|
||||
|
||||
await contractWrappers.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
|
||||
const fromAddressAllowance = await contractWrappers.token.getAllowanceAsync(
|
||||
token.address,
|
||||
fromAddress,
|
||||
senderAddress,
|
||||
);
|
||||
expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount);
|
||||
|
||||
return expect(
|
||||
contractWrappers.token.transferFromAsync(
|
||||
token.address,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
senderAddress,
|
||||
transferAmount,
|
||||
),
|
||||
).to.be.rejectedWith(ContractWrappersError.InsufficientBalanceForTransfer);
|
||||
});
|
||||
it('should successfully transfer tokens', async () => {
|
||||
const fromAddress = coinbase;
|
||||
|
||||
const preBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress);
|
||||
expect(preBalance).to.be.bignumber.equal(0);
|
||||
|
||||
const transferAmount = new BigNumber(42);
|
||||
await contractWrappers.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount);
|
||||
|
||||
await contractWrappers.token.transferFromAsync(
|
||||
token.address,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
senderAddress,
|
||||
transferAmount,
|
||||
);
|
||||
const postBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress);
|
||||
return expect(postBalance).to.be.bignumber.equal(transferAmount);
|
||||
});
|
||||
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
|
||||
const fromAddress = coinbase;
|
||||
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
|
||||
return expect(
|
||||
contractWrappers.token.transferFromAsync(
|
||||
nonExistentTokenAddress,
|
||||
fromAddress,
|
||||
toAddress,
|
||||
senderAddress,
|
||||
new BigNumber(42),
|
||||
),
|
||||
).to.be.rejectedWith(ContractWrappersError.TokenContractDoesNotExist);
|
||||
});
|
||||
});
|
||||
describe('#getBalanceAsync', () => {
|
||||
describe('With provider with accounts', () => {
|
||||
it('should return the balance for an existing ERC20 token', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
const balance = await contractWrappers.token.getBalanceAsync(token.address, ownerAddress);
|
||||
const expectedBalance = new BigNumber('1000000000000000000000000000');
|
||||
return expect(balance).to.be.bignumber.equal(expectedBalance);
|
||||
});
|
||||
it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => {
|
||||
const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065';
|
||||
const ownerAddress = coinbase;
|
||||
return expect(
|
||||
contractWrappers.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress),
|
||||
).to.be.rejectedWith(ContractWrappersError.TokenContractDoesNotExist);
|
||||
});
|
||||
it('should return a balance of 0 for a non-existent owner address', async () => {
|
||||
const token = tokens[0];
|
||||
const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
|
||||
const balance = await contractWrappers.token.getBalanceAsync(token.address, nonExistentOwner);
|
||||
const expectedBalance = new BigNumber(0);
|
||||
return expect(balance).to.be.bignumber.equal(expectedBalance);
|
||||
});
|
||||
});
|
||||
describe('With provider without accounts', () => {
|
||||
let zeroExContractWithoutAccounts: ContractWrappers;
|
||||
before(async () => {
|
||||
const hasAddresses = false;
|
||||
const emptyWalletProvider = addEmptyWalletSubprovider(provider);
|
||||
zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config);
|
||||
});
|
||||
it('should return balance even when called with provider instance without addresses', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
const balance = await zeroExContractWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress);
|
||||
const expectedBalance = new BigNumber('1000000000000000000000000000');
|
||||
return expect(balance).to.be.bignumber.equal(expectedBalance);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('#setAllowanceAsync', () => {
|
||||
it("should set the spender's allowance", async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
const spenderAddress = addressWithoutFunds;
|
||||
|
||||
const allowanceBeforeSet = await contractWrappers.token.getAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
);
|
||||
const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
|
||||
expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
|
||||
|
||||
const amountInBaseUnits = new BigNumber(50);
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
amountInBaseUnits,
|
||||
);
|
||||
|
||||
const allowanceAfterSet = await contractWrappers.token.getAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
);
|
||||
const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
|
||||
return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
|
||||
});
|
||||
});
|
||||
describe('#setUnlimitedAllowanceAsync', () => {
|
||||
it("should set the unlimited spender's allowance", async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
const spenderAddress = addressWithoutFunds;
|
||||
|
||||
await contractWrappers.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress);
|
||||
const allowance = await contractWrappers.token.getAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
);
|
||||
return expect(allowance).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
it('should reduce the gas cost for transfers including tokens with unlimited allowance support', async () => {
|
||||
const transferAmount = new BigNumber(5);
|
||||
const zrx = tokenUtils.getProtocolTokenOrThrow();
|
||||
const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses;
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
zrx.address,
|
||||
coinbase,
|
||||
userWithNormalAllowance,
|
||||
transferAmount,
|
||||
);
|
||||
await contractWrappers.token.setUnlimitedAllowanceAsync(zrx.address, coinbase, userWithUnlimitedAllowance);
|
||||
|
||||
const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
|
||||
const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
|
||||
userWithUnlimitedAllowance,
|
||||
);
|
||||
|
||||
await contractWrappers.token.transferFromAsync(
|
||||
zrx.address,
|
||||
coinbase,
|
||||
userWithNormalAllowance,
|
||||
userWithNormalAllowance,
|
||||
transferAmount,
|
||||
);
|
||||
await contractWrappers.token.transferFromAsync(
|
||||
zrx.address,
|
||||
coinbase,
|
||||
userWithUnlimitedAllowance,
|
||||
userWithUnlimitedAllowance,
|
||||
transferAmount,
|
||||
);
|
||||
|
||||
const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
|
||||
const finalBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
|
||||
userWithUnlimitedAllowance,
|
||||
);
|
||||
|
||||
const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance);
|
||||
const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance);
|
||||
|
||||
// In theory the gas cost with unlimited allowance should be smaller, but with testrpc it's actually bigger.
|
||||
// This needs to be investigated in ethereumjs-vm. This test is essentially a repro.
|
||||
// TODO: Make this test pass with inverted assertion.
|
||||
expect(unlimitedGasCost.toNumber()).to.be.gt(normalGasCost.toNumber());
|
||||
});
|
||||
});
|
||||
describe('#getAllowanceAsync', () => {
|
||||
describe('With provider with accounts', () => {
|
||||
it('should get the proxy allowance', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
const spenderAddress = addressWithoutFunds;
|
||||
|
||||
const amountInBaseUnits = new BigNumber(50);
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
amountInBaseUnits,
|
||||
);
|
||||
|
||||
const allowance = await contractWrappers.token.getAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
);
|
||||
const expectedAllowance = amountInBaseUnits;
|
||||
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
|
||||
});
|
||||
it('should return 0 if no allowance set yet', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
const spenderAddress = addressWithoutFunds;
|
||||
const allowance = await contractWrappers.token.getAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
);
|
||||
const expectedAllowance = new BigNumber(0);
|
||||
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
|
||||
});
|
||||
});
|
||||
describe('With provider without accounts', () => {
|
||||
let zeroExContractWithoutAccounts: ContractWrappers;
|
||||
before(async () => {
|
||||
const hasAddresses = false;
|
||||
const emptyWalletProvider = addEmptyWalletSubprovider(provider);
|
||||
zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config);
|
||||
});
|
||||
it('should get the proxy allowance', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
const spenderAddress = addressWithoutFunds;
|
||||
|
||||
const amountInBaseUnits = new BigNumber(50);
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
amountInBaseUnits,
|
||||
);
|
||||
|
||||
const allowance = await zeroExContractWithoutAccounts.token.getAllowanceAsync(
|
||||
token.address,
|
||||
ownerAddress,
|
||||
spenderAddress,
|
||||
);
|
||||
const expectedAllowance = amountInBaseUnits;
|
||||
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('#getProxyAllowanceAsync', () => {
|
||||
it('should get the proxy allowance', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
|
||||
const amountInBaseUnits = new BigNumber(50);
|
||||
await contractWrappers.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
|
||||
|
||||
const allowance = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress);
|
||||
const expectedAllowance = amountInBaseUnits;
|
||||
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
|
||||
});
|
||||
});
|
||||
describe('#setProxyAllowanceAsync', () => {
|
||||
it('should set the proxy allowance', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
|
||||
const allowanceBeforeSet = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress);
|
||||
const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
|
||||
expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
|
||||
|
||||
const amountInBaseUnits = new BigNumber(50);
|
||||
await contractWrappers.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits);
|
||||
|
||||
const allowanceAfterSet = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress);
|
||||
const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
|
||||
return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
|
||||
});
|
||||
});
|
||||
describe('#setUnlimitedProxyAllowanceAsync', () => {
|
||||
it('should set the unlimited proxy allowance', async () => {
|
||||
const token = tokens[0];
|
||||
const ownerAddress = coinbase;
|
||||
|
||||
await contractWrappers.token.setUnlimitedProxyAllowanceAsync(token.address, ownerAddress);
|
||||
const allowance = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress);
|
||||
return expect(allowance).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
});
|
||||
describe('#subscribe', () => {
|
||||
const indexFilterValues = {};
|
||||
let tokenAddress: string;
|
||||
const transferAmount = new BigNumber(42);
|
||||
const allowanceAmount = new BigNumber(42);
|
||||
before(() => {
|
||||
const token = tokens[0];
|
||||
tokenAddress = token.address;
|
||||
});
|
||||
afterEach(() => {
|
||||
contractWrappers.token.unsubscribeAll();
|
||||
});
|
||||
// Hack: Mocha does not allow a test to be both async and have a `done` callback
|
||||
// Since we need to await the receipt of the event in the `subscribe` callback,
|
||||
// we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
|
||||
// wrap the rest of the test in an async block
|
||||
// Source: https://github.com/mochajs/mocha/issues/2407
|
||||
it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<TransferContractEventArgs>) => {
|
||||
expect(logEvent.isRemoved).to.be.false();
|
||||
expect(logEvent.log.logIndex).to.be.equal(0);
|
||||
expect(logEvent.log.transactionIndex).to.be.equal(0);
|
||||
expect(logEvent.log.blockNumber).to.be.a('number');
|
||||
const args = logEvent.log.args;
|
||||
expect(args._from).to.be.equal(coinbase);
|
||||
expect(args._to).to.be.equal(addressWithoutFunds);
|
||||
expect(args._value).to.be.bignumber.equal(transferAmount);
|
||||
},
|
||||
);
|
||||
contractWrappers.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback);
|
||||
await contractWrappers.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
|
||||
expect(logEvent).to.not.be.undefined();
|
||||
expect(logEvent.isRemoved).to.be.false();
|
||||
const args = logEvent.log.args;
|
||||
expect(args._owner).to.be.equal(coinbase);
|
||||
expect(args._spender).to.be.equal(addressWithoutFunds);
|
||||
expect(args._value).to.be.bignumber.equal(allowanceAmount);
|
||||
},
|
||||
);
|
||||
contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback);
|
||||
await contractWrappers.token.setAllowanceAsync(
|
||||
tokenAddress,
|
||||
coinbase,
|
||||
addressWithoutFunds,
|
||||
allowanceAmount,
|
||||
);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
|
||||
done(new Error('Expected this subscription to have been cancelled'));
|
||||
},
|
||||
);
|
||||
contractWrappers.token.subscribe(
|
||||
tokenAddress,
|
||||
TokenEvents.Transfer,
|
||||
indexFilterValues,
|
||||
callbackNeverToBeCalled,
|
||||
);
|
||||
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
|
||||
contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID);
|
||||
contractWrappers.token.subscribe(
|
||||
tokenAddress,
|
||||
TokenEvents.Transfer,
|
||||
indexFilterValues,
|
||||
callbackToBeCalled,
|
||||
);
|
||||
await contractWrappers.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
|
||||
})().catch(done);
|
||||
});
|
||||
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
|
||||
(async () => {
|
||||
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
|
||||
(logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => {
|
||||
done(new Error('Expected this subscription to have been cancelled'));
|
||||
},
|
||||
);
|
||||
const subscriptionToken = contractWrappers.token.subscribe(
|
||||
tokenAddress,
|
||||
TokenEvents.Transfer,
|
||||
indexFilterValues,
|
||||
callbackNeverToBeCalled,
|
||||
);
|
||||
contractWrappers.token.unsubscribe(subscriptionToken);
|
||||
await contractWrappers.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount);
|
||||
done();
|
||||
})().catch(done);
|
||||
});
|
||||
});
|
||||
describe('#getLogsAsync', () => {
|
||||
let tokenAddress: string;
|
||||
let tokenTransferProxyAddress: string;
|
||||
const blockRange: BlockRange = {
|
||||
fromBlock: 0,
|
||||
toBlock: BlockParamLiteral.Latest,
|
||||
};
|
||||
let txHash: string;
|
||||
before(() => {
|
||||
const token = tokens[0];
|
||||
tokenAddress = token.address;
|
||||
tokenTransferProxyAddress = contractWrappers.proxy.getContractAddress();
|
||||
});
|
||||
it('should get logs with decoded args emitted by Approval', async () => {
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const eventName = TokenEvents.Approval;
|
||||
const indexFilterValues = {};
|
||||
const logs = await contractWrappers.token.getLogsAsync<ApprovalContractEventArgs>(
|
||||
tokenAddress,
|
||||
eventName,
|
||||
blockRange,
|
||||
indexFilterValues,
|
||||
);
|
||||
expect(logs).to.have.length(1);
|
||||
const args = logs[0].args;
|
||||
expect(logs[0].event).to.be.equal(eventName);
|
||||
expect(args._owner).to.be.equal(coinbase);
|
||||
expect(args._spender).to.be.equal(tokenTransferProxyAddress);
|
||||
expect(args._value).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
|
||||
});
|
||||
it('should only get the logs with the correct event name', async () => {
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const differentEventName = TokenEvents.Transfer;
|
||||
const indexFilterValues = {};
|
||||
const logs = await contractWrappers.token.getLogsAsync(
|
||||
tokenAddress,
|
||||
differentEventName,
|
||||
blockRange,
|
||||
indexFilterValues,
|
||||
);
|
||||
expect(logs).to.have.length(0);
|
||||
});
|
||||
it('should only get the logs with the correct indexed fields', async () => {
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, addressWithoutFunds);
|
||||
await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const eventName = TokenEvents.Approval;
|
||||
const indexFilterValues = {
|
||||
_owner: coinbase,
|
||||
};
|
||||
const logs = await contractWrappers.token.getLogsAsync<ApprovalContractEventArgs>(
|
||||
tokenAddress,
|
||||
eventName,
|
||||
blockRange,
|
||||
indexFilterValues,
|
||||
);
|
||||
expect(logs).to.have.length(1);
|
||||
const args = logs[0].args;
|
||||
expect(args._owner).to.be.equal(coinbase);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
||||
|
||||
function addEmptyWalletSubprovider(p: Provider): Provider {
|
||||
const providerEngine = new Web3ProviderEngine();
|
||||
providerEngine.addProvider(new EmptyWalletSubprovider());
|
||||
const currentSubproviders = (p as any)._providers;
|
||||
for (const subprovider of currentSubproviders) {
|
||||
providerEngine.addProvider(subprovider);
|
||||
}
|
||||
providerEngine.start();
|
||||
return providerEngine;
|
||||
}
|
||||
13
packages/contract-wrappers/test/utils/chai_setup.ts
Normal file
13
packages/contract-wrappers/test/utils/chai_setup.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as chai from 'chai';
|
||||
import chaiAsPromised = require('chai-as-promised');
|
||||
import ChaiBigNumber = require('chai-bignumber');
|
||||
import * as dirtyChai from 'dirty-chai';
|
||||
|
||||
export const chaiSetup = {
|
||||
configure() {
|
||||
chai.config.includeStack = true;
|
||||
chai.use(ChaiBigNumber());
|
||||
chai.use(dirtyChai);
|
||||
chai.use(chaiAsPromised);
|
||||
},
|
||||
};
|
||||
9
packages/contract-wrappers/test/utils/constants.ts
Normal file
9
packages/contract-wrappers/test/utils/constants.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const constants = {
|
||||
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
|
||||
ROPSTEN_NETWORK_ID: 3,
|
||||
KOVAN_NETWORK_ID: 42,
|
||||
TESTRPC_NETWORK_ID: 50,
|
||||
KOVAN_RPC_URL: 'https://kovan.infura.io/',
|
||||
ROPSTEN_RPC_URL: 'https://ropsten.infura.io/',
|
||||
ZRX_DECIMALS: 18,
|
||||
};
|
||||
18
packages/contract-wrappers/test/utils/deployer.ts
Normal file
18
packages/contract-wrappers/test/utils/deployer.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Deployer } from '@0xproject/deployer';
|
||||
import { devConstants } from '@0xproject/dev-utils';
|
||||
import * as path from 'path';
|
||||
|
||||
import { constants } from './constants';
|
||||
|
||||
import { provider } from './web3_wrapper';
|
||||
|
||||
const artifactsDir = path.resolve('test', 'artifacts');
|
||||
const deployerOpts = {
|
||||
artifactsDir,
|
||||
provider,
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
defaults: {
|
||||
gas: devConstants.GAS_ESTIMATE,
|
||||
},
|
||||
};
|
||||
export const deployer = new Deployer(deployerOpts);
|
||||
33
packages/contract-wrappers/test/utils/token_utils.ts
Normal file
33
packages/contract-wrappers/test/utils/token_utils.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { InternalContractWrappersError, Token } from '../../src/types';
|
||||
|
||||
const PROTOCOL_TOKEN_SYMBOL = 'ZRX';
|
||||
const WETH_TOKEN_SYMBOL = 'WETH';
|
||||
|
||||
export class TokenUtils {
|
||||
private _tokens: Token[];
|
||||
constructor(tokens: Token[]) {
|
||||
this._tokens = tokens;
|
||||
}
|
||||
public getProtocolTokenOrThrow(): Token {
|
||||
const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL });
|
||||
if (_.isUndefined(zrxToken)) {
|
||||
throw new Error(InternalContractWrappersError.ZrxNotInTokenRegistry);
|
||||
}
|
||||
return zrxToken;
|
||||
}
|
||||
public getWethTokenOrThrow(): Token {
|
||||
const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL });
|
||||
if (_.isUndefined(wethToken)) {
|
||||
throw new Error(InternalContractWrappersError.WethNotInTokenRegistry);
|
||||
}
|
||||
return wethToken;
|
||||
}
|
||||
public getDummyTokens(): Token[] {
|
||||
const dummyTokens = _.filter(this._tokens, token => {
|
||||
return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol);
|
||||
});
|
||||
return dummyTokens;
|
||||
}
|
||||
}
|
||||
9
packages/contract-wrappers/test/utils/web3_wrapper.ts
Normal file
9
packages/contract-wrappers/test/utils/web3_wrapper.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { devConstants, web3Factory } from '@0xproject/dev-utils';
|
||||
import { Provider } from '@0xproject/types';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
|
||||
const web3 = web3Factory.create({ shouldUseInProcessGanache: true });
|
||||
const provider: Provider = web3.currentProvider;
|
||||
const web3Wrapper = new Web3Wrapper(web3.currentProvider);
|
||||
|
||||
export { provider, web3Wrapper };
|
||||
Reference in New Issue
Block a user