aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contract-wrappers/test
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contract-wrappers/test')
-rw-r--r--packages/contract-wrappers/test/dutch_auction_wrapper_test.ts128
-rw-r--r--packages/contract-wrappers/test/utils/dutch_auction_utils.ts153
2 files changed, 281 insertions, 0 deletions
diff --git a/packages/contract-wrappers/test/dutch_auction_wrapper_test.ts b/packages/contract-wrappers/test/dutch_auction_wrapper_test.ts
new file mode 100644
index 000000000..d7a6ca015
--- /dev/null
+++ b/packages/contract-wrappers/test/dutch_auction_wrapper_test.ts
@@ -0,0 +1,128 @@
+import { expectTransactionFailedAsync, getLatestBlockTimestampAsync } from '@0x/contracts-test-utils';
+import { BlockchainLifecycle } from '@0x/dev-utils';
+import { assetDataUtils } from '@0x/order-utils';
+import { RevertReason, SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import * as chai from 'chai';
+import 'mocha';
+
+import { ContractWrappers } 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('#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,
+ );
+ });
+ });
+});
diff --git a/packages/contract-wrappers/test/utils/dutch_auction_utils.ts b/packages/contract-wrappers/test/utils/dutch_auction_utils.ts
new file mode 100644
index 000000000..8e2aef217
--- /dev/null
+++ b/packages/contract-wrappers/test/utils/dutch_auction_utils.ts
@@ -0,0 +1,153 @@
+import { DummyERC20TokenContract } from '@0x/abi-gen-wrappers';
+import * as artifacts from '@0x/contract-artifacts';
+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(
+ artifacts.DummyERC20Token.compilerOutput.abi,
+ 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(
+ artifacts.DummyERC20Token.compilerOutput.abi,
+ 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);
+ }
+}