aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts')
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts220
1 files changed, 220 insertions, 0 deletions
diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts
new file mode 100644
index 000000000..13ef0fe01
--- /dev/null
+++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts
@@ -0,0 +1,220 @@
+import { schemas } from '@0xproject/json-schemas';
+import { AssetProxyId, SignedOrder } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ContractAbi } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { artifacts } from '../artifacts';
+import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
+import { txOptsSchema } from '../schemas/tx_opts_schema';
+import { TransactionOpts } from '../types';
+import { assert } from '../utils/assert';
+import { calldataOptimizationUtils } from '../utils/calldata_optimization_utils';
+import { constants } from '../utils/constants';
+
+import { ContractWrapper } from './contract_wrapper';
+import { ForwarderContract } from './generated/forwarder';
+
+/**
+ * This class includes the functionality related to interacting with the Forwarder contract.
+ */
+export class ForwarderWrapper extends ContractWrapper {
+ public abi: ContractAbi = artifacts.Forwarder.compilerOutput.abi;
+ private _forwarderContractIfExists?: ForwarderContract;
+ private _contractAddressIfExists?: string;
+ private _zrxContractAddressIfExists?: string;
+ constructor(
+ web3Wrapper: Web3Wrapper,
+ networkId: number,
+ contractAddressIfExists?: string,
+ zrxContractAddressIfExists?: string,
+ ) {
+ super(web3Wrapper, networkId);
+ this._contractAddressIfExists = contractAddressIfExists;
+ this._zrxContractAddressIfExists = zrxContractAddressIfExists;
+ }
+ /**
+ * Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
+ * Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
+ * 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
+ * Any ETH not spent will be refunded to sender.
+ * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
+ * All orders must specify WETH as the takerAsset
+ * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
+ * Provider provided at instantiation.
+ * @param ethAmount The amount of eth to send with the transaction (in wei).
+ * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
+ * Used to purchase ZRX for primary order fees.
+ * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ * Defaults to 0.
+ * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async marketSellOrdersWithEthAsync(
+ signedOrders: SignedOrder[],
+ takerAddress: string,
+ ethAmount: BigNumber,
+ signedFeeOrders: SignedOrder[] = [],
+ feePercentage: BigNumber = constants.ZERO_AMOUNT,
+ feeRecipientAddress: string = constants.NULL_ADDRESS,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ // type assertions
+ assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ assert.isBigNumber('ethAmount', ethAmount);
+ assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema);
+ assert.isBigNumber('feePercentage', feePercentage);
+ assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress);
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ // other assertions
+ assert.ordersCanBeUsedForForwarderContract(signedOrders, this.getEtherTokenAddress());
+ assert.feeOrdersCanBeUsedForForwarderContract(
+ signedFeeOrders,
+ this.getZRXTokenAddress(),
+ this.getEtherTokenAddress(),
+ );
+ // lowercase input addresses
+ const normalizedTakerAddress = takerAddress.toLowerCase();
+ const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase();
+ // optimize orders
+ const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders);
+ const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders);
+ // send transaction
+ const forwarderContractInstance = await this._getForwarderContractAsync();
+ const txHash = await forwarderContractInstance.marketSellOrdersWithEth.sendTransactionAsync(
+ optimizedMarketOrders,
+ _.map(optimizedMarketOrders, order => order.signature),
+ optimizedFeeOrders,
+ _.map(optimizedFeeOrders, order => order.signature),
+ feePercentage,
+ feeRecipientAddress,
+ {
+ value: ethAmount,
+ from: normalizedTakerAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Attempt to purchase makerAssetFillAmount of makerAsset by selling ethAmount provided with transaction.
+ * Any ZRX required to pay fees for primary orders will automatically be purchased by the contract.
+ * Any ETH not spent will be refunded to sender.
+ * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
+ * All orders must specify WETH as the takerAsset
+ * @param makerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
+ * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
+ * Provider provided at instantiation.
+ * @param ethAmount The amount of eth to send with the transaction (in wei).
+ * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
+ * Used to purchase ZRX for primary order fees.
+ * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ * Defaults to 0.
+ * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async marketBuyOrdersWithEthAsync(
+ signedOrders: SignedOrder[],
+ makerAssetFillAmount: BigNumber,
+ takerAddress: string,
+ ethAmount: BigNumber,
+ signedFeeOrders: SignedOrder[] = [],
+ feePercentage: BigNumber = constants.ZERO_AMOUNT,
+ feeRecipientAddress: string = constants.NULL_ADDRESS,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ // type assertions
+ assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
+ assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount);
+ await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
+ assert.isBigNumber('ethAmount', ethAmount);
+ assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema);
+ assert.isBigNumber('feePercentage', feePercentage);
+ assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress);
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ // other assertions
+ assert.ordersCanBeUsedForForwarderContract(signedOrders, this.getEtherTokenAddress());
+ assert.feeOrdersCanBeUsedForForwarderContract(
+ signedFeeOrders,
+ this.getZRXTokenAddress(),
+ this.getEtherTokenAddress(),
+ );
+ // lowercase input addresses
+ const normalizedTakerAddress = takerAddress.toLowerCase();
+ const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase();
+ // optimize orders
+ const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders);
+ const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders);
+ // send transaction
+ const forwarderContractInstance = await this._getForwarderContractAsync();
+ const txHash = await forwarderContractInstance.marketBuyOrdersWithEth.sendTransactionAsync(
+ optimizedMarketOrders,
+ makerAssetFillAmount,
+ _.map(optimizedMarketOrders, order => order.signature),
+ optimizedFeeOrders,
+ _.map(optimizedFeeOrders, order => order.signature),
+ feePercentage,
+ feeRecipientAddress,
+ {
+ value: ethAmount,
+ from: normalizedTakerAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ },
+ );
+ return txHash;
+ }
+ /**
+ * Retrieves the Ethereum address of the Forwarder contract deployed on the network
+ * that the user-passed web3 provider is connected to.
+ * @returns The Ethereum address of the Forwarder contract being used.
+ */
+ public getContractAddress(): string {
+ const contractAddress = this._getContractAddress(artifacts.Forwarder, this._contractAddressIfExists);
+ return contractAddress;
+ }
+ /**
+ * Returns the ZRX token address used by the forwarder contract.
+ * @return Address of ZRX token
+ */
+ public getZRXTokenAddress(): string {
+ const contractAddress = this._getContractAddress(artifacts.ZRXToken, this._zrxContractAddressIfExists);
+ return contractAddress;
+ }
+ /**
+ * Returns the Ether token address used by the forwarder contract.
+ * @return Address of Ether token
+ */
+ public getEtherTokenAddress(): string {
+ const contractAddress = this._getContractAddress(artifacts.EtherToken);
+ return contractAddress;
+ }
+ // HACK: We don't want this method to be visible to the other units within that package but not to the end user.
+ // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused.
+ // tslint:disable-next-line:no-unused-variable
+ private _invalidateContractInstance(): void {
+ delete this._forwarderContractIfExists;
+ }
+ private async _getForwarderContractAsync(): Promise<ForwarderContract> {
+ if (!_.isUndefined(this._forwarderContractIfExists)) {
+ return this._forwarderContractIfExists;
+ }
+ const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
+ artifacts.Forwarder,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new ForwarderContract(
+ abi,
+ address,
+ this._web3Wrapper.getProvider(),
+ this._web3Wrapper.getContractDefaults(),
+ );
+ this._forwarderContractIfExists = contractInstance;
+ return this._forwarderContractIfExists;
+ }
+}