From c71781d9abf7f2654a51dbef95bd18200ea2bde1 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Wed, 18 Jul 2018 15:37:34 +0200 Subject: Merge --- packages/fill-scenarios/src/artifacts.ts | 6 +- packages/fill-scenarios/src/constants.ts | 1 + packages/fill-scenarios/src/fill_scenarios.ts | 298 ++++++++++++++++++++++++++ packages/fill-scenarios/src/index.ts | 223 +------------------ 4 files changed, 304 insertions(+), 224 deletions(-) create mode 100644 packages/fill-scenarios/src/fill_scenarios.ts (limited to 'packages/fill-scenarios/src') diff --git a/packages/fill-scenarios/src/artifacts.ts b/packages/fill-scenarios/src/artifacts.ts index f75374ba2..7f68ae26a 100644 --- a/packages/fill-scenarios/src/artifacts.ts +++ b/packages/fill-scenarios/src/artifacts.ts @@ -1,9 +1,11 @@ import { ContractArtifact } from '@0xproject/sol-compiler'; -import * as ERC20Token from './artifacts/ERC20Token.json'; +import * as DummyERC20Token from './artifacts/DummyERC20Token.json'; +import * as DummyERC721Token from './artifacts/DummyERC721Token.json'; import * as Exchange from './artifacts/Exchange.json'; export const artifacts = { - ERC20Token: (ERC20Token as any) as ContractArtifact, + DummyERC20Token: (DummyERC20Token as any) as ContractArtifact, + DummyERC721Token: (DummyERC721Token as any) as ContractArtifact, Exchange: (Exchange as any) as ContractArtifact, }; diff --git a/packages/fill-scenarios/src/constants.ts b/packages/fill-scenarios/src/constants.ts index ec2fe744a..5661b059f 100644 --- a/packages/fill-scenarios/src/constants.ts +++ b/packages/fill-scenarios/src/constants.ts @@ -1,3 +1,4 @@ export const constants = { + AWAIT_TRANSACTION_MINED_MS: 0, NULL_ADDRESS: '0x0000000000000000000000000000000000000000', }; diff --git a/packages/fill-scenarios/src/fill_scenarios.ts b/packages/fill-scenarios/src/fill_scenarios.ts new file mode 100644 index 000000000..8294657e7 --- /dev/null +++ b/packages/fill-scenarios/src/fill_scenarios.ts @@ -0,0 +1,298 @@ +import { assetProxyUtils, orderFactory } from '@0xproject/order-utils'; +import { AssetProxyId, ERC721AssetData, OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Provider } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { artifacts } from './artifacts'; +import { constants } from './constants'; +import { DummyERC20TokenContract } from './generated_contract_wrappers/dummy_erc20_token'; +import { DummyERC721TokenContract } from './generated_contract_wrappers/dummy_erc721_token'; +import { ExchangeContract } from './generated_contract_wrappers/exchange'; + +export class FillScenarios { + private _web3Wrapper: Web3Wrapper; + private _userAddresses: string[]; + private _coinbase: string; + private _zrxTokenAddress: string; + private _exchangeAddress: string; + private _erc20ProxyAddress: string; + private _erc721ProxyAddress: string; + constructor( + provider: Provider, + userAddresses: string[], + zrxTokenAddress: string, + exchangeAddress: string, + erc20ProxyAddress: string, + erc721ProxyAddress: string, + ) { + this._web3Wrapper = new Web3Wrapper(provider); + this._userAddresses = userAddresses; + this._coinbase = userAddresses[0]; + this._zrxTokenAddress = zrxTokenAddress; + this._exchangeAddress = exchangeAddress; + this._erc20ProxyAddress = erc20ProxyAddress; + this._erc721ProxyAddress = erc721ProxyAddress; + } + public async createFillableSignedOrderAsync( + makerAssetData: string, + takerAssetData: string, + makerAddress: string, + takerAddress: string, + fillableAmount: BigNumber, + expirationTimeSeconds?: BigNumber, + ): Promise { + return this.createAsymmetricFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + fillableAmount, + expirationTimeSeconds, + ); + } + public async createFillableSignedOrderWithFeesAsync( + makerAssetData: string, + takerAssetData: string, + makerFee: BigNumber, + takerFee: BigNumber, + makerAddress: string, + takerAddress: string, + fillableAmount: BigNumber, + feeRecepientAddress: string, + expirationTimeSeconds?: BigNumber, + ): Promise { + return this._createAsymmetricFillableSignedOrderWithFeesAsync( + makerAssetData, + takerAssetData, + makerFee, + takerFee, + makerAddress, + takerAddress, + fillableAmount, + fillableAmount, + feeRecepientAddress, + expirationTimeSeconds, + ); + } + public async createAsymmetricFillableSignedOrderAsync( + makerAssetData: string, + takerAssetData: string, + makerAddress: string, + takerAddress: string, + makerFillableAmount: BigNumber, + takerFillableAmount: BigNumber, + expirationTimeSeconds?: BigNumber, + ): Promise { + const makerFee = new BigNumber(0); + const takerFee = new BigNumber(0); + const feeRecepientAddress = constants.NULL_ADDRESS; + return this._createAsymmetricFillableSignedOrderWithFeesAsync( + makerAssetData, + takerAssetData, + makerFee, + takerFee, + makerAddress, + takerAddress, + makerFillableAmount, + takerFillableAmount, + feeRecepientAddress, + expirationTimeSeconds, + ); + } + public async createPartiallyFilledSignedOrderAsync( + makerAssetData: string, + takerAssetData: string, + takerAddress: string, + fillableAmount: BigNumber, + partialFillAmount: BigNumber, + ): Promise { + const [makerAddress] = this._userAddresses; + const signedOrder = await this.createAsymmetricFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + fillableAmount, + ); + const exchangeInstance = new ExchangeContract( + artifacts.Exchange.compilerOutput.abi, + signedOrder.exchangeAddress, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + + const orderWithoutExchangeAddress = _.omit(signedOrder, [ + 'signature', + 'exchangeAddress', + ]) as OrderWithoutExchangeAddress; + + const txHash = await exchangeInstance.fillOrder.sendTransactionAsync( + orderWithoutExchangeAddress, + partialFillAmount, + signedOrder.signature, + { from: takerAddress }, + ); + await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + return signedOrder; + } + private async _createAsymmetricFillableSignedOrderWithFeesAsync( + makerAssetData: string, + takerAssetData: string, + makerFee: BigNumber, + takerFee: BigNumber, + makerAddress: string, + takerAddress: string, + makerFillableAmount: BigNumber, + takerFillableAmount: BigNumber, + feeRecepientAddress: string, + expirationTimeSeconds?: BigNumber, + ): Promise { + const decodedMakerAssetData = assetProxyUtils.decodeAssetDataOrThrow(makerAssetData); + if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) { + await this._increaseERC20BalanceAndAllowanceAsync( + decodedMakerAssetData.tokenAddress, + makerAddress, + makerFillableAmount, + ); + } else { + if (!makerFillableAmount.equals(1)) { + throw new Error(`ERC721 makerFillableAmount should be equal 1. Found: ${makerFillableAmount}`); + } + await this._increaseERC721BalanceAndAllowanceAsync( + decodedMakerAssetData.tokenAddress, + makerAddress, + (decodedMakerAssetData as ERC721AssetData).tokenId, + ); + } + const decodedTakerAssetData = assetProxyUtils.decodeAssetDataOrThrow(takerAssetData); + if (decodedTakerAssetData.assetProxyId === AssetProxyId.ERC20) { + const takerTokenAddress = decodedTakerAssetData.tokenAddress; + await this._increaseERC20BalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount); + } else { + if (!takerFillableAmount.equals(1)) { + throw new Error(`ERC721 takerFillableAmount should be equal 1. Found: ${takerFillableAmount}`); + } + await this._increaseERC721BalanceAndAllowanceAsync( + decodedTakerAssetData.tokenAddress, + takerAddress, + (decodedMakerAssetData as ERC721AssetData).tokenId, + ); + } + // Fees + await Promise.all([ + this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee), + this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee), + ]); + const senderAddress = constants.NULL_ADDRESS; + + const signedOrder = await orderFactory.createSignedOrderAsync( + this._web3Wrapper.getProvider(), + makerAddress, + takerAddress, + senderAddress, + makerFee, + takerFee, + makerFillableAmount, + makerAssetData, + takerFillableAmount, + takerAssetData, + this._exchangeAddress, + feeRecepientAddress, + expirationTimeSeconds, + ); + return signedOrder; + } + private async _increaseERC721BalanceAndAllowanceAsync( + tokenAddress: string, + address: string, + tokenId: BigNumber, + ): Promise { + await this._increaseERC721BalanceAsync(tokenAddress, address, tokenId); + await this._increaseERC721AllowanceAsync(tokenAddress, address, tokenId); + } + private async _increaseERC721AllowanceAsync( + tokenAddress: string, + address: string, + tokenId: BigNumber, + ): Promise { + const erc721Token = new DummyERC721TokenContract( + artifacts.DummyERC721Token.compilerOutput.abi, + tokenAddress, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + const txHash = await erc721Token.approve.sendTransactionAsync(this._erc721ProxyAddress, tokenId, { + from: address, + }); + await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + } + private async _increaseERC721BalanceAsync( + tokenAddress: string, + address: string, + tokenId: BigNumber, + ): Promise { + const erc721Token = new DummyERC721TokenContract( + artifacts.DummyERC721Token.compilerOutput.abi, + tokenAddress, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + try { + const currentOwner = await erc721Token.ownerOf.callAsync(tokenId); + if (currentOwner !== address) { + throw new Error(`Token ${tokenAddress}:${tokenId} is already owner by ${currentOwner}`); + } + } catch (err) { + const txHash = await erc721Token.mint.sendTransactionAsync(address, tokenId, { from: this._coinbase }); + await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + } + } + private async _increaseERC20BalanceAndAllowanceAsync( + tokenAddress: string, + address: string, + amount: BigNumber, + ): Promise { + 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 { + 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 { + 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); + } +} diff --git a/packages/fill-scenarios/src/index.ts b/packages/fill-scenarios/src/index.ts index 3ffcb3aa9..c51145cdb 100644 --- a/packages/fill-scenarios/src/index.ts +++ b/packages/fill-scenarios/src/index.ts @@ -1,222 +1 @@ -import { assetProxyUtils, orderFactory } from '@0xproject/order-utils'; -import { OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import { Provider } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { artifacts } from './artifacts'; -import { constants } from './constants'; -import { ERC20TokenContract } from './generated_contract_wrappers/erc20_token'; -import { ExchangeContract } from './generated_contract_wrappers/exchange'; - -export class FillScenarios { - private readonly _web3Wrapper: Web3Wrapper; - private readonly _userAddresses: string[]; - private readonly _coinbase: string; - private readonly _zrxTokenAddress: string; - private readonly _exchangeAddress: string; - private readonly _erc20ProxyAddress: string; - constructor( - provider: Provider, - userAddresses: string[], - zrxTokenAddress: string, - exchangeAddress: string, - erc20ProxyAddress: string, - ) { - this._web3Wrapper = new Web3Wrapper(provider); - this._userAddresses = userAddresses; - this._coinbase = userAddresses[0]; - this._zrxTokenAddress = zrxTokenAddress; - this._exchangeAddress = exchangeAddress; - this._erc20ProxyAddress = erc20ProxyAddress; - } - public async createFillableSignedOrderAsync( - makerAssetData: string, - takerAssetData: string, - makerAddress: string, - takerAddress: string, - fillableAmount: BigNumber, - expirationTimeSeconds?: BigNumber, - ): Promise { - return this.createAsymmetricFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - fillableAmount, - expirationTimeSeconds, - ); - } - public async createFillableSignedOrderWithFeesAsync( - makerAssetData: string, - takerAssetData: string, - makerFee: BigNumber, - takerFee: BigNumber, - makerAddress: string, - takerAddress: string, - fillableAmount: BigNumber, - feeRecepientAddress: string, - expirationTimeSeconds?: BigNumber, - ): Promise { - return this._createAsymmetricFillableSignedOrderWithFeesAsync( - makerAssetData, - takerAssetData, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - fillableAmount, - feeRecepientAddress, - expirationTimeSeconds, - ); - } - public async createAsymmetricFillableSignedOrderAsync( - makerAssetData: string, - takerAssetData: string, - makerAddress: string, - takerAddress: string, - makerFillableAmount: BigNumber, - takerFillableAmount: BigNumber, - expirationTimeSeconds?: BigNumber, - ): Promise { - const makerFee = new BigNumber(0); - const takerFee = new BigNumber(0); - const feeRecepientAddress = constants.NULL_ADDRESS; - return this._createAsymmetricFillableSignedOrderWithFeesAsync( - makerAssetData, - takerAssetData, - makerFee, - takerFee, - makerAddress, - takerAddress, - makerFillableAmount, - takerFillableAmount, - feeRecepientAddress, - expirationTimeSeconds, - ); - } - public async createPartiallyFilledSignedOrderAsync( - makerAssetData: string, - takerAssetData: string, - takerAddress: string, - fillableAmount: BigNumber, - partialFillAmount: BigNumber, - ): Promise { - const [makerAddress] = this._userAddresses; - const signedOrder = await this.createAsymmetricFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmount, - fillableAmount, - ); - const exchangeInstance = new ExchangeContract( - artifacts.Exchange.compilerOutput.abi, - signedOrder.exchangeAddress, - this._web3Wrapper.getProvider(), - this._web3Wrapper.getContractDefaults(), - ); - - const orderWithoutExchangeAddress = _.omit(signedOrder, [ - 'signature', - 'exchangeAddress', - ]) as OrderWithoutExchangeAddress; - - await exchangeInstance.fillOrder.sendTransactionAsync( - orderWithoutExchangeAddress, - partialFillAmount, - signedOrder.signature, - { from: takerAddress }, - ); - return signedOrder; - } - private async _createAsymmetricFillableSignedOrderWithFeesAsync( - makerAssetData: string, - takerAssetData: string, - makerFee: BigNumber, - takerFee: BigNumber, - makerAddress: string, - takerAddress: string, - makerFillableAmount: BigNumber, - takerFillableAmount: BigNumber, - feeRecepientAddress: string, - expirationTimeSeconds?: BigNumber, - ): Promise { - const makerERC20AssetData = assetProxyUtils.decodeERC20AssetData(makerAssetData); - const makerTokenAddress = makerERC20AssetData.tokenAddress; - const takerERC20AssetData = assetProxyUtils.decodeERC20AssetData(takerAssetData); - const takerTokenAddress = takerERC20AssetData.tokenAddress; - await Promise.all([ - this._increaseERC20BalanceAndAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount), - this._increaseERC20BalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount), - ]); - await Promise.all([ - this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee), - this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, takerAddress, takerFee), - ]); - const senderAddress = constants.NULL_ADDRESS; - - const signedOrder = await orderFactory.createSignedOrderAsync( - this._web3Wrapper.getProvider(), - makerAddress, - takerAddress, - senderAddress, - makerFee, - takerFee, - makerFillableAmount, - makerAssetData, - takerFillableAmount, - takerAssetData, - this._exchangeAddress, - feeRecepientAddress, - expirationTimeSeconds, - ); - return signedOrder; - } - private async _increaseERC20BalanceAndAllowanceAsync( - tokenAddress: string, - address: string, - amount: BigNumber, - ): Promise { - 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 { - const token = new ERC20TokenContract( - artifacts.ERC20Token.compilerOutput.abi, - tokenAddress, - this._web3Wrapper.getProvider(), - this._web3Wrapper.getContractDefaults(), - ); - await token.transfer.sendTransactionAsync(address, amount, { - from: this._coinbase, - }); - } - private async _increaseERC20AllowanceAsync( - tokenAddress: string, - address: string, - amount: BigNumber, - ): Promise { - const tokenInstance = new ERC20TokenContract( - artifacts.ERC20Token.compilerOutput.abi, - tokenAddress, - this._web3Wrapper.getProvider(), - this._web3Wrapper.getContractDefaults(), - ); - const oldMakerAllowance = await tokenInstance.allowance.callAsync(address, this._erc20ProxyAddress); - const newMakerAllowance = oldMakerAllowance.plus(amount); - - await tokenInstance.approve.sendTransactionAsync(this._erc20ProxyAddress, newMakerAllowance, { - from: address, - }); - } -} +export { FillScenarios } from './fill_scenarios'; -- cgit v1.2.3