switch @0x/contract-wrappers to generated wrappers (#2037)

* switch @0x/contract-wrappers to generated wrappers

- remove TransactionEncoder
- move TokenUtils to @0x/dev-utils
- detailed changes in #2040
This commit is contained in:
Xianny
2019-08-08 07:29:30 -07:00
committed by GitHub
parent 5ac7ff7084
commit 8ce390be3c
112 changed files with 37252 additions and 7181 deletions

View File

@@ -8,6 +8,7 @@ import * as _ from 'lodash';
import 'mocha';
import { ContractAddresses, ContractWrappers } from '../src';
import { getAbiEncodedTransactionData } from '../src/utils/getAbiEncodedTransactionData';
import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
@@ -80,8 +81,14 @@ describe('ABI Decoding Calldata', () => {
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
const transactionEncoder = await contractWrappers.exchange.transactionEncoderAsync();
matchOrdersTxData = transactionEncoder.matchOrdersTx(signedOrderLeft, signedOrderRight);
matchOrdersTxData = getAbiEncodedTransactionData(
contractWrappers.exchange,
'matchOrders',
signedOrderLeft,
signedOrderRight,
signedOrderLeft.signature,
signedOrderRight.signature,
);
});
describe('decode', () => {

View File

@@ -1,59 +0,0 @@
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
import { calldataOptimizationUtils } from '../src/utils/calldata_optimization_utils';
import { constants } from '../src/utils/constants';
import { chaiSetup } from './utils/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
// utility for generating a set of order objects with mostly NULL values
// except for a specified makerAssetData and takerAssetData
const FAKE_ORDERS_COUNT = 5;
const generateFakeOrders = (makerAssetData: string, takerAssetData: string) =>
_.map(_.range(FAKE_ORDERS_COUNT), index => {
const order = orderFactory.createOrder(
constants.NULL_ADDRESS,
constants.ZERO_AMOUNT,
makerAssetData,
constants.ZERO_AMOUNT,
takerAssetData,
constants.NULL_ADDRESS,
);
return {
...order,
signature: 'dummy signature',
};
});
describe('calldataOptimizationUtils', () => {
const fakeMakerAssetData = 'fakeMakerAssetData';
const fakeTakerAssetData = 'fakeTakerAssetData';
const orders = generateFakeOrders(fakeMakerAssetData, fakeTakerAssetData);
describe('#optimizeForwarderOrders', () => {
it('should make makerAssetData `0x` unless first order', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders);
expect(optimizedOrders[0].makerAssetData).to.equal(fakeMakerAssetData);
const ordersWithoutHead = _.slice(optimizedOrders, 1);
_.forEach(ordersWithoutHead, order => expect(order.makerAssetData).to.equal(constants.NULL_BYTES));
});
it('should make all takerAssetData `0x`', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders);
_.forEach(optimizedOrders, order => expect(order.takerAssetData).to.equal(constants.NULL_BYTES));
});
});
describe('#optimizeForwarderFeeOrders', () => {
it('should make all makerAssetData `0x`', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(orders);
_.forEach(optimizedOrders, order => expect(order.makerAssetData).to.equal(constants.NULL_BYTES));
});
it('should make all takerAssetData `0x`', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(orders);
_.forEach(optimizedOrders, order => expect(order.takerAssetData).to.equal(constants.NULL_BYTES));
});
});
});

View File

@@ -1,7 +1,7 @@
import { CoordinatorRegistryContract } from '@0x/abi-gen-wrappers';
import { constants } from '@0x/contracts-test-utils';
import { defaultOrmConfig, getAppAsync } from '@0x/coordinator-server';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
@@ -16,7 +16,6 @@ import { CoordinatorServerErrorMsg } from '../src/utils/coordinator_server_types
import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
@@ -71,7 +70,7 @@ describe('CoordinatorWrapper', () => {
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
zrxTokenAddress = contractAddresses.zrxToken;
fillScenarios = new FillScenarios(
provider,
userAddresses,
@@ -422,7 +421,7 @@ describe('CoordinatorWrapper', () => {
txHash = await contractWrappers.coordinator.hardCancelOrdersUpToAsync(targetOrderEpoch, makerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderEpoch = await contractWrappers.exchange.getOrderEpochAsync(
const orderEpoch = await contractWrappers.exchange.orderEpoch.callAsync(
makerAddress,
contractWrappers.coordinator.address,
);

View File

@@ -1,143 +0,0 @@
import { expectTransactionFailedAsync, getLatestBlockTimestampAsync } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { ERC20AssetData, RevertReason, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import { ContractWrappers, DutchAuctionWrapper } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { DutchAuctionUtils } from './utils/dutch_auction_utils';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:custom-no-magic-numbers
describe('DutchAuctionWrapper', () => {
const makerAssetAmount = new BigNumber(5);
const auctionEndTakerAmount = new BigNumber(10);
const auctionBeginTakerAmount = auctionEndTakerAmount.times(2);
const tenMinutesInSeconds = 10 * 60;
let contractWrappers: ContractWrappers;
let exchangeContractAddress: string;
let userAddresses: string[];
let makerAddress: string;
let takerAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let buyOrder: SignedOrder;
let sellOrder: SignedOrder;
let makerTokenAssetData: string;
let takerTokenAssetData: string;
let auctionBeginTimeSeconds: BigNumber;
let auctionEndTimeSeconds: BigNumber;
before(async () => {
// setup contract wrappers & addresses
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
[, makerAddress, takerAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
// construct asset data for tokens being swapped
[makerTokenAssetData, takerTokenAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
// setup auction details in maker asset data
const currentBlockTimestamp: number = await getLatestBlockTimestampAsync();
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp + tenMinutesInSeconds);
// create auction orders
const coinbase = userAddresses[0];
const dutchAuctionUtils = new DutchAuctionUtils(
web3Wrapper,
coinbase,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
);
sellOrder = await dutchAuctionUtils.createSignedSellOrderAsync(
auctionBeginTimeSeconds,
auctionEndTimeSeconds,
auctionBeginTakerAmount,
auctionEndTakerAmount,
makerAssetAmount,
makerTokenAssetData,
takerTokenAssetData,
makerAddress,
constants.NULL_ADDRESS,
);
buyOrder = await dutchAuctionUtils.createSignedBuyOrderAsync(sellOrder, takerAddress);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('.decodeDutchAuctionAssetData', () => {
it('decodes to the encoded values', async () => {
const encodedAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
makerTokenAssetData,
auctionBeginTimeSeconds,
makerAssetAmount,
);
const decodedAssetData = DutchAuctionWrapper.decodeDutchAuctionData(encodedAssetData);
// tslint:disable-next-line:no-unnecessary-type-assertion
const erc20AssetData = decodedAssetData.assetData as ERC20AssetData;
expect(erc20AssetData.tokenAddress).to.eq(makerTokenAddress);
expect(decodedAssetData.beginAmount).to.be.bignumber.eq(makerAssetAmount);
expect(decodedAssetData.beginTimeSeconds).to.be.bignumber.eq(auctionBeginTimeSeconds);
});
});
describe('#matchOrdersAsync', () => {
it('should match two orders', async () => {
const txHash = await contractWrappers.dutchAuction.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
it('should throw when invalid transaction and shouldValidate is true', async () => {
// request match with bad buy/sell orders
const badSellOrder = buyOrder;
const badBuyOrder = sellOrder;
return expectTransactionFailedAsync(
contractWrappers.dutchAuction.matchOrdersAsync(badBuyOrder, badSellOrder, takerAddress, {
shouldValidate: true,
}),
RevertReason.InvalidAssetData,
);
});
});
describe('#getAuctionDetailsAsync', () => {
it('should get auction details', async () => {
// get auction details
const auctionDetails = await contractWrappers.dutchAuction.getAuctionDetailsAsync(sellOrder);
// run some basic sanity checks on the return value
expect(auctionDetails.beginTimeSeconds, 'auctionDetails.beginTimeSeconds').to.be.bignumber.equal(
auctionBeginTimeSeconds,
);
expect(auctionDetails.beginAmount, 'auctionDetails.beginAmount').to.be.bignumber.equal(
auctionBeginTakerAmount,
);
expect(auctionDetails.endTimeSeconds, 'auctionDetails.endTimeSeconds').to.be.bignumber.equal(
auctionEndTimeSeconds,
);
});
});
});

View File

@@ -1,39 +0,0 @@
import * as chai from 'chai';
import { ContractWrappers } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { provider } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
describe('ERC20ProxyWrapper', () => {
let contractWrappers: ContractWrappers;
before(async () => {
const contractAddresses = await migrateOnceAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
});
describe('#isAuthorizedAsync', () => {
it('should return false if the address is not authorized', async () => {
const isAuthorized = await contractWrappers.erc20Proxy.isAuthorizedAsync(constants.NULL_ADDRESS);
expect(isAuthorized).to.be.false();
});
});
describe('#getAuthorizedAddressesAsync', () => {
it('should return the list of authorized addresses', async () => {
const authorizedAddresses = await contractWrappers.erc20Proxy.getAuthorizedAddressesAsync();
for (const authorizedAddress of authorizedAddresses) {
const isAuthorized = await contractWrappers.erc20Proxy.isAuthorizedAsync(authorizedAddress);
expect(isAuthorized).to.be.true();
}
});
});
});

View File

@@ -1,633 +0,0 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { EmptyWalletSubprovider, Web3ProviderEngine } from '@0x/subproviders';
import { DoneCallback } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import {
BlockParamLiteral,
BlockRange,
ContractWrappers,
ContractWrappersConfig,
ContractWrappersError,
DecodedLogEvent,
ERC20TokenApprovalEventArgs,
ERC20TokenEvents,
ERC20TokenTransferEventArgs,
} from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
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('ERC20Wrapper', () => {
let contractWrappers: ContractWrappers;
let contractAddresses: ContractAddresses;
let userAddresses: string[];
let tokens: string[];
let coinbase: string;
let addressWithoutFunds: string;
let config: ContractWrappersConfig;
before(async () => {
contractAddresses = await migrateOnceAsync();
config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
tokens = tokenUtils.getDummyERC20TokenAddresses();
coinbase = userAddresses[0];
addressWithoutFunds = userAddresses[1];
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#transferAsync', () => {
let tokenAddress: string;
let transferAmount: BigNumber;
before(() => {
tokenAddress = tokens[0];
transferAmount = new BigNumber(42);
});
it('should successfully transfer tokens', async () => {
const fromAddress = coinbase;
const toAddress = addressWithoutFunds;
const preBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress);
expect(preBalance).to.be.bignumber.equal(0);
await contractWrappers.erc20Token.transferAsync(tokenAddress, fromAddress, toAddress, transferAmount);
const postBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, 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.erc20Token.transferAsync(tokenAddress, fromAddress, toAddress, transferAmount),
).to.be.rejectedWith(ContractWrappersError.InsufficientBalanceForTransfer);
});
});
describe('#transferFromAsync', () => {
let tokenAddress: string;
let toAddress: string;
let senderAddress: string;
before(async () => {
tokenAddress = 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.erc20Token.getBalanceAsync(tokenAddress, fromAddress);
expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
const fromAddressAllowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
fromAddress,
toAddress,
);
expect(fromAddressAllowance).to.be.bignumber.equal(0);
return expect(
contractWrappers.erc20Token.transferFromAsync(
tokenAddress,
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.erc20Token.setAllowanceAsync(tokenAddress, fromAddress, toAddress, transferAmount);
return expect(
contractWrappers.erc20Token.transferFromAsync(
tokenAddress,
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.erc20Token.getBalanceAsync(tokenAddress, fromAddress);
expect(fromAddressBalance).to.be.bignumber.equal(0);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
fromAddress,
senderAddress,
transferAmount,
);
const fromAddressAllowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
fromAddress,
senderAddress,
);
expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount);
return expect(
contractWrappers.erc20Token.transferFromAsync(
tokenAddress,
fromAddress,
toAddress,
senderAddress,
transferAmount,
),
).to.be.rejectedWith(ContractWrappersError.InsufficientBalanceForTransfer);
});
it('should successfully transfer tokens', async () => {
const fromAddress = coinbase;
const preBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress);
expect(preBalance).to.be.bignumber.equal(0);
const transferAmount = new BigNumber(42);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
fromAddress,
senderAddress,
transferAmount,
);
await contractWrappers.erc20Token.transferFromAsync(
tokenAddress,
fromAddress,
toAddress,
senderAddress,
transferAmount,
);
const postBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress);
return expect(postBalance).to.be.bignumber.equal(transferAmount);
});
});
describe('#getBalanceAsync', () => {
describe('With provider with accounts', () => {
it('should return the balance for an existing ERC20 token', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const balance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, ownerAddress);
const expectedBalance = new BigNumber('1000000000000000000000000000');
return expect(balance).to.be.bignumber.equal(expectedBalance);
});
it('should return a balance of 0 for a non-existent owner address', async () => {
const tokenAddress = tokens[0];
const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
const balance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, 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 emptyWalletProvider = addEmptyWalletSubprovider(provider);
zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config);
});
it('should return balance even when called with provider instance without addresses', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const balance = await zeroExContractWithoutAccounts.erc20Token.getBalanceAsync(
tokenAddress,
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 tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const allowanceBeforeSet = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
amountInBaseUnits,
);
const allowanceAfterSet = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
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 tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
await contractWrappers.erc20Token.setUnlimitedAllowanceAsync(tokenAddress, ownerAddress, spenderAddress);
const allowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
return expect(allowance).to.be.bignumber.equal(
contractWrappers.erc20Token.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 zrxAddress = contractAddresses.zrxToken;
const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses;
await contractWrappers.erc20Token.setAllowanceAsync(
zrxAddress,
coinbase,
userWithNormalAllowance,
transferAmount,
);
await contractWrappers.erc20Token.setUnlimitedAllowanceAsync(
zrxAddress,
coinbase,
userWithUnlimitedAllowance,
);
const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
userWithUnlimitedAllowance,
);
await contractWrappers.erc20Token.transferFromAsync(
zrxAddress,
coinbase,
userWithNormalAllowance,
userWithNormalAllowance,
transferAmount,
);
await contractWrappers.erc20Token.transferFromAsync(
zrxAddress,
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 tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
amountInBaseUnits,
);
const allowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
it('should return 0 if no allowance set yet', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const allowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
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 emptyWalletProvider = addEmptyWalletSubprovider(provider);
zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config);
});
it('should get the proxy allowance', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
amountInBaseUnits,
);
const allowance = await zeroExContractWithoutAccounts.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
});
});
describe('#getProxyAllowanceAsync', () => {
it('should get the proxy allowance', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setProxyAllowanceAsync(tokenAddress, ownerAddress, amountInBaseUnits);
const allowance = await contractWrappers.erc20Token.getProxyAllowanceAsync(tokenAddress, ownerAddress);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
});
describe('#setProxyAllowanceAsync', () => {
it('should set the proxy allowance', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const allowanceBeforeSet = await contractWrappers.erc20Token.getProxyAllowanceAsync(
tokenAddress,
ownerAddress,
);
const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setProxyAllowanceAsync(tokenAddress, ownerAddress, amountInBaseUnits);
const allowanceAfterSet = await contractWrappers.erc20Token.getProxyAllowanceAsync(
tokenAddress,
ownerAddress,
);
const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
});
});
describe('#setUnlimitedProxyAllowanceAsync', () => {
it('should set the unlimited proxy allowance', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, ownerAddress);
const allowance = await contractWrappers.erc20Token.getProxyAllowanceAsync(tokenAddress, ownerAddress);
return expect(allowance).to.be.bignumber.equal(
contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
);
});
});
describe('#subscribe', () => {
const indexFilterValues = {};
let tokenAddress: string;
const transferAmount = new BigNumber(42);
const allowanceAmount = new BigNumber(42);
before(() => {
tokenAddress = tokens[0];
});
afterEach(() => {
contractWrappers.erc20Token.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<ERC20TokenTransferEventArgs>) => {
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.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Transfer,
indexFilterValues,
callback,
);
await contractWrappers.erc20Token.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<ERC20TokenApprovalEventArgs>) => {
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.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Approval,
indexFilterValues,
callback,
);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
coinbase,
addressWithoutFunds,
allowanceAmount,
);
})().catch(done);
});
it('Outstanding subscriptions are cancelled when contractWrappers.unsubscribeAll called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_logEvent: DecodedLogEvent<ERC20TokenApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
contractWrappers.unsubscribeAll();
contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Transfer,
indexFilterValues,
callbackToBeCalled,
);
await contractWrappers.erc20Token.transferAsync(
tokenAddress,
coinbase,
addressWithoutFunds,
transferAmount,
);
})().catch(done);
});
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_logEvent: DecodedLogEvent<ERC20TokenApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
const subscriptionToken = contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
contractWrappers.erc20Token.unsubscribe(subscriptionToken);
await contractWrappers.erc20Token.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(() => {
tokenAddress = tokens[0];
tokenTransferProxyAddress = contractWrappers.erc20Proxy.address;
});
it('should get logs with decoded args emitted by Approval', async () => {
txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = ERC20TokenEvents.Approval;
const indexFilterValues = {};
const logs = await contractWrappers.erc20Token.getLogsAsync<ERC20TokenApprovalEventArgs>(
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.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
});
it('should only get the logs with the correct event name', async () => {
txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const differentEventName = ERC20TokenEvents.Transfer;
const indexFilterValues = {};
const logs = await contractWrappers.erc20Token.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.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
tokenAddress,
addressWithoutFunds,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = ERC20TokenEvents.Approval;
const indexFilterValues = {
_owner: coinbase,
};
const logs = await contractWrappers.erc20Token.getLogsAsync<ERC20TokenApprovalEventArgs>(
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: Web3ProviderEngine): Web3ProviderEngine {
const providerEngine = new Web3ProviderEngine();
providerEngine.addProvider(new EmptyWalletSubprovider());
const currentSubproviders = (p as any)._providers;
for (const subprovider of currentSubproviders) {
providerEngine.addProvider(subprovider);
}
providerUtils.startProviderEngine(providerEngine);
return providerEngine;
}

View File

@@ -1,39 +0,0 @@
import * as chai from 'chai';
import { ContractWrappers } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { provider } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
describe('ERC721ProxyWrapper', () => {
let contractWrappers: ContractWrappers;
before(async () => {
const contractAddresses = await migrateOnceAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
});
describe('#isAuthorizedAsync', () => {
it('should return false if the address is not authorized', async () => {
const isAuthorized = await contractWrappers.erc721Proxy.isAuthorizedAsync(constants.NULL_ADDRESS);
expect(isAuthorized).to.be.false();
});
});
describe('#getAuthorizedAddressesAsync', () => {
it('should return the list of authorized addresses', async () => {
const authorizedAddresses = await contractWrappers.erc721Proxy.getAuthorizedAddressesAsync();
for (const authorizedAddress of authorizedAddresses) {
const isAuthorized = await contractWrappers.erc721Proxy.isAuthorizedAsync(authorizedAddress);
expect(isAuthorized).to.be.true();
}
});
});
});

View File

@@ -1,465 +0,0 @@
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { EmptyWalletSubprovider, Web3ProviderEngine } from '@0x/subproviders';
import { DoneCallback } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import {
BlockParamLiteral,
BlockRange,
ContractWrappers,
ContractWrappersConfig,
ContractWrappersError,
DecodedLogEvent,
ERC721TokenApprovalEventArgs,
ERC721TokenApprovalForAllEventArgs,
ERC721TokenEvents,
ERC721TokenTransferEventArgs,
} from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
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('ERC721Wrapper', () => {
let contractWrappers: ContractWrappers;
let userAddresses: string[];
let tokens: string[];
let ownerAddress: string;
let tokenAddress: string;
let anotherOwnerAddress: string;
let operatorAddress: string;
let approvedAddress: string;
let receiverAddress: string;
let config: ContractWrappersConfig;
before(async () => {
const contractAddresses = await migrateOnceAsync();
config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
tokens = tokenUtils.getDummyERC721TokenAddresses();
tokenAddress = tokens[0];
[ownerAddress, operatorAddress, anotherOwnerAddress, approvedAddress, receiverAddress] = userAddresses;
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#transferFromAsync', () => {
it('should fail to transfer NFT if fromAddress has no approvals set', async () => {
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
return expect(
contractWrappers.erc721Token.transferFromAsync(tokenAddress, receiverAddress, approvedAddress, tokenId),
).to.be.rejectedWith(ContractWrappersError.ERC721NoApproval);
});
it('should successfully transfer tokens when sender is an approved address', async () => {
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
let txHash = await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const owner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
expect(owner).to.be.equal(ownerAddress);
txHash = await contractWrappers.erc721Token.transferFromAsync(
tokenAddress,
receiverAddress,
approvedAddress,
tokenId,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const newOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
expect(newOwner).to.be.equal(receiverAddress);
});
it('should successfully transfer tokens when sender is an approved operator', async () => {
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
const isApprovedForAll = true;
let txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApprovedForAll,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const owner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
expect(owner).to.be.equal(ownerAddress);
txHash = await contractWrappers.erc721Token.transferFromAsync(
tokenAddress,
receiverAddress,
operatorAddress,
tokenId,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const newOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
expect(newOwner).to.be.equal(receiverAddress);
});
});
describe('#getTokenCountAsync', () => {
describe('With provider with accounts', () => {
it('should return the count for an existing ERC721 token', async () => {
let tokenCount = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, ownerAddress);
expect(tokenCount).to.be.bignumber.equal(0);
await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
tokenCount = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, ownerAddress);
expect(tokenCount).to.be.bignumber.equal(1);
});
it('should return a balance of 0 for a non-existent owner address', async () => {
const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
const balance = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, 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 emptyWalletProvider = addEmptyWalletSubprovider(provider);
zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config);
});
it('should return balance even when called with provider instance without addresses', async () => {
const balance = await zeroExContractWithoutAccounts.erc721Token.getTokenCountAsync(
tokenAddress,
ownerAddress,
);
return expect(balance).to.be.bignumber.equal(0);
});
});
});
describe('#getOwnerOfAsync', () => {
it('should return the owner for an existing ERC721 token', async () => {
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
const tokenOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId);
expect(tokenOwner).to.be.bignumber.equal(ownerAddress);
});
it('should return undefined not 0 for a non-existent ERC721', async () => {
const fakeTokenId = new BigNumber(42);
return expect(contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, fakeTokenId)).to.be.rejectedWith(
ContractWrappersError.ERC721OwnerNotFound,
);
});
});
describe('#setApprovalForAllAsync/isApprovedForAllAsync', () => {
it('should check if operator address is approved', async () => {
let isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
);
expect(isApprovedForAll).to.be.false();
// set
isApprovedForAll = true;
let txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApprovedForAll,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
);
expect(isApprovedForAll).to.be.true();
// unset
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
false,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
);
expect(isApprovedForAll).to.be.false();
});
});
describe('#setProxyApprovalForAllAsync/isProxyApprovedForAllAsync', () => {
it('should check if proxy address is approved', async () => {
let isApprovedForAll = true;
const txHash = await contractWrappers.erc721Token.setProxyApprovalForAllAsync(
tokenAddress,
ownerAddress,
isApprovedForAll,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
isApprovedForAll = await contractWrappers.erc721Token.isProxyApprovedForAllAsync(
tokenAddress,
ownerAddress,
);
expect(isApprovedForAll).to.be.true();
});
});
describe('#setApprovalAsync/getApprovedIfExistsAsync', () => {
it("should set the spender's approval", async () => {
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
const approvalBeforeSet = await contractWrappers.erc721Token.getApprovedIfExistsAsync(
tokenAddress,
tokenId,
);
expect(approvalBeforeSet).to.be.undefined();
await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId);
const approvalAfterSet = await contractWrappers.erc721Token.getApprovedIfExistsAsync(tokenAddress, tokenId);
expect(approvalAfterSet).to.be.equal(approvedAddress);
});
});
describe('#setProxyApprovalAsync/isProxyApprovedAsync', () => {
it('should set the proxy approval', async () => {
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
const isProxyApprovedBeforeSet = await contractWrappers.erc721Token.isProxyApprovedAsync(
tokenAddress,
tokenId,
);
expect(isProxyApprovedBeforeSet).to.be.false();
await contractWrappers.erc721Token.setProxyApprovalAsync(tokenAddress, tokenId);
const isProxyApprovedAfterSet = await contractWrappers.erc721Token.isProxyApprovedAsync(
tokenAddress,
tokenId,
);
expect(isProxyApprovedAfterSet).to.be.true();
});
});
describe('#subscribe', () => {
const indexFilterValues = {};
afterEach(() => {
contractWrappers.erc721Token.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<ERC721TokenTransferEventArgs>) => {
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(ownerAddress);
expect(args._to).to.be.equal(receiverAddress);
expect(args._tokenId).to.be.bignumber.equal(tokenId);
},
);
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
const isApprovedForAll = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApprovedForAll,
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
contractWrappers.erc721Token.subscribe(
tokenAddress,
ERC721TokenEvents.Transfer,
indexFilterValues,
callback,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc721Token.transferFromAsync(
tokenAddress,
receiverAddress,
operatorAddress,
tokenId,
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
})().catch(done);
});
it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => {
(async () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ERC721TokenApprovalEventArgs>) => {
expect(logEvent).to.not.be.undefined();
expect(logEvent.isRemoved).to.be.false();
const args = logEvent.log.args;
expect(args._owner).to.be.equal(ownerAddress);
expect(args._approved).to.be.equal(approvedAddress);
expect(args._tokenId).to.be.bignumber.equal(tokenId);
},
);
contractWrappers.erc721Token.subscribe(
tokenAddress,
ERC721TokenEvents.Approval,
indexFilterValues,
callback,
);
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
})().catch(done);
});
it('Outstanding subscriptions are cancelled when contractWrappers.unsubscribeAll called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ERC721TokenApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
contractWrappers.erc721Token.subscribe(
tokenAddress,
ERC721TokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
contractWrappers.unsubscribeAll();
contractWrappers.erc721Token.subscribe(
tokenAddress,
ERC721TokenEvents.Approval,
indexFilterValues,
callbackToBeCalled,
);
const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
done();
})().catch(done);
});
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ERC721TokenApprovalForAllEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
const subscriptionToken = contractWrappers.erc721Token.subscribe(
tokenAddress,
ERC721TokenEvents.ApprovalForAll,
indexFilterValues,
callbackNeverToBeCalled,
);
contractWrappers.erc721Token.unsubscribe(subscriptionToken);
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApproved,
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
done();
})().catch(done);
});
});
describe('#getLogsAsync', () => {
const blockRange: BlockRange = {
fromBlock: 0,
toBlock: BlockParamLiteral.Latest,
};
let txHash: string;
it('should get logs with decoded args emitted by ApprovalForAll', async () => {
const isApprovedForAll = true;
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApprovedForAll,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = ERC721TokenEvents.ApprovalForAll;
const indexFilterValues = {};
const logs = await contractWrappers.erc721Token.getLogsAsync<ERC721TokenApprovalForAllEventArgs>(
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(ownerAddress);
expect(args._operator).to.be.equal(operatorAddress);
expect(args._approved).to.be.equal(isApprovedForAll);
});
it('should only get the logs with the correct event name', async () => {
const isApprovedForAll = true;
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApprovedForAll,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const differentEventName = ERC721TokenEvents.Transfer;
const indexFilterValues = {};
const logs = await contractWrappers.erc721Token.getLogsAsync(
tokenAddress,
differentEventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(0);
});
it('should only get the logs with the correct indexed fields', async () => {
const isApprovedForAll = true;
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApprovedForAll,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
txHash = await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
anotherOwnerAddress,
operatorAddress,
isApprovedForAll,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = ERC721TokenEvents.ApprovalForAll;
const indexFilterValues = {
_owner: anotherOwnerAddress,
};
const logs = await contractWrappers.erc721Token.getLogsAsync<ERC721TokenApprovalForAllEventArgs>(
tokenAddress,
eventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(1);
const args = logs[0].args;
expect(args._owner).to.be.equal(anotherOwnerAddress);
});
});
});
// tslint:disable:max-file-line-count
function addEmptyWalletSubprovider(p: Web3ProviderEngine): Web3ProviderEngine {
const providerEngine = new Web3ProviderEngine();
providerEngine.addProvider(new EmptyWalletSubprovider());
const currentSubproviders = (p as any)._providers;
for (const subprovider of currentSubproviders) {
providerEngine.addProvider(subprovider);
}
providerUtils.startProviderEngine(providerEngine);
return providerEngine;
}

View File

@@ -1,437 +0,0 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { DoneCallback } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import 'mocha';
import {
BlockParamLiteral,
BlockRange,
ContractWrappers,
ContractWrappersError,
WETH9ApprovalEventArgs,
WETH9DepositEventArgs,
WETH9Events,
WETH9TransferEventArgs,
WETH9WithdrawalEventArgs,
} from '../src';
import { DecodedLogEvent } from '../src/types';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
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 contractAddresses: ContractAddresses;
let userAddresses: string[];
let addressWithETH: string;
let wethContractAddress: string;
let depositWeiAmount: BigNumber;
const decimalPlaces = 7;
let addressWithoutFunds: string;
const gasPrice = new BigNumber(1);
const transferAmount = new BigNumber(42);
const allowanceAmount = new BigNumber(42);
const depositAmount = new BigNumber(42);
const withdrawalAmount = new BigNumber(42);
before(async () => {
contractAddresses = await migrateOnceAsync();
const config = {
gasPrice,
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
addressWithETH = userAddresses[0];
wethContractAddress = contractAddresses.etherToken;
depositWeiAmount = Web3Wrapper.toWei(new BigNumber(5));
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 = contractAddresses.etherToken;
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.erc20Token.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.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const postETHBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const postWETHBalanceInBaseUnits = await contractWrappers.erc20Token.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.plus(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.erc20Token.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.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const postETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const postWETHBalanceInBaseUnits = await contractWrappers.erc20Token.getBalanceAsync(
wethContractAddress,
addressWithETH,
);
expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
const expectedETHBalance = preETHBalance.plus(depositWeiAmount).integerValue(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.erc20Token.getBalanceAsync(
wethContractAddress,
addressWithETH,
);
expect(preWETHBalance).to.be.bignumber.equal(0);
// tslint:disable-next-line:custom-no-magic-numbers
const overWETHBalance = preWETHBalance.plus(999999999);
return expect(
contractWrappers.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH),
).to.be.rejectedWith(ContractWrappersError.InsufficientWEthBalanceForWithdrawal);
});
});
describe('#subscribe', () => {
const indexFilterValues = {};
let etherTokenAddress: string;
before(async () => {
etherTokenAddress = contractAddresses.etherToken;
});
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<WETH9TransferEventArgs>) => {
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,
WETH9Events.Transfer,
indexFilterValues,
callback,
);
await contractWrappers.erc20Token.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<WETH9ApprovalEventArgs>) => {
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,
WETH9Events.Approval,
indexFilterValues,
callback,
);
await contractWrappers.erc20Token.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<WETH9DepositEventArgs>) => {
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,
WETH9Events.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<WETH9WithdrawalEventArgs>) => {
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,
WETH9Events.Withdrawal,
indexFilterValues,
callback,
);
await contractWrappers.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
})().catch(done);
});
it('should cancel outstanding subscriptions when contractWrappers.unsubscribeAll is called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_logEvent: DecodedLogEvent<WETH9ApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
contractWrappers.unsubscribeAll();
await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Transfer,
indexFilterValues,
callbackToBeCalled,
);
await contractWrappers.erc20Token.transferAsync(
etherTokenAddress,
addressWithETH,
addressWithoutFunds,
transferAmount,
);
})().catch(done);
});
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_logEvent: DecodedLogEvent<WETH9ApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
const subscriptionToken = contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
contractWrappers.etherToken.unsubscribe(subscriptionToken);
await contractWrappers.erc20Token.transferAsync(
etherTokenAddress,
addressWithETH,
addressWithoutFunds,
transferAmount,
);
done();
})().catch(done);
});
});
describe('#getLogsAsync', () => {
let etherTokenAddress: string;
let erc20ProxyAddress: string;
let blockRange: BlockRange;
let txHash: string;
before(async () => {
addressWithETH = userAddresses[0];
etherTokenAddress = contractAddresses.etherToken;
erc20ProxyAddress = contractWrappers.erc20Proxy.address;
// Start the block range after all migrations to avoid unexpected logs
const currentBlock: number = await web3Wrapper.getBlockNumberAsync();
const fromBlock = currentBlock + 1;
blockRange = {
fromBlock,
toBlock: BlockParamLiteral.Latest,
};
});
it('should get logs with decoded args emitted by Approval', async () => {
txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
etherTokenAddress,
addressWithETH,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = WETH9Events.Approval;
const indexFilterValues = {};
const logs = await contractWrappers.etherToken.getLogsAsync<WETH9ApprovalEventArgs>(
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(erc20ProxyAddress);
expect(args._value).to.be.bignumber.equal(contractWrappers.erc20Token.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 = WETH9Events.Deposit;
const indexFilterValues = {};
const logs = await contractWrappers.etherToken.getLogsAsync<WETH9DepositEventArgs>(
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.erc20Token.setUnlimitedProxyAllowanceAsync(
etherTokenAddress,
addressWithETH,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const differentEventName = WETH9Events.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.erc20Token.setUnlimitedProxyAllowanceAsync(
etherTokenAddress,
addressWithETH,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(
etherTokenAddress,
addressWithoutFunds,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = WETH9Events.Approval;
const indexFilterValues = {
_owner: addressWithETH,
};
const logs = await contractWrappers.etherToken.getLogsAsync<WETH9ApprovalEventArgs>(
etherTokenAddress,
eventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(1);
const args = logs[0].args;
expect(args._owner).to.be.equal(addressWithETH);
});
});
});

View File

@@ -1,8 +1,7 @@
import { DummyERC20TokenContract } from '@0x/abi-gen-wrappers';
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils, orderHashUtils, signatureUtils } from '@0x/order-utils';
import { DoneCallback, RevertReason, SignedOrder } from '@0x/types';
import { DoneCallback, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { BlockParamLiteral } from 'ethereum-types';
@@ -10,8 +9,8 @@ import 'mocha';
import { ContractWrappers, ExchangeCancelEventArgs, ExchangeEvents, ExchangeFillEventArgs, OrderStatus } from '../src';
import { DecodedLogEvent } from '../src/types';
import { _getDefaultContractAddresses } from '../src/utils/contract_addresses';
import { UntransferrableDummyERC20Token } from './artifacts/UntransferrableDummyERC20Token';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
@@ -35,7 +34,6 @@ describe('ExchangeWrapper', () => {
let takerAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let txHash: string;
const fillableAmount = new BigNumber(5);
const takerTokenFillAmount = new BigNumber(5);
let signedOrder: SignedOrder;
@@ -52,7 +50,7 @@ describe('ExchangeWrapper', () => {
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
zrxTokenAddress = contractAddresses.zrxToken;
fillScenarios = new FillScenarios(
provider,
userAddresses,
@@ -94,71 +92,71 @@ describe('ExchangeWrapper', () => {
describe('fill order(s)', () => {
describe('#fillOrderAsync', () => {
it('should fill a valid order', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#fillOrderNoThrowAsync', () => {
it('should fill a valid order', async () => {
txHash = await contractWrappers.exchange.fillOrderNoThrowAsync(
await contractWrappers.exchange.fillOrderNoThrow.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
describe('#fillOrKillOrderAsync', () => {
it('should fill or kill a valid order', async () => {
txHash = await contractWrappers.exchange.fillOrKillOrderAsync(
await contractWrappers.exchange.fillOrKillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#batchFillOrdersAsync', () => {
it('should fill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
txHash = await contractWrappers.exchange.batchFillOrdersAsync(
await contractWrappers.exchange.batchFillOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#marketBuyOrdersAsync', () => {
it('should maker buy', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketBuyOrdersAsync(
await contractWrappers.exchange.marketBuyOrders.awaitTransactionSuccessAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#marketBuyOrdersNoThrowAsync', () => {
it('should no throw maker buy', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketBuyOrdersNoThrowAsync(
await contractWrappers.exchange.marketBuyOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
@@ -166,25 +164,25 @@ describe('ExchangeWrapper', () => {
it('should maker sell', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketSellOrdersAsync(
await contractWrappers.exchange.marketSellOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#marketSellOrdersNoThrowAsync', () => {
it('should no throw maker sell', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketSellOrdersNoThrowAsync(
await contractWrappers.exchange.marketSellOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
@@ -192,15 +190,15 @@ describe('ExchangeWrapper', () => {
it('should fill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
txHash = await contractWrappers.exchange.batchFillOrdersNoThrowAsync(
await contractWrappers.exchange.batchFillOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
let orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
let orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
orderInfo = await contractWrappers.exchange.getOrderInfoAsync(anotherSignedOrder);
orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(anotherSignedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
@@ -208,12 +206,12 @@ describe('ExchangeWrapper', () => {
it('should fill or kill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
txHash = await contractWrappers.exchange.batchFillOrKillOrdersAsync(
await contractWrappers.exchange.batchFillOrKillOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#matchOrdersAsync', () => {
@@ -225,35 +223,39 @@ describe('ExchangeWrapper', () => {
takerAddress,
fillableAmount,
);
txHash = await contractWrappers.exchange.matchOrdersAsync(
await contractWrappers.exchange.matchOrders.awaitTransactionSuccessAsync(
signedOrder,
matchingSignedOrder,
takerAddress,
signedOrder.signature,
matchingSignedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
});
describe('cancel order(s)', () => {
describe('#cancelOrderAsync', () => {
it('should cancel a valid order', async () => {
txHash = await contractWrappers.exchange.cancelOrderAsync(signedOrder);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
await contractWrappers.exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder, {
from: makerAddress,
});
});
});
describe('#batchCancelOrdersAsync', () => {
it('should cancel a batch of valid orders', async () => {
const orders = [signedOrder, anotherSignedOrder];
txHash = await contractWrappers.exchange.batchCancelOrdersAsync(orders);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
await contractWrappers.exchange.batchCancelOrders.awaitTransactionSuccessAsync(orders, {
from: makerAddress,
});
});
});
describe('#cancelOrdersUpTo/getOrderEpochAsync', () => {
it('should cancel orders up to target order epoch', async () => {
const targetOrderEpoch = new BigNumber(42);
txHash = await contractWrappers.exchange.cancelOrdersUpToAsync(targetOrderEpoch, makerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderEpoch = await contractWrappers.exchange.getOrderEpochAsync(
await contractWrappers.exchange.cancelOrdersUpTo.awaitTransactionSuccessAsync(targetOrderEpoch, {
from: makerAddress,
});
const orderEpoch = await contractWrappers.exchange.orderEpoch.callAsync(
makerAddress,
constants.NULL_ADDRESS,
);
@@ -261,138 +263,35 @@ describe('ExchangeWrapper', () => {
});
});
});
describe('#getZRXAssetData', () => {
it('should get the asset data', () => {
const ZRX_ASSET_DATA = contractWrappers.exchange.getZRXAssetData();
const ASSET_DATA_HEX_LENGTH = 74;
expect(ZRX_ASSET_DATA).to.have.length(ASSET_DATA_HEX_LENGTH);
});
});
describe('#getOrderInfoAsync', () => {
it('should get the order info', async () => {
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(orderInfo.orderHash).to.be.equal(orderHash);
});
});
describe('#getOrdersInfoAsync', () => {
it('should get the orders info', async () => {
const ordersInfo = await contractWrappers.exchange.getOrdersInfoAsync([signedOrder, anotherSignedOrder]);
const ordersInfo = await contractWrappers.exchange.getOrdersInfo.callAsync([
signedOrder,
anotherSignedOrder,
]);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(ordersInfo[0].orderHash).to.be.equal(orderHash);
const anotherOrderHash = orderHashUtils.getOrderHashHex(anotherSignedOrder);
expect(ordersInfo[1].orderHash).to.be.equal(anotherOrderHash);
});
});
describe('#validateOrderFillableOrThrowAsync', () => {
it('should throw if signature is invalid', async () => {
const signedOrderWithInvalidSignature = {
...signedOrder,
signature:
'0x1b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403',
};
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrderWithInvalidSignature),
).to.eventually.to.be.rejectedWith(RevertReason.InvalidOrderSignature);
});
it('should validate the order with the current balances and allowances for the maker', async () => {
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
validateRemainingOrderAmountIsFillable: false,
});
});
it('should validate the order with remaining fillable amount for the order', async () => {
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
});
it('should validate the order with specified amount', async () => {
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
expectedFillTakerTokenAmount: signedOrder.takerAssetAmount,
});
});
it('should throw if the amount is greater than the allowance/balance', async () => {
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
// tslint:disable-next-line:custom-no-magic-numbers
expectedFillTakerTokenAmount: new BigNumber(2).pow(256).minus(1),
}),
).to.eventually.to.be.rejected();
});
it('should throw when the maker does not have enough balance for the remaining order amount', async () => {
const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// Change maker balance to have less than the order amount
const remainingBalance = makerBalance.minus(signedOrder.makerAssetAmount.minus(1));
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc20Token.transferAsync(
makerTokenAddress,
makerAddress,
constants.NULL_ADDRESS,
remainingBalance,
),
);
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder),
).to.eventually.to.be.rejected();
});
it('should validate the order when remaining order amount has some fillable amount', async () => {
const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// Change maker balance to have less than the order amount
const remainingBalance = makerBalance.minus(signedOrder.makerAssetAmount.minus(1));
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc20Token.transferAsync(
makerTokenAddress,
makerAddress,
constants.NULL_ADDRESS,
remainingBalance,
),
);
// An amount is still transferrable
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
validateRemainingOrderAmountIsFillable: false,
});
});
it('should throw when the ERC20 token has transfer restrictions', async () => {
const artifactDependencies = {};
const untransferrableToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
UntransferrableDummyERC20Token,
provider,
{ from: userAddresses[0] },
artifactDependencies,
'UntransferrableToken',
'UTT',
new BigNumber(constants.ZRX_DECIMALS),
// tslint:disable-next-line:custom-no-magic-numbers
new BigNumber(2).pow(20).minus(1),
);
const untransferrableMakerAssetData = assetDataUtils.encodeERC20AssetData(untransferrableToken.address);
const invalidSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
untransferrableMakerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc20Token.setProxyAllowanceAsync(
untransferrableToken.address,
makerAddress,
signedOrder.makerAssetAmount,
),
);
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(invalidSignedOrder),
).to.eventually.to.be.rejectedWith('TRANSFER_FAILED');
});
});
describe('#isValidSignature', () => {
it('should check if the signature is valid', async () => {
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
let isValid = await contractWrappers.exchange.isValidSignatureAsync(
let isValid = await contractWrappers.exchange.isValidSignature.callAsync(
orderHash,
signedOrder.makerAddress,
signedOrder.signature,
);
expect(isValid).to.be.true();
isValid = await contractWrappers.exchange.isValidSignatureAsync(
isValid = await contractWrappers.exchange.isValidSignature.callAsync(
orderHash,
signedOrder.takerAddress,
signedOrder.signature,
@@ -404,7 +303,10 @@ describe('ExchangeWrapper', () => {
it('should check if the validator is allowed', async () => {
const signerAddress = makerAddress;
const validatorAddress = constants.NULL_ADDRESS;
const isAllowed = await contractWrappers.exchange.isAllowedValidatorAsync(signerAddress, validatorAddress);
const isAllowed = await contractWrappers.exchange.allowedValidators.callAsync(
signerAddress,
validatorAddress,
);
expect(isAllowed).to.be.false();
});
});
@@ -413,52 +315,50 @@ describe('ExchangeWrapper', () => {
const validatorAddress = constants.NULL_ADDRESS;
const isApproved = true;
const senderAddress = makerAddress;
txHash = await contractWrappers.exchange.setSignatureValidatorApprovalAsync(
await contractWrappers.exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorAddress,
isApproved,
senderAddress,
{ from: senderAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#isTransactionExecutedAsync', () => {
it('should check if the transaction is executed', async () => {
const transactionHash = '0x0000000000000000000000000000000000000000000000000000000000000000';
const isExecuted = await contractWrappers.exchange.isTransactionExecutedAsync(transactionHash);
const isExecuted = await contractWrappers.exchange.transactions.callAsync(transactionHash);
expect(isExecuted).to.be.false();
});
});
describe('#getAssetProxyBySignatureAsync', () => {
it('should fill or kill a valid order', async () => {
const erc20ProxyId = await contractWrappers.erc20Proxy.getProxyIdAsync();
const erc20ProxyAddressById = await contractWrappers.exchange.getAssetProxyBySignatureAsync(erc20ProxyId);
const erc20ProxyId = await contractWrappers.erc20Proxy.getProxyId.callAsync();
const erc20ProxyAddressById = await contractWrappers.exchange.getAssetProxy.callAsync(erc20ProxyId);
const erc20ProxyAddress = contractWrappers.erc20Proxy.address;
expect(erc20ProxyAddressById).to.be.equal(erc20ProxyAddress);
const erc721ProxyId = await contractWrappers.erc721Proxy.getProxyIdAsync();
const erc721ProxyAddressById = await contractWrappers.exchange.getAssetProxyBySignatureAsync(erc721ProxyId);
const erc721ProxyId = await contractWrappers.erc721Proxy.getProxyId.callAsync();
const erc721ProxyAddressById = await contractWrappers.exchange.getAssetProxy.callAsync(erc721ProxyId);
const erc721ProxyAddress = contractWrappers.erc721Proxy.address;
expect(erc721ProxyAddressById).to.be.equal(erc721ProxyAddress);
});
});
describe('#preSignAsync/isPreSignedAsync', () => {
describe('#preSign/isPresigned', () => {
it('should preSign the hash', async () => {
const senderAddress = takerAddress;
const hash = orderHashUtils.getOrderHashHex(signedOrder);
const signerAddress = signedOrder.makerAddress;
let isPreSigned = await contractWrappers.exchange.isPreSignedAsync(hash, signerAddress);
let isPreSigned = await contractWrappers.exchange.preSigned.callAsync(hash, signerAddress);
expect(isPreSigned).to.be.false();
txHash = await contractWrappers.exchange.preSignAsync(
await contractWrappers.exchange.preSign.awaitTransactionSuccessAsync(
hash,
signerAddress,
signedOrder.signature,
senderAddress,
{ from: senderAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
isPreSigned = await contractWrappers.exchange.isPreSignedAsync(hash, signerAddress);
isPreSigned = await contractWrappers.exchange.preSigned.callAsync(hash, signerAddress);
expect(isPreSigned).to.be.true();
const preSignedSignature = '0x06';
const isValidSignature = await contractWrappers.exchange.isValidSignatureAsync(
const isValidSignature = await contractWrappers.exchange.isValidSignature.callAsync(
hash,
signerAddress,
preSignedSignature,
@@ -477,7 +377,7 @@ describe('ExchangeWrapper', () => {
});
describe('#getVersionAsync', () => {
it('should return version the hash', async () => {
const version = await contractWrappers.exchange.getVersionAsync();
const version = await contractWrappers.exchange.VERSION.callAsync();
const VERSION = '2.0.0';
expect(version).to.be.equal(VERSION);
});
@@ -510,10 +410,11 @@ describe('ExchangeWrapper', () => {
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback);
await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
})().catch(done);
});
@@ -525,7 +426,9 @@ describe('ExchangeWrapper', () => {
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Cancel, indexFilterValues, callback);
await contractWrappers.exchange.cancelOrderAsync(signedOrder);
await contractWrappers.exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder, {
from: makerAddress,
});
})().catch(done);
});
it('Outstanding subscriptions are cancelled when contractWrappers.unsubscribeAll called', (done: DoneCallback) => {
@@ -545,10 +448,11 @@ describe('ExchangeWrapper', () => {
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback);
await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
})().catch(done);
});
@@ -565,10 +469,11 @@ describe('ExchangeWrapper', () => {
callbackNeverToBeCalled,
);
contractWrappers.exchange.unsubscribe(subscriptionToken);
await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
done();
})().catch(done);
@@ -580,7 +485,12 @@ describe('ExchangeWrapper', () => {
toBlock: BlockParamLiteral.Latest,
};
it('should get logs with decoded args emitted by Fill', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const eventName = ExchangeEvents.Fill;
const indexFilterValues = {};
const logs = await contractWrappers.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
@@ -588,8 +498,12 @@ describe('ExchangeWrapper', () => {
expect(logs[0].event).to.be.equal(eventName);
});
it('should only get the logs with the correct event name', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const differentEventName = ExchangeEvents.Cancel;
const indexFilterValues = {};
const logs = await contractWrappers.exchange.getLogsAsync(
@@ -600,8 +514,12 @@ describe('ExchangeWrapper', () => {
expect(logs).to.have.length(0);
});
it('should only get the logs with the correct indexed fields', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const signedOrderWithAnotherMakerAddress = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
@@ -609,13 +527,12 @@ describe('ExchangeWrapper', () => {
takerAddress,
fillableAmount,
);
txHash = await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrderWithAnotherMakerAddress,
takerTokenFillAmount,
takerAddress,
signedOrderWithAnotherMakerAddress.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = ExchangeEvents.Fill;
const indexFilterValues = {
makerAddress: anotherMakerAddress,

View File

@@ -1,167 +0,0 @@
import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import { ContractWrappers, OrderStatus } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:custom-no-magic-numbers
describe('ForwarderWrapper', () => {
const fillableAmount = new BigNumber(5);
let contractWrappers: ContractWrappers;
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
let zrxTokenAddress: string;
let userAddresses: string[];
let makerAddress: string;
let takerAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let signedOrder: SignedOrder;
let anotherSignedOrder: SignedOrder;
before(async () => {
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
fillScenarios = new FillScenarios(
provider,
userAddresses,
zrxTokenAddress,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
contractWrappers.erc721Proxy.address,
);
[, makerAddress, takerAddress] = userAddresses;
[makerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
takerTokenAddress = contractWrappers.forwarder.etherTokenAddress;
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#marketBuyOrdersWithEthAsync', () => {
it('should market buy orders with eth', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = signedOrder.makerAssetAmount.plus(anotherSignedOrder.makerAssetAmount);
const txHash = await contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
makerAssetFillAmount,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const ordersInfo = await contractWrappers.exchange.getOrdersInfoAsync([signedOrder, anotherSignedOrder]);
expect(ordersInfo[0].orderStatus).to.be.equal(OrderStatus.FullyFilled);
expect(ordersInfo[1].orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
it('should throw when invalid transaction and shouldValidate is true', async () => {
const signedOrders = [signedOrder];
// request more makerAsset than what is available
const makerAssetFillAmount = signedOrder.makerAssetAmount.plus(100);
return expect(
contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
makerAssetFillAmount,
[],
0,
constants.NULL_ADDRESS,
{
shouldValidate: true,
},
),
).to.be.rejectedWith('COMPLETE_FILL_FAILED');
});
});
describe('#marketSellOrdersWithEthAsync', () => {
it('should market sell orders with eth', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = signedOrder.makerAssetAmount.plus(anotherSignedOrder.makerAssetAmount);
const txHash = await contractWrappers.forwarder.marketSellOrdersWithEthAsync(
signedOrders,
takerAddress,
makerAssetFillAmount,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const ordersInfo = await contractWrappers.exchange.getOrdersInfoAsync([signedOrder, anotherSignedOrder]);
expect(ordersInfo[0].orderStatus).to.be.equal(OrderStatus.FullyFilled);
expect(ordersInfo[1].orderStatus).to.be.equal(OrderStatus.Fillable);
expect(ordersInfo[1].orderTakerAssetFilledAmount).to.be.bignumber.equal(new BigNumber(4)); // only 95% of ETH is sold
});
it('should throw when invalid transaction and shouldValidate is true', async () => {
// create an order with fees, we try to fill it but we do not provide enough ETH to cover the fees
const signedOrderWithFee = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerAssetData,
takerAssetData,
constants.ZERO_AMOUNT,
new BigNumber(100),
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
constants.NULL_ADDRESS,
);
const signedOrders = [signedOrderWithFee];
const makerAssetFillAmount = signedOrder.makerAssetAmount;
return expect(
contractWrappers.forwarder.marketSellOrdersWithEthAsync(
signedOrders,
takerAddress,
makerAssetFillAmount,
[],
0,
constants.NULL_ADDRESS,
{
shouldValidate: true,
},
),
).to.be.rejectedWith('COMPLETE_FILL_FAILED');
});
});
});

View File

@@ -1,146 +0,0 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
import { ContractWrappers, OrderStatus } from '../src';
import { OrderInfo, TraderInfo } from '../src/types';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
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('OrderValidator', () => {
const fillableAmount = new BigNumber(5);
let contractWrappers: ContractWrappers;
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
let zrxTokenAddress: string;
let zrxTokenAssetData: string;
let userAddresses: string[];
let coinbase: string;
let makerAddress: string;
let takerAddress: string;
let feeRecipient: string;
let anotherMakerAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let signedOrder: SignedOrder;
let anotherSignedOrder: SignedOrder;
let contractAddresses: ContractAddresses;
before(async () => {
contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
zrxTokenAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress);
fillScenarios = new FillScenarios(
provider,
userAddresses,
zrxTokenAddress,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
contractWrappers.erc721Proxy.address,
);
[coinbase, makerAddress, takerAddress, feeRecipient, anotherMakerAddress] = userAddresses;
[makerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
takerTokenAddress = contractAddresses.etherToken;
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
zrxTokenAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#getOrdersAndTradersInfoAsync', () => {
let signedOrders: SignedOrder[];
let takerAddresses: string[];
let ordersInfo: OrderInfo[];
let tradersInfo: TraderInfo[];
beforeEach(async () => {
signedOrders = [signedOrder, anotherSignedOrder];
takerAddresses = [takerAddress, takerAddress];
const ordersAndTradersInfo = await contractWrappers.orderValidator.getOrdersAndTradersInfoAsync(
signedOrders,
takerAddresses,
);
ordersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.orderInfo);
tradersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.traderInfo);
});
it('should return the same number of order infos and trader infos as input orders', async () => {
expect(ordersInfo.length).to.be.equal(signedOrders.length);
expect(tradersInfo.length).to.be.equal(takerAddresses.length);
});
it('should return correct on-chain order info for input orders', async () => {
const firstOrderInfo = ordersInfo[0];
const secondOrderInfo = ordersInfo[1];
expect(firstOrderInfo.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(firstOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
expect(secondOrderInfo.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(secondOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return correct on-chain trader info for input takers', async () => {
const firstTraderInfo = tradersInfo[0];
const secondTraderInfo = tradersInfo[1];
expect(firstTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0));
expect(firstTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0));
expect(firstTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0));
expect(firstTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0));
});
});
});

View File

@@ -1,115 +0,0 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { runMigrationsAsync } from '@0x/migrations';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import 'mocha';
import { ContractWrappers } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { tokenUtils } from './utils/token_utils';
chaiSetup.configure();
const expect = chai.expect;
describe('Revert Validation ExchangeWrapper', () => {
let contractWrappers: ContractWrappers;
let userAddresses: string[];
let fillScenarios: FillScenarios;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAddress: string;
let takerAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let txHash: string;
let blockchainLifecycle: BlockchainLifecycle;
let web3Wrapper: Web3Wrapper;
const fillableAmount = new BigNumber(5);
const takerTokenFillAmount = new BigNumber(5);
let signedOrder: SignedOrder;
before(async () => {
// vmErrorsOnRPCResponse is useful for quick feedback and testing during development
// but is not the default behaviour in production. Here we ensure our failure cases
// are handled in an environment which behaves similar to production
const provider = web3Factory.getRpcProvider({
shouldUseInProcessGanache: true,
shouldThrowErrorsOnGanacheRPCResponse: false,
});
web3Wrapper = new Web3Wrapper(provider);
blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// Re-deploy the artifacts in this provider, rather than in the default provider exposed in
// the beforeAll hook. This is due to the fact that the default provider enabled vmErrorsOnRPCResponse
// and we are explicity testing with vmErrorsOnRPCResponse disabled.
const txDefaults = {
gas: devConstants.GAS_LIMIT,
from: devConstants.TESTRPC_FIRST_ADDRESS,
};
await blockchainLifecycle.startAsync();
const contractAddresses = await runMigrationsAsync(provider, txDefaults);
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
fillScenarios = new FillScenarios(
provider,
userAddresses,
contractAddresses.zrxToken,
contractAddresses.exchange,
contractAddresses.erc20Proxy,
contractAddresses.erc721Proxy,
);
[, makerAddress, takerAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#fillOrderAsync', () => {
it('should throw the revert reason when shouldValidate is true and a fill would revert', async () => {
// Create a scenario where the fill will revert
const makerTokenBalance = await contractWrappers.erc20Token.getBalanceAsync(
makerTokenAddress,
makerAddress,
);
// Transfer all of the tokens from maker to create a failure scenario
txHash = await contractWrappers.erc20Token.transferAsync(
makerTokenAddress,
makerAddress,
takerAddress,
makerTokenBalance,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
return expect(
contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress, {
shouldValidate: true,
}),
).to.be.rejectedWith('TRANSFER_FAILED');
});
});
});

View File

@@ -1,75 +0,0 @@
import { BlockchainLifecycle } from '@0x/dev-utils';
import { DoneCallback } from '@0x/types';
import * as _ from 'lodash';
import 'mocha';
import * as Sinon from 'sinon';
import {
ContractWrappers,
ContractWrappersConfig,
DecodedLogEvent,
ERC20TokenApprovalEventArgs,
ERC20TokenEvents,
} from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('SubscriptionTest', () => {
let contractWrappers: ContractWrappers;
let config: ContractWrappersConfig;
before(async () => {
const contractAddresses = await migrateOnceAsync();
config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
};
contractWrappers = new ContractWrappers(provider, config);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#subscribe', () => {
const indexFilterValues = {};
let tokenAddress: string;
let stubs: Sinon.SinonStub[] = [];
before(() => {
const tokenAddresses = tokenUtils.getDummyERC20TokenAddresses();
tokenAddress = tokenAddresses[0];
});
afterEach(() => {
contractWrappers.erc20Token.unsubscribeAll();
_.each(stubs, s => s.restore());
stubs = [];
});
it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
(async () => {
const callback = (err: Error | null, _logEvent?: DecodedLogEvent<ERC20TokenApprovalEventArgs>) =>
_.noop.bind(_);
contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Approval,
indexFilterValues,
callback,
);
stubs = [
Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockIfExistsAsync').throws(
new Error('JSON RPC error'),
),
];
contractWrappers.erc20Token.unsubscribeAll();
done();
})().catch(done);
});
});
});

View File

@@ -1,210 +0,0 @@
import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils, generatePseudoRandomSalt, orderHashUtils, signatureUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import 'mocha';
import { ContractWrappers } from '../src';
import { TransactionEncoder } from '../src/utils/transaction_encoder';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('TransactionEncoder', () => {
let contractWrappers: ContractWrappers;
let userAddresses: string[];
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let coinbase: string;
let makerAddress: string;
let senderAddress: string;
let takerAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let txHash: string;
const fillableAmount = new BigNumber(5);
const takerTokenFillAmount = new BigNumber(5);
let signedOrder: SignedOrder;
before(async () => {
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
const zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
fillScenarios = new FillScenarios(
provider,
userAddresses,
zrxTokenAddress,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
contractWrappers.erc721Proxy.address,
);
[coinbase, makerAddress, takerAddress, senderAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('encode and executeTransaction', () => {
const executeTransactionOrThrowAsync = async (
encoder: TransactionEncoder,
data: string,
signerAddress: string = takerAddress,
): Promise<void> => {
const salt = generatePseudoRandomSalt();
const transactionHash = encoder.getTransactionHashHex(data, salt, signerAddress);
const signature = await signatureUtils.ecSignHashAsync(provider, transactionHash, signerAddress);
txHash = await contractWrappers.exchange.executeTransactionAsync(
salt,
signerAddress,
data,
signature,
senderAddress,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
};
describe('#fillOrderTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.fillOrderTx(signedOrder, takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#fillOrderNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.fillOrderNoThrowTx(signedOrder, takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#fillOrKillOrderTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.fillOrKillOrderTx(signedOrder, takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketSellOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketSellOrdersTx([signedOrder], takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketSellOrdersNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketSellOrdersNoThrowTx([signedOrder], takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketBuyOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketBuyOrdersTx([signedOrder], fillableAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketBuyOrdersNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketBuyOrdersNoThrowTx([signedOrder], fillableAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#preSignTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const signature = signedOrder.signature;
const data = encoder.preSignTx(orderHash, makerAddress, signature);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#setSignatureValidatorApprovalTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const isApproved = true;
const data = encoder.setSignatureValidatorApprovalTx(senderAddress, isApproved);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchFillOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchFillOrdersTx([signedOrder], [takerTokenFillAmount]);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchFillOrKillOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchFillOrKillOrdersTx([signedOrder], [takerTokenFillAmount]);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchFillOrdersNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchFillOrdersNoThrowTx([signedOrder], [takerTokenFillAmount]);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchCancelOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchCancelOrdersTx([signedOrder]);
const signerAddress = makerAddress;
await executeTransactionOrThrowAsync(encoder, data, signerAddress);
});
});
describe('#cancelOrderTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.cancelOrderTx(signedOrder);
const signerAddress = makerAddress;
await executeTransactionOrThrowAsync(encoder, data, signerAddress);
});
});
describe('#cancelOrdersUpToTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const targetEpoch = signedOrder.salt;
const data = encoder.cancelOrdersUpToTx(targetEpoch);
const signerAddress = makerAddress;
await executeTransactionOrThrowAsync(encoder, data, signerAddress);
});
});
});
});

View File

@@ -1,150 +0,0 @@
import { DummyERC20TokenContract } from '@0x/abi-gen-wrappers';
import { assetDataUtils } from '@0x/order-utils';
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { DutchAuctionWrapper } from '../../src/contract_wrappers/dutch_auction_wrapper';
import { constants } from './constants';
export class DutchAuctionUtils {
private readonly _web3Wrapper: Web3Wrapper;
private readonly _coinbase: string;
private readonly _exchangeAddress: string;
private readonly _erc20ProxyAddress: string;
constructor(web3Wrapper: Web3Wrapper, coinbase: string, exchangeAddress: string, erc20ProxyAddress: string) {
this._web3Wrapper = web3Wrapper;
this._coinbase = coinbase;
this._exchangeAddress = exchangeAddress;
this._erc20ProxyAddress = erc20ProxyAddress;
}
public async createSignedSellOrderAsync(
auctionBeginTimeSections: BigNumber,
acutionEndTimeSeconds: BigNumber,
auctionBeginTakerAssetAmount: BigNumber,
auctionEndTakerAssetAmount: BigNumber,
makerAssetAmount: BigNumber,
makerAssetData: string,
takerAssetData: string,
makerAddress: string,
takerAddress: string,
senderAddress?: string,
makerFee?: BigNumber,
takerFee?: BigNumber,
feeRecipientAddress?: string,
): Promise<SignedOrder> {
// Notes on sell order:
// - The `takerAssetAmount` is set to the `auctionEndTakerAssetAmount`, which is the lowest amount the
// the seller can expect to receive
// - The `makerAssetData` is overloaded to include the auction begin time and begin taker asset amount
const makerAssetDataWithAuctionDetails = DutchAuctionWrapper.encodeDutchAuctionAssetData(
makerAssetData,
auctionBeginTimeSections,
auctionBeginTakerAssetAmount,
);
const signedOrder = await orderFactory.createSignedOrderAsync(
this._web3Wrapper.getProvider(),
makerAddress,
makerAssetAmount,
makerAssetDataWithAuctionDetails,
auctionEndTakerAssetAmount,
takerAssetData,
this._exchangeAddress,
{
takerAddress,
senderAddress,
makerFee,
takerFee,
feeRecipientAddress,
expirationTimeSeconds: acutionEndTimeSeconds,
},
);
const erc20AssetData = assetDataUtils.decodeERC20AssetData(makerAssetData);
await this._increaseERC20BalanceAndAllowanceAsync(erc20AssetData.tokenAddress, makerAddress, makerAssetAmount);
return signedOrder;
}
public async createSignedBuyOrderAsync(
sellOrder: SignedOrder,
buyerAddress: string,
senderAddress?: string,
makerFee?: BigNumber,
takerFee?: BigNumber,
feeRecipientAddress?: string,
expirationTimeSeconds?: BigNumber,
): Promise<SignedOrder> {
const dutchAuctionData = DutchAuctionWrapper.decodeDutchAuctionData(sellOrder.makerAssetData);
// Notes on buy order:
// - The `makerAssetAmount` is set to `dutchAuctionData.beginAmount`, which is
// the highest amount the buyer would have to pay out at any point during the auction.
// - The `takerAssetAmount` is set to the seller's `makerAssetAmount`, as the buyer
// receives the entire amount being sold by the seller.
// - The `makerAssetData`/`takerAssetData` are reversed from the sell order
const signedOrder = await orderFactory.createSignedOrderAsync(
this._web3Wrapper.getProvider(),
buyerAddress,
dutchAuctionData.beginAmount,
sellOrder.takerAssetData,
sellOrder.makerAssetAmount,
sellOrder.makerAssetData,
sellOrder.exchangeAddress,
{
senderAddress,
makerFee,
takerFee,
feeRecipientAddress,
expirationTimeSeconds,
},
);
const buyerERC20AssetData = assetDataUtils.decodeERC20AssetData(sellOrder.takerAssetData);
await this._increaseERC20BalanceAndAllowanceAsync(
buyerERC20AssetData.tokenAddress,
buyerAddress,
dutchAuctionData.beginAmount,
);
return signedOrder;
}
private async _increaseERC20BalanceAndAllowanceAsync(
tokenAddress: string,
address: string,
amount: BigNumber,
): Promise<void> {
if (amount.isZero() || address === constants.NULL_ADDRESS) {
return; // noop
}
await Promise.all([
this._increaseERC20BalanceAsync(tokenAddress, address, amount),
this._increaseERC20AllowanceAsync(tokenAddress, address, amount),
]);
}
private async _increaseERC20BalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> {
const erc20Token = new DummyERC20TokenContract(
tokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
const txHash = await erc20Token.transfer.sendTransactionAsync(address, amount, {
from: this._coinbase,
});
await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
}
private async _increaseERC20AllowanceAsync(
tokenAddress: string,
address: string,
amount: BigNumber,
): Promise<void> {
const erc20Token = new DummyERC20TokenContract(
tokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
const oldMakerAllowance = await erc20Token.allowance.callAsync(address, this._erc20ProxyAddress);
const newMakerAllowance = oldMakerAllowance.plus(amount);
const txHash = await erc20Token.approve.sendTransactionAsync(this._erc20ProxyAddress, newMakerAllowance, {
from: address,
});
await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
}
}