Merge pull request #1465 from 0xProject/feature/contracts/dutchAuctionWrapper
Dutch Auction Wrapper
This commit is contained in:
commit
0ac36cef28
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added Dutch Auction Wrapper",
|
||||
"pr": 1465
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
|
@ -45,6 +45,7 @@
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^1.0.19",
|
||||
"@0x/contract-wrappers": "^4.1.3",
|
||||
"@0x/contracts-test-utils": "^1.0.2",
|
||||
"@0x/dev-utils": "^1.0.21",
|
||||
"@0x/sol-compiler": "^1.1.16",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DutchAuctionWrapper } from '@0x/contract-wrappers';
|
||||
import {
|
||||
artifacts as protocolArtifacts,
|
||||
ERC20Wrapper,
|
||||
@ -29,12 +30,11 @@ import { RevertReason, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import ethAbi = require('ethereumjs-abi');
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction';
|
||||
import { artifacts } from '../../src/artifacts';
|
||||
import { DutchAuctionTestWrapper } from '../utils/dutch_auction_test_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
@ -68,19 +68,8 @@ describe(ContractName.DutchAuction, () => {
|
||||
let erc721MakerAssetIds: BigNumber[];
|
||||
const tenMinutesInSeconds = 10 * 60;
|
||||
|
||||
function extendMakerAssetData(makerAssetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string {
|
||||
return ethUtil.bufferToHex(
|
||||
Buffer.concat([
|
||||
ethUtil.toBuffer(makerAssetData),
|
||||
ethUtil.toBuffer(
|
||||
(ethAbi as any).rawEncode(
|
||||
['uint256', 'uint256'],
|
||||
[beginTimeSeconds.toString(), beginAmount.toString()],
|
||||
),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
let dutchAuctionTestWrapper: DutchAuctionTestWrapper;
|
||||
let defaultERC20MakerAssetData: string;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@ -136,6 +125,7 @@ describe(ContractName.DutchAuction, () => {
|
||||
dutchAuctionInstance.address,
|
||||
provider,
|
||||
);
|
||||
dutchAuctionTestWrapper = new DutchAuctionTestWrapper(dutchAuctionInstance, provider);
|
||||
|
||||
defaultMakerAssetAddress = erc20TokenA.address;
|
||||
const defaultTakerAssetAddress = wethContract.address;
|
||||
@ -174,7 +164,7 @@ describe(ContractName.DutchAuction, () => {
|
||||
feeRecipientAddress,
|
||||
// taker address or sender address should be set to the ducth auction contract
|
||||
takerAddress: dutchAuctionContract.address,
|
||||
makerAssetData: extendMakerAssetData(
|
||||
makerAssetData: DutchAuctionWrapper.encodeDutchAuctionAssetData(
|
||||
assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
@ -199,6 +189,7 @@ describe(ContractName.DutchAuction, () => {
|
||||
const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
|
||||
sellerOrderFactory = new OrderFactory(makerPrivateKey, sellerDefaultOrderParams);
|
||||
buyerOrderFactory = new OrderFactory(takerPrivateKey, buyerDefaultOrderParams);
|
||||
defaultERC20MakerAssetData = assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress);
|
||||
});
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
@ -215,49 +206,41 @@ describe(ContractName.DutchAuction, () => {
|
||||
describe('matchOrders', () => {
|
||||
it('should be worth the begin price at the begining of the auction', async () => {
|
||||
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2);
|
||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
||||
makerAssetData: extendMakerAssetData(
|
||||
assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
),
|
||||
});
|
||||
const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
|
||||
defaultERC20MakerAssetData,
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
);
|
||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerAssetData });
|
||||
const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
||||
expect(auctionDetails.currentTimeSeconds).to.be.bignumber.lte(auctionBeginTimeSeconds);
|
||||
expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount);
|
||||
expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
|
||||
});
|
||||
it('should be be worth the end price at the end of the auction', async () => {
|
||||
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
|
||||
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
|
||||
const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
|
||||
defaultERC20MakerAssetData,
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
);
|
||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
||||
makerAssetData: extendMakerAssetData(
|
||||
assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
),
|
||||
makerAssetData,
|
||||
expirationTimeSeconds: auctionEndTimeSeconds,
|
||||
});
|
||||
const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
||||
expect(auctionDetails.currentTimeSeconds).to.be.bignumber.gte(auctionEndTimeSeconds);
|
||||
expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount);
|
||||
expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
|
||||
});
|
||||
it('should match orders at current amount and send excess to buyer', async () => {
|
||||
const beforeAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
const beforeAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: beforeAuctionDetails.currentAmount.times(2),
|
||||
});
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal(
|
||||
constants.ZERO_AMOUNT,
|
||||
@ -276,17 +259,8 @@ describe(ContractName.DutchAuction, () => {
|
||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
||||
makerFee: new BigNumber(1),
|
||||
});
|
||||
const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
|
||||
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
|
||||
@ -299,18 +273,9 @@ describe(ContractName.DutchAuction, () => {
|
||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync({
|
||||
makerFee: new BigNumber(1),
|
||||
});
|
||||
const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
);
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
||||
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
|
||||
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
|
||||
);
|
||||
@ -321,24 +286,17 @@ describe(ContractName.DutchAuction, () => {
|
||||
it('should revert when auction expires', async () => {
|
||||
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
|
||||
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
|
||||
const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
|
||||
defaultERC20MakerAssetData,
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
);
|
||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
||||
expirationTimeSeconds: auctionEndTimeSeconds,
|
||||
makerAssetData: extendMakerAssetData(
|
||||
assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
),
|
||||
makerAssetData,
|
||||
});
|
||||
return expectTransactionFailedAsync(
|
||||
dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
),
|
||||
dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
|
||||
RevertReason.AuctionExpired,
|
||||
);
|
||||
});
|
||||
@ -347,15 +305,7 @@ describe(ContractName.DutchAuction, () => {
|
||||
makerAssetAmount: sellOrder.takerAssetAmount,
|
||||
});
|
||||
return expectTransactionFailedAsync(
|
||||
dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
),
|
||||
dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
|
||||
RevertReason.AuctionInvalidAmount,
|
||||
);
|
||||
});
|
||||
@ -364,38 +314,23 @@ describe(ContractName.DutchAuction, () => {
|
||||
takerAssetAmount: auctionBeginAmount.plus(1),
|
||||
});
|
||||
return expectTransactionFailedAsync(
|
||||
dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
),
|
||||
dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
|
||||
RevertReason.AuctionInvalidAmount,
|
||||
);
|
||||
});
|
||||
it('begin time is less than end time', async () => {
|
||||
auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds);
|
||||
const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
|
||||
defaultERC20MakerAssetData,
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
);
|
||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
||||
expirationTimeSeconds: auctionEndTimeSeconds,
|
||||
makerAssetData: extendMakerAssetData(
|
||||
assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
),
|
||||
makerAssetData,
|
||||
});
|
||||
return expectTransactionFailedAsync(
|
||||
dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
),
|
||||
dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
|
||||
RevertReason.AuctionInvalidBeginTime,
|
||||
);
|
||||
});
|
||||
@ -404,45 +339,30 @@ describe(ContractName.DutchAuction, () => {
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
return expectTransactionFailedAsync(
|
||||
dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
),
|
||||
dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress),
|
||||
RevertReason.InvalidAssetData,
|
||||
);
|
||||
});
|
||||
|
||||
describe('ERC721', () => {
|
||||
it('should match orders when ERC721', async () => {
|
||||
const makerAssetId = erc721MakerAssetIds[0];
|
||||
const erc721MakerAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId);
|
||||
const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
|
||||
erc721MakerAssetData,
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
);
|
||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: extendMakerAssetData(
|
||||
assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
||||
auctionBeginTimeSeconds,
|
||||
auctionBeginAmount,
|
||||
),
|
||||
makerAssetData,
|
||||
});
|
||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync({
|
||||
takerAssetAmount: new BigNumber(1),
|
||||
takerAssetData: sellOrder.makerAssetData,
|
||||
});
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
),
|
||||
);
|
||||
const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
// 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
|
||||
|
@ -0,0 +1,62 @@
|
||||
import { artifacts as protocolArtifacts } from '@0x/contracts-protocol';
|
||||
import { LogDecoder } from '@0x/contracts-test-utils';
|
||||
import { artifacts as tokensArtifacts } from '@0x/contracts-tokens';
|
||||
import { DutchAuctionDetails, SignedOrder } from '@0x/types';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction';
|
||||
import { artifacts } from '../../src/artifacts';
|
||||
|
||||
export class DutchAuctionTestWrapper {
|
||||
private readonly _dutchAuctionContract: DutchAuctionContract;
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _logDecoder: LogDecoder;
|
||||
|
||||
constructor(contractInstance: DutchAuctionContract, provider: Provider) {
|
||||
this._dutchAuctionContract = contractInstance;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._logDecoder = new LogDecoder(this._web3Wrapper, {
|
||||
...artifacts,
|
||||
...tokensArtifacts,
|
||||
...protocolArtifacts,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 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,
|
||||
from: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._dutchAuctionContract.matchOrders.sendTransactionAsync(
|
||||
buyOrder,
|
||||
sellOrder,
|
||||
buyOrder.signature,
|
||||
sellOrder.signature,
|
||||
{
|
||||
from,
|
||||
},
|
||||
);
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
/**
|
||||
* 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> {
|
||||
const auctionDetails = await this._dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
|
||||
return auctionDetails;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ export { assetDataUtils, signatureUtils, generatePseudoRandomSalt, orderHashUtil
|
||||
|
||||
export {
|
||||
ContractWrappers,
|
||||
DutchAuctionWrapper,
|
||||
ERC20TokenWrapper,
|
||||
ERC721TokenWrapper,
|
||||
EtherTokenWrapper,
|
||||
@ -27,6 +28,7 @@ export {
|
||||
OrderAndTraderInfo,
|
||||
TraderInfo,
|
||||
ValidateOrderFillableOpts,
|
||||
DutchAuctionData,
|
||||
} from '@0x/contract-wrappers';
|
||||
|
||||
export {
|
||||
@ -79,6 +81,7 @@ export {
|
||||
OrderStateInvalid,
|
||||
OrderState,
|
||||
AssetProxyId,
|
||||
AssetData,
|
||||
SingleAssetData,
|
||||
ERC20AssetData,
|
||||
ERC721AssetData,
|
||||
@ -88,6 +91,7 @@ export {
|
||||
ObjectMap,
|
||||
OrderRelevantState,
|
||||
Stats,
|
||||
DutchAuctionDetails,
|
||||
} from '@0x/types';
|
||||
|
||||
export {
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added Dutch Auction Wrapper",
|
||||
"pr": 1465
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.0.2",
|
||||
"changes": [
|
||||
|
@ -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"
|
||||
},
|
||||
"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": {
|
||||
"type": "git",
|
||||
|
@ -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
|
@ -1,6 +1,7 @@
|
||||
export * from './generated-wrappers/asset_proxy_owner';
|
||||
export * from './generated-wrappers/dummy_erc20_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_token';
|
||||
export * from './generated-wrappers/erc721_proxy';
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added testnet entries for Dutch Auction contract (kovan,rinkeby,ropsten)",
|
||||
"pr": 1465
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
|
@ -9,6 +9,7 @@ export interface ContractAddresses {
|
||||
assetProxyOwner: string;
|
||||
forwarder: string;
|
||||
orderValidator: string;
|
||||
dutchAuction: string;
|
||||
}
|
||||
|
||||
export enum NetworkId {
|
||||
@ -19,6 +20,8 @@ export enum NetworkId {
|
||||
Ganache = 50,
|
||||
}
|
||||
|
||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
1: {
|
||||
erc20Proxy: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e',
|
||||
@ -29,6 +32,8 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
|
||||
forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1',
|
||||
orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138',
|
||||
// @todo hysz/dekz: Add mainnet address once deployed.
|
||||
dutchAuction: NULL_ADDRESS,
|
||||
},
|
||||
3: {
|
||||
erc20Proxy: '0xb1408f4c245a23c31b98d2c626777d4c0d766caa',
|
||||
@ -39,6 +44,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
assetProxyOwner: '0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b',
|
||||
forwarder: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e',
|
||||
orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f',
|
||||
dutchAuction: '0x2df6b59309f35ada230ec7d61d7d97355017a1df',
|
||||
},
|
||||
4: {
|
||||
exchange: '0xbce0b5f6eb618c565c3e5f5cd69652bbc279f44e',
|
||||
@ -49,6 +55,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
assetProxyOwner: '0xe1703da878afcebff5b7624a826902af475b9c03',
|
||||
forwarder: '0x2d40589abbdee84961f3a7656b9af7adb0ee5ab4',
|
||||
orderValidator: '0x0c5173a51e26b29d6126c686756fb9fbef71f762',
|
||||
dutchAuction: '0xdd7bd6437e67c422879364740ab5855fe3dc41f7',
|
||||
},
|
||||
42: {
|
||||
erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e',
|
||||
@ -59,6 +66,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
assetProxyOwner: '0x2c824d2882baa668e0d5202b1e7f2922278703f8',
|
||||
forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
|
||||
orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d',
|
||||
dutchAuction: '0xe11667fb51f34c5367f40d7e379327ce32ee7150',
|
||||
},
|
||||
// NetworkId 50 represents our Ganache snapshot generated from migrations.
|
||||
50: {
|
||||
@ -70,6 +78,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
assetProxyOwner: '0x34d402f14d58e001d8efbe6585051bf9706aa064',
|
||||
forwarder: '0xb69e673309512a9d726f87304c6984054f87a93b',
|
||||
orderValidator: '0xe86bb98fcf9bff3512c74589b78fb168200cc546',
|
||||
dutchAuction: '0xdc688d29394a3f1e6f1e5100862776691afaf3d2',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"pr": 1465,
|
||||
"note": "Added artifact for Dutch Auction contract"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.2",
|
||||
"changes": [
|
||||
|
310
packages/contract-artifacts/artifacts/DutchAuction.json
generated
Normal file
310
packages/contract-artifacts/artifacts/DutchAuction.json
generated
Normal file
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
import * as AssetProxyOwner from '../artifacts/AssetProxyOwner.json';
|
||||
import * as DutchAuction from '../artifacts/DutchAuction.json';
|
||||
import * as DummyERC20Token from '../artifacts/DummyERC20Token.json';
|
||||
import * as DummyERC721Token from '../artifacts/DummyERC721Token.json';
|
||||
import * as ERC20Proxy from '../artifacts/ERC20Proxy.json';
|
||||
@ -15,6 +16,7 @@ import * as ZRXToken from '../artifacts/ZRXToken.json';
|
||||
|
||||
export {
|
||||
AssetProxyOwner,
|
||||
DutchAuction,
|
||||
DummyERC20Token,
|
||||
DummyERC721Token,
|
||||
ERC20Proxy,
|
||||
|
@ -8,6 +8,7 @@
|
||||
"include": ["./src/**/*"],
|
||||
"files": [
|
||||
"./artifacts/AssetProxyOwner.json",
|
||||
"./artifacts/DutchAuction.json",
|
||||
"./artifacts/DummyERC20Token.json",
|
||||
"./artifacts/DummyERC721Token.json",
|
||||
"./artifacts/ERC20Proxy.json",
|
||||
|
@ -1,6 +1,15 @@
|
||||
[
|
||||
{
|
||||
"version": "4.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added Dutch Auction wrapper",
|
||||
"pr": 1465
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add support for Trust Wallet signature denial error"
|
||||
|
@ -69,6 +69,7 @@
|
||||
"@0x/assert": "^1.0.20",
|
||||
"@0x/contract-addresses": "^2.0.0",
|
||||
"@0x/contract-artifacts": "^1.1.2",
|
||||
"@0x/contracts-test-utils": "^1.0.2",
|
||||
"@0x/fill-scenarios": "^1.0.16",
|
||||
"@0x/json-schemas": "^2.1.4",
|
||||
"@0x/order-utils": "^3.0.7",
|
||||
@ -76,6 +77,7 @@
|
||||
"@0x/typescript-typings": "^3.0.6",
|
||||
"@0x/utils": "^2.0.8",
|
||||
"@0x/web3-wrapper": "^3.2.1",
|
||||
"ethereumjs-abi": "0.6.5",
|
||||
"ethereum-types": "^1.1.4",
|
||||
"ethereumjs-blockstream": "6.0.0",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
|
@ -12,6 +12,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { Provider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper';
|
||||
import { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper';
|
||||
import { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper';
|
||||
import { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper';
|
||||
@ -65,6 +66,10 @@ export class ContractWrappers {
|
||||
* An instance of the OrderValidatorWrapper class containing methods for interacting with any OrderValidator smart contract.
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
@ -141,6 +146,11 @@ export class ContractWrappers {
|
||||
config.networkId,
|
||||
contractAddresses.orderValidator,
|
||||
);
|
||||
this.dutchAuction = new DutchAuctionWrapper(
|
||||
this._web3Wrapper,
|
||||
config.networkId,
|
||||
contractAddresses.dutchAuction,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Unsubscribes from all subscriptions for all contracts.
|
||||
|
@ -0,0 +1,182 @@
|
||||
import { DutchAuctionContract } from '@0x/abi-gen-wrappers';
|
||||
import { DutchAuction } from '@0x/contract-artifacts';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { DutchAuctionDetails, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { ContractAbi } from 'ethereum-types';
|
||||
import * as ethAbi from 'ethereumjs-abi';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
|
||||
import { txOptsSchema } from '../schemas/tx_opts_schema';
|
||||
import { DutchAuctionData, DutchAuctionWrapperError, OrderTransactionOpts } from '../types';
|
||||
import { assert } from '../utils/assert';
|
||||
import { _getDefaultContractAddresses } from '../utils/contract_addresses';
|
||||
|
||||
import { ContractWrapper } from './contract_wrapper';
|
||||
|
||||
export class DutchAuctionWrapper extends ContractWrapper {
|
||||
public abi: ContractAbi = DutchAuction.compilerOutput.abi;
|
||||
public address: string;
|
||||
private _dutchAuctionContractIfExists?: DutchAuctionContract;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* Dutch auction details are encoded with the asset data for a 0x order. This function decodes a hex
|
||||
* encoded assetData string, containing information both about the asset being traded and the
|
||||
* dutch auction.
|
||||
* @param dutchAuctionData Hex encoded assetData string for the asset being auctioned.
|
||||
* @return An object containing the auction asset, auction begin time and auction begin amount.
|
||||
*/
|
||||
public static decodeDutchAuctionData(dutchAuctionData: string): DutchAuctionData {
|
||||
const dutchAuctionDataBuffer = ethUtil.toBuffer(dutchAuctionData);
|
||||
// Decode asset data
|
||||
const dutchAuctionDataLengthInBytes = 64;
|
||||
const assetDataBuffer = dutchAuctionDataBuffer.slice(
|
||||
0,
|
||||
dutchAuctionDataBuffer.byteLength - dutchAuctionDataLengthInBytes,
|
||||
);
|
||||
const assetDataHex = ethUtil.bufferToHex(assetDataBuffer);
|
||||
const assetData = assetDataUtils.decodeAssetDataOrThrow(assetDataHex);
|
||||
// Decode auction details
|
||||
const dutchAuctionDetailsBuffer = dutchAuctionDataBuffer.slice(
|
||||
dutchAuctionDataBuffer.byteLength - dutchAuctionDataLengthInBytes,
|
||||
);
|
||||
const [beginTimeSecondsAsBN, beginAmountAsBN] = ethAbi.rawDecode(
|
||||
['uint256', 'uint256'],
|
||||
dutchAuctionDetailsBuffer,
|
||||
);
|
||||
const beginTimeSeconds = new BigNumber(`0x${beginTimeSecondsAsBN.toString()}`);
|
||||
const beginAmount = new BigNumber(`0x${beginAmountAsBN.toString()}`);
|
||||
return {
|
||||
assetData,
|
||||
beginTimeSeconds,
|
||||
beginAmount,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
|
||||
super(web3Wrapper, networkId);
|
||||
this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).dutchAuction : 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 takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
|
||||
* Provider provided at instantiation.
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
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(DutchAuctionWrapperError.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;
|
||||
}
|
||||
/**
|
||||
* Fetches 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 auctionDetails = await dutchAuctionInstance.getAuctionDetails.callAsync(sellOrder);
|
||||
return auctionDetails;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
@ -34,6 +34,7 @@ export { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper';
|
||||
export { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper';
|
||||
export { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper';
|
||||
export { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper';
|
||||
export { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper';
|
||||
|
||||
export { TransactionEncoder } from './utils/transaction_encoder';
|
||||
|
||||
@ -54,9 +55,21 @@ export {
|
||||
OrderAndTraderInfo,
|
||||
TraderInfo,
|
||||
ValidateOrderFillableOpts,
|
||||
DutchAuctionData,
|
||||
} from './types';
|
||||
|
||||
export { Order, SignedOrder, AssetProxyId } from '@0x/types';
|
||||
export {
|
||||
AssetData,
|
||||
ERC20AssetData,
|
||||
ERC721AssetData,
|
||||
SingleAssetData,
|
||||
MultiAssetData,
|
||||
MultiAssetDataWithRecursiveDecoding,
|
||||
DutchAuctionDetails,
|
||||
Order,
|
||||
SignedOrder,
|
||||
AssetProxyId,
|
||||
} from '@0x/types';
|
||||
|
||||
export {
|
||||
BlockParamLiteral,
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
WETH9Events,
|
||||
} from '@0x/abi-gen-wrappers';
|
||||
import { ContractAddresses } from '@0x/contract-addresses';
|
||||
import { OrderState, SignedOrder } from '@0x/types';
|
||||
import { AssetData, OrderState, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { BlockParam, ContractEventArg, DecodedLogArgs, LogEntryEvent, LogWithDecodedArgs } from 'ethereum-types';
|
||||
@ -206,3 +206,13 @@ export interface BalanceAndAllowance {
|
||||
balance: BigNumber;
|
||||
allowance: BigNumber;
|
||||
}
|
||||
|
||||
export enum DutchAuctionWrapperError {
|
||||
AssetDataMismatch = 'ASSET_DATA_MISMATCH',
|
||||
}
|
||||
|
||||
export interface DutchAuctionData {
|
||||
assetData: AssetData;
|
||||
beginTimeSeconds: BigNumber;
|
||||
beginAmount: BigNumber;
|
||||
}
|
||||
|
128
packages/contract-wrappers/test/dutch_auction_wrapper_test.ts
Normal file
128
packages/contract-wrappers/test/dutch_auction_wrapper_test.ts
Normal file
@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
153
packages/contract-wrappers/test/utils/dutch_auction_utils.ts
Normal file
153
packages/contract-wrappers/test/utils/dutch_auction_utils.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added migrations for Dutch Auction contract",
|
||||
"pr": 1465
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.2.2",
|
||||
"changes": [
|
||||
|
@ -141,6 +141,14 @@ export async function runMigrationsAsync(provider: Provider, txDefaults: Partial
|
||||
zrxAssetData,
|
||||
);
|
||||
|
||||
// DutchAuction
|
||||
const dutchAuction = await wrappers.DutchAuctionContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DutchAuction,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchange.address,
|
||||
);
|
||||
|
||||
// Fund the Forwarder with ZRX
|
||||
const zrxDecimals = await zrxToken.decimals.callAsync();
|
||||
const zrxForwarderAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5000), zrxDecimals);
|
||||
@ -157,6 +165,7 @@ export async function runMigrationsAsync(provider: Provider, txDefaults: Partial
|
||||
assetProxyOwner: assetProxyOwner.address,
|
||||
forwarder: forwarder.address,
|
||||
orderValidator: orderValidator.address,
|
||||
dutchAuction: dutchAuction.address,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,10 @@
|
||||
{
|
||||
"note": "Fix a bug when hardcoded CHANGELOG paths cause fetching release notes to fail",
|
||||
"pr": 1311
|
||||
},
|
||||
{
|
||||
"note": "Added DutchAuctionWrapper to the CLASSES_WITH_HIDDEN_CONSTRUCTORS array",
|
||||
"pr": 1465
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -37,6 +37,7 @@ export const docGenConfigs: DocGenConfigs = {
|
||||
// and getting confused. Any class name in this list will not have it's constructor rendered in our docs.
|
||||
CLASSES_WITH_HIDDEN_CONSTRUCTORS: [
|
||||
'AssetBuyer',
|
||||
'DutchAuctionWrapper',
|
||||
'ERC20ProxyWrapper',
|
||||
'ERC20TokenWrapper',
|
||||
'ERC721ProxyWrapper',
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.5.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added types for Dutch Auction contract",
|
||||
"pr": 1465
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.1",
|
||||
"changes": [
|
||||
|
@ -676,3 +676,12 @@ export interface SimpleEvmOutput {
|
||||
export interface SimpleEvmBytecodeOutput {
|
||||
object: string;
|
||||
}
|
||||
|
||||
export interface DutchAuctionDetails {
|
||||
beginTimeSeconds: BigNumber;
|
||||
endTimeSeconds: BigNumber;
|
||||
beginAmount: BigNumber;
|
||||
endAmount: BigNumber;
|
||||
currentAmount: BigNumber;
|
||||
currentTimeSeconds: BigNumber;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user