Dutch wrapper

This commit is contained in:
Greg Hysen 2018-12-20 15:39:19 -08:00
parent 89fcbec43b
commit 43b648e7dc
15 changed files with 1092 additions and 47 deletions

View File

@ -34,7 +34,7 @@ import * as _ from 'lodash';
import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction'; import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction';
import { artifacts } from '../../src/artifacts'; import { artifacts } from '../../src/artifacts';
import { DutchAuctionWrapper } from '../utils/dutch_auction_wrapper'; import { DutchAuctionTestWrapper } from '../utils/dutch_auction_test_wrapper';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@ -68,7 +68,7 @@ describe(ContractName.DutchAuction, () => {
let erc721MakerAssetIds: BigNumber[]; let erc721MakerAssetIds: BigNumber[];
const tenMinutesInSeconds = 10 * 60; const tenMinutesInSeconds = 10 * 60;
let dutchAuctionWrapper: DutchAuctionWrapper; let dutchAuctionTestWrapper: DutchAuctionTestWrapper;
let defaultERC20MakerAssetData: string; let defaultERC20MakerAssetData: string;
before(async () => { before(async () => {
@ -125,7 +125,7 @@ describe(ContractName.DutchAuction, () => {
dutchAuctionInstance.address, dutchAuctionInstance.address,
provider, provider,
); );
dutchAuctionWrapper = new DutchAuctionWrapper(dutchAuctionInstance, provider); dutchAuctionTestWrapper = new DutchAuctionTestWrapper(dutchAuctionInstance, provider);
defaultMakerAssetAddress = erc20TokenA.address; defaultMakerAssetAddress = erc20TokenA.address;
const defaultTakerAssetAddress = wethContract.address; const defaultTakerAssetAddress = wethContract.address;
@ -164,7 +164,7 @@ describe(ContractName.DutchAuction, () => {
feeRecipientAddress, feeRecipientAddress,
// taker address or sender address should be set to the ducth auction contract // taker address or sender address should be set to the ducth auction contract
takerAddress: dutchAuctionContract.address, takerAddress: dutchAuctionContract.address,
makerAssetData: assetDataUtils.encodeDutchAuctionAssetData( makerAssetData: DutchAuctionTestWrapper.encodeDutchAuctionAssetData(
assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
auctionBeginTimeSeconds, auctionBeginTimeSeconds,
auctionBeginAmount, auctionBeginAmount,
@ -206,13 +206,13 @@ describe(ContractName.DutchAuction, () => {
describe('matchOrders', () => { describe('matchOrders', () => {
it('should be worth the begin price at the begining of the auction', async () => { it('should be worth the begin price at the begining of the auction', async () => {
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2); auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData( const makerAssetData = DutchAuctionTestWrapper.encodeDutchAuctionAssetData(
defaultERC20MakerAssetData, defaultERC20MakerAssetData,
auctionBeginTimeSeconds, auctionBeginTimeSeconds,
auctionBeginAmount, auctionBeginAmount,
); );
sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerAssetData }); sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerAssetData });
const auctionDetails = await dutchAuctionWrapper.getAuctionDetailsAsync(sellOrder); const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
expect(auctionDetails.currentTimeSeconds).to.be.bignumber.lte(auctionBeginTimeSeconds); expect(auctionDetails.currentTimeSeconds).to.be.bignumber.lte(auctionBeginTimeSeconds);
expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount); expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount);
expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
@ -220,7 +220,7 @@ describe(ContractName.DutchAuction, () => {
it('should be be worth the end price at the end of the auction', async () => { it('should be be worth the end price at the end of the auction', async () => {
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData( const makerAssetData = DutchAuctionTestWrapper.encodeDutchAuctionAssetData(
defaultERC20MakerAssetData, defaultERC20MakerAssetData,
auctionBeginTimeSeconds, auctionBeginTimeSeconds,
auctionBeginAmount, auctionBeginAmount,
@ -229,18 +229,18 @@ describe(ContractName.DutchAuction, () => {
makerAssetData, makerAssetData,
expirationTimeSeconds: auctionEndTimeSeconds, expirationTimeSeconds: auctionEndTimeSeconds,
}); });
const auctionDetails = await dutchAuctionWrapper.getAuctionDetailsAsync(sellOrder); const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
expect(auctionDetails.currentTimeSeconds).to.be.bignumber.gte(auctionEndTimeSeconds); expect(auctionDetails.currentTimeSeconds).to.be.bignumber.gte(auctionEndTimeSeconds);
expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount); expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount);
expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
}); });
it('should match orders at current amount and send excess to buyer', async () => { it('should match orders at current amount and send excess to buyer', async () => {
const beforeAuctionDetails = await dutchAuctionWrapper.getAuctionDetailsAsync(sellOrder); const beforeAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
buyOrder = await buyerOrderFactory.newSignedOrderAsync({ buyOrder = await buyerOrderFactory.newSignedOrderAsync({
makerAssetAmount: beforeAuctionDetails.currentAmount.times(2), makerAssetAmount: beforeAuctionDetails.currentAmount.times(2),
}); });
await dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
const afterAuctionDetails = await dutchAuctionWrapper.getAuctionDetailsAsync(sellOrder); const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
const newBalances = await erc20Wrapper.getBalancesAsync(); const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal( expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal(
constants.ZERO_AMOUNT, constants.ZERO_AMOUNT,
@ -259,8 +259,8 @@ describe(ContractName.DutchAuction, () => {
sellOrder = await sellerOrderFactory.newSignedOrderAsync({ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
makerFee: new BigNumber(1), makerFee: new BigNumber(1),
}); });
await dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
const afterAuctionDetails = await dutchAuctionWrapper.getAuctionDetailsAsync(sellOrder); const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
const newBalances = await erc20Wrapper.getBalancesAsync(); const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
@ -273,9 +273,9 @@ describe(ContractName.DutchAuction, () => {
buyOrder = await buyerOrderFactory.newSignedOrderAsync({ buyOrder = await buyerOrderFactory.newSignedOrderAsync({
makerFee: new BigNumber(1), makerFee: new BigNumber(1),
}); });
await dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
const newBalances = await erc20Wrapper.getBalancesAsync(); const newBalances = await erc20Wrapper.getBalancesAsync();
const afterAuctionDetails = await dutchAuctionWrapper.getAuctionDetailsAsync(sellOrder); const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
); );
@ -286,7 +286,7 @@ describe(ContractName.DutchAuction, () => {
it('should revert when auction expires', async () => { it('should revert when auction expires', async () => {
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData( const makerAssetData = DutchAuctionTestWrapper.encodeDutchAuctionAssetData(
defaultERC20MakerAssetData, defaultERC20MakerAssetData,
auctionBeginTimeSeconds, auctionBeginTimeSeconds,
auctionBeginAmount, auctionBeginAmount,
@ -296,7 +296,7 @@ describe(ContractName.DutchAuction, () => {
makerAssetData, makerAssetData,
}); });
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
RevertReason.AuctionExpired, RevertReason.AuctionExpired,
); );
}); });
@ -305,7 +305,7 @@ describe(ContractName.DutchAuction, () => {
makerAssetAmount: sellOrder.takerAssetAmount, makerAssetAmount: sellOrder.takerAssetAmount,
}); });
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
RevertReason.AuctionInvalidAmount, RevertReason.AuctionInvalidAmount,
); );
}); });
@ -314,13 +314,13 @@ describe(ContractName.DutchAuction, () => {
takerAssetAmount: auctionBeginAmount.plus(1), takerAssetAmount: auctionBeginAmount.plus(1),
}); });
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
RevertReason.AuctionInvalidAmount, RevertReason.AuctionInvalidAmount,
); );
}); });
it('begin time is less than end time', async () => { it('begin time is less than end time', async () => {
auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds); auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData( const makerAssetData = DutchAuctionTestWrapper.encodeDutchAuctionAssetData(
defaultERC20MakerAssetData, defaultERC20MakerAssetData,
auctionBeginTimeSeconds, auctionBeginTimeSeconds,
auctionBeginAmount, auctionBeginAmount,
@ -330,7 +330,7 @@ describe(ContractName.DutchAuction, () => {
makerAssetData, makerAssetData,
}); });
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
RevertReason.AuctionInvalidBeginTime, RevertReason.AuctionInvalidBeginTime,
); );
}); });
@ -339,7 +339,7 @@ describe(ContractName.DutchAuction, () => {
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
}); });
return expectTransactionFailedAsync( return expectTransactionFailedAsync(
dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
RevertReason.InvalidAssetData, RevertReason.InvalidAssetData,
); );
}); });
@ -348,7 +348,7 @@ describe(ContractName.DutchAuction, () => {
it('should match orders when ERC721', async () => { it('should match orders when ERC721', async () => {
const makerAssetId = erc721MakerAssetIds[0]; const makerAssetId = erc721MakerAssetIds[0];
const erc721MakerAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId); const erc721MakerAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId);
const makerAssetData = assetDataUtils.encodeDutchAuctionAssetData( const makerAssetData = DutchAuctionTestWrapper.encodeDutchAuctionAssetData(
erc721MakerAssetData, erc721MakerAssetData,
auctionBeginTimeSeconds, auctionBeginTimeSeconds,
auctionBeginAmount, auctionBeginAmount,
@ -361,8 +361,8 @@ describe(ContractName.DutchAuction, () => {
takerAssetAmount: new BigNumber(1), takerAssetAmount: new BigNumber(1),
takerAssetData: sellOrder.makerAssetData, takerAssetData: sellOrder.makerAssetData,
}); });
await dutchAuctionWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
const afterAuctionDetails = await dutchAuctionWrapper.getAuctionDetailsAsync(sellOrder); const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
const newBalances = await erc20Wrapper.getBalancesAsync(); const newBalances = await erc20Wrapper.getBalancesAsync();
// HACK gte used here due to a bug in ganache where the timestamp can change // HACK gte used here due to a bug in ganache where the timestamp can change
// between multiple calls to the same block. Which can move the amount in our case // between multiple calls to the same block. Which can move the amount in our case

View File

@ -5,11 +5,13 @@ import { DutchAuctionDetails, SignedOrder } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper'; import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { BigNumber } from '@0x/utils';
import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction'; import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction';
import { artifacts } from '../../src/artifacts'; import { artifacts } from '../../src/artifacts';
import { DutchAuctionWrapper } from '@0x/contract-wrappers';
export class DutchAuctionWrapper { export class DutchAuctionTestWrapper {
private readonly _dutchAuctionContract: DutchAuctionContract; private readonly _dutchAuctionContract: DutchAuctionContract;
private readonly _web3Wrapper: Web3Wrapper; private readonly _web3Wrapper: Web3Wrapper;
private readonly _logDecoder: LogDecoder; private readonly _logDecoder: LogDecoder;
@ -59,4 +61,16 @@ export class DutchAuctionWrapper {
const afterAuctionDetails = await this._dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); const afterAuctionDetails = await this._dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
return afterAuctionDetails; return afterAuctionDetails;
} }
/**
* Dutch auction details are encoded with the asset data for a 0x order. This function produces a hex
* encoded assetData string, containing information both about the asset being traded and the
* dutch auction; which is usable in the makerAssetData or takerAssetData fields in a 0x order.
* @param assetData Hex encoded assetData string for the asset being auctioned.
* @param beginTimeSeconds Begin time of the dutch auction.
* @param beginAmount Starting amount being sold in the dutch auction.
* @return The hex encoded assetData string.
*/
public static encodeDutchAuctionAssetData(assetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string {
return DutchAuctionWrapper.encodeDutchAuctionAssetData(assetData, beginTimeSeconds, beginAmount);
};
} }

View File

@ -18,7 +18,7 @@
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output src/generated-wrappers --backend ethers" "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output src/generated-wrappers --backend ethers"
}, },
"config": { "config": {
"abis": "../contract-artifacts/artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC20Token|ERC721Proxy|ERC721Token|Exchange|Forwarder|IValidator|IWallet|OrderValidator|WETH9|ZRXToken).json" "abis": "../contract-artifacts/artifacts/@(AssetProxyOwner|DutchAuction|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC20Token|ERC721Proxy|ERC721Token|Exchange|Forwarder|IValidator|IWallet|OrderValidator|WETH9|ZRXToken).json"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -0,0 +1,322 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma whitespace class-name
// tslint:disable:no-unused-variable
// tslint:disable:no-unbound-method
import { BaseContract } from '@0x/base-contract';
import { BlockParam, BlockParamLiteral, CallData, ContractAbi, ContractArtifact, DecodedLogArgs, MethodAbi, Provider, TxData, TxDataPayable } from 'ethereum-types';
import { BigNumber, classUtils, logUtils } from '@0x/utils';
import { SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as ethers from 'ethers';
import * as _ from 'lodash';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class DutchAuctionContract extends BaseContract {
public getAuctionDetails = {
async sendTransactionAsync(
order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
txData: Partial<TxData> = {},
): Promise<string> {
const self = this as any as DutchAuctionContract;
const inputAbi = self._lookupAbi('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').inputs;
[order
] = BaseContract._formatABIDataItemList(inputAbi, [order
], BaseContract._bigNumberToString.bind(self));
BaseContract.strictArgumentEncodingCheck(inputAbi, [order
]);
const encodedData = self._lookupEthersInterface('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').functions.getAuctionDetails.encode([order
]);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
self.getAuctionDetails.estimateGasAsync.bind(
self,
order
),
);
const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
return txHash;
},
async estimateGasAsync(
order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
txData: Partial<TxData> = {},
): Promise<number> {
const self = this as any as DutchAuctionContract;
const inputAbi = self._lookupAbi('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').inputs;
[order
] = BaseContract._formatABIDataItemList(inputAbi, [order
], BaseContract._bigNumberToString);
const encodedData = self._lookupEthersInterface('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').functions.getAuctionDetails.encode([order
]);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
return gas;
},
getABIEncodedTransactionData(
order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
): string {
const self = this as any as DutchAuctionContract;
const inputAbi = self._lookupAbi('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').inputs;
[order
] = BaseContract._formatABIDataItemList(inputAbi, [order
], BaseContract._bigNumberToString);
const abiEncodedTransactionData = self._lookupEthersInterface('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').functions.getAuctionDetails.encode([order
]);
return abiEncodedTransactionData;
},
async callAsync(
order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<{beginTimeSeconds: BigNumber;endTimeSeconds: BigNumber;beginAmount: BigNumber;endAmount: BigNumber;currentAmount: BigNumber;currentTimeSeconds: BigNumber}
> {
const self = this as any as DutchAuctionContract;
const functionSignature = 'getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})';
const inputAbi = self._lookupAbi(functionSignature).inputs;
[order
] = BaseContract._formatABIDataItemList(inputAbi, [order
], BaseContract._bigNumberToString.bind(self));
BaseContract.strictArgumentEncodingCheck(inputAbi, [order
]);
const ethersFunction = self._lookupEthersInterface(functionSignature).functions.getAuctionDetails;
const encodedData = ethersFunction.encode([order
]);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
let resultArray = ethersFunction.decode(rawCallResult);
const outputAbi = (_.find(self.abi, {name: 'getAuctionDetails'}) as MethodAbi).outputs;
resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._lowercaseAddress.bind(this));
resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._bnToBigNumber.bind(this));
return resultArray[0];
},
};
public matchOrders = {
async sendTransactionAsync(
buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
buySignature: string,
sellSignature: string,
txData: Partial<TxData> = {},
): Promise<string> {
const self = this as any as DutchAuctionContract;
const inputAbi = self._lookupAbi('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').inputs;
[buyOrder,
sellOrder,
buySignature,
sellSignature
] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder,
sellOrder,
buySignature,
sellSignature
], BaseContract._bigNumberToString.bind(self));
BaseContract.strictArgumentEncodingCheck(inputAbi, [buyOrder,
sellOrder,
buySignature,
sellSignature
]);
const encodedData = self._lookupEthersInterface('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').functions.matchOrders.encode([buyOrder,
sellOrder,
buySignature,
sellSignature
]);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
self.matchOrders.estimateGasAsync.bind(
self,
buyOrder,
sellOrder,
buySignature,
sellSignature
),
);
const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
return txHash;
},
async estimateGasAsync(
buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
buySignature: string,
sellSignature: string,
txData: Partial<TxData> = {},
): Promise<number> {
const self = this as any as DutchAuctionContract;
const inputAbi = self._lookupAbi('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').inputs;
[buyOrder,
sellOrder,
buySignature,
sellSignature
] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder,
sellOrder,
buySignature,
sellSignature
], BaseContract._bigNumberToString);
const encodedData = self._lookupEthersInterface('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').functions.matchOrders.encode([buyOrder,
sellOrder,
buySignature,
sellSignature
]);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
return gas;
},
getABIEncodedTransactionData(
buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
buySignature: string,
sellSignature: string,
): string {
const self = this as any as DutchAuctionContract;
const inputAbi = self._lookupAbi('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').inputs;
[buyOrder,
sellOrder,
buySignature,
sellSignature
] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder,
sellOrder,
buySignature,
sellSignature
], BaseContract._bigNumberToString);
const abiEncodedTransactionData = self._lookupEthersInterface('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').functions.matchOrders.encode([buyOrder,
sellOrder,
buySignature,
sellSignature
]);
return abiEncodedTransactionData;
},
async callAsync(
buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string},
buySignature: string,
sellSignature: string,
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<{left: {makerAssetFilledAmount: BigNumber;takerAssetFilledAmount: BigNumber;makerFeePaid: BigNumber;takerFeePaid: BigNumber};right: {makerAssetFilledAmount: BigNumber;takerAssetFilledAmount: BigNumber;makerFeePaid: BigNumber;takerFeePaid: BigNumber};leftMakerAssetSpreadAmount: BigNumber}
> {
const self = this as any as DutchAuctionContract;
const functionSignature = 'matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)';
const inputAbi = self._lookupAbi(functionSignature).inputs;
[buyOrder,
sellOrder,
buySignature,
sellSignature
] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder,
sellOrder,
buySignature,
sellSignature
], BaseContract._bigNumberToString.bind(self));
BaseContract.strictArgumentEncodingCheck(inputAbi, [buyOrder,
sellOrder,
buySignature,
sellSignature
]);
const ethersFunction = self._lookupEthersInterface(functionSignature).functions.matchOrders;
const encodedData = ethersFunction.encode([buyOrder,
sellOrder,
buySignature,
sellSignature
]);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
let resultArray = ethersFunction.decode(rawCallResult);
const outputAbi = (_.find(self.abi, {name: 'matchOrders'}) as MethodAbi).outputs;
resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._lowercaseAddress.bind(this));
resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._bnToBigNumber.bind(this));
return resultArray[0];
},
};
public static async deployFrom0xArtifactAsync(
artifact: ContractArtifact | SimpleContractArtifact,
provider: Provider,
txDefaults: Partial<TxData>,
_exchange: string,
): Promise<DutchAuctionContract> {
if (_.isUndefined(artifact.compilerOutput)) {
throw new Error('Compiler output not found in the artifact file');
}
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
return DutchAuctionContract.deployAsync(bytecode, abi, provider, txDefaults, _exchange
);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
provider: Provider,
txDefaults: Partial<TxData>,
_exchange: string,
): Promise<DutchAuctionContract> {
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
[_exchange
] = BaseContract._formatABIDataItemList(
constructorAbi.inputs,
[_exchange
],
BaseContract._bigNumberToString,
);
const iface = new ethers.utils.Interface(abi);
const deployInfo = iface.deployFunction;
const txData = deployInfo.encode(bytecode, [_exchange
]);
const web3Wrapper = new Web3Wrapper(provider);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{data: txData},
txDefaults,
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`DutchAuction successfully deployed at ${txReceipt.contractAddress}`);
const contractInstance = new DutchAuctionContract(abi, txReceipt.contractAddress as string, provider, txDefaults);
contractInstance.constructorArgs = [_exchange
];
return contractInstance;
}
constructor(abi: ContractAbi, address: string, provider: Provider, txDefaults?: Partial<TxData>) {
super('DutchAuction', abi, address, provider, txDefaults);
classUtils.bindAll(this, ['_ethersInterfacesByFunctionSignature', 'address', 'abi', '_web3Wrapper']);
}
} // tslint:disable:max-file-line-count
// tslint:enable:no-unbound-method

View File

@ -1,6 +1,7 @@
export * from './generated-wrappers/asset_proxy_owner'; export * from './generated-wrappers/asset_proxy_owner';
export * from './generated-wrappers/dummy_erc20_token'; export * from './generated-wrappers/dummy_erc20_token';
export * from './generated-wrappers/dummy_erc721_token'; export * from './generated-wrappers/dummy_erc721_token';
export * from './generated-wrappers/dutch_auction';
export * from './generated-wrappers/erc20_proxy'; export * from './generated-wrappers/erc20_proxy';
export * from './generated-wrappers/erc20_token'; export * from './generated-wrappers/erc20_token';
export * from './generated-wrappers/erc721_proxy'; export * from './generated-wrappers/erc721_proxy';

View File

@ -9,6 +9,7 @@ export interface ContractAddresses {
assetProxyOwner: string; assetProxyOwner: string;
forwarder: string; forwarder: string;
orderValidator: string; orderValidator: string;
dutchAuction: string;
} }
export enum NetworkId { export enum NetworkId {
@ -29,6 +30,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1', forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1',
orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138', orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138',
dutchAuction: '0x',
}, },
3: { 3: {
erc20Proxy: '0xb1408f4c245a23c31b98d2c626777d4c0d766caa', erc20Proxy: '0xb1408f4c245a23c31b98d2c626777d4c0d766caa',
@ -39,6 +41,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
assetProxyOwner: '0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b', assetProxyOwner: '0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b',
forwarder: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e', forwarder: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e',
orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f', orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f',
dutchAuction: '0x',
}, },
4: { 4: {
exchange: '0xbce0b5f6eb618c565c3e5f5cd69652bbc279f44e', exchange: '0xbce0b5f6eb618c565c3e5f5cd69652bbc279f44e',
@ -49,6 +52,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
assetProxyOwner: '0xe1703da878afcebff5b7624a826902af475b9c03', assetProxyOwner: '0xe1703da878afcebff5b7624a826902af475b9c03',
forwarder: '0x2d40589abbdee84961f3a7656b9af7adb0ee5ab4', forwarder: '0x2d40589abbdee84961f3a7656b9af7adb0ee5ab4',
orderValidator: '0x0c5173a51e26b29d6126c686756fb9fbef71f762', orderValidator: '0x0c5173a51e26b29d6126c686756fb9fbef71f762',
dutchAuction: '0x',
}, },
42: { 42: {
erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e', erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e',
@ -59,6 +63,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
assetProxyOwner: '0x2c824d2882baa668e0d5202b1e7f2922278703f8', assetProxyOwner: '0x2c824d2882baa668e0d5202b1e7f2922278703f8',
forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d', orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d',
dutchAuction: '0x',
}, },
// NetworkId 50 represents our Ganache snapshot generated from migrations. // NetworkId 50 represents our Ganache snapshot generated from migrations.
50: { 50: {
@ -70,6 +75,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
assetProxyOwner: '0x34d402f14d58e001d8efbe6585051bf9706aa064', assetProxyOwner: '0x34d402f14d58e001d8efbe6585051bf9706aa064',
forwarder: '0xb69e673309512a9d726f87304c6984054f87a93b', forwarder: '0xb69e673309512a9d726f87304c6984054f87a93b',
orderValidator: '0xe86bb98fcf9bff3512c74589b78fb168200cc546', orderValidator: '0xe86bb98fcf9bff3512c74589b78fb168200cc546',
dutchAuction: '0x',
}, },
}; };

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,5 @@
import * as AssetProxyOwner from '../artifacts/AssetProxyOwner.json'; import * as AssetProxyOwner from '../artifacts/AssetProxyOwner.json';
import * as DutchAuction from '../artifacts/DutchAuction.json';
import * as DummyERC20Token from '../artifacts/DummyERC20Token.json'; import * as DummyERC20Token from '../artifacts/DummyERC20Token.json';
import * as DummyERC721Token from '../artifacts/DummyERC721Token.json'; import * as DummyERC721Token from '../artifacts/DummyERC721Token.json';
import * as ERC20Proxy from '../artifacts/ERC20Proxy.json'; import * as ERC20Proxy from '../artifacts/ERC20Proxy.json';
@ -15,6 +16,7 @@ import * as ZRXToken from '../artifacts/ZRXToken.json';
export { export {
AssetProxyOwner, AssetProxyOwner,
DutchAuction,
DummyERC20Token, DummyERC20Token,
DummyERC721Token, DummyERC721Token,
ERC20Proxy, ERC20Proxy,

View File

@ -8,6 +8,7 @@
"include": ["./src/**/*"], "include": ["./src/**/*"],
"files": [ "files": [
"./artifacts/AssetProxyOwner.json", "./artifacts/AssetProxyOwner.json",
"./artifacts/DutchAuction.json",
"./artifacts/DummyERC20Token.json", "./artifacts/DummyERC20Token.json",
"./artifacts/DummyERC721Token.json", "./artifacts/DummyERC721Token.json",
"./artifacts/ERC20Proxy.json", "./artifacts/ERC20Proxy.json",

View File

@ -20,6 +20,7 @@ import { EtherTokenWrapper } from './contract_wrappers/ether_token_wrapper';
import { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; import { ExchangeWrapper } from './contract_wrappers/exchange_wrapper';
import { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper'; import { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper';
import { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper'; import { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper';
import { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper';
import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema'; import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema';
import { ContractWrappersConfig } from './types'; import { ContractWrappersConfig } from './types';
import { assert } from './utils/assert'; import { assert } from './utils/assert';
@ -65,6 +66,10 @@ export class ContractWrappers {
* An instance of the OrderValidatorWrapper class containing methods for interacting with any OrderValidator smart contract. * An instance of the OrderValidatorWrapper class containing methods for interacting with any OrderValidator smart contract.
*/ */
public orderValidator: OrderValidatorWrapper; public orderValidator: OrderValidatorWrapper;
/**
* An instance of the DutchAuctionWrapper class containing methods for interacting with any DutchAuction smart contract.
*/
public dutchAuction: DutchAuctionWrapper;
private readonly _web3Wrapper: Web3Wrapper; private readonly _web3Wrapper: Web3Wrapper;
/** /**
@ -141,6 +146,11 @@ export class ContractWrappers {
config.networkId, config.networkId,
contractAddresses.orderValidator, contractAddresses.orderValidator,
); );
this.dutchAuction = new DutchAuctionWrapper(
this._web3Wrapper,
config.networkId,
contractAddresses.orderValidator,
);
} }
/** /**
* Unsubscribes from all subscriptions for all contracts. * Unsubscribes from all subscriptions for all contracts.

View File

@ -0,0 +1,151 @@
import { artifacts as protocolArtifacts } from '@0x/contracts-protocol';
import { DutchAuctionContract } from '@0x/abi-gen-wrappers';
import { DutchAuction } from '@0x/contract-artifacts';
import { LogDecoder } from '@0x/contracts-test-utils';
import { artifacts as tokensArtifacts } from '@0x/contracts-tokens';
import { _getDefaultContractAddresses } from '../utils/contract_addresses';
import { DutchAuctionDetails, SignedOrder } from '@0x/types';
import { ContractAbi } from 'ethereum-types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { BigNumber } from '@0x/utils';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import ethAbi = require('ethereumjs-abi');
import { schemas } from '@0x/json-schemas';
import { assert } from '../utils/assert';
import ethUtil = require('ethereumjs-util');
import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
import { OrderTransactionOpts } from '../types';
import { ContractWrapper } from './contract_wrapper';
import { ExchangeWrapperError } from '../types';
export class DutchAuctionWrapper extends ContractWrapper {
public abi: ContractAbi = DutchAuction.compilerOutput.abi;
public address: string;
private _dutchAuctionContractIfExists?: DutchAuctionContract;
/**
* Instantiate DutchAuctionWrapper
* @param web3Wrapper Web3Wrapper instance to use.
* @param networkId Desired networkId.
* @param address The address of the Dutch Auction contract. If undefined, will
* default to the known address corresponding to the networkId.
*/
constructor(
web3Wrapper: Web3Wrapper,
networkId: number,
address: string,
) {
super(web3Wrapper, networkId);
this.address = address;
}
/**
* Matches the buy and sell orders at an amount given the following: the current block time, the auction
* start time and the auction begin amount. The sell order is a an order at the lowest amount
* at the end of the auction. Excess from the match is transferred to the seller.
* Over time the price moves from beginAmount to endAmount given the current block.timestamp.
* @param buyOrder The Buyer's order. This order is for the current expected price of the auction.
* @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
* @param from Address the transaction is being sent from.
* @return Transaction receipt with decoded logs.
*/
public async matchOrdersAsync(
buyOrder: SignedOrder,
sellOrder: SignedOrder,
takerAddress: string,
orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true },
): Promise<string> {
// type assertions
assert.doesConformToSchema('buyOrder', buyOrder, schemas.signedOrderSchema);
assert.doesConformToSchema('sellOrder', sellOrder, schemas.signedOrderSchema);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
const normalizedTakerAddress = takerAddress.toLowerCase();
// other assertions
if (
sellOrder.makerAssetData !== buyOrder.takerAssetData ||
sellOrder.takerAssetData !== buyOrder.makerAssetData
) {
throw new Error(ExchangeWrapperError.AssetDataMismatch);
}
// get contract
const dutchAuctionInstance = await this._getDutchAuctionContractAsync();
// validate transaction
if (orderTransactionOpts.shouldValidate) {
await dutchAuctionInstance.matchOrders.callAsync(
buyOrder,
sellOrder,
buyOrder.signature,
sellOrder.signature,
{
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
}
// send transaction
const txHash = await dutchAuctionInstance.matchOrders.sendTransactionAsync(
buyOrder,
sellOrder,
buyOrder.signature,
sellOrder.signature,
{
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
return txHash;
}
/**
* Calculates the Auction Details for the given order
* @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
* @return The dutch auction details.
*/
public async getAuctionDetailsAsync(sellOrder: SignedOrder): Promise<DutchAuctionDetails> {
// type assertions
assert.doesConformToSchema('sellOrder', sellOrder, schemas.signedOrderSchema);
// get contract
const dutchAuctionInstance = await this._getDutchAuctionContractAsync();
// call contract
const afterAuctionDetails = await dutchAuctionInstance.getAuctionDetails.callAsync(sellOrder);
return afterAuctionDetails;
}
private async _getDutchAuctionContractAsync(): Promise<DutchAuctionContract> {
if (!_.isUndefined(this._dutchAuctionContractIfExists)) {
return this._dutchAuctionContractIfExists;
}
const contractInstance = new DutchAuctionContract(
this.abi,
this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
this._dutchAuctionContractIfExists = contractInstance;
return this._dutchAuctionContractIfExists;
}
/**
* Dutch auction details are encoded with the asset data for a 0x order. This function produces a hex
* encoded assetData string, containing information both about the asset being traded and the
* dutch auction; which is usable in the makerAssetData or takerAssetData fields in a 0x order.
* @param assetData Hex encoded assetData string for the asset being auctioned.
* @param beginTimeSeconds Begin time of the dutch auction.
* @param beginAmount Starting amount being sold in the dutch auction.
* @return The hex encoded assetData string.
*/
public static encodeDutchAuctionAssetData(assetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string {
const assetDataBuffer = ethUtil.toBuffer(assetData);
const abiEncodedAuctionData = (ethAbi as any).rawEncode(
['uint256', 'uint256'],
[beginTimeSeconds.toString(), beginAmount.toString()],
);
const abiEncodedAuctionDataBuffer = ethUtil.toBuffer(abiEncodedAuctionData);
const dutchAuctionDataBuffer = Buffer.concat([assetDataBuffer, abiEncodedAuctionDataBuffer]);
const dutchAuctionData = ethUtil.bufferToHex(dutchAuctionDataBuffer);
return dutchAuctionData;
};
}

View File

@ -34,6 +34,7 @@ export { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper';
export { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper'; export { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper';
export { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper'; export { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper';
export { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper'; export { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper';
export { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper';
export { TransactionEncoder } from './utils/transaction_encoder'; export { TransactionEncoder } from './utils/transaction_encoder';

View File

@ -0,0 +1,156 @@
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';
import { getLatestBlockTimestampAsync } from '@0x/contracts-test-utils';
import { DutchAuction } from '@0x/contract-artifacts';
import { DutchAuctionWrapper } from '../src/contract_wrappers/dutch_auction_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:custom-no-magic-numbers
describe.only('DutchAuctionWrapper', () => {
const fillableAmount = new BigNumber(5);
const tenMinutesInSeconds = 10 * 60;
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 buyOrder: SignedOrder;
let sellOrder: SignedOrder;
let makerTokenAssetData: string;
let takerTokenAssetData: string;
before(async () => {
console.log(`BEOGIN DEPLOYINH`);
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
console.log(`DEPLOYINH`);
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;
// construct asset data for tokens being swapped
[makerTokenAssetData, takerTokenAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
// encode auction details in maker asset data
const auctionBeginAmount = fillableAmount;
const currentBlockTimestamp = await getLatestBlockTimestampAsync();
const auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
makerTokenAssetData,
auctionBeginTimeSeconds,
auctionBeginAmount
);
takerAssetData = takerTokenAssetData;
// create sell / buy orders for auction
// note that the maker/taker asset datas are swapped in the `buyOrder`
sellOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
buyOrder = await fillScenarios.createFillableSignedOrderAsync(
takerAssetData,
makerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
});
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 expect(
await contractWrappers.dutchAuction.matchOrdersAsync(
badBuyOrder,
badSellOrder,
takerAddress,
{
shouldValidate: true,
},
),
).to.be.rejectedWith('COMPLETE_FILL_FAILED');
});
});
describe('#getAuctionDetailsAsync', () => {
it('should be worth the begin price at the begining of the auction', async () => {
// setup auction details
const auctionBeginAmount = fillableAmount;
const currentBlockTimestamp = await getLatestBlockTimestampAsync();
const auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + tenMinutesInSeconds);
const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
makerTokenAssetData,
auctionBeginTimeSeconds,
auctionBeginAmount
);
const order = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
const auctionDetails = await contractWrappers.dutchAuction.getAuctionDetailsAsync(order);
expect(auctionDetails.currentTimeSeconds).to.be.bignumber.lte(auctionBeginTimeSeconds);
expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount);
expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
});
});
});

View File

@ -141,6 +141,14 @@ export async function runMigrationsAsync(provider: Provider, txDefaults: Partial
zrxAssetData, zrxAssetData,
); );
// DutchAuction
const dutchAuction = await wrappers.DutchAuctionContract.deployFrom0xArtifactAsync(
artifacts.DutchAuction,
provider,
txDefaults,
exchange.address,
);
// Fund the Forwarder with ZRX // Fund the Forwarder with ZRX
const zrxDecimals = await zrxToken.decimals.callAsync(); const zrxDecimals = await zrxToken.decimals.callAsync();
const zrxForwarderAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5000), zrxDecimals); const zrxForwarderAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5000), zrxDecimals);
@ -157,6 +165,7 @@ export async function runMigrationsAsync(provider: Provider, txDefaults: Partial
assetProxyOwner: assetProxyOwner.address, assetProxyOwner: assetProxyOwner.address,
forwarder: forwarder.address, forwarder: forwarder.address,
orderValidator: orderValidator.address, orderValidator: orderValidator.address,
dutchAuction: dutchAuction.address,
}; };
} }

View File

@ -305,24 +305,4 @@ export const assetDataUtils = {
throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`); throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`);
} }
}, },
/**
* Dutch auction details are encoded with the asset data for a 0x order. This function produces a hex
* encoded assetData string, containing information both about the asset being traded and the
* dutch auction; which is usable in the makerAssetData or takerAssetData fields in a 0x order.
* @param assetData Hex encoded assetData string for the asset being auctioned.
* @param beginTimeSeconds Begin time of the dutch auction.
* @param beginAmount Starting amount being sold in the dutch auction.
* @return The hex encoded assetData string.
*/
encodeDutchAuctionAssetData(assetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string {
const assetDataBuffer = ethUtil.toBuffer(assetData);
const abiEncodedAuctionData = (ethAbi as any).rawEncode(
['uint256', 'uint256'],
[beginTimeSeconds.toString(), beginAmount.toString()],
);
const abiEncodedAuctionDataBuffer = ethUtil.toBuffer(abiEncodedAuctionData);
const dutchAuctionDataBuffer = Buffer.concat([assetDataBuffer, abiEncodedAuctionDataBuffer]);
const dutchAuctionData = ethUtil.bufferToHex(dutchAuctionDataBuffer);
return dutchAuctionData;
},
}; };