diff options
Diffstat (limited to 'packages/contract-wrappers')
10 files changed, 372 insertions, 4 deletions
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index b3c9b0fb3..b7c89a325 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,5 +1,13 @@ [ { + "version": "1.0.1-rc.6", + "changes": [ + { + "note": "Add `OrderValidatorWrapper`" + } + ] + }, + { "version": "1.0.1-rc.5", "changes": [ { diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index 7d9417d65..a49a81d61 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -14,7 +14,7 @@ "watch_without_deps": "yarn pre_build && tsc -w", "build": "yarn pre_build && tsc", "pre_build": "run-s update_artifacts_v2_beta update_artifacts_v2 generate_contract_wrappers copy_artifacts", - "generate_contract_wrappers": "abi-gen --abis 'src/artifacts/@(Exchange|DummyERC20Token|DummyERC721Token|ZRXToken|ERC20Token|ERC721Token|WETH9|ERC20Proxy|ERC721Proxy|Forwarder).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers", + "generate_contract_wrappers": "abi-gen --abis 'src/artifacts/@(Exchange|DummyERC20Token|DummyERC721Token|ZRXToken|ERC20Token|ERC721Token|WETH9|ERC20Proxy|ERC721Proxy|Forwarder|OrderValidator).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers", "lint": "tslint --project . --exclude **/src/contract_wrappers/**/* --exclude **/lib/**/*", "test:circleci": "run-s test:coverage", "test": "yarn run_mocha", diff --git a/packages/contract-wrappers/src/artifacts.ts b/packages/contract-wrappers/src/artifacts.ts index 7e67544d2..925f34162 100644 --- a/packages/contract-wrappers/src/artifacts.ts +++ b/packages/contract-wrappers/src/artifacts.ts @@ -8,6 +8,7 @@ import * as ERC721Proxy from './artifacts/ERC721Proxy.json'; import * as ERC721Token from './artifacts/ERC721Token.json'; import * as Exchange from './artifacts/Exchange.json'; import * as Forwarder from './artifacts/Forwarder.json'; +import * as OrderValidator from './artifacts/OrderValidator.json'; import * as EtherToken from './artifacts/WETH9.json'; import * as ZRXToken from './artifacts/ZRXToken.json'; @@ -22,4 +23,5 @@ export const artifacts = { ERC20Proxy: (ERC20Proxy as any) as ContractArtifact, ERC721Proxy: (ERC721Proxy as any) as ContractArtifact, Forwarder: (Forwarder as any) as ContractArtifact, + OrderValidator: (OrderValidator as any) as ContractArtifact, }; diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index 4277a0746..8ca0fc93c 100644 --- a/packages/contract-wrappers/src/contract_wrappers.ts +++ b/packages/contract-wrappers/src/contract_wrappers.ts @@ -12,6 +12,7 @@ import { ERC721TokenWrapper } from './contract_wrappers/erc721_token_wrapper'; import { EtherTokenWrapper } from './contract_wrappers/ether_token_wrapper'; import { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; import { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper'; +import { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper'; import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema'; import { contractWrappersPrivateNetworkConfigSchema } from './schemas/contract_wrappers_private_network_config_schema'; import { contractWrappersPublicNetworkConfigSchema } from './schemas/contract_wrappers_public_network_config_schema'; @@ -52,6 +53,10 @@ export class ContractWrappers { * An instance of the ForwarderWrapper class containing methods for interacting with any Forwarder smart contract. */ public forwarder: ForwarderWrapper; + /** + * An instance of the OrderValidatorWrapper class containing methods for interacting with any OrderValidator smart contract. + */ + public orderValidator: OrderValidatorWrapper; private _web3Wrapper: Web3Wrapper; /** @@ -116,6 +121,7 @@ export class ContractWrappers { config.forwarderContractAddress, config.zrxContractAddress, ); + this.orderValidator = new OrderValidatorWrapper(this._web3Wrapper, config.networkId); } /** * Sets a new web3 provider for 0x.js. Updating the provider will stop all diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts new file mode 100644 index 000000000..1da88f624 --- /dev/null +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -0,0 +1,187 @@ +import { schemas } from '@0xproject/json-schemas'; +import { 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 { BalanceAndAllowance, OrderAndTraderInfo, TraderInfo } from '../types'; +import { assert } from '../utils/assert'; + +import { ContractWrapper } from './contract_wrapper'; +import { OrderValidatorContract } from './generated/order_validator'; + +/** + * This class includes the functionality related to interacting with the OrderValidator contract. + */ +export class OrderValidatorWrapper extends ContractWrapper { + public abi: ContractAbi = artifacts.OrderValidator.compilerOutput.abi; + private _orderValidatorContractIfExists?: OrderValidatorContract; + /** + * Instantiate OrderValidatorWrapper + * @param web3Wrapper Web3Wrapper instance to use + * @param networkId Desired networkId + */ + constructor(web3Wrapper: Web3Wrapper, networkId: number) { + super(web3Wrapper, networkId); + } + /** + * Get an object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address + * @param order An object conforming to SignedOrder + * @param takerAddress An ethereum address + * @return OrderAndTraderInfo + */ + public async getOrderAndTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise<OrderAndTraderInfo> { + assert.doesConformToSchema('order', order, schemas.signedOrderSchema); + assert.isETHAddressHex('takerAddress', takerAddress); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const orderAndTraderInfo = await OrderValidatorContractInstance.getOrderAndTraderInfo.callAsync( + order, + takerAddress, + ); + const result = { + orderInfo: orderAndTraderInfo[0], + traderInfo: orderAndTraderInfo[1], + }; + return result; + } + /** + * Get an array of objects conforming to OrderAndTraderInfo containing on-chain information of the provided orders and addresses + * @param orders An array of objects conforming to SignedOrder + * @param takerAddresses An array of ethereum addresses + * @return array of OrderAndTraderInfo + */ + public async getOrdersAndTradersInfoAsync( + orders: SignedOrder[], + takerAddresses: string[], + ): Promise<OrderAndTraderInfo[]> { + assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); + _.forEach(takerAddresses, (takerAddress, index) => + assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress), + ); + assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length'); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const ordersAndTradersInfo = await OrderValidatorContractInstance.getOrdersAndTradersInfo.callAsync( + orders, + takerAddresses, + ); + const orderInfos = ordersAndTradersInfo[0]; + const traderInfos = ordersAndTradersInfo[1]; + const result = _.map(orderInfos, (orderInfo, index) => { + const traderInfo = traderInfos[index]; + return { + orderInfo, + traderInfo, + }; + }); + return result; + } + /** + * Get an object conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order + * @param order An object conforming to SignedOrder + * @param takerAddress An ethereum address + * @return TraderInfo + */ + public async getTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise<TraderInfo> { + assert.doesConformToSchema('order', order, schemas.signedOrderSchema); + assert.isETHAddressHex('takerAddress', takerAddress); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const result = await OrderValidatorContractInstance.getTraderInfo.callAsync(order, takerAddress); + return result; + } + /** + * Get an array of objects conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order + * @param orders An array of objects conforming to SignedOrder + * @param takerAddresses An array of ethereum addresses + * @return array of TraderInfo + */ + public async getTradersInfoAsync(orders: SignedOrder[], takerAddresses: string[]): Promise<TraderInfo[]> { + assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); + _.forEach(takerAddresses, (takerAddress, index) => + assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress), + ); + assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length'); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const result = await OrderValidatorContractInstance.getTradersInfo.callAsync(orders, takerAddresses); + return result; + } + /** + * Get an object conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and assetData + * @param address An ethereum address + * @param assetData An encoded string that can be decoded by a specified proxy contract + * @return BalanceAndAllowance + */ + public async getBalanceAndAllowanceAsync(address: string, assetData: string): Promise<BalanceAndAllowance> { + assert.isETHAddressHex('address', address); + assert.isHexString('assetData', assetData); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const balanceAndAllowance = await OrderValidatorContractInstance.getBalanceAndAllowance.callAsync( + address, + assetData, + ); + const result = { + balance: balanceAndAllowance[0], + allowance: balanceAndAllowance[1], + }; + return result; + } + /** + * Get an array of objects conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and array of assetDatas + * @param address An ethereum address + * @param assetDatas An array of encoded strings that can be decoded by a specified proxy contract + * @return BalanceAndAllowance + */ + public async getBalancesAndAllowancesAsync(address: string, assetDatas: string[]): Promise<BalanceAndAllowance[]> { + assert.isETHAddressHex('address', address); + _.forEach(assetDatas, (assetData, index) => assert.isHexString(`assetDatas[${index}]`, assetData)); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const balancesAndAllowances = await OrderValidatorContractInstance.getBalancesAndAllowances.callAsync( + address, + assetDatas, + ); + const balances = balancesAndAllowances[0]; + const allowances = balancesAndAllowances[1]; + const result = _.map(balances, (balance, index) => { + const allowance = allowances[index]; + return { + balance, + allowance, + }; + }); + return result; + } + /** + * Get owner address of tokenId by calling `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token. + * @param tokenAddress An ethereum address + * @param tokenId An ERC721 tokenId + * @return Owner of tokenId or null address if unowned + */ + public async getERC721TokenOwnerAsync(tokenAddress: string, tokenId: BigNumber): Promise<string | undefined> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isBigNumber('tokenId', tokenId); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const result = await OrderValidatorContractInstance.getERC721TokenOwner.callAsync(tokenAddress, tokenId); + return result; + } + // 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._orderValidatorContractIfExists; + } + private async _getOrderValidatorContractAsync(): Promise<OrderValidatorContract> { + if (!_.isUndefined(this._orderValidatorContractIfExists)) { + return this._orderValidatorContractIfExists; + } + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(artifacts.OrderValidator); + const contractInstance = new OrderValidatorContract( + abi, + address, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + this._orderValidatorContractIfExists = contractInstance; + return this._orderValidatorContractIfExists; + } +} diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index 5e691fc21..b66d8460e 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -6,6 +6,7 @@ export { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; 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 { TransactionEncoder } from './utils/transaction_encoder'; @@ -21,6 +22,9 @@ export { OrderInfo, EventCallback, DecodedLogEvent, + BalanceAndAllowance, + OrderAndTraderInfo, + TraderInfo, } from './types'; export { Order, SignedOrder, AssetProxyId } from '@0xproject/types'; diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index 2b3cdc591..e0b12b7c9 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -188,3 +188,24 @@ export enum OrderStatus { FULLY_FILLED, CANCELLED, } + +export interface TraderInfo { + makerBalance: BigNumber; + makerAllowance: BigNumber; + takerBalance: BigNumber; + takerAllowance: BigNumber; + makerZrxBalance: BigNumber; + makerZrxAllowance: BigNumber; + takerZrxBalance: BigNumber; + takerZrxAllowance: BigNumber; +} + +export interface OrderAndTraderInfo { + orderInfo: OrderInfo; + traderInfo: TraderInfo; +} + +export interface BalanceAndAllowance { + balance: BigNumber; + allowance: BigNumber; +} diff --git a/packages/contract-wrappers/test/forwarder_wrapper_test.ts b/packages/contract-wrappers/test/forwarder_wrapper_test.ts index d0b21225c..a969807b2 100644 --- a/packages/contract-wrappers/test/forwarder_wrapper_test.ts +++ b/packages/contract-wrappers/test/forwarder_wrapper_test.ts @@ -25,10 +25,8 @@ describe('ForwarderWrapper', () => { blockPollingIntervalMs: 0, }; const fillableAmount = new BigNumber(5); - const takerTokenFillAmount = new BigNumber(5); let contractWrappers: ContractWrappers; let fillScenarios: FillScenarios; - let forwarderContractAddress: string; let exchangeContractAddress: string; let zrxTokenAddress: string; let userAddresses: string[]; @@ -46,7 +44,6 @@ describe('ForwarderWrapper', () => { before(async () => { await blockchainLifecycle.startAsync(); contractWrappers = new ContractWrappers(provider, contractWrappersConfig); - forwarderContractAddress = contractWrappers.forwarder.getContractAddress(); exchangeContractAddress = contractWrappers.exchange.getContractAddress(); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); zrxTokenAddress = tokenUtils.getProtocolTokenAddress(); diff --git a/packages/contract-wrappers/test/order_validator_wrapper_test.ts b/packages/contract-wrappers/test/order_validator_wrapper_test.ts new file mode 100644 index 000000000..2fdb00a71 --- /dev/null +++ b/packages/contract-wrappers/test/order_validator_wrapper_test.ts @@ -0,0 +1,142 @@ +import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils'; +import { FillScenarios } from '@0xproject/fill-scenarios'; +import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; +import { DoneCallback, SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import { BlockParamLiteral } from 'ethereum-types'; +import * as _ from 'lodash'; +import 'mocha'; + +import { ContractWrappers, ExchangeCancelEventArgs, ExchangeEvents, ExchangeFillEventArgs, OrderStatus } from '../src'; +import { DecodedLogEvent, OrderInfo, TraderInfo } from '../src/types'; + +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { tokenUtils } from './utils/token_utils'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('OrderValidator', () => { + const contractWrappersConfig = { + networkId: constants.TESTRPC_NETWORK_ID, + blockPollingIntervalMs: 0, + }; + const fillableAmount = new BigNumber(5); + const partialFillAmount = new BigNumber(2); + let contractWrappers: ContractWrappers; + let fillScenarios: FillScenarios; + let exchangeContractAddress: string; + let zrxTokenAddress: string; + let zrxTokenAssetData: string; + let userAddresses: string[]; + let coinbase: string; + let makerAddress: string; + let takerAddress: string; + let feeRecipient: string; + let anotherMakerAddress: string; + let makerTokenAddress: string; + let takerTokenAddress: string; + let makerAssetData: string; + let takerAssetData: string; + let signedOrder: SignedOrder; + let anotherSignedOrder: SignedOrder; + before(async () => { + await blockchainLifecycle.startAsync(); + contractWrappers = new ContractWrappers(provider, contractWrappersConfig); + exchangeContractAddress = contractWrappers.exchange.getContractAddress(); + userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + zrxTokenAddress = tokenUtils.getProtocolTokenAddress(); + zrxTokenAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress); + fillScenarios = new FillScenarios( + provider, + userAddresses, + zrxTokenAddress, + exchangeContractAddress, + contractWrappers.erc20Proxy.getContractAddress(), + contractWrappers.erc721Proxy.getContractAddress(), + ); + [coinbase, makerAddress, takerAddress, feeRecipient, anotherMakerAddress] = userAddresses; + [makerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + takerTokenAddress = tokenUtils.getWethTokenAddress(); + [makerAssetData, takerAssetData] = [ + assetDataUtils.encodeERC20AssetData(makerTokenAddress), + assetDataUtils.encodeERC20AssetData(takerTokenAddress), + ]; + + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + makerAddress, + constants.NULL_ADDRESS, + fillableAmount, + ); + anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( + zrxTokenAssetData, + takerAssetData, + makerAddress, + constants.NULL_ADDRESS, + fillableAmount, + ); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#getOrdersAndTradersInfoAsync', () => { + let signedOrders: SignedOrder[]; + let takerAddresses: string[]; + let ordersInfo: OrderInfo[]; + let tradersInfo: TraderInfo[]; + beforeEach(async () => { + signedOrders = [signedOrder, anotherSignedOrder]; + takerAddresses = [takerAddress, takerAddress]; + const ordersAndTradersInfo = await contractWrappers.orderValidator.getOrdersAndTradersInfoAsync( + signedOrders, + takerAddresses, + ); + ordersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.orderInfo); + tradersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.traderInfo); + }); + it('should return the same number of order infos and trader infos as input orders', async () => { + expect(ordersInfo.length).to.be.equal(signedOrders.length); + expect(tradersInfo.length).to.be.equal(takerAddresses.length); + }); + it('should return correct on-chain order info for input orders', async () => { + const firstOrderInfo = ordersInfo[0]; + const secondOrderInfo = ordersInfo[1]; + expect(firstOrderInfo.orderStatus).to.be.equal(OrderStatus.FILLABLE); + expect(firstOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT); + expect(secondOrderInfo.orderStatus).to.be.equal(OrderStatus.FILLABLE); + expect(secondOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('should return correct on-chain trader info for input takers', async () => { + const firstTraderInfo = tradersInfo[0]; + const secondTraderInfo = tradersInfo[1]; + expect(firstTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0)); + expect(firstTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0)); + expect(firstTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0)); + expect(firstTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0)); + }); + }); +}); diff --git a/packages/contract-wrappers/test/utils/constants.ts b/packages/contract-wrappers/test/utils/constants.ts index 60c3370a2..f38728b77 100644 --- a/packages/contract-wrappers/test/utils/constants.ts +++ b/packages/contract-wrappers/test/utils/constants.ts @@ -15,4 +15,5 @@ export const constants = { DUMMY_TOKEN_TOTAL_SUPPLY: new BigNumber(10 ** 27), // tslint:disable-line:custom-no-magic-numbers NUM_DUMMY_ERC20_TO_DEPLOY: 3, NUM_DUMMY_ERC721_TO_DEPLOY: 1, + ZERO_AMOUNT: new BigNumber(0), }; |