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:
@@ -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', () => {
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user