aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contract-wrappers/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contract-wrappers/src')
-rw-r--r--packages/contract-wrappers/src/contract_wrappers.ts10
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts182
-rw-r--r--packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts74
-rw-r--r--packages/contract-wrappers/src/index.ts15
-rw-r--r--packages/contract-wrappers/src/types.ts12
-rw-r--r--packages/contract-wrappers/src/utils/constants.ts3
-rw-r--r--packages/contract-wrappers/src/utils/decorators.ts5
7 files changed, 267 insertions, 34 deletions
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/fetchers/asset_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
index d10cffe57..1ff130a48 100644
--- a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
+++ b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
@@ -1,8 +1,7 @@
-// tslint:disable:no-unnecessary-type-assertion
import { AbstractBalanceAndProxyAllowanceFetcher, assetDataUtils } from '@0x/order-utils';
-import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BlockParamLiteral } from 'ethereum-types';
+import * as _ from 'lodash';
import { ERC20TokenWrapper } from '../contract_wrappers/erc20_token_wrapper';
import { ERC721TokenWrapper } from '../contract_wrappers/erc721_token_wrapper';
@@ -18,42 +17,45 @@ export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndP
}
public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
- if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) {
- const decodedERC20AssetData = decodedAssetData as ERC20AssetData;
- const balance = await this._erc20Token.getBalanceAsync(decodedERC20AssetData.tokenAddress, userAddress, {
+ let balance: BigNumber | undefined;
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ balance = await this._erc20Token.getBalanceAsync(decodedAssetData.tokenAddress, userAddress, {
defaultBlock: this._stateLayer,
});
- return balance;
- } else {
- const decodedERC721AssetData = decodedAssetData as ERC721AssetData;
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
const tokenOwner = await this._erc721Token.getOwnerOfAsync(
- decodedERC721AssetData.tokenAddress,
- decodedERC721AssetData.tokenId,
+ decodedAssetData.tokenAddress,
+ decodedAssetData.tokenId,
{
defaultBlock: this._stateLayer,
},
);
- const balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0);
- return balance;
+ balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0);
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ // The `balance` for MultiAssetData is the total units of the entire `assetData` that are held by the `userAddress`.
+ for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
+ const nestedAmountElement = decodedAssetData.amounts[index];
+ const nestedAssetBalance = (await this.getBalanceAsync(
+ nestedAssetDataElement,
+ userAddress,
+ )).dividedToIntegerBy(nestedAmountElement);
+ if (_.isUndefined(balance) || nestedAssetBalance.lessThan(balance)) {
+ balance = nestedAssetBalance;
+ }
+ }
}
+ return balance as BigNumber;
}
public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
- if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) {
- const decodedERC20AssetData = decodedAssetData as ERC20AssetData;
- const proxyAllowance = await this._erc20Token.getProxyAllowanceAsync(
- decodedERC20AssetData.tokenAddress,
- userAddress,
- {
- defaultBlock: this._stateLayer,
- },
- );
- return proxyAllowance;
- } else {
- const decodedERC721AssetData = decodedAssetData as ERC721AssetData;
-
+ let proxyAllowance: BigNumber | undefined;
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ proxyAllowance = await this._erc20Token.getProxyAllowanceAsync(decodedAssetData.tokenAddress, userAddress, {
+ defaultBlock: this._stateLayer,
+ });
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync(
- decodedERC721AssetData.tokenAddress,
+ decodedAssetData.tokenAddress,
userAddress,
{
defaultBlock: this._stateLayer,
@@ -63,15 +65,27 @@ export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndP
return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
} else {
const isApproved = await this._erc721Token.isProxyApprovedAsync(
- decodedERC721AssetData.tokenAddress,
- decodedERC721AssetData.tokenId,
+ decodedAssetData.tokenAddress,
+ decodedAssetData.tokenId,
{
defaultBlock: this._stateLayer,
},
);
- const proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0);
- return proxyAllowance;
+ proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0);
+ }
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ // The `proxyAllowance` for MultiAssetData is the total units of the entire `assetData` that the proxies have been approved to spend by the `userAddress`.
+ for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
+ const nestedAmountElement = decodedAssetData.amounts[index];
+ const nestedAssetAllowance = (await this.getProxyAllowanceAsync(
+ nestedAssetDataElement,
+ userAddress,
+ )).dividedToIntegerBy(nestedAmountElement);
+ if (_.isUndefined(proxyAllowance) || nestedAssetAllowance.lessThan(proxyAllowance)) {
+ proxyAllowance = nestedAssetAllowance;
+ }
}
}
+ return proxyAllowance as BigNumber;
}
}
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/src/utils/constants.ts b/packages/contract-wrappers/src/utils/constants.ts
index c587ba526..94afdc112 100644
--- a/packages/contract-wrappers/src/utils/constants.ts
+++ b/packages/contract-wrappers/src/utils/constants.ts
@@ -14,5 +14,6 @@ export const constants = {
ZERO_AMOUNT: new BigNumber(0),
ONE_AMOUNT: new BigNumber(1),
ETHER_TOKEN_DECIMALS: 18,
- USER_DENIED_SIGNATURE_PATTERN: 'User denied transaction signature',
+ METAMASK_USER_DENIED_SIGNATURE_PATTERN: 'User denied transaction signature',
+ TRUST_WALLET_USER_DENIED_SIGNATURE_PATTERN: 'cancelled',
};
diff --git a/packages/contract-wrappers/src/utils/decorators.ts b/packages/contract-wrappers/src/utils/decorators.ts
index a4207ae4c..3acfa3a88 100644
--- a/packages/contract-wrappers/src/utils/decorators.ts
+++ b/packages/contract-wrappers/src/utils/decorators.ts
@@ -30,7 +30,10 @@ const schemaErrorTransformer = (error: Error) => {
};
const signatureRequestErrorTransformer = (error: Error) => {
- if (_.includes(error.message, constants.USER_DENIED_SIGNATURE_PATTERN)) {
+ if (
+ _.includes(error.message, constants.METAMASK_USER_DENIED_SIGNATURE_PATTERN) ||
+ _.includes(error.message, constants.TRUST_WALLET_USER_DENIED_SIGNATURE_PATTERN)
+ ) {
const errMsg = ContractWrappersError.SignatureRequestDenied;
return new Error(errMsg);
}