diff options
Diffstat (limited to 'packages/contract-wrappers')
-rw-r--r-- | packages/contract-wrappers/CHANGELOG.json | 9 | ||||
-rw-r--r-- | packages/contract-wrappers/package.json | 2 | ||||
-rw-r--r-- | packages/contract-wrappers/src/contract_wrappers.ts | 10 | ||||
-rw-r--r-- | packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts | 182 | ||||
-rw-r--r-- | packages/contract-wrappers/src/index.ts | 15 | ||||
-rw-r--r-- | packages/contract-wrappers/src/types.ts | 12 | ||||
-rw-r--r-- | packages/contract-wrappers/test/dutch_auction_wrapper_test.ts | 128 | ||||
-rw-r--r-- | packages/contract-wrappers/test/utils/dutch_auction_utils.ts | 153 |
8 files changed, 509 insertions, 2 deletions
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 581fbdee1..677292030 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -3,6 +3,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" }, { diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index f3f7301fd..e4de75ab2 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -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", diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index 0c535bd5c..4e594593e 100644 --- a/packages/contract-wrappers/src/contract_wrappers.ts +++ b/packages/contract-wrappers/src/contract_wrappers.ts @@ -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. diff --git a/packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts new file mode 100644 index 000000000..c1aceff47 --- /dev/null +++ b/packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts @@ -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; + } +} diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index d66ff5c9c..69bbe3c91 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -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, diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index 14d4649ae..945ca88cd 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -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; +} diff --git a/packages/contract-wrappers/test/dutch_auction_wrapper_test.ts b/packages/contract-wrappers/test/dutch_auction_wrapper_test.ts new file mode 100644 index 000000000..d7a6ca015 --- /dev/null +++ b/packages/contract-wrappers/test/dutch_auction_wrapper_test.ts @@ -0,0 +1,128 @@ +import { expectTransactionFailedAsync, getLatestBlockTimestampAsync } from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { assetDataUtils } from '@0x/order-utils'; +import { RevertReason, SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; +import 'mocha'; + +import { ContractWrappers } from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { DutchAuctionUtils } from './utils/dutch_auction_utils'; +import { migrateOnceAsync } from './utils/migrate'; +import { tokenUtils } from './utils/token_utils'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +// tslint:disable:custom-no-magic-numbers +describe('DutchAuctionWrapper', () => { + const makerAssetAmount = new BigNumber(5); + const auctionEndTakerAmount = new BigNumber(10); + const auctionBeginTakerAmount = auctionEndTakerAmount.times(2); + const tenMinutesInSeconds = 10 * 60; + let contractWrappers: ContractWrappers; + let exchangeContractAddress: string; + let userAddresses: string[]; + let makerAddress: string; + let takerAddress: string; + let makerTokenAddress: string; + let takerTokenAddress: string; + let buyOrder: SignedOrder; + let sellOrder: SignedOrder; + let makerTokenAssetData: string; + let takerTokenAssetData: string; + let auctionBeginTimeSeconds: BigNumber; + let auctionEndTimeSeconds: BigNumber; + before(async () => { + // setup contract wrappers & addresses + const contractAddresses = await migrateOnceAsync(); + await blockchainLifecycle.startAsync(); + const config = { + networkId: constants.TESTRPC_NETWORK_ID, + contractAddresses, + blockPollingIntervalMs: 10, + }; + contractWrappers = new ContractWrappers(provider, config); + exchangeContractAddress = contractWrappers.exchange.address; + userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + [, makerAddress, takerAddress] = userAddresses; + [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + // construct asset data for tokens being swapped + [makerTokenAssetData, takerTokenAssetData] = [ + assetDataUtils.encodeERC20AssetData(makerTokenAddress), + assetDataUtils.encodeERC20AssetData(takerTokenAddress), + ]; + // setup auction details in maker asset data + const currentBlockTimestamp: number = await getLatestBlockTimestampAsync(); + auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); + auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp + tenMinutesInSeconds); + // create auction orders + const coinbase = userAddresses[0]; + const dutchAuctionUtils = new DutchAuctionUtils( + web3Wrapper, + coinbase, + exchangeContractAddress, + contractWrappers.erc20Proxy.address, + ); + sellOrder = await dutchAuctionUtils.createSignedSellOrderAsync( + auctionBeginTimeSeconds, + auctionEndTimeSeconds, + auctionBeginTakerAmount, + auctionEndTakerAmount, + makerAssetAmount, + makerTokenAssetData, + takerTokenAssetData, + makerAddress, + constants.NULL_ADDRESS, + ); + buyOrder = await dutchAuctionUtils.createSignedBuyOrderAsync(sellOrder, takerAddress); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#matchOrdersAsync', () => { + it('should match two orders', async () => { + const txHash = await contractWrappers.dutchAuction.matchOrdersAsync(buyOrder, sellOrder, takerAddress); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + }); + it('should throw when invalid transaction and shouldValidate is true', async () => { + // request match with bad buy/sell orders + const badSellOrder = buyOrder; + const badBuyOrder = sellOrder; + return expectTransactionFailedAsync( + contractWrappers.dutchAuction.matchOrdersAsync(badBuyOrder, badSellOrder, takerAddress, { + shouldValidate: true, + }), + RevertReason.InvalidAssetData, + ); + }); + }); + + describe('#getAuctionDetailsAsync', () => { + it('should get auction details', async () => { + // get auction details + const auctionDetails = await contractWrappers.dutchAuction.getAuctionDetailsAsync(sellOrder); + // run some basic sanity checks on the return value + expect(auctionDetails.beginTimeSeconds, 'auctionDetails.beginTimeSeconds').to.be.bignumber.equal( + auctionBeginTimeSeconds, + ); + expect(auctionDetails.beginAmount, 'auctionDetails.beginAmount').to.be.bignumber.equal( + auctionBeginTakerAmount, + ); + expect(auctionDetails.endTimeSeconds, 'auctionDetails.endTimeSeconds').to.be.bignumber.equal( + auctionEndTimeSeconds, + ); + }); + }); +}); diff --git a/packages/contract-wrappers/test/utils/dutch_auction_utils.ts b/packages/contract-wrappers/test/utils/dutch_auction_utils.ts new file mode 100644 index 000000000..8e2aef217 --- /dev/null +++ b/packages/contract-wrappers/test/utils/dutch_auction_utils.ts @@ -0,0 +1,153 @@ +import { DummyERC20TokenContract } from '@0x/abi-gen-wrappers'; +import * as artifacts from '@0x/contract-artifacts'; +import { assetDataUtils } from '@0x/order-utils'; +import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; + +import { DutchAuctionWrapper } from '../../src/contract_wrappers/dutch_auction_wrapper'; + +import { constants } from './constants'; + +export class DutchAuctionUtils { + private readonly _web3Wrapper: Web3Wrapper; + private readonly _coinbase: string; + private readonly _exchangeAddress: string; + private readonly _erc20ProxyAddress: string; + + constructor(web3Wrapper: Web3Wrapper, coinbase: string, exchangeAddress: string, erc20ProxyAddress: string) { + this._web3Wrapper = web3Wrapper; + this._coinbase = coinbase; + this._exchangeAddress = exchangeAddress; + this._erc20ProxyAddress = erc20ProxyAddress; + } + public async createSignedSellOrderAsync( + auctionBeginTimeSections: BigNumber, + acutionEndTimeSeconds: BigNumber, + auctionBeginTakerAssetAmount: BigNumber, + auctionEndTakerAssetAmount: BigNumber, + makerAssetAmount: BigNumber, + makerAssetData: string, + takerAssetData: string, + makerAddress: string, + takerAddress: string, + senderAddress?: string, + makerFee?: BigNumber, + takerFee?: BigNumber, + feeRecipientAddress?: string, + ): Promise<SignedOrder> { + // Notes on sell order: + // - The `takerAssetAmount` is set to the `auctionEndTakerAssetAmount`, which is the lowest amount the + // the seller can expect to receive + // - The `makerAssetData` is overloaded to include the auction begin time and begin taker asset amount + const makerAssetDataWithAuctionDetails = DutchAuctionWrapper.encodeDutchAuctionAssetData( + makerAssetData, + auctionBeginTimeSections, + auctionBeginTakerAssetAmount, + ); + const signedOrder = await orderFactory.createSignedOrderAsync( + this._web3Wrapper.getProvider(), + makerAddress, + makerAssetAmount, + makerAssetDataWithAuctionDetails, + auctionEndTakerAssetAmount, + takerAssetData, + this._exchangeAddress, + { + takerAddress, + senderAddress, + makerFee, + takerFee, + feeRecipientAddress, + expirationTimeSeconds: acutionEndTimeSeconds, + }, + ); + const erc20AssetData = assetDataUtils.decodeERC20AssetData(makerAssetData); + await this._increaseERC20BalanceAndAllowanceAsync(erc20AssetData.tokenAddress, makerAddress, makerAssetAmount); + return signedOrder; + } + public async createSignedBuyOrderAsync( + sellOrder: SignedOrder, + buyerAddress: string, + senderAddress?: string, + makerFee?: BigNumber, + takerFee?: BigNumber, + feeRecipientAddress?: string, + expirationTimeSeconds?: BigNumber, + ): Promise<SignedOrder> { + const dutchAuctionData = DutchAuctionWrapper.decodeDutchAuctionData(sellOrder.makerAssetData); + // Notes on buy order: + // - The `makerAssetAmount` is set to `dutchAuctionData.beginAmount`, which is + // the highest amount the buyer would have to pay out at any point during the auction. + // - The `takerAssetAmount` is set to the seller's `makerAssetAmount`, as the buyer + // receives the entire amount being sold by the seller. + // - The `makerAssetData`/`takerAssetData` are reversed from the sell order + const signedOrder = await orderFactory.createSignedOrderAsync( + this._web3Wrapper.getProvider(), + buyerAddress, + dutchAuctionData.beginAmount, + sellOrder.takerAssetData, + sellOrder.makerAssetAmount, + sellOrder.makerAssetData, + sellOrder.exchangeAddress, + { + senderAddress, + makerFee, + takerFee, + feeRecipientAddress, + expirationTimeSeconds, + }, + ); + const buyerERC20AssetData = assetDataUtils.decodeERC20AssetData(sellOrder.takerAssetData); + await this._increaseERC20BalanceAndAllowanceAsync( + buyerERC20AssetData.tokenAddress, + buyerAddress, + dutchAuctionData.beginAmount, + ); + return signedOrder; + } + private async _increaseERC20BalanceAndAllowanceAsync( + tokenAddress: string, + address: string, + amount: BigNumber, + ): Promise<void> { + if (amount.isZero() || address === constants.NULL_ADDRESS) { + return; // noop + } + await Promise.all([ + this._increaseERC20BalanceAsync(tokenAddress, address, amount), + this._increaseERC20AllowanceAsync(tokenAddress, address, amount), + ]); + } + private async _increaseERC20BalanceAsync(tokenAddress: string, address: string, amount: BigNumber): Promise<void> { + const erc20Token = new DummyERC20TokenContract( + artifacts.DummyERC20Token.compilerOutput.abi, + tokenAddress, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + const txHash = await erc20Token.transfer.sendTransactionAsync(address, amount, { + from: this._coinbase, + }); + await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + } + private async _increaseERC20AllowanceAsync( + tokenAddress: string, + address: string, + amount: BigNumber, + ): Promise<void> { + const erc20Token = new DummyERC20TokenContract( + artifacts.DummyERC20Token.compilerOutput.abi, + tokenAddress, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + const oldMakerAllowance = await erc20Token.allowance.callAsync(address, this._erc20ProxyAddress); + const newMakerAllowance = oldMakerAllowance.plus(amount); + const txHash = await erc20Token.approve.sendTransactionAsync(this._erc20ProxyAddress, newMakerAllowance, { + from: address, + }); + await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + } +} |