diff options
Diffstat (limited to 'packages')
329 files changed, 9784 insertions, 1877 deletions
diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 32351ad82..2f7320fea 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,5 +1,16 @@ [ { + "version": "3.0.0", + "changes": [ + { + "note": + "Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types. No longer export `AssetData`.", + "pr": 1363 + } + ], + "timestamp": 1547040760 + }, + { "version": "2.0.8", "changes": [ { diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 2923fdf03..258dee66a 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.0 - _January 9, 2019_ + + * Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types. No longer export `AssetData`. (#1363) + ## v2.0.8 - _December 13, 2018_ * Dependencies updated diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 2960c9e4b..74522c784 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "2.0.8", + "version": "3.0.0", "engines": { "node": ">=6.12" }, @@ -42,10 +42,10 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0x/abi-gen-wrappers": "^2.0.2", - "@0x/contract-addresses": "^2.0.0", - "@0x/dev-utils": "^1.0.21", - "@0x/migrations": "^2.2.2", + "@0x/abi-gen-wrappers": "^2.1.0", + "@0x/contract-addresses": "^2.1.0", + "@0x/dev-utils": "^1.0.22", + "@0x/migrations": "^2.3.0", "@0x/tslint-config": "^2.0.0", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -72,16 +72,16 @@ "webpack": "^4.20.2" }, "dependencies": { - "@0x/assert": "^1.0.20", - "@0x/base-contract": "^3.0.10", - "@0x/contract-wrappers": "^4.1.3", - "@0x/order-utils": "^3.0.7", - "@0x/order-watcher": "^2.2.8", - "@0x/subproviders": "^2.1.8", - "@0x/types": "^1.4.1", + "@0x/assert": "^1.0.21", + "@0x/base-contract": "^3.0.11", + "@0x/contract-wrappers": "^4.2.0", + "@0x/order-utils": "^3.1.0", + "@0x/order-watcher": "^2.4.0", + "@0x/subproviders": "^2.1.9", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@types/web3-provider-engine": "^14.0.0", "ethereum-types": "^1.1.4", "ethers": "~4.0.4", diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 2df360b96..006e4cf29 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -4,6 +4,7 @@ export { assetDataUtils, signatureUtils, generatePseudoRandomSalt, orderHashUtil export { ContractWrappers, + DutchAuctionWrapper, ERC20TokenWrapper, ERC721TokenWrapper, EtherTokenWrapper, @@ -27,6 +28,7 @@ export { OrderAndTraderInfo, TraderInfo, ValidateOrderFillableOpts, + DutchAuctionData, } from '@0x/contract-wrappers'; export { @@ -80,11 +82,16 @@ export { OrderState, AssetProxyId, AssetData, + SingleAssetData, ERC20AssetData, ERC721AssetData, + MultiAssetData, + MultiAssetDataWithRecursiveDecoding, SignatureType, + ObjectMap, OrderRelevantState, Stats, + DutchAuctionDetails, } from '@0x/types'; export { diff --git a/packages/abi-gen-wrappers/CHANGELOG.json b/packages/abi-gen-wrappers/CHANGELOG.json index d46e828f4..404ba0f1b 100644 --- a/packages/abi-gen-wrappers/CHANGELOG.json +++ b/packages/abi-gen-wrappers/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "2.1.0", + "changes": [ + { + "note": "Added Dutch Auction Wrapper", + "pr": 1465 + } + ], + "timestamp": 1547040760 + }, + { "version": "2.0.2", "changes": [ { diff --git a/packages/abi-gen-wrappers/CHANGELOG.md b/packages/abi-gen-wrappers/CHANGELOG.md index c13c7a60f..4090e045a 100644 --- a/packages/abi-gen-wrappers/CHANGELOG.md +++ b/packages/abi-gen-wrappers/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.0 - _January 9, 2019_ + + * Added Dutch Auction Wrapper (#1465) + ## v2.0.2 - _December 13, 2018_ * Dependencies updated diff --git a/packages/abi-gen-wrappers/package.json b/packages/abi-gen-wrappers/package.json index 25ba3a6e0..f7d40d591 100644 --- a/packages/abi-gen-wrappers/package.json +++ b/packages/abi-gen-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0x/abi-gen-wrappers", - "version": "2.0.2", + "version": "2.1.0", "engines": { "node": ">=6.12" }, @@ -18,7 +18,7 @@ "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output src/generated-wrappers --backend ethers" }, "config": { - "abis": "../contract-artifacts/artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC20Token|ERC721Proxy|ERC721Token|Exchange|Forwarder|IValidator|IWallet|OrderValidator|WETH9|ZRXToken).json" + "abis": "../contract-artifacts/artifacts/@(AssetProxyOwner|DutchAuction|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC20Token|ERC721Proxy|ERC721Token|Exchange|Forwarder|IValidator|IWallet|OrderValidator|WETH9|ZRXToken).json" }, "repository": { "type": "git", @@ -30,19 +30,19 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen-wrappers/README.md", "devDependencies": { - "@0x/abi-gen": "^1.0.19", + "@0x/abi-gen": "^1.0.20", "@0x/abi-gen-templates": "^1.0.1", "@0x/tslint-config": "^2.0.0", - "@0x/types": "^1.4.1", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/types": "^1.5.0", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "ethereum-types": "^1.1.4", "ethers": "~4.0.4", "lodash": "^4.17.5", "shx": "^0.2.2" }, "dependencies": { - "@0x/base-contract": "^3.0.10" + "@0x/base-contract": "^3.0.11" }, "publishConfig": { "access": "public" diff --git a/packages/abi-gen-wrappers/src/generated-wrappers/dutch_auction.ts b/packages/abi-gen-wrappers/src/generated-wrappers/dutch_auction.ts new file mode 100644 index 000000000..90e233756 --- /dev/null +++ b/packages/abi-gen-wrappers/src/generated-wrappers/dutch_auction.ts @@ -0,0 +1,322 @@ +// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma whitespace class-name +// tslint:disable:no-unused-variable +// tslint:disable:no-unbound-method +import { BaseContract } from '@0x/base-contract'; +import { BlockParam, BlockParamLiteral, CallData, ContractAbi, ContractArtifact, DecodedLogArgs, MethodAbi, Provider, TxData, TxDataPayable } from 'ethereum-types'; +import { BigNumber, classUtils, logUtils } from '@0x/utils'; +import { SimpleContractArtifact } from '@0x/types'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as ethers from 'ethers'; +import * as _ from 'lodash'; +// tslint:enable:no-unused-variable + + +/* istanbul ignore next */ +// tslint:disable:no-parameter-reassignment +// tslint:disable-next-line:class-name +export class DutchAuctionContract extends BaseContract { + public getAuctionDetails = { + async sendTransactionAsync( + order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + txData: Partial<TxData> = {}, + ): Promise<string> { + const self = this as any as DutchAuctionContract; + const inputAbi = self._lookupAbi('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').inputs; + [order + ] = BaseContract._formatABIDataItemList(inputAbi, [order + ], BaseContract._bigNumberToString.bind(self)); + BaseContract.strictArgumentEncodingCheck(inputAbi, [order + ]); + const encodedData = self._lookupEthersInterface('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').functions.getAuctionDetails.encode([order + ]); + const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...txData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + self.getAuctionDetails.estimateGasAsync.bind( + self, + order + ), + ); + const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); + return txHash; + }, + async estimateGasAsync( + order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + txData: Partial<TxData> = {}, + ): Promise<number> { + const self = this as any as DutchAuctionContract; + const inputAbi = self._lookupAbi('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').inputs; + [order + ] = BaseContract._formatABIDataItemList(inputAbi, [order + ], BaseContract._bigNumberToString); + const encodedData = self._lookupEthersInterface('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').functions.getAuctionDetails.encode([order + ]); + const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...txData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + ); + const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults); + return gas; + }, + getABIEncodedTransactionData( + order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + ): string { + const self = this as any as DutchAuctionContract; + const inputAbi = self._lookupAbi('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').inputs; + [order + ] = BaseContract._formatABIDataItemList(inputAbi, [order + ], BaseContract._bigNumberToString); + const abiEncodedTransactionData = self._lookupEthersInterface('getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})').functions.getAuctionDetails.encode([order + ]); + return abiEncodedTransactionData; + }, + async callAsync( + order: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + callData: Partial<CallData> = {}, + defaultBlock?: BlockParam, + ): Promise<{beginTimeSeconds: BigNumber;endTimeSeconds: BigNumber;beginAmount: BigNumber;endAmount: BigNumber;currentAmount: BigNumber;currentTimeSeconds: BigNumber} + > { + const self = this as any as DutchAuctionContract; + const functionSignature = 'getAuctionDetails({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes})'; + const inputAbi = self._lookupAbi(functionSignature).inputs; + [order + ] = BaseContract._formatABIDataItemList(inputAbi, [order + ], BaseContract._bigNumberToString.bind(self)); + BaseContract.strictArgumentEncodingCheck(inputAbi, [order + ]); + const ethersFunction = self._lookupEthersInterface(functionSignature).functions.getAuctionDetails; + const encodedData = ethersFunction.encode([order + ]); + const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...callData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + ); + const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock); + BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); + let resultArray = ethersFunction.decode(rawCallResult); + const outputAbi = (_.find(self.abi, {name: 'getAuctionDetails'}) as MethodAbi).outputs; + resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._lowercaseAddress.bind(this)); + resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._bnToBigNumber.bind(this)); + return resultArray[0]; + }, + }; + public matchOrders = { + async sendTransactionAsync( + buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + buySignature: string, + sellSignature: string, + txData: Partial<TxData> = {}, + ): Promise<string> { + const self = this as any as DutchAuctionContract; + const inputAbi = self._lookupAbi('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').inputs; + [buyOrder, + sellOrder, + buySignature, + sellSignature + ] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder, + sellOrder, + buySignature, + sellSignature + ], BaseContract._bigNumberToString.bind(self)); + BaseContract.strictArgumentEncodingCheck(inputAbi, [buyOrder, + sellOrder, + buySignature, + sellSignature + ]); + const encodedData = self._lookupEthersInterface('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').functions.matchOrders.encode([buyOrder, + sellOrder, + buySignature, + sellSignature + ]); + const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...txData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + self.matchOrders.estimateGasAsync.bind( + self, + buyOrder, + sellOrder, + buySignature, + sellSignature + ), + ); + const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); + return txHash; + }, + async estimateGasAsync( + buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + buySignature: string, + sellSignature: string, + txData: Partial<TxData> = {}, + ): Promise<number> { + const self = this as any as DutchAuctionContract; + const inputAbi = self._lookupAbi('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').inputs; + [buyOrder, + sellOrder, + buySignature, + sellSignature + ] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder, + sellOrder, + buySignature, + sellSignature + ], BaseContract._bigNumberToString); + const encodedData = self._lookupEthersInterface('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').functions.matchOrders.encode([buyOrder, + sellOrder, + buySignature, + sellSignature + ]); + const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...txData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + ); + const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults); + return gas; + }, + getABIEncodedTransactionData( + buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + buySignature: string, + sellSignature: string, + ): string { + const self = this as any as DutchAuctionContract; + const inputAbi = self._lookupAbi('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').inputs; + [buyOrder, + sellOrder, + buySignature, + sellSignature + ] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder, + sellOrder, + buySignature, + sellSignature + ], BaseContract._bigNumberToString); + const abiEncodedTransactionData = self._lookupEthersInterface('matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)').functions.matchOrders.encode([buyOrder, + sellOrder, + buySignature, + sellSignature + ]); + return abiEncodedTransactionData; + }, + async callAsync( + buyOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + sellOrder: {makerAddress: string;takerAddress: string;feeRecipientAddress: string;senderAddress: string;makerAssetAmount: BigNumber;takerAssetAmount: BigNumber;makerFee: BigNumber;takerFee: BigNumber;expirationTimeSeconds: BigNumber;salt: BigNumber;makerAssetData: string;takerAssetData: string}, + buySignature: string, + sellSignature: string, + callData: Partial<CallData> = {}, + defaultBlock?: BlockParam, + ): Promise<{left: {makerAssetFilledAmount: BigNumber;takerAssetFilledAmount: BigNumber;makerFeePaid: BigNumber;takerFeePaid: BigNumber};right: {makerAssetFilledAmount: BigNumber;takerAssetFilledAmount: BigNumber;makerFeePaid: BigNumber;takerFeePaid: BigNumber};leftMakerAssetSpreadAmount: BigNumber} + > { + const self = this as any as DutchAuctionContract; + const functionSignature = 'matchOrders({address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},{address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes},bytes,bytes)'; + const inputAbi = self._lookupAbi(functionSignature).inputs; + [buyOrder, + sellOrder, + buySignature, + sellSignature + ] = BaseContract._formatABIDataItemList(inputAbi, [buyOrder, + sellOrder, + buySignature, + sellSignature + ], BaseContract._bigNumberToString.bind(self)); + BaseContract.strictArgumentEncodingCheck(inputAbi, [buyOrder, + sellOrder, + buySignature, + sellSignature + ]); + const ethersFunction = self._lookupEthersInterface(functionSignature).functions.matchOrders; + const encodedData = ethersFunction.encode([buyOrder, + sellOrder, + buySignature, + sellSignature + ]); + const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...callData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + ); + const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock); + BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); + let resultArray = ethersFunction.decode(rawCallResult); + const outputAbi = (_.find(self.abi, {name: 'matchOrders'}) as MethodAbi).outputs; + resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._lowercaseAddress.bind(this)); + resultArray = BaseContract._formatABIDataItemList(outputAbi, resultArray, BaseContract._bnToBigNumber.bind(this)); + return resultArray[0]; + }, + }; + public static async deployFrom0xArtifactAsync( + artifact: ContractArtifact | SimpleContractArtifact, + provider: Provider, + txDefaults: Partial<TxData>, + _exchange: string, + ): Promise<DutchAuctionContract> { + if (_.isUndefined(artifact.compilerOutput)) { + throw new Error('Compiler output not found in the artifact file'); + } + const bytecode = artifact.compilerOutput.evm.bytecode.object; + const abi = artifact.compilerOutput.abi; + return DutchAuctionContract.deployAsync(bytecode, abi, provider, txDefaults, _exchange +); + } + public static async deployAsync( + bytecode: string, + abi: ContractAbi, + provider: Provider, + txDefaults: Partial<TxData>, + _exchange: string, + ): Promise<DutchAuctionContract> { + const constructorAbi = BaseContract._lookupConstructorAbi(abi); + [_exchange +] = BaseContract._formatABIDataItemList( + constructorAbi.inputs, + [_exchange +], + BaseContract._bigNumberToString, + ); + const iface = new ethers.utils.Interface(abi); + const deployInfo = iface.deployFunction; + const txData = deployInfo.encode(bytecode, [_exchange +]); + const web3Wrapper = new Web3Wrapper(provider); + const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + {data: txData}, + txDefaults, + web3Wrapper.estimateGasAsync.bind(web3Wrapper), + ); + const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults); + logUtils.log(`transactionHash: ${txHash}`); + const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash); + logUtils.log(`DutchAuction successfully deployed at ${txReceipt.contractAddress}`); + const contractInstance = new DutchAuctionContract(abi, txReceipt.contractAddress as string, provider, txDefaults); + contractInstance.constructorArgs = [_exchange +]; + return contractInstance; + } + constructor(abi: ContractAbi, address: string, provider: Provider, txDefaults?: Partial<TxData>) { + super('DutchAuction', abi, address, provider, txDefaults); + classUtils.bindAll(this, ['_ethersInterfacesByFunctionSignature', 'address', 'abi', '_web3Wrapper']); + } +} // tslint:disable:max-file-line-count +// tslint:enable:no-unbound-method diff --git a/packages/abi-gen-wrappers/src/index.ts b/packages/abi-gen-wrappers/src/index.ts index de418214b..b5a7d0cfe 100644 --- a/packages/abi-gen-wrappers/src/index.ts +++ b/packages/abi-gen-wrappers/src/index.ts @@ -1,6 +1,7 @@ export * from './generated-wrappers/asset_proxy_owner'; export * from './generated-wrappers/dummy_erc20_token'; export * from './generated-wrappers/dummy_erc721_token'; +export * from './generated-wrappers/dutch_auction'; export * from './generated-wrappers/erc20_proxy'; export * from './generated-wrappers/erc20_token'; export * from './generated-wrappers/erc721_proxy'; diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 253fb124d..19cceb509 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "1.0.20", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "1.0.19", "changes": [ { diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index 4ca19ad7e..26d42cd36 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.20 - _January 9, 2019_ + + * Dependencies updated + ## v1.0.19 - _December 13, 2018_ * Dependencies updated diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index b122c742d..4a2754233 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -1,6 +1,6 @@ { "name": "@0x/abi-gen", - "version": "1.0.19", + "version": "1.0.20", "engines": { "node": ">=6.12" }, @@ -32,7 +32,7 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md", "dependencies": { "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", + "@0x/utils": "^2.1.1", "chalk": "^2.3.0", "ethereum-types": "^1.1.4", "glob": "^7.1.2", diff --git a/packages/assert/CHANGELOG.json b/packages/assert/CHANGELOG.json index 3805a044f..275ec2e4e 100644 --- a/packages/assert/CHANGELOG.json +++ b/packages/assert/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "1.0.21", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "1.0.20", "changes": [ { diff --git a/packages/assert/CHANGELOG.md b/packages/assert/CHANGELOG.md index ef84c48d4..25383b2d1 100644 --- a/packages/assert/CHANGELOG.md +++ b/packages/assert/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.21 - _January 9, 2019_ + + * Dependencies updated + ## v1.0.20 - _December 13, 2018_ * Dependencies updated diff --git a/packages/assert/package.json b/packages/assert/package.json index cec1748cd..e11ccb3cb 100644 --- a/packages/assert/package.json +++ b/packages/assert/package.json @@ -1,6 +1,6 @@ { "name": "@0x/assert", - "version": "1.0.20", + "version": "1.0.21", "engines": { "node": ">=6.12" }, @@ -44,9 +44,9 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/json-schemas": "^2.1.4", + "@0x/json-schemas": "^2.1.5", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", + "@0x/utils": "^2.1.1", "lodash": "^4.17.5", "valid-url": "^1.0.9" }, diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 470d9b03b..a7f8d3418 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,5 +1,23 @@ [ { + "version": "4.0.0", + "changes": [ + { + "note": "Raise custom InsufficientAssetLiquidityError error with amountAvailableToFill attribute", + "pr": 1437 + } + ] + }, + { + "timestamp": 1547040760, + "version": "3.0.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "3.0.4", "changes": [ { diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md index cadb1acf8..70bbe8302 100644 --- a/packages/asset-buyer/CHANGELOG.md +++ b/packages/asset-buyer/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.5 - _January 9, 2019_ + + * Dependencies updated + ## v3.0.4 - _December 13, 2018_ * Dependencies updated diff --git a/packages/asset-buyer/README.md b/packages/asset-buyer/README.md index 383a3836a..b854bda11 100644 --- a/packages/asset-buyer/README.md +++ b/packages/asset-buyer/README.md @@ -1,7 +1,5 @@ ## @0x/asset-buyer -**Warning: In Beta, has not been extensively tested.** - Convenience package for buying assets represented on the Ethereum blockchain using 0x. In its simplest form, the package helps in the usage of the [0x forwarder contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md), which allows users to execute [Wrapped Ether](https://weth.io/) based 0x orders without having to set allowances, wrap Ether or own ZRX, meaning they can buy tokens with Ether alone. Given some liquidity (0x signed orders), it helps estimate the Ether cost of buying a certain asset (giving a range) and then buying that asset. In its more advanced and useful form, it integrates with the [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) and takes care of sourcing liquidity for you given an SRA compliant endpoint. The final result is a library that tells you what assets are available, provides an Ether based quote for any asset desired, and allows you to buy that asset using Ether alone. diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index 401aec120..dd6780e48 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -1,6 +1,6 @@ { "name": "@0x/asset-buyer", - "version": "3.0.4", + "version": "3.0.5", "engines": { "node": ">=6.12" }, @@ -36,16 +36,16 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md", "dependencies": { - "@0x/assert": "^1.0.20", - "@0x/connect": "^3.0.10", - "@0x/contract-wrappers": "^4.1.3", - "@0x/json-schemas": "^2.1.4", - "@0x/order-utils": "^3.0.7", - "@0x/subproviders": "^2.1.8", - "@0x/types": "^1.4.1", + "@0x/assert": "^1.0.21", + "@0x/connect": "^3.0.11", + "@0x/contract-wrappers": "^4.2.0", + "@0x/json-schemas": "^2.1.5", + "@0x/order-utils": "^3.1.0", + "@0x/subproviders": "^2.1.9", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "ethereum-types": "^1.1.4", "lodash": "^4.17.5" }, diff --git a/packages/asset-buyer/src/errors.ts b/packages/asset-buyer/src/errors.ts new file mode 100644 index 000000000..ec5fe548c --- /dev/null +++ b/packages/asset-buyer/src/errors.ts @@ -0,0 +1,22 @@ +import { BigNumber } from '@0x/utils'; + +import { AssetBuyerError } from './types'; + +/** + * Error class representing insufficient asset liquidity + */ +export class InsufficientAssetLiquidityError extends Error { + /** + * The amount availabe to fill (in base units) factoring in slippage. + */ + public amountAvailableToFill: BigNumber; + /** + * @param amountAvailableToFill The amount availabe to fill (in base units) factoring in slippage + */ + constructor(amountAvailableToFill: BigNumber) { + super(AssetBuyerError.InsufficientAssetLiquidity); + this.amountAvailableToFill = amountAvailableToFill; + // Setting prototype so instanceof works. See https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, InsufficientAssetLiquidityError.prototype); + } +} diff --git a/packages/asset-buyer/src/index.ts b/packages/asset-buyer/src/index.ts index 8418edb42..a42d7e272 100644 --- a/packages/asset-buyer/src/index.ts +++ b/packages/asset-buyer/src/index.ts @@ -9,6 +9,7 @@ export { SignedOrder } from '@0x/types'; export { BigNumber } from '@0x/utils'; export { AssetBuyer } from './asset_buyer'; +export { InsufficientAssetLiquidityError } from './errors'; export { BasicOrderProvider } from './order_providers/basic_order_provider'; export { StandardRelayerAPIOrderProvider } from './order_providers/standard_relayer_api_order_provider'; export { diff --git a/packages/asset-buyer/src/types.ts b/packages/asset-buyer/src/types.ts index 3b573edca..d5d6be695 100644 --- a/packages/asset-buyer/src/types.ts +++ b/packages/asset-buyer/src/types.ts @@ -102,7 +102,7 @@ export interface AssetBuyerOpts { } /** - * Possible errors thrown by an AssetBuyer instance or associated static methods. + * Possible error messages thrown by an AssetBuyer instance or associated static methods. */ export enum AssetBuyerError { NoEtherTokenContractFound = 'NO_ETHER_TOKEN_CONTRACT_FOUND', diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index b15b880c2..fcded6ab1 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -3,6 +3,7 @@ import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; import { constants } from '../constants'; +import { InsufficientAssetLiquidityError } from '../errors'; import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types'; import { orderUtils } from './order_utils'; @@ -33,7 +34,18 @@ export const buyQuoteCalculator = { }); // if we do not have enough orders to cover the desired assetBuyAmount, throw if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { - throw new Error(AssetBuyerError.InsufficientAssetLiquidity); + // We needed the amount they requested to buy, plus the amount for slippage + const totalAmountRequested = assetBuyAmount.plus(slippageBufferAmount); + const amountAbleToFill = totalAmountRequested.minus(remainingFillAmount); + // multiplierNeededWithSlippage represents what we need to multiply the assetBuyAmount by + // in order to get the total amount needed considering slippage + // i.e. if slippagePercent was 0.2 (20%), multiplierNeededWithSlippage would be 1.2 + const multiplierNeededWithSlippage = new BigNumber(1).plus(slippagePercentage); + // Given amountAvailableToFillConsideringSlippage * multiplierNeededWithSlippage = amountAbleToFill + // We divide amountUnableToFill by multiplierNeededWithSlippage to determine amountAvailableToFillConsideringSlippage + const amountAvailableToFillConsideringSlippage = amountAbleToFill.div(multiplierNeededWithSlippage).floor(); + + throw new InsufficientAssetLiquidityError(amountAvailableToFillConsideringSlippage); } // if we are not buying ZRX: // given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage) diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts index a30017b72..fdc17ef25 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts @@ -1,4 +1,5 @@ import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; +import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; @@ -8,6 +9,7 @@ import { AssetBuyerError, OrdersAndFillableAmounts } from '../src/types'; import { buyQuoteCalculator } from '../src/utils/buy_quote_calculator'; import { chaiSetup } from './utils/chai_setup'; +import { testHelpers } from './utils/test_helpers'; chaiSetup.configure(); const expect = chai.expect; @@ -15,6 +17,10 @@ const expect = chai.expect; // tslint:disable:custom-no-magic-numbers describe('buyQuoteCalculator', () => { describe('#calculate', () => { + let firstOrder: SignedOrder; + let firstRemainingFillAmount: BigNumber; + let secondOrder: SignedOrder; + let secondRemainingFillAmount: BigNumber; let ordersAndFillableAmounts: OrdersAndFillableAmounts; let smallFeeOrderAndFillableAmount: OrdersAndFillableAmounts; let allFeeOrdersAndFillableAmounts: OrdersAndFillableAmounts; @@ -24,18 +30,18 @@ describe('buyQuoteCalculator', () => { // the second order has a rate of 2 makerAsset / WETH with a takerFee of 100 ZRX and has 200 / 200 makerAsset units left to fill (completely fillable) // generate one order for fees // the fee order has a rate of 1 ZRX / WETH with no taker fee and has 100 ZRX left to fill (completely fillable) - const firstOrder = orderFactory.createSignedOrderFromPartial({ + firstOrder = orderFactory.createSignedOrderFromPartial({ makerAssetAmount: new BigNumber(400), takerAssetAmount: new BigNumber(100), takerFee: new BigNumber(200), }); - const firstRemainingFillAmount = new BigNumber(200); - const secondOrder = orderFactory.createSignedOrderFromPartial({ + firstRemainingFillAmount = new BigNumber(200); + secondOrder = orderFactory.createSignedOrderFromPartial({ makerAssetAmount: new BigNumber(200), takerAssetAmount: new BigNumber(100), takerFee: new BigNumber(100), }); - const secondRemainingFillAmount = secondOrder.makerAssetAmount; + secondRemainingFillAmount = secondOrder.makerAssetAmount; ordersAndFillableAmounts = { orders: [firstOrder, secondOrder], remainingFillableMakerAssetAmounts: [firstRemainingFillAmount, secondRemainingFillAmount], @@ -61,18 +67,137 @@ describe('buyQuoteCalculator', () => { ], }; }); - it('should throw if not enough maker asset liquidity', () => { - // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units + describe('InsufficientLiquidityError', () => { + it('should throw if not enough maker asset liquidity (multiple orders)', () => { + // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units + const errorFunction = () => { + buyQuoteCalculator.calculate( + ordersAndFillableAmounts, + smallFeeOrderAndFillableAmount, + new BigNumber(500), + 0, + 0, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(400)); + }); + it('should throw if not enough maker asset liquidity (multiple orders with 20% slippage)', () => { + // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units + const errorFunction = () => { + buyQuoteCalculator.calculate( + ordersAndFillableAmounts, + smallFeeOrderAndFillableAmount, + new BigNumber(500), + 0, + 0.2, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(333)); + }); + it('should throw if not enough maker asset liquidity (multiple orders with 5% slippage)', () => { + // we have 400 makerAsset units available to fill but attempt to calculate a quote for 500 makerAsset units + const errorFunction = () => { + buyQuoteCalculator.calculate( + ordersAndFillableAmounts, + smallFeeOrderAndFillableAmount, + new BigNumber(600), + 0, + 0.05, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(380)); + }); + it('should throw if not enough maker asset liquidity (partially filled order)', () => { + const firstOrderAndFillableAmount: OrdersAndFillableAmounts = { + orders: [firstOrder], + remainingFillableMakerAssetAmounts: [firstRemainingFillAmount], + }; + + const errorFunction = () => { + buyQuoteCalculator.calculate( + firstOrderAndFillableAmount, + smallFeeOrderAndFillableAmount, + new BigNumber(201), + 0, + 0, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(200)); + }); + it('should throw if not enough maker asset liquidity (completely fillable order)', () => { + const completelyFillableOrder = orderFactory.createSignedOrderFromPartial({ + makerAssetAmount: new BigNumber(123), + takerAssetAmount: new BigNumber(100), + takerFee: new BigNumber(200), + }); + const completelyFillableOrdersAndFillableAmount: OrdersAndFillableAmounts = { + orders: [completelyFillableOrder], + remainingFillableMakerAssetAmounts: [completelyFillableOrder.makerAssetAmount], + }; + const errorFunction = () => { + buyQuoteCalculator.calculate( + completelyFillableOrdersAndFillableAmount, + smallFeeOrderAndFillableAmount, + new BigNumber(124), + 0, + 0, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(123)); + }); + it('should throw with 1 amount available if no slippage', () => { + const smallOrder = orderFactory.createSignedOrderFromPartial({ + makerAssetAmount: new BigNumber(1), + takerAssetAmount: new BigNumber(1), + takerFee: new BigNumber(0), + }); + const errorFunction = () => { + buyQuoteCalculator.calculate( + { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, + smallFeeOrderAndFillableAmount, + new BigNumber(600), + 0, + 0, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, new BigNumber(1)); + }); + it('should throw without amount available to fill if amount rounds to 0', () => { + const smallOrder = orderFactory.createSignedOrderFromPartial({ + makerAssetAmount: new BigNumber(1), + takerAssetAmount: new BigNumber(1), + takerFee: new BigNumber(0), + }); + const errorFunction = () => { + buyQuoteCalculator.calculate( + { orders: [smallOrder], remainingFillableMakerAssetAmounts: [smallOrder.makerAssetAmount] }, + smallFeeOrderAndFillableAmount, + new BigNumber(600), + 0, + 0.2, + false, + ); + }; + testHelpers.expectInsufficientLiquidityError(expect, errorFunction, undefined); + }); + }); + it('should not throw if order is fillable', () => { expect(() => buyQuoteCalculator.calculate( ordersAndFillableAmounts, - smallFeeOrderAndFillableAmount, - new BigNumber(500), + allFeeOrdersAndFillableAmounts, + new BigNumber(300), 0, 0, false, ), - ).to.throw(AssetBuyerError.InsufficientAssetLiquidity); + ).to.not.throw(); }); it('should throw if not enough ZRX liquidity', () => { // we request 300 makerAsset units but the ZRX order is only enough to fill the first order, which only has 200 makerAssetUnits available diff --git a/packages/asset-buyer/test/utils/test_helpers.ts b/packages/asset-buyer/test/utils/test_helpers.ts new file mode 100644 index 000000000..9c7c244af --- /dev/null +++ b/packages/asset-buyer/test/utils/test_helpers.ts @@ -0,0 +1,26 @@ +import { BigNumber } from '@0x/utils'; + +import { InsufficientAssetLiquidityError } from '../../src/errors'; + +export const testHelpers = { + expectInsufficientLiquidityError: ( + expect: Chai.ExpectStatic, + functionWhichTriggersError: () => void, + expectedAmountAvailableToFill?: BigNumber, + ): void => { + let wasErrorThrown = false; + try { + functionWhichTriggersError(); + } catch (e) { + wasErrorThrown = true; + expect(e).to.be.instanceOf(InsufficientAssetLiquidityError); + if (expectedAmountAvailableToFill) { + expect(e.amountAvailableToFill).to.be.bignumber.equal(expectedAmountAvailableToFill); + } else { + expect(e.amountAvailableToFill).to.be.undefined(); + } + } + + expect(wasErrorThrown).to.be.true(); + }, +}; diff --git a/packages/base-contract/CHANGELOG.json b/packages/base-contract/CHANGELOG.json index a4cd17d42..1a6e3f845 100644 --- a/packages/base-contract/CHANGELOG.json +++ b/packages/base-contract/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "3.0.11", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "3.0.10", "changes": [ { diff --git a/packages/base-contract/CHANGELOG.md b/packages/base-contract/CHANGELOG.md index a8ce346d7..7a1a96639 100644 --- a/packages/base-contract/CHANGELOG.md +++ b/packages/base-contract/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.11 - _January 9, 2019_ + + * Dependencies updated + ## v3.0.10 - _December 13, 2018_ * Dependencies updated diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index 87d70dd5f..7193c4881 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -1,6 +1,6 @@ { "name": "@0x/base-contract", - "version": "3.0.10", + "version": "3.0.11", "engines": { "node": ">=6.12" }, @@ -41,8 +41,8 @@ }, "dependencies": { "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "ethereum-types": "^1.1.4", "ethers": "~4.0.4", "lodash": "^4.17.5" diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index e5bde40ae..fe32990e2 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "3.0.11", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "3.0.10", "changes": [ { diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md index a5f95b988..d01caa613 100644 --- a/packages/connect/CHANGELOG.md +++ b/packages/connect/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.11 - _January 9, 2019_ + + * Dependencies updated + ## v3.0.10 - _December 13, 2018_ * Dependencies updated diff --git a/packages/connect/package.json b/packages/connect/package.json index 4985d0410..76817f23e 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -1,6 +1,6 @@ { "name": "@0x/connect", - "version": "3.0.10", + "version": "3.0.11", "engines": { "node": ">=6.12" }, @@ -44,12 +44,12 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md", "dependencies": { - "@0x/assert": "^1.0.20", - "@0x/json-schemas": "^2.1.4", - "@0x/order-utils": "^3.0.7", - "@0x/types": "^1.4.1", + "@0x/assert": "^1.0.21", + "@0x/json-schemas": "^2.1.5", + "@0x/order-utils": "^3.1.0", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", + "@0x/utils": "^2.1.1", "lodash": "^4.17.5", "query-string": "^5.0.1", "sinon": "^4.0.0", diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 21ffaf510..36684d443 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "2.1.0", + "changes": [ + { + "note": "Added testnet entries for Dutch Auction contract (kovan,rinkeby,ropsten)", + "pr": 1465 + } + ], + "timestamp": 1547040760 + }, + { "version": "2.0.0", "changes": [ { diff --git a/packages/contract-addresses/CHANGELOG.md b/packages/contract-addresses/CHANGELOG.md index c006c3b22..d7f2ff7ea 100644 --- a/packages/contract-addresses/CHANGELOG.md +++ b/packages/contract-addresses/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.0 - _January 9, 2019_ + + * Added testnet entries for Dutch Auction contract (kovan,rinkeby,ropsten) (#1465) + ## v2.0.0 - _November 28, 2018_ * Redeployed Rinkeby with testnet Exchange artifact (#1318) diff --git a/packages/contract-addresses/package.json b/packages/contract-addresses/package.json index c75ae6efa..c5a133ddf 100644 --- a/packages/contract-addresses/package.json +++ b/packages/contract-addresses/package.json @@ -1,6 +1,6 @@ { "name": "@0x/contract-addresses", - "version": "2.0.0", + "version": "2.1.0", "engines": { "node": ">=6.12" }, diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 7989631e3..d181a1bec 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -9,6 +9,7 @@ export interface ContractAddresses { assetProxyOwner: string; forwarder: string; orderValidator: string; + dutchAuction: string; } export enum NetworkId { @@ -19,6 +20,8 @@ export enum NetworkId { Ganache = 50, } +const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; + const networkToAddresses: { [networkId: number]: ContractAddresses } = { 1: { erc20Proxy: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e', @@ -29,6 +32,8 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1', orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138', + // @todo hysz/dekz: Add mainnet address once deployed. + dutchAuction: NULL_ADDRESS, }, 3: { erc20Proxy: '0xb1408f4c245a23c31b98d2c626777d4c0d766caa', @@ -39,6 +44,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { assetProxyOwner: '0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b', forwarder: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e', orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f', + dutchAuction: '0x2df6b59309f35ada230ec7d61d7d97355017a1df', }, 4: { exchange: '0xbce0b5f6eb618c565c3e5f5cd69652bbc279f44e', @@ -49,6 +55,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { assetProxyOwner: '0xe1703da878afcebff5b7624a826902af475b9c03', forwarder: '0x2d40589abbdee84961f3a7656b9af7adb0ee5ab4', orderValidator: '0x0c5173a51e26b29d6126c686756fb9fbef71f762', + dutchAuction: '0xdd7bd6437e67c422879364740ab5855fe3dc41f7', }, 42: { erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e', @@ -59,6 +66,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { assetProxyOwner: '0x2c824d2882baa668e0d5202b1e7f2922278703f8', forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d', + dutchAuction: '0xe11667fb51f34c5367f40d7e379327ce32ee7150', }, // NetworkId 50 represents our Ganache snapshot generated from migrations. 50: { @@ -70,6 +78,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { assetProxyOwner: '0x34d402f14d58e001d8efbe6585051bf9706aa064', forwarder: '0xb69e673309512a9d726f87304c6984054f87a93b', orderValidator: '0xe86bb98fcf9bff3512c74589b78fb168200cc546', + dutchAuction: '0xdc688d29394a3f1e6f1e5100862776691afaf3d2', }, }; diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json index 03c88e71a..237014d09 100644 --- a/packages/contract-artifacts/CHANGELOG.json +++ b/packages/contract-artifacts/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "1.2.0", + "changes": [ + { + "pr": 1465, + "note": "Added artifact for Dutch Auction contract" + } + ], + "timestamp": 1547040760 + }, + { "version": "1.1.2", "changes": [ { diff --git a/packages/contract-artifacts/CHANGELOG.md b/packages/contract-artifacts/CHANGELOG.md index 9e48058f5..7f96cb571 100644 --- a/packages/contract-artifacts/CHANGELOG.md +++ b/packages/contract-artifacts/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.2.0 - _January 9, 2019_ + + * Added artifact for Dutch Auction contract (#1465) + ## v1.1.2 - _November 28, 2018_ * Update Exchange artifact to receive ZRX asset data as a constructor argument (#1309) diff --git a/packages/contract-artifacts/artifacts/DutchAuction.json b/packages/contract-artifacts/artifacts/DutchAuction.json new file mode 100644 index 000000000..3c1cb8c4c --- /dev/null +++ b/packages/contract-artifacts/artifacts/DutchAuction.json @@ -0,0 +1,310 @@ +{ + "schemaVersion": "2.0.0", + "contractName": "DutchAuction", + "compilerOutput": { + "abi": [ + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + } + ], + "name": "getAuctionDetails", + "outputs": [ + { + "components": [ + { + "name": "beginTimeSeconds", + "type": "uint256" + }, + { + "name": "endTimeSeconds", + "type": "uint256" + }, + { + "name": "beginAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "currentAmount", + "type": "uint256" + }, + { + "name": "currentTimeSeconds", + "type": "uint256" + } + ], + "name": "auctionDetails", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "buyOrder", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "sellOrder", + "type": "tuple" + }, + { + "name": "buySignature", + "type": "bytes" + }, + { + "name": "sellSignature", + "type": "bytes" + } + ], + "name": "matchOrders", + "outputs": [ + { + "components": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "left", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "right", + "type": "tuple" + }, + { + "name": "leftMakerAssetSpreadAmount", + "type": "uint256" + } + ], + "name": "matchedFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_exchange", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + } + ], + "evm": { + "bytecode": { + "linkReferences": {}, + "object": "0x608060405234801561001057600080fd5b50604051602080611352833981018060405261002f9190810190610067565b60008054600160a060020a031916600160a060020a0392909216919091179055610099565b6000610060825161008d565b9392505050565b60006020828403121561007957600080fd5b60006100858484610054565b949350505050565b600160a060020a031690565b6112aa806100a86000396000f30060806040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632e9cd03381146100505780633c28d86114610086575b600080fd5b34801561005c57600080fd5b5061007061006b366004610b25565b6100b3565b60405161007d919061110a565b60405180910390f35b34801561009257600080fd5b506100a66100a1366004610b5a565b61029c565b60405161007d9190611118565b6100bb6107b1565b610140820151516000808080808080606488101561010e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110aa565b60405180910390fd5b6101408a0151610146907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a0163ffffffff6105f216565b6101408b0151909750610181907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a0163ffffffff6105f216565b9550868a61010001511115156101c3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110ba565b868a61010001510394508960a001519350838611151561020f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110fa565b50505083865261010087018051602088015260408701849052606087018290524260a088018190529051828503919081900386821015610255576080890186905261028f565b6101008a0151821061026d576080890184905261028f565b6102898461028461027e8487610607565b8861066d565b610684565b60808a01525b5050505050505050919050565b6102a46107e8565b6102ac6107b1565b6000606060008060006102be8a6100b3565b805160a08201519197501115610300576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110ea565b60a08601516101008b015111610342576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110ca565b608080870151908c01511015610384576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110fa565b6000546040517f3c28d86100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690633c28d861906103e0908e908e908e908e90600401611127565b61012060405180830381600087803b1580156103fb57600080fd5b505af115801561040f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104339190810190610b06565b96508660400151945060008511156105e4576101608a0151935061045e84601063ffffffff6106c316565b92506104728b608001518760800151610724565b915061047e8583610724565b905060008111156105325789516040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163a9059cbb916104de9190859060040161105f565b602060405180830381600087803b1580156104f857600080fd5b505af115801561050c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105309190810190610ae0565b505b60008211156105e4578a516040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163a9059cbb916105909190869060040161105f565b602060405180830381600087803b1580156105aa57600080fd5b505af11580156105be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105e29190810190610ae0565b505b505050505050949350505050565b60006105fe8383610766565b90505b92915050565b60008083151561061a5760009150610666565b5082820282848281151561062a57fe5b0414610662576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061109a565b8091505b5092915050565b600080828481151561067b57fe5b04949350505050565b600082820183811015610662576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061109a565b600081601401835110151515610705576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110da565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600082821115610760576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061107a565b50900390565b6000816020018351101515156107a8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061108a565b50016020015190565b60c0604051908101604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b610120604051908101604052806107fd610817565b815260200161080a610817565b8152602001600081525090565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b600061084c82356111ef565b9392505050565b600061084c825161120b565b6000601f8201831361087057600080fd5b813561088361087e826111a5565b61117e565b9150808252602083016020830185838301111561089f57600080fd5b6108aa838284611210565b50505092915050565b6000608082840312156108c557600080fd5b6108cf608061117e565b905060006108dd8484610ad4565b82525060206108ee84848301610ad4565b602083015250604061090284828501610ad4565b604083015250606061091684828501610ad4565b60608301525092915050565b6000610120828403121561093557600080fd5b61093f606061117e565b9050600061094d84846108b3565b825250608061095e848483016108b3565b60208301525061010061097384828501610ad4565b60408301525092915050565b6000610180828403121561099257600080fd5b61099d61018061117e565b905060006109ab8484610840565b82525060206109bc84848301610840565b60208301525060406109d084828501610840565b60408301525060606109e484828501610840565b60608301525060806109f884828501610ac8565b60808301525060a0610a0c84828501610ac8565b60a08301525060c0610a2084828501610ac8565b60c08301525060e0610a3484828501610ac8565b60e083015250610100610a4984828501610ac8565b61010083015250610120610a5f84828501610ac8565b6101208301525061014082013567ffffffffffffffff811115610a8157600080fd5b610a8d8482850161085f565b6101408301525061016082013567ffffffffffffffff811115610aaf57600080fd5b610abb8482850161085f565b6101608301525092915050565b600061084c8235611208565b600061084c8251611208565b600060208284031215610af257600080fd5b6000610afe8484610853565b949350505050565b60006101208284031215610b1957600080fd5b6000610afe8484610922565b600060208284031215610b3757600080fd5b813567ffffffffffffffff811115610b4e57600080fd5b610afe8482850161097f565b60008060008060808587031215610b7057600080fd5b843567ffffffffffffffff811115610b8757600080fd5b610b938782880161097f565b945050602085013567ffffffffffffffff811115610bb057600080fd5b610bbc8782880161097f565b935050604085013567ffffffffffffffff811115610bd957600080fd5b610be58782880161085f565b925050606085013567ffffffffffffffff811115610c0257600080fd5b610c0e8782880161085f565b91505092959194509250565b610c23816111ef565b82525050565b6000610c34826111eb565b808452610c4881602086016020860161121c565b610c5181611248565b9093016020019392505050565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b601281527f494e56414c49445f41535345545f444154410000000000000000000000000000602082015260400190565b601281527f494e56414c49445f424547494e5f54494d450000000000000000000000000000602082015260400190565b600f81527f41554354494f4e5f455850495245440000000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601381527f41554354494f4e5f4e4f545f5354415254454400000000000000000000000000602082015260400190565b600e81527f494e56414c49445f414d4f554e54000000000000000000000000000000000000602082015260400190565b805160c0830190610e6b8482611056565b506020820151610e7e6020850182611056565b506040820151610e916040850182611056565b506060820151610ea46060850182611056565b506080820151610eb76080850182611056565b5060a0820151610eca60a0850182611056565b50505050565b80516080830190610ee18482611056565b506020820151610ef46020850182611056565b506040820151610f076040850182611056565b506060820151610eca6060850182611056565b8051610120830190610f2c8482610ed0565b506020820151610f3f6080850182610ed0565b506040820151610eca610100850182611056565b8051600090610180840190610f688582610c1a565b506020830151610f7b6020860182610c1a565b506040830151610f8e6040860182610c1a565b506060830151610fa16060860182610c1a565b506080830151610fb46080860182611056565b5060a0830151610fc760a0860182611056565b5060c0830151610fda60c0860182611056565b5060e0830151610fed60e0860182611056565b50610100830151611002610100860182611056565b50610120830151611017610120860182611056565b506101408301518482036101408601526110318282610c29565b91505061016083015184820361016086015261104d8282610c29565b95945050505050565b610c2381611208565b6040810161106d8285610c1a565b61084c6020830184611056565b6020808252810161060181610c5e565b6020808252810161060181610c8e565b6020808252810161060181610ce4565b6020808252810161060181610d14565b6020808252810161060181610d44565b6020808252810161060181610d74565b6020808252810161060181610da4565b6020808252810161060181610dfa565b6020808252810161060181610e2a565b60c081016106018284610e5a565b61012081016106018284610f1a565b608080825281016111388187610f53565b9050818103602083015261114c8186610f53565b905081810360408301526111608185610c29565b905081810360608301526111748184610c29565b9695505050505050565b60405181810167ffffffffffffffff8111828210171561119d57600080fd5b604052919050565b600067ffffffffffffffff8211156111bc57600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b151590565b82818337506000910152565b60005b8381101561123757818101518382015260200161121f565b83811115610eca5750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a723058209cf70b04b75fc23e6762ae4c6c8f25ccad25dfcb39cf1dd3966eb27ce513de036c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH1 0x20 DUP1 PUSH2 0x1352 DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE PUSH2 0x2F SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x67 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP3 SWAP1 SWAP3 AND SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE PUSH2 0x99 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x60 DUP3 MLOAD PUSH2 0x8D JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x79 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x85 DUP5 DUP5 PUSH2 0x54 JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST PUSH2 0x12AA DUP1 PUSH2 0xA8 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x4B JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x2E9CD033 DUP2 EQ PUSH2 0x50 JUMPI DUP1 PUSH4 0x3C28D861 EQ PUSH2 0x86 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x70 PUSH2 0x6B CALLDATASIZE PUSH1 0x4 PUSH2 0xB25 JUMP JUMPDEST PUSH2 0xB3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x7D SWAP2 SWAP1 PUSH2 0x110A JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x92 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xA6 PUSH2 0xA1 CALLDATASIZE PUSH1 0x4 PUSH2 0xB5A JUMP JUMPDEST PUSH2 0x29C JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x7D SWAP2 SWAP1 PUSH2 0x1118 JUMP JUMPDEST PUSH2 0xBB PUSH2 0x7B1 JUMP JUMPDEST PUSH2 0x140 DUP3 ADD MLOAD MLOAD PUSH1 0x0 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 PUSH1 0x64 DUP9 LT ISZERO PUSH2 0x10E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10AA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x140 DUP11 ADD MLOAD PUSH2 0x146 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0 DUP11 ADD PUSH4 0xFFFFFFFF PUSH2 0x5F2 AND JUMP JUMPDEST PUSH2 0x140 DUP12 ADD MLOAD SWAP1 SWAP8 POP PUSH2 0x181 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP11 ADD PUSH4 0xFFFFFFFF PUSH2 0x5F2 AND JUMP JUMPDEST SWAP6 POP DUP7 DUP11 PUSH2 0x100 ADD MLOAD GT ISZERO ISZERO PUSH2 0x1C3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10BA JUMP JUMPDEST DUP7 DUP11 PUSH2 0x100 ADD MLOAD SUB SWAP5 POP DUP10 PUSH1 0xA0 ADD MLOAD SWAP4 POP DUP4 DUP7 GT ISZERO ISZERO PUSH2 0x20F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10FA JUMP JUMPDEST POP POP POP DUP4 DUP7 MSTORE PUSH2 0x100 DUP8 ADD DUP1 MLOAD PUSH1 0x20 DUP9 ADD MSTORE PUSH1 0x40 DUP8 ADD DUP5 SWAP1 MSTORE PUSH1 0x60 DUP8 ADD DUP3 SWAP1 MSTORE TIMESTAMP PUSH1 0xA0 DUP9 ADD DUP2 SWAP1 MSTORE SWAP1 MLOAD DUP3 DUP6 SUB SWAP2 SWAP1 DUP2 SWAP1 SUB DUP7 DUP3 LT ISZERO PUSH2 0x255 JUMPI PUSH1 0x80 DUP10 ADD DUP7 SWAP1 MSTORE PUSH2 0x28F JUMP JUMPDEST PUSH2 0x100 DUP11 ADD MLOAD DUP3 LT PUSH2 0x26D JUMPI PUSH1 0x80 DUP10 ADD DUP5 SWAP1 MSTORE PUSH2 0x28F JUMP JUMPDEST PUSH2 0x289 DUP5 PUSH2 0x284 PUSH2 0x27E DUP5 DUP8 PUSH2 0x607 JUMP JUMPDEST DUP9 PUSH2 0x66D JUMP JUMPDEST PUSH2 0x684 JUMP JUMPDEST PUSH1 0x80 DUP11 ADD MSTORE JUMPDEST POP POP POP POP POP POP POP POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0x2A4 PUSH2 0x7E8 JUMP JUMPDEST PUSH2 0x2AC PUSH2 0x7B1 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x2BE DUP11 PUSH2 0xB3 JUMP JUMPDEST DUP1 MLOAD PUSH1 0xA0 DUP3 ADD MLOAD SWAP2 SWAP8 POP GT ISZERO PUSH2 0x300 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10EA JUMP JUMPDEST PUSH1 0xA0 DUP7 ADD MLOAD PUSH2 0x100 DUP12 ADD MLOAD GT PUSH2 0x342 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10CA JUMP JUMPDEST PUSH1 0x80 DUP1 DUP8 ADD MLOAD SWAP1 DUP13 ADD MLOAD LT ISZERO PUSH2 0x384 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10FA JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x3C28D86100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0x3C28D861 SWAP1 PUSH2 0x3E0 SWAP1 DUP15 SWAP1 DUP15 SWAP1 DUP15 SWAP1 DUP15 SWAP1 PUSH1 0x4 ADD PUSH2 0x1127 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x3FB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x40F JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x433 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xB06 JUMP JUMPDEST SWAP7 POP DUP7 PUSH1 0x40 ADD MLOAD SWAP5 POP PUSH1 0x0 DUP6 GT ISZERO PUSH2 0x5E4 JUMPI PUSH2 0x160 DUP11 ADD MLOAD SWAP4 POP PUSH2 0x45E DUP5 PUSH1 0x10 PUSH4 0xFFFFFFFF PUSH2 0x6C3 AND JUMP JUMPDEST SWAP3 POP PUSH2 0x472 DUP12 PUSH1 0x80 ADD MLOAD DUP8 PUSH1 0x80 ADD MLOAD PUSH2 0x724 JUMP JUMPDEST SWAP2 POP PUSH2 0x47E DUP6 DUP4 PUSH2 0x724 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 DUP2 GT ISZERO PUSH2 0x532 JUMPI DUP10 MLOAD PUSH1 0x40 MLOAD PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0xA9059CBB SWAP2 PUSH2 0x4DE SWAP2 SWAP1 DUP6 SWAP1 PUSH1 0x4 ADD PUSH2 0x105F JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x4F8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x50C JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x530 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xAE0 JUMP JUMPDEST POP JUMPDEST PUSH1 0x0 DUP3 GT ISZERO PUSH2 0x5E4 JUMPI DUP11 MLOAD PUSH1 0x40 MLOAD PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0xA9059CBB SWAP2 PUSH2 0x590 SWAP2 SWAP1 DUP7 SWAP1 PUSH1 0x4 ADD PUSH2 0x105F JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x5AA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x5BE JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x5E2 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xAE0 JUMP JUMPDEST POP JUMPDEST POP POP POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x5FE DUP4 DUP4 PUSH2 0x766 JUMP JUMPDEST SWAP1 POP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 ISZERO ISZERO PUSH2 0x61A JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0x666 JUMP JUMPDEST POP DUP3 DUP3 MUL DUP3 DUP5 DUP3 DUP2 ISZERO ISZERO PUSH2 0x62A JUMPI INVALID JUMPDEST DIV EQ PUSH2 0x662 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x109A JUMP JUMPDEST DUP1 SWAP2 POP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 DUP5 DUP2 ISZERO ISZERO PUSH2 0x67B JUMPI INVALID JUMPDEST DIV SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0x662 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x109A JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x705 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10DA JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0x760 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x107A JUMP JUMPDEST POP SWAP1 SUB SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x7A8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x108A JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0xC0 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH2 0x7FD PUSH2 0x817 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH2 0x80A PUSH2 0x817 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x80 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 CALLDATALOAD PUSH2 0x11EF JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 MLOAD PUSH2 0x120B JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x870 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x883 PUSH2 0x87E DUP3 PUSH2 0x11A5 JUMP JUMPDEST PUSH2 0x117E JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0x89F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x8AA DUP4 DUP3 DUP5 PUSH2 0x1210 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x80 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x8C5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x8CF PUSH1 0x80 PUSH2 0x117E JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x8DD DUP5 DUP5 PUSH2 0xAD4 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x8EE DUP5 DUP5 DUP4 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x902 DUP5 DUP3 DUP6 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x916 DUP5 DUP3 DUP6 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x120 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x935 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x93F PUSH1 0x60 PUSH2 0x117E JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x94D DUP5 DUP5 PUSH2 0x8B3 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x80 PUSH2 0x95E DUP5 DUP5 DUP4 ADD PUSH2 0x8B3 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x973 DUP5 DUP3 DUP6 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x992 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x99D PUSH2 0x180 PUSH2 0x117E JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x9AB DUP5 DUP5 PUSH2 0x840 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x9BC DUP5 DUP5 DUP4 ADD PUSH2 0x840 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x9D0 DUP5 DUP3 DUP6 ADD PUSH2 0x840 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x9E4 DUP5 DUP3 DUP6 ADD PUSH2 0x840 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x9F8 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0xA0C DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0xA20 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0xA34 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0xA49 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0xA5F DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xA81 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xA8D DUP5 DUP3 DUP6 ADD PUSH2 0x85F JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xAAF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xABB DUP5 DUP3 DUP6 ADD PUSH2 0x85F JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 CALLDATALOAD PUSH2 0x1208 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 MLOAD PUSH2 0x1208 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xAF2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xAFE DUP5 DUP5 PUSH2 0x853 JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x120 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xB19 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xAFE DUP5 DUP5 PUSH2 0x922 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xB37 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xB4E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xAFE DUP5 DUP3 DUP6 ADD PUSH2 0x97F JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0xB70 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP5 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xB87 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xB93 DUP8 DUP3 DUP9 ADD PUSH2 0x97F JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xBB0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xBBC DUP8 DUP3 DUP9 ADD PUSH2 0x97F JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xBD9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xBE5 DUP8 DUP3 DUP9 ADD PUSH2 0x85F JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xC02 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xC0E DUP8 DUP3 DUP9 ADD PUSH2 0x85F JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH2 0xC23 DUP2 PUSH2 0x11EF JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC34 DUP3 PUSH2 0x11EB JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0xC48 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x121C JUMP JUMPDEST PUSH2 0xC51 DUP2 PUSH2 0x1248 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x55494E543235365F554E444552464C4F57000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x55494E543235365F4F564552464C4F5700000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F41535345545F444154410000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F424547494E5F54494D450000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xF DUP2 MSTORE PUSH32 0x41554354494F4E5F455850495245440000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x13 DUP2 MSTORE PUSH32 0x41554354494F4E5F4E4F545F5354415254454400000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x494E56414C49445F414D4F554E54000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0xC0 DUP4 ADD SWAP1 PUSH2 0xE6B DUP5 DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0xE7E PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0xE91 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0xEA4 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x80 DUP3 ADD MLOAD PUSH2 0xEB7 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xA0 DUP3 ADD MLOAD PUSH2 0xECA PUSH1 0xA0 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x80 DUP4 ADD SWAP1 PUSH2 0xEE1 DUP5 DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0xEF4 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0xF07 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0xECA PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST DUP1 MLOAD PUSH2 0x120 DUP4 ADD SWAP1 PUSH2 0xF2C DUP5 DUP3 PUSH2 0xED0 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0xF3F PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0xED0 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0xECA PUSH2 0x100 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH2 0x180 DUP5 ADD SWAP1 PUSH2 0xF68 DUP6 DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x20 DUP4 ADD MLOAD PUSH2 0xF7B PUSH1 0x20 DUP7 ADD DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x40 DUP4 ADD MLOAD PUSH2 0xF8E PUSH1 0x40 DUP7 ADD DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x60 DUP4 ADD MLOAD PUSH2 0xFA1 PUSH1 0x60 DUP7 ADD DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x80 DUP4 ADD MLOAD PUSH2 0xFB4 PUSH1 0x80 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xA0 DUP4 ADD MLOAD PUSH2 0xFC7 PUSH1 0xA0 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xC0 DUP4 ADD MLOAD PUSH2 0xFDA PUSH1 0xC0 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xE0 DUP4 ADD MLOAD PUSH2 0xFED PUSH1 0xE0 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH2 0x100 DUP4 ADD MLOAD PUSH2 0x1002 PUSH2 0x100 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH2 0x120 DUP4 ADD MLOAD PUSH2 0x1017 PUSH2 0x120 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH2 0x140 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x140 DUP7 ADD MSTORE PUSH2 0x1031 DUP3 DUP3 PUSH2 0xC29 JUMP JUMPDEST SWAP2 POP POP PUSH2 0x160 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x160 DUP7 ADD MSTORE PUSH2 0x104D DUP3 DUP3 PUSH2 0xC29 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH2 0xC23 DUP2 PUSH2 0x1208 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x106D DUP3 DUP6 PUSH2 0xC1A JUMP JUMPDEST PUSH2 0x84C PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x1056 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xC5E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xC8E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xCE4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xD14 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xD44 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xD74 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xDA4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xDFA JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xE2A JUMP JUMPDEST PUSH1 0xC0 DUP2 ADD PUSH2 0x601 DUP3 DUP5 PUSH2 0xE5A JUMP JUMPDEST PUSH2 0x120 DUP2 ADD PUSH2 0x601 DUP3 DUP5 PUSH2 0xF1A JUMP JUMPDEST PUSH1 0x80 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x1138 DUP2 DUP8 PUSH2 0xF53 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x114C DUP2 DUP7 PUSH2 0xF53 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x1160 DUP2 DUP6 PUSH2 0xC29 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x60 DUP4 ADD MSTORE PUSH2 0x1174 DUP2 DUP5 PUSH2 0xC29 JUMP JUMPDEST SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x119D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x11BC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x1237 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x121F JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0xECA JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 SWAP13 0xf7 SIGNEXTEND DIV 0xb7 0x5f 0xc2 RETURNDATACOPY PUSH8 0x62AE4C6C8F25CCAD 0x25 0xdf 0xcb CODECOPY 0xcf SAR 0xd3 SWAP7 PUSH15 0xB27CE513DE036C6578706572696D65 PUSH15 0x74616CF50037000000000000000000 ", + "sourceMap": "986:9378:28:-;;;1673:99;8:9:-1;5:2;;;30:1;27;20:12;5:2;1673:99:28;;;;;;;;;;;;;;;;;;;;;;1734:8;:31;;-1:-1:-1;;;;;;1734:31:28;-1:-1:-1;;;;;1734:31:28;;;;;;;;;;986:9378;;5:122:-1;;83:39;114:6;108:13;83:39;;;74:48;68:59;-1:-1;;;68:59;134:263;;249:2;237:9;228:7;224:23;220:32;217:2;;;265:1;262;255:12;217:2;300:1;317:64;373:7;353:9;317:64;;;307:74;211:186;-1:-1;;;;211:186;404:128;-1:-1;;;;;473:54;;456:76;;986:9378:28;;;;;;" + }, + "deployedBytecode": { + "linkReferences": {}, + "object": "0x60806040526004361061004b5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632e9cd03381146100505780633c28d86114610086575b600080fd5b34801561005c57600080fd5b5061007061006b366004610b25565b6100b3565b60405161007d919061110a565b60405180910390f35b34801561009257600080fd5b506100a66100a1366004610b5a565b61029c565b60405161007d9190611118565b6100bb6107b1565b610140820151516000808080808080606488101561010e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110aa565b60405180910390fd5b6101408a0151610146907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a0163ffffffff6105f216565b6101408b0151909750610181907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a0163ffffffff6105f216565b9550868a61010001511115156101c3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110ba565b868a61010001510394508960a001519350838611151561020f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110fa565b50505083865261010087018051602088015260408701849052606087018290524260a088018190529051828503919081900386821015610255576080890186905261028f565b6101008a0151821061026d576080890184905261028f565b6102898461028461027e8487610607565b8861066d565b610684565b60808a01525b5050505050505050919050565b6102a46107e8565b6102ac6107b1565b6000606060008060006102be8a6100b3565b805160a08201519197501115610300576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110ea565b60a08601516101008b015111610342576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110ca565b608080870151908c01511015610384576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110fa565b6000546040517f3c28d86100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690633c28d861906103e0908e908e908e908e90600401611127565b61012060405180830381600087803b1580156103fb57600080fd5b505af115801561040f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104339190810190610b06565b96508660400151945060008511156105e4576101608a0151935061045e84601063ffffffff6106c316565b92506104728b608001518760800151610724565b915061047e8583610724565b905060008111156105325789516040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163a9059cbb916104de9190859060040161105f565b602060405180830381600087803b1580156104f857600080fd5b505af115801561050c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105309190810190610ae0565b505b60008211156105e4578a516040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163a9059cbb916105909190869060040161105f565b602060405180830381600087803b1580156105aa57600080fd5b505af11580156105be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105e29190810190610ae0565b505b505050505050949350505050565b60006105fe8383610766565b90505b92915050565b60008083151561061a5760009150610666565b5082820282848281151561062a57fe5b0414610662576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061109a565b8091505b5092915050565b600080828481151561067b57fe5b04949350505050565b600082820183811015610662576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061109a565b600081601401835110151515610705576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610105906110da565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600082821115610760576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061107a565b50900390565b6000816020018351101515156107a8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101059061108a565b50016020015190565b60c0604051908101604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b610120604051908101604052806107fd610817565b815260200161080a610817565b8152602001600081525090565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b600061084c82356111ef565b9392505050565b600061084c825161120b565b6000601f8201831361087057600080fd5b813561088361087e826111a5565b61117e565b9150808252602083016020830185838301111561089f57600080fd5b6108aa838284611210565b50505092915050565b6000608082840312156108c557600080fd5b6108cf608061117e565b905060006108dd8484610ad4565b82525060206108ee84848301610ad4565b602083015250604061090284828501610ad4565b604083015250606061091684828501610ad4565b60608301525092915050565b6000610120828403121561093557600080fd5b61093f606061117e565b9050600061094d84846108b3565b825250608061095e848483016108b3565b60208301525061010061097384828501610ad4565b60408301525092915050565b6000610180828403121561099257600080fd5b61099d61018061117e565b905060006109ab8484610840565b82525060206109bc84848301610840565b60208301525060406109d084828501610840565b60408301525060606109e484828501610840565b60608301525060806109f884828501610ac8565b60808301525060a0610a0c84828501610ac8565b60a08301525060c0610a2084828501610ac8565b60c08301525060e0610a3484828501610ac8565b60e083015250610100610a4984828501610ac8565b61010083015250610120610a5f84828501610ac8565b6101208301525061014082013567ffffffffffffffff811115610a8157600080fd5b610a8d8482850161085f565b6101408301525061016082013567ffffffffffffffff811115610aaf57600080fd5b610abb8482850161085f565b6101608301525092915050565b600061084c8235611208565b600061084c8251611208565b600060208284031215610af257600080fd5b6000610afe8484610853565b949350505050565b60006101208284031215610b1957600080fd5b6000610afe8484610922565b600060208284031215610b3757600080fd5b813567ffffffffffffffff811115610b4e57600080fd5b610afe8482850161097f565b60008060008060808587031215610b7057600080fd5b843567ffffffffffffffff811115610b8757600080fd5b610b938782880161097f565b945050602085013567ffffffffffffffff811115610bb057600080fd5b610bbc8782880161097f565b935050604085013567ffffffffffffffff811115610bd957600080fd5b610be58782880161085f565b925050606085013567ffffffffffffffff811115610c0257600080fd5b610c0e8782880161085f565b91505092959194509250565b610c23816111ef565b82525050565b6000610c34826111eb565b808452610c4881602086016020860161121c565b610c5181611248565b9093016020019392505050565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b601281527f494e56414c49445f41535345545f444154410000000000000000000000000000602082015260400190565b601281527f494e56414c49445f424547494e5f54494d450000000000000000000000000000602082015260400190565b600f81527f41554354494f4e5f455850495245440000000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601381527f41554354494f4e5f4e4f545f5354415254454400000000000000000000000000602082015260400190565b600e81527f494e56414c49445f414d4f554e54000000000000000000000000000000000000602082015260400190565b805160c0830190610e6b8482611056565b506020820151610e7e6020850182611056565b506040820151610e916040850182611056565b506060820151610ea46060850182611056565b506080820151610eb76080850182611056565b5060a0820151610eca60a0850182611056565b50505050565b80516080830190610ee18482611056565b506020820151610ef46020850182611056565b506040820151610f076040850182611056565b506060820151610eca6060850182611056565b8051610120830190610f2c8482610ed0565b506020820151610f3f6080850182610ed0565b506040820151610eca610100850182611056565b8051600090610180840190610f688582610c1a565b506020830151610f7b6020860182610c1a565b506040830151610f8e6040860182610c1a565b506060830151610fa16060860182610c1a565b506080830151610fb46080860182611056565b5060a0830151610fc760a0860182611056565b5060c0830151610fda60c0860182611056565b5060e0830151610fed60e0860182611056565b50610100830151611002610100860182611056565b50610120830151611017610120860182611056565b506101408301518482036101408601526110318282610c29565b91505061016083015184820361016086015261104d8282610c29565b95945050505050565b610c2381611208565b6040810161106d8285610c1a565b61084c6020830184611056565b6020808252810161060181610c5e565b6020808252810161060181610c8e565b6020808252810161060181610ce4565b6020808252810161060181610d14565b6020808252810161060181610d44565b6020808252810161060181610d74565b6020808252810161060181610da4565b6020808252810161060181610dfa565b6020808252810161060181610e2a565b60c081016106018284610e5a565b61012081016106018284610f1a565b608080825281016111388187610f53565b9050818103602083015261114c8186610f53565b905081810360408301526111608185610c29565b905081810360608301526111748184610c29565b9695505050505050565b60405181810167ffffffffffffffff8111828210171561119d57600080fd5b604052919050565b600067ffffffffffffffff8211156111bc57600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b151590565b82818337506000910152565b60005b8381101561123757818101518382015260200161121f565b83811115610eca5750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a723058209cf70b04b75fc23e6762ae4c6c8f25ccad25dfcb39cf1dd3966eb27ce513de036c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x4B JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x2E9CD033 DUP2 EQ PUSH2 0x50 JUMPI DUP1 PUSH4 0x3C28D861 EQ PUSH2 0x86 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x70 PUSH2 0x6B CALLDATASIZE PUSH1 0x4 PUSH2 0xB25 JUMP JUMPDEST PUSH2 0xB3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x7D SWAP2 SWAP1 PUSH2 0x110A JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x92 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xA6 PUSH2 0xA1 CALLDATASIZE PUSH1 0x4 PUSH2 0xB5A JUMP JUMPDEST PUSH2 0x29C JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x7D SWAP2 SWAP1 PUSH2 0x1118 JUMP JUMPDEST PUSH2 0xBB PUSH2 0x7B1 JUMP JUMPDEST PUSH2 0x140 DUP3 ADD MLOAD MLOAD PUSH1 0x0 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 PUSH1 0x64 DUP9 LT ISZERO PUSH2 0x10E JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10AA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x140 DUP11 ADD MLOAD PUSH2 0x146 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0 DUP11 ADD PUSH4 0xFFFFFFFF PUSH2 0x5F2 AND JUMP JUMPDEST PUSH2 0x140 DUP12 ADD MLOAD SWAP1 SWAP8 POP PUSH2 0x181 SWAP1 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 DUP11 ADD PUSH4 0xFFFFFFFF PUSH2 0x5F2 AND JUMP JUMPDEST SWAP6 POP DUP7 DUP11 PUSH2 0x100 ADD MLOAD GT ISZERO ISZERO PUSH2 0x1C3 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10BA JUMP JUMPDEST DUP7 DUP11 PUSH2 0x100 ADD MLOAD SUB SWAP5 POP DUP10 PUSH1 0xA0 ADD MLOAD SWAP4 POP DUP4 DUP7 GT ISZERO ISZERO PUSH2 0x20F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10FA JUMP JUMPDEST POP POP POP DUP4 DUP7 MSTORE PUSH2 0x100 DUP8 ADD DUP1 MLOAD PUSH1 0x20 DUP9 ADD MSTORE PUSH1 0x40 DUP8 ADD DUP5 SWAP1 MSTORE PUSH1 0x60 DUP8 ADD DUP3 SWAP1 MSTORE TIMESTAMP PUSH1 0xA0 DUP9 ADD DUP2 SWAP1 MSTORE SWAP1 MLOAD DUP3 DUP6 SUB SWAP2 SWAP1 DUP2 SWAP1 SUB DUP7 DUP3 LT ISZERO PUSH2 0x255 JUMPI PUSH1 0x80 DUP10 ADD DUP7 SWAP1 MSTORE PUSH2 0x28F JUMP JUMPDEST PUSH2 0x100 DUP11 ADD MLOAD DUP3 LT PUSH2 0x26D JUMPI PUSH1 0x80 DUP10 ADD DUP5 SWAP1 MSTORE PUSH2 0x28F JUMP JUMPDEST PUSH2 0x289 DUP5 PUSH2 0x284 PUSH2 0x27E DUP5 DUP8 PUSH2 0x607 JUMP JUMPDEST DUP9 PUSH2 0x66D JUMP JUMPDEST PUSH2 0x684 JUMP JUMPDEST PUSH1 0x80 DUP11 ADD MSTORE JUMPDEST POP POP POP POP POP POP POP POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH2 0x2A4 PUSH2 0x7E8 JUMP JUMPDEST PUSH2 0x2AC PUSH2 0x7B1 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP1 PUSH1 0x0 PUSH2 0x2BE DUP11 PUSH2 0xB3 JUMP JUMPDEST DUP1 MLOAD PUSH1 0xA0 DUP3 ADD MLOAD SWAP2 SWAP8 POP GT ISZERO PUSH2 0x300 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10EA JUMP JUMPDEST PUSH1 0xA0 DUP7 ADD MLOAD PUSH2 0x100 DUP12 ADD MLOAD GT PUSH2 0x342 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10CA JUMP JUMPDEST PUSH1 0x80 DUP1 DUP8 ADD MLOAD SWAP1 DUP13 ADD MLOAD LT ISZERO PUSH2 0x384 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10FA JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x3C28D86100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0x3C28D861 SWAP1 PUSH2 0x3E0 SWAP1 DUP15 SWAP1 DUP15 SWAP1 DUP15 SWAP1 DUP15 SWAP1 PUSH1 0x4 ADD PUSH2 0x1127 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x3FB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x40F JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x433 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xB06 JUMP JUMPDEST SWAP7 POP DUP7 PUSH1 0x40 ADD MLOAD SWAP5 POP PUSH1 0x0 DUP6 GT ISZERO PUSH2 0x5E4 JUMPI PUSH2 0x160 DUP11 ADD MLOAD SWAP4 POP PUSH2 0x45E DUP5 PUSH1 0x10 PUSH4 0xFFFFFFFF PUSH2 0x6C3 AND JUMP JUMPDEST SWAP3 POP PUSH2 0x472 DUP12 PUSH1 0x80 ADD MLOAD DUP8 PUSH1 0x80 ADD MLOAD PUSH2 0x724 JUMP JUMPDEST SWAP2 POP PUSH2 0x47E DUP6 DUP4 PUSH2 0x724 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 DUP2 GT ISZERO PUSH2 0x532 JUMPI DUP10 MLOAD PUSH1 0x40 MLOAD PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0xA9059CBB SWAP2 PUSH2 0x4DE SWAP2 SWAP1 DUP6 SWAP1 PUSH1 0x4 ADD PUSH2 0x105F JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x4F8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x50C JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x530 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xAE0 JUMP JUMPDEST POP JUMPDEST PUSH1 0x0 DUP3 GT ISZERO PUSH2 0x5E4 JUMPI DUP11 MLOAD PUSH1 0x40 MLOAD PUSH32 0xA9059CBB00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP6 AND SWAP2 PUSH4 0xA9059CBB SWAP2 PUSH2 0x590 SWAP2 SWAP1 DUP7 SWAP1 PUSH1 0x4 ADD PUSH2 0x105F JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x5AA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x5BE JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x5E2 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xAE0 JUMP JUMPDEST POP JUMPDEST POP POP POP POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x5FE DUP4 DUP4 PUSH2 0x766 JUMP JUMPDEST SWAP1 POP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP4 ISZERO ISZERO PUSH2 0x61A JUMPI PUSH1 0x0 SWAP2 POP PUSH2 0x666 JUMP JUMPDEST POP DUP3 DUP3 MUL DUP3 DUP5 DUP3 DUP2 ISZERO ISZERO PUSH2 0x62A JUMPI INVALID JUMPDEST DIV EQ PUSH2 0x662 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x109A JUMP JUMPDEST DUP1 SWAP2 POP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP3 DUP5 DUP2 ISZERO ISZERO PUSH2 0x67B JUMPI INVALID JUMPDEST DIV SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0x662 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x109A JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x705 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x10DA JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0x760 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x107A JUMP JUMPDEST POP SWAP1 SUB SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0x7A8 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x105 SWAP1 PUSH2 0x108A JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0xC0 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH2 0x120 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH2 0x7FD PUSH2 0x817 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH2 0x80A PUSH2 0x817 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x80 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 CALLDATALOAD PUSH2 0x11EF JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 MLOAD PUSH2 0x120B JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0x870 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0x883 PUSH2 0x87E DUP3 PUSH2 0x11A5 JUMP JUMPDEST PUSH2 0x117E JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0x89F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x8AA DUP4 DUP3 DUP5 PUSH2 0x1210 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x80 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x8C5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x8CF PUSH1 0x80 PUSH2 0x117E JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x8DD DUP5 DUP5 PUSH2 0xAD4 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x8EE DUP5 DUP5 DUP4 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x902 DUP5 DUP3 DUP6 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x916 DUP5 DUP3 DUP6 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x120 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x935 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x93F PUSH1 0x60 PUSH2 0x117E JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x94D DUP5 DUP5 PUSH2 0x8B3 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x80 PUSH2 0x95E DUP5 DUP5 DUP4 ADD PUSH2 0x8B3 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x973 DUP5 DUP3 DUP6 ADD PUSH2 0xAD4 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x992 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x99D PUSH2 0x180 PUSH2 0x117E JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0x9AB DUP5 DUP5 PUSH2 0x840 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0x9BC DUP5 DUP5 DUP4 ADD PUSH2 0x840 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x9D0 DUP5 DUP3 DUP6 ADD PUSH2 0x840 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x9E4 DUP5 DUP3 DUP6 ADD PUSH2 0x840 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x9F8 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0xA0C DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0xA20 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0xA34 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0xA49 DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0xA5F DUP5 DUP3 DUP6 ADD PUSH2 0xAC8 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xA81 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xA8D DUP5 DUP3 DUP6 ADD PUSH2 0x85F JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xAAF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xABB DUP5 DUP3 DUP6 ADD PUSH2 0x85F JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 CALLDATALOAD PUSH2 0x1208 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x84C DUP3 MLOAD PUSH2 0x1208 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xAF2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xAFE DUP5 DUP5 PUSH2 0x853 JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x120 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xB19 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xAFE DUP5 DUP5 PUSH2 0x922 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xB37 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xB4E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xAFE DUP5 DUP3 DUP6 ADD PUSH2 0x97F JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0xB70 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP5 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xB87 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xB93 DUP8 DUP3 DUP9 ADD PUSH2 0x97F JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xBB0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xBBC DUP8 DUP3 DUP9 ADD PUSH2 0x97F JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xBD9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xBE5 DUP8 DUP3 DUP9 ADD PUSH2 0x85F JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xC02 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xC0E DUP8 DUP3 DUP9 ADD PUSH2 0x85F JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH2 0xC23 DUP2 PUSH2 0x11EF JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC34 DUP3 PUSH2 0x11EB JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0xC48 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x121C JUMP JUMPDEST PUSH2 0xC51 DUP2 PUSH2 0x1248 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x11 DUP2 MSTORE PUSH32 0x55494E543235365F554E444552464C4F57000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x10 DUP2 MSTORE PUSH32 0x55494E543235365F4F564552464C4F5700000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F41535345545F444154410000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x12 DUP2 MSTORE PUSH32 0x494E56414C49445F424547494E5F54494D450000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xF DUP2 MSTORE PUSH32 0x41554354494F4E5F455850495245440000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x13 DUP2 MSTORE PUSH32 0x41554354494F4E5F4E4F545F5354415254454400000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0xE DUP2 MSTORE PUSH32 0x494E56414C49445F414D4F554E54000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0xC0 DUP4 ADD SWAP1 PUSH2 0xE6B DUP5 DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0xE7E PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0xE91 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0xEA4 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x80 DUP3 ADD MLOAD PUSH2 0xEB7 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xA0 DUP3 ADD MLOAD PUSH2 0xECA PUSH1 0xA0 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x80 DUP4 ADD SWAP1 PUSH2 0xEE1 DUP5 DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0xEF4 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0xF07 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0xECA PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST DUP1 MLOAD PUSH2 0x120 DUP4 ADD SWAP1 PUSH2 0xF2C DUP5 DUP3 PUSH2 0xED0 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0xF3F PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0xED0 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0xECA PUSH2 0x100 DUP6 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH2 0x180 DUP5 ADD SWAP1 PUSH2 0xF68 DUP6 DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x20 DUP4 ADD MLOAD PUSH2 0xF7B PUSH1 0x20 DUP7 ADD DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x40 DUP4 ADD MLOAD PUSH2 0xF8E PUSH1 0x40 DUP7 ADD DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x60 DUP4 ADD MLOAD PUSH2 0xFA1 PUSH1 0x60 DUP7 ADD DUP3 PUSH2 0xC1A JUMP JUMPDEST POP PUSH1 0x80 DUP4 ADD MLOAD PUSH2 0xFB4 PUSH1 0x80 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xA0 DUP4 ADD MLOAD PUSH2 0xFC7 PUSH1 0xA0 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xC0 DUP4 ADD MLOAD PUSH2 0xFDA PUSH1 0xC0 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH1 0xE0 DUP4 ADD MLOAD PUSH2 0xFED PUSH1 0xE0 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH2 0x100 DUP4 ADD MLOAD PUSH2 0x1002 PUSH2 0x100 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH2 0x120 DUP4 ADD MLOAD PUSH2 0x1017 PUSH2 0x120 DUP7 ADD DUP3 PUSH2 0x1056 JUMP JUMPDEST POP PUSH2 0x140 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x140 DUP7 ADD MSTORE PUSH2 0x1031 DUP3 DUP3 PUSH2 0xC29 JUMP JUMPDEST SWAP2 POP POP PUSH2 0x160 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x160 DUP7 ADD MSTORE PUSH2 0x104D DUP3 DUP3 PUSH2 0xC29 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH2 0xC23 DUP2 PUSH2 0x1208 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x106D DUP3 DUP6 PUSH2 0xC1A JUMP JUMPDEST PUSH2 0x84C PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x1056 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xC5E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xC8E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xCE4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xD14 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xD44 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xD74 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xDA4 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xDFA JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x601 DUP2 PUSH2 0xE2A JUMP JUMPDEST PUSH1 0xC0 DUP2 ADD PUSH2 0x601 DUP3 DUP5 PUSH2 0xE5A JUMP JUMPDEST PUSH2 0x120 DUP2 ADD PUSH2 0x601 DUP3 DUP5 PUSH2 0xF1A JUMP JUMPDEST PUSH1 0x80 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x1138 DUP2 DUP8 PUSH2 0xF53 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x114C DUP2 DUP7 PUSH2 0xF53 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x1160 DUP2 DUP6 PUSH2 0xC29 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x60 DUP4 ADD MSTORE PUSH2 0x1174 DUP2 DUP5 PUSH2 0xC29 JUMP JUMPDEST SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x119D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x11BC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x1237 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x121F JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0xECA JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 SWAP13 0xf7 SIGNEXTEND DIV 0xb7 0x5f 0xc2 RETURNDATACOPY PUSH8 0x62AE4C6C8F25CCAD 0x25 0xdf 0xcb CODECOPY 0xcf SAR 0xd3 SWAP7 PUSH15 0xB27CE513DE036C6578706572696D65 PUSH15 0x74616CF50037000000000000000000 ", + "sourceMap": "986:9378:28:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;7335:3027;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7335:3027:28;;;;;;;;;;;;;;;;;;;;;;;;;3690:3508;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3690:3508:28;;;;;;;;;;;;;;;;;7335:3027;7437:36;;:::i;:::-;7520:20;;;;:27;7489:28;;;;;;;8295:3;8271:27;;;8250:92;;;;;;;;;;;;;;;;;;;;;;8386:20;;;;:59;;8419:25;;;8386:59;:32;:59;:::i;:::-;8484:20;;;;8352:93;;-1:-1:-1;8484:59:28;;8517:25;;;8484:59;:32;:59;:::i;:::-;8455:88;;8657:23;8627:5;:27;;;:53;8606:118;;;;;;;;;;;;;;;;8795:23;8767:5;:27;;;:51;8734:84;;8900:5;:22;;;8880:42;;8974:9;8953:18;:30;8932:91;;;;;;;;;;;;;;;;-1:-1:-1;;;9192:57:28;;;9291:27;;;;;9259:29;;;:59;9328:26;;;:47;;;9385:24;;;:36;;;9167:15;9431:33;;;:45;;;9522:27;;9055:28;;;;9167:15;9522:37;;;9573:35;;;9569:756;;;9717:28;;;:49;;;9569:756;;;9800:27;;;;9787:40;;9783:542;;10009:28;;;:40;;;9783:542;;;10111:203;10136:9;10163:137;10192:46;10200:24;10226:11;10192:7;:46::i;:::-;10260:22;10163:7;:137::i;:::-;10111:7;:203::i;:::-;10080:28;;;:234;9783:542;7335:3027;;;;;;;;;;;:::o;3690:3508::-;3901:59;;:::i;:::-;3976:36;;:::i;:::-;5398:34;6127:22;6190:13;6404:25;6510:26;4015:28;4033:9;4015:17;:28::i;:::-;4161:31;;4124:33;;;;3976:67;;-1:-1:-1;;4124:68:28;4103:134;;;;;;;;;;;;;;4413:33;;;;4379:31;;;;:67;4358:129;;;;;;;;;;;;;;4627:28;;;;;4598:25;;;;:57;;4577:118;;;;;;;;;;;;;;4780:8;;:128;;;;;:8;;;;;:20;;:128;;4814:8;;4836:9;;4859:12;;4885:13;;4780:128;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4780:128:28;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4780:128:28;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;4780:128:28;;;;;;;;;4759:149;;5435:18;:45;;;5398:82;;5523:1;5494:26;:30;5490:1667;;;6152:24;;;;;-1:-1:-1;6206:25:28;6152:24;6228:2;6206:25;:21;:25;:::i;:::-;6190:41;;6432:64;6440:8;:25;;;6467:14;:28;;;6432:7;:64::i;:::-;6404:92;;6539:54;6547:26;6575:17;6539:7;:54::i;:::-;6510:83;;6766:1;6745:18;:22;6741:132;;;6815:22;;6787:71;;;;;:27;;;;;;:71;;6815:22;6839:18;;6787:71;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6787:71:28;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6787:71:28;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6787:71:28;;;;;;;;;;6741:132;7042:1;7022:17;:21;7018:129;;;7091:21;;7063:69;;;;;:27;;;;;;:69;;7091:21;7114:17;;7063:69;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;7063:69:28;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7063:69:28;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;7063:69:28;;;;;;;;;;7018:129;3690:3508;;;;;;;;;;;;:::o;14708:220:17:-;14829:14;14876:21;14888:1;14891:5;14876:11;:21::i;:::-;14868:30;-1:-1:-1;14708:220:17;;;;;:::o;51:288:20:-;137:7;;164:6;;160:45;;;193:1;186:8;;;;160:45;-1:-1:-1;226:5:20;;;230:1;226;:5;262;;;;;;;;:10;241:73;;;;;;;;;;;;;;331:1;324:8;;51:288;;;;;;:::o;345:151::-;431:7;454:9;470:1;466;:5;;;;;;;;;345:151;-1:-1:-1;;;;345:151:20:o;716:230::-;802:7;837:5;;;873:6;;;;852:69;;;;;;;;;;;;;10268:886:17;10389:14;10452:5;10460:2;10452:10;10440:1;:8;:22;;10419:135;;;;;;;;;;;;;;;;-1:-1:-1;11056:13:17;10801:2;11056:13;11050:20;11072:42;11046:69;;10268:886::o;502:208:20:-;588:7;632:6;;;;611:70;;;;;;;;;;;;;;-1:-1:-1;698:5:20;;;502:208::o;13290:490:17:-;13411:14;13474:5;13482:2;13474:10;13462:1;:8;:22;;13441:107;;;;;;;;;;;;;;;;-1:-1:-1;13727:13:17;13629:2;13727:13;13721:20;;13290:490::o;986:9378:28:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;;63:55;57:66;-1:-1;;;57:66;130:116;;205:36;233:6;227:13;205:36;;254:432;;344:4;332:17;;328:27;-1:-1;318:2;;369:1;366;359:12;318:2;406:6;393:20;428:60;443:44;480:6;443:44;;;428:60;;;419:69;;508:6;501:5;494:21;544:4;536:6;532:17;577:4;570:5;566:16;612:3;603:6;598:3;594:16;591:25;588:2;;;629:1;626;619:12;588:2;639:41;673:6;668:3;663;639:41;;;311:375;;;;;;;;1183:861;;1308:4;1296:9;1291:3;1287:19;1283:30;1280:2;;;1326:1;1323;1316:12;1280:2;1344:20;1359:4;1344:20;;;1335:29;-1:-1;1432:1;1463:60;1519:3;1499:9;1463:60;;;1439:85;;-1:-1;1603:2;1636:60;1692:3;1668:22;;;1636:60;;;1629:4;1622:5;1618:16;1611:86;1545:163;1766:2;1799:60;1855:3;1846:6;1835:9;1831:22;1799:60;;;1792:4;1785:5;1781:16;1774:86;1718:153;1929:2;1962:60;2018:3;2009:6;1998:9;1994:22;1962:60;;;1955:4;1948:5;1944:16;1937:86;1881:153;1274:770;;;;;2098:741;;2234:5;2222:9;2217:3;2213:19;2209:31;2206:2;;;2253:1;2250;2243:12;2206:2;2271:20;2286:4;2271:20;;;2262:29;-1:-1;2341:1;2372:85;2453:3;2433:9;2372:85;;;2348:110;;-1:-1;2520:3;2554:85;2635:3;2611:22;;;2554:85;;;2547:4;2540:5;2536:16;2529:111;2479:172;2723:3;2757:60;2813:3;2804:6;2793:9;2789:22;2757:60;;;2750:4;2743:5;2739:16;2732:86;2661:168;2200:639;;;;;2874:2208;;2985:5;2973:9;2968:3;2964:19;2960:31;2957:2;;;3004:1;3001;2994:12;2957:2;3022:21;3037:5;3022:21;;;3013:30;-1:-1;3101:1;3132:49;3177:3;3157:9;3132:49;;;3108:74;;-1:-1;3251:2;3284:49;3329:3;3305:22;;;3284:49;;;3277:4;3270:5;3266:16;3259:75;3203:142;3410:2;3443:49;3488:3;3479:6;3468:9;3464:22;3443:49;;;3436:4;3429:5;3425:16;3418:75;3355:149;3563:2;3596:49;3641:3;3632:6;3621:9;3617:22;3596:49;;;3589:4;3582:5;3578:16;3571:75;3514:143;3719:3;3753:49;3798:3;3789:6;3778:9;3774:22;3753:49;;;3746:4;3739:5;3735:16;3728:75;3667:147;3876:3;3910:49;3955:3;3946:6;3935:9;3931:22;3910:49;;;3903:4;3896:5;3892:16;3885:75;3824:147;4025:3;4059:49;4104:3;4095:6;4084:9;4080:22;4059:49;;;4052:4;4045:5;4041:16;4034:75;3981:139;4174:3;4208:49;4253:3;4244:6;4233:9;4229:22;4208:49;;;4201:4;4194:5;4190:16;4183:75;4130:139;4336:3;4371:49;4416:3;4407:6;4396:9;4392:22;4371:49;;;4363:5;4356;4352:17;4345:76;4279:153;4482:3;4517:49;4562:3;4553:6;4542:9;4538:22;4517:49;;;4509:5;4502;4498:17;4491:76;4442:136;4666:3;4655:9;4651:19;4638:33;4691:18;4683:6;4680:30;4677:2;;;4723:1;4720;4713:12;4677:2;4759:54;4809:3;4800:6;4789:9;4785:22;4759:54;;;4751:5;4744;4740:17;4733:81;4588:237;4913:3;4902:9;4898:19;4885:33;4938:18;4930:6;4927:30;4924:2;;;4970:1;4967;4960:12;4924:2;5006:54;5056:3;5047:6;5036:9;5032:22;5006:54;;;4998:5;4991;4987:17;4980:81;4835:237;2951:2131;;;;;5089:118;;5156:46;5194:6;5181:20;5156:46;;5214:122;;5292:39;5323:6;5317:13;5292:39;;5343:257;;5455:2;5443:9;5434:7;5430:23;5426:32;5423:2;;;5471:1;5468;5461:12;5423:2;5506:1;5523:61;5576:7;5556:9;5523:61;;;5513:71;5417:183;-1:-1;;;;5417:183;5607:336;;5758:3;5746:9;5737:7;5733:23;5729:33;5726:2;;;5775:1;5772;5765:12;5726:2;5810:1;5827:100;5919:7;5899:9;5827:100;;5950:371;;6076:2;6064:9;6055:7;6051:23;6047:32;6044:2;;;6092:1;6089;6082:12;6044:2;6127:31;;6178:18;6167:30;;6164:2;;;6210:1;6207;6200:12;6164:2;6230:75;6297:7;6288:6;6277:9;6273:22;6230:75;;6328:1085;;;;;6545:3;6533:9;6524:7;6520:23;6516:33;6513:2;;;6562:1;6559;6552:12;6513:2;6597:31;;6648:18;6637:30;;6634:2;;;6680:1;6677;6670:12;6634:2;6700:75;6767:7;6758:6;6747:9;6743:22;6700:75;;;6690:85;;6576:205;6840:2;6829:9;6825:18;6812:32;6864:18;6856:6;6853:30;6850:2;;;6896:1;6893;6886:12;6850:2;6916:75;6983:7;6974:6;6963:9;6959:22;6916:75;;;6906:85;;6791:206;7056:2;7045:9;7041:18;7028:32;7080:18;7072:6;7069:30;7066:2;;;7112:1;7109;7102:12;7066:2;7132:62;7186:7;7177:6;7166:9;7162:22;7132:62;;;7122:72;;7007:193;7259:2;7248:9;7244:18;7231:32;7283:18;7275:6;7272:30;7269:2;;;7315:1;7312;7305:12;7269:2;7335:62;7389:7;7380:6;7369:9;7365:22;7335:62;;;7325:72;;7210:193;6507:906;;;;;;;;7420:110;7493:31;7518:5;7493:31;;;7488:3;7481:44;7475:55;;;7537:297;;7637:38;7669:5;7637:38;;;7692:6;7687:3;7680:19;7704:63;7760:6;7753:4;7748:3;7744:14;7737:4;7730:5;7726:16;7704:63;;;7799:29;7821:6;7799:29;;;7779:50;;;7792:4;7779:50;;7617:217;-1:-1;;;7617:217;8138:296;8293:2;8281:15;;8330:66;8325:2;8316:12;;8309:88;8425:2;8416:12;;8274:160;8443:397;8598:2;8586:15;;8635:66;8630:2;8621:12;;8614:88;8736:66;8731:2;8722:12;;8715:88;8831:2;8822:12;;8579:261;8849:296;9004:2;8992:15;;9041:66;9036:2;9027:12;;9020:88;9136:2;9127:12;;8985:160;9154:296;9309:2;9297:15;;9346:66;9341:2;9332:12;;9325:88;9441:2;9432:12;;9290:160;9459:296;9614:2;9602:15;;9651:66;9646:2;9637:12;;9630:88;9746:2;9737:12;;9595:160;9764:296;9919:2;9907:15;;9956:66;9951:2;9942:12;;9935:88;10051:2;10042:12;;9900:160;10069:397;10224:2;10212:15;;10261:66;10256:2;10247:12;;10240:88;10362:66;10357:2;10348:12;;10341:88;10457:2;10448:12;;10205:261;10475:296;10630:2;10618:15;;10667:66;10662:2;10653:12;;10646:88;10762:2;10753:12;;10611:160;10780:296;10935:2;10923:15;;10972:66;10967:2;10958:12;;10951:88;11067:2;11058:12;;10916:160;11163:1231;11387:22;;11306:4;11297:14;;;11421:61;11301:3;11387:22;11421:61;;;11326:168;11580:4;11573:5;11569:16;11563:23;11598:62;11654:4;11649:3;11645:14;11632:11;11598:62;;;11504:168;11755:4;11748:5;11744:16;11738:23;11773:62;11829:4;11824:3;11820:14;11807:11;11773:62;;;11682:165;11928:4;11921:5;11917:16;11911:23;11946:62;12002:4;11997:3;11993:14;11980:11;11946:62;;;11857:163;12105:4;12098:5;12094:16;12088:23;12123:62;12179:4;12174:3;12170:14;12157:11;12123:62;;;12030:167;12287:4;12280:5;12276:16;12270:23;12305:62;12361:4;12356:3;12352:14;12339:11;12305:62;;;12207:172;11279:1115;;;;12478:884;12702:22;;12615:4;12606:14;;;12736:61;12610:3;12702:22;12736:61;;;12635:174;12903:4;12896:5;12892:16;12886:23;12921:62;12977:4;12972:3;12968:14;12955:11;12921:62;;;12819:176;13079:4;13072:5;13068:16;13062:23;13097:62;13153:4;13148:3;13144:14;13131:11;13097:62;;;13005:166;13255:4;13248:5;13244:16;13238:23;13273:62;13329:4;13324:3;13320:14;13307:11;13273:62;;13460:815;13685:22;;13615:5;13606:15;;;13719:115;13610:3;13685:22;13719:115;;;13636:210;13923:4;13916:5;13912:16;13906:23;13941:116;14051:4;14046:3;14042:14;14029:11;13941:116;;;13856:213;14167:4;14160:5;14156:16;14150:23;14185:63;14241:5;14236:3;14232:15;14219:11;14185:63;;14335:2417;14548:22;;14335:2417;;14470:5;14461:15;;;14582:61;14465:3;14548:22;14582:61;;;14491:164;14739:4;14732:5;14728:16;14722:23;14757:62;14813:4;14808:3;14804:14;14791:11;14757:62;;;14665:166;14922:4;14915:5;14911:16;14905:23;14940:62;14996:4;14991:3;14987:14;14974:11;14940:62;;;14841:173;15099:4;15092:5;15088:16;15082:23;15117:62;15173:4;15168:3;15164:14;15151:11;15117:62;;;15024:167;15279:4;15272:5;15268:16;15262:23;15297:62;15353:4;15348:3;15344:14;15331:11;15297:62;;;15201:170;15459:4;15452:5;15448:16;15442:23;15477:62;15533:4;15528:3;15524:14;15511:11;15477:62;;;15381:170;15631:4;15624:5;15620:16;15614:23;15649:62;15705:4;15700:3;15696:14;15683:11;15649:62;;;15561:162;15803:4;15796:5;15792:16;15786:23;15821:62;15877:4;15872:3;15868:14;15855:11;15821:62;;;15733:162;15988:5;15981;15977:17;15971:24;16007:63;16063:5;16058:3;16054:15;16041:11;16007:63;;;15905:177;16158:5;16151;16147:17;16141:24;16177:63;16233:5;16228:3;16224:15;16211:11;16177:63;;;16092:160;16338:5;16331;16327:17;16321:24;16391:3;16385:4;16381:14;16373:5;16368:3;16364:15;16357:39;16411:66;16472:4;16459:11;16411:66;;;16403:74;;16262:227;16575:5;16568;16564:17;16558:24;16628:3;16622:4;16618:14;16610:5;16605:3;16601:15;16594:39;16648:66;16709:4;16696:11;16648:66;;;16640:74;14443:2309;-1:-1;;;;;14443:2309;16759:110;16832:31;16857:5;16832:31;;16876:294;17012:2;16997:18;;17026:61;17001:9;17060:6;17026:61;;;17098:62;17156:2;17145:9;17141:18;17132:6;17098:62;;17177:387;17358:2;17372:47;;;17343:18;;17433:121;17343:18;17433:121;;17571:387;17752:2;17766:47;;;17737:18;;17827:121;17737:18;17827:121;;17965:387;18146:2;18160:47;;;18131:18;;18221:121;18131:18;18221:121;;18359:387;18540:2;18554:47;;;18525:18;;18615:121;18525:18;18615:121;;18753:387;18934:2;18948:47;;;18919:18;;19009:121;18919:18;19009:121;;19147:387;19328:2;19342:47;;;19313:18;;19403:121;19313:18;19403:121;;19541:387;19722:2;19736:47;;;19707:18;;19797:121;19707:18;19797:121;;19935:387;20116:2;20130:47;;;20101:18;;20191:121;20101:18;20191:121;;20329:387;20510:2;20524:47;;;20495:18;;20585:121;20495:18;20585:121;;20723:314;20891:3;20876:19;;20906:121;20880:9;21000:6;20906:121;;21044:338;21224:3;21209:19;;21239:133;21213:9;21345:6;21239:133;;21389:937;21705:3;21720:47;;;21690:19;;21781:92;21690:19;21859:6;21781:92;;;21773:100;;21921:9;21915:4;21911:20;21906:2;21895:9;21891:18;21884:48;21946:92;22033:4;22024:6;21946:92;;;21938:100;;22086:9;22080:4;22076:20;22071:2;22060:9;22056:18;22049:48;22111:66;22172:4;22163:6;22111:66;;;22103:74;;22225:9;22219:4;22215:20;22210:2;22199:9;22195:18;22188:48;22250:66;22311:4;22302:6;22250:66;;;22242:74;21676:650;-1:-1;;;;;;21676:650;22333:256;22395:2;22389:9;22421:17;;;22496:18;22481:34;;22517:22;;;22478:62;22475:2;;;22553:1;22550;22543:12;22475:2;22569;22562:22;22373:216;;-1:-1;22373:216;22596:254;;22735:18;22727:6;22724:30;22721:2;;;22767:1;22764;22757:12;22721:2;-1:-1;22840:4;22811;22788:17;;;;22807:9;22784:33;22830:15;;22658:192;23122:87;23192:12;;23176:33;23314:128;23394:42;23383:54;;23366:76;23449:79;23518:5;23501:27;23670:92;23743:13;23736:21;;23719:43;23856:145;23937:6;23932:3;23927;23914:30;-1:-1;23993:1;23975:16;;23968:27;23907:94;24010:268;24075:1;24082:101;24096:6;24093:1;24090:13;24082:101;;;24163:11;;;24157:18;24144:11;;;24137:39;24118:2;24111:10;24082:101;;;24198:6;24195:1;24192:13;24189:2;;;-1:-1;;24263:1;24245:16;;24238:27;24059:219;24286:97;24374:2;24354:14;24370:7;24350:28;;24334:49" + } + } + }, + "networks": {} +} diff --git a/packages/contract-artifacts/package.json b/packages/contract-artifacts/package.json index 71d887545..b819520c6 100644 --- a/packages/contract-artifacts/package.json +++ b/packages/contract-artifacts/package.json @@ -1,6 +1,6 @@ { "name": "@0x/contract-artifacts", - "version": "1.1.2", + "version": "1.2.0", "engines": { "node": ">=6.12" }, diff --git a/packages/contract-artifacts/src/index.ts b/packages/contract-artifacts/src/index.ts index f30276dd9..bd5f8fee3 100644 --- a/packages/contract-artifacts/src/index.ts +++ b/packages/contract-artifacts/src/index.ts @@ -1,4 +1,5 @@ import * as AssetProxyOwner from '../artifacts/AssetProxyOwner.json'; +import * as DutchAuction from '../artifacts/DutchAuction.json'; import * as DummyERC20Token from '../artifacts/DummyERC20Token.json'; import * as DummyERC721Token from '../artifacts/DummyERC721Token.json'; import * as ERC20Proxy from '../artifacts/ERC20Proxy.json'; @@ -15,6 +16,7 @@ import * as ZRXToken from '../artifacts/ZRXToken.json'; export { AssetProxyOwner, + DutchAuction, DummyERC20Token, DummyERC721Token, ERC20Proxy, diff --git a/packages/contract-artifacts/tsconfig.json b/packages/contract-artifacts/tsconfig.json index cce086209..59169fceb 100644 --- a/packages/contract-artifacts/tsconfig.json +++ b/packages/contract-artifacts/tsconfig.json @@ -8,6 +8,7 @@ "include": ["./src/**/*"], "files": [ "./artifacts/AssetProxyOwner.json", + "./artifacts/DutchAuction.json", "./artifacts/DummyERC20Token.json", "./artifacts/DummyERC721Token.json", "./artifacts/ERC20Proxy.json", diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index d39027797..fd8dfa427 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,9 +1,23 @@ [ { + "version": "4.2.0", + "changes": [ + { + "note": "Added Dutch Auction wrapper", + "pr": 1465 + } + ], + "timestamp": 1547040760 + }, + { "version": "4.1.4", "changes": [ { "note": "Add support for Trust Wallet signature denial error" + }, + { + "note": "Add balance and allowance queries for `MultiAssetProxy`", + "pr": 1363 } ] }, diff --git a/packages/contract-wrappers/CHANGELOG.md b/packages/contract-wrappers/CHANGELOG.md index 595fbcc31..fe7bb7b81 100644 --- a/packages/contract-wrappers/CHANGELOG.md +++ b/packages/contract-wrappers/CHANGELOG.md @@ -5,6 +5,15 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v4.2.0 - _January 9, 2019_ + + * Added Dutch Auction wrapper (#1465) + +## v4.1.4 - _Invalid date_ + + * Add support for Trust Wallet signature denial error + * Add balance and allowance queries for `MultiAssetProxy` (#1363) + ## v4.1.3 - _December 13, 2018_ * Dependencies updated diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index f3f7301fd..708b4249a 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0x/contract-wrappers", - "version": "4.1.3", + "version": "4.2.0", "description": "Smart TS wrappers for 0x smart contracts", "keywords": [ "0xproject", @@ -37,9 +37,9 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.21", - "@0x/migrations": "^2.2.2", - "@0x/subproviders": "^2.1.8", + "@0x/dev-utils": "^1.0.22", + "@0x/migrations": "^2.3.0", + "@0x/subproviders": "^2.1.9", "@0x/tslint-config": "^2.0.0", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -65,18 +65,20 @@ "web3-provider-engine": "14.0.6" }, "dependencies": { - "@0x/abi-gen-wrappers": "^2.0.2", - "@0x/assert": "^1.0.20", - "@0x/contract-addresses": "^2.0.0", - "@0x/contract-artifacts": "^1.1.2", - "@0x/fill-scenarios": "^1.0.16", - "@0x/json-schemas": "^2.1.4", - "@0x/order-utils": "^3.0.7", - "@0x/types": "^1.4.1", + "@0x/abi-gen-wrappers": "^2.1.0", + "@0x/assert": "^1.0.21", + "@0x/contract-addresses": "^2.1.0", + "@0x/contract-artifacts": "^1.2.0", + "@0x/contracts-test-utils": "^1.0.3", + "@0x/fill-scenarios": "^1.1.0", + "@0x/json-schemas": "^2.1.5", + "@0x/order-utils": "^3.1.0", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "ethereum-types": "^1.1.4", + "ethereumjs-abi": "0.6.5", "ethereumjs-blockstream": "6.0.0", "ethereumjs-util": "^5.1.1", "ethers": "~4.0.4", 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/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); + } +} diff --git a/packages/dev-tools-pages/.gitignore b/packages/dev-tools-pages/.gitignore new file mode 100644 index 000000000..557b82311 --- /dev/null +++ b/packages/dev-tools-pages/.gitignore @@ -0,0 +1,3 @@ +public +assets/fonts/*.woff +assets/fonts/*.woff2
\ No newline at end of file diff --git a/packages/dev-tools-pages/assets/crawl.html b/packages/dev-tools-pages/assets/crawl.html new file mode 100644 index 000000000..9135c3ede --- /dev/null +++ b/packages/dev-tools-pages/assets/crawl.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <title>Document</title> +</head> +<body> + +<ul> +<li><a href="/compiler">Compiler</a></li> +<li><a href="/cov">Cov</a></li> +<li><a href="/profiler">Profiler</a></li> +<li><a href="/trace">Trace</a></li> + +</ul> + +</body> +</html>
\ No newline at end of file diff --git a/packages/dev-tools-pages/assets/favicons/compiler.ico b/packages/dev-tools-pages/assets/favicons/compiler.ico Binary files differnew file mode 100644 index 000000000..ff9409b2a --- /dev/null +++ b/packages/dev-tools-pages/assets/favicons/compiler.ico diff --git a/packages/dev-tools-pages/assets/favicons/cov.ico b/packages/dev-tools-pages/assets/favicons/cov.ico Binary files differnew file mode 100644 index 000000000..f04f4bf56 --- /dev/null +++ b/packages/dev-tools-pages/assets/favicons/cov.ico diff --git a/packages/dev-tools-pages/assets/favicons/profiler.ico b/packages/dev-tools-pages/assets/favicons/profiler.ico Binary files differnew file mode 100644 index 000000000..0a8fff329 --- /dev/null +++ b/packages/dev-tools-pages/assets/favicons/profiler.ico diff --git a/packages/dev-tools-pages/assets/favicons/trace.ico b/packages/dev-tools-pages/assets/favicons/trace.ico Binary files differnew file mode 100644 index 000000000..744a18860 --- /dev/null +++ b/packages/dev-tools-pages/assets/favicons/trace.ico diff --git a/packages/dev-tools-pages/less/all.less b/packages/dev-tools-pages/assets/fonts/.gitkeep index e69de29bb..e69de29bb 100644 --- a/packages/dev-tools-pages/less/all.less +++ b/packages/dev-tools-pages/assets/fonts/.gitkeep diff --git a/packages/dev-tools-pages/assets/images/og-compiler.png b/packages/dev-tools-pages/assets/images/og-compiler.png Binary files differnew file mode 100644 index 000000000..871e8f279 --- /dev/null +++ b/packages/dev-tools-pages/assets/images/og-compiler.png diff --git a/packages/dev-tools-pages/assets/images/og-cov.png b/packages/dev-tools-pages/assets/images/og-cov.png Binary files differnew file mode 100644 index 000000000..1cfeada7a --- /dev/null +++ b/packages/dev-tools-pages/assets/images/og-cov.png diff --git a/packages/dev-tools-pages/assets/images/og-profiler.png b/packages/dev-tools-pages/assets/images/og-profiler.png Binary files differnew file mode 100644 index 000000000..4338f23ea --- /dev/null +++ b/packages/dev-tools-pages/assets/images/og-profiler.png diff --git a/packages/dev-tools-pages/assets/images/og-trace.png b/packages/dev-tools-pages/assets/images/og-trace.png Binary files differnew file mode 100644 index 000000000..5318128f2 --- /dev/null +++ b/packages/dev-tools-pages/assets/images/og-trace.png diff --git a/packages/dev-tools-pages/assets/index.html b/packages/dev-tools-pages/assets/index.html new file mode 100644 index 000000000..5ab1a45f1 --- /dev/null +++ b/packages/dev-tools-pages/assets/index.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <link rel="preload" href="/fonts/MaisonNeue-Book-subset.woff2" as="font" type="font/woff2" crossorigin> + <link rel="preload" href="/fonts/MaisonNeue-Bold-subset.woff2" as="font" type="font/woff2" crossorigin> + <title><%= htmlWebpackPlugin.options.title %></title> +</head> +<body> + <div id="app"></div> +</body> +</html>
\ No newline at end of file diff --git a/packages/dev-tools-pages/package.json b/packages/dev-tools-pages/package.json index 754db3208..db927849e 100644 --- a/packages/dev-tools-pages/package.json +++ b/packages/dev-tools-pages/package.json @@ -1,13 +1,13 @@ { "name": "@0x/dev-tools-pages", - "version": "0.0.10", + "version": "0.0.11", "engines": { "node": ">=6.12" }, "private": true, "description": "0x Dev tools pages", "scripts": { - "build": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production", + "build": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production && react-snap", "build:ci": "yarn build", "build:dev": "../../node_modules/.bin/webpack --mode development", "clean": "shx rm -f public/bundle*", @@ -16,33 +16,43 @@ }, "license": "Apache-2.0", "dependencies": { - "@0x/react-shared": "^1.0.25", + "@0x/react-shared": "^1.1.0", "basscss": "^8.0.3", "bowser": "^1.9.3", + "highlight.js": "^9.13.1", "less": "^2.7.2", - "lodash": "^4.17.5", "polished": "^1.9.2", - "react": "^16.4.2", - "react-document-title": "^2.0.3", - "react-dom": "^16.4.2", - "react-helmet": "^5.2.0", - "styled-components": "^3.3.0" + "react": "^16.5.2", + "react-dom": "^16.5.2", + "react-loadable": "^5.5.0", + "react-lottie": "^1.2.3", + "react-tabs": "^2.3.0", + "styled-components": "^4.1.1", + "styled-normalize": "^8.0.1" }, "devDependencies": { + "@types/highlight.js": "^9.12.3", "@types/lodash": "4.14.104", "@types/node": "*", "@types/react": "^16.4.2", "@types/react-dom": "^16.0.7", - "@types/react-helmet": "^5.0.6", + "@types/react-loadable": "^5.4.1", + "@types/react-lottie": "^1.2.0", "@types/react-router-dom": "^4.0.4", + "@types/react-tabs": "^2.3.0", "@types/react-tap-event-plugin": "0.0.30", - "@types/styled-components": "^4.0.0", + "@types/styled-components": "4.1.1", "awesome-typescript-loader": "^5.2.1", + "clean-webpack-plugin": "^0.1.19", + "copy-webpack-plugin": "^4.5.4", "copyfiles": "^2.0.0", "css-loader": "0.23.x", + "html-webpack-plugin": "^3.2.0", "less-loader": "^4.1.0", "make-promises-safe": "^1.1.0", "raw-loader": "^0.5.1", + "react-snap": "^1.19.0", + "react-svg-loader": "^2.1.0", "shx": "^0.2.2", "source-map-loader": "^0.2.4", "style-loader": "0.23.x", @@ -52,7 +62,15 @@ "typescript": "3.0.1", "uglifyjs-webpack-plugin": "^2.0.1", "webpack": "^4.20.2", + "webpack-bundle-analyzer": "^3.0.3", "webpack-cli": "3.1.2", "webpack-dev-server": "^3.1.9" + }, + "reactSnap": { + "source": "public", + "puppeteerArgs": [ + "--no-sandbox", + "--disable-setuid-sandbox" + ] } } diff --git a/packages/dev-tools-pages/pages.js b/packages/dev-tools-pages/pages.js new file mode 100644 index 000000000..7609a07e9 --- /dev/null +++ b/packages/dev-tools-pages/pages.js @@ -0,0 +1,72 @@ +const pages = [ + { + title: 'sol-compiler', + filename: 'compiler/index.html', + template: 'assets/index.html', + chunks: ['compiler'], + favicon: 'assets/favicons/compiler.ico', + minify: true, + meta: { + description: 'Solidity compilation that just works', + 'og-title': { property: 'og:title', content: 'sol-compiler' }, + 'og-description': { property: 'og:description', content: 'Solidity compilation that just works' }, + 'og-type': { property: 'og:type', content: 'website' }, + 'og-image': { property: 'og:image', content: '/images/og-compiler' }, + 'twitter:site': '@0xproject', + 'twitter:image': '/images/og-compiler', + }, + }, + { + title: 'sol-cov', + filename: 'cov/index.html', + template: 'assets/index.html', + chunks: ['cov'], + favicon: 'assets/favicons/cov.ico', + minify: true, + meta: { + description: 'Solidity code coverage', + 'og-title': { property: 'og:title', content: 'sol-cov' }, + 'og-description': { property: 'og:description', content: 'Solidity code coverage' }, + 'og-type': { property: 'og:type', content: 'website' }, + 'og-image': { property: 'og:image', content: '/images/og-cov' }, + 'twitter:site': '@0xproject', + 'twitter:image': '/images/og-cov', + }, + }, + { + title: 'sol-profiler', + filename: 'profiler/index.html', + template: 'assets/index.html', + chunks: ['profiler'], + favicon: 'assets/favicons/profiler.ico', + minify: true, + meta: { + description: 'Gas profiling for Solidity', + 'og-title': { property: 'og:title', content: 'sol-profiler' }, + 'og-description': { property: 'og:description', content: 'Gas profiling for Solidity' }, + 'og-type': { property: 'og:type', content: 'website' }, + 'og-image': { property: 'og:image', content: '/images/og-profiler' }, + 'twitter:site': '@0xproject', + 'twitter:image': '/images/og-profiler', + }, + }, + { + title: 'sol-trace', + filename: 'trace/index.html', + template: 'assets/index.html', + chunks: ['trace'], + favicon: 'assets/favicons/trace.ico', + minify: true, + meta: { + description: 'Human-readable stack traces', + 'og-title': { property: 'og:title', content: 'sol-trace' }, + 'og-description': { property: 'og:description', content: 'Human-readable stack traces' }, + 'og-type': { property: 'og:type', content: 'website' }, + 'og-image': { property: 'og:image', content: '/images/og-trace' }, + 'twitter:site': '@0xproject', + 'twitter:image': '/images/og-trace', + }, + }, +]; + +module.exports = pages; diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_custom.css b/packages/dev-tools-pages/public/css/basscss_responsive_custom.css deleted file mode 100644 index 5f8bd9117..000000000 --- a/packages/dev-tools-pages/public/css/basscss_responsive_custom.css +++ /dev/null @@ -1,85 +0,0 @@ -/* Custom Basscss Responsive Utilities */ - -@media (max-width: 52em) { - .sm-center { - text-align: center; - } - .sm-align-middle { - vertical-align: middle; - } - .sm-align-top { - vertical-align: top; - } - .sm-left-align { - text-align: left; - } - .sm-right-align { - text-align: right; - } - .sm-table-cell { - display: table-cell; - } - .sm-mx-auto { - margin-left: auto; - margin-right: auto; - } - .sm-right { - float: right; - } -} - -@media (min-width: 52em) { - .md-center { - text-align: center; - } - .md-align-middle { - vertical-align: middle; - } - .md-align-top { - vertical-align: top; - } - .md-left-align { - text-align: left; - } - .md-right-align { - text-align: right; - } - .md-table-cell { - display: table-cell; - } - .md-mx-auto { - margin-left: auto; - margin-right: auto; - } - .md-right { - float: right; - } -} - -@media (min-width: 64em) { - .lg-center { - text-align: center; - } - .lg-align-middle { - vertical-align: middle; - } - .lg-align-top { - vertical-align: top; - } - .lg-left-align { - text-align: left; - } - .lg-right-align { - text-align: right; - } - .lg-table-cell { - display: table-cell; - } - .lg-mx-auto { - margin-left: auto; - margin-right: auto; - } - .lg-right { - float: right; - } -} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_margin.css b/packages/dev-tools-pages/public/css/basscss_responsive_margin.css deleted file mode 100644 index c9f3e855c..000000000 --- a/packages/dev-tools-pages/public/css/basscss_responsive_margin.css +++ /dev/null @@ -1,453 +0,0 @@ -/* Basscss Responsive Margin */ - -@media (max-width: 52em) { - /* Modified by Fabio Berger to max-width from min-width */ - - .sm-m0 { - margin: 0; - } - .sm-mt0 { - margin-top: 0; - } - .sm-mr0 { - margin-right: 0; - } - .sm-mb0 { - margin-bottom: 0; - } - .sm-ml0 { - margin-left: 0; - } - .sm-mx0 { - margin-left: 0; - margin-right: 0; - } - .sm-my0 { - margin-top: 0; - margin-bottom: 0; - } - - .sm-m1 { - margin: 0.5rem; - } - .sm-mt1 { - margin-top: 0.5rem; - } - .sm-mr1 { - margin-right: 0.5rem; - } - .sm-mb1 { - margin-bottom: 0.5rem; - } - .sm-ml1 { - margin-left: 0.5rem; - } - .sm-mx1 { - margin-left: 0.5rem; - margin-right: 0.5rem; - } - .sm-my1 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; - } - - .sm-m2 { - margin: 1rem; - } - .sm-mt2 { - margin-top: 1rem; - } - .sm-mr2 { - margin-right: 1rem; - } - .sm-mb2 { - margin-bottom: 1rem; - } - .sm-ml2 { - margin-left: 1rem; - } - .sm-mx2 { - margin-left: 1rem; - margin-right: 1rem; - } - .sm-my2 { - margin-top: 1rem; - margin-bottom: 1rem; - } - - .sm-m3 { - margin: 2rem; - } - .sm-mt3 { - margin-top: 2rem; - } - .sm-mr3 { - margin-right: 2rem; - } - .sm-mb3 { - margin-bottom: 2rem; - } - .sm-ml3 { - margin-left: 2rem; - } - .sm-mx3 { - margin-left: 2rem; - margin-right: 2rem; - } - .sm-my3 { - margin-top: 2rem; - margin-bottom: 2rem; - } - - .sm-m4 { - margin: 4rem; - } - .sm-mt4 { - margin-top: 4rem; - } - .sm-mr4 { - margin-right: 4rem; - } - .sm-mb4 { - margin-bottom: 4rem; - } - .sm-ml4 { - margin-left: 4rem; - } - .sm-mx4 { - margin-left: 4rem; - margin-right: 4rem; - } - .sm-my4 { - margin-top: 4rem; - margin-bottom: 4rem; - } - - .sm-mxn1 { - margin-left: -0.5rem; - margin-right: -0.5rem; - } - .sm-mxn2 { - margin-left: -1rem; - margin-right: -1rem; - } - .sm-mxn3 { - margin-left: -2rem; - margin-right: -2rem; - } - .sm-mxn4 { - margin-left: -4rem; - margin-right: -4rem; - } - - .sm-ml-auto { - margin-left: auto; - } - .sm-mr-auto { - margin-right: auto; - } - .sm-mx-auto { - margin-left: auto; - margin-right: auto; - } -} - -@media (min-width: 52em) { - .md-m0 { - margin: 0; - } - .md-mt0 { - margin-top: 0; - } - .md-mr0 { - margin-right: 0; - } - .md-mb0 { - margin-bottom: 0; - } - .md-ml0 { - margin-left: 0; - } - .md-mx0 { - margin-left: 0; - margin-right: 0; - } - .md-my0 { - margin-top: 0; - margin-bottom: 0; - } - - .md-m1 { - margin: 0.5rem; - } - .md-mt1 { - margin-top: 0.5rem; - } - .md-mr1 { - margin-right: 0.5rem; - } - .md-mb1 { - margin-bottom: 0.5rem; - } - .md-ml1 { - margin-left: 0.5rem; - } - .md-mx1 { - margin-left: 0.5rem; - margin-right: 0.5rem; - } - .md-my1 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; - } - - .md-m2 { - margin: 1rem; - } - .md-mt2 { - margin-top: 1rem; - } - .md-mr2 { - margin-right: 1rem; - } - .md-mb2 { - margin-bottom: 1rem; - } - .md-ml2 { - margin-left: 1rem; - } - .md-mx2 { - margin-left: 1rem; - margin-right: 1rem; - } - .md-my2 { - margin-top: 1rem; - margin-bottom: 1rem; - } - - .md-m3 { - margin: 2rem; - } - .md-mt3 { - margin-top: 2rem; - } - .md-mr3 { - margin-right: 2rem; - } - .md-mb3 { - margin-bottom: 2rem; - } - .md-ml3 { - margin-left: 2rem; - } - .md-mx3 { - margin-left: 2rem; - margin-right: 2rem; - } - .md-my3 { - margin-top: 2rem; - margin-bottom: 2rem; - } - - .md-m4 { - margin: 4rem; - } - .md-mt4 { - margin-top: 4rem; - } - .md-mr4 { - margin-right: 4rem; - } - .md-mb4 { - margin-bottom: 4rem; - } - .md-ml4 { - margin-left: 4rem; - } - .md-mx4 { - margin-left: 4rem; - margin-right: 4rem; - } - .md-my4 { - margin-top: 4rem; - margin-bottom: 4rem; - } - - .md-mxn1 { - margin-left: -0.5rem; - margin-right: -0.5rem; - } - .md-mxn2 { - margin-left: -1rem; - margin-right: -1rem; - } - .md-mxn3 { - margin-left: -2rem; - margin-right: -2rem; - } - .md-mxn4 { - margin-left: -4rem; - margin-right: -4rem; - } - - .md-ml-auto { - margin-left: auto; - } - .md-mr-auto { - margin-right: auto; - } - .md-mx-auto { - margin-left: auto; - margin-right: auto; - } -} - -@media (min-width: 64em) { - .lg-m0 { - margin: 0; - } - .lg-mt0 { - margin-top: 0; - } - .lg-mr0 { - margin-right: 0; - } - .lg-mb0 { - margin-bottom: 0; - } - .lg-ml0 { - margin-left: 0; - } - .lg-mx0 { - margin-left: 0; - margin-right: 0; - } - .lg-my0 { - margin-top: 0; - margin-bottom: 0; - } - - .lg-m1 { - margin: 0.5rem; - } - .lg-mt1 { - margin-top: 0.5rem; - } - .lg-mr1 { - margin-right: 0.5rem; - } - .lg-mb1 { - margin-bottom: 0.5rem; - } - .lg-ml1 { - margin-left: 0.5rem; - } - .lg-mx1 { - margin-left: 0.5rem; - margin-right: 0.5rem; - } - .lg-my1 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; - } - - .lg-m2 { - margin: 1rem; - } - .lg-mt2 { - margin-top: 1rem; - } - .lg-mr2 { - margin-right: 1rem; - } - .lg-mb2 { - margin-bottom: 1rem; - } - .lg-ml2 { - margin-left: 1rem; - } - .lg-mx2 { - margin-left: 1rem; - margin-right: 1rem; - } - .lg-my2 { - margin-top: 1rem; - margin-bottom: 1rem; - } - - .lg-m3 { - margin: 2rem; - } - .lg-mt3 { - margin-top: 2rem; - } - .lg-mr3 { - margin-right: 2rem; - } - .lg-mb3 { - margin-bottom: 2rem; - } - .lg-ml3 { - margin-left: 2rem; - } - .lg-mx3 { - margin-left: 2rem; - margin-right: 2rem; - } - .lg-my3 { - margin-top: 2rem; - margin-bottom: 2rem; - } - - .lg-m4 { - margin: 4rem; - } - .lg-mt4 { - margin-top: 4rem; - } - .lg-mr4 { - margin-right: 4rem; - } - .lg-mb4 { - margin-bottom: 4rem; - } - .lg-ml4 { - margin-left: 4rem; - } - .lg-mx4 { - margin-left: 4rem; - margin-right: 4rem; - } - .lg-my4 { - margin-top: 4rem; - margin-bottom: 4rem; - } - - .lg-mxn1 { - margin-left: -0.5rem; - margin-right: -0.5rem; - } - .lg-mxn2 { - margin-left: -1rem; - margin-right: -1rem; - } - .lg-mxn3 { - margin-left: -2rem; - margin-right: -2rem; - } - .lg-mxn4 { - margin-left: -4rem; - margin-right: -4rem; - } - - .lg-ml-auto { - margin-left: auto; - } - .lg-mr-auto { - margin-right: auto; - } - .lg-mx-auto { - margin-left: auto; - margin-right: auto; - } -} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_padding.css b/packages/dev-tools-pages/public/css/basscss_responsive_padding.css deleted file mode 100644 index e027c2d65..000000000 --- a/packages/dev-tools-pages/public/css/basscss_responsive_padding.css +++ /dev/null @@ -1,134 +0,0 @@ -/* Basscss Responsive Padding */ -/* Modified by Fabio Berger to include xs prefix */ - -@media (max-width: 52em) { /* Modified by Fabio Berger to max-width from min-width */ - - .sm-p0 { padding: 0 } - .sm-pt0 { padding-top: 0 } - .sm-pr0 { padding-right: 0 } - .sm-pb0 { padding-bottom: 0 } - .sm-pl0 { padding-left: 0 } - .sm-px0 { padding-left: 0; padding-right: 0 } - .sm-py0 { padding-top: 0; padding-bottom: 0 } - - .sm-p1 { padding: .5rem } - .sm-pt1 { padding-top: .5rem } - .sm-pr1 { padding-right: .5rem } - .sm-pb1 { padding-bottom: .5rem } - .sm-pl1 { padding-left: .5rem } - .sm-px1 { padding-left: .5rem; padding-right: .5rem } - .sm-py1 { padding-top: .5rem; padding-bottom: .5rem } - - .sm-p2 { padding: 1rem } - .sm-pt2 { padding-top: 1rem } - .sm-pr2 { padding-right: 1rem } - .sm-pb2 { padding-bottom: 1rem } - .sm-pl2 { padding-left: 1rem } - .sm-px2 { padding-left: 1rem; padding-right: 1rem } - .sm-py2 { padding-top: 1rem; padding-bottom: 1rem } - - .sm-p3 { padding: 2rem } - .sm-pt3 { padding-top: 2rem } - .sm-pr3 { padding-right: 2rem } - .sm-pb3 { padding-bottom: 2rem } - .sm-pl3 { padding-left: 2rem } - .sm-px3 { padding-left: 2rem; padding-right: 2rem } - .sm-py3 { padding-top: 2rem; padding-bottom: 2rem } - - .sm-p4 { padding: 4rem } - .sm-pt4 { padding-top: 4rem } - .sm-pr4 { padding-right: 4rem } - .sm-pb4 { padding-bottom: 4rem } - .sm-pl4 { padding-left: 4rem } - .sm-px4 { padding-left: 4rem; padding-right: 4rem } - .sm-py4 { padding-top: 4rem; padding-bottom: 4rem } - -} - -@media (min-width: 52em) { - - .md-p0 { padding: 0 } - .md-pt0 { padding-top: 0 } - .md-pr0 { padding-right: 0 } - .md-pb0 { padding-bottom: 0 } - .md-pl0 { padding-left: 0 } - .md-px0 { padding-left: 0; padding-right: 0 } - .md-py0 { padding-top: 0; padding-bottom: 0 } - - .md-p1 { padding: .5rem } - .md-pt1 { padding-top: .5rem } - .md-pr1 { padding-right: .5rem } - .md-pb1 { padding-bottom: .5rem } - .md-pl1 { padding-left: .5rem } - .md-px1 { padding-left: .5rem; padding-right: .5rem } - .md-py1 { padding-top: .5rem; padding-bottom: .5rem } - - .md-p2 { padding: 1rem } - .md-pt2 { padding-top: 1rem } - .md-pr2 { padding-right: 1rem } - .md-pb2 { padding-bottom: 1rem } - .md-pl2 { padding-left: 1rem } - .md-px2 { padding-left: 1rem; padding-right: 1rem } - .md-py2 { padding-top: 1rem; padding-bottom: 1rem } - - .md-p3 { padding: 2rem } - .md-pt3 { padding-top: 2rem } - .md-pr3 { padding-right: 2rem } - .md-pb3 { padding-bottom: 2rem } - .md-pl3 { padding-left: 2rem } - .md-px3 { padding-left: 2rem; padding-right: 2rem } - .md-py3 { padding-top: 2rem; padding-bottom: 2rem } - - .md-p4 { padding: 4rem } - .md-pt4 { padding-top: 4rem } - .md-pr4 { padding-right: 4rem } - .md-pb4 { padding-bottom: 4rem } - .md-pl4 { padding-left: 4rem } - .md-px4 { padding-left: 4rem; padding-right: 4rem } - .md-py4 { padding-top: 4rem; padding-bottom: 4rem } - -} - -@media (min-width: 64em) { - - .lg-p0 { padding: 0 } - .lg-pt0 { padding-top: 0 } - .lg-pr0 { padding-right: 0 } - .lg-pb0 { padding-bottom: 0 } - .lg-pl0 { padding-left: 0 } - .lg-px0 { padding-left: 0; padding-right: 0 } - .lg-py0 { padding-top: 0; padding-bottom: 0 } - - .lg-p1 { padding: .5rem } - .lg-pt1 { padding-top: .5rem } - .lg-pr1 { padding-right: .5rem } - .lg-pb1 { padding-bottom: .5rem } - .lg-pl1 { padding-left: .5rem } - .lg-px1 { padding-left: .5rem; padding-right: .5rem } - .lg-py1 { padding-top: .5rem; padding-bottom: .5rem } - - .lg-p2 { padding: 1rem } - .lg-pt2 { padding-top: 1rem } - .lg-pr2 { padding-right: 1rem } - .lg-pb2 { padding-bottom: 1rem } - .lg-pl2 { padding-left: 1rem } - .lg-px2 { padding-left: 1rem; padding-right: 1rem } - .lg-py2 { padding-top: 1rem; padding-bottom: 1rem } - - .lg-p3 { padding: 2rem } - .lg-pt3 { padding-top: 2rem } - .lg-pr3 { padding-right: 2rem } - .lg-pb3 { padding-bottom: 2rem } - .lg-pl3 { padding-left: 2rem } - .lg-px3 { padding-left: 2rem; padding-right: 2rem } - .lg-py3 { padding-top: 2rem; padding-bottom: 2rem } - - .lg-p4 { padding: 4rem } - .lg-pt4 { padding-top: 4rem } - .lg-pr4 { padding-right: 4rem } - .lg-pb4 { padding-bottom: 4rem } - .lg-pl4 { padding-left: 4rem } - .lg-px4 { padding-left: 4rem; padding-right: 4rem } - .lg-py4 { padding-top: 4rem; padding-bottom: 4rem } - -} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css b/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css deleted file mode 100644 index cae23b4e7..000000000 --- a/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css +++ /dev/null @@ -1,35 +0,0 @@ -/* Basscss Responsive Type Scale */ -/* Modified by Fabio Berger to include xs prefix */ - -@media (max-width: 52em) { /* Modified by Fabio Berger to max-width from min-width */ - .sm-h00 { font-size: 4rem } - .sm-h0 { font-size: 3rem } - .sm-h1 { font-size: 2rem } - .sm-h2 { font-size: 1.5rem } - .sm-h3 { font-size: 1.25rem } - .sm-h4 { font-size: 1rem } - .sm-h5 { font-size: .875rem } - .sm-h6 { font-size: .75rem } -} - -@media (min-width: 52em) { - .md-h00 { font-size: 4rem } - .md-h0 { font-size: 3rem } - .md-h1 { font-size: 2rem } - .md-h2 { font-size: 1.5rem } - .md-h3 { font-size: 1.25rem } - .md-h4 { font-size: 1rem } - .md-h5 { font-size: .875rem } - .md-h6 { font-size: .75rem } -} - -@media (min-width: 64em) { - .lg-h00 { font-size: 4rem } - .lg-h0 { font-size: 3rem } - .lg-h1 { font-size: 2rem } - .lg-h2 { font-size: 1.5rem } - .lg-h3 { font-size: 1.25rem } - .lg-h4 { font-size: 1rem } - .lg-h5 { font-size: .875rem } - .lg-h6 { font-size: .75rem } -} diff --git a/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png b/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png Binary files differdeleted file mode 100755 index 68c493c4f..000000000 --- a/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png +++ /dev/null diff --git a/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png b/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png Binary files differdeleted file mode 100755 index a5abb0eb3..000000000 --- a/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png +++ /dev/null diff --git a/packages/dev-tools-pages/public/images/favicon/favicon.ico b/packages/dev-tools-pages/public/images/favicon/favicon.ico Binary files differdeleted file mode 100755 index b7ada2a1c..000000000 --- a/packages/dev-tools-pages/public/images/favicon/favicon.ico +++ /dev/null diff --git a/packages/dev-tools-pages/public/index.html b/packages/dev-tools-pages/public/index.html deleted file mode 100644 index f62d7b255..000000000 --- a/packages/dev-tools-pages/public/index.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <meta name="description" content="" /> - <meta property="og:type" content="website" /> - <meta property="og:title" content="0x" /> - <meta property="og:description" content="" /> - <meta property="og:image" content="/images/og_image.png" /> - <title>0x: The Protocol for Trading Tokens</title> - <link rel="icon" type="image/png" href="/images/favicon/favicon-2-32x32.png" sizes="32x32" /> - <link rel="icon" type="image/png" href="/images/favicon/favicon-2-16x16.png" sizes="16x16" /> - <link rel="stylesheet" href="/css/basscss_responsive_custom.css"> - <link rel="stylesheet" href="/css/basscss_responsive_padding.css"> - <link rel="stylesheet" href="/css/basscss_responsive_margin.css"> - <link rel="stylesheet" href="/css/basscss_responsive_type_scale.css"> -</head> - -<body style="margin: 0px; min-width: 355px;"> - <div id="app"></div> - <script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script> -</body> - -</html>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/components/animations/compiler/data.json b/packages/dev-tools-pages/ts/components/animations/compiler/data.json new file mode 100644 index 000000000..6b10e537b --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/compiler/data.json @@ -0,0 +1 @@ +{"v":"5.4.1","fr":30,"ip":0,"op":420,"w":4300,"h":1400,"nm":"header-compiler","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Path Copy 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[328.5,911.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-61.75,-99.75],[99.75,61.75],[61.75,99.75],[-99.75,-61.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 3","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","parent":16,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-280,-180,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-39.25,87.75],[87.75,-39.25],[39.25,-87.75],[-87.75,39.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1261.5,738.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[15.75,82.75],[82.75,15.75],[-15.75,-82.75],[-82.75,-15.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Path","parent":12,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[370,-209,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[23.25,-23.25],[-23.25,23.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Path","parent":12,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[347,-269,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-60.25,-60.25],[60.25,60.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Path","parent":12,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[330,-250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-37.25,-84.25],[84.25,37.25],[37.25,84.25],[-84.25,-37.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Path Copy","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[449.5,348.5,0],"e":[223.5,574.5,0],"to":[-37.6666679382324,37.6666679382324,0],"ti":[37.6666679382324,-37.6666679382324,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":210,"s":[223.5,574.5,0],"e":[223.5,574.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390.5,"s":[223.5,574.5,0],"e":[449.5,348.5,0],"to":[37.6666679382324,-37.6666679382324,0],"ti":[-37.6666679382324,37.6666679382324,0]},{"t":420}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[9.42,-69.25],[-69.25,9.42],[-9.42,69.25],[69.25,-9.42]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2150,700,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1935,-90],[-1765,-260]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2020,87],[-1946,13]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2020,87],[-1946,13]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2020,87],[-1946,13]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2020,87],[-1946,13]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1935,-90],[-1765,-260]],"c":false}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.678431372549,0.803921628466,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[1,-1],"e":[0,0],"to":[-0.16666667163372,0.16666667163372],"ti":[0.16666667163372,-0.16666667163372]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":210,"s":[0,0],"e":[0,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[0,0],"e":[1,-1],"to":[0.16666667163372,-0.16666667163372],"ti":[-0.16666667163372,0.16666667163372]},{"t":420}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":527,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[2150,700,0],"e":[1920,930,0],"to":[-38.3333320617676,38.3333320617676,0],"ti":[38.3333320617676,-38.3333320617676,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":210,"s":[1920,930,0],"e":[1920,930,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[1920,930,0],"e":[2150,700,0],"to":[38.3333320617676,-38.3333320617676,0],"ti":[-38.3333320617676,38.3333320617676,0]},{"t":420}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1806,-241],[-1748,-183]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1750,-185],[-1748,-183]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1750,-185],[-1748,-183]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1750,-185],[-1748,-183]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1750,-185],[-1748,-183]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1806,-241],[-1748,-183]],"c":false}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.678431372549,0.803921628466,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":527,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Path Copy 2","parent":8,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-134,194,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[28.03,-56.75],[-56.75,28.03],[-28.03,56.75],[56.75,-28.03]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.53,-28.25],[20.5,9.03],[20.72,9.25],[56.75,-28.03]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.53,-28.25],[20.5,9.03],[20.72,9.25],[56.75,-28.03]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.53,-28.25],[20.5,9.03],[20.72,9.25],[56.75,-28.03]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":391,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.53,-28.25],[20.5,9.03],[20.72,9.25],[56.75,-28.03]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[28.03,-56.75],[-56.75,28.03],[-28.03,56.75],[56.75,-28.03]],"c":true}]},{"t":421}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 2","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Big block","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[571.5,588.5,0],"e":[697.5,462.5,0],"to":[21,-21,0],"ti":[-21,21,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"n":"0p667_0p667_0p167_0p167","t":120,"s":[697.5,462.5,0],"e":[697.5,462.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":300,"s":[697.5,462.5,0],"e":[571.5,588.5,0],"to":[-21,21,0],"ti":[21,-21,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[2,-2,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[0.117646998985,0.67843095368,0.803921987496,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[60.75,-182.25],[-182.25,60.75],[-60.75,182.25],[182.25,-60.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Path Copy 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[389.5,770.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-92.25,-29.25],[-92.25,-29.25],[29.25,92.25],[29.25,92.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-29.25,-92.25],[-92.25,-29.25],[29.25,92.25],[92.25,29.25]],"c":true}]},{"t":120}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 4","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Path Copy 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[273,647,0],"e":[317,695,0],"to":[7.33333349227905,8,0],"ti":[-7.33333349227905,-8,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[317,695,0],"e":[317,695,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":300,"s":[317,695,0],"e":[273,647,0],"to":[-7.33333349227905,-8,0],"ti":[7.33333349227905,8,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[30.75,-31.25],[-31.5,31]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 9","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Path Copy 8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[329,832,0],"e":[349,812,0],"to":[3.33333325386047,-3.33333325386047,0],"ti":[-3.33333325386047,3.33333325386047,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[349,812,0],"e":[349,812,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":300,"s":[349,812,0],"e":[329,832,0],"to":[-3.33333325386047,3.33333325386047,0],"ti":[3.33333325386047,-3.33333325386047,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-61,-61],[61,61]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 8","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"wide box","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[971.5,988.5,0],"e":[1096.5,864.5,0],"to":[10.0566339492798,-9.97618103027344,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[1096.5,864.5,0],"e":[1096.5,864.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":300,"s":[1096.5,864.5,0],"e":[971.5,988.5,0],"to":[0,0,0],"ti":[-20.8333339691162,20.6666660308838,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[-1,1,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-129.25,-52.25],[-52.25,-129.25],[129.25,52.25],[52.25,129.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[993,766,0],"e":[1071,688,0],"to":[13,-13,0],"ti":[-13,13,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[1071,688,0],"e":[1071,688,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":300,"s":[1071,688,0],"e":[993,766,0],"to":[-13,13,0],"ti":[13,-13,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-89.5,-89.5],[89.5,89.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[1115,850,0],"e":[1311.5,799.5,0],"to":[18.5410747528076,-4.90381097793579,0],"ti":[-5.73690891265869,1.51731848716736,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[1311.5,799.5,0],"e":[1311.5,799.5,0],"to":[33.0833320617676,-8.75,0],"ti":[5.73690891265869,-1.51731848716736,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":300,"s":[1311.5,799.5,0],"e":[1115,850,0],"to":[-5.73690891265869,1.51731848716736,0],"ti":[-33.0833320617676,8.75,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[103.689,103.689,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[30.5,-30.5],[-30.5,30.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1011.5,748.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[120.75,57.75],[58.25,120.25],[-120.25,-58.25],[-57.75,-120.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[120.514,57.986],[119.427,59.073],[-59.073,-119.427],[-57.986,-120.514]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":120,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[120.514,57.986],[119.427,59.073],[-59.073,-119.427],[-57.986,-120.514]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[120.514,57.986],[119.427,59.073],[-59.073,-119.427],[-57.986,-120.514]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p833_1_0p167_0p167","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[120.514,57.986],[119.427,59.073],[-59.073,-119.427],[-57.986,-120.514]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[120.75,57.75],[58.25,120.25],[-120.25,-58.25],[-57.75,-120.75]],"c":true}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3620,905,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[33,163],[-163,-33],[-33,-163],[163,33]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3274,751,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[86,-140],[140,-86],[-86,140],[-140,86]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3770,475,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-2,-162],[162,2],[2,162],[-162,-2]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3465,340,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[80,-24],[24,-80],[-82,26],[-26,82]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[3420.5,966.5,0],"e":[3389.5,996.5,0],"to":[-5.16666650772095,5,0],"ti":[5.16666650772095,-5,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"n":"0p667_0p667_0p167_0p167","t":120,"s":[3389.5,996.5,0],"e":[3389.5,996.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[3389.5,996.5,0],"e":[3420.5,966.5,0],"to":[5.16666650772095,-5,0],"ti":[-5.16666650772095,5,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-62.75,-62.75],[-62.25,-62.25]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-62.75,-62.75],[62.75,62.75]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"n":"0p667_1_0p167_0","t":120,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-62.75,-62.75],[62.75,62.75]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-62.75,-62.75],[62.75,62.75]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-62.75,-62.75],[62.75,62.75]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-62.75,-62.75],[-62.25,-62.25]],"c":false}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[3219.5,915.5,0],"e":[3339.5,1035.5,0],"to":[20,20,0],"ti":[-20,-20,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"n":"0p667_0p667_0p167_0p167","t":120,"s":[3339.5,1035.5,0],"e":[3339.5,1035.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[3339.5,1035.5,0],"e":[3219.5,915.5,0],"to":[-20,-20,0],"ti":[20,20,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[38.25,-38.25],[38.25,-38.25]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[38.25,-38.25],[-38.25,38.25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"n":"0p667_1_0p167_0","t":120,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[38.25,-38.25],[-38.25,38.25]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[38.25,-38.25],[-38.25,38.25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[38.25,-38.25],[-38.25,38.25]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[38.25,-38.25],[38.25,-38.25]],"c":false}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3344,1041,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[42,42],[0,0],[0,0],[0,0]],"v":[[-25,-101],[-24,-100],[-24.5,-99.5],[-25.5,-100.5]],"c":false}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-25,-101],[101,25],[25,101],[-101,-25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"n":"0p667_1_0p167_0","t":120,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-25,-101],[101,25],[25,101],[-101,-25]],"c":false}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-25,-101],[101,25],[25,101],[-101,-25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-25,-101],[101,25],[25,101],[-101,-25]],"c":false}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[42,42],[0,0],[0,0],[0,0]],"v":[[-25,-101],[-24,-100],[-24.5,-99.5],[-25.5,-100.5]],"c":false}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[3142.5,483.5,0],"e":[3266.5,359.5,0],"to":[20.6666660308838,-20.6666660308838,0],"ti":[-20.6666660308838,20.6666660308838,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[3266.5,359.5,0],"e":[3266.5,359.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[3266.5,359.5,0],"e":[3142.5,483.5,0],"to":[-20.6666660308838,20.6666660308838,0],"ti":[20.6666660308838,-20.6666660308838,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-73.25,-73.25],[73.25,73.25]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-33.25,-33.25],[73.25,73.25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-33.25,-33.25],[73.25,73.25]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-33.25,-33.25],[73.25,73.25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-33.25,-33.25],[73.25,73.25]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-73.25,-73.25],[73.25,73.25]],"c":false}]},{"t":329.5}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[3163,420,0],"e":[3327,588,0],"to":[27.3333339691162,28,0],"ti":[-27.3333339691162,-28,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[3327,588,0],"e":[3327,588,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[3327,588,0],"e":[3163,420,0],"to":[-27.3333339691162,-28,0],"ti":[27.3333339691162,28,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[42,-42],[-42,42]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[42,-42],[42.312,-42.125]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[42,-42],[42.312,-42.125]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[42,-42],[42.312,-42.125]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[42,-42],[42.312,-42.125]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[42,-42],[-42,42]],"c":false}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3184,441,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[31,115],[-115,-31],[-31,-115],[115,31]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[113.5,31.5],[8,-74],[8,-74],[113.5,31.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[113.5,31.5],[8,-74],[8,-74],[113.5,31.5]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[113.5,31.5],[8,-74],[8,-74],[113.5,31.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[113.5,31.5],[8,-74],[8,-74],[113.5,31.5]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[31,115],[-115,-31],[-31,-115],[115,31]],"c":true}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[2954,751,0],"e":[3120,585,0],"to":[27.6666660308838,-27.6666660308838,0],"ti":[-27.6666660308838,27.6666660308838,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[3120,585,0],"e":[3120,585,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[3120,585,0],"e":[2954,751,0],"to":[-27.6666660308838,27.6666660308838,0],"ti":[27.6666660308838,-27.6666660308838,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[146,-40],[40,-146],[-146,40],[-40,146]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117646999657,0.678430974483,0.803921997547,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":188,"s":[2046,804,0],"e":[2150,700,0],"to":[17.3333339691162,-17.3333339691162,0],"ti":[-17.3333339691162,17.3333339691162,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":210,"s":[2150,700,0],"e":[2150,700,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[2150,700,0],"e":[2046,804,0],"to":[-17.3333339691162,17.3333339691162,0],"ti":[17.3333339691162,-17.3333339691162,0]},{"t":410}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1727,-6],[1858,125]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1727,-6],[1968,235]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1727,-6],[1968,235]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1727,-6],[1968,235]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1727,-6],[1968,235]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1727,-6],[1858,125]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.678431372549,0.803921628466,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":527,"st":0,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2120,670,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1684,158],[1685,157]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1746,220],[1963,3]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1746,220],[1963,3]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1746,220],[1963,3]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1746,220],[1963,3]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1684,158],[1685,157]],"c":false}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.678431372549,0.803921628466,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":527,"st":0,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3138,417,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-2516,745]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.678431372549,0.803921628466,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[1,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[636,382],[636,382],[766,512],[766,512]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[854,164],[636,382],[876,622],[1094,404]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[854,164],[636,382],[876,622],[1094,404]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[854,164],[636,382],[876,622],[1094,404]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[854,164],[636,382],[876,622],[1094,404]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[636,382],[636,382],[766,512],[766,512]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.678431372549,0.803921628466,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":527,"st":0,"bm":0}],"markers":[{"tm":90,"cm":"1","dr":0},{"tm":120,"cm":"2","dr":0},{"tm":180,"cm":"3","dr":0},{"tm":210,"cm":"4","dr":0},{"tm":300,"cm":"5","dr":0},{"tm":330,"cm":"6","dr":0},{"tm":390,"cm":"7","dr":0},{"tm":420,"cm":"8","dr":0}]}
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/components/animations/compiler/index.tsx b/packages/dev-tools-pages/ts/components/animations/compiler/index.tsx new file mode 100644 index 000000000..ba98f8da3 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/compiler/index.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +import { BaseAnimation } from '../index'; + +import * as animationData from './data.json'; + +const CompilerAnimation: React.StatelessComponent<{}> = () => ( + <BaseAnimation animationData={animationData} width={2150} height={700} /> +); + +export { CompilerAnimation }; diff --git a/packages/dev-tools-pages/ts/components/animations/cov/data.json b/packages/dev-tools-pages/ts/components/animations/cov/data.json new file mode 100644 index 000000000..a259c2787 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/cov/data.json @@ -0,0 +1 @@ +{"v":"5.4.1","fr":30,"ip":0,"op":420,"w":3962,"h":1320,"nm":"header-cov","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3418.5,924.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.696,"y":1},"o":{"x":0.304,"y":0},"n":"0p696_1_0p304_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[31.25,66.25],[66.25,31.25],[-31.25,-66.25],[-66.25,-31.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[21.25,86.25],[71.25,36.25],[-31.25,-66.25],[-81.25,-16.25]],"c":true}]},{"i":{"x":0.696,"y":1},"o":{"x":0.333,"y":0},"n":"0p696_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[21.25,86.25],[71.25,36.25],[-31.25,-66.25],[-81.25,-16.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[21.25,86.25],[71.25,36.25],[-31.25,-66.25],[-81.25,-16.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[21.25,86.25],[71.25,36.25],[-31.25,-66.25],[-81.25,-16.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[31.25,66.25],[66.25,31.25],[-31.25,-66.25],[-66.25,-31.25]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3392.5,970.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[18.25,89.25],[89.25,18.25],[-18.25,-89.25],[-89.25,-18.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2958,886,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[64,-94],[94,-64],[-64,94],[-94,64]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[80,-126],[118,-88],[-64,94],[-102,56]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[80,-126],[118,-88],[-64,94],[-102,56]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[80,-126],[118,-88],[-64,94],[-102,56]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[80,-126],[118,-88],[-64,94],[-102,56]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[64,-94],[94,-64],[-64,94],[-94,64]],"c":true}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2988,816,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[79,-129],[129,-79],[-79,129],[-129,79]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3588,678,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-3784,-472]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.733332974303,0.572548959769,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"n":"0p833_1_0p167_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-18.5,-75],[75,18.5],[18.5,75],[-75,-18.5]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-26.5,-27],[47,46.5],[18.5,75],[-55,1.5]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-26.5,-27],[47,46.5],[18.5,75],[-55,1.5]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-26.5,-27],[47,46.5],[18.5,75],[-55,1.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-26.5,-27],[47,46.5],[18.5,75],[-55,1.5]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-18.5,-75],[75,18.5],[18.5,75],[-75,-18.5]],"c":true}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3678,548,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[26.5,-140],[140,-26.5],[-26.5,140],[-140,26.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3650.5,150.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-11.75,59.25],[59.25,-11.75],[11.75,-59.25],[-59.25,11.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3272.5,260.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[31.25,66.25],[66.25,31.25],[-31.25,-66.25],[-66.25,-31.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[27.25,34.25],[48.25,13.25],[-31.25,-66.25],[-52.25,-45.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":211,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[27.25,34.25],[48.25,13.25],[-31.25,-66.25],[-52.25,-45.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[27.25,34.25],[48.25,13.25],[-31.25,-66.25],[-52.25,-45.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[27.25,34.25],[48.25,13.25],[-31.25,-66.25],[-52.25,-45.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[31.25,66.25],[66.25,31.25],[-31.25,-66.25],[-66.25,-31.25]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3246.5,306.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[18.25,89.25],[89.25,18.25],[-18.25,-89.25],[-89.25,-18.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2758.5,720.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-33.25,11.75],[-11.75,33.25],[33.25,-11.75],[11.75,-33.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2729.5,707.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-47.75,5.25],[-5.25,47.75],[47.75,-5.25],[5.25,-47.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1285.5,815.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36.75,-8.25],[-8.25,36.75],[-36.75,8.25],[8.25,-36.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[94.75,37.75],[43.75,88.75],[-36.75,8.25],[14.25,-42.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":211,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[94.75,37.75],[43.75,88.75],[-36.75,8.25],[14.25,-42.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[94.75,37.75],[43.75,88.75],[-36.75,8.25],[14.25,-42.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[94.75,37.75],[43.75,88.75],[-36.75,8.25],[14.25,-42.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36.75,-8.25],[-8.25,36.75],[-36.75,8.25],[8.25,-36.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1415.5,905.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[101.75,36.75],[36.75,101.75],[-101.75,-36.75],[-36.75,-101.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[611,329,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[62.5,-95.5],[95.5,-62.5],[-62.5,95.5],[-95.5,62.5]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[43,-47],[61.5,-28.5],[-62.5,95.5],[-81,77]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[43,-47],[61.5,-28.5],[-62.5,95.5],[-81,77]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[43,-47],[61.5,-28.5],[-62.5,95.5],[-81,77]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[43,-47],[61.5,-28.5],[-62.5,95.5],[-81,77]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[62.5,-95.5],[95.5,-62.5],[-62.5,95.5],[-95.5,62.5]],"c":true}]},{"t":329}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[644,262,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[79,-129],[129,-79],[-79,129],[-129,79]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1126.5,374.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[39.25,16.25],[16.25,39.25],[-39.25,-16.25],[-16.25,-39.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1186.5,414.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[69.25,36.25],[36.25,69.25],[-69.25,-36.25],[-36.25,-69.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[375,944,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-36,19.5],[-0.5,-16],[166,150.5],[130.5,186]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10,45.5],[13.5,22],[154,162.5],[130.5,186]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10,45.5],[13.5,22],[154,162.5],[130.5,186]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10,45.5],[13.5,22],[154,162.5],[130.5,186]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-10,45.5],[13.5,22],[154,162.5],[130.5,186]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-36,19.5],[-0.5,-16],[166,150.5],[130.5,186]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[375,944,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-186,-130.5],[-130.5,-186],[186,130.5],[130.5,186]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[815.5,863.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[39.75,-93.75],[93.75,-39.75],[-39.75,93.75],[-93.75,39.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[33.25,-145.25],[116.25,-62.25],[-39.75,93.75],[-122.75,10.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[33.25,-145.25],[116.25,-62.25],[-39.75,93.75],[-122.75,10.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[33.25,-145.25],[116.25,-62.25],[-39.75,93.75],[-122.75,10.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[33.25,-145.25],[116.25,-62.25],[-39.75,93.75],[-122.75,10.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[39.75,-93.75],[93.75,-39.75],[-39.75,93.75],[-93.75,39.75]],"c":true}]},{"t":329}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[777.5,721.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[20.75,-164.75],[164.75,-20.75],[-20.75,164.75],[-164.75,20.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.7333329916,0.572548985481,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":524.5,"st":0,"bm":0}],"markers":[{"tm":90,"cm":"1","dr":0},{"tm":120,"cm":"2","dr":0},{"tm":180,"cm":"3","dr":0},{"tm":210,"cm":"4","dr":0},{"tm":300,"cm":"5","dr":0},{"tm":330,"cm":"6","dr":0},{"tm":390,"cm":"7","dr":0},{"tm":420,"cm":"8","dr":0}]}
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/components/animations/cov/index.tsx b/packages/dev-tools-pages/ts/components/animations/cov/index.tsx new file mode 100644 index 000000000..bd872cc85 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/cov/index.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +import { BaseAnimation } from '../index'; + +import * as animationData from './data.json'; + +const CovAnimation: React.StatelessComponent<{}> = () => ( + <BaseAnimation animationData={animationData} width={1981} height={660} /> +); + +export { CovAnimation }; diff --git a/packages/dev-tools-pages/ts/components/animations/index.tsx b/packages/dev-tools-pages/ts/components/animations/index.tsx new file mode 100644 index 000000000..8e5421f5c --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/index.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import Lottie from 'react-lottie'; +import styled from 'styled-components'; + +import { media } from 'ts/variables'; + +interface AnimationProps { + animationData: object; + width: number; + height: number; +} + +interface AnimationState { + width?: number | undefined; + height?: number | undefined; +} + +class BaseAnimation extends React.PureComponent<AnimationProps, AnimationState> { + public state: AnimationState = { + height: undefined, + width: undefined, + }; + private _timeout = undefined as number; + public componentDidMount(): void { + this._updateAnimationSize(); + window.addEventListener('resize', this._handleResize.bind(this)); + } + public componentWillUnmount(): void { + window.removeEventListener('resize', this._handleResize.bind(this)); + } + public render(): React.ReactNode { + const { animationData } = this.props; + const height = this.state.height || this.props.height; + const width = this.state.width || this.props.width; + + return ( + <Container height={height}> + <InnerContainer> + <Lottie + width={width} + height={height} + options={{ + loop: true, + autoplay: true, + animationData, + }} + /> + </InnerContainer> + </Container> + ); + } + private _handleResize(): void { + clearTimeout(this._timeout); + this._timeout = window.setTimeout(this._updateAnimationSize.bind(this), 50); + } + private _updateAnimationSize(): void { + const windowWidth = window.innerWidth; + let width; + let height; + if (windowWidth <= 1000) { + const maxWidth = windowWidth + 250; + const ratio = maxWidth / this.props.width; + + height = Math.round(this.props.height * ratio); + width = Math.round(this.props.width * ratio); + } + + this.setState({ width, height }); + } +} + +const Container = + styled.div < + AnimationProps > + ` + width: 100%; + height: ${props => props.height}px; + position: absolute; + top: 40%; + left: 0; + z-index: -1; + overflow: hidden; + ${media.large` + top: 100%; + transform: translateY(-50%); + `}; +`; + +const InnerContainer = styled.div` + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); +`; + +export { BaseAnimation }; diff --git a/packages/dev-tools-pages/ts/components/animations/profiler/data.json b/packages/dev-tools-pages/ts/components/animations/profiler/data.json new file mode 100644 index 000000000..2d769bc50 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/profiler/data.json @@ -0,0 +1 @@ +{"v":"5.4.1","fr":30,"ip":0,"op":420,"w":3970,"h":1314,"nm":"header-profiler","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Path Copy","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":90,"s":[376,448,0],"e":[376,392,0],"to":[0,-9.33333301544189,0],"ti":[0,9.33333301544189,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[376,392,0],"e":[376,392,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[376,392,0],"e":[376,448,0],"to":[0,9.33333301544189,0],"ti":[0,-9.33333301544189,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-169.69,0],[134.5,0]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.19,0],[106.5,0]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.19,0],[106.5,0]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.19,0],[106.5,0]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-142.19,0],[106.5,0]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-169.69,0],[134.5,0]],"c":false}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3466.5,998.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[45.75,155.75],[-155.75,-45.75],[-45.75,-155.75],[155.75,45.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[1090.5,537,0],"e":[1170.5,617,0],"to":[12.0437898635864,12.0437898635864,0],"ti":[-1.2895439863205,-1.2895439863205,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[1170.5,617,0],"e":[1170.5,617,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[1170.5,617,0],"e":[1090.5,537,0],"to":[-13.3333330154419,-13.3333330154419,0],"ti":[13.3333330154419,13.3333330154419,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-41.25,-0.5],[41.25,-0.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[834,765,0],"e":[834,813,0],"to":[0,8,0],"ti":[0,-8,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"n":"0p833_0p833_0p333_0p333","t":210,"s":[834,813,0],"e":[834,813,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[834,813,0],"e":[834,765,0],"to":[0,-8,0],"ti":[0,8,0]},{"t":419}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.999},"o":{"x":0.167,"y":0.001},"n":"0p833_0p999_0p167_0p001","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-100,-0.5],[100,-0.5]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-122,-0.5],[125,-0.5]],"c":true}]},{"i":{"x":0.833,"y":0.999},"o":{"x":0.333,"y":0},"n":"0p833_0p999_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-122,-0.5],[125,-0.5]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-122,-0.5],[125,-0.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-122,-0.5],[125,-0.5]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-100,-0.5],[100,-0.5]],"c":false}]},{"t":419}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[748.5,387,0],"e":[748.5,347,0],"to":[0,-6.66666650772095,0],"ti":[0,6.66666650772095,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"n":"0p833_0p833_0p333_0p333","t":210,"s":[748.5,347,0],"e":[748.5,347,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[748.5,347,0],"e":[748.5,387,0],"to":[0,6.66666650772095,0],"ti":[0,-6.66666650772095,0]},{"t":419}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.999},"o":{"x":0.167,"y":0.001},"n":"0p833_0p999_0p167_0p001","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-36.25,-0.5],[36.25,-0.5]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.75,-3],[58.75,-3]],"c":true}]},{"i":{"x":0.833,"y":0.999},"o":{"x":0.333,"y":0},"n":"0p833_0p999_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.75,-3],[58.75,-3]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.75,-3],[58.75,-3]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.75,-3],[58.75,-3]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-36.25,-0.5],[36.25,-0.5]],"c":false}]},{"t":419}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[3006.5,763,0],"e":[3006.5,823,0],"to":[0,10,0],"ti":[0,-10,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"n":"0p833_0p833_0p333_0p333","t":210,"s":[3006.5,823,0],"e":[3006.5,823,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[3006.5,823,0],"e":[3006.5,763,0],"to":[0,-10,0],"ti":[0,10,0]},{"t":419}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.999},"o":{"x":0.167,"y":0},"n":"0p833_0p999_0p167_0","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-66.25,-0.5],[66.25,-0.5]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-36.25,-0.5],[36.25,-0.5]],"c":true}]},{"i":{"x":0.833,"y":0.999},"o":{"x":0.333,"y":0},"n":"0p833_0p999_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-36.25,-0.5],[36.25,-0.5]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-36.25,-0.5],[36.25,-0.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-36.25,-0.5],[36.25,-0.5]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-66.25,-0.5],[66.25,-0.5]],"c":false}]},{"t":419}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":180,"s":[3375.5,857,0],"e":[3375.5,817,0],"to":[0,-6.66666650772095,0],"ti":[0,6.66666650772095,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0.333},"n":"0p833_0p833_0p333_0p333","t":210,"s":[3375.5,817,0],"e":[3375.5,817,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[3375.5,817,0],"e":[3375.5,857,0],"to":[0,6.66666650772095,0],"ti":[0,-6.66666650772095,0]},{"t":419}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.999},"o":{"x":0.167,"y":0.001},"n":"0p833_0p999_0p167_0p001","t":180,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-84.25,-0.5],[84.25,-0.5]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-63.75,-0.5],[64.25,-0.5]],"c":true}]},{"i":{"x":0.833,"y":0.999},"o":{"x":0.333,"y":0},"n":"0p833_0p999_0p333_0","t":210,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-63.75,-0.5],[64.25,-0.5]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-63.75,-0.5],[64.25,-0.5]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-63.75,-0.5],[64.25,-0.5]],"c":true}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-84.25,-0.5],[84.25,-0.5]],"c":false}]},{"t":419}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-63.75,-0.5],[64.25,-0.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":90,"s":[3511,409,0],"e":[3491,289,0],"to":[-3.33333325386047,-20,0],"ti":[3.33333325386047,20,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[3491,289,0],"e":[3491,289,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[3491,289,0],"e":[3511,409,0],"to":[3.33333325386047,20,0],"ti":[-3.33333325386047,-20,0]},{"t":330}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-51.5,-0.5],[50.5,-0.5]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.5,-0.5],[120.5,-0.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":120,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.5,-0.5],[120.5,-0.5]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.5,-0.5],[120.5,-0.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-101.5,-0.5],[120.5,-0.5]],"c":false}],"e":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-51.5,-0.5],[50.5,-0.5]],"c":false}]},{"t":330}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Path Copy 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[3718.5,576,0],"e":[3598.5,696,0],"to":[-20,20,0],"ti":[20,-20,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.333,"y":0.333},"n":"0p667_0p667_0p333_0p333","t":120,"s":[3598.5,696,0],"e":[3598.5,696,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[3598.5,696,0],"e":[3718.5,576,0],"to":[20,-20,0],"ti":[-20,20,0]},{"t":332}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-39.25,0],[39.25,0]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 2","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3738,557,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[114.5,-75],[75,-114.5],[-114.5,75],[-75,114.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3502,257,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[127,4.5],[4.5,127],[-127,-4.5],[-4.5,-127]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2653.5,703.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-92.25,55.75],[-55.75,92.25],[92.25,-55.75],[55.75,-92.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3087.5,645.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[40.25,-124.75],[124.75,-40.25],[-40.25,124.75],[-124.75,40.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1728,878,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[66,15],[15,66],[-66,-15],[-15,-66]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1200,646,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-95.5,-137],[-137,-95.5],[95.5,137],[137,95.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[356.5,496.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-7.75,-176.75],[-176.75,-7.75],[7.75,176.75],[176.75,7.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[821.5,268.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36.25,-95.25],[95.25,-36.25],[-36.25,95.25],[-95.25,36.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[634.5,1150.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-8.75,68.25],[68.25,-8.75],[8.75,-68.25],[-68.25,8.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Path","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[858,834,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[11,135],[-135,-11],[-11,-135],[135,11]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.44313699007,0.2666670084,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0}],"markers":[{"tm":90,"cm":"1","dr":0},{"tm":120,"cm":"2","dr":0},{"tm":180,"cm":"3","dr":0},{"tm":210,"cm":"4","dr":0},{"tm":300,"cm":"5","dr":0},{"tm":330,"cm":"6","dr":0},{"tm":390,"cm":"7","dr":0},{"tm":419,"cm":"8","dr":0}]}
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/components/animations/profiler/index.tsx b/packages/dev-tools-pages/ts/components/animations/profiler/index.tsx new file mode 100644 index 000000000..a2848b762 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/profiler/index.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +import { BaseAnimation } from '../index'; + +import * as animationData from './data.json'; + +const ProfilerAnimation: React.StatelessComponent<{}> = () => ( + <BaseAnimation animationData={animationData} width={1985} height={657} /> +); + +export { ProfilerAnimation }; diff --git a/packages/dev-tools-pages/ts/components/animations/trace/data.json b/packages/dev-tools-pages/ts/components/animations/trace/data.json new file mode 100644 index 000000000..7b2936fff --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/trace/data.json @@ -0,0 +1 @@ +{"v":"5.4.1","fr":30,"ip":0,"op":420,"w":4482,"h":1220,"nm":"header-trace","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2241,610,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":0,"op":529,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"▽ header-trace","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2242,609,0],"ix":2},"a":{"a":0,"k":[2238,607,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3391.5,737.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[101.75,-11.75],[-11.75,101.75],[-101.75,11.75],[11.75,-101.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Path 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[4035.5,479.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-221.75,-158.25],[-158.25,-221.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[201.75,178.25],[158.25,221.75],[-221.75,-158.25],[-178.25,-201.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[201.75,178.25],[158.25,221.75],[-221.75,-158.25],[-178.25,-201.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[201.75,178.25],[158.25,221.75],[-221.75,-158.25],[-178.25,-201.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[201.75,178.25],[158.25,221.75],[-221.75,-158.25],[-178.25,-201.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-221.75,-158.25],[-158.25,-221.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Path 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[4035.5,479.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-221.75,-158.25],[-158.25,-221.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[211.75,168.25],[158.25,221.75],[-211.75,-148.25],[-158.25,-201.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[211.75,168.25],[158.25,221.75],[-211.75,-148.25],[-158.25,-201.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[211.75,168.25],[158.25,221.75],[-211.75,-148.25],[-158.25,-201.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"n":"0p667_1_0p167_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[211.75,168.25],[158.25,221.75],[-211.75,-148.25],[-158.25,-201.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-221.75,-158.25],[-158.25,-221.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Path 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[4035.5,479.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-221.75,-158.25],[-158.25,-221.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-201.75,-138.25],[-138.25,-201.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-201.75,-138.25],[-138.25,-201.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-201.75,-138.25],[-138.25,-201.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-201.75,-138.25],[-138.25,-201.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-221.75,-158.25],[-158.25,-221.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[4031.5,477.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[221.75,158.25],[158.25,221.75],[-221.75,-158.25],[-158.25,-221.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3671.5,517.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[21.75,158.25],[158.25,21.75],[-21.75,-158.25],[-158.25,-21.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Path Copy 12","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3803.5,646.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[93.75,-43.75],[-43.75,93.75],[-73.75,63.75],[63.75,-73.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-63.75],[-63.75,73.75],[-73.75,63.75],[63.75,-73.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-63.75],[-63.75,73.75],[-73.75,63.75],[63.75,-73.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-63.75],[-63.75,73.75],[-73.75,63.75],[63.75,-73.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-63.75],[-63.75,73.75],[-73.75,63.75],[63.75,-73.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[93.75,-43.75],[-43.75,93.75],[-73.75,63.75],[63.75,-73.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 12","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Path Copy 11","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3803.5,666.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[93.75,-53.75],[-43.75,83.75],[-73.75,53.75],[63.75,-83.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-53.75],[-53.75,73.75],[-73.75,53.75],[53.75,-73.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-53.75],[-53.75,73.75],[-73.75,53.75],[53.75,-73.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-53.75],[-53.75,73.75],[-73.75,53.75],[53.75,-73.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-53.75],[-53.75,73.75],[-73.75,53.75],[53.75,-73.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[93.75,-53.75],[-43.75,83.75],[-73.75,53.75],[63.75,-83.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 11","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Path Copy 10","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3803.5,686.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[93.75,-63.75],[-43.75,73.75],[-73.75,43.75],[63.75,-93.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-43.75],[-43.75,73.75],[-73.75,43.75],[43.75,-73.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-43.75],[-43.75,73.75],[-73.75,43.75],[43.75,-73.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-43.75],[-43.75,73.75],[-73.75,43.75],[43.75,-73.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[73.75,-43.75],[-43.75,73.75],[-73.75,43.75],[43.75,-73.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[93.75,-63.75],[-43.75,73.75],[-73.75,43.75],[63.75,-93.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 10","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3101.5,747.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[136.75,-126.75],[-126.75,136.75],[-136.75,126.75],[126.75,-136.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-106.75],[-106.75,156.75],[-136.75,126.75],[126.75,-136.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-106.75],[-106.75,156.75],[-136.75,126.75],[126.75,-136.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-106.75],[-106.75,156.75],[-136.75,126.75],[126.75,-136.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-106.75],[-106.75,156.75],[-136.75,126.75],[126.75,-136.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[136.75,-126.75],[-126.75,136.75],[-136.75,126.75],[126.75,-136.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3101.5,767.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[136.75,-116.75],[-116.75,136.75],[-136.75,116.75],[116.75,-136.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[146.75,-106.75],[-106.75,146.75],[-136.75,116.75],[116.75,-136.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[146.75,-106.75],[-106.75,146.75],[-136.75,116.75],[116.75,-136.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[146.75,-106.75],[-106.75,146.75],[-136.75,116.75],[116.75,-136.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[146.75,-106.75],[-106.75,146.75],[-136.75,116.75],[116.75,-136.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[136.75,-116.75],[-116.75,136.75],[-136.75,116.75],[116.75,-136.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3101.5,787.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[136.75,-106.75],[-106.75,136.75],[-136.75,106.75],[106.75,-136.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-126.75],[-106.75,136.75],[-136.75,106.75],[126.75,-156.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-126.75],[-106.75,136.75],[-136.75,106.75],[126.75,-156.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-126.75],[-106.75,136.75],[-136.75,106.75],[126.75,-156.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,-126.75],[-106.75,136.75],[-136.75,106.75],[126.75,-156.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[136.75,-106.75],[-106.75,136.75],[-136.75,106.75],[106.75,-136.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3121.5,767.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[146.75,-116.75],[-116.75,146.75],[-146.75,116.75],[116.75,-146.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2881.5,567.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.998},"o":{"x":0.167,"y":0.001},"n":"0p833_0p998_0p167_0p001","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[66.75,-16.75],[-16.75,66.75],[-66.75,16.75],[16.75,-66.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[86.75,3.25],[3.25,86.75],[-66.75,16.75],[16.75,-66.75]],"c":true}]},{"i":{"x":0.833,"y":0.998},"o":{"x":0.333,"y":0},"n":"0p833_0p998_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[86.75,3.25],[3.25,86.75],[-66.75,16.75],[16.75,-66.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[86.75,3.25],[3.25,86.75],[-66.75,16.75],[16.75,-66.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[86.75,3.25],[3.25,86.75],[-66.75,16.75],[16.75,-66.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[66.75,-16.75],[-16.75,66.75],[-66.75,16.75],[16.75,-66.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2911.5,577.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.999},"o":{"x":0.167,"y":0.002},"n":"0p833_0p999_0p167_0p002","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[61.75,-11.75],[-11.75,61.75],[-61.75,11.75],[11.75,-61.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[71.75,-1.75],[-11.75,81.75],[-71.75,21.75],[11.75,-61.75]],"c":true}]},{"i":{"x":0.833,"y":0.999},"o":{"x":0.333,"y":0},"n":"0p833_0p999_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[71.75,-1.75],[-11.75,81.75],[-71.75,21.75],[11.75,-61.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[71.75,-1.75],[-11.75,81.75],[-71.75,21.75],[11.75,-61.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[71.75,-1.75],[-11.75,81.75],[-71.75,21.75],[11.75,-61.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[61.75,-11.75],[-11.75,61.75],[-61.75,11.75],[11.75,-61.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2941.5,587.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.998},"o":{"x":0.167,"y":0.002},"n":"0p833_0p998_0p167_0p002","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.75,-6.75],[-6.75,56.75],[-56.75,6.75],[6.75,-56.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.75,-6.75],[-26.75,76.75],[-76.75,26.75],[6.75,-56.75]],"c":true}]},{"i":{"x":0.833,"y":0.998},"o":{"x":0.333,"y":0},"n":"0p833_0p998_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.75,-6.75],[-26.75,76.75],[-76.75,26.75],[6.75,-56.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.75,-6.75],[-26.75,76.75],[-76.75,26.75],[6.75,-56.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.75,-6.75],[-26.75,76.75],[-76.75,26.75],[6.75,-56.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[56.75,-6.75],[-6.75,56.75],[-56.75,6.75],[6.75,-56.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[2901.5,587.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[76.75,-6.75],[-6.75,76.75],[-76.75,6.75],[6.75,-76.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3411.5,177.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-88.25,-31.75],[-31.75,-88.25],[88.25,31.75],[31.75,88.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3431.5,157.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-78.25,-41.75],[-41.75,-78.25],[78.25,41.75],[41.75,78.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3411.5,157.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-78.25,-31.75],[-31.75,-78.25],[78.25,31.75],[31.75,78.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3391.5,157.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-78.25,-21.75],[-21.75,-78.25],[78.25,21.75],[21.75,78.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[3101.5,627.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[136.75,-186.75],[-186.75,136.75],[-136.75,186.75],[186.75,-136.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1444.5,626.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36.75,63.25],[63.25,36.75],[-36.75,-63.25],[-63.25,-36.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"Path 4","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[704.5,527.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-16.75,-183.25],[183.25,16.75],[16.75,183.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1064.5,887.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-3.25,163.25],[163.25,-3.25],[3.25,-163.25],[-163.25,3.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"Path Copy 4","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1594.5,776.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[101.75,-51.75],[-51.75,101.75],[-101.75,51.75],[51.75,-101.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[121.75,-31.75],[-31.75,121.75],[-101.75,51.75],[51.75,-101.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[121.75,-31.75],[-31.75,121.75],[-101.75,51.75],[51.75,-101.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[121.75,-31.75],[-31.75,121.75],[-101.75,51.75],[51.75,-101.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"n":"0p667_1_0p167_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[121.75,-31.75],[-31.75,121.75],[-101.75,51.75],[51.75,-101.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[101.75,-51.75],[-51.75,101.75],[-101.75,51.75],[51.75,-101.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 4","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"Path Copy 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1624.5,787.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[96.75,-46.75],[-46.75,96.75],[-96.75,46.75],[46.75,-96.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[106.75,-36.75],[-46.75,116.75],[-116.75,46.75],[36.75,-106.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[106.75,-36.75],[-46.75,116.75],[-116.75,46.75],[36.75,-106.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[106.75,-36.75],[-46.75,116.75],[-116.75,46.75],[36.75,-106.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"n":"0p667_1_0p167_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[106.75,-36.75],[-46.75,116.75],[-116.75,46.75],[36.75,-106.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[96.75,-46.75],[-46.75,96.75],[-96.75,46.75],[46.75,-96.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 3","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":"Path Copy 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1654.5,797.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.75,-41.75],[-41.75,91.75],[-91.75,41.75],[41.75,-91.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.75,-41.75],[-61.75,111.75],[-131.75,41.75],[21.75,-111.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.75,-41.75],[-61.75,111.75],[-131.75,41.75],[21.75,-111.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.75,-41.75],[-61.75,111.75],[-131.75,41.75],[21.75,-111.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"n":"0p667_1_0p167_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.75,-41.75],[-61.75,111.75],[-131.75,41.75],[21.75,-111.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.75,-41.75],[-41.75,91.75],[-91.75,41.75],[41.75,-91.75]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 2","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1615.5,796.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[41.75,-111.75],[-111.75,41.75],[-41.75,111.75],[111.75,-41.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":"Path 3","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[704.5,527.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.998},"o":{"x":0.167,"y":0.002},"n":"0p833_0p998_0p167_0p002","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-16.75,-183.25],[183.25,16.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-159.25,7.25],[7.25,-159.25],[183.25,16.75],[16.75,183.25]],"c":true}]},{"i":{"x":0.713,"y":0.984},"o":{"x":0.333,"y":0},"n":"0p713_0p984_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-159.25,7.25],[7.25,-159.25],[183.25,16.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-159.25,7.25],[7.25,-159.25],[183.25,16.75],[16.75,183.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-159.25,7.25],[7.25,-159.25],[183.25,16.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-16.75,-183.25],[183.25,16.75],[16.75,183.25]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"Path 2","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[702.5,527.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.996},"o":{"x":0.167,"y":0.002},"n":"0p833_0p996_0p167_0p002","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-16.75,-183.25],[183.25,16.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-169.25,-4.75],[-14.75,-159.25],[171.25,26.75],[16.75,183.25]],"c":true}]},{"i":{"x":0.689,"y":0.993},"o":{"x":0.333,"y":0},"n":"0p689_0p993_0p333_0","t":211,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-169.25,-4.75],[-14.75,-159.25],[171.25,26.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-169.25,-4.75],[-14.75,-159.25],[171.25,26.75],[16.75,183.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-169.25,-4.75],[-14.75,-159.25],[171.25,26.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-16.75,-183.25],[183.25,16.75],[16.75,183.25]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[704.5,527.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.994},"o":{"x":0.167,"y":0.003},"n":"0p833_0p994_0p167_0p003","t":180,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-16.75,-183.25],[183.25,16.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-40.75,-159.25],[159.25,40.75],[16.75,183.25]],"c":true}]},{"i":{"x":0.682,"y":0.993},"o":{"x":0.333,"y":0},"n":"0p682_0p993_0p333_0","t":210,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-40.75,-159.25],[159.25,40.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-40.75,-159.25],[159.25,40.75],[16.75,183.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":390,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-40.75,-159.25],[159.25,40.75],[16.75,183.25]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-183.25,-16.75],[-16.75,-183.25],[183.25,16.75],[16.75,183.25]],"c":true}]},{"t":420}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":36,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1205.5,527.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,113.25],[113.25,156.75],[-156.75,-113.25],[-113.25,-156.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,113.25],[93.25,176.75],[-176.75,-93.25],[-113.25,-156.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,113.25],[93.25,176.75],[-176.75,-93.25],[-113.25,-156.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,113.25],[93.25,176.75],[-176.75,-93.25],[-113.25,-156.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,113.25],[93.25,176.75],[-176.75,-93.25],[-113.25,-156.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,113.25],[113.25,156.75],[-156.75,-113.25],[-113.25,-156.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":37,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1205.5,547.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,103.25],[103.25,156.75],[-156.75,-103.25],[-103.25,-156.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,103.25],[93.25,166.75],[-176.75,-103.25],[-113.25,-166.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,103.25],[93.25,166.75],[-176.75,-103.25],[-113.25,-166.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,103.25],[93.25,166.75],[-176.75,-103.25],[-113.25,-166.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,103.25],[93.25,166.75],[-176.75,-103.25],[-113.25,-166.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,103.25],[103.25,156.75],[-156.75,-103.25],[-103.25,-156.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":38,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1205.5,567.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,93.25],[93.25,156.75],[-156.75,-93.25],[-93.25,-156.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,93.25],[93.25,156.75],[-176.75,-113.25],[-113.25,-176.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":121,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,93.25],[93.25,156.75],[-176.75,-113.25],[-113.25,-176.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,93.25],[93.25,156.75],[-176.75,-113.25],[-113.25,-176.75]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,93.25],[93.25,156.75],[-176.75,-113.25],[-113.25,-176.75]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[156.75,93.25],[93.25,156.75],[-156.75,-93.25],[-93.25,-156.75]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":39,"ty":4,"nm":"Path Copy 16","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[549.878,808.122,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,65.119],[41.119,140.059],[-140.061,-41.121],[-65.121,-116.061]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,65.119],[65.119,116.059],[-116.061,-65.121],[-65.121,-116.061]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":122,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,65.119],[65.119,116.059],[-116.061,-65.121],[-65.121,-116.061]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,65.119],[65.119,116.059],[-116.061,-65.121],[-65.121,-116.061]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,65.119],[65.119,116.059],[-116.061,-65.121],[-65.121,-116.061]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,65.119],[41.119,140.059],[-140.061,-41.121],[-65.121,-116.061]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":-270,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 16","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":40,"ty":4,"nm":"Path Copy 15","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[526,808.122,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,53.409],[41.409,128.059],[-140.061,-53.411],[-65.411,-128.061]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,53.409],[53.409,116.059],[-116.061,-53.411],[-53.411,-116.061]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":122,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,53.409],[53.409,116.059],[-116.061,-53.411],[-53.411,-116.061]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,53.409],[53.409,116.059],[-116.061,-53.411],[-53.411,-116.061]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,53.409],[53.409,116.059],[-116.061,-53.411],[-53.411,-116.061]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,53.409],[41.409,128.059],[-140.061,-53.411],[-65.411,-128.061]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":-270,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 15","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":41,"ty":4,"nm":"Path Copy 14","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[503.425,808.122,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"n":"0p833_1_0p333_0","t":90,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,42.045],[41.699,116.405],[-138.061,-64.045],[-63.701,-138.405]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,42.045],[41.699,116.405],[-116.061,-42.045],[-41.701,-116.405]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":122,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,42.045],[41.699,116.405],[-116.061,-42.045],[-41.701,-116.405]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,42.045],[41.699,116.405],[-116.061,-42.045],[-41.701,-116.405]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":300,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,42.045],[41.699,116.405],[-116.061,-42.045],[-41.701,-116.405]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[116.059,42.045],[41.699,116.405],[-138.061,-64.045],[-63.701,-138.405]],"c":true}]},{"t":331}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":-270,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 14","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":42,"ty":4,"nm":"Path Copy 13","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[391,697,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[195.5,121.05],[121.05,195.5],[-195.5,-121.05],[-121.05,-195.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":-270,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path Copy 13","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0},{"ddd":0,"ind":43,"ty":4,"nm":"Path","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1185.5,548.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[166.75,103.25],[103.25,166.75],[-166.75,-103.25],[-103.25,-166.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.30980399251,0.462745010853,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":420,"st":0,"bm":0}],"markers":[{"tm":90,"cm":"1","dr":0},{"tm":121,"cm":"2","dr":0},{"tm":180,"cm":"3","dr":0},{"tm":210,"cm":"4","dr":0},{"tm":300,"cm":"5","dr":0},{"tm":331,"cm":"6","dr":0},{"tm":390,"cm":"7","dr":0},{"tm":420,"cm":"8","dr":0}]}
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/components/animations/trace/index.tsx b/packages/dev-tools-pages/ts/components/animations/trace/index.tsx new file mode 100644 index 000000000..2009c3cec --- /dev/null +++ b/packages/dev-tools-pages/ts/components/animations/trace/index.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; + +import { BaseAnimation } from '../index'; + +import * as animationData from './data.json'; + +const TraceAnimation: React.StatelessComponent<{}> = () => ( + <BaseAnimation animationData={animationData} width={2241} height={610} /> +); + +export { TraceAnimation }; diff --git a/packages/dev-tools-pages/ts/components/base.tsx b/packages/dev-tools-pages/ts/components/base.tsx new file mode 100644 index 000000000..4eb4e3ae3 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/base.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { ThemeProvider } from 'styled-components'; + +import { Footer } from 'ts/components/footer'; +import { Header } from 'ts/components/header'; +import { ThemeContext } from 'ts/context'; +import { GlobalStyles } from 'ts/globalStyles'; + +interface BaseProps { + context: any; +} + +const Base: React.StatelessComponent<BaseProps> = props => ( + <ThemeContext.Provider value={props.context}> + <ThemeProvider theme={props.context}> + <React.Fragment> + <GlobalStyles colors={props.context.colors} /> + <Header /> + {props.children} + <Footer /> + </React.Fragment> + </ThemeProvider> + </ThemeContext.Provider> +); + +export { Base }; diff --git a/packages/dev-tools-pages/ts/components/breakout.tsx b/packages/dev-tools-pages/ts/components/breakout.tsx new file mode 100644 index 000000000..505d8de41 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/breakout.tsx @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +import { media } from 'ts/variables'; + +const Breakout = styled.div` + ${media.small` + margin-left: -2.5rem; + width: calc(100% + 5rem); + `}; +`; + +export { Breakout }; diff --git a/packages/dev-tools-pages/ts/components/button.tsx b/packages/dev-tools-pages/ts/components/button.tsx new file mode 100644 index 000000000..d3b36ebfd --- /dev/null +++ b/packages/dev-tools-pages/ts/components/button.tsx @@ -0,0 +1,55 @@ +import styled from 'styled-components'; + +import { colors, media } from 'ts/variables'; + +interface ButtonProps { + large?: boolean; +} + +const Button = + styled.button < + ButtonProps > + ` + font-family: inherit; + line-height: 1; + font-weight: 500; + white-space: nowrap; + vertical-align: middle; + background-color: ${props => props.theme.colors.secondary}; + color: ${colors.black}; + border: 0; + border-radius: 5rem; + display: inline-flex; + justify-content: space-between; + align-items: center; + + ${props => + props.large + ? ` + font-size: 1rem; + padding: 1.1875rem 2.375rem 1.0625rem; + ` + : ` + font-size: .875rem; + padding: .5625rem 1.25rem; + `} + + :hover, :focus { + background-color: ${props => props.theme.colors.secondary_alt}; + outline: 0; + } + + ${media.small` + font-size: .875rem; + padding: .5625rem 1.25rem; + `} + + ${props => + props.large && + media.small` + font-size: 1rem; + padding: 1rem 1.5rem .75rem; + `} +`; + +export { Button }; diff --git a/packages/dev-tools-pages/ts/components/code.tsx b/packages/dev-tools-pages/ts/components/code.tsx new file mode 100644 index 000000000..71d7a7752 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/code.tsx @@ -0,0 +1,204 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import styled from 'styled-components'; + +import { colors } from 'ts/variables'; + +import { Button as BaseButton } from './button'; + +const isTouch = Boolean( + 'ontouchstart' in window || + (window as any).navigator.maxTouchPoints > 0 || + (window as any).navigator.msMaxTouchPoints > 0, +); + +interface CodeProps { + language?: string; + isLight?: boolean; + isDiff?: boolean; + gutter?: Array<number | undefined>; + gutterLength?: number; + canCopy?: boolean; + isEtc?: boolean; +} + +interface CodeState { + hlCode?: string; + didCopy?: boolean; +} + +const Button = styled(BaseButton)` + opacity: ${isTouch ? '1' : '0'}; + position: absolute; + top: 1rem; + right: 1rem; + transition: opacity 0.2s; + :focus { + opacity: 1; + } +`; + +const Container = styled.div` + position: relative; + &:hover ${Button} { + opacity: 1; + } +`; + +const Base = + styled.div < + CodeProps > + ` + font-size: .875rem; + color: ${props => (_.isUndefined(props.language) ? colors.white : 'inherit')}; + background-color: ${props => + props.isLight ? 'rgba(255,255,255,.15)' : _.isUndefined(props.language) ? colors.black : '#F1F4F5'}; + white-space: ${props => (_.isUndefined(props.language) ? 'nowrap' : '')}; + position: relative; + + ${props => + props.isDiff + ? ` + background-color: #E9ECED; + display: flex; + padding-top: 1.5rem; + padding-bottom: 1.5rem; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + ` + : ``} +`; + +const CodeDiff: React.StatelessComponent<any> = ({ gutterLength, ...props }) => <code {...props} />; +const StyledCodeDiff = styled(CodeDiff)` + ::before { + content: ''; + width: calc(0.75rem + ${props => props.gutterLength}ch); + background-color: #e2e5e6; + position: absolute; + top: 0; + left: 0; + bottom: 0; + } + + [class^='line-'] { + display: inline-block; + width: 100%; + position: relative; + padding-right: 1.5rem; + padding-left: calc(2.25rem + ${props => props.gutterLength}ch); + + ::before { + content: attr(data-gutter); + + width: ${props => props.gutterLength}; + padding-left: 0.375rem; + padding-right: 0.375rem; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + z-index: 1; + } + } + + .line-addition { + background-color: rgba(0, 202, 105, 0.1); + } + .line-deletion { + background-color: rgba(255, 0, 0, 0.07); + } +`; + +const StyledPre = + styled.pre < + CodeProps > + ` + margin: 0; + ${props => + !props.isDiff + ? ` + padding: 1.5rem; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + ` + : ``}; +`; + +const StyledCopyInput = styled.textarea` + opacity: 0; + height: 0; + position: absolute; + top: 0; + right: 0; + z-index: -1; +`; + +class Code extends React.Component<CodeProps, CodeState> { + public state: CodeState = {}; + private readonly _code = React.createRef<HTMLTextAreaElement>(); + public componentDidMount(): void { + // _onMountAsync is only setting state, so no point in handling the promise + // tslint:disable-next-line:no-floating-promises + this._onMountAsync(); + } + public render(): React.ReactNode { + const { language, isLight, isDiff, children, gutterLength, canCopy } = this.props; + const { hlCode } = this.state; + + return ( + <Container> + <Base language={language} isDiff={isDiff} isLight={isLight}> + <StyledPre isDiff={isDiff}> + {_.isUndefined(hlCode) ? ( + <code>{children}</code> + ) : ( + <StyledCodeDiff + gutterLength={gutterLength} + dangerouslySetInnerHTML={hlCode ? { __html: this.state.hlCode } : null} + /> + )} + </StyledPre> + {!('clipboard' in navigator) ? ( + <StyledCopyInput readOnly={true} aria-hidden="true" ref={this._code} value={children} /> + ) : null} + </Base> + {navigator.userAgent !== 'ReactSnap' && canCopy ? ( + <Button onClick={this._handleCopyAsync.bind(this)}>{this.state.didCopy ? 'Copied' : 'Copy'}</Button> + ) : null} + </Container> + ); + } + private async _onMountAsync(): Promise<void> { + const { language, children, isDiff, gutter, isEtc } = this.props; + + const code = children as string; + + if (language !== undefined) { + const { highlight } = await System.import(/* webpackChunkName: 'highlightjs' */ 'ts/highlight'); + + this.setState({ + hlCode: highlight({ language, code, isDiff, gutter, isEtc }), + }); + } + } + private async _handleCopyAsync(): Promise<void> { + try { + if ('clipboard' in navigator) { + await (navigator as any).clipboard.writeText(this.props.children); + this.setState({ didCopy: true }); + } else { + const lastActive = document.activeElement as HTMLElement; + this._code.current.focus(); + this._code.current.select(); + document.execCommand('copy'); + lastActive.focus(); + this.setState({ didCopy: true }); + } + } catch (error) { + this.setState({ didCopy: false }); + } + } +} + +export { Code }; diff --git a/packages/dev-tools-pages/ts/components/compiler.tsx b/packages/dev-tools-pages/ts/components/compiler.tsx new file mode 100644 index 000000000..694a535dd --- /dev/null +++ b/packages/dev-tools-pages/ts/components/compiler.tsx @@ -0,0 +1,90 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import styled from 'styled-components'; + +import { colors, media } from 'ts/variables'; + +import { Breakout } from './breakout'; +import { Container } from './container'; +import { InlineCode } from './inline-code'; + +const Cards = styled.dl` + column-count: 3; + column-gap: 1.25rem; + + ${media.medium` + column-count: 1; + `}; +`; + +const Card = styled.div` + background-color: ${colors.lightGray}; + padding: 3.125rem; + padding-bottom: 2.5rem; + display: inline-block; + margin-bottom: 1.25rem; + width: 100%; + + ${media.medium` + padding: 1.875rem; + `}; +`; + +const Dt = styled.dt` + font-weight: 500; + display: inline; + ::after { + content: '. '; + } +`; + +const Dd = styled.dd` + display: inline; + margin-left: 0; +`; + +const cards = [ + { + title: 'A Project-centric', + body: ( + <React.Fragment> + Compiles an entire project instead of only individual <InlineCode isAlt={true}>.sol</InlineCode> files. + </React.Fragment> + ), + }, + { + title: 'Incremental builds', + body: 'Recompiles your smart contracts after they have changed', + }, + { + title: 'Customizable artifacts', + body: + 'Stores only the required compiler output in your artifacts, so you can have complete control over your bundle size.', + }, + { + title: 'Seamless', + body: 'Fetches and caches the required compiler binaries.', + }, + { + title: 'Versioning', + body: + 'Compiles each contract with the version specified at the top of its file (sol-compiler even supports version ranges!).', + }, +]; + +const Compiler: React.StatelessComponent<{}> = () => ( + <Container> + <Breakout> + <Cards> + {_.map(cards, card => ( + <Card key={card.title.split(' ').join('-')}> + <Dt>{card.title}</Dt> + <Dd>{card.body}</Dd> + </Card> + ))} + </Cards> + </Breakout> + </Container> +); + +export { Compiler }; diff --git a/packages/dev-tools-pages/ts/components/container.tsx b/packages/dev-tools-pages/ts/components/container.tsx new file mode 100644 index 000000000..1b9212ed2 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/container.tsx @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +interface ContainerProps { + wide?: boolean; +} + +const Container = + styled.div < + ContainerProps > + ` + max-width: 77.5rem; + margin: 0 auto; + width: ${props => (props.wide ? '100%' : 'calc(100% - 5rem)')}; +`; + +export { Container }; diff --git a/packages/dev-tools-pages/ts/components/content-block.tsx b/packages/dev-tools-pages/ts/components/content-block.tsx new file mode 100644 index 000000000..b800c5d44 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/content-block.tsx @@ -0,0 +1,78 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import styled from 'styled-components'; + +import { ContextInterface } from 'ts/context'; +import { media } from 'ts/variables'; + +import { Alpha, Beta } from './typography'; + +const Base = styled.div` + display: flex; + align-items: flex-start; + justify-content: space-between; + :not(:last-of-type) { + margin-bottom: 6.25rem; + } + ${Beta} { + margin-bottom: 2.5rem; + } + ${media.small` + display: block; + :not(:last-of-type) { + margin-bottom: 3.125rem; + } + `}; +`; + +const Content = styled.div` + width: 66.693548387%; + ${media.small` + width: 100%; + `}; +`; + +const Item = styled.div` + p { + max-width: 31.25rem; + } + + &:not(:last-of-type) { + margin-bottom: 2.5rem; + ${media.small` + margin-bottom: 1.875rem; + `}; + } +`; + +const StyledTitle = styled(Alpha)` + color: ${props => props.color}; + ${media.small` + & + div { + margin-top: 1.5rem; + } + `}; +`; + +interface ContentBlockProps extends ContextInterface { + title: string; + main?: boolean; + children?: React.ReactNode; +} + +const ContentBlock: React.StatelessComponent<ContentBlockProps> = props => { + const children = React.Children.map(props.children, child => { + return <Item>{child}</Item>; + }); + + const Title = props.main ? StyledTitle : Beta; + + return ( + <Base> + <Title color={props.colors}>{props.title}</Title> + {_.isUndefined(children) ? null : <Content>{children}</Content>} + </Base> + ); +}; + +export { ContentBlock }; diff --git a/packages/dev-tools-pages/ts/components/content.tsx b/packages/dev-tools-pages/ts/components/content.tsx new file mode 100644 index 000000000..f74e09af7 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/content.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { Container } from './container'; + +const StyledMain = + styled.div < + MainProps > + ` + padding-top: 6.25rem; + padding-bottom: 6.25rem; + ${props => + props.dark + ? ` + background-color: #000; + color: #fff; + p:not([class]) { + color: #CCC; + } + ` + : ''}; +`; + +interface MainProps { + dark?: boolean; +} + +const Content: React.StatelessComponent<MainProps> = props => ( + <StyledMain dark={props.dark}> + <Container>{props.children}</Container> + </StyledMain> +); + +export { Content }; diff --git a/packages/dev-tools-pages/ts/components/footer.tsx b/packages/dev-tools-pages/ts/components/footer.tsx new file mode 100644 index 000000000..04fd9b88e --- /dev/null +++ b/packages/dev-tools-pages/ts/components/footer.tsx @@ -0,0 +1,133 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import styled from 'styled-components'; + +import { context as compiler } from 'ts/context/compiler'; +import { context as cov } from 'ts/context/cov'; +import { context as profiler } from 'ts/context/profiler'; +import { context as trace } from 'ts/context/trace'; +import MainIcon from 'ts/icons/logos/0x.svg'; +import { media } from 'ts/variables'; + +import { Container } from './container'; +import { Alpha, Beta } from './typography'; + +const tools = [trace, cov, compiler, profiler]; + +const Footer: React.StatelessComponent<{}> = () => ( + <StyledFooter> + <Container> + <Top> + <Alpha>Other tools by 0x</Alpha> + <List> + {_.map(tools, ({ title, subtitle, icon }) => ( + <ListItem key={title}> + <ListLink href="#"> + <Icon as={icon as 'svg'} /> + <div> + <Beta>{title}</Beta> + <p>{subtitle}</p> + </div> + </ListLink> + </ListItem> + ))} + </List> + </Top> + <Media as="aside"> + <Icon as={MainIcon} /> + <Small> + 0x is an open, permissionless protocol allowing for tokens to be traded on the Ethereum blockchain. + </Small> + </Media> + </Container> + </StyledFooter> +); + +const StyledFooter = styled.footer` + background-color: ${props => props.theme.colors.secondary}; + padding-top: 6.25rem; + padding-bottom: 5.4375rem; + + ${media.small`padding-top: 2.5rem;`}; +`; + +const Top = styled.div` + display: flex; + justify-content: space-between; + margin-bottom: 9.375rem; + + ${media.medium` + display: block; + margin-bottom: 5.3125rem; + `}; + + ${Alpha} { + ${media.medium`margin-bottom: 3.8125rem;`}; + } +`; + +const Icon = styled.div` + margin-right: 1.3125rem; + flex-shrink: 0; + ${media.small`margin-right: 0.9375rem`}; +`; + +const Media = styled.div` + display: flex; + align-items: center; + + ${Icon} { + margin-top: 0.5rem; + align-self: flex-start; + } +`; + +const List = styled.ul` + list-style: none; + margin: 0; + padding: 0; + width: 66.693548387%; + display: flex; + flex-wrap: wrap; + + ${media.medium` + width: 100%; + `}; + + ${media.small` + display: block; + `}; +`; + +const ListItem = styled.li` + margin-bottom: 3.3125rem; + padding-right: 1rem; + flex-basis: 50%; + :nth-last-of-type(-n + 2) { + margin-bottom: 0; + + ${media.small`margin-bottom: 1.875rem`}; + } + + ${media.small` + margin-bottom: 1.875rem + :last-of-type { + margin-bottom: 0; + } + `}; +`; + +const ListLink = styled.a` + display: flex; + align-items: center; + :hover { + color: ${props => props.theme.colors.dark}; + } +`; + +const Small = styled.small` + font-size: 1em; + max-width: 37.5rem; +`; + +export { Footer }; diff --git a/packages/dev-tools-pages/ts/components/header.tsx b/packages/dev-tools-pages/ts/components/header.tsx new file mode 100644 index 000000000..2787692c1 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/header.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { ContextInterface, ThemeContext } from 'ts/context'; +import { media } from 'ts/variables'; + +import { Container } from './container'; +import { Small } from './typography'; + +const Header: React.StatelessComponent<{}> = () => ( + <ThemeContext.Consumer> + {({ icon, title }: ContextInterface) => ( + <StyledHeader> + <Container> + <LogoMark> + <Logo as={icon as 'svg'} /> + <Title>{title}</Title> + </LogoMark> + + <Link as="a" href="https://0xproject.com/"> + Built by 0x + </Link> + </Container> + </StyledHeader> + )} + </ThemeContext.Consumer> +); + +const StyledHeader = styled.header` + padding-top: 3.75rem; + padding-bottom: 0.875rem; + width: 100%; + position: absolute; + top: 0; + left: 0; + z-index: 2; + ${Container} { + display: flex; + justify-content: space-between; + align-items: center; + } + + ${media.small`padding-top: 2.125rem;`}; +`; + +const LogoMark = styled.div` + position: relative; + height: 1.75rem; + display: flex; + align-items: center; +`; + +const Logo = styled.div` + color: ${props => props.theme.colors.main}; + width: 1.75rem; + height: 100%; +`; + +const Title = styled.h1` + font-size: 1.5rem; + line-height: 1; + white-space: nowrap; + margin-top: 2px; + margin-bottom: 0; + margin-left: 0.8125rem; + + ${media.small`font-size: 1.25rem;`}; +`; + +const Link = styled(Small)` + :hover { + text-decoration: underline; + } +`; + +export { Header }; diff --git a/packages/dev-tools-pages/ts/components/hero.tsx b/packages/dev-tools-pages/ts/components/hero.tsx new file mode 100644 index 000000000..cdcad6982 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/hero.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { ContextInterface, ThemeContext } from 'ts/context'; +import { media } from 'ts/variables'; + +import { Button } from './button'; +import { Beta } from './typography'; + +const Hero: React.StatelessComponent<ContextInterface> = ({ children }) => ( + <ThemeContext.Consumer> + {({ subtitle, tagline }: ContextInterface) => ( + <StyledHero> + <HeroContainer> + <Subtitle>{subtitle}</Subtitle> + <Tagline as="p">{tagline}</Tagline> + <Button as="a" href="#" large={true}> + Read the Docs + </Button> + </HeroContainer> + {navigator.userAgent !== 'ReactSnap' ? children : null} + </StyledHero> + )} + </ThemeContext.Consumer> +); + +const StyledHero = styled.section` + text-align: center; + padding-top: 9.375rem; + padding-bottom: 2rem; + padding-left: 2.5rem; + padding-right: 2.5rem; + min-height: min-content; + max-height: 37.5rem; + height: 80vh; + position: relative; +`; + +const HeroContainer = styled.div` + margin: 0 auto; + max-width: 590px; +`; + +const Subtitle = styled.h2` + font-size: 3.75rem; + line-height: 1.16; + margin-bottom: 1.5rem; + + ${media.small` + font-size: 2.25rem; + line-height: 1.1; + margin-bottom: 1.375rem; + `}; +`; + +const Tagline = styled(Beta)` + margin-bottom: 2rem; + ${media.small` + margin-bottom: 1.25rem; + `}; +`; + +export { Hero }; diff --git a/packages/dev-tools-pages/ts/components/inline-code.tsx b/packages/dev-tools-pages/ts/components/inline-code.tsx new file mode 100644 index 000000000..561666b0f --- /dev/null +++ b/packages/dev-tools-pages/ts/components/inline-code.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { colors } from '../variables'; + +interface InlineCodeProps { + isAlt?: boolean; + children: React.ReactNode; +} + +const Code: React.StatelessComponent<InlineCodeProps> = ({ isAlt, children, ...props }) => ( + <code {...props}>{children}</code> +); + +const InlineCode = styled(Code)` + background-color: ${props => (props.isAlt ? '#E5E8E9' : colors.blueGray)}; + padding: 0.3125rem; +`; + +export { InlineCode }; diff --git a/packages/dev-tools-pages/ts/components/intro.tsx b/packages/dev-tools-pages/ts/components/intro.tsx new file mode 100644 index 000000000..a0e76ab17 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/intro.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { colors, media } from 'ts/variables'; + +import { Breakout } from './breakout'; +import { Container } from './container'; +import { Alpha, Lead } from './typography'; + +const Main = styled.div` + background-color: ${colors.lightGray}; + padding: 6.25rem; + display: flex; + justify-content: space-between; + + ${media.large` + padding: 2.5rem; + `}; + ${media.medium` + display: block; + `}; +`; + +const Title = styled(Alpha)` + margin-bottom: 2.5rem; + + ${media.medium`margin-bottom: 2.25rem;`}; +`; + +const StyledIntroLead = styled(Lead)` + max-width: 25.9375rem; + margin-right: 2rem; + ${media.medium` + max-width: 100%; + margin-bottom: 1.5625rem; + `}; +`; + +const StyledIntroAside = styled.div` + max-width: 32.5rem; + position: relative; + ${media.medium` + max-width: 100%; + `}; +`; + +interface IntroLeadProps { + title: string; +} + +const IntroLead: React.StatelessComponent<IntroLeadProps> = props => ( + <StyledIntroLead as="div"> + <Title>{props.title}</Title> + {props.children} + </StyledIntroLead> +); + +const IntroAside: React.StatelessComponent<{}> = props => ( + <Breakout> + <StyledIntroAside>{props.children}</StyledIntroAside> + </Breakout> +); + +const Intro: React.StatelessComponent<{}> = props => ( + <Container wide={true}> + <Main>{props.children}</Main> + </Container> +); + +export { IntroLead, IntroAside, Intro }; diff --git a/packages/dev-tools-pages/ts/components/list.tsx b/packages/dev-tools-pages/ts/components/list.tsx new file mode 100644 index 000000000..ffa7c1cf3 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/list.tsx @@ -0,0 +1,50 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import styled from 'styled-components'; + +import { media } from 'ts/variables'; + +const StyledList = styled.ul` + list-style-type: none; + margin: 0; + padding: 0; + position: relative; +`; + +const StyledItem = styled.li` + position: relative; + padding-left: 1.625rem; + + :before { + content: ''; + border: 1px solid black; + width: 0.625rem; + height: 0.625rem; + display: inline-block; + position: absolute; + margin-top: 2px; + top: 0.3125rem; + left: 0; + transform: rotate(45deg); + } + :not(:last-child) { + margin-bottom: 0.5625rem; + ${media.small` + margin-bottom: 0.375rem; + `}; + } +`; + +interface ListProps { + items?: []; +} + +const List: React.StatelessComponent<ListProps> = props => ( + <StyledList> + {props.children !== undefined + ? props.children + : _.map(props.items, (bullet, index) => <StyledItem key={index}>{bullet}</StyledItem>)} + </StyledList> +); + +export { List, StyledItem as ListItem }; diff --git a/packages/dev-tools-pages/ts/components/meta_tags.tsx b/packages/dev-tools-pages/ts/components/meta_tags.tsx deleted file mode 100644 index f6c43d23f..000000000 --- a/packages/dev-tools-pages/ts/components/meta_tags.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import { Helmet } from 'react-helmet'; - -export interface MetaTagsProps { - title: string; - description: string; - imgSrc?: string; -} - -export const MetaTags: React.StatelessComponent<MetaTagsProps> = ({ title, description, imgSrc }) => ( - <Helmet> - <title>{title}</title> - <meta name="description" content={description} /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={description} /> - <meta property="og:type" content="website" /> - <meta property="og:image" content={imgSrc} /> - <meta name="twitter:site" content="@0xproject" /> - <meta name="twitter:image" content={imgSrc} /> - </Helmet> -); - -MetaTags.defaultProps = { - imgSrc: '/images/og_image.png', -}; diff --git a/packages/dev-tools-pages/ts/components/tabs.tsx b/packages/dev-tools-pages/ts/components/tabs.tsx new file mode 100644 index 000000000..1d26f2777 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/tabs.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import { Tab, TabList, TabPanel, Tabs as ReactTabs } from 'react-tabs'; +import styled from 'styled-components'; + +import { colors } from 'ts/variables'; + +import { Breakout } from './breakout'; + +const StyledTabList = styled(TabList)` + text-transform: uppercase; + list-style: none; + margin: 0; + padding: 0; + overflow: hidden; +`; + +const StyledTab = styled(Tab)` + background-color: ${props => props.theme.colors.secondary}; + height: 2.5rem; + padding-left: 1rem; + padding-right: 1rem; + display: flex; + justify-content: space-around; + align-items: center; + float: left; + &:not(:first-of-type) { + margin-left: 0.25rem; + } + + &[aria-selected='true'] { + background-color: ${colors.gray}; + } + + &[aria-selected='false']:focus, + &[aria-selected='false']:hover { + background-color: ${props => props.theme.colors.secondary_alt}; + cursor: pointer; + } +`; + +const Tabs: React.StatelessComponent<{}> = props => ( + <Breakout> + <ReactTabs> + <StyledTabList> + {React.Children.map(props.children, child => { + const { title } = React.cloneElement(child as React.ReactElement<any>).props; + return <StyledTab>{title}</StyledTab>; + })} + </StyledTabList> + + {React.Children.map(props.children, child => <TabPanel>{child}</TabPanel>)} + </ReactTabs> + </Breakout> +); + +interface TabBlockProps { + title: string; +} + +const TabBlock: React.StatelessComponent<TabBlockProps> = props => <React.Fragment>{props.children}</React.Fragment>; + +const ContextTabs = Tabs; + +export { ContextTabs as Tabs, TabBlock }; diff --git a/packages/dev-tools-pages/ts/components/trace.tsx b/packages/dev-tools-pages/ts/components/trace.tsx new file mode 100644 index 000000000..108154648 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/trace.tsx @@ -0,0 +1,223 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { ContextInterface, ThemeContext } from 'ts/context'; +import ExactLocation from 'ts/icons/exact-location.svg'; +import NoLocation from 'ts/icons/no-location.svg'; +import TimeConsuming from 'ts/icons/time-consuming.svg'; +import TimeSaving from 'ts/icons/time-saving.svg'; +import { colors, media } from 'ts/variables'; + +import { Breakout } from './breakout'; +import { Code } from './code'; +import { Container } from './container'; +import { Alpha, Gamma, Lead } from './typography'; + +const Trace: React.StatelessComponent<{}> = () => ( + <ThemeContext.Consumer> + {(props: ContextInterface) => ( + <StyledSection background={props.colors.secondary}> + <Wrapper> + <Block> + <Alpha>The Issue</Alpha> + <MainCopy> + Every time an Ethereum transaction fails, it's extremely hard to trace down the + troublemaking line of code. The only hint you'll get is a generic error. + </MainCopy> + <Breakout> + <Code isLight={true}>Error: VM Exception while processing transaction: rever</Code> + </Breakout> + + <List> + <Item> + <Copy dark={true}> + <Gamma as="h3">No location</Gamma> + <p> + The error basically says "anything could have gone wrong here", which keeps you + completely in the dark about its exact location. + </p> + </Copy> + <Icon as={NoLocation} /> + </Item> + + <Item> + <Copy dark={true}> + <Gamma as="h3">Time-consuming</Gamma> + <p> + Working with a large code-base that contains hundreds of smart contracts, + finding the failing line of code quickly becomes a daunting task. + </p> + </Copy> + <Icon as={TimeConsuming} /> + </Item> + </List> + </Block> + + <Block background={props.colors.secondary}> + <Alpha>The Fix</Alpha> + <MainCopy> + Sol-trace will give you full stack traces, including contract names, line numbers and code + snippets, every time you encounter an error. + </MainCopy> + <Breakout> + <Code isLight={true} language="javascript"> + {`contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol:51:8 + require( + isValidSignature( + hash, + signerAddress, + signature + ), + "INVALID_SIGNATURE" + )`} + </Code> + </Breakout> + + <List> + <Item> + <Copy> + <Gamma as="h3">Exact location</Gamma> + <p> + It shows you the exact location of the specific code linen and where it was + called from. + </p> + </Copy> + <Icon as={ExactLocation} /> + </Item> + + <Item> + <Copy> + <Gamma as="h3">Time-saving</Gamma> + <p> + Turning "Your code failed somewhere, good luck debugging it" into "Your code + failed on line X of contract Y", it drastically improves the developer + experience. + </p> + </Copy> + <Icon as={TimeSaving} /> + </Item> + </List> + </Block> + </Wrapper> + </StyledSection> + )} + </ThemeContext.Consumer> +); + +interface TraceProps { + background?: string; +} + +const StyledSection = + styled.section < + TraceProps > + ` + max-width: 90rem; + margin: 0 auto; + background: linear-gradient(to right, ${colors.black} 50%, ${props => props.background} 50%); + overflow: hidden; + ${media.large` + background: none + padding-top: 0; + padding-bottom: 0; + `}; +`; + +const Wrapper = styled(Container)` + display: flex; + + ${Alpha} { + padding-bottom: 2.5rem; + + ${media.small`padding-bottom: 1.875rem;`}; + } + + ${media.large` + display: block; + width: 100%; + `}; +`; + +const Block = + styled.div < + TraceProps > + ` + width: 50%; + background: ${props => (props.background ? props.background : colors.black)}; + color: ${props => (props.background ? 'inherit' : colors.white)}; + padding-top: 6.25rem; + padding-bottom: 5.25rem; + + :first-of-type { + padding-right: 6.25rem; + } + :last-of-type { + padding-left: 6.25rem; + } + + ${media.xlarge` + :first-of-type { + padding-right: 2.5rem; + } + :last-of-type { + padding-left: 2.5rem; + } + `} + ${media.large` + width: 100%; + padding: 2.5rem; + `} + + ${media.small` + padding-left: 1.875rem; + padding-right: 1.875rem; + `}; +`; + +const MainCopy = styled(Lead)` + margin-bottom: 3.1875rem; + ${media.small` + margin-bottom: 1.125rem; + `}; +`; + +const List = styled.ul` + margin-top: 6.25rem; + margin-bottom: 0; + padding: 0; + + ${media.small`margin-top: 3.4375rem;`}; +`; + +const Item = styled.li` + display: flex; + align-items: center; + + :not(:last-child) { + margin-bottom: 4.4375rem; + + ${media.small`margin-bottom: 3.4375rem;`}; + } +`; + +const Copy = + styled.div < + { dark: boolean } > + ` + margin-right: 5.875rem; + ${props => + props.dark && + ` + p { + color: #ccc; + } + `} + + ${media.small`margin-right: 2.0625rem;`}; +`; + +const Icon = styled.div` + flex-shrink: 0; +`; + +export { Trace }; diff --git a/packages/dev-tools-pages/ts/components/typography.tsx b/packages/dev-tools-pages/ts/components/typography.tsx new file mode 100644 index 000000000..e4b13163a --- /dev/null +++ b/packages/dev-tools-pages/ts/components/typography.tsx @@ -0,0 +1,33 @@ +import styled from 'styled-components'; + +import { media } from '../variables'; + +const Alpha = styled.h2` + font-size: 1.75rem; + line-height: 1; + + ${media.small`font-size: 1.5rem;`}; +`; + +const Beta = styled.h3` + font-size: 1.25rem; + line-height: 1.65; +`; + +const Gamma = styled.h4` + font-size: 1rem; + + ${media.small`font-size: 0.875rem;`}; +`; + +const Lead = styled.p` + font-size: 1.25rem; + line-height: 1.6; + ${media.small`font-size: 1rem;`}; +`; + +const Small = styled.p` + font-size: 0.875rem; +`; + +export { Alpha, Beta, Gamma, Lead, Small }; diff --git a/packages/dev-tools-pages/ts/components/ui/button.tsx b/packages/dev-tools-pages/ts/components/ui/button.tsx deleted file mode 100644 index 754eca40e..000000000 --- a/packages/dev-tools-pages/ts/components/ui/button.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { darken, saturate } from 'polished'; -import * as React from 'react'; -import styled from 'styled-components'; - -/** - * AN EXAMPLE OF HOW TO CREATE A STYLED COMPONENT USING STYLED-COMPONENTS - * SEE: https://www.styled-components.com/docs/basics#coming-from-css - */ -export interface ButtonProps { - backgroundColor?: string; - borderColor?: string; - width?: string; - padding?: string; - type?: string; - isDisabled?: boolean; - onClick?: (event: React.MouseEvent<HTMLElement>) => void; - className?: string; -} - -const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => ( - <button type={type} className={className} onClick={isDisabled ? undefined : onClick} disabled={isDisabled}> - {children} - </button> -); - -const darkenOnHoverAmount = 0.1; -const darkenOnActiveAmount = 0.2; -const saturateOnFocusAmount = 0.2; -export const Button = styled(PlainButton)` - cursor: ${props => (props.isDisabled ? 'default' : 'pointer')}; - transition: background-color, opacity 0.5s ease; - padding: ${props => props.padding}; - border-radius: 3px; - outline: none; - width: ${props => props.width}; - background-color: ${props => (props.backgroundColor ? props.backgroundColor : 'none')}; - border: ${props => (props.borderColor ? `1px solid ${props.backgroundColor}` : 'none')}; - &:hover { - background-color: ${props => - !props.isDisabled ? darken(darkenOnHoverAmount, props.backgroundColor) : ''} !important; - } - &:active { - background-color: ${props => (!props.isDisabled ? darken(darkenOnActiveAmount, props.backgroundColor) : '')}; - } - &:disabled { - opacity: 0.5; - } - &:focus { - background-color: ${props => saturate(saturateOnFocusAmount, props.backgroundColor)}; - } -`; - -Button.defaultProps = { - backgroundColor: 'red', - width: 'auto', - isDisabled: false, - padding: '1em 2.2em', -}; -Button.displayName = 'Button'; diff --git a/packages/dev-tools-pages/ts/components/ui/container.tsx b/packages/dev-tools-pages/ts/components/ui/container.tsx deleted file mode 100644 index f2ae68b70..000000000 --- a/packages/dev-tools-pages/ts/components/ui/container.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from 'react'; - -type StringOrNum = string | number; - -export type ContainerTag = 'div' | 'span'; - -export interface ContainerProps { - marginTop?: StringOrNum; - marginBottom?: StringOrNum; - marginRight?: StringOrNum; - marginLeft?: StringOrNum; - padding?: StringOrNum; - paddingTop?: StringOrNum; - paddingBottom?: StringOrNum; - paddingRight?: StringOrNum; - paddingLeft?: StringOrNum; - backgroundColor?: string; - borderRadius?: StringOrNum; - maxWidth?: StringOrNum; - maxHeight?: StringOrNum; - width?: StringOrNum; - height?: StringOrNum; - minWidth?: StringOrNum; - minHeight?: StringOrNum; - isHidden?: boolean; - className?: string; - position?: 'absolute' | 'fixed' | 'relative' | 'unset'; - display?: 'inline-block' | 'block' | 'inline-flex' | 'inline'; - top?: string; - left?: string; - right?: string; - bottom?: string; - zIndex?: number; - Tag?: ContainerTag; - cursor?: string; - id?: string; - onClick?: (event: React.MouseEvent<HTMLElement>) => void; - overflowX?: 'scroll' | 'hidden' | 'auto' | 'visible'; -} - -export const Container: React.StatelessComponent<ContainerProps> = props => { - const { children, className, Tag, isHidden, id, onClick, ...style } = props; - const visibility = isHidden ? 'hidden' : undefined; - return ( - <Tag id={id} style={{ ...style, visibility }} className={className} onClick={onClick}> - {children} - </Tag> - ); -}; - -Container.defaultProps = { - Tag: 'div', -}; - -Container.displayName = 'Container'; diff --git a/packages/dev-tools-pages/ts/components/ui/text.tsx b/packages/dev-tools-pages/ts/components/ui/text.tsx deleted file mode 100644 index b4a61457f..000000000 --- a/packages/dev-tools-pages/ts/components/ui/text.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { colors } from '@0x/react-shared'; -import { darken } from 'polished'; -import * as React from 'react'; -import styled from 'styled-components'; - -export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i'; - -export interface TextProps { - className?: string; - Tag?: TextTag; - fontSize?: string; - fontFamily?: string; - fontStyle?: string; - fontColor?: string; - lineHeight?: string; - minHeight?: string; - center?: boolean; - fontWeight?: number | string; - textDecorationLine?: string; - onClick?: (event: React.MouseEvent<HTMLElement>) => void; - hoverColor?: string; - noWrap?: boolean; - display?: string; -} - -const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick, Tag }) => ( - <Tag className={className} onClick={onClick}> - {children} - </Tag> -); - -export const Text = styled(PlainText)` - font-family: ${props => props.fontFamily}; - font-style: ${props => props.fontStyle}; - font-weight: ${props => props.fontWeight}; - font-size: ${props => props.fontSize}; - text-decoration-line: ${props => props.textDecorationLine}; - ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')}; - ${props => (props.center ? 'text-align: center' : '')}; - color: ${props => props.fontColor}; - ${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')}; - ${props => (props.onClick ? 'cursor: pointer' : '')}; - transition: color 0.5s ease; - ${props => (props.noWrap ? 'white-space: nowrap' : '')}; - ${props => (props.display ? `display: ${props.display}` : '')}; - &:hover { - ${props => (props.onClick ? `color: ${props.hoverColor || darken(0.3, props.fontColor || 'black')}` : '')}; - } -`; - -Text.defaultProps = { - fontFamily: 'Roboto', - fontStyle: 'normal', - fontWeight: 400, - fontColor: colors.black, - fontSize: '15px', - lineHeight: '1.5em', - textDecorationLine: 'none', - Tag: 'div', - noWrap: false, -}; - -Text.displayName = 'Text'; - -export const Title: React.StatelessComponent<TextProps> = props => <Text {...props} />; - -Title.defaultProps = { - Tag: 'h2', - fontSize: '20px', - fontWeight: 600, - fontColor: colors.black, -}; - -Title.displayName = 'Title'; diff --git a/packages/dev-tools-pages/ts/context/compiler.tsx b/packages/dev-tools-pages/ts/context/compiler.tsx new file mode 100644 index 000000000..177e265e5 --- /dev/null +++ b/packages/dev-tools-pages/ts/context/compiler.tsx @@ -0,0 +1,19 @@ +import Icon from 'ts/icons/logos/compiler.svg'; + +import { ContextInterface } from './index'; + +export const context: ContextInterface = { + title: 'sol-compiler', + name: 'compiler', + subtitle: 'Solidity compilation that just works', + tagline: 'Seamlessly compile an entire solidity project and generate customisable artifacts', + icon: Icon, + colors: { + main: '#1EADCD', + secondary: '#D1F4FC', + secondary_alt: '#C4F2FC', + type: '#30C3E3', + type_alt: '#16A9C9', + dark: '#4B818D', + }, +}; diff --git a/packages/dev-tools-pages/ts/context/cov.tsx b/packages/dev-tools-pages/ts/context/cov.tsx new file mode 100644 index 000000000..1ade45e9d --- /dev/null +++ b/packages/dev-tools-pages/ts/context/cov.tsx @@ -0,0 +1,19 @@ +import Icon from 'ts/icons/logos/cov.svg'; + +import { ContextInterface } from './index'; + +export const context: ContextInterface = { + title: 'sol-cov', + name: 'cov', + subtitle: 'Solidity code coverage', + tagline: 'Measure Solidity code coverage', + icon: Icon, + colors: { + main: '#BB9200', + secondary: '#F1DB8D', + secondary_alt: '#F1D882', + type: '#D7AE1B', + type_alt: '#BD9406', + dark: '#817033', + }, +}; diff --git a/packages/dev-tools-pages/ts/context/index.tsx b/packages/dev-tools-pages/ts/context/index.tsx new file mode 100644 index 000000000..35c647ad6 --- /dev/null +++ b/packages/dev-tools-pages/ts/context/index.tsx @@ -0,0 +1,21 @@ +import { createContext } from 'react'; + +interface ContextInterface { + title?: string; + name?: string; + subtitle?: string; + tagline?: string; + icon?: React.ReactNode; + colors?: { + main: string; + secondary: string; + secondary_alt: string; + type: string; + type_alt: string; + dark: string; + }; +} + +const ThemeContext = createContext({}); + +export { ThemeContext, ContextInterface }; diff --git a/packages/dev-tools-pages/ts/context/profiler.tsx b/packages/dev-tools-pages/ts/context/profiler.tsx new file mode 100644 index 000000000..5ccfa5b4c --- /dev/null +++ b/packages/dev-tools-pages/ts/context/profiler.tsx @@ -0,0 +1,19 @@ +import Icon from 'ts/icons/logos/profiler.svg'; + +import { ContextInterface } from './index'; + +export const context: ContextInterface = { + title: 'sol-profiler', + name: 'profiler', + subtitle: 'Gas profiling for Solidity', + tagline: "Implement data-guided optimizations by profiling your contract's gas usage", + icon: Icon, + colors: { + main: '#FF7144', + secondary: '#FED7CB', + secondary_alt: '#FECEBE', + type: '#EB8666', + type_alt: '#D16745', + dark: '#985C49', + }, +}; diff --git a/packages/dev-tools-pages/ts/context/trace.tsx b/packages/dev-tools-pages/ts/context/trace.tsx new file mode 100644 index 000000000..9627cc0a4 --- /dev/null +++ b/packages/dev-tools-pages/ts/context/trace.tsx @@ -0,0 +1,19 @@ +import Icon from 'ts/icons/logos/trace.svg'; + +import { ContextInterface } from './index'; + +export const context: ContextInterface = { + title: 'sol-trace', + name: 'trace', + subtitle: 'Human-readable stack traces', + tagline: 'Immediately locate Solidity errors and rapidly debug failed transactions', + icon: Icon, + colors: { + main: '#4F76FF', + secondary: '#CDD8FF', + secondary_alt: '#BFCDFF', + type: '#7090FF', + type_alt: '#355CE5', + dark: '#2A4ABC', + }, +}; diff --git a/packages/dev-tools-pages/ts/globalStyles.tsx b/packages/dev-tools-pages/ts/globalStyles.tsx new file mode 100644 index 000000000..a77fb4eef --- /dev/null +++ b/packages/dev-tools-pages/ts/globalStyles.tsx @@ -0,0 +1,90 @@ +import hljsStyles from 'highlight.js/styles/github-gist.css'; +import { createGlobalStyle } from 'styled-components'; +import styledNormalize from 'styled-normalize'; + +import { ContextInterface } from 'ts/context'; +import { media } from 'ts/variables'; + +const GlobalStyles = + createGlobalStyle < + ContextInterface > + ` + ${styledNormalize} + ${hljsStyles} + + @font-face { + font-family: "Maison Neue"; + src: url("/fonts/MaisonNeue-Book-subset.woff2") format("woff2"), url("/fonts/MaisonNeue-Book-subset.woff") format("woff"); + font-weight: 300; + font-display: swap; + unicode-range: U+20-7E; + } + @font-face { + font-family: "Maison Neue"; + src: url("/fonts/MaisonNeue-Bold-subset.woff2") format("woff2"), url("/fonts/MaisonNeue-Bold-subset.woff") format("woff"); + font-weight: 500; + font-display: swap; + unicode-range: U+20-7E; + } + @font-face { + font-family: "Maison Neue Mono"; + src: url("/fonts/MaisonNeue-Mono-subset.woff2") format("woff2"), url("/fonts/MaisonNeue-Mono-subset.woff") format("woff"); + font-weight: 300; + font-display: optional; + unicode-range: U+20-7E; + } + + html { + font-size: 100%; + box-sizing: border-box; + } + + *, *::before, *::after { + box-sizing: inherit; + } + + body { + font-family: "Maison Neue", system-ui, sans-serif; + font-weight: 300; + font-size: 1rem; + line-height: 1.8; + + ${media.small`font-size: 0.875rem;`}; + } + + a { + color: inherit; + text-decoration: none; + } + + a:not([class]) { + color: ${props => props.colors.type_alt}; + text-decoration: none; + + &:hover { + color: ${props => props.colors.type_alt}; + } + } + + h1, h2, h3, h4 { + font-weight: 500; + margin: 0; + } + + p { + margin-top: 0; + margin-bottom: 1em; + &:not([class]):last-of-type { + margin-bottom: 0; + } + } + + code { + font-family: "Maison Neue Mono", monospace; + ${media.small` + font-size: .75rem; + `} + } +`; + +export { GlobalStyles }; diff --git a/packages/dev-tools-pages/ts/globals.d.ts b/packages/dev-tools-pages/ts/globals.d.ts index d0890161c..bfe13ee71 100644 --- a/packages/dev-tools-pages/ts/globals.d.ts +++ b/packages/dev-tools-pages/ts/globals.d.ts @@ -1,5 +1,10 @@ declare module 'whatwg-fetch'; declare module 'react-document-title'; +declare module 'highlight.js/lib/highlight'; +declare module 'highlight.js/lib/languages/javascript'; +declare module 'highlight.js/lib/languages/json'; + +declare var System: any; declare module '*.json' { const json: any; @@ -7,3 +12,13 @@ declare module '*.json' { export default json; /* tslint:enable */ } + +declare module '*.css' { + const css: any; + export default css; +} + +declare module '*.svg' { + const svg: any; + export default svg; +} diff --git a/packages/dev-tools-pages/ts/highlight.tsx b/packages/dev-tools-pages/ts/highlight.tsx new file mode 100644 index 000000000..02f4a753e --- /dev/null +++ b/packages/dev-tools-pages/ts/highlight.tsx @@ -0,0 +1,67 @@ +import * as hljs from 'highlight.js/lib/highlight'; +import * as javascript from 'highlight.js/lib/languages/javascript'; +import * as json from 'highlight.js/lib/languages/json'; +import * as _ from 'lodash'; + +hljs.registerLanguage('javascript', javascript); +hljs.registerLanguage('json', json); + +interface PatchInterface { + [key: string]: string; +} + +const PATCH_TYPES: PatchInterface = { + '+': 'addition', + '-': 'deletion', + '!': 'change', +}; + +function diffHighlight(language: string, code: any, gutter: any): string { + return _.map(code.split(/\r?\n/g), (line: string, index: number) => { + let type; + let currentLine = line; + + if (/^-{3} [^-]+ -{4}$|^\*{3} [^*]+ \*{4}$|^@@ [^@]+ @@$/.test(currentLine)) { + type = 'chunk'; + } else if (/^Index: |^[+\-*]{3}|^[*=]{5,}$/.test(currentLine)) { + type = 'header'; + } else { + type = PATCH_TYPES[currentLine[0]] || 'null'; + currentLine = currentLine.replace(/^[+\-! ]/, ''); + } + + const g = gutter[index]; + + return `<span data-gutter="${g !== undefined ? `${g}x` : ''}" class="line-${type}">${ + hljs.highlight(language, currentLine).value + }</span>`; + }).join('\n'); +} + +interface HighlightProps { + language: string; + code: string; + isDiff?: boolean; + gutter?: []; + isEtc?: boolean; +} + +function highlight({ language, code, isDiff, gutter, isEtc }: HighlightProps): string { + if (isDiff) { + return diffHighlight(language, code, gutter); + } + + const hlCode = hljs.highlight(language, code).value; + + if (!isEtc) { + return hlCode; + } + + const hc = hlCode.split(/\r?\n/g); + hc.splice(1, 0, ' ...'); + hc.splice(hc.length - 1, 0, ' ...'); + + return hc.join('\n'); +} + +export { highlight }; diff --git a/packages/dev-tools-pages/ts/icons/exact-location.svg b/packages/dev-tools-pages/ts/icons/exact-location.svg new file mode 100644 index 000000000..4934cad58 --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/exact-location.svg @@ -0,0 +1 @@ +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M80 33.6772c0 5.1116-1.8855 10.4454-4.7935 15.617-2.9036 5.1637-6.7886 10.0987-10.6962 14.3941-3.9047 4.2922-7.8128 7.9248-10.746 10.4851-1.4659 1.2795-2.6866 2.2897-3.5393 2.9788-.2841.2296-.5273.4235-.725.5798-.1977-.1563-.4409-.3502-.725-.5798-.8527-.6891-2.0734-1.6993-3.5393-2.9788-2.9332-2.5603-6.8413-6.1929-10.746-10.4851-3.9076-4.2954-7.7926-9.2304-10.6962-14.3941C20.8855 44.1226 19 38.7888 19 33.6772 19 16.7294 32.6606 3 49.5 3 66.3394 3 80 16.7294 80 33.6772z" stroke="#fff" stroke-width="2"/><path d="M50 98V55M27.5 77H73" stroke="#000" stroke-width="2"/><circle cx="50" cy="34" r="10" stroke="#fff" stroke-width="2"/></svg>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/icons/logos/0x.svg b/packages/dev-tools-pages/ts/icons/logos/0x.svg new file mode 100644 index 000000000..1856389db --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/logos/0x.svg @@ -0,0 +1 @@ +<svg width="37" height="37" viewBox="0 0 37 37" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M9.20465 34.4904c2.38415 1.3926 5.13725 2.2736 8.08905 2.4725 4.0871.2558 7.9188-.8242 11.1261-2.8704 1.5894-1.0232 3.0085-2.2737 4.229-3.7231-.9934-1.3926-2.0719-2.842-3.2072-4.3199-.3123-.3979-.6245-.7958-.9367-1.1937-1.1921 1.9042-2.9518 3.4389-4.9953 4.4052l-3.1789-3.0978-11.12605 8.3272zM2.44955 9.30988C1.08718 11.6972.235696 14.3971.0370167 17.3244-.218428 21.417.860116 25.2821 2.90368 28.4652c1.02177 1.5916 2.27062 3.0126 3.71814 4.2347 1.39075-.9947 2.83827-2.0747 4.31418-3.2115.3974-.3127.7947-.6253 1.1921-.9379-1.9017-1.2221-3.43434-2.9841-4.39936-5.0304l3.12206-3.1831L2.44955 9.30988zM27.7954 2.51741C25.4112 1.12481 22.6581.243776 19.7063.0448336 15.6192-.23937 11.7591.840605 8.55184 2.91529 6.96241 3.93843 5.54327 5.18893 4.32281 6.63837c.9934 1.3926 2.07194 2.84204 3.20725 4.31993.31221.3979.62442.7957.93663 1.1936 1.22046-1.9041 2.95181-3.43884 5.02371-4.40514l2.9802 2.87044 11.3248-8.09979zM34.5788 27.6126c1.334-2.3589 2.1855-5.0304 2.3842-7.9293.2554-4.0925-.8231-7.9293-2.8667-11.14077-1.0218-1.59154-2.2706-3.01256-3.7181-4.23464-1.3908.99472-2.8383 2.07469-4.3142 3.21151-.3974.31262-.7947.62525-1.1921.93787 1.9301 1.22208 3.4627 2.98413 4.4277 5.03043l.0284.0568-3.0085 3.1263 8.2593 10.9418z"/></svg>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/icons/logos/compiler.svg b/packages/dev-tools-pages/ts/icons/logos/compiler.svg new file mode 100644 index 000000000..43c338f29 --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/logos/compiler.svg @@ -0,0 +1,3 @@ +<svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M0 28V15V0H15H28V15V28H15H0ZM26 13V2H15V13H26ZM15 15H26V26H15V15ZM13 13V2H2V13H13ZM2 15H13V26H2V15Z" /> +</svg> diff --git a/packages/dev-tools-pages/ts/icons/logos/cov.svg b/packages/dev-tools-pages/ts/icons/logos/cov.svg new file mode 100644 index 000000000..2c4dffb83 --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/logos/cov.svg @@ -0,0 +1,3 @@ +<svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H28V28H0V0ZM2 2V26H26V16H12V2H2ZM14 2V14H26V2H14Z"/> +</svg> diff --git a/packages/dev-tools-pages/ts/icons/logos/profiler.svg b/packages/dev-tools-pages/ts/icons/logos/profiler.svg new file mode 100644 index 000000000..c3ea61294 --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/logos/profiler.svg @@ -0,0 +1,3 @@ +<svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M2 2H26V9H2V2ZM2 11V26H26V11H2ZM0 0H2H26H28V2V26V28H26H2H0V26V2V0Z" /> +</svg> diff --git a/packages/dev-tools-pages/ts/icons/logos/trace.svg b/packages/dev-tools-pages/ts/icons/logos/trace.svg new file mode 100644 index 000000000..c207b24cb --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/logos/trace.svg @@ -0,0 +1 @@ +<svg width="28" height="28" viewBox="0 0 28 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M10 15H2V2h24v13H10zm0 2H2v9h8v-9zm2 9h14v-9H12v9zm-2 2H0V0h28v28H10z"/></svg>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/icons/no-location.svg b/packages/dev-tools-pages/ts/icons/no-location.svg new file mode 100644 index 000000000..7df008ee1 --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/no-location.svg @@ -0,0 +1 @@ +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"><path opacity=".5" d="M88.2609 41.4937c0 6.4322-2.3631 13.1253-5.9866 19.5928-3.6191 6.4597-8.4581 12.6276-13.3196 17.9909-4.8586 5.3602-9.7205 9.8958-13.3691 13.0921-1.8236 1.5975-3.3423 2.8589-4.4037 3.7198-.4285.3475-.7823.6297-1.0515.8423-.2692-.2126-.623-.4948-1.0514-.8423-1.0614-.8609-2.5802-2.1223-4.4038-3.7198-3.6486-3.1963-8.5105-7.7319-13.3691-13.0921-4.8614-5.3633-9.7004-11.5312-13.3196-17.9909C14.363 54.619 12 47.9259 12 41.4937 12 20.2255 29.0803 3 50.1304 3c21.0502 0 38.1305 17.2255 38.1305 38.4937z" stroke="#CDD8FF" stroke-width="2"/><circle opacity=".5" cx="50" cy="42" r="18" stroke="#CDD8FF" stroke-width="2"/><path d="M22.5 69.5L78 14" stroke="#fff" stroke-width="2"/></svg>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/icons/time-consuming.svg b/packages/dev-tools-pages/ts/icons/time-consuming.svg new file mode 100644 index 000000000..330723394 --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/time-consuming.svg @@ -0,0 +1 @@ +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"><circle opacity=".5" cx="50" cy="50" r="42" stroke="#CDD8FF" stroke-width="2"/><path opacity=".5" d="M34.6621 79.4343c6.9918 3.5363 15.2613 4.61 23.4252 2.4225 8.1639-2.1875 14.7886-7.2521 19.0755-13.8105M33.8711 58c2.9454 5.9269 9.0615 10 16.129 10 1.5538 0 3.0617-.1969 4.5-.5671M30 25.0185C35.4784 20.6269 42.4324 18 50 18c13.4295 0 24.9268 8.2727 29.6739 20" stroke="#CDD8FF" stroke-width="2"/><path d="M35 64l17.5-12.5L37.5 9" stroke="#fff" stroke-width="2"/></svg>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/icons/time-saving.svg b/packages/dev-tools-pages/ts/icons/time-saving.svg new file mode 100644 index 000000000..935b1204e --- /dev/null +++ b/packages/dev-tools-pages/ts/icons/time-saving.svg @@ -0,0 +1 @@ +<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="49.9092" cy="49.7499" r="40.4824" transform="rotate(45 49.9092 49.7499)" stroke="#fff" stroke-width="2"/><path d="M21.5991 79.4236l8.5269-8.5269M71.3965 29.6267l8.5268-8.5268M69.6914 70.8967l8.5269 8.5269M20.5761 21.7819l9.2091 9.2091M9.01002 50.4736H21.0688M79.4334 50.4736h12.0588M49.0449 78.45v12.0588M49.0449 8.99112V22.0146" stroke="#fff" stroke-width="2"/><path d="M35.584 36.789l14.3252 14.3252 30.0146-30.0146" stroke="#000" stroke-width="2"/></svg>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/index.tsx b/packages/dev-tools-pages/ts/index.tsx deleted file mode 100644 index 4591c6d76..000000000 --- a/packages/dev-tools-pages/ts/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from 'react'; -import { render } from 'react-dom'; -import { MetaTags } from 'ts/components/meta_tags'; -import { Landing } from 'ts/pages/landing'; - -import 'basscss/css/basscss.css'; - -const DOCUMENT_TITLE = ''; -const DOCUMENT_DESCRIPTION = ''; - -render( - <div> - <MetaTags title={DOCUMENT_TITLE} description={DOCUMENT_DESCRIPTION} /> - <Landing /> - </div>, - document.getElementById('app'), -); diff --git a/packages/dev-tools-pages/ts/pages/compiler.tsx b/packages/dev-tools-pages/ts/pages/compiler.tsx new file mode 100644 index 000000000..93a667562 --- /dev/null +++ b/packages/dev-tools-pages/ts/pages/compiler.tsx @@ -0,0 +1,170 @@ +import * as React from 'react'; +import { hydrate, render } from 'react-dom'; +import * as Loadable from 'react-loadable'; + +import { context } from 'ts/context/compiler'; + +import { Base } from 'ts/components/base'; +import { Breakout } from 'ts/components/breakout'; +import { Code } from 'ts/components/code'; +import { Compiler as CompilerComponent } from 'ts/components/compiler'; +import { Content } from 'ts/components/content'; +import { ContentBlock } from 'ts/components/content-block'; +import { Hero } from 'ts/components/hero'; +import { InlineCode } from 'ts/components/inline-code'; +import { Lead } from 'ts/components/typography'; + +const Animation = Loadable({ + loader: () => System.import(/* webpackChunkName: 'compiler-animation' */ 'ts/components/animations/compiler'), + loading: () => <div />, + delay: 1000, + render(loadable: { CompilerAnimation: any }): React.ReactNode { + const Component = loadable.CompilerAnimation; + return <Component />; + }, +}); + +const Compiler: React.StatelessComponent<{}> = () => ( + <Base context={context}> + <Hero> + <Animation /> + </Hero> + <CompilerComponent /> + <Content> + <ContentBlock main={true} title="Get started" /> + <ContentBlock title="Install"> + <Breakout> + <Code canCopy={true}>npm install @0x/sol-compiler --g</Code> + </Breakout> + </ContentBlock> + + <ContentBlock title="Run"> + <Breakout> + <Code>cd /your_project_dir && sol-compiler</Code> + </Breakout> + </ContentBlock> + + <ContentBlock title="Configure"> + <p> + Configure via a <InlineCode>compiler.json</InlineCode> file. + </p> + <Breakout> + <Code>mkdir compiler.json</Code> + </Breakout> + <p>Example of settings:</p> + <Breakout> + <Code language="json"> + {`{ + "contractsDir": "contracts", + "artifactsDir": "artifacts", + "contracts": "*", + "compilerSettings": { + "optimizer": { "enabled": false }, + "outputSelection": { + "*": { + "*": ["abi", "evm.bytecode.object"] + } + } + } +}`} + </Code> + </Breakout> + </ContentBlock> + </Content> + <Content dark={true}> + <ContentBlock main={true} title="Artifacts"> + <Lead> + Sol compiler uses solidity standard JSON output format for the artifacts. This way, you can define + which parts of the artifact you need. + </Lead> + </ContentBlock> + + <ContentBlock title="Production"> + <p> + Sol compiler uses solidity standard JSON output format for the artifacts. This way, you can define + which parts of the artifact you need. + </p> + <Breakout> + <Code isLight={true} language="json" isEtc={true}> + {`{ + "compilerSettings": { + "outputSelection": { + "*": { + "*": ["abi"] + } + } + } +}`} + </Code> + </Breakout> + <Breakout> + <Code isLight={true} language="json" isEtc={true}> + {`{ + "compilerOutput": { + "abi": [...], + }, +}`} + </Code> + </Breakout> + </ContentBlock> + <ContentBlock title="Development"> + <p> + Sometimes you need to use some debuggers or other dev tools and you’ll need more info in the + artifact. + </p> + <Breakout> + <Code isLight={true} language="json" isEtc={true}> + {`{ + "compilerSettings": { + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + } +}`} + </Code> + </Breakout> + + <Breakout> + <Code isLight={true} language="json" isEtc={true}> + {`{ + "compilerOutput": { + "abi": [...], + "evm": { + "bytecode": { + "object": "0xdeadbeef", + "sourceMap": "26:480:..." + }, + "deployedBytecode": { + "object": "0xdeadbeef", + "sourceMap": "26:480:0..." + } + } + } + "sources": { + "Migrations.sol": { + "id": 0 + } + }, +}`} + </Code> + </Breakout> + </ContentBlock> + </Content> + </Base> +); + +const root = document.getElementById('app'); + +if (root.hasChildNodes()) { + hydrate(<Compiler />, root); +} else { + render(<Compiler />, root); +} diff --git a/packages/dev-tools-pages/ts/pages/cov.tsx b/packages/dev-tools-pages/ts/pages/cov.tsx new file mode 100644 index 000000000..ff1ced27d --- /dev/null +++ b/packages/dev-tools-pages/ts/pages/cov.tsx @@ -0,0 +1,149 @@ +import * as React from 'react'; +import { hydrate, render } from 'react-dom'; +import * as Loadable from 'react-loadable'; + +import { context } from 'ts/context/cov'; + +import { Base } from 'ts/components/base'; +import { Breakout } from 'ts/components/breakout'; +import { Code } from 'ts/components/code'; +import { Content } from 'ts/components/content'; +import { ContentBlock } from 'ts/components/content-block'; +import { Hero } from 'ts/components/hero'; +import { InlineCode } from 'ts/components/inline-code'; +import { Intro, IntroAside, IntroLead } from 'ts/components/intro'; +import { List, ListItem } from 'ts/components/list'; +import { TabBlock, Tabs } from 'ts/components/tabs'; + +const Animation = Loadable({ + loader: () => System.import(/* webpackChunkName: 'cov-animation' */ 'ts/components/animations/cov'), + loading: () => <div />, + delay: 1000, + render(loadable: { CovAnimation: any }): React.ReactNode { + const Component = loadable.CovAnimation; + return <Component />; + }, +}); + +const Cov: React.StatelessComponent<{}> = () => ( + <Base context={context}> + <Hero> + <Animation /> + </Hero> + <Intro> + <IntroLead title="Measure your tests"> + <p> + When it comes to writing smart contracts, testing is one of the most important steps of the process. + In order to quantify the robustness of your Solidity testing suite, you need to measure its code + coverage. + </p> + </IntroLead> + <IntroAside> + <Code + language="javascript" + isDiff={true} + gutterLength={2} + gutter={[4, undefined, 4, 4, 4, undefined, 4, 2, 2, 2]} + > + {`+function executeTransaction(uint transactionId) + public ++ notExecuted(transactionId) ++ fullyConfirmed(transactionId) ++ pastTimeLock(transactionId) +{ ++ Transaction storage tx = transactions[transactionId] ++ tx.executed = true ++ if (tx.destination.call.value(tx.value)(tx.data)) ++ Execution(transactionId) + else { +- ExecutionFailure(transactionId) +- tx.executed = false + } +}`} + </Code> + </IntroAside> + </Intro> + + <Content> + <ContentBlock main={true} title="Get started" /> + <ContentBlock title="Prerequisites"> + <List> + <ListItem> + Use <a href="#">ganache-cli</a> as a backing node. + </ListItem> + <ListItem> + Understand and use <a href="#">web3-provider-engine</a>. + </ListItem> + </List> + </ContentBlock> + <ContentBlock title="Installation"> + <Breakout> + <Code>npm install @0x/sol-cov --save</Code> + </Breakout> + + <p> + Sol-cov is a subprovider that needs to be prepended to your <a href="#">provider engine</a>. + Depending on your project setup, you will need to use a specific ArtifactAdapter. Sol-cov ships with + the <InlineCode>SolCompilerArtifactAdapter</InlineCode> for use with <a href="#">Sol-compiler</a>{' '} + and <InlineCode>TruffleArtifactAdapter</InlineCode> for use with the{' '} + <a href="#">Truffle framework</a>. You can also write your own and support any artifact format. + </p> + + <Tabs> + <TabBlock title="Sol-compiler"> + <Code language="javascript"> + {`import { SolCompilerArtifactAdapter } from '@0x/sol-trace'; + +// Both artifactsDir and contractsDir are optional and will be fetched from compiler.json if not passed in +const artifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, contractsDir);`} + </Code> + </TabBlock> + <TabBlock title="Truffle"> + <Code language="javascript"> + {`import { TruffleArtifactAdapter } from '@0x/sol-trace'; + +const projectRoot = '.'; +const solcVersion = '0.4.24'; +const artifactAdapter = new TruffleArtifactAdapter(projectRoot, solcVersion);`} + </Code> + </TabBlock> + <TabBlock title="Custom"> + <Code language="javascript"> + {`import { AbstractArtifactAdapter } from '@0x/sol-trace'; + +class YourCustomArtifactsAdapter extends AbstractArtifactAdapter {...}; +const artifactAdapter = new YourCustomArtifactsAdapter(...);`} + </Code> + </TabBlock> + </Tabs> + <p> + Now that we have an <InlineCode>artifactAdapter</InlineCode>, we can create a{' '} + <InlineCode>RevertTraceSubprovider</InlineCode> and append it to our provider engine. + </p> + + <Breakout> + <Code language="javascript"> + {`import { ProviderEngine, RpcSubprovider } from 'web3-provider-engine'; +import { RevertTraceSubprovider } from '@0x/sol-cov'; + +const defaultFromAddress = "..."; // Some ethereum address with test funds +const revertTraceSubprovider = new RevertTraceSubprovider(artifactAdapter, defaultFromAddress); + +const providerEngine = new ProviderEngine(); +providerEngine.addProvider(revertTraceSubprovider); +providerEngine.addProvider(new RpcSubprovider({rpcUrl: 'http://localhost:8545'})); +providerEngine.start();`} + </Code> + </Breakout> + </ContentBlock> + </Content> + </Base> +); + +const root = document.getElementById('app'); + +if (root.hasChildNodes()) { + hydrate(<Cov />, root); +} else { + render(<Cov />, root); +} diff --git a/packages/dev-tools-pages/ts/pages/landing.tsx b/packages/dev-tools-pages/ts/pages/landing.tsx deleted file mode 100644 index a70a9de46..000000000 --- a/packages/dev-tools-pages/ts/pages/landing.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as _ from 'lodash'; -import * as React from 'react'; - -import { Button } from '../components/ui/button'; -import { Container } from '../components/ui/container'; -import { Text } from '../components/ui/text'; - -interface LandingProps {} - -interface LandingState {} - -export class Landing extends React.Component<LandingProps, LandingState> { - constructor(props: LandingProps) { - super(props); - } - public render(): React.ReactNode { - return ( - <Container id="landing" className="clearfix"> - <Container className="mx-auto p4" width="200px"> - <Button> - <Text fontColor="white">Click me!</Text> - </Button> - </Container> - </Container> - ); - } -} diff --git a/packages/dev-tools-pages/ts/pages/profiler.tsx b/packages/dev-tools-pages/ts/pages/profiler.tsx new file mode 100644 index 000000000..860e2c25e --- /dev/null +++ b/packages/dev-tools-pages/ts/pages/profiler.tsx @@ -0,0 +1,146 @@ +import * as React from 'react'; +import { hydrate, render } from 'react-dom'; +import * as Loadable from 'react-loadable'; + +import { context } from 'ts/context/profiler'; + +import { Base } from 'ts/components/base'; +import { Breakout } from 'ts/components/breakout'; +import { Code } from 'ts/components/code'; +import { Content } from 'ts/components/content'; +import { ContentBlock } from 'ts/components/content-block'; +import { Hero } from 'ts/components/hero'; +import { InlineCode } from 'ts/components/inline-code'; +import { Intro, IntroAside, IntroLead } from 'ts/components/intro'; +import { List, ListItem } from 'ts/components/list'; +import { TabBlock, Tabs } from 'ts/components/tabs'; + +const Animation = Loadable({ + loader: () => System.import(/* webpackChunkName: 'profiler-animation' */ 'ts/components/animations/profiler'), + loading: () => <div />, + delay: 1000, + render(loadable: { ProfilerAnimation: any }): React.ReactNode { + const Component = loadable.ProfilerAnimation; + return <Component />; + }, +}); + +const Profiler: React.StatelessComponent<{}> = () => ( + <Base context={context}> + <Hero> + <Animation /> + </Hero> + <Intro> + <IntroLead title="Outline gas usage"> + <p> + Sol-profiler gathers line-by-line gas usage for any transaction submitted through your provider. + This will help you find unexpected inefficiencies in parts of your smart contract, and take a + data-driven approach to optimizing it. + </p> + </IntroLead> + <IntroAside> + <Code + language="javascript" + isDiff={true} + gutterLength={6} + gutter={[15, 15, undefined, 21747, 20303, 1435]} + > + {`+function() public payable { ++ deposit(); +} ++function deposit() public payabble { ++ balanceOf[msg.sender] += msg.value; ++ Deposit(msg.sender, msg.value); +} +-function withdraw(uint wad) public { +- require(balanceOf[msg.sender] >= wad); +- balanceOf[msg.sender] -= wad; +- msg.sender.transfer(wad); +- Withdrawal(msg.sender, wad); +}`} + </Code> + </IntroAside> + </Intro> + <Content> + <ContentBlock main={true} title="Get started" /> + <ContentBlock title="Prerequisites"> + <List> + <ListItem> + Use <a href="#">ganache-cli</a> as a backing node. + </ListItem> + <ListItem> + Understand and use <a href="#">web3-provider-engine</a>. + </ListItem> + </List> + </ContentBlock> + <ContentBlock title="Installation"> + <Breakout> + <Code>npm install @0x/sol-trace --save</Code> + </Breakout> + + <p> + Sol-trace is a subprovider that needs to be prepended to your <a href="#">provider engine</a>. + Depending on your project setup, you will need to use a specific ArtifactAdapter. Sol-trace ships + with the <InlineCode>SolCompilerArtifactAdapter</InlineCode> for use with{' '} + <a href="#">Sol-compiler</a> and <InlineCode>TruffleArtifactAdapter</InlineCode> for use with the{' '} + <a href="#">Truffle framework</a>. You can also write your own and support any artifact format. + </p> + + <Tabs> + <TabBlock title="Sol-compiler"> + <Code language="javascript"> + {`import { SolCompilerArtifactAdapter } from '@0x/sol-trace'; + +// Both artifactsDir and contractsDir are optional and will be fetched from compiler.json if not passed in +const artifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, contractsDir);`} + </Code> + </TabBlock> + <TabBlock title="Truffle"> + <Code language="javascript"> + {`import { TruffleArtifactAdapter } from '@0x/sol-trace'; + +const projectRoot = '.'; +const solcVersion = '0.4.24'; +const artifactAdapter = new TruffleArtifactAdapter(projectRoot, solcVersion);`} + </Code> + </TabBlock> + <TabBlock title="Custom"> + <Code language="javascript"> + {`import { AbstractArtifactAdapter } from '@0x/sol-trace'; + +class YourCustomArtifactsAdapter extends AbstractArtifactAdapter {...}; +const artifactAdapter = new YourCustomArtifactsAdapter(...);`} + </Code> + </TabBlock> + </Tabs> + <p> + Now that we have an <InlineCode>artifactAdapter</InlineCode>, we can create a{' '} + <InlineCode>RevertTraceSubprovider</InlineCode> and append it to our provider engine. + </p> + + <Breakout> + <Code language="javascript"> + {`import { ProviderEngine, RpcSubprovider } from 'web3-provider-engine'; +import { RevertTraceSubprovider } from '@0x/sol-cov'; + +const defaultFromAddress = "..."; // Some ethereum address with test funds +const revertTraceSubprovider = new RevertTraceSubprovider(artifactAdapter, defaultFromAddress); + +const providerEngine = new ProviderEngine(); +providerEngine.addProvider(revertTraceSubprovider); +providerEngine.addProvider(new RpcSubprovider({rpcUrl: 'http://localhost:8545'})); +providerEngine.start();`} + </Code> + </Breakout> + </ContentBlock> + </Content> + </Base> +); + +const root = document.getElementById('app'); + +if (root.hasChildNodes()) { + hydrate(<Profiler />, root); +} else { + render(<Profiler />, root); +} diff --git a/packages/dev-tools-pages/ts/pages/trace.tsx b/packages/dev-tools-pages/ts/pages/trace.tsx new file mode 100644 index 000000000..a25d2eff8 --- /dev/null +++ b/packages/dev-tools-pages/ts/pages/trace.tsx @@ -0,0 +1,116 @@ +import * as React from 'react'; +import { hydrate, render } from 'react-dom'; +import * as Loadable from 'react-loadable'; + +import { context } from 'ts/context/trace'; + +import { Base } from 'ts/components/base'; +import { Breakout } from 'ts/components/breakout'; +import { Code } from 'ts/components/code'; +import { Content } from 'ts/components/content'; +import { ContentBlock } from 'ts/components/content-block'; +import { Hero } from 'ts/components/hero'; +import { InlineCode } from 'ts/components/inline-code'; +import { List, ListItem } from 'ts/components/list'; +import { TabBlock, Tabs } from 'ts/components/tabs'; +import { Trace as TraceComponent } from 'ts/components/trace'; + +const Animation = Loadable({ + loader: () => System.import(/* webpackChunkName: 'trace-animation' */ 'ts/components/animations/trace'), + loading: () => <div />, + delay: 1000, + render(loadable: { TraceAnimation: any }): React.ReactNode { + const Component = loadable.TraceAnimation; + return <Component />; + }, +}); + +const Trace: React.StatelessComponent<{}> = () => ( + <Base context={context}> + <Hero> + <Animation /> + </Hero> + <TraceComponent /> + <Content> + <ContentBlock main={true} title="Get started" /> + <ContentBlock title="Prerequisites"> + <List> + <ListItem> + Use <a href="#">ganache-cli</a> as a backing node. + </ListItem> + <ListItem> + Understand and use <a href="#">web3-provider-engine</a>. + </ListItem> + </List> + </ContentBlock> + <ContentBlock title="Installation"> + <Breakout> + <Code>npm install @0x/sol-trace --save</Code> + </Breakout> + + <p> + Sol-trace is a subprovider that needs to be prepended to your <a href="#">provider engine</a>. + Depending on your project setup, you will need to use a specific ArtifactAdapter. Sol-trace ships + with the <InlineCode>SolCompilerArtifactAdapter</InlineCode> for use with{' '} + <a href="#">Sol-compiler</a> and <InlineCode>TruffleArtifactAdapter</InlineCode> for use with the{' '} + <a href="#">Truffle framework</a>. You can also write your own and support any artifact format. + </p> + + <Tabs> + <TabBlock title="Sol-compiler"> + <Code language="javascript"> + {`import { SolCompilerArtifactAdapter } from '@0x/sol-trace'; + +// Both artifactsDir and contractsDir are optional and will be fetched from compiler.json if not passed in +const artifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, contractsDir);`} + </Code> + </TabBlock> + <TabBlock title="Truffle"> + <Code language="javascript"> + {`import { TruffleArtifactAdapter } from '@0x/sol-trace'; + +const projectRoot = '.'; +const solcVersion = '0.4.24'; +const artifactAdapter = new TruffleArtifactAdapter(projectRoot, solcVersion);`} + </Code> + </TabBlock> + <TabBlock title="Custom"> + <Code language="javascript"> + {`import { AbstractArtifactAdapter } from '@0x/sol-trace'; + +class YourCustomArtifactsAdapter extends AbstractArtifactAdapter {...}; +const artifactAdapter = new YourCustomArtifactsAdapter(...);`} + </Code> + </TabBlock> + </Tabs> + <p> + Now that we have an <InlineCode>artifactAdapter</InlineCode>, we can create a{' '} + <InlineCode>RevertTraceSubprovider</InlineCode> and append it to our provider engine. + </p> + + <Breakout> + <Code language="javascript"> + {`import { ProviderEngine, RpcSubprovider } from 'web3-provider-engine'; +import { RevertTraceSubprovider } from '@0x/sol-cov'; + +const defaultFromAddress = "..."; // Some ethereum address with test funds +const revertTraceSubprovider = new RevertTraceSubprovider(artifactAdapter, defaultFromAddress); + +const providerEngine = new ProviderEngine(); +providerEngine.addProvider(revertTraceSubprovider); +providerEngine.addProvider(new RpcSubprovider({rpcUrl: 'http://localhost:8545'})); +providerEngine.start();`} + </Code> + </Breakout> + </ContentBlock> + </Content> + </Base> +); + +const root = document.getElementById('app'); + +if (root.hasChildNodes()) { + hydrate(<Trace />, root); +} else { + render(<Trace />, root); +} diff --git a/packages/dev-tools-pages/ts/utils/utils.ts b/packages/dev-tools-pages/ts/utils/utils.ts deleted file mode 100644 index b274706a2..000000000 --- a/packages/dev-tools-pages/ts/utils/utils.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as bowser from 'bowser'; -import * as _ from 'lodash'; - -export const utils = { - getColSize(items: number): number { - const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid - const colSize = bassCssGridSize / items; - if (!_.isInteger(colSize)) { - throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`); - } - return colSize; - }, - getCurrentBaseUrl(): string { - const port = window.location.port; - const hasPort = !_.isUndefined(port); - const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`; - return baseUrl; - }, - onPageLoadPromise: new Promise<void>((resolve, _reject) => { - if (document.readyState === 'complete') { - resolve(); - return; - } - window.onload = () => resolve(); - }), - openUrl(url: string): void { - window.open(url, '_blank'); - }, - isMobileOperatingSystem(): boolean { - return bowser.mobile; - }, -}; diff --git a/packages/dev-tools-pages/ts/variables.tsx b/packages/dev-tools-pages/ts/variables.tsx new file mode 100644 index 000000000..5ea495036 --- /dev/null +++ b/packages/dev-tools-pages/ts/variables.tsx @@ -0,0 +1,34 @@ +import { css } from 'styled-components'; + +const colors = { + black: '#000000', + white: '#FFFFFF', + lightGray: '#F1F4F5', + gray: '#F1F2F7', + darkGray: '#E9ECED', + darkestGray: '#E2E5E6', + blueGray: '#ECEFF9', +}; + +interface SizesInterface { + [key: string]: number; +} + +const sizes: SizesInterface = { + xlarge: 1420, + large: 1000, + medium: 900, + small: 650, +}; + +const media = Object.keys(sizes).reduce((acc: any, label: string) => { + acc[label] = (args: any) => css` + @media (max-width: ${sizes[label] / 16}em) { + ${css(args)}; + } + `; + + return acc; +}, {}); + +export { colors, media }; diff --git a/packages/dev-tools-pages/webpack.config.js b/packages/dev-tools-pages/webpack.config.js index 6dfcf74e7..640297770 100644 --- a/packages/dev-tools-pages/webpack.config.js +++ b/packages/dev-tools-pages/webpack.config.js @@ -1,13 +1,24 @@ const path = require('path'); const webpack = require('webpack'); const TerserPlugin = require('terser-webpack-plugin'); +const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const HtmlWebpackPlugin = require('html-webpack-plugin'); const childProcess = require('child_process'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const CleanWebpackPlugin = require('clean-webpack-plugin'); + +const pages = require('./pages'); const config = { - entry: ['./ts/index.tsx'], + entry: { + compiler: './ts/pages/compiler.tsx', + cov: './ts/pages/cov.tsx', + profiler: './ts/pages/profiler.tsx', + trace: './ts/pages/trace.tsx', + }, output: { path: path.join(__dirname, '/public'), - filename: 'bundle.js', + filename: 'bundle-[name].js', chunkFilename: 'bundle-[name].js', publicPath: '/', }, @@ -48,6 +59,10 @@ const config = { test: /\.css$/, loaders: ['style-loader', 'css-loader'], }, + { + test: /\.svg$/, + loaders: ['react-svg-loader'], + }, ], }, optimization: { @@ -60,21 +75,36 @@ const config = { devServer: { port: 3572, disableHostCheck: true, + overlay: true, + historyApiFallback: true, }, }; module.exports = (_env, argv) => { - let plugins = []; + let plugins = [ + new CleanWebpackPlugin('public'), + ...pages.map(p => new HtmlWebpackPlugin(p)), + new CopyWebpackPlugin([ + { from: 'assets/crawl.html', to: 'index.html' }, + { from: 'assets/fonts', to: 'fonts' }, + { from: 'assets/images', to: 'images' }, + ]), + ]; if (argv.mode === 'development') { config.mode = 'development'; } else { config.mode = 'production'; + config.output.filename = 'bundle-[name].[chunkhash].js'; + config.output.chunkFilename = 'bundle-[name].[chunkhash].js'; + plugins = plugins.concat([ new webpack.DefinePlugin({ 'process.env': { - NODE_ENV: JSON.stringify(process.env.NODE_ENV), + NODE_ENV: JSON.stringify(process.env.NODE_ENV || config.mode), }, }), + // commented out to check the bundle when needed + //new BundleAnalyzerPlugin(), ]); } console.log('i 「atl」: Mode: ', config.mode); diff --git a/packages/dev-utils/CHANGELOG.json b/packages/dev-utils/CHANGELOG.json index b6cc74ce7..8f355466c 100644 --- a/packages/dev-utils/CHANGELOG.json +++ b/packages/dev-utils/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "1.0.22", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "1.0.21", "changes": [ { diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 9a55e4e58..04ee860b5 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.22 - _January 9, 2019_ + + * Dependencies updated + ## v1.0.21 - _December 13, 2018_ * Dependencies updated diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index a3eb4651a..cb27ad5c5 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/dev-utils", - "version": "1.0.21", + "version": "1.0.22", "engines": { "node": ">=6.12" }, @@ -41,11 +41,11 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/subproviders": "^2.1.8", - "@0x/types": "^1.4.1", + "@0x/subproviders": "^2.1.9", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@types/web3-provider-engine": "^14.0.0", "chai": "^4.0.1", "ethereum-types": "^1.1.4", diff --git a/packages/dev-utils/src/web3_factory.ts b/packages/dev-utils/src/web3_factory.ts index b22bcc88b..5f8981a46 100644 --- a/packages/dev-utils/src/web3_factory.ts +++ b/packages/dev-utils/src/web3_factory.ts @@ -17,6 +17,7 @@ export interface Web3Config { shouldThrowErrorsOnGanacheRPCResponse?: boolean; // default: true rpcUrl?: string; // default: localhost:8545 shouldUseFakeGasEstimate?: boolean; // default: true + ganacheDatabasePath?: string; // default: undefined, creates a tmp dir } export const web3Factory = { @@ -45,9 +46,14 @@ export const web3Factory = { const shouldThrowErrorsOnGanacheRPCResponse = _.isUndefined(config.shouldThrowErrorsOnGanacheRPCResponse) || config.shouldThrowErrorsOnGanacheRPCResponse; + if (!_.isUndefined(config.ganacheDatabasePath)) { + // Saving the snapshot to a local db. Ganache requires this directory to exist + fs.mkdirSync(config.ganacheDatabasePath); + } provider.addProvider( new GanacheSubprovider({ vmErrorsOnRPCResponse: shouldThrowErrorsOnGanacheRPCResponse, + db_path: config.ganacheDatabasePath, gasLimit: constants.GAS_LIMIT, logger, verbose: env.parseBoolean(EnvVars.VerboseGanache), diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index ca256399a..125a62cdf 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "1.1.0", + "changes": [ + { + "note": "Add support for MultiAssetProxy", + "pr": 1363 + } + ], + "timestamp": 1547040760 + }, + { "version": "1.0.16", "changes": [ { diff --git a/packages/fill-scenarios/CHANGELOG.md b/packages/fill-scenarios/CHANGELOG.md index e9b88547d..45cee1ce3 100644 --- a/packages/fill-scenarios/CHANGELOG.md +++ b/packages/fill-scenarios/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.0 - _January 9, 2019_ + + * Add support for MultiAssetProxy (#1363) + ## v1.0.16 - _December 13, 2018_ * Dependencies updated diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 7a9d21e0a..a38dcc1c3 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@0x/fill-scenarios", - "version": "1.0.16", + "version": "1.1.0", "description": "0x order fill scenario generator", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -28,14 +28,14 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^2.0.2", - "@0x/base-contract": "^3.0.10", - "@0x/contract-artifacts": "^1.1.2", - "@0x/order-utils": "^3.0.7", - "@0x/types": "^1.4.1", + "@0x/abi-gen-wrappers": "^2.1.0", + "@0x/base-contract": "^3.0.11", + "@0x/contract-artifacts": "^1.2.0", + "@0x/order-utils": "^3.1.0", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "ethereum-types": "^1.1.4", "ethers": "~4.0.4", "lodash": "^4.17.5" diff --git a/packages/fill-scenarios/src/fill_scenarios.ts b/packages/fill-scenarios/src/fill_scenarios.ts index 0154bcd0a..ce1f7f9ff 100644 --- a/packages/fill-scenarios/src/fill_scenarios.ts +++ b/packages/fill-scenarios/src/fill_scenarios.ts @@ -2,7 +2,7 @@ import { DummyERC20TokenContract, DummyERC721TokenContract, ExchangeContract } f import * as artifacts from '@0x/contract-artifacts'; import { assetDataUtils } from '@0x/order-utils'; import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; -import { AssetProxyId, ERC721AssetData, OrderWithoutExchangeAddress, SignedOrder } from '@0x/types'; +import { OrderWithoutExchangeAddress, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -150,39 +150,8 @@ export class FillScenarios { feeRecipientAddress: string, expirationTimeSeconds?: BigNumber, ): Promise<SignedOrder> { - const decodedMakerAssetData = assetDataUtils.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, - // tslint:disable-next-line:no-unnecessary-type-assertion - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } - const decodedTakerAssetData = assetDataUtils.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, - // tslint:disable-next-line:no-unnecessary-type-assertion - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } + await this._increaseBalanceAndAllowanceWithAssetDataAsync(makerAssetData, makerAddress, makerFillableAmount); + await this._increaseBalanceAndAllowanceWithAssetDataAsync(takerAssetData, takerAddress, takerFillableAmount); // Fees await Promise.all([ this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee), @@ -298,4 +267,30 @@ export class FillScenarios { }); await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); } + private async _increaseBalanceAndAllowanceWithAssetDataAsync( + assetData: string, + userAddress: string, + amount: BigNumber, + ): Promise<void> { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + await this._increaseERC20BalanceAndAllowanceAsync(decodedAssetData.tokenAddress, userAddress, amount); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + await this._increaseERC721BalanceAndAllowanceAsync( + decodedAssetData.tokenAddress, + userAddress, + decodedAssetData.tokenId, + ); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) { + const amountsElement = decodedAssetData.amounts[index]; + const totalAmount = amount.times(amountsElement); + await this._increaseBalanceAndAllowanceWithAssetDataAsync( + nestedAssetDataElement, + userAddress, + totalAmount, + ); + } + } + } } diff --git a/packages/instant/package.json b/packages/instant/package.json index 0a5e152ca..0888fc6a6 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -1,6 +1,6 @@ { "name": "@0x/instant", - "version": "1.0.4", + "version": "1.0.5", "engines": { "node": ">=6.12" }, @@ -41,15 +41,15 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { - "@0x/assert": "^1.0.20", - "@0x/asset-buyer": "^3.0.4", - "@0x/json-schemas": "^2.1.4", - "@0x/order-utils": "^3.0.7", - "@0x/subproviders": "^2.1.8", - "@0x/types": "^1.4.1", + "@0x/assert": "^1.0.21", + "@0x/asset-buyer": "^3.0.5", + "@0x/json-schemas": "^2.1.5", + "@0x/order-utils": "^3.1.0", + "@0x/subproviders": "^2.1.9", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "bowser": "^1.9.4", "copy-to-clipboard": "^3.0.8", "ethereum-types": "^1.1.4", @@ -76,7 +76,7 @@ "@types/react-dom": "^16.0.8", "@types/react-redux": "^6.0.9", "@types/redux": "^3.6.0", - "@types/styled-components": "^4.0.1", + "@types/styled-components": "4.0.1", "awesome-typescript-loader": "^5.2.1", "dotenv-cli": "^1.4.0", "enzyme": "^3.6.0", diff --git a/packages/instant/src/components/amount_placeholder.tsx b/packages/instant/src/components/amount_placeholder.tsx index 29ce8fafb..290e34a07 100644 --- a/packages/instant/src/components/amount_placeholder.tsx +++ b/packages/instant/src/components/amount_placeholder.tsx @@ -30,3 +30,5 @@ export const AmountPlaceholder: React.StatelessComponent<AmountPlaceholderProps> return <PlainPlaceholder color={props.color} />; } }; + +AmountPlaceholder.displayName = 'AmountPlaceholder'; diff --git a/packages/instant/src/components/animations/slide_animation.tsx b/packages/instant/src/components/animations/slide_animation.tsx index 5992bcba7..6ac47e9a6 100644 --- a/packages/instant/src/components/animations/slide_animation.tsx +++ b/packages/instant/src/components/animations/slide_animation.tsx @@ -11,6 +11,7 @@ export interface SlideAnimationProps { slideOutSettings: OptionallyScreenSpecific<PositionAnimationSettings>; zIndex?: OptionallyScreenSpecific<number>; height?: string; + onAnimationEnd?: () => void; } export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = props => { @@ -19,8 +20,15 @@ export const SlideAnimation: React.StatelessComponent<SlideAnimationProps> = pro } const positionSettings = props.animationState === 'slidIn' ? props.slideInSettings : props.slideOutSettings; return ( - <PositionAnimation height={props.height} positionSettings={positionSettings} zIndex={props.zIndex}> + <PositionAnimation + onAnimationEnd={props.onAnimationEnd} + height={props.height} + positionSettings={positionSettings} + zIndex={props.zIndex} + > {props.children} </PositionAnimation> ); }; + +SlideAnimation.displayName = 'SlideAnimation'; diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index 5c9c28ae4..551e857a5 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -32,7 +32,7 @@ export interface BuyButtonProps { onBuyFailure: (buyQuote: BuyQuote, txHash: string) => void; } -export class BuyButton extends React.Component<BuyButtonProps> { +export class BuyButton extends React.PureComponent<BuyButtonProps> { public static defaultProps = { onClick: util.boundNoop, onBuySuccess: util.boundNoop, diff --git a/packages/instant/src/components/buy_order_progress.tsx b/packages/instant/src/components/buy_order_progress.tsx index a19f5a4d0..11ac5d5e0 100644 --- a/packages/instant/src/components/buy_order_progress.tsx +++ b/packages/instant/src/components/buy_order_progress.tsx @@ -31,3 +31,5 @@ export const BuyOrderProgress: React.StatelessComponent<BuyOrderProgressProps> = } return null; }; + +BuyOrderProgress.displayName = 'BuyOrderProgress'; diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx index 833818900..1214559d1 100644 --- a/packages/instant/src/components/buy_order_state_buttons.tsx +++ b/packages/instant/src/components/buy_order_state_buttons.tsx @@ -71,3 +71,5 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP /> ); }; + +BuyOrderStateButtons.displayName = 'BuyOrderStateButtons'; diff --git a/packages/instant/src/components/erc20_asset_amount_input.tsx b/packages/instant/src/components/erc20_asset_amount_input.tsx index 4da82eb73..0418f9165 100644 --- a/packages/instant/src/components/erc20_asset_amount_input.tsx +++ b/packages/instant/src/components/erc20_asset_amount_input.tsx @@ -31,7 +31,7 @@ export interface ERC20AssetAmountInputState { currentFontSizePx: number; } -export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInputProps, ERC20AssetAmountInputState> { +export class ERC20AssetAmountInput extends React.PureComponent<ERC20AssetAmountInputProps, ERC20AssetAmountInputState> { public static defaultProps = { onChange: util.boundNoop, isDisabled: false, diff --git a/packages/instant/src/components/erc20_token_selector.tsx b/packages/instant/src/components/erc20_token_selector.tsx index cb8a8c797..a26fb5cf5 100644 --- a/packages/instant/src/components/erc20_token_selector.tsx +++ b/packages/instant/src/components/erc20_token_selector.tsx @@ -21,12 +21,12 @@ export interface ERC20TokenSelectorState { searchQuery: string; } -export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> { +export class ERC20TokenSelector extends React.PureComponent<ERC20TokenSelectorProps> { public state: ERC20TokenSelectorState = { searchQuery: '', }; public render(): React.ReactNode { - const { tokens, onTokenSelect } = this.props; + const { tokens } = this.props; return ( <Container height="100%"> <Container marginBottom="10px"> @@ -42,12 +42,11 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> tabIndex={-1} /> <Container overflow="scroll" height="calc(100% - 90px)" marginTop="10px"> - {_.map(tokens, token => { - if (!this._isTokenQueryMatch(token)) { - return null; - } - return <TokenSelectorRow key={token.assetData} token={token} onClick={onTokenSelect} />; - })} + <TokenRowFilter + tokens={tokens} + onClick={this._handleTokenClick} + searchQuery={this.state.searchQuery} + /> </Container> </Container> ); @@ -59,8 +58,32 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> }); analytics.trackTokenSelectorSearched(searchQuery); }; + private readonly _handleTokenClick = (token: ERC20Asset): void => { + this.props.onTokenSelect(token); + }; +} + +interface TokenRowFilterProps { + tokens: ERC20Asset[]; + onClick: (token: ERC20Asset) => void; + searchQuery: string; +} + +class TokenRowFilter extends React.Component<TokenRowFilterProps> { + public render(): React.ReactNode { + return _.map(this.props.tokens, token => { + if (!this._isTokenQueryMatch(token)) { + return null; + } + return <TokenSelectorRow key={token.assetData} token={token} onClick={this.props.onClick} />; + }); + } + public shouldComponentUpdate(nextProps: TokenRowFilterProps): boolean { + const arePropsDeeplyEqual = _.isEqual(nextProps, this.props); + return !arePropsDeeplyEqual; + } private readonly _isTokenQueryMatch = (token: ERC20Asset): boolean => { - const { searchQuery } = this.state; + const { searchQuery } = this.props; const searchQueryLowerCase = searchQuery.toLowerCase().trim(); if (searchQueryLowerCase === '') { return true; @@ -76,7 +99,7 @@ interface TokenSelectorRowProps { onClick: (token: ERC20Asset) => void; } -class TokenSelectorRow extends React.Component<TokenSelectorRowProps> { +class TokenSelectorRow extends React.PureComponent<TokenSelectorRowProps> { public render(): React.ReactNode { const { token } = this.props; const circleColor = token.metaData.primaryColor || 'black'; @@ -131,21 +154,23 @@ const getTokenIcon = (symbol: string): React.StatelessComponent | undefined => { } }; -const TokenSelectorRowIcon: React.StatelessComponent<TokenSelectorRowIconProps> = props => { - const { token } = props; - const iconUrlIfExists = token.metaData.iconUrl; +class TokenSelectorRowIcon extends React.PureComponent<TokenSelectorRowIconProps> { + public render(): React.ReactNode { + const { token } = this.props; + const iconUrlIfExists = token.metaData.iconUrl; - const TokenIcon = getTokenIcon(token.metaData.symbol); - const displaySymbol = assetUtils.bestNameForAsset(token); - if (!_.isUndefined(iconUrlIfExists)) { - return <img src={iconUrlIfExists} />; - } else if (!_.isUndefined(TokenIcon)) { - return <TokenIcon />; - } else { - return ( - <Text fontColor={ColorOption.white} fontSize="8px"> - {displaySymbol} - </Text> - ); + const TokenIcon = getTokenIcon(token.metaData.symbol); + const displaySymbol = assetUtils.bestNameForAsset(token); + if (!_.isUndefined(iconUrlIfExists)) { + return <img src={iconUrlIfExists} />; + } else if (!_.isUndefined(TokenIcon)) { + return <TokenIcon />; + } else { + return ( + <Text fontColor={ColorOption.white} fontSize="8px"> + {displaySymbol} + </Text> + ); + } } -}; +} diff --git a/packages/instant/src/components/install_wallet_panel_content.tsx b/packages/instant/src/components/install_wallet_panel_content.tsx index 481d82da0..1af3dafbb 100644 --- a/packages/instant/src/components/install_wallet_panel_content.tsx +++ b/packages/instant/src/components/install_wallet_panel_content.tsx @@ -18,7 +18,7 @@ import { Button } from './ui/button'; export interface InstallWalletPanelContentProps {} -export class InstallWalletPanelContent extends React.Component<InstallWalletPanelContentProps> { +export class InstallWalletPanelContent extends React.PureComponent<InstallWalletPanelContentProps> { public render(): React.ReactNode { const panelProps = this._getStandardPanelContentProps(); return <StandardPanelContent {...panelProps} />; diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index 5b1f9592d..e943f68d7 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -28,7 +28,7 @@ const ICON_WIDTH = 34; const ICON_HEIGHT = 34; const ICON_COLOR = ColorOption.white; -export class InstantHeading extends React.Component<InstantHeadingProps, {}> { +export class InstantHeading extends React.PureComponent<InstantHeadingProps, {}> { public render(): React.ReactNode { const iconOrAmounts = this._renderIcon() || this._renderAmountsSection(); return ( diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx index 9c10ef9e6..4db20b13e 100644 --- a/packages/instant/src/components/order_details.tsx +++ b/packages/instant/src/components/order_details.tsx @@ -26,7 +26,7 @@ export interface OrderDetailsProps { onBaseCurrencySwitchEth: () => void; onBaseCurrencySwitchUsd: () => void; } -export class OrderDetails extends React.Component<OrderDetailsProps> { +export class OrderDetails extends React.PureComponent<OrderDetailsProps> { public render(): React.ReactNode { const shouldShowUsdError = this.props.baseCurrency === BaseCurrency.USD && this._hadErrorFetchingUsdPrice(); return ( @@ -200,7 +200,7 @@ export interface OrderDetailsRowProps { primaryValue: React.ReactNode; secondaryValue?: React.ReactNode; } -export class OrderDetailsRow extends React.Component<OrderDetailsRowProps, {}> { +export class OrderDetailsRow extends React.PureComponent<OrderDetailsRowProps, {}> { public render(): React.ReactNode { return ( <Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}> diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx index abadf4bd6..ada9f7bab 100644 --- a/packages/instant/src/components/payment_method.tsx +++ b/packages/instant/src/components/payment_method.tsx @@ -24,7 +24,7 @@ export interface PaymentMethodProps { onUnlockWalletClick: () => void; } -export class PaymentMethod extends React.Component<PaymentMethodProps> { +export class PaymentMethod extends React.PureComponent<PaymentMethodProps> { public render(): React.ReactNode { return ( <Container width="100%" height="120px" padding="20px 20px 0px 20px"> diff --git a/packages/instant/src/components/payment_method_dropdown.tsx b/packages/instant/src/components/payment_method_dropdown.tsx index 872ac0831..e463e3eae 100644 --- a/packages/instant/src/components/payment_method_dropdown.tsx +++ b/packages/instant/src/components/payment_method_dropdown.tsx @@ -16,7 +16,7 @@ export interface PaymentMethodDropdownProps { network: Network; } -export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdownProps> { +export class PaymentMethodDropdown extends React.PureComponent<PaymentMethodDropdownProps> { public render(): React.ReactNode { const { accountAddress, accountEthBalanceInWei } = this.props; const value = format.ethAddress(accountAddress); diff --git a/packages/instant/src/components/placing_order_button.tsx b/packages/instant/src/components/placing_order_button.tsx index 2516b90b1..528a305dc 100644 --- a/packages/instant/src/components/placing_order_button.tsx +++ b/packages/instant/src/components/placing_order_button.tsx @@ -14,3 +14,5 @@ export const PlacingOrderButton: React.StatelessComponent<{}> = props => ( Placing Order… </Button> ); + +PlacingOrderButton.displayName = 'PlacingOrderButton'; diff --git a/packages/instant/src/components/scaling_amount_input.tsx b/packages/instant/src/components/scaling_amount_input.tsx index 4feb0502d..7dc1fdc0c 100644 --- a/packages/instant/src/components/scaling_amount_input.tsx +++ b/packages/instant/src/components/scaling_amount_input.tsx @@ -26,7 +26,7 @@ interface ScalingAmountInputState { } const { stringToMaybeBigNumber, areMaybeBigNumbersEqual } = maybeBigNumberUtil; -export class ScalingAmountInput extends React.Component<ScalingAmountInputProps, ScalingAmountInputState> { +export class ScalingAmountInput extends React.PureComponent<ScalingAmountInputProps, ScalingAmountInputState> { public static defaultProps = { onAmountChange: util.boundNoop, onFontSizeChange: util.boundNoop, diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx index 00aea37da..c31de1fb5 100644 --- a/packages/instant/src/components/scaling_input.tsx +++ b/packages/instant/src/components/scaling_input.tsx @@ -51,7 +51,7 @@ const defaultScalingSettings: ScalingSettings = { additionalInputSpaceInCh: 0.4, }; -export class ScalingInput extends React.Component<ScalingInputProps> { +export class ScalingInput extends React.PureComponent<ScalingInputProps> { public static defaultProps = { onChange: util.boundNoop, onFontSizeChange: util.boundNoop, diff --git a/packages/instant/src/components/secondary_button.tsx b/packages/instant/src/components/secondary_button.tsx index 705390e28..0714ce287 100644 --- a/packages/instant/src/components/secondary_button.tsx +++ b/packages/instant/src/components/secondary_button.tsx @@ -24,3 +24,4 @@ export const SecondaryButton: React.StatelessComponent<SecondaryButtonProps> = p SecondaryButton.defaultProps = { width: '100%', }; +SecondaryButton.displayName = 'SecondaryButton'; diff --git a/packages/instant/src/components/section_header.tsx b/packages/instant/src/components/section_header.tsx index d0974ebdc..2185b67ba 100644 --- a/packages/instant/src/components/section_header.tsx +++ b/packages/instant/src/components/section_header.tsx @@ -18,3 +18,4 @@ export const SectionHeader: React.StatelessComponent<SectionHeaderProps> = props </Text> ); }; +SectionHeader.displayName = 'SectionHeader'; diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx index b59e2a905..c7c6732cf 100644 --- a/packages/instant/src/components/sliding_error.tsx +++ b/packages/instant/src/components/sliding_error.tsx @@ -38,6 +38,8 @@ export const Error: React.StatelessComponent<ErrorProps> = props => ( </Container> ); +Error.displayName = 'Error'; + export interface SlidingErrorProps extends ErrorProps { animationState: SlideAnimationState; } @@ -94,3 +96,5 @@ export const SlidingError: React.StatelessComponent<SlidingErrorProps> = props = </SlideAnimation> ); }; + +SlidingError.displayName = 'SlidingError'; diff --git a/packages/instant/src/components/sliding_panel.tsx b/packages/instant/src/components/sliding_panel.tsx index 7f9037049..9b09a0d80 100644 --- a/packages/instant/src/components/sliding_panel.tsx +++ b/packages/instant/src/components/sliding_panel.tsx @@ -26,42 +26,48 @@ export const Panel: React.StatelessComponent<PanelProps> = ({ children, onClose </Container> ); +Panel.displayName = 'Panel'; + export interface SlidingPanelProps extends PanelProps { animationState: SlideAnimationState; + onAnimationEnd?: () => void; } -export const SlidingPanel: React.StatelessComponent<SlidingPanelProps> = props => { - if (props.animationState === 'none') { - return null; +export class SlidingPanel extends React.PureComponent<SlidingPanelProps> { + public render(): React.ReactNode { + if (this.props.animationState === 'none') { + return null; + } + const { animationState, onAnimationEnd, ...rest } = this.props; + const slideAmount = '100%'; + const slideUpSettings: PositionAnimationSettings = { + duration: '0.3s', + timingFunction: 'ease-in-out', + top: { + from: slideAmount, + to: '0px', + }, + position: 'absolute', + }; + const slideDownSettings: PositionAnimationSettings = { + duration: '0.3s', + timingFunction: 'ease-out', + top: { + from: '0px', + to: slideAmount, + }, + position: 'absolute', + }; + return ( + <SlideAnimation + slideInSettings={slideUpSettings} + slideOutSettings={slideDownSettings} + animationState={animationState} + height="100%" + onAnimationEnd={onAnimationEnd} + > + <Panel {...rest} /> + </SlideAnimation> + ); } - const { animationState, ...rest } = props; - const slideAmount = '100%'; - const slideUpSettings: PositionAnimationSettings = { - duration: '0.3s', - timingFunction: 'ease-in-out', - top: { - from: slideAmount, - to: '0px', - }, - position: 'absolute', - }; - const slideDownSettings: PositionAnimationSettings = { - duration: '0.3s', - timingFunction: 'ease-out', - top: { - from: '0px', - to: slideAmount, - }, - position: 'absolute', - }; - return ( - <SlideAnimation - slideInSettings={slideUpSettings} - slideOutSettings={slideDownSettings} - animationState={animationState} - height="100%" - > - <Panel {...rest} /> - </SlideAnimation> - ); -}; +} diff --git a/packages/instant/src/components/standard_panel_content.tsx b/packages/instant/src/components/standard_panel_content.tsx index 79b7bff24..f2987df82 100644 --- a/packages/instant/src/components/standard_panel_content.tsx +++ b/packages/instant/src/components/standard_panel_content.tsx @@ -71,3 +71,5 @@ export const StandardPanelContent: React.StatelessComponent<StandardPanelContent <Container>{action}</Container> </Container> ); + +StandardPanelContent.displayName = 'StandardPanelContent'; diff --git a/packages/instant/src/components/standard_sliding_panel.tsx b/packages/instant/src/components/standard_sliding_panel.tsx index 9f517d273..bcc9d3dce 100644 --- a/packages/instant/src/components/standard_sliding_panel.tsx +++ b/packages/instant/src/components/standard_sliding_panel.tsx @@ -9,7 +9,7 @@ export interface StandardSlidingPanelProps extends StandardSlidingPanelSettings onClose: () => void; } -export class StandardSlidingPanel extends React.Component<StandardSlidingPanelProps> { +export class StandardSlidingPanel extends React.PureComponent<StandardSlidingPanelProps> { public render(): React.ReactNode { const { animationState, content, onClose } = this.props; return ( diff --git a/packages/instant/src/components/time_counter.tsx b/packages/instant/src/components/time_counter.tsx index f9b68163c..93dc497d5 100644 --- a/packages/instant/src/components/time_counter.tsx +++ b/packages/instant/src/components/time_counter.tsx @@ -16,7 +16,7 @@ interface TimeCounterState { elapsedSeconds: number; } -export class TimeCounter extends React.Component<TimeCounterProps, TimeCounterState> { +export class TimeCounter extends React.PureComponent<TimeCounterProps, TimeCounterState> { public state = { elapsedSeconds: 0, }; diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx index fb3927088..b1644b871 100644 --- a/packages/instant/src/components/timed_progress_bar.tsx +++ b/packages/instant/src/components/timed_progress_bar.tsx @@ -17,7 +17,7 @@ export interface TimedProgressBarProps { * Goes from 0% -> PROGRESS_STALL_AT_WIDTH over time of expectedTimeMs * When hasEnded set to true, goes to 100% through animation of PROGRESS_FINISH_ANIMATION_TIME_MS length of time */ -export class TimedProgressBar extends React.Component<TimedProgressBarProps, {}> { +export class TimedProgressBar extends React.PureComponent<TimedProgressBarProps, {}> { private readonly _barRef = React.createRef<HTMLDivElement>(); public render(): React.ReactNode { diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx index 636eb8fc9..58d7d5871 100644 --- a/packages/instant/src/components/ui/container.tsx +++ b/packages/instant/src/components/ui/container.tsx @@ -36,6 +36,7 @@ export interface ContainerProps { cursor?: string; overflow?: string; darkenOnHover?: boolean; + rawHoverColor?: string; boxShadowOnHover?: boolean; flexGrow?: string | number; } @@ -87,6 +88,7 @@ export const Container = background-color: ${props => getBackgroundColor(props.theme, props.backgroundColor, props.rawBackgroundColor)}; border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')}; &:hover { + ${props => (props.rawHoverColor ? `background-color: ${props.rawHoverColor}` : '')} ${props => props.darkenOnHover ? `background-color: ${ diff --git a/packages/instant/src/components/ui/dropdown.tsx b/packages/instant/src/components/ui/dropdown.tsx index 02e87d639..8788d3d59 100644 --- a/packages/instant/src/components/ui/dropdown.tsx +++ b/packages/instant/src/components/ui/dropdown.tsx @@ -1,7 +1,8 @@ import * as _ from 'lodash'; +import { transparentize } from 'polished'; import * as React from 'react'; -import { ColorOption, completelyTransparent } from '../../style/theme'; +import { ColorOption, completelyTransparent, ThemeConsumer } from '../../style/theme'; import { zIndex } from '../../style/z_index'; import { Container } from './container'; @@ -26,7 +27,7 @@ export interface DropdownState { isOpen: boolean; } -export class Dropdown extends React.Component<DropdownProps, DropdownState> { +export class Dropdown extends React.PureComponent<DropdownProps, DropdownState> { public static defaultProps = { items: [], }; @@ -121,20 +122,26 @@ export interface DropdownItemProps extends DropdownItemConfig { } export const DropdownItem: React.StatelessComponent<DropdownItemProps> = ({ text, onClick, isLast }) => ( - <Container - onClick={onClick} - cursor="pointer" - darkenOnHover={true} - backgroundColor={ColorOption.white} - padding="0.8em" - borderTop="0" - border="1px solid" - borderRadius={isLast ? '0px 0px 4px 4px' : undefined} - width="100%" - borderColor={ColorOption.feintGrey} - > - <Text fontSize="14px" fontColor={ColorOption.darkGrey}> - {text} - </Text> - </Container> + <ThemeConsumer> + {theme => ( + <Container + onClick={onClick} + cursor="pointer" + rawHoverColor={transparentize(0.9, theme[ColorOption.primaryColor])} + backgroundColor={ColorOption.white} + padding="0.8em" + borderTop="0" + border="1px solid" + borderRadius={isLast ? '0px 0px 4px 4px' : undefined} + width="100%" + borderColor={ColorOption.feintGrey} + > + <Text fontSize="14px" fontColor={ColorOption.darkGrey}> + {text} + </Text> + </Container> + )} + </ThemeConsumer> ); + +DropdownItem.displayName = 'DropdownItem'; diff --git a/packages/instant/src/components/wallet_prompt.tsx b/packages/instant/src/components/wallet_prompt.tsx index c07cfe7b5..10433767f 100644 --- a/packages/instant/src/components/wallet_prompt.tsx +++ b/packages/instant/src/components/wallet_prompt.tsx @@ -45,3 +45,5 @@ WalletPrompt.defaultProps = { primaryColor: ColorOption.darkOrange, secondaryColor: ColorOption.lightOrange, }; + +WalletPrompt.displayName = 'WalletPrompt'; diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index 2267b4dbf..e9cb48e61 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -17,3 +17,5 @@ export const ZeroExInstant: React.StatelessComponent<ZeroExInstantProps> = props </div> ); }; + +ZeroExInstant.displayName = 'ZeroExInstant'; diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx index 0337c7714..e8c64d5d1 100644 --- a/packages/instant/src/components/zero_ex_instant_container.tsx +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -24,7 +24,10 @@ export interface ZeroExInstantContainerState { tokenSelectionPanelAnimationState: SlideAnimationState; } -export class ZeroExInstantContainer extends React.Component<ZeroExInstantContainerProps, ZeroExInstantContainerState> { +export class ZeroExInstantContainer extends React.PureComponent< + ZeroExInstantContainerProps, + ZeroExInstantContainerState +> { public state = { tokenSelectionPanelAnimationState: 'none' as SlideAnimationState, }; @@ -60,6 +63,7 @@ export class ZeroExInstantContainer extends React.Component<ZeroExInstantContain <SlidingPanel animationState={this.state.tokenSelectionPanelAnimationState} onClose={this._handlePanelCloseClickedX} + onAnimationEnd={this._handleSlidingPanelAnimationEnd} > <AvailableERC20TokenSelector onTokenSelect={this._handlePanelCloseAfterChose} /> </SlidingPanel> @@ -98,4 +102,11 @@ export class ZeroExInstantContainer extends React.Component<ZeroExInstantContain tokenSelectionPanelAnimationState: 'slidOut', }); }; + private readonly _handleSlidingPanelAnimationEnd = (): void => { + if (this.state.tokenSelectionPanelAnimationState === 'slidOut') { + // When the slidOut animation completes, don't keep the panel mounted. + // Performance optimization + this.setState({ tokenSelectionPanelAnimationState: 'none' }); + } + }; } diff --git a/packages/instant/src/components/zero_ex_instant_overlay.tsx b/packages/instant/src/components/zero_ex_instant_overlay.tsx index 96e560691..38a716091 100644 --- a/packages/instant/src/components/zero_ex_instant_overlay.tsx +++ b/packages/instant/src/components/zero_ex_instant_overlay.tsx @@ -49,3 +49,5 @@ export const ZeroExInstantOverlay: React.StatelessComponent<ZeroExInstantOverlay </ZeroExInstantProvider> ); }; + +ZeroExInstantOverlay.displayName = 'ZeroExInstantOverlay'; diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 2de327cd7..ec8e82ee3 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -21,7 +21,7 @@ import { providerStateFactory } from '../util/provider_state_factory'; export type ZeroExInstantProviderProps = ZeroExInstantBaseConfig; -export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> { +export class ZeroExInstantProvider extends React.PureComponent<ZeroExInstantProviderProps> { private readonly _store: Store; private _accountUpdateHeartbeat?: Heartbeater; private _buyQuoteHeartbeat?: Heartbeater; diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 22f0cb6a4..67558c84a 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -16,6 +16,7 @@ export const ONE_SECOND_MS = 1000; export const ONE_MINUTE_MS = ONE_SECOND_MS * 60; export const GIT_SHA = process.env.GIT_SHA; export const NODE_ENV = process.env.NODE_ENV; +export const SLIPPAGE_PERCENTAGE = 0.2; export const NPM_PACKAGE_VERSION = process.env.NPM_PACKAGE_VERSION; export const DEFAULT_UNKOWN_ASSET_NAME = '???'; export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5; diff --git a/packages/instant/src/containers/latest_error.tsx b/packages/instant/src/containers/latest_error.tsx index 0d4349124..6da4558ef 100644 --- a/packages/instant/src/containers/latest_error.tsx +++ b/packages/instant/src/containers/latest_error.tsx @@ -14,7 +14,7 @@ import { zIndex } from '../style/z_index'; import { Asset, DisplayStatus, Omit, SlideAnimationState } from '../types'; import { errorFlasher } from '../util/error_flasher'; -export interface LatestErrorComponentProps { +interface LatestErrorComponentProps { asset?: Asset; latestErrorMessage?: string; animationState: SlideAnimationState; @@ -22,7 +22,7 @@ export interface LatestErrorComponentProps { onOverlayClick: () => void; } -export const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => { +const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => { if (!props.latestErrorMessage) { // Render a hidden SlidingError such that instant does not move when a real error is rendered. return ( diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index faaeb7c22..b009a327f 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -1,8 +1,10 @@ -import { AssetBuyerError } from '@0x/asset-buyer'; +import { AssetBuyerError, InsufficientAssetLiquidityError } from '@0x/asset-buyer'; import { AssetProxyId, ObjectMap } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; -import { DEFAULT_UNKOWN_ASSET_NAME } from '../constants'; +import { BIG_NUMBER_ZERO, DEFAULT_UNKOWN_ASSET_NAME } from '../constants'; import { assetDataNetworkMapping } from '../data/asset_data_network_mapping'; import { Asset, AssetMetaData, ERC20Asset, Network, ZeroExInstantError } from '../types'; @@ -111,6 +113,21 @@ export const assetUtils = { assetBuyerErrorMessage: (asset: ERC20Asset, error: Error): string | undefined => { if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); + if ( + error instanceof InsufficientAssetLiquidityError && + error.amountAvailableToFill.greaterThan(BIG_NUMBER_ZERO) + ) { + const unitAmountAvailableToFill = Web3Wrapper.toUnitAmount( + error.amountAvailableToFill, + asset.metaData.decimals, + ); + const roundedUnitAmountAvailableToFill = unitAmountAvailableToFill.round(2, BigNumber.ROUND_DOWN); + + if (roundedUnitAmountAvailableToFill.greaterThan(BIG_NUMBER_ZERO)) { + return `There are only ${roundedUnitAmountAvailableToFill} ${assetName} available to buy`; + } + } + return `Not enough ${assetName} available`; } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { return 'Not enough ZRX available'; diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 6191c92e3..37974e71c 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -5,6 +5,7 @@ import * as _ from 'lodash'; import { Dispatch } from 'redux'; import { oc } from 'ts-optchain'; +import { SLIPPAGE_PERCENTAGE } from '../constants'; import { Action, actions } from '../redux/actions'; import { AffiliateInfo, ERC20Asset, QuoteFetchOrigin } from '../types'; import { analytics } from '../util/analytics'; @@ -33,8 +34,12 @@ export const buyQuoteUpdater = { } const feePercentage = oc(options.affiliateInfo).feePercentage(); let newBuyQuote: BuyQuote | undefined; + const slippagePercentage = SLIPPAGE_PERCENTAGE; try { - newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage }); + newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { + feePercentage, + slippagePercentage, + }); } catch (error) { const errorMessage = assetUtils.assetBuyerErrorMessage(asset, error); diff --git a/packages/instant/test/util/asset.test.ts b/packages/instant/test/util/asset.test.ts index fc4e4e2e4..402a556d5 100644 --- a/packages/instant/test/util/asset.test.ts +++ b/packages/instant/test/util/asset.test.ts @@ -1,5 +1,6 @@ -import { AssetBuyerError } from '@0x/asset-buyer'; +import { AssetBuyerError, BigNumber, InsufficientAssetLiquidityError } from '@0x/asset-buyer'; import { AssetProxyId, ObjectMap } from '@0x/types'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { Asset, AssetMetaData, ERC20Asset, ERC20AssetMetaData, Network, ZeroExInstantError } from '../../src/types'; import { assetUtils } from '../../src/util/asset'; @@ -19,6 +20,16 @@ const ZRX_ASSET: ERC20Asset = { const META_DATA_MAP: ObjectMap<AssetMetaData> = { [ZRX_ASSET_DATA]: ZRX_META_DATA, }; +const WAX_ASSET: ERC20Asset = { + assetData: '0xf47261b000000000000000000000000039bb259f66e1c59d5abef88375979b4d20d98022', + metaData: { + assetProxyId: AssetProxyId.ERC20, + decimals: 8, + primaryColor: '#EDB740', + symbol: 'wax', + name: 'WAX', + }, +}; describe('assetDataUtil', () => { describe('bestNameForAsset', () => { @@ -47,13 +58,39 @@ describe('assetDataUtil', () => { }); }); describe('assetBuyerErrorMessage', () => { - it('should return message for InsufficientAssetLiquidity', () => { + it('should return message for generic InsufficientAssetLiquidity error', () => { const insufficientAssetError = new Error(AssetBuyerError.InsufficientAssetLiquidity); expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientAssetError)).toEqual( 'Not enough ZRX available', ); }); - it('should return message for InsufficientAssetLiquidity', () => { + describe('InsufficientAssetLiquidityError', () => { + it('should return custom message for token w/ 18 decimals', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(20.059), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 20.05 ZRX available to buy'); + }); + it('should return custom message for token w/ 18 decimals and small amount available', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.01), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 0.01 ZRX available to buy'); + }); + it('should return custom message for token w/ 8 decimals', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 8); + expect( + assetUtils.assetBuyerErrorMessage(WAX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('There are only 3 WAX available to buy'); + }); + it('should return generic message when amount available rounds to zero', () => { + const amountAvailable = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.002), 18); + expect( + assetUtils.assetBuyerErrorMessage(ZRX_ASSET, new InsufficientAssetLiquidityError(amountAvailable)), + ).toEqual('Not enough ZRX available'); + }); + }); + it('should return message for InsufficientZrxLiquidity', () => { const insufficientZrxError = new Error(AssetBuyerError.InsufficientZrxLiquidity); expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientZrxError)).toEqual( 'Not enough ZRX available', diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index 201190145..79435426b 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "2.1.5", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "2.1.4", "changes": [ { diff --git a/packages/json-schemas/CHANGELOG.md b/packages/json-schemas/CHANGELOG.md index 7e407bdda..6d3373336 100644 --- a/packages/json-schemas/CHANGELOG.md +++ b/packages/json-schemas/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.5 - _January 9, 2019_ + + * Dependencies updated + ## v2.1.4 - _December 13, 2018_ * Dependencies updated diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index 57dd9dc00..5103486db 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@0x/json-schemas", - "version": "2.1.4", + "version": "2.1.5", "engines": { "node": ">=6.12" }, @@ -46,7 +46,7 @@ }, "devDependencies": { "@0x/tslint-config": "^2.0.0", - "@0x/utils": "^2.0.8", + "@0x/utils": "^2.1.1", "@types/lodash.foreach": "^4.5.3", "@types/lodash.values": "^4.3.3", "@types/mocha": "^2.2.42", diff --git a/packages/json-schemas/schemas/asset_pairs_request_opts_schema.json b/packages/json-schemas/schemas/asset_pairs_request_opts_schema.json index 174a8fdc3..fad0bd371 100644 --- a/packages/json-schemas/schemas/asset_pairs_request_opts_schema.json +++ b/packages/json-schemas/schemas/asset_pairs_request_opts_schema.json @@ -1,5 +1,5 @@ { - "id": "/AssetPairsRequestOpts", + "id": "/AssetPairsRequestOptsSchema", "type": "object", "properties": { "assetDataA": { "$ref": "/hexSchema" }, diff --git a/packages/json-schemas/schemas/call_data_schema.json b/packages/json-schemas/schemas/call_data_schema.json index c5972e8c1..e5e6e3282 100644 --- a/packages/json-schemas/schemas/call_data_schema.json +++ b/packages/json-schemas/schemas/call_data_schema.json @@ -4,13 +4,13 @@ "from": { "$ref": "/addressSchema" }, "to": { "$ref": "/addressSchema" }, "value": { - "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }] + "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumberSchema" }] }, "gas": { - "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }] + "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumberSchema" }] }, "gasPrice": { - "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }] + "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumberSchema" }] }, "data": { "type": "string", diff --git a/packages/json-schemas/schemas/ec_signature_schema.json b/packages/json-schemas/schemas/ec_signature_schema.json index bc79ca5e9..52ccfe7bb 100644 --- a/packages/json-schemas/schemas/ec_signature_schema.json +++ b/packages/json-schemas/schemas/ec_signature_schema.json @@ -1,5 +1,5 @@ { - "id": "/ECSignature", + "id": "/ecSignatureSchema", "properties": { "v": { "type": "number", diff --git a/packages/json-schemas/schemas/js_number.json b/packages/json-schemas/schemas/js_number_schema.json index 6a72d92c0..7df1c4747 100644 --- a/packages/json-schemas/schemas/js_number.json +++ b/packages/json-schemas/schemas/js_number_schema.json @@ -1,5 +1,5 @@ { - "id": "/jsNumber", + "id": "/jsNumberSchema", "type": "number", "minimum": 0 } diff --git a/packages/json-schemas/schemas/order_config_request_schema.json b/packages/json-schemas/schemas/order_config_request_schema.json index ca9b2e30e..19b043e7f 100644 --- a/packages/json-schemas/schemas/order_config_request_schema.json +++ b/packages/json-schemas/schemas/order_config_request_schema.json @@ -1,5 +1,5 @@ { - "id": "/OrderConfigRequest", + "id": "/OrderConfigRequestSchema", "type": "object", "properties": { "makerAddress": { "$ref": "/addressSchema" }, diff --git a/packages/json-schemas/schemas/orderbook_request_schema.json b/packages/json-schemas/schemas/orderbook_request_schema.json index 27848bdcb..5ce6e8ab0 100644 --- a/packages/json-schemas/schemas/orderbook_request_schema.json +++ b/packages/json-schemas/schemas/orderbook_request_schema.json @@ -1,9 +1,9 @@ { - "id": "/OrderBookRequest", + "id": "/OrderbookRequestSchema", "type": "object", "properties": { "baseAssetData": { "$ref": "/hexSchema" }, "quoteAssetData": { "$ref": "/hexSchema" } }, "required": ["baseAssetData", "quoteAssetData"] -}
\ No newline at end of file +} diff --git a/packages/json-schemas/schemas/orders_request_opts_schema.json b/packages/json-schemas/schemas/orders_request_opts_schema.json index 10da51060..4c1b9b4e9 100644 --- a/packages/json-schemas/schemas/orders_request_opts_schema.json +++ b/packages/json-schemas/schemas/orders_request_opts_schema.json @@ -1,5 +1,5 @@ { - "id": "/OrdersRequestOpts", + "id": "/OrdersRequestOptsSchema", "type": "object", "properties": { "makerAssetProxyId": { "$ref": "/hexSchema" }, diff --git a/packages/json-schemas/schemas/paged_request_opts_schema.json b/packages/json-schemas/schemas/paged_request_opts_schema.json index 7cfc73947..f143c28b0 100644 --- a/packages/json-schemas/schemas/paged_request_opts_schema.json +++ b/packages/json-schemas/schemas/paged_request_opts_schema.json @@ -1,5 +1,5 @@ { - "id": "/PagedRequestOpts", + "id": "/PagedRequestOptsSchema", "type": "object", "properties": { "page": { "type": "number" }, diff --git a/packages/json-schemas/schemas/request_opts_schema.json b/packages/json-schemas/schemas/request_opts_schema.json index b50547d18..2206f5016 100644 --- a/packages/json-schemas/schemas/request_opts_schema.json +++ b/packages/json-schemas/schemas/request_opts_schema.json @@ -1,5 +1,5 @@ { - "id": "/RequestOpts", + "id": "/RequestOptsSchema", "type": "object", "properties": { "networkId": { "type": "number" } diff --git a/packages/json-schemas/schemas/tx_data_schema.json b/packages/json-schemas/schemas/tx_data_schema.json index 4643521ce..8c3daba4e 100644 --- a/packages/json-schemas/schemas/tx_data_schema.json +++ b/packages/json-schemas/schemas/tx_data_schema.json @@ -4,13 +4,13 @@ "from": { "$ref": "/addressSchema" }, "to": { "$ref": "/addressSchema" }, "value": { - "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }] + "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumberSchema" }] }, "gas": { - "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }] + "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumberSchema" }] }, "gasPrice": { - "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumber" }] + "oneOf": [{ "$ref": "/numberSchema" }, { "$ref": "/jsNumberSchema" }] }, "data": { "type": "string", diff --git a/packages/json-schemas/schemas/whole_number_schema.json b/packages/json-schemas/schemas/whole_number_schema.json index 944ce820e..aa469954c 100644 --- a/packages/json-schemas/schemas/whole_number_schema.json +++ b/packages/json-schemas/schemas/whole_number_schema.json @@ -1,5 +1,12 @@ { "id": "/wholeNumberSchema", - "type": "string", - "pattern": "^\\d+$" + "anyOf": [ + { + "type": "string", + "pattern": "^\\d+$" + }, + { + "type": "integer" + } + ] } diff --git a/packages/json-schemas/src/schema_validator.ts b/packages/json-schemas/src/schema_validator.ts index 3f303137b..43647b594 100644 --- a/packages/json-schemas/src/schema_validator.ts +++ b/packages/json-schemas/src/schema_validator.ts @@ -8,12 +8,18 @@ import { schemas } from './schemas'; */ export class SchemaValidator { private readonly _validator: Validator; + private static _assertSchemaDefined(schema: Schema): void { + if (schema === undefined) { + throw new Error(`Cannot add undefined schema`); + } + } /** * Instantiates a SchemaValidator instance */ constructor() { this._validator = new Validator(); for (const schema of values(schemas)) { + SchemaValidator._assertSchemaDefined(schema); this._validator.addSchema(schema, schema.id); } } @@ -24,6 +30,7 @@ export class SchemaValidator { * @param schema The schema to add */ public addSchema(schema: Schema): void { + SchemaValidator._assertSchemaDefined(schema); this._validator.addSchema(schema, schema.id); } // In order to validate a complex JS object using jsonschema, we must replace any complex @@ -37,6 +44,7 @@ export class SchemaValidator { * @returns The results of the validation */ public validate(instance: any, schema: Schema): ValidatorResult { + SchemaValidator._assertSchemaDefined(schema); const jsonSchemaCompatibleObject = JSON.parse(JSON.stringify(instance)); return this._validator.validate(jsonSchemaCompatibleObject, schema); } diff --git a/packages/json-schemas/src/schemas.ts b/packages/json-schemas/src/schemas.ts index 050f4e625..9e8eb6959 100644 --- a/packages/json-schemas/src/schemas.ts +++ b/packages/json-schemas/src/schemas.ts @@ -8,7 +8,7 @@ import * as ecSignatureSchema from '../schemas/ec_signature_schema.json'; import * as eip712TypedDataSchema from '../schemas/eip712_typed_data_schema.json'; import * as hexSchema from '../schemas/hex_schema.json'; import * as indexFilterValuesSchema from '../schemas/index_filter_values_schema.json'; -import * as jsNumber from '../schemas/js_number.json'; +import * as jsNumber from '../schemas/js_number_schema.json'; import * as numberSchema from '../schemas/number_schema.json'; import * as orderCancellationRequestsSchema from '../schemas/order_cancel_schema.json'; import * as orderConfigRequestSchema from '../schemas/order_config_request_schema.json'; diff --git a/packages/json-schemas/tsconfig.json b/packages/json-schemas/tsconfig.json index ec573290c..7d7ce1d7e 100644 --- a/packages/json-schemas/tsconfig.json +++ b/packages/json-schemas/tsconfig.json @@ -42,7 +42,7 @@ "./schemas/relayer_api_orders_schema.json", "./schemas/signed_orders_schema.json", "./schemas/token_schema.json", - "./schemas/js_number.json", + "./schemas/js_number_schema.json", "./schemas/zero_ex_transaction_schema.json", "./schemas/tx_data_schema.json", "./schemas/index_filter_values_schema.json", diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 0e9fa4920..65232639b 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -1,6 +1,6 @@ { "name": "@0x/metacoin", - "version": "0.0.32", + "version": "0.0.33", "engines": { "node": ">=6.12" }, @@ -29,16 +29,16 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@0x/abi-gen": "^1.0.19", + "@0x/abi-gen": "^1.0.20", "@0x/abi-gen-templates": "^1.0.1", - "@0x/base-contract": "^3.0.10", - "@0x/sol-cov": "^2.1.16", - "@0x/subproviders": "^2.1.8", + "@0x/base-contract": "^3.0.11", + "@0x/sol-cov": "^2.1.17", + "@0x/subproviders": "^2.1.9", "@0x/tslint-config": "^2.0.0", - "@0x/types": "^1.4.1", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@types/mocha": "^5.2.2", "copyfiles": "^2.0.0", "ethereum-types": "^1.1.4", @@ -47,8 +47,8 @@ "run-s": "^0.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.21", - "@0x/sol-compiler": "^1.1.16", + "@0x/dev-utils": "^1.0.22", + "@0x/sol-compiler": "^2.0.0", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "chai-bignumber": "^2.0.1", diff --git a/packages/migrations/.gitignore b/packages/migrations/.gitignore new file mode 100644 index 000000000..4de81c5a8 --- /dev/null +++ b/packages/migrations/.gitignore @@ -0,0 +1,2 @@ +*.zip +0x_ganache_snapshot diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index e7b18f12b..e09106bcd 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "2.3.0", + "changes": [ + { + "note": "Added migrations for Dutch Auction contract", + "pr": 1465 + } + ], + "timestamp": 1547040760 + }, + { "version": "2.2.2", "changes": [ { diff --git a/packages/migrations/CHANGELOG.md b/packages/migrations/CHANGELOG.md index 0b7b9a364..420769187 100644 --- a/packages/migrations/CHANGELOG.md +++ b/packages/migrations/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.3.0 - _January 9, 2019_ + + * Added migrations for Dutch Auction contract (#1465) + ## v2.2.2 - _December 13, 2018_ * Dependencies updated diff --git a/packages/migrations/Dockerfile b/packages/migrations/Dockerfile new file mode 100644 index 000000000..c4d6128c2 --- /dev/null +++ b/packages/migrations/Dockerfile @@ -0,0 +1,15 @@ +FROM mhart/alpine-node:10 + +WORKDIR /usr/src/app + +RUN npm install -g ganache-cli@6.1.6 + +ENV MNEMONIC "concert load couple harbor equip island argue ramp clarify fence smart topic" +ENV NETWORK_ID 50 +ENV VERSION "latest" +ENV SNAPSHOT_HOST "http://ganache-snapshots.0x.org.s3-website.us-east-2.amazonaws.com" +ENV SNAPSHOT_NAME "0x_ganache_snapshot" +EXPOSE 8545 + +CMD [ "sh", "-c", "wget $SNAPSHOT_HOST/$SNAPSHOT_NAME-$VERSION.zip -O snapshot.zip && unzip snapshot.zip && ganache-cli --gasLimit 10000000 --db $SNAPSHOT_NAME --noVMErrorsOnRPCResponse -p 8545 --networkId \"$NETWORK_ID\" -m \"$MNEMONIC\" -h 0.0.0.0"] + diff --git a/packages/migrations/README.md b/packages/migrations/README.md index b90d730eb..1e8b92bf8 100644 --- a/packages/migrations/README.md +++ b/packages/migrations/README.md @@ -57,3 +57,44 @@ In order to migrate the V2 0x smart contracts to TestRPC/Ganache running at `htt ```bash yarn migrate:v2 ``` + +### Publish + +#### 0x Ganache Snapshot + +The 0x Ganache snapshot can be generated and published in this package. In order to build the snapshot for this version of migrations run: + +```bash +yarn build:snapshot +``` + +This will run the migrations in Ganache and output a zip file to be uploaded to the s3 bucket. For example, after running this command you will have created `0x_ganache_snapshot-2.2.2.zip`. To publish the zip file to the s3 bucket run: + +```bash +yarn publish:snapshot +``` + +This snapshot will now be publicly available at http://ganache-snapshots.0x.org.s3.amazonaws.com/0x_ganache_snapshot-latest.zip and also versioned with the package.json version. + +#### 0x Ganache Docker Image + +We also publish a simple docker image which downloads the latest snapshot, extracts and runs Ganache. This is not required to be built when migrations change as it always downloads and runs the latest zip file. If you have made changes to the Dockerfile then a publish of the image is required. To do this run: + +```bash +yarn build:snapshot:docker +yarn publish:snapshot:docker +``` + +The result is a published docker image to the 0xorg docker registry. To start the docker image run: + +```bash +docker run -p 8545:8545 -ti 0xorg/ganache-cli:latest +``` + +This will pull the latest zip in the s3 bucket, extract and start Ganache with the snapshot. + +In the event you need a specific version of the published Ganache snapshot run the following specifying the VERSION environment variable: + +```bash +docker run -e VERSION=2.2.2 -p 8545:8545 -ti 0xorg/ganache-cli:latest +``` diff --git a/packages/migrations/package.json b/packages/migrations/package.json index 72ffe67b2..774236ab4 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -1,6 +1,6 @@ { "name": "@0x/migrations", - "version": "2.2.2", + "version": "2.3.0", "engines": { "node": ">=6.12" }, @@ -10,13 +10,22 @@ "scripts": { "build": "tsc -b", "build:ci": "yarn build", - "clean": "shx rm -rf lib", + "clean": "shx rm -rf lib ${npm_package_config_snapshot_name} ${npm_package_config_snapshot_name}-*.zip", "lint": "tslint --format stylish --project .", "migrate:v2": "run-s build script:migrate:v2", + "migrate:v2:snapshot": "run-s build script:migrate:v2:snapshot", "script:migrate:v2": "node ./lib/migrate.js", - "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" + "script:migrate:v2:snapshot": "node ./lib/migrate_snapshot.js", + "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES", + "build:snapshot": "rm -rf ${npm_package_config_snapshot_name} && yarn migrate:v2:snapshot && zip -r \"${npm_package_config_snapshot_name}-${npm_package_version}.zip\" ${npm_package_config_snapshot_name}", + "build:snapshot:docker": "docker build --tag ${npm_package_config_docker_snapshot_name}:${npm_package_version} --tag ${npm_package_config_docker_snapshot_name}:latest .", + "publish:snapshot": "aws s3 cp ${npm_package_config_snapshot_name}-${npm_package_version}.zip ${npm_package_config_s3_snapshot_bucket} && aws s3 cp ${npm_package_config_s3_snapshot_bucket}/${npm_package_config_snapshot_name}-${npm_package_version}.zip ${npm_package_config_s3_snapshot_bucket}/${npm_package_config_snapshot_name}-latest.zip", + "publish:snapshot:docker": "docker push ${npm_package_config_docker_snapshot_name}:latest" }, "config": { + "s3_snapshot_bucket": "s3://ganache-snapshots.0x.org", + "docker_snapshot_name": "0xorg/ganache-cli", + "snapshot_name": "0x_ganache_snapshot", "postpublish": { "assets": [] } @@ -26,9 +35,9 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0x/dev-utils": "^1.0.21", + "@0x/dev-utils": "^1.0.22", "@0x/tslint-config": "^2.0.0", - "@0x/types": "^1.4.1", + "@0x/types": "^1.5.0", "@types/yargs": "^10.0.0", "make-promises-safe": "^1.1.0", "npm-run-all": "^4.1.2", @@ -39,16 +48,16 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0x/abi-gen-wrappers": "^2.0.2", - "@0x/base-contract": "^3.0.10", - "@0x/contract-addresses": "^2.0.0", - "@0x/contract-artifacts": "^1.1.2", - "@0x/order-utils": "^3.0.7", - "@0x/sol-compiler": "^1.1.16", - "@0x/subproviders": "^2.1.8", + "@0x/abi-gen-wrappers": "^2.1.0", + "@0x/base-contract": "^3.0.11", + "@0x/contract-addresses": "^2.1.0", + "@0x/contract-artifacts": "^1.2.0", + "@0x/order-utils": "^3.1.0", + "@0x/sol-compiler": "^2.0.0", + "@0x/subproviders": "^2.1.9", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@ledgerhq/hw-app-eth": "^4.3.0", "ethereum-types": "^1.1.4", "ethers": "~4.0.4", diff --git a/packages/migrations/src/migrate_snapshot.ts b/packages/migrations/src/migrate_snapshot.ts new file mode 100644 index 000000000..13fb063da --- /dev/null +++ b/packages/migrations/src/migrate_snapshot.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node +import { devConstants, web3Factory } from '@0x/dev-utils'; +import { logUtils } from '@0x/utils'; +import { Provider } from 'ethereum-types'; +import * as fs from 'fs'; +import * as _ from 'lodash'; +import * as path from 'path'; + +import { runMigrationsAsync } from './migration'; + +(async () => { + let providerConfigs; + let provider: Provider; + let txDefaults; + const packageJsonPath = path.join(__dirname, '..', 'package.json'); + const packageJsonString = fs.readFileSync(packageJsonPath, 'utf8'); + const packageJson = JSON.parse(packageJsonString); + if (_.isUndefined(packageJson.config) || _.isUndefined(packageJson.config.snapshot_name)) { + throw new Error(`Did not find 'snapshot_name' key in package.json config`); + } + + providerConfigs = { shouldUseInProcessGanache: true, ganacheDatabasePath: packageJson.config.snapshot_name }; + provider = web3Factory.getRpcProvider(providerConfigs); + txDefaults = { + from: devConstants.TESTRPC_FIRST_ADDRESS, + }; + await runMigrationsAsync(provider, txDefaults); + process.exit(0); +})().catch(err => { + logUtils.log(err); + process.exit(1); +}); diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index c684c4970..99d1719f1 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -141,6 +141,14 @@ export async function runMigrationsAsync(provider: Provider, txDefaults: Partial zrxAssetData, ); + // DutchAuction + const dutchAuction = await wrappers.DutchAuctionContract.deployFrom0xArtifactAsync( + artifacts.DutchAuction, + provider, + txDefaults, + exchange.address, + ); + // Fund the Forwarder with ZRX const zrxDecimals = await zrxToken.decimals.callAsync(); const zrxForwarderAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5000), zrxDecimals); @@ -157,6 +165,7 @@ export async function runMigrationsAsync(provider: Provider, txDefaults: Partial assetProxyOwner: assetProxyOwner.address, forwarder: forwarder.address, orderValidator: orderValidator.address, + dutchAuction: dutchAuction.address, }; } diff --git a/packages/monorepo-scripts/CHANGELOG.json b/packages/monorepo-scripts/CHANGELOG.json index 428168437..cebe7ec72 100644 --- a/packages/monorepo-scripts/CHANGELOG.json +++ b/packages/monorepo-scripts/CHANGELOG.json @@ -17,6 +17,10 @@ { "note": "Fix a bug when hardcoded CHANGELOG paths cause fetching release notes to fail", "pr": 1311 + }, + { + "note": "Added DutchAuctionWrapper to the CLASSES_WITH_HIDDEN_CONSTRUCTORS array", + "pr": 1465 } ] }, diff --git a/packages/monorepo-scripts/package.json b/packages/monorepo-scripts/package.json index a64f101fe..3286b8412 100644 --- a/packages/monorepo-scripts/package.json +++ b/packages/monorepo-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@0x/monorepo-scripts", - "version": "1.0.15", + "version": "1.0.16", "engines": { "node": ">=6.12" }, diff --git a/packages/monorepo-scripts/src/doc_gen_configs.ts b/packages/monorepo-scripts/src/doc_gen_configs.ts index 7a14f8664..7a4e6bb2c 100644 --- a/packages/monorepo-scripts/src/doc_gen_configs.ts +++ b/packages/monorepo-scripts/src/doc_gen_configs.ts @@ -9,6 +9,7 @@ export const docGenConfigs: DocGenConfigs = { Array: 'https://developer.mozilla.org/pt-PT/docs/Web/JavaScript/Reference/Global_Objects/Array', BigNumber: 'http://mikemcl.github.io/bignumber.js', Error: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error', + ErrorConstructor: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error', Buffer: 'https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v9/index.d.ts#L262', 'solc.StandardContractOutput': 'https://solidity.readthedocs.io/en/v0.4.24/using-the-compiler.html#output-description', @@ -37,6 +38,7 @@ export const docGenConfigs: DocGenConfigs = { // and getting confused. Any class name in this list will not have it's constructor rendered in our docs. CLASSES_WITH_HIDDEN_CONSTRUCTORS: [ 'AssetBuyer', + 'DutchAuctionWrapper', 'ERC20ProxyWrapper', 'ERC20TokenWrapper', 'ERC721ProxyWrapper', diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 11889b92e..d3f15f092 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,5 +1,16 @@ [ { + "version": "3.1.0", + "changes": [ + { + "note": + "Use new ABI encoder, add encoding/decoding logic for MultiAsset assetData, and add information to return values in orderStateUtils", + "pr": 1363 + } + ], + "timestamp": 1547040760 + }, + { "version": "3.0.7", "changes": [ { diff --git a/packages/order-utils/CHANGELOG.md b/packages/order-utils/CHANGELOG.md index bc8b48767..eb36616d1 100644 --- a/packages/order-utils/CHANGELOG.md +++ b/packages/order-utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.1.0 - _January 9, 2019_ + + * Use new ABI encoder, add encoding/decoding logic for MultiAsset assetData, and add information to return values in orderStateUtils (#1363) + ## v3.0.7 - _December 13, 2018_ * Dependencies updated diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 400c9b66f..e59ea664f 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/order-utils", - "version": "3.0.7", + "version": "3.1.0", "engines": { "node": ">=6.12" }, @@ -35,7 +35,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md", "devDependencies": { - "@0x/dev-utils": "^1.0.21", + "@0x/dev-utils": "^1.0.22", "@0x/tslint-config": "^2.0.0", "@types/bn.js": "^4.11.0", "@types/lodash": "4.14.104", @@ -53,15 +53,15 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^2.0.2", - "@0x/assert": "^1.0.20", - "@0x/base-contract": "^3.0.10", - "@0x/contract-artifacts": "^1.1.2", - "@0x/json-schemas": "^2.1.4", - "@0x/types": "^1.4.1", + "@0x/abi-gen-wrappers": "^2.1.0", + "@0x/assert": "^1.0.21", + "@0x/base-contract": "^3.0.11", + "@0x/contract-artifacts": "^1.2.0", + "@0x/json-schemas": "^2.1.5", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@types/node": "*", "bn.js": "^4.11.8", "ethereum-types": "^1.1.4", diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 9bbef3a23..f314891e2 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -1,10 +1,19 @@ -import { AssetData, AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import ethAbi = require('ethereumjs-abi'); -import ethUtil = require('ethereumjs-util'); +import { + AssetProxyId, + ERC20AssetData, + ERC721AssetData, + MultiAssetData, + MultiAssetDataWithRecursiveDecoding, + SingleAssetData, +} from '@0x/types'; +import { AbiEncoder, BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; import { constants } from './constants'; +const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: true }; +const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; + export const assetDataUtils = { /** * Encodes an ERC20 token address into a hex encoded assetData string, usable in the makerAssetData or @@ -13,7 +22,10 @@ export const assetDataUtils = { * @return The hex encoded assetData string */ encodeERC20AssetData(tokenAddress: string): string { - return ethUtil.bufferToHex(ethAbi.simpleEncode('ERC20Token(address)', tokenAddress)); + const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI); + const args = [tokenAddress]; + const assetData = abiEncoder.encode(args, encodingRules); + return assetData; }, /** * Decodes an ERC20 assetData hex string into it's corresponding ERC20 tokenAddress & assetProxyId @@ -21,26 +33,14 @@ export const assetDataUtils = { * @return An object containing the decoded tokenAddress & assetProxyId */ decodeERC20AssetData(assetData: string): ERC20AssetData { - const data = ethUtil.toBuffer(assetData); - if (data.byteLength < constants.ERC20_ASSET_DATA_BYTE_LENGTH) { - throw new Error( - `Could not decode ERC20 Proxy Data. Expected length of encoded data to be at least ${ - constants.ERC20_ASSET_DATA_BYTE_LENGTH - }. Got ${data.byteLength}`, - ); - } - const assetProxyId = ethUtil.bufferToHex(data.slice(0, constants.SELECTOR_LENGTH)); - if (assetProxyId !== AssetProxyId.ERC20) { - throw new Error( - `Could not decode ERC20 Proxy Data. Expected Asset Proxy Id to be ERC20 (${ - AssetProxyId.ERC20 - }), but got ${assetProxyId}`, - ); - } - const [tokenAddress] = ethAbi.rawDecode(['address'], data.slice(constants.SELECTOR_LENGTH)); + assetDataUtils.assertIsERC20AssetData(assetData); + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI); + const decodedAssetData = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, - tokenAddress: ethUtil.addHexPrefix(tokenAddress), + // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion + tokenAddress: (decodedAssetData as any).tokenContract, }; }, /** @@ -51,14 +51,10 @@ export const assetDataUtils = { * @return The hex encoded assetData string */ encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string { - // TODO: Pass `tokendId` as a BigNumber. - return ethUtil.bufferToHex( - ethAbi.simpleEncode( - 'ERC721Token(address,uint256)', - tokenAddress, - `0x${tokenId.toString(constants.BASE_16)}`, - ), - ); + const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI); + const args = [tokenAddress, tokenId]; + const assetData = abiEncoder.encode(args, encodingRules); + return assetData; }, /** * Decodes an ERC721 assetData hex string into it's corresponding ERC721 tokenAddress, tokenId & assetProxyId @@ -66,27 +62,99 @@ export const assetDataUtils = { * @return An object containing the decoded tokenAddress, tokenId & assetProxyId */ decodeERC721AssetData(assetData: string): ERC721AssetData { - const data = ethUtil.toBuffer(assetData); - if (data.byteLength < constants.ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH) { + assetDataUtils.assertIsERC721AssetData(assetData); + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI); + const decodedAssetData = abiEncoder.decode(assetData, decodingRules); + return { + assetProxyId, + // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion + tokenAddress: (decodedAssetData as any).tokenContract, + tokenId: (decodedAssetData as any).tokenId, + }; + }, + /** + * Encodes assetData for multiple AssetProxies into a single hex encoded assetData string, usable in the makerAssetData or + * takerAssetData fields in a 0x order. + * @param amounts Amounts of each asset that correspond to a single unit within an order. + * @param nestedAssetData assetData strings that correspond to a valid assetProxyId. + * @return The hex encoded assetData string + */ + encodeMultiAssetData(amounts: BigNumber[], nestedAssetData: string[]): string { + if (amounts.length !== nestedAssetData.length) { throw new Error( - `Could not decode ERC721 Asset Data. Expected length of encoded data to be at least ${ - constants.ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - }. Got ${data.byteLength}`, + `Invalid MultiAsset arguments. Expected length of 'amounts' (${ + amounts.length + }) to equal length of 'nestedAssetData' (${nestedAssetData.length})`, ); } - const assetProxyId = ethUtil.bufferToHex(data.slice(0, constants.SELECTOR_LENGTH)); - if (assetProxyId !== AssetProxyId.ERC721) { + _.forEach(nestedAssetData, assetDataElement => assetDataUtils.validateAssetDataOrThrow(assetDataElement)); + const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI); + const args = [amounts, nestedAssetData]; + const assetData = abiEncoder.encode(args, encodingRules); + return assetData; + }, + /** + * Decodes a MultiAsset assetData hex string into it's corresponding amounts and nestedAssetData + * @param assetData Hex encoded assetData string to decode + * @return An object containing the decoded amounts and nestedAssetData + */ + decodeMultiAssetData(assetData: string): MultiAssetData { + assetDataUtils.assertIsMultiAssetData(assetData); + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI); + const decodedAssetData = abiEncoder.decode(assetData, decodingRules); + // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion + const amounts = (decodedAssetData as any).amounts; + const nestedAssetData = (decodedAssetData as any).nestedAssetData; + if (amounts.length !== nestedAssetData.length) { throw new Error( - `Could not decode ERC721 Asset Data. Expected Asset Proxy Id to be ERC721 (${ - AssetProxyId.ERC721 - }), but got ${assetProxyId}`, + `Invalid MultiAsset assetData. Expected length of 'amounts' (${ + amounts.length + }) to equal length of 'nestedAssetData' (${nestedAssetData.length})`, ); } - const [tokenAddress, tokenId] = ethAbi.rawDecode(['address', 'uint256'], data.slice(constants.SELECTOR_LENGTH)); return { assetProxyId, - tokenAddress: ethUtil.addHexPrefix(tokenAddress), - tokenId: new BigNumber(tokenId.toString()), + amounts, + nestedAssetData, + }; + }, + /** + * Decodes a MultiAsset assetData hex string into it's corresponding amounts and decoded nestedAssetData elements (all nested elements are flattened) + * @param assetData Hex encoded assetData string to decode + * @return An object containing the decoded amounts and nestedAssetData + */ + decodeMultiAssetDataRecursively(assetData: string): MultiAssetDataWithRecursiveDecoding { + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + const amounts: any[] = []; + const decodedNestedAssetData = _.map( + decodedAssetData.nestedAssetData as string[], + (nestedAssetDataElement, index) => { + const decodedNestedAssetDataElement = assetDataUtils.decodeAssetDataOrThrow(nestedAssetDataElement); + if (decodedNestedAssetDataElement.assetProxyId === AssetProxyId.MultiAsset) { + const recursivelyDecodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively( + nestedAssetDataElement, + ); + amounts.push( + _.map(recursivelyDecodedAssetData.amounts, amountElement => + amountElement.times(decodedAssetData.amounts[index]), + ), + ); + return recursivelyDecodedAssetData.nestedAssetData; + } else { + amounts.push(decodedAssetData.amounts[index]); + return decodedNestedAssetDataElement as SingleAssetData; + } + }, + ); + const flattenedAmounts = _.flattenDeep(amounts); + const flattenedDecodedNestedAssetData = _.flattenDeep(decodedNestedAssetData); + return { + assetProxyId: decodedAssetData.assetProxyId, + amounts: flattenedAmounts, + // tslint:disable-next-line:no-unnecessary-type-assertion + nestedAssetData: flattenedDecodedNestedAssetData as SingleAssetData[], }; }, /** @@ -95,24 +163,133 @@ export const assetDataUtils = { * @return The assetProxyId */ decodeAssetProxyId(assetData: string): AssetProxyId { - const encodedAssetData = ethUtil.toBuffer(assetData); - if (encodedAssetData.byteLength < constants.SELECTOR_LENGTH) { + if (assetData.length < constants.SELECTOR_CHAR_LENGTH_WITH_PREFIX) { throw new Error( - `Could not decode assetData. Expected length of encoded data to be at least 4. Got ${ - encodedAssetData.byteLength + `Could not decode assetData. Expected length of encoded data to be at least 10. Got ${ + assetData.length }`, ); } - const encodedAssetProxyId = encodedAssetData.slice(0, constants.SELECTOR_LENGTH); - const assetProxyId = decodeAssetProxyId(encodedAssetProxyId); + const assetProxyId = assetData.slice(0, constants.SELECTOR_CHAR_LENGTH_WITH_PREFIX); + if ( + assetProxyId !== AssetProxyId.ERC20 && + assetProxyId !== AssetProxyId.ERC721 && + assetProxyId !== AssetProxyId.MultiAsset + ) { + throw new Error(`Invalid assetProxyId: ${assetProxyId}`); + } return assetProxyId; }, /** + * Checks if the decoded asset data is valid ERC20 data + * @param decodedAssetData The decoded asset data to check + */ + isERC20AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC20AssetData { + return decodedAssetData.assetProxyId === AssetProxyId.ERC20; + }, + /** + * Checks if the decoded asset data is valid ERC721 data + * @param decodedAssetData The decoded asset data to check + */ + isERC721AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC721AssetData { + return decodedAssetData.assetProxyId === AssetProxyId.ERC721; + }, + /** + * Checks if the decoded asset data is valid MultiAsset data + * @param decodedAssetData The decoded asset data to check + */ + isMultiAssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is MultiAssetData { + return decodedAssetData.assetProxyId === AssetProxyId.MultiAsset; + }, + /** + * Throws if the length or assetProxyId are invalid for the ERC20Proxy. + * @param assetData Hex encoded assetData string + */ + assertIsERC20AssetData(assetData: string): void { + if (assetData.length < constants.ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { + throw new Error( + `Could not decode ERC20 Proxy Data. Expected length of encoded data to be at least ${ + constants.ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX + }. Got ${assetData.length}`, + ); + } + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + if (assetProxyId !== AssetProxyId.ERC20) { + throw new Error( + `Could not decode ERC20 assetData. Expected assetProxyId to be ERC20 (${ + AssetProxyId.ERC20 + }), but got ${assetProxyId}`, + ); + } + }, + /** + * Throws if the length or assetProxyId are invalid for the ERC721Proxy. + * @param assetData Hex encoded assetData string + */ + assertIsERC721AssetData(assetData: string): void { + if (assetData.length < constants.ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { + throw new Error( + `Could not decode ERC721 assetData. Expected length of encoded data to be at least ${ + constants.ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX + }. Got ${assetData.length}`, + ); + } + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + if (assetProxyId !== AssetProxyId.ERC721) { + throw new Error( + `Could not decode ERC721 assetData. Expected assetProxyId to be ERC721 (${ + AssetProxyId.ERC721 + }), but got ${assetProxyId}`, + ); + } + }, + /** + * Throws if the length or assetProxyId are invalid for the MultiAssetProxy. + * @param assetData Hex encoded assetData string + */ + assertIsMultiAssetData(assetData: string): void { + if (assetData.length < constants.MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { + throw new Error( + `Could not decode MultiAsset assetData. Expected length of encoded data to be at least ${ + constants.MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX + }. Got ${assetData.length}`, + ); + } + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + if (assetProxyId !== AssetProxyId.MultiAsset) { + throw new Error( + `Could not decode MultiAsset assetData. Expected assetProxyId to be MultiAsset (${ + AssetProxyId.MultiAsset + }), but got ${assetProxyId}`, + ); + } + }, + /** + * Throws if the length or assetProxyId are invalid for the corresponding AssetProxy. + * @param assetData Hex encoded assetData string + */ + validateAssetDataOrThrow(assetData: string): void { + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + assetDataUtils.assertIsERC20AssetData(assetData); + break; + case AssetProxyId.ERC721: + assetDataUtils.assertIsERC721AssetData(assetData); + break; + case AssetProxyId.MultiAsset: + assetDataUtils.assertIsMultiAssetData(assetData); + break; + default: + throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`); + } + }, + /** * Decode any assetData into it's corresponding assetData object * @param assetData Hex encoded assetData string to decode * @return Either a ERC20 or ERC721 assetData object */ - decodeAssetDataOrThrow(assetData: string): AssetData { + decodeAssetDataOrThrow(assetData: string): SingleAssetData | MultiAssetData { const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); switch (assetProxyId) { case AssetProxyId.ERC20: @@ -121,19 +298,11 @@ export const assetDataUtils = { case AssetProxyId.ERC721: const erc721AssetData = assetDataUtils.decodeERC721AssetData(assetData); return erc721AssetData; + case AssetProxyId.MultiAsset: + const multiAssetData = assetDataUtils.decodeMultiAssetData(assetData); + return multiAssetData; default: throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`); } }, }; - -function decodeAssetProxyId(encodedAssetProxyId: Buffer): AssetProxyId { - const hexString = ethUtil.bufferToHex(encodedAssetProxyId); - if (hexString === AssetProxyId.ERC20) { - return AssetProxyId.ERC20; - } - if (hexString === AssetProxyId.ERC721) { - return AssetProxyId.ERC721; - } - throw new Error(`Invalid ProxyId: ${hexString}`); -} diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts index 10029dcc3..a9a687719 100644 --- a/packages/order-utils/src/constants.ts +++ b/packages/order-utils/src/constants.ts @@ -1,16 +1,71 @@ import { BigNumber } from '@0x/utils'; +import { MethodAbi } from 'ethereum-types'; + +const ERC20_METHOD_ABI: MethodAbi = { + constant: false, + inputs: [ + { + name: 'tokenContract', + type: 'address', + }, + ], + name: 'ERC20Token', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +const ERC721_METHOD_ABI: MethodAbi = { + constant: false, + inputs: [ + { + name: 'tokenContract', + type: 'address', + }, + { + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'ERC721Token', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +const MULTI_ASSET_METHOD_ABI: MethodAbi = { + constant: false, + inputs: [ + { + name: 'amounts', + type: 'uint256[]', + }, + { + name: 'nestedAssetData', + type: 'bytes[]', + }, + ], + name: 'MultiAsset', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', NULL_BYTES: '0x', + NULL_ERC20_ASSET_DATA: '0xf47261b00000000000000000000000000000000000000000000000000000000000000000', // tslint:disable-next-line:custom-no-magic-numbers UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), TESTRPC_NETWORK_ID: 50, ADDRESS_LENGTH: 20, - ERC20_ASSET_DATA_BYTE_LENGTH: 36, - ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH: 53, - SELECTOR_LENGTH: 4, - BASE_16: 16, + ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 74, + ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 136, + MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 266, + SELECTOR_CHAR_LENGTH_WITH_PREFIX: 10, INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite ZERO_AMOUNT: new BigNumber(0), EIP712_DOMAIN_NAME: '0x Protocol', @@ -48,4 +103,7 @@ export const constants = { { name: 'data', type: 'bytes' }, ], }, + ERC20_METHOD_ABI, + ERC721_METHOD_ABI, + MULTI_ASSET_METHOD_ABI, }; diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index 7a38b35df..06621fd9e 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -1,7 +1,8 @@ -import { ExchangeContractErrs } from '@0x/types'; +import { AssetProxyId, ExchangeContractErrs } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store'; +import { assetDataUtils } from './asset_data_utils'; import { constants } from './constants'; import { TradeSide, TransferType } from './types'; @@ -74,24 +75,51 @@ export class ExchangeTransferSimulator { tradeSide: TradeSide, transferType: TransferType, ): Promise<void> { - // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/ - // allowances for the taker. We do however, want to increase the balance of the maker since the maker - // might be relying on those funds to fill subsequent orders or pay the order's fees. - if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) { - await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); - return; + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/ + // allowances for the taker. We do however, want to increase the balance of the maker since the maker + // might be relying on those funds to fill subsequent orders or pay the order's fees. + if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) { + await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); + return; + } + const balance = await this._store.getBalanceAsync(assetData, from); + const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, from); + if (proxyAllowance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError( + FailureReason.ProxyAllowance, + tradeSide, + transferType, + ); + } + if (balance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); + } + await this._decreaseProxyAllowanceAsync(assetData, from, amountInBaseUnits); + await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits); + await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); + break; + case AssetProxyId.MultiAsset: + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) { + const amountsElement = decodedAssetData.amounts[index]; + const totalAmount = amountInBaseUnits.times(amountsElement); + await this.transferFromAsync( + nestedAssetDataElement, + from, + to, + totalAmount, + tradeSide, + transferType, + ); + } + break; + default: + break; } - const balance = await this._store.getBalanceAsync(assetData, from); - const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, from); - if (proxyAllowance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); - } - if (balance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); - } - await this._decreaseProxyAllowanceAsync(assetData, from, amountInBaseUnits); - await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits); - await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); } private async _decreaseProxyAllowanceAsync( assetData: string, diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index e70d43efb..2150a02e4 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -34,11 +34,14 @@ export { OrderRelevantState, OrderState, ECSignature, - AssetData, + SingleAssetData, ERC20AssetData, ERC721AssetData, + MultiAssetData, + MultiAssetDataWithRecursiveDecoding, AssetProxyId, SignatureType, + ObjectMap, OrderStateValid, OrderStateInvalid, ExchangeContractErrs, diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index fe0d6c773..389419587 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -1,5 +1,6 @@ import { ExchangeContractErrs, + ObjectMap, OrderRelevantState, OrderState, OrderStateInvalid, @@ -7,9 +8,11 @@ import { SignedOrder, } from '@0x/types'; import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; +import { assetDataUtils } from './asset_data_utils'; import { orderHashUtils } from './order_hash'; import { OrderValidationUtils } from './order_validation_utils'; import { RemainingFillableCalculator } from './remaining_fillable_calculator'; @@ -18,7 +21,9 @@ import { utils } from './utils'; interface SidedOrderRelevantState { isMakerSide: boolean; traderBalance: BigNumber; + traderIndividualBalances: ObjectMap<BigNumber>; traderProxyAllowance: BigNumber; + traderIndividualProxyAllowances: ObjectMap<BigNumber>; traderFeeBalance: BigNumber; traderFeeProxyAllowance: BigNumber; filledTakerAssetAmount: BigNumber; @@ -121,7 +126,9 @@ export class OrderStateUtils { const sidedOrderRelevantState = { isMakerSide: true, traderBalance: orderRelevantState.makerBalance, + traderIndividualBalances: orderRelevantState.makerIndividualBalances, traderProxyAllowance: orderRelevantState.makerProxyAllowance, + traderIndividualProxyAllowances: orderRelevantState.makerIndividualProxyAllowances, traderFeeBalance: orderRelevantState.makerFeeBalance, traderFeeProxyAllowance: orderRelevantState.makerFeeProxyAllowance, filledTakerAssetAmount: orderRelevantState.filledTakerAssetAmount, @@ -165,7 +172,9 @@ export class OrderStateUtils { const orderRelevantState = { makerBalance: sidedOrderRelevantState.traderBalance, + makerIndividualBalances: sidedOrderRelevantState.traderIndividualBalances, makerProxyAllowance: sidedOrderRelevantState.traderProxyAllowance, + makerIndividualProxyAllowances: sidedOrderRelevantState.traderIndividualProxyAllowances, makerFeeBalance: sidedOrderRelevantState.traderFeeBalance, makerFeeProxyAllowance: sidedOrderRelevantState.traderFeeProxyAllowance, filledTakerAssetAmount: sidedOrderRelevantState.filledTakerAssetAmount, @@ -236,10 +245,12 @@ export class OrderStateUtils { const isAssetZRX = assetData === zrxAssetData; const traderBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress); + const traderIndividualBalances = await this._getAssetBalancesAsync(assetData, traderAddress); const traderProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( assetData, traderAddress, ); + const traderIndividualProxyAllowances = await this._getAssetProxyAllowancesAsync(assetData, traderAddress); const traderFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( zrxAssetData, traderAddress, @@ -278,7 +289,9 @@ export class OrderStateUtils { const sidedOrderRelevantState = { isMakerSide, traderBalance, + traderIndividualBalances, traderProxyAllowance, + traderIndividualProxyAllowances, traderFeeBalance, traderFeeProxyAllowance, filledTakerAssetAmount, @@ -287,4 +300,47 @@ export class OrderStateUtils { }; return sidedOrderRelevantState; } + private async _getAssetBalancesAsync( + assetData: string, + traderAddress: string, + initialBalances: ObjectMap<BigNumber> = {}, + ): Promise<ObjectMap<BigNumber>> { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + let balances: ObjectMap<BigNumber> = { ...initialBalances }; + if (assetDataUtils.isERC20AssetData(decodedAssetData) || assetDataUtils.isERC721AssetData(decodedAssetData)) { + const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress); + const tokenAddress = decodedAssetData.tokenAddress; + balances[tokenAddress] = _.isUndefined(initialBalances[tokenAddress]) + ? balance + : balances[tokenAddress].add(balance); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + for (const assetDataElement of decodedAssetData.nestedAssetData) { + balances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, balances); + } + } + return balances; + } + private async _getAssetProxyAllowancesAsync( + assetData: string, + traderAddress: string, + initialAllowances: ObjectMap<BigNumber> = {}, + ): Promise<ObjectMap<BigNumber>> { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + let allowances: ObjectMap<BigNumber> = { ...initialAllowances }; + if (assetDataUtils.isERC20AssetData(decodedAssetData) || assetDataUtils.isERC721AssetData(decodedAssetData)) { + const allowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( + assetData, + traderAddress, + ); + const tokenAddress = decodedAssetData.tokenAddress; + allowances[tokenAddress] = _.isUndefined(initialAllowances[tokenAddress]) + ? allowance + : allowances[tokenAddress].add(allowance); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + for (const assetDataElement of decodedAssetData.nestedAssetData) { + allowances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, allowances); + } + } + return allowances; + } } diff --git a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts index f42a76d0c..ae3e36238 100644 --- a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts +++ b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts @@ -119,7 +119,7 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx public deleteAllERC721ProxyAllowance(tokenAddress: string, userAddress: string): void { for (const assetData in this._proxyAllowance) { if (this._proxyAllowance.hasOwnProperty(assetData)) { - const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + const decodedAssetData = assetDataUtils.decodeERC721AssetData(assetData); if ( decodedAssetData.assetProxyId === AssetProxyId.ERC721 && decodedAssetData.tokenAddress === tokenAddress && diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index f175b7a38..c498c5a00 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -1,6 +1,6 @@ import * as chai from 'chai'; -import { ERC20AssetData, ERC721AssetData } from '@0x/types'; +import { AssetProxyId, ERC721AssetData } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { assetDataUtils } from '../src/asset_data_utils'; @@ -10,41 +10,101 @@ import { chaiSetup } from './utils/chai_setup'; chaiSetup.configure(); const expect = chai.expect; -const KNOWN_ENCODINGS = [ - { - address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', - assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48', - }, - { - address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', - tokenId: new BigNumber(1), - assetData: - '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001', - }, -]; - -const ERC20_ASSET_PROXY_ID = '0xf47261b0'; -const ERC721_ASSET_PROXY_ID = '0x02571792'; +const KNOWN_ERC20_ENCODING = { + address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48', +}; +const KNOWN_ERC721_ENCODING = { + address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + tokenId: new BigNumber(1), + assetData: + '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001', +}; +const KNOWN_MULTI_ASSET_ENCODING = { + amounts: [new BigNumber(1), new BigNumber(1)], + nestedAssetData: [KNOWN_ERC20_ENCODING.assetData, KNOWN_ERC721_ENCODING.assetData], + assetData: + '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000', +}; describe('assetDataUtils', () => { it('should encode ERC20', () => { - const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ENCODINGS[0].address); - expect(assetData).to.equal(KNOWN_ENCODINGS[0].assetData); + const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ERC20_ENCODING.address); + expect(assetData).to.equal(KNOWN_ERC20_ENCODING.assetData); }); it('should decode ERC20', () => { - const assetData: ERC20AssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ENCODINGS[0].assetData); - expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[0].address); - expect(assetData.assetProxyId).to.equal(ERC20_ASSET_PROXY_ID); + const decodedAssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ERC20_ENCODING.assetData); + expect(decodedAssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC20); }); it('should encode ERC721', () => { - const assetData = assetDataUtils.encodeERC721AssetData(KNOWN_ENCODINGS[1].address, KNOWN_ENCODINGS[1] - .tokenId as BigNumber); - expect(assetData).to.equal(KNOWN_ENCODINGS[1].assetData); + const assetData = assetDataUtils.encodeERC721AssetData( + KNOWN_ERC721_ENCODING.address, + KNOWN_ERC721_ENCODING.tokenId, + ); + expect(assetData).to.equal(KNOWN_ERC721_ENCODING.assetData); }); it('should decode ERC721', () => { - const assetData: ERC721AssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ENCODINGS[1].assetData); - expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[1].address); - expect(assetData.assetProxyId).to.equal(ERC721_ASSET_PROXY_ID); - expect(assetData.tokenId).to.be.bignumber.equal(KNOWN_ENCODINGS[1].tokenId); + const decodedAssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ERC721_ENCODING.assetData); + expect(decodedAssetData.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedAssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + }); + it('should encode ERC20 and ERC721 multiAssetData', () => { + const assetData = assetDataUtils.encodeMultiAssetData( + KNOWN_MULTI_ASSET_ENCODING.amounts, + KNOWN_MULTI_ASSET_ENCODING.nestedAssetData, + ); + expect(assetData).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData); + }); + it('should decode ERC20 and ERC721 multiAssetData', () => { + const decodedAssetData = assetDataUtils.decodeMultiAssetData(KNOWN_MULTI_ASSET_ENCODING.assetData); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); + expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); + expect(decodedAssetData.nestedAssetData).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.nestedAssetData); + }); + it('should recursively decode ERC20 and ERC721 multiAssetData', () => { + const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(KNOWN_MULTI_ASSET_ENCODING.assetData); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); + expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); + const decodedErc20AssetData = decodedAssetData.nestedAssetData[0]; + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData = decodedAssetData.nestedAssetData[1] as ERC721AssetData; + expect(decodedErc20AssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20); + expect(decodedErc721AssetData.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedErc721AssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + }); + it('should recursively decode nested assetData within multiAssetData', () => { + const amounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2)]; + const nestedAssetData = [ + KNOWN_ERC20_ENCODING.assetData, + KNOWN_ERC721_ENCODING.assetData, + KNOWN_MULTI_ASSET_ENCODING.assetData, + ]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(assetData); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); + const expectedAmounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2), new BigNumber(2)]; + expect(decodedAssetData.amounts).to.deep.equal(expectedAmounts); + const expectedLength = 4; + expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedLength); + const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[0]; + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData1 = decodedAssetData.nestedAssetData[1] as ERC721AssetData; + const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[2]; + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData2 = decodedAssetData.nestedAssetData[3] as ERC721AssetData; + expect(decodedErc20AssetData1.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20); + expect(decodedErc721AssetData1.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData1.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedErc721AssetData1.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + expect(decodedErc20AssetData2.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData2.assetProxyId).to.equal(AssetProxyId.ERC20); + expect(decodedErc721AssetData2.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData2.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedErc721AssetData2.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); }); }); diff --git a/packages/order-utils/test/utils/test_order_factory.ts b/packages/order-utils/test/utils/test_order_factory.ts index 145332674..4efe0b38e 100644 --- a/packages/order-utils/test/utils/test_order_factory.ts +++ b/packages/order-utils/test/utils/test_order_factory.ts @@ -7,9 +7,9 @@ import { orderFactory } from '../../src/order_factory'; const BASE_TEST_ORDER: Order = orderFactory.createOrder( constants.NULL_ADDRESS, constants.ZERO_AMOUNT, - constants.NULL_ADDRESS, + constants.NULL_ERC20_ASSET_DATA, constants.ZERO_AMOUNT, - constants.NULL_ADDRESS, + constants.NULL_ERC20_ASSET_DATA, constants.NULL_ADDRESS, ); const BASE_TEST_SIGNED_ORDER: SignedOrder = { diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index 304dc45fd..afd523f88 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "2.4.0", + "changes": [ + { + "note": "Add support for `MultiAssetProxy`", + "pr": 1363 + } + ], + "timestamp": 1547040760 + }, + { "version": "2.3.0", "changes": [ { diff --git a/packages/order-watcher/CHANGELOG.md b/packages/order-watcher/CHANGELOG.md index 4e49b4637..600b9fa6f 100644 --- a/packages/order-watcher/CHANGELOG.md +++ b/packages/order-watcher/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.4.0 - _January 9, 2019_ + + * Add support for `MultiAssetProxy` (#1363) + +## v2.3.0 - _Invalid date_ + + * Added a WebSocket interface to OrderWatcher so that it can be used by a client written in any language (#1427) + ## v2.2.8 - _December 13, 2018_ * Dependencies updated diff --git a/packages/order-watcher/Dockerfile b/packages/order-watcher/Dockerfile new file mode 100644 index 000000000..3ffa1b72f --- /dev/null +++ b/packages/order-watcher/Dockerfile @@ -0,0 +1,13 @@ +FROM node + +WORKDIR /order-watcher + +COPY package.json . +RUN npm i +RUN npm install forever -g + +COPY . . + +EXPOSE 8080 + +CMD ["forever", "./lib/src/server.js"] diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 16a46294e..5919759dd 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@0x/order-watcher", - "version": "2.2.8", + "version": "2.4.0", "description": "An order watcher daemon that watches for order validity", "keywords": [ "0x", @@ -33,8 +33,9 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.21", - "@0x/migrations": "^2.2.2", + "@0x/dev-utils": "^1.0.22", + "@0x/migrations": "^2.3.0", + "@0x/subproviders": "^2.1.9", "@0x/tslint-config": "^2.0.0", "@types/bintrees": "^1.0.2", "@types/lodash": "4.14.104", @@ -57,19 +58,19 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^2.0.2", - "@0x/assert": "^1.0.20", - "@0x/base-contract": "^3.0.10", - "@0x/contract-addresses": "^2.0.0", - "@0x/contract-artifacts": "^1.1.2", - "@0x/contract-wrappers": "^4.1.3", - "@0x/fill-scenarios": "^1.0.16", - "@0x/json-schemas": "^2.1.4", - "@0x/order-utils": "^3.0.7", - "@0x/types": "^1.4.1", + "@0x/abi-gen-wrappers": "^2.1.0", + "@0x/assert": "^1.0.21", + "@0x/base-contract": "^3.0.11", + "@0x/contract-addresses": "^2.1.0", + "@0x/contract-artifacts": "^1.2.0", + "@0x/contract-wrappers": "^4.2.0", + "@0x/fill-scenarios": "^1.1.0", + "@0x/json-schemas": "^2.1.5", + "@0x/order-utils": "^3.1.0", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "bintrees": "^1.0.2", "ethereum-types": "^1.1.4", "ethereumjs-blockstream": "6.0.0", diff --git a/packages/order-watcher/src/index.ts b/packages/order-watcher/src/index.ts index e275a0c6a..1f4e5eff1 100644 --- a/packages/order-watcher/src/index.ts +++ b/packages/order-watcher/src/index.ts @@ -7,6 +7,7 @@ export { OrderStateInvalid, OrderState, ExchangeContractErrs, + ObjectMap, OrderRelevantState, Stats, } from '@0x/types'; diff --git a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts index a956a94db..d1085014c 100644 --- a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts +++ b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts @@ -1,6 +1,5 @@ -// tslint:disable:no-unnecessary-type-assertion import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC20AssetData, ERC721AssetData, SignedOrder } from '@0x/types'; +import { AssetProxyId, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; @@ -62,35 +61,18 @@ export class DependentOrderHashesTracker { return dependentOrderHashes; } public addToDependentOrderHashes(signedOrder: SignedOrder): void { - const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); - if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) { - this._addToERC20DependentOrderHashes(signedOrder, (decodedMakerAssetData as ERC20AssetData).tokenAddress); - } else { - this._addToERC721DependentOrderHashes( - signedOrder, - (decodedMakerAssetData as ERC721AssetData).tokenAddress, - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } + this._addAssetDataToDependentOrderHashes(signedOrder, signedOrder.makerAssetData); this._addToERC20DependentOrderHashes(signedOrder, this._zrxTokenAddress); this._addToMakerDependentOrderHashes(signedOrder); } public removeFromDependentOrderHashes(signedOrder: SignedOrder): void { - const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); - if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) { - this._removeFromERC20DependentOrderhashes( - signedOrder, - (decodedMakerAssetData as ERC20AssetData).tokenAddress, - ); - } else { - this._removeFromERC721DependentOrderhashes( - signedOrder, - (decodedMakerAssetData as ERC721AssetData).tokenAddress, - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } + this._removeAssetDataFromDependentOrderHashes(signedOrder, signedOrder.makerAssetData); // If makerToken === ZRX then we already removed it and we don't need to remove it again. - if ((decodedMakerAssetData as ERC20AssetData).tokenAddress !== this._zrxTokenAddress) { + const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); + if ( + assetDataUtils.isERC20AssetData(decodedMakerAssetData) && + decodedMakerAssetData.tokenAddress !== this._zrxTokenAddress + ) { this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress); } this._removeFromMakerDependentOrderhashes(signedOrder); @@ -167,6 +149,18 @@ export class DependentOrderHashesTracker { tokenId.toString() ].add(orderHash); } + private _addAssetDataToDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + this._addToERC20DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + this._addToERC721DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress, decodedAssetData.tokenId); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._addAssetDataToDependentOrderHashes(signedOrder, nestedAssetDataElement), + ); + } + } private _addToMakerDependentOrderHashes(signedOrder: SignedOrder): void { const orderHash = orderHashUtils.getOrderHashHex(signedOrder); if (_.isUndefined(this._orderHashesByMakerAddress[signedOrder.makerAddress])) { @@ -230,4 +224,20 @@ export class DependentOrderHashesTracker { delete this._orderHashesByMakerAddress[signedOrder.makerAddress]; } } + private _removeAssetDataFromDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + this._removeFromERC20DependentOrderhashes(signedOrder, decodedAssetData.tokenAddress); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + this._removeFromERC721DependentOrderhashes( + signedOrder, + decodedAssetData.tokenAddress, + decodedAssetData.tokenId, + ); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._removeAssetDataFromDependentOrderHashes(signedOrder, nestedAssetDataElement), + ); + } + } } diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts index 96c5ca7b4..a06fd0cfe 100644 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ b/packages/order-watcher/src/order_watcher/order_watcher.ts @@ -161,14 +161,7 @@ export class OrderWatcher { this._dependentOrderHashesTracker.addToDependentOrderHashes(signedOrder); const orderAssetDatas = [signedOrder.makerAssetData, signedOrder.takerAssetData]; - _.each(orderAssetDatas, assetData => { - const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) { - this._collisionResistantAbiDecoder.addERC20Token(decodedAssetData.tokenAddress); - } else if (decodedAssetData.assetProxyId === AssetProxyId.ERC721) { - this._collisionResistantAbiDecoder.addERC721Token(decodedAssetData.tokenAddress); - } - }); + _.each(orderAssetDatas, assetData => this._addAssetDataToAbiDecoder(assetData)); } /** * Removes an order from the orderWatcher @@ -236,31 +229,71 @@ export class OrderWatcher { await this._emitRevalidateOrdersAsync([orderHash]); } } + private _addAssetDataToAbiDecoder(assetData: string): void { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + this._collisionResistantAbiDecoder.addERC20Token(decodedAssetData.tokenAddress); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + this._collisionResistantAbiDecoder.addERC721Token(decodedAssetData.tokenAddress); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._addAssetDataToAbiDecoder(nestedAssetDataElement), + ); + } + } + private _deleteLazyStoreBalance(assetData: string, userAddress: string): void { + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + this._balanceAndProxyAllowanceLazyStore.deleteBalance(assetData, userAddress); + break; + case AssetProxyId.MultiAsset: + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._deleteLazyStoreBalance(nestedAssetDataElement, userAddress), + ); + break; + default: + break; + } + } + private _deleteLazyStoreProxyAllowance(assetData: string, userAddress: string): void { + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(assetData, userAddress); + break; + case AssetProxyId.MultiAsset: + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._deleteLazyStoreProxyAllowance(nestedAssetDataElement, userAddress), + ); + break; + default: + break; + } + } private _cleanupOrderRelatedState(orderHash: string): void { const signedOrder = this._orderByOrderHash[orderHash]; this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash); this._orderFilledCancelledLazyStore.deleteIsCancelled(orderHash); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerAssetData, signedOrder.makerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance( - signedOrder.makerAssetData, - signedOrder.makerAddress, - ); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerAssetData, signedOrder.takerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance( - signedOrder.takerAssetData, - signedOrder.takerAddress, - ); + this._deleteLazyStoreBalance(signedOrder.makerAssetData, signedOrder.makerAddress); + this._deleteLazyStoreProxyAllowance(signedOrder.makerAssetData, signedOrder.makerAddress); + this._deleteLazyStoreBalance(signedOrder.takerAssetData, signedOrder.takerAddress); + this._deleteLazyStoreProxyAllowance(signedOrder.takerAssetData, signedOrder.takerAddress); const zrxAssetData = this._orderFilledCancelledLazyStore.getZRXAssetData(); if (!signedOrder.makerFee.isZero()) { - this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.makerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.makerAddress); + this._deleteLazyStoreBalance(zrxAssetData, signedOrder.makerAddress); + this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.makerAddress); } if (!signedOrder.takerFee.isZero()) { - this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.takerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.takerAddress); + this._deleteLazyStoreBalance(zrxAssetData, signedOrder.takerAddress); + this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.takerAddress); } } private _onOrderExpired(orderHash: string): void { @@ -302,7 +335,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC20TokenApprovalEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner); + this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, @@ -315,7 +348,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC721TokenApprovalEventArgs; const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner); + this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, @@ -333,8 +366,8 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC20TokenTransferEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to); + this._deleteLazyStoreBalance(tokenAssetData, args._from); + this._deleteLazyStoreBalance(tokenAssetData, args._to); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._from, @@ -347,8 +380,8 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC721TokenTransferEventArgs; const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to); + this._deleteLazyStoreBalance(tokenAssetData, args._from); + this._deleteLazyStoreBalance(tokenAssetData, args._to); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._from, @@ -375,7 +408,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as WETH9DepositEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner); + this._deleteLazyStoreBalance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, @@ -388,7 +421,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as WETH9WithdrawalEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner); + this._deleteLazyStoreBalance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, diff --git a/packages/order-watcher/src/server.ts b/packages/order-watcher/src/server.ts new file mode 100644 index 000000000..1d31e87ab --- /dev/null +++ b/packages/order-watcher/src/server.ts @@ -0,0 +1,44 @@ +import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses'; +import { RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders'; +import * as _ from 'lodash'; + +import { OrderWatcherWebSocketServer } from './order_watcher/order_watcher_web_socket_server'; + +const GANACHE_NETWORK_ID = 50; +const DEFAULT_RPC_URL = 'http://localhost:8545'; + +const provider = new Web3ProviderEngine(); +const jsonRpcUrl = process.env.JSON_RPC_URL || DEFAULT_RPC_URL; +const rpcSubprovider = new RPCSubprovider(jsonRpcUrl); +provider.addProvider(rpcSubprovider); +provider.start(); + +const networkId = process.env.NETWORK_ID !== undefined ? _.parseInt(process.env.NETWORK_ID) : GANACHE_NETWORK_ID; + +const contractAddressesString = process.env.contractAddresses; +const contractAddressesIfExists = + contractAddressesString === undefined + ? getContractAddressesForNetworkOrThrow(networkId) + : JSON.parse(contractAddressesString); + +const orderWatcherConfig: any = { + isVerbose: process.env.IS_VERBOSE === 'true', +}; +const orderExpirationCheckingIntervalMs = process.env.ORDER_EXPIRATION_CHECKING_INTERVAL_MS; +if (orderExpirationCheckingIntervalMs !== undefined) { + orderWatcherConfig.orderExpirationCheckingIntervalMs = _.parseInt(orderExpirationCheckingIntervalMs); +} +const eventPollingIntervalMs = process.env.EVENT_POLLING_INTERVAL_MS; +if (eventPollingIntervalMs !== undefined) { + orderWatcherConfig.eventPollingIntervalMs = _.parseInt(eventPollingIntervalMs); +} +const expirationMarginMs = process.env.EXPIRATION_MARGIN_MS; +if (expirationMarginMs !== undefined) { + orderWatcherConfig.expirationMarginMs = _.parseInt(expirationMarginMs); +} +const cleanupJobIntervalMs = process.env.CLEANUP_JOB_INTERVAL_MS; +if (cleanupJobIntervalMs !== undefined) { + orderWatcherConfig.cleanupJobIntervalMs = _.parseInt(cleanupJobIntervalMs); +} +const wsServer = new OrderWatcherWebSocketServer(provider, networkId, contractAddressesIfExists, orderWatcherConfig); +wsServer.start(); diff --git a/packages/order-watcher/test/order_watcher_test.ts b/packages/order-watcher/test/order_watcher_test.ts index 271e5dec5..41dc884d5 100644 --- a/packages/order-watcher/test/order_watcher_test.ts +++ b/packages/order-watcher/test/order_watcher_test.ts @@ -675,5 +675,213 @@ describe('OrderWatcher', () => { })().catch(done); }); }); + describe('multiAsset', async () => { + const tokenId = new BigNumber(42); + const [makerErc721TokenAddress] = tokenUtils.getDummyERC721TokenAddresses(); + const makerErc721AssetData = assetDataUtils.encodeERC721AssetData(makerErc721TokenAddress, tokenId); + const fillableErc721Amount = new BigNumber(1); + const [makerErc20TokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + const makerErc20AssetData = assetDataUtils.encodeERC20AssetData(makerErc20TokenAddress); + const fillableErc20Amount = new BigNumber(2); + const multiAssetAmounts = [fillableErc721Amount, fillableErc20Amount]; + const nestedAssetData = [makerErc721AssetData, makerErc20AssetData]; + const makerMultiAssetData = assetDataUtils.encodeMultiAssetData(multiAssetAmounts, nestedAssetData); + it('should emit orderStateInvalid when maker allowance of ERC721 token set to 0 for watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); + }); + orderWatcher.subscribe(callback); + await contractWrappers.erc721Token.setApprovalAsync( + makerErc721TokenAddress, + constants.NULL_ADDRESS, + tokenId, + ); + })().catch(done); + }); + it('should emit orderStateInvalid when maker allowance for all of ERC721 token set to 0 for watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + await contractWrappers.erc721Token.setApprovalAsync( + makerErc721TokenAddress, + constants.NULL_ADDRESS, + tokenId, + ); + let isApproved = true; + await contractWrappers.erc721Token.setProxyApprovalForAllAsync( + makerErc721TokenAddress, + makerAddress, + isApproved, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); + }); + orderWatcher.subscribe(callback); + isApproved = false; + await contractWrappers.erc721Token.setProxyApprovalForAllAsync( + makerErc721TokenAddress, + makerAddress, + isApproved, + ); + })().catch(done); + }); + it('should emit orderStateInvalid when maker moves ERC721 backing watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); + }); + orderWatcher.subscribe(callback); + await contractWrappers.erc721Token.transferFromAsync( + makerErc721TokenAddress, + coinbase, + makerAddress, + tokenId, + ); + })().catch(done); + }); + it('should emit orderStateInvalid when maker allowance of ERC20 token set to 0 for watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); + }); + orderWatcher.subscribe(callback); + await contractWrappers.erc20Token.setProxyAllowanceAsync( + makerErc20TokenAddress, + makerAddress, + new BigNumber(0), + ); + })().catch(done); + }); + it('should not emit an orderState event when irrelevant ERC20 Transfer event received', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((_orderState: OrderState) => { + throw new Error('OrderState callback fired for irrelevant order'); + }); + orderWatcher.subscribe(callback); + const notTheMaker = userAddresses[0]; + const anyRecipient = takerAddress; + const transferAmount = new BigNumber(2); + await contractWrappers.erc20Token.transferAsync( + makerTokenAddress, + notTheMaker, + anyRecipient, + transferAmount, + ); + setTimeout(() => { + done(); + }, TIMEOUT_MS); + })().catch(done); + }); + it('should emit orderStateInvalid when makerAddress moves ERC20 balance backing watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); + }); + orderWatcher.subscribe(callback); + const anyRecipient = takerAddress; + const makerBalance = await contractWrappers.erc20Token.getBalanceAsync( + makerTokenAddress, + makerAddress, + ); + await contractWrappers.erc20Token.transferAsync( + makerTokenAddress, + makerAddress, + anyRecipient, + makerBalance, + ); + })().catch(done); + }); + // TODO(abandeali1): The following test will fail until the MAP has been deployed and activated. + it.skip('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); + }); + orderWatcher.subscribe(callback); + + await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress); + })().catch(done); + }); + }); }); }); // tslint:disable:max-file-line-count diff --git a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts b/packages/order-watcher/test/order_watcher_web_socket_server_test.ts index 578e0de61..36135f65c 100644 --- a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts +++ b/packages/order-watcher/test/order_watcher_web_socket_server_test.ts @@ -1,9 +1,10 @@ +import { ContractAddresses } from '@0x/contract-addresses'; import { ContractWrappers } from '@0x/contract-wrappers'; import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { FillScenarios } from '@0x/fill-scenarios'; import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { ExchangeContractErrs, OrderStateInvalid, OrderStateValid, SignedOrder } from '@0x/types'; +import { ExchangeContractErrs, OrderStateInvalid, SignedOrder } from '@0x/types'; import { BigNumber, logUtils } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as chai from 'chai'; @@ -26,7 +27,7 @@ interface WsMessage { data: string; } -describe.only('OrderWatcherWebSocketServer', async () => { +describe('OrderWatcherWebSocketServer', async () => { let contractWrappers: ContractWrappers; let wsServer: OrderWatcherWebSocketServer; let wsClient: WebSocket.w3cwebsocket; @@ -44,14 +45,16 @@ describe.only('OrderWatcherWebSocketServer', async () => { let orderHash: string; let addOrderPayload: AddOrderRequest; let removeOrderPayload: RemoveOrderRequest; + let networkId: number; + let contractAddresses: ContractAddresses; const decimals = constants.ZRX_DECIMALS; const fillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), decimals); before(async () => { // Set up constants - const contractAddresses = await migrateOnceAsync(); + contractAddresses = await migrateOnceAsync(); await blockchainLifecycle.startAsync(); - const networkId = constants.TESTRPC_NETWORK_ID; + networkId = constants.TESTRPC_NETWORK_ID; const config = { networkId, contractAddresses, @@ -93,17 +96,16 @@ describe.only('OrderWatcherWebSocketServer', async () => { method: OrderWatcherMethod.RemoveOrder, params: { orderHash }, }; - - // Prepare OrderWatcher WebSocket server - const orderWatcherConfig = { - isVerbose: true, - }; - wsServer = new OrderWatcherWebSocketServer(provider, networkId, contractAddresses, orderWatcherConfig); }); after(async () => { await blockchainLifecycle.revertAsync(); }); beforeEach(async () => { + // Prepare OrderWatcher WebSocket server + const orderWatcherConfig = { + isVerbose: true, + }; + wsServer = new OrderWatcherWebSocketServer(provider, networkId, contractAddresses, orderWatcherConfig); wsServer.start(); await blockchainLifecycle.startAsync(); wsClient = new WebSocket.w3cwebsocket('ws://127.0.0.1:8080/'); @@ -260,7 +262,9 @@ describe.only('OrderWatcherWebSocketServer', async () => { id: 1, jsonrpc: '2.0', method: 'ADD_ORDER', - signedOrder: nonZeroMakerFeeSignedOrder, + params: { + signedOrder: nonZeroMakerFeeSignedOrder, + }, }; // Set up a second client and have it add the order @@ -278,15 +282,15 @@ describe.only('OrderWatcherWebSocketServer', async () => { // Check that both clients receive the emitted event by awaiting the onMessageAsync promises let updateMsg = await clientOneOnMessagePromise; let updateData = JSON.parse(updateMsg.data); - let orderState = updateData.result as OrderStateValid; - expect(orderState.isValid).to.be.true(); - expect(orderState.orderRelevantState.makerFeeProxyAllowance).to.be.eq('0'); + let orderState = updateData.result as OrderStateInvalid; + expect(orderState.isValid).to.be.false(); + expect(orderState.error).to.be.eq('INSUFFICIENT_MAKER_FEE_ALLOWANCE'); updateMsg = await clientTwoOnMessagePromise; updateData = JSON.parse(updateMsg.data); - orderState = updateData.result as OrderStateValid; - expect(orderState.isValid).to.be.true(); - expect(orderState.orderRelevantState.makerFeeProxyAllowance).to.be.eq('0'); + orderState = updateData.result as OrderStateInvalid; + expect(orderState.isValid).to.be.false(); + expect(orderState.error).to.be.eq('INSUFFICIENT_MAKER_FEE_ALLOWANCE'); wsClientTwo.close(); logUtils.log(`${new Date()} [Client] Closed.`); diff --git a/packages/pipeline/migrations/1545440485644-CreateCopperTables.ts b/packages/pipeline/migrations/1545440485644-CreateCopperTables.ts new file mode 100644 index 000000000..64bf70af4 --- /dev/null +++ b/packages/pipeline/migrations/1545440485644-CreateCopperTables.ts @@ -0,0 +1,103 @@ +import { MigrationInterface, QueryRunner, Table } from 'typeorm'; + +const leads = new Table({ + name: 'raw.copper_leads', + columns: [ + { name: 'id', type: 'bigint', isPrimary: true }, + { name: 'name', type: 'varchar', isNullable: true }, + { name: 'first_name', type: 'varchar', isNullable: true }, + { name: 'last_name', type: 'varchar', isNullable: true }, + { name: 'middle_name', type: 'varchar', isNullable: true }, + { name: 'assignee_id', type: 'bigint', isNullable: true }, + { name: 'company_name', type: 'varchar', isNullable: true }, + { name: 'customer_source_id', type: 'bigint', isNullable: true }, + { name: 'monetary_value', type: 'integer', isNullable: true }, + { name: 'status', type: 'varchar' }, + { name: 'status_id', type: 'bigint' }, + { name: 'title', type: 'varchar', isNullable: true }, + { name: 'date_created', type: 'bigint' }, + { name: 'date_modified', type: 'bigint', isPrimary: true }, + ], +}); +const activities = new Table({ + name: 'raw.copper_activities', + columns: [ + { name: 'id', type: 'bigint', isPrimary: true }, + { name: 'parent_id', type: 'bigint' }, + { name: 'parent_type', type: 'varchar' }, + { name: 'type_id', type: 'bigint' }, + { name: 'type_category', type: 'varchar' }, + { name: 'type_name', type: 'varchar', isNullable: true }, + { name: 'user_id', type: 'bigint' }, + { name: 'old_value_id', type: 'bigint', isNullable: true }, + { name: 'old_value_name', type: 'varchar', isNullable: true }, + { name: 'new_value_id', type: 'bigint', isNullable: true }, + { name: 'new_value_name', type: 'varchar', isNullable: true }, + { name: 'date_created', type: 'bigint' }, + { name: 'date_modified', type: 'bigint', isPrimary: true }, + ], +}); + +const opportunities = new Table({ + name: 'raw.copper_opportunities', + columns: [ + { name: 'id', type: 'bigint', isPrimary: true }, + { name: 'name', type: 'varchar' }, + { name: 'assignee_id', isNullable: true, type: 'bigint' }, + { name: 'close_date', isNullable: true, type: 'varchar' }, + { name: 'company_id', isNullable: true, type: 'bigint' }, + { name: 'company_name', isNullable: true, type: 'varchar' }, + { name: 'customer_source_id', isNullable: true, type: 'bigint' }, + { name: 'loss_reason_id', isNullable: true, type: 'bigint' }, + { name: 'pipeline_id', type: 'bigint' }, + { name: 'pipeline_stage_id', type: 'bigint' }, + { name: 'primary_contact_id', isNullable: true, type: 'bigint' }, + { name: 'priority', isNullable: true, type: 'varchar' }, + { name: 'status', type: 'varchar' }, + { name: 'interaction_count', type: 'bigint' }, + { name: 'monetary_value', isNullable: true, type: 'integer' }, + { name: 'win_probability', isNullable: true, type: 'integer' }, + { name: 'date_created', type: 'bigint' }, + { name: 'date_modified', type: 'bigint', isPrimary: true }, + { name: 'custom_fields', type: 'jsonb' }, + ], +}); + +const activityTypes = new Table({ + name: 'raw.copper_activity_types', + columns: [ + { name: 'id', type: 'bigint', isPrimary: true }, + { name: 'category', type: 'varchar' }, + { name: 'name', type: 'varchar' }, + { name: 'is_disabled', type: 'boolean', isNullable: true }, + { name: 'count_as_interaction', type: 'boolean', isNullable: true }, + ], +}); + +const customFields = new Table({ + name: 'raw.copper_custom_fields', + columns: [ + { name: 'id', type: 'bigint', isPrimary: true }, + { name: 'name', type: 'varchar' }, + { name: 'data_type', type: 'varchar' }, + { name: 'field_type', type: 'varchar', isNullable: true }, + ], +}); + +export class CreateCopperTables1544055699284 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise<any> { + await queryRunner.createTable(leads); + await queryRunner.createTable(activities); + await queryRunner.createTable(opportunities); + await queryRunner.createTable(activityTypes); + await queryRunner.createTable(customFields); + } + + public async down(queryRunner: QueryRunner): Promise<any> { + await queryRunner.dropTable(leads.name); + await queryRunner.dropTable(activities.name); + await queryRunner.dropTable(opportunities.name); + await queryRunner.dropTable(activityTypes.name); + await queryRunner.dropTable(customFields.name); + } +} diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index a40f3d21c..47150300d 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -1,6 +1,6 @@ { "name": "@0x/pipeline", - "version": "1.0.2", + "version": "1.0.3", "private": true, "description": "Data pipeline for offline analysis", "scripts": { @@ -16,7 +16,7 @@ "test:coverage": "nyc npm run test:all --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib", - "lint": "tslint --project . --format stylish --exclude ./migrations/**/*", + "lint": "tslint --project . --format stylish --exclude ./migrations/**/* --exclude ./test/fixtures/**/**/*.json", "migrate:run": "yarn typeorm migration:run --config ./lib/src/ormconfig", "migrate:revert": "yarn typeorm migration:revert --config ./lib/src/ormconfig", "migrate:create": "yarn typeorm migration:create --config ./lib/src/ormconfig --dir migrations" @@ -39,16 +39,16 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/connect": "^3.0.10", - "@0x/contract-addresses": "^2.0.0", - "@0x/contract-artifacts": "^1.0.1", + "@0x/connect": "^3.0.11", + "@0x/contract-addresses": "^2.1.0", + "@0x/contract-artifacts": "^1.2.0", "@0x/contract-wrappers": "^3.0.0", - "@0x/dev-utils": "^1.0.21", - "@0x/order-utils": "^2.0.0", - "@0x/subproviders": "^2.1.8", - "@0x/types": "^1.4.1", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/dev-utils": "^1.0.22", + "@0x/order-utils": "^3.1.0", + "@0x/subproviders": "^2.1.9", + "@0x/types": "^1.5.0", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@types/dockerode": "^2.5.9", "@types/p-limit": "^2.0.0", "async-parallel": "^1.2.3", diff --git a/packages/pipeline/src/data_sources/copper/index.ts b/packages/pipeline/src/data_sources/copper/index.ts new file mode 100644 index 000000000..15df2fd7d --- /dev/null +++ b/packages/pipeline/src/data_sources/copper/index.ts @@ -0,0 +1,126 @@ +import { fetchAsync } from '@0x/utils'; +import Bottleneck from 'bottleneck'; + +import { + CopperActivityTypeCategory, + CopperActivityTypeResponse, + CopperCustomFieldResponse, + CopperSearchResponse, +} from '../../parsers/copper'; + +const HTTP_OK_STATUS = 200; +const COPPER_URI = 'https://api.prosperworks.com/developer_api/v1'; + +const DEFAULT_PAGINATION_PARAMS = { + page_size: 200, + sort_by: 'date_modified', + sort_direction: 'desc', +}; + +export type CopperSearchParams = CopperLeadSearchParams | CopperActivitySearchParams | CopperOpportunitySearchParams; +export interface CopperLeadSearchParams { + page_number?: number; +} + +export interface CopperActivitySearchParams { + minimum_activity_date: number; + page_number?: number; +} + +export interface CopperOpportunitySearchParams { + sort_by: string; // must override the default 'date_modified' for this endpoint + page_number?: number; +} +export enum CopperEndpoint { + Leads = '/leads/search', + Opportunities = '/opportunities/search', + Activities = '/activities/search', +} +const ONE_SECOND = 1000; + +function httpErrorCheck(response: Response): void { + if (response.status !== HTTP_OK_STATUS) { + throw new Error(`HTTP error while scraping Copper: [${JSON.stringify(response)}]`); + } +} +export class CopperSource { + private readonly _accessToken: string; + private readonly _userEmail: string; + private readonly _defaultHeaders: any; + private readonly _limiter: Bottleneck; + + constructor(maxConcurrentRequests: number, accessToken: string, userEmail: string) { + this._accessToken = accessToken; + this._userEmail = userEmail; + this._defaultHeaders = { + 'Content-Type': 'application/json', + 'X-PW-AccessToken': this._accessToken, + 'X-PW-Application': 'developer_api', + 'X-PW-UserEmail': this._userEmail, + }; + this._limiter = new Bottleneck({ + minTime: ONE_SECOND / maxConcurrentRequests, + reservoir: 30, + reservoirRefreshAmount: 30, + reservoirRefreshInterval: maxConcurrentRequests, + }); + } + + public async fetchNumberOfPagesAsync(endpoint: CopperEndpoint, searchParams?: CopperSearchParams): Promise<number> { + const resp = await this._limiter.schedule(() => + fetchAsync(COPPER_URI + endpoint, { + method: 'POST', + body: JSON.stringify({ ...DEFAULT_PAGINATION_PARAMS, ...searchParams }), + headers: this._defaultHeaders, + }), + ); + + httpErrorCheck(resp); + + // total number of records that match the request parameters + if (resp.headers.has('X-Pw-Total')) { + const totalRecords: number = parseInt(resp.headers.get('X-Pw-Total') as string, 10); // tslint:disable-line:custom-no-magic-numbers + return Math.ceil(totalRecords / DEFAULT_PAGINATION_PARAMS.page_size); + } else { + return 1; + } + } + public async fetchSearchResultsAsync<T extends CopperSearchResponse>( + endpoint: CopperEndpoint, + searchParams?: CopperSearchParams, + ): Promise<T[]> { + const request = { ...DEFAULT_PAGINATION_PARAMS, ...searchParams }; + const response = await this._limiter.schedule(() => + fetchAsync(COPPER_URI + endpoint, { + method: 'POST', + body: JSON.stringify(request), + headers: this._defaultHeaders, + }), + ); + httpErrorCheck(response); + const json: T[] = await response.json(); + return json; + } + + public async fetchActivityTypesAsync(): Promise<Map<CopperActivityTypeCategory, CopperActivityTypeResponse[]>> { + const response = await this._limiter.schedule(() => + fetchAsync(`${COPPER_URI}/activity_types`, { + method: 'GET', + headers: this._defaultHeaders, + }), + ); + httpErrorCheck(response); + return response.json(); + } + + public async fetchCustomFieldsAsync(): Promise<CopperCustomFieldResponse[]> { + const response = await this._limiter.schedule(() => + fetchAsync(`${COPPER_URI}/custom_field_definitions`, { + method: 'GET', + headers: this._defaultHeaders, + }), + ); + httpErrorCheck(response); + return response.json(); + } +} diff --git a/packages/pipeline/src/entities/copper_activity.ts b/packages/pipeline/src/entities/copper_activity.ts new file mode 100644 index 000000000..cbc034285 --- /dev/null +++ b/packages/pipeline/src/entities/copper_activity.ts @@ -0,0 +1,41 @@ +import { Column, Entity, Index, PrimaryColumn } from 'typeorm'; + +import { numberToBigIntTransformer } from '../utils'; + +@Entity({ name: 'copper_activities', schema: 'raw' }) +export class CopperActivity { + @PrimaryColumn({ type: 'bigint', transformer: numberToBigIntTransformer }) + public id!: number; + + @Index() + @Column({ name: 'parent_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public parentId!: number; + @Column({ name: 'parent_type', type: 'varchar' }) + public parentType!: string; + + // join with CopperActivityType + @Index() + @Column({ name: 'type_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public typeId!: number; + @Column({ name: 'type_category', type: 'varchar' }) + public typeCategory!: string; + @Column({ name: 'type_name', type: 'varchar', nullable: true }) + public typeName?: string; + + @Column({ name: 'user_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public userId!: number; + @Column({ name: 'old_value_id', type: 'bigint', nullable: true, transformer: numberToBigIntTransformer }) + public oldValueId?: number; + @Column({ name: 'old_value_name', type: 'varchar', nullable: true }) + public oldValueName?: string; + @Column({ name: 'new_value_id', type: 'bigint', nullable: true, transformer: numberToBigIntTransformer }) + public newValueId?: number; + @Column({ name: 'new_value_name', type: 'varchar', nullable: true }) + public newValueName?: string; + + @Index() + @Column({ name: 'date_created', type: 'bigint', transformer: numberToBigIntTransformer }) + public dateCreated!: number; + @PrimaryColumn({ name: 'date_modified', type: 'bigint', transformer: numberToBigIntTransformer }) + public dateModified!: number; +} diff --git a/packages/pipeline/src/entities/copper_activity_type.ts b/packages/pipeline/src/entities/copper_activity_type.ts new file mode 100644 index 000000000..8fb2dcf70 --- /dev/null +++ b/packages/pipeline/src/entities/copper_activity_type.ts @@ -0,0 +1,17 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { numberToBigIntTransformer } from '../utils'; + +@Entity({ name: 'copper_activity_types', schema: 'raw' }) +export class CopperActivityType { + @PrimaryColumn({ type: 'bigint', transformer: numberToBigIntTransformer }) + public id!: number; + @Column({ name: 'category', type: 'varchar' }) + public category!: string; + @Column({ name: 'name', type: 'varchar' }) + public name!: string; + @Column({ name: 'is_disabled', type: 'boolean', nullable: true }) + public isDisabled?: boolean; + @Column({ name: 'count_as_interaction', type: 'boolean', nullable: true }) + public countAsInteraction?: boolean; +} diff --git a/packages/pipeline/src/entities/copper_custom_field.ts b/packages/pipeline/src/entities/copper_custom_field.ts new file mode 100644 index 000000000..f23f6ab22 --- /dev/null +++ b/packages/pipeline/src/entities/copper_custom_field.ts @@ -0,0 +1,15 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { numberToBigIntTransformer } from '../utils'; + +@Entity({ name: 'copper_custom_fields', schema: 'raw' }) +export class CopperCustomField { + @PrimaryColumn({ type: 'bigint', transformer: numberToBigIntTransformer }) + public id!: number; + @Column({ name: 'data_type', type: 'varchar' }) + public dataType!: string; + @Column({ name: 'field_type', type: 'varchar', nullable: true }) + public fieldType?: string; + @Column({ name: 'name', type: 'varchar' }) + public name!: string; +} diff --git a/packages/pipeline/src/entities/copper_lead.ts b/packages/pipeline/src/entities/copper_lead.ts new file mode 100644 index 000000000..c51ccd761 --- /dev/null +++ b/packages/pipeline/src/entities/copper_lead.ts @@ -0,0 +1,38 @@ +import { Column, Entity, Index, PrimaryColumn } from 'typeorm'; + +import { numberToBigIntTransformer } from '../utils'; + +@Entity({ name: 'copper_leads', schema: 'raw' }) +export class CopperLead { + @PrimaryColumn({ type: 'bigint', transformer: numberToBigIntTransformer }) + public id!: number; + + @Column({ name: 'name', type: 'varchar', nullable: true }) + public name?: string; + @Column({ name: 'first_name', type: 'varchar', nullable: true }) + public firstName?: string; + @Column({ name: 'last_name', type: 'varchar', nullable: true }) + public lastName?: string; + @Column({ name: 'middle_name', type: 'varchar', nullable: true }) + public middleName?: string; + @Column({ name: 'assignee_id', type: 'bigint', transformer: numberToBigIntTransformer, nullable: true }) + public assigneeId?: number; + @Column({ name: 'company_name', type: 'varchar', nullable: true }) + public companyName?: string; + @Column({ name: 'customer_source_id', type: 'bigint', transformer: numberToBigIntTransformer, nullable: true }) + public customerSourceId?: number; + @Column({ name: 'monetary_value', type: 'integer', nullable: true }) + public monetaryValue?: number; + @Column({ name: 'status', type: 'varchar' }) + public status!: string; + @Column({ name: 'status_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public statusId!: number; + @Column({ name: 'title', type: 'varchar', nullable: true }) + public title?: string; + + @Index() + @Column({ name: 'date_created', type: 'bigint', transformer: numberToBigIntTransformer }) + public dateCreated!: number; + @PrimaryColumn({ name: 'date_modified', type: 'bigint', transformer: numberToBigIntTransformer }) + public dateModified!: number; +} diff --git a/packages/pipeline/src/entities/copper_opportunity.ts b/packages/pipeline/src/entities/copper_opportunity.ts new file mode 100644 index 000000000..e12bd69ce --- /dev/null +++ b/packages/pipeline/src/entities/copper_opportunity.ts @@ -0,0 +1,45 @@ +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +import { numberToBigIntTransformer } from '../utils'; + +@Entity({ name: 'copper_opportunities', schema: 'raw' }) +export class CopperOpportunity { + @PrimaryColumn({ name: 'id', type: 'bigint', transformer: numberToBigIntTransformer }) + public id!: number; + @Column({ name: 'name', type: 'varchar' }) + public name!: string; + @Column({ name: 'assignee_id', nullable: true, type: 'bigint', transformer: numberToBigIntTransformer }) + public assigneeId?: number; + @Column({ name: 'close_date', nullable: true, type: 'varchar' }) + public closeDate?: string; + @Column({ name: 'company_id', nullable: true, type: 'bigint', transformer: numberToBigIntTransformer }) + public companyId?: number; + @Column({ name: 'company_name', nullable: true, type: 'varchar' }) + public companyName?: string; + @Column({ name: 'customer_source_id', nullable: true, type: 'bigint', transformer: numberToBigIntTransformer }) + public customerSourceId?: number; + @Column({ name: 'loss_reason_id', nullable: true, type: 'bigint', transformer: numberToBigIntTransformer }) + public lossReasonId?: number; + @Column({ name: 'pipeline_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public pipelineId!: number; + @Column({ name: 'pipeline_stage_id', type: 'bigint', transformer: numberToBigIntTransformer }) + public pipelineStageId!: number; + @Column({ name: 'primary_contact_id', nullable: true, type: 'bigint', transformer: numberToBigIntTransformer }) + public primaryContactId?: number; + @Column({ name: 'priority', nullable: true, type: 'varchar' }) + public priority?: string; + @Column({ name: 'status', type: 'varchar' }) + public status!: string; + @Column({ name: 'interaction_count', type: 'bigint', transformer: numberToBigIntTransformer }) + public interactionCount!: number; + @Column({ name: 'monetary_value', nullable: true, type: 'integer' }) + public monetaryValue?: number; + @Column({ name: 'win_probability', nullable: true, type: 'integer' }) + public winProbability?: number; + @Column({ name: 'date_created', type: 'bigint', transformer: numberToBigIntTransformer }) + public dateCreated!: number; + @PrimaryColumn({ name: 'date_modified', type: 'bigint', transformer: numberToBigIntTransformer }) + public dateModified!: number; + @Column({ name: 'custom_fields', type: 'jsonb' }) + public customFields!: { [key: number]: number }; +} diff --git a/packages/pipeline/src/entities/index.ts b/packages/pipeline/src/entities/index.ts index cc3de78bb..27c153c07 100644 --- a/packages/pipeline/src/entities/index.ts +++ b/packages/pipeline/src/entities/index.ts @@ -16,4 +16,10 @@ export { TokenOrderbookSnapshot } from './token_order'; export { Transaction } from './transaction'; export { ERC20ApprovalEvent } from './erc20_approval_event'; +export { CopperLead } from './copper_lead'; +export { CopperActivity } from './copper_activity'; +export { CopperOpportunity } from './copper_opportunity'; +export { CopperActivityType } from './copper_activity_type'; +export { CopperCustomField } from './copper_custom_field'; + export type ExchangeEvent = ExchangeFillEvent | ExchangeCancelEvent | ExchangeCancelUpToEvent; diff --git a/packages/pipeline/src/entities/token_order.ts b/packages/pipeline/src/entities/token_order.ts index 4b8f0abc3..2709747cb 100644 --- a/packages/pipeline/src/entities/token_order.ts +++ b/packages/pipeline/src/entities/token_order.ts @@ -1,7 +1,6 @@ import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; -import { OrderType } from '../types'; import { bigNumberTransformer, numberToBigIntTransformer } from '../utils'; @Entity({ name: 'token_orderbook_snapshots', schema: 'raw' }) @@ -11,7 +10,7 @@ export class TokenOrderbookSnapshot { @PrimaryColumn({ name: 'source' }) public source!: string; @PrimaryColumn({ name: 'order_type' }) - public orderType!: OrderType; + public orderType!: string; @PrimaryColumn({ name: 'price', type: 'numeric', transformer: bigNumberTransformer }) public price!: BigNumber; @PrimaryColumn({ name: 'base_asset_symbol' }) diff --git a/packages/pipeline/src/ormconfig.ts b/packages/pipeline/src/ormconfig.ts index fe11d81d5..2700714cd 100644 --- a/packages/pipeline/src/ormconfig.ts +++ b/packages/pipeline/src/ormconfig.ts @@ -2,6 +2,11 @@ import { ConnectionOptions } from 'typeorm'; import { Block, + CopperActivity, + CopperActivityType, + CopperCustomField, + CopperLead, + CopperOpportunity, DexTrade, ERC20ApprovalEvent, ExchangeCancelEvent, @@ -18,6 +23,11 @@ import { const entities = [ Block, + CopperOpportunity, + CopperActivity, + CopperActivityType, + CopperCustomField, + CopperLead, DexTrade, ExchangeCancelEvent, ExchangeCancelUpToEvent, diff --git a/packages/pipeline/src/parsers/copper/index.ts b/packages/pipeline/src/parsers/copper/index.ts new file mode 100644 index 000000000..6c0c5abd5 --- /dev/null +++ b/packages/pipeline/src/parsers/copper/index.ts @@ -0,0 +1,259 @@ +import * as R from 'ramda'; + +import { CopperActivity, CopperActivityType, CopperCustomField, CopperLead, CopperOpportunity } from '../../entities'; + +const ONE_SECOND = 1000; +export type CopperSearchResponse = CopperLeadResponse | CopperActivityResponse | CopperOpportunityResponse; +export interface CopperLeadResponse { + id: number; + name?: string; + first_name?: string; + last_name?: string; + middle_name?: string; + assignee_id?: number; + company_name?: string; + customer_source_id?: number; + monetary_value?: number; + status: string; + status_id: number; + title?: string; + date_created: number; // in seconds + date_modified: number; // in seconds +} + +export interface CopperActivityResponse { + id: number; + parent: CopperActivityParentResponse; + type: CopperActivityTypeResponse; + user_id: number; + activity_date: number; + old_value: CopperActivityValueResponse; + new_value: CopperActivityValueResponse; + date_created: number; // in seconds + date_modified: number; // in seconds +} + +export interface CopperActivityValueResponse { + id: number; + name: string; +} +export interface CopperActivityParentResponse { + id: number; + type: string; +} + +// custom activity types +export enum CopperActivityTypeCategory { + user = 'user', + system = 'system', +} +export interface CopperActivityTypeResponse { + id: number; + category: CopperActivityTypeCategory; + name: string; + is_disabled?: boolean; + count_as_interaction?: boolean; +} + +export interface CopperOpportunityResponse { + id: number; + name: string; + assignee_id?: number; + close_date?: string; + company_id?: number; + company_name?: string; + customer_source_id?: number; + loss_reason_id?: number; + pipeline_id: number; + pipeline_stage_id: number; + primary_contact_id?: number; + priority?: string; + status: string; + tags: string[]; + interaction_count: number; + monetary_value?: number; + win_probability?: number; + date_created: number; // in seconds + date_modified: number; // in seconds + custom_fields: CopperNestedCustomFieldResponse[]; +} +interface CopperNestedCustomFieldResponse { + custom_field_definition_id: number; + value: number | number[] | null; +} +// custom fields +export enum CopperCustomFieldType { + String = 'String', + Text = 'Text', + Dropdown = 'Dropdown', + MultiSelect = 'MultiSelect', // not in API documentation but shows up in results + Date = 'Date', + Checkbox = 'Checkbox', + Float = 'Float', + URL = 'URL', + Percentage = 'Percentage', + Currency = 'Currency', + Connect = 'Connect', +} +export interface CopperCustomFieldOptionResponse { + id: number; + name: string; +} +export interface CopperCustomFieldResponse { + id: number; + name: string; + data_type: CopperCustomFieldType; + options?: CopperCustomFieldOptionResponse[]; +} +/** + * Parse response from Copper API /search/leads/ + * + * @param leads - The array of leads returned from the API + * @returns Returns an array of Copper Lead entities + */ +export function parseLeads(leads: CopperLeadResponse[]): CopperLead[] { + return leads.map(lead => { + const entity = new CopperLead(); + entity.id = lead.id; + entity.name = lead.name || undefined; + entity.firstName = lead.first_name || undefined; + entity.lastName = lead.last_name || undefined; + entity.middleName = lead.middle_name || undefined; + entity.assigneeId = lead.assignee_id || undefined; + entity.companyName = lead.company_name || undefined; + entity.customerSourceId = lead.customer_source_id || undefined; + entity.monetaryValue = lead.monetary_value || undefined; + entity.status = lead.status; + entity.statusId = lead.status_id; + entity.title = lead.title || undefined; + entity.dateCreated = lead.date_created * ONE_SECOND; + entity.dateModified = lead.date_modified * ONE_SECOND; + return entity; + }); +} + +/** + * Parse response from Copper API /search/activities/ + * + * @param activities - The array of activities returned from the API + * @returns Returns an array of Copper Activity entities + */ +export function parseActivities(activities: CopperActivityResponse[]): CopperActivity[] { + return activities.map(activity => { + const entity = new CopperActivity(); + entity.id = activity.id; + + entity.parentId = activity.parent.id; + entity.parentType = activity.parent.type; + + entity.typeId = activity.type.id; + entity.typeCategory = activity.type.category.toString(); + entity.typeName = activity.type.name; + + entity.userId = activity.user_id; + entity.dateCreated = activity.date_created * ONE_SECOND; + entity.dateModified = activity.date_modified * ONE_SECOND; + + // nested nullable fields + entity.oldValueId = R.path(['old_value', 'id'], activity); + entity.oldValueName = R.path(['old_value', 'name'], activity); + entity.newValueId = R.path(['new_value', 'id'], activity); + entity.newValueName = R.path(['new_value', 'name'], activity); + + return entity; + }); +} + +/** + * Parse response from Copper API /search/opportunities/ + * + * @param opportunities - The array of opportunities returned from the API + * @returns Returns an array of Copper Opportunity entities + */ +export function parseOpportunities(opportunities: CopperOpportunityResponse[]): CopperOpportunity[] { + return opportunities.map(opp => { + const customFields: { [key: number]: number } = opp.custom_fields + .filter(f => f.value !== null) + .map(f => ({ + ...f, + value: ([] as number[]).concat(f.value || []), // normalise all values to number[] + })) + .map(f => f.value.map(val => [f.custom_field_definition_id, val] as [number, number])) // pair each value with the custom_field_definition_id + .reduce((acc, pair) => acc.concat(pair)) // flatten + .reduce<{ [key: number]: number }>((obj, [key, value]) => { + // transform into object literal + obj[key] = value; + return obj; + }, {}); + + const entity = new CopperOpportunity(); + entity.id = opp.id; + entity.name = opp.name; + entity.assigneeId = opp.assignee_id || undefined; + entity.closeDate = opp.close_date || undefined; + entity.companyId = opp.company_id || undefined; + entity.companyName = opp.company_name || undefined; + entity.customerSourceId = opp.customer_source_id || undefined; + entity.lossReasonId = opp.loss_reason_id || undefined; + entity.pipelineId = opp.pipeline_id; + entity.pipelineStageId = opp.pipeline_stage_id; + entity.primaryContactId = opp.primary_contact_id || undefined; + entity.priority = opp.priority || undefined; + entity.status = opp.status; + entity.interactionCount = opp.interaction_count; + entity.monetaryValue = opp.monetary_value || undefined; + entity.winProbability = opp.win_probability === null ? undefined : opp.win_probability; + entity.dateCreated = opp.date_created * ONE_SECOND; + entity.dateModified = opp.date_modified * ONE_SECOND; + entity.customFields = customFields; + return entity; + }); +} + +/** + * Parse response from Copper API /activity_types/ + * + * @param activityTypeResponse - Activity Types response from the API, keyed by "user" or "system" + * @returns Returns an array of Copper Activity Type entities + */ +export function parseActivityTypes( + activityTypeResponse: Map<CopperActivityTypeCategory, CopperActivityTypeResponse[]>, +): CopperActivityType[] { + const values: CopperActivityTypeResponse[] = R.flatten(Object.values(activityTypeResponse)); + return values.map(activityType => ({ + id: activityType.id, + name: activityType.name, + category: activityType.category.toString(), + isDisabled: activityType.is_disabled, + countAsInteraction: activityType.count_as_interaction, + })); +} + +/** + * Parse response from Copper API /custom_field_definitions/ + * + * @param customFieldResponse - array of custom field definitions returned from the API, consisting of top-level fields and nested fields + * @returns Returns an array of Copper Custom Field entities + */ +export function parseCustomFields(customFieldResponse: CopperCustomFieldResponse[]): CopperCustomField[] { + function parseTopLevelField(field: CopperCustomFieldResponse): CopperCustomField[] { + const topLevelField: CopperCustomField = { + id: field.id, + name: field.name, + dataType: field.data_type.toString(), + }; + + if (field.options !== undefined) { + const nestedFields: CopperCustomField[] = field.options.map(option => ({ + id: option.id, + name: option.name, + dataType: field.name, + fieldType: 'option', + })); + return nestedFields.concat(topLevelField); + } else { + return [topLevelField]; + } + } + return R.chain(parseTopLevelField, customFieldResponse); +} diff --git a/packages/pipeline/src/parsers/ddex_orders/index.ts b/packages/pipeline/src/parsers/ddex_orders/index.ts index d7b97efbe..eeb9c9d5b 100644 --- a/packages/pipeline/src/parsers/ddex_orders/index.ts +++ b/packages/pipeline/src/parsers/ddex_orders/index.ts @@ -23,8 +23,12 @@ export function parseDdexOrders( ): TokenOrder[] { const aggregatedBids = aggregateOrders(ddexOrderbook.bids); const aggregatedAsks = aggregateOrders(ddexOrderbook.asks); - const parsedBids = aggregatedBids.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'bid', source, order)); - const parsedAsks = aggregatedAsks.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'ask', source, order)); + const parsedBids = aggregatedBids.map(order => + parseDdexOrder(ddexMarket, observedTimestamp, OrderType.Bid, source, order), + ); + const parsedAsks = aggregatedAsks.map(order => + parseDdexOrder(ddexMarket, observedTimestamp, OrderType.Ask, source, order), + ); return parsedBids.concat(parsedAsks); } diff --git a/packages/pipeline/src/parsers/events/exchange_events.ts b/packages/pipeline/src/parsers/events/exchange_events.ts index e18106c75..9c4a5f89a 100644 --- a/packages/pipeline/src/parsers/events/exchange_events.ts +++ b/packages/pipeline/src/parsers/events/exchange_events.ts @@ -5,7 +5,7 @@ import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent } from '../../entities'; -import { bigNumbertoStringOrNull } from '../../utils'; +import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils'; /** * Parses raw event logs for a fill event and returns an array of @@ -40,9 +40,7 @@ export const parseExchangeCancelUpToEvents: ( */ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs<ExchangeFillEventArgs>): ExchangeFillEvent { const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const exchangeFillEvent = new ExchangeFillEvent(); exchangeFillEvent.contractAddress = eventLog.address as string; exchangeFillEvent.blockNumber = eventLog.blockNumber as number; @@ -59,16 +57,24 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs<Exchang exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid; exchangeFillEvent.orderHash = eventLog.args.orderHash; exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData; - exchangeFillEvent.makerAssetType = makerAssetType; + // tslint:disable-next-line:no-unnecessary-type-assertion + exchangeFillEvent.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId); exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId; - exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData + exchangeFillEvent.makerTokenAddress = assetDataUtils.isMultiAssetData(makerAssetData) + ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.makerAssetData).nestedAssetData[0].tokenAddress + : makerAssetData.tokenAddress; // tslint has a false positive here. Type assertion is required. // tslint:disable-next-line:no-unnecessary-type-assertion exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData; - exchangeFillEvent.takerAssetType = takerAssetType; + // tslint:disable-next-line:no-unnecessary-type-assertion + exchangeFillEvent.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId); exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId; - exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData + exchangeFillEvent.takerTokenAddress = assetDataUtils.isMultiAssetData(takerAssetData) + ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.takerAssetData).nestedAssetData[0].tokenAddress + : takerAssetData.tokenAddress; // tslint:disable-next-line:no-unnecessary-type-assertion exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); return exchangeFillEvent; @@ -83,9 +89,7 @@ export function _convertToExchangeCancelEvent( eventLog: LogWithDecodedArgs<ExchangeCancelEventArgs>, ): ExchangeCancelEvent { const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const exchangeCancelEvent = new ExchangeCancelEvent(); exchangeCancelEvent.contractAddress = eventLog.address as string; exchangeCancelEvent.blockNumber = eventLog.blockNumber as number; @@ -98,15 +102,23 @@ export function _convertToExchangeCancelEvent( exchangeCancelEvent.senderAddress = eventLog.args.senderAddress; exchangeCancelEvent.orderHash = eventLog.args.orderHash; exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData; - exchangeCancelEvent.makerAssetType = makerAssetType; + // tslint:disable-next-line:no-unnecessary-type-assertion + exchangeCancelEvent.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId); exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId; - exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData + exchangeCancelEvent.makerTokenAddress = assetDataUtils.isMultiAssetData(makerAssetData) + ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.makerAssetData).nestedAssetData[0].tokenAddress + : makerAssetData.tokenAddress; // tslint:disable-next-line:no-unnecessary-type-assertion exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData; - exchangeCancelEvent.takerAssetType = takerAssetType; + // tslint:disable-next-line:no-unnecessary-type-assertion + exchangeCancelEvent.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId); exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId; - exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData + exchangeCancelEvent.takerTokenAddress = assetDataUtils.isMultiAssetData(takerAssetData) + ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.takerAssetData).nestedAssetData[0].tokenAddress + : takerAssetData.tokenAddress; // tslint:disable-next-line:no-unnecessary-type-assertion exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); return exchangeCancelEvent; diff --git a/packages/pipeline/src/parsers/idex_orders/index.ts b/packages/pipeline/src/parsers/idex_orders/index.ts index dfe27455c..14b871195 100644 --- a/packages/pipeline/src/parsers/idex_orders/index.ts +++ b/packages/pipeline/src/parsers/idex_orders/index.ts @@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils'; import { aggregateOrders } from '../utils'; -import { IdexOrder, IdexOrderbook, IdexOrderParam } from '../../data_sources/idex'; +import { IdexOrderbook, IdexOrderParam } from '../../data_sources/idex'; import { TokenOrderbookSnapshot as TokenOrder } from '../../entities'; import { OrderType } from '../../types'; @@ -21,7 +21,9 @@ export function parseIdexOrders(idexOrderbook: IdexOrderbook, observedTimestamp: const idexBidOrder = idexOrderbook.bids[0]; const parsedBids = aggregatedBids.length > 0 - ? aggregatedBids.map(order => parseIdexOrder(idexBidOrder.params, observedTimestamp, 'bid', source, order)) + ? aggregatedBids.map(order => + parseIdexOrder(idexBidOrder.params, observedTimestamp, OrderType.Bid, source, order), + ) : []; const aggregatedAsks = aggregateOrders(idexOrderbook.asks); @@ -29,7 +31,9 @@ export function parseIdexOrders(idexOrderbook: IdexOrderbook, observedTimestamp: const idexAskOrder = idexOrderbook.asks[0]; const parsedAsks = aggregatedAsks.length > 0 - ? aggregatedAsks.map(order => parseIdexOrder(idexAskOrder.params, observedTimestamp, 'ask', source, order)) + ? aggregatedAsks.map(order => + parseIdexOrder(idexAskOrder.params, observedTimestamp, OrderType.Ask, source, order), + ) : []; return parsedBids.concat(parsedAsks); } @@ -62,7 +66,7 @@ export function parseIdexOrder( tokenOrder.baseVolume = amount; tokenOrder.quoteVolume = price.times(amount); - if (orderType === 'bid') { + if (orderType === OrderType.Bid) { tokenOrder.baseAssetSymbol = idexOrderParam.buySymbol; tokenOrder.baseAssetAddress = idexOrderParam.tokenBuy; tokenOrder.quoteAssetSymbol = idexOrderParam.sellSymbol; diff --git a/packages/pipeline/src/parsers/oasis_orders/index.ts b/packages/pipeline/src/parsers/oasis_orders/index.ts index 13997f31b..b71fb65b9 100644 --- a/packages/pipeline/src/parsers/oasis_orders/index.ts +++ b/packages/pipeline/src/parsers/oasis_orders/index.ts @@ -23,13 +23,13 @@ export function parseOasisOrders( observedTimestamp: number, source: string, ): TokenOrder[] { - const aggregatedBids = aggregateOrders(R.filter(R.propEq('act', 'bid'), oasisOrderbook)); - const aggregatedAsks = aggregateOrders(R.filter(R.propEq('act', 'ask'), oasisOrderbook)); + const aggregatedBids = aggregateOrders(R.filter(R.propEq('act', OrderType.Bid), oasisOrderbook)); + const aggregatedAsks = aggregateOrders(R.filter(R.propEq('act', OrderType.Ask), oasisOrderbook)); const parsedBids = aggregatedBids.map(order => - parseOasisOrder(oasisMarket, observedTimestamp, 'bid', source, order), + parseOasisOrder(oasisMarket, observedTimestamp, OrderType.Bid, source, order), ); const parsedAsks = aggregatedAsks.map(order => - parseOasisOrder(oasisMarket, observedTimestamp, 'ask', source, order), + parseOasisOrder(oasisMarket, observedTimestamp, OrderType.Ask, source, order), ); return parsedBids.concat(parsedAsks); } diff --git a/packages/pipeline/src/parsers/paradex_orders/index.ts b/packages/pipeline/src/parsers/paradex_orders/index.ts index 5ceeb64a4..85990dae4 100644 --- a/packages/pipeline/src/parsers/paradex_orders/index.ts +++ b/packages/pipeline/src/parsers/paradex_orders/index.ts @@ -21,10 +21,10 @@ export function parseParadexOrders( source: string, ): TokenOrder[] { const parsedBids = paradexOrderbookResponse.bids.map(order => - parseParadexOrder(paradexMarket, observedTimestamp, 'bid', source, order), + parseParadexOrder(paradexMarket, observedTimestamp, OrderType.Bid, source, order), ); const parsedAsks = paradexOrderbookResponse.asks.map(order => - parseParadexOrder(paradexMarket, observedTimestamp, 'ask', source, order), + parseParadexOrder(paradexMarket, observedTimestamp, OrderType.Ask, source, order), ); return parsedBids.concat(parsedAsks); } diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index ef8901e40..13fe632a4 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -4,7 +4,7 @@ import { AssetProxyId, ERC721AssetData } from '@0x/types'; import * as R from 'ramda'; import { SraOrder } from '../../entities'; -import { bigNumbertoStringOrNull } from '../../utils'; +import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils'; /** * Parses a raw order response from an SRA endpoint and returns an array of @@ -22,9 +22,7 @@ export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { export function _convertToEntity(apiOrder: APIOrder): SraOrder { // TODO(albrow): refactor out common asset data decoding code. const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const sraOrder = new SraOrder(); sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; @@ -43,16 +41,24 @@ export function _convertToEntity(apiOrder: APIOrder): SraOrder { sraOrder.signature = apiOrder.order.signature; sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData; - sraOrder.makerAssetType = makerAssetType; + // tslint:disable-next-line:no-unnecessary-type-assertion + sraOrder.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId); sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; - sraOrder.makerTokenAddress = makerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData + sraOrder.makerTokenAddress = assetDataUtils.isMultiAssetData(makerAssetData) + ? assetDataUtils.decodeMultiAssetDataRecursively(apiOrder.order.makerAssetData).nestedAssetData[0].tokenAddress + : makerAssetData.tokenAddress; // tslint has a false positive here. Type assertion is required. // tslint:disable-next-line:no-unnecessary-type-assertion sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; - sraOrder.takerAssetType = takerAssetType; + // tslint:disable-next-line:no-unnecessary-type-assertion + sraOrder.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId); sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; - sraOrder.takerTokenAddress = takerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData + sraOrder.takerTokenAddress = assetDataUtils.isMultiAssetData(takerAssetData) + ? assetDataUtils.decodeMultiAssetDataRecursively(apiOrder.order.takerAssetData).nestedAssetData[0].tokenAddress + : takerAssetData.tokenAddress; // tslint:disable-next-line:no-unnecessary-type-assertion sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); diff --git a/packages/pipeline/src/scripts/pull_copper.ts b/packages/pipeline/src/scripts/pull_copper.ts new file mode 100644 index 000000000..69814f209 --- /dev/null +++ b/packages/pipeline/src/scripts/pull_copper.ts @@ -0,0 +1,129 @@ +// tslint:disable:no-console +import * as R from 'ramda'; +import { Connection, ConnectionOptions, createConnection, Repository } from 'typeorm'; + +import { CopperEndpoint, CopperSearchParams, CopperSource } from '../data_sources/copper'; +import { CopperActivity, CopperActivityType, CopperCustomField, CopperLead, CopperOpportunity } from '../entities'; +import * as ormConfig from '../ormconfig'; +import { + CopperSearchResponse, + parseActivities, + parseActivityTypes, + parseCustomFields, + parseLeads, + parseOpportunities, +} from '../parsers/copper'; +import { handleError } from '../utils'; +const ONE_SECOND = 1000; +const COPPER_RATE_LIMIT = 10; +let connection: Connection; + +(async () => { + connection = await createConnection(ormConfig as ConnectionOptions); + + const accessToken = process.env.COPPER_ACCESS_TOKEN; + const userEmail = process.env.COPPER_USER_EMAIL; + if (accessToken === undefined || userEmail === undefined) { + throw new Error('Missing required env var: COPPER_ACCESS_TOKEN and/or COPPER_USER_EMAIL'); + } + const source = new CopperSource(COPPER_RATE_LIMIT, accessToken, userEmail); + + const fetchPromises = [ + fetchAndSaveLeadsAsync(source), + fetchAndSaveOpportunitiesAsync(source), + fetchAndSaveActivitiesAsync(source), + fetchAndSaveCustomFieldsAsync(source), + fetchAndSaveActivityTypesAsync(source), + ]; + fetchPromises.forEach(async fn => { + await fn; + }); +})().catch(handleError); + +async function fetchAndSaveLeadsAsync(source: CopperSource): Promise<void> { + const repository = connection.getRepository(CopperLead); + const startTime = await getMaxAsync(connection, 'date_modified', 'raw.copper_leads'); + console.log(`Fetching Copper leads starting from ${startTime}...`); + await fetchAndSaveAsync(CopperEndpoint.Leads, source, startTime, {}, parseLeads, repository); +} + +async function fetchAndSaveOpportunitiesAsync(source: CopperSource): Promise<void> { + const repository = connection.getRepository(CopperOpportunity); + const startTime = await getMaxAsync(connection, 'date_modified', 'raw.copper_opportunities'); + console.log(`Fetching Copper opportunities starting from ${startTime}...`); + await fetchAndSaveAsync( + CopperEndpoint.Opportunities, + source, + startTime, + { sort_by: 'name' }, + parseOpportunities, + repository, + ); +} + +async function fetchAndSaveActivitiesAsync(source: CopperSource): Promise<void> { + const repository = connection.getRepository(CopperActivity); + const startTime = await getMaxAsync(connection, 'date_modified', 'raw.copper_activities'); + const searchParams = { + minimum_activity_date: Math.floor(startTime / ONE_SECOND), + }; + console.log(`Fetching Copper activities starting from ${startTime}...`); + await fetchAndSaveAsync(CopperEndpoint.Activities, source, startTime, searchParams, parseActivities, repository); +} + +async function getMaxAsync(conn: Connection, sortColumn: string, tableName: string): Promise<number> { + const queryResult = await conn.query(`SELECT MAX(${sortColumn}) as _max from ${tableName};`); + if (R.isEmpty(queryResult)) { + return 0; + } else { + return queryResult[0]._max; + } +} + +// (Xianny): Copper API doesn't allow queries to filter by date. To ensure that we are filling in ascending chronological +// order and not missing any records, we are scraping all available pages. If Copper data gets larger, +// it would make sense to search for and start filling from the first page that contains a new record. +// This search would increase our network calls and is not efficient to implement with our current small volume +// of Copper records. +async function fetchAndSaveAsync<T extends CopperSearchResponse, E>( + endpoint: CopperEndpoint, + source: CopperSource, + startTime: number, + searchParams: CopperSearchParams, + parseFn: (recs: T[]) => E[], + repository: Repository<E>, +): Promise<void> { + let saved = 0; + const numPages = await source.fetchNumberOfPagesAsync(endpoint); + try { + for (let i = numPages; i > 0; i--) { + console.log(`Fetching page ${i}/${numPages} of ${endpoint}...`); + const raw = await source.fetchSearchResultsAsync<T>(endpoint, { + ...searchParams, + page_number: i, + }); + const newRecords = raw.filter(rec => rec.date_modified * ONE_SECOND > startTime); + const parsed = parseFn(newRecords); + await repository.save<any>(parsed); + saved += newRecords.length; + } + } catch (err) { + console.log(`Error fetching ${endpoint}, stopping: ${err.stack}`); + } finally { + console.log(`Saved ${saved} items from ${endpoint}, done.`); + } +} + +async function fetchAndSaveActivityTypesAsync(source: CopperSource): Promise<void> { + console.log(`Fetching Copper activity types...`); + const activityTypes = await source.fetchActivityTypesAsync(); + const repository = connection.getRepository(CopperActivityType); + await repository.save(parseActivityTypes(activityTypes)); +} + +async function fetchAndSaveCustomFieldsAsync(source: CopperSource): Promise<void> { + console.log(`Fetching Copper custom fields...`); + const customFields = await source.fetchCustomFieldsAsync(); + const repository = connection.getRepository(CopperCustomField); + await repository.save(parseCustomFields(customFields)); +} diff --git a/packages/pipeline/src/scripts/pull_missing_blocks.ts b/packages/pipeline/src/scripts/pull_missing_blocks.ts index bb5385126..ced9d99eb 100644 --- a/packages/pipeline/src/scripts/pull_missing_blocks.ts +++ b/packages/pipeline/src/scripts/pull_missing_blocks.ts @@ -14,20 +14,29 @@ import { handleError, INFURA_ROOT_URL } from '../utils'; // Number of blocks to save at once. const BATCH_SAVE_SIZE = 1000; // Maximum number of requests to send at once. -const MAX_CONCURRENCY = 10; +const MAX_CONCURRENCY = 20; // Maximum number of blocks to query for at once. This is also the maximum // number of blocks we will hold in memory prior to being saved to the database. const MAX_BLOCKS_PER_QUERY = 1000; let connection: Connection; +const tablesWithMissingBlocks = [ + 'raw.exchange_fill_events', + 'raw.exchange_cancel_events', + 'raw.exchange_cancel_up_to_events', + 'raw.erc20_approval_events', +]; + (async () => { connection = await createConnection(ormConfig as ConnectionOptions); const provider = web3Factory.getRpcProvider({ rpcUrl: INFURA_ROOT_URL, }); const web3Source = new Web3Source(provider); - await getAllMissingBlocksAsync(web3Source); + for (const tableName of tablesWithMissingBlocks) { + await getAllMissingBlocksAsync(web3Source, tableName); + } process.exit(0); })().catch(handleError); @@ -35,10 +44,11 @@ interface MissingBlocksResponse { block_number: string; } -async function getAllMissingBlocksAsync(web3Source: Web3Source): Promise<void> { +async function getAllMissingBlocksAsync(web3Source: Web3Source, tableName: string): Promise<void> { const blocksRepository = connection.getRepository(Block); while (true) { - const blockNumbers = await getMissingBlockNumbersAsync(); + console.log(`Checking for missing blocks in ${tableName}...`); + const blockNumbers = await getMissingBlockNumbersAsync(tableName); if (blockNumbers.length === 0) { // There are no more missing blocks. We're done. break; @@ -46,24 +56,14 @@ async function getAllMissingBlocksAsync(web3Source: Web3Source): Promise<void> { await getAndSaveBlocksAsync(web3Source, blocksRepository, blockNumbers); } const totalBlocks = await blocksRepository.count(); - console.log(`Done saving blocks. There are now ${totalBlocks} total blocks.`); + console.log(`Done saving blocks for ${tableName}. There are now ${totalBlocks} total blocks.`); } -async function getMissingBlockNumbersAsync(): Promise<number[]> { - // Note(albrow): The easiest way to get all the blocks we need is to - // consider all the events tables together in a single query. If this query - // gets too slow, we should consider re-architecting so that we can work on - // getting the blocks for one type of event at a time. +async function getMissingBlockNumbersAsync(tableName: string): Promise<number[]> { + // This query returns up to `MAX_BLOCKS_PER_QUERY` distinct block numbers + // which are present in `tableName` but not in `raw.blocks`. const response = (await connection.query( - `WITH all_events AS ( - SELECT block_number FROM raw.exchange_fill_events - UNION SELECT block_number FROM raw.exchange_cancel_events - UNION SELECT block_number FROM raw.exchange_cancel_up_to_events - UNION SELECT block_number FROM raw.erc20_approval_events - ) - SELECT DISTINCT(block_number) FROM all_events - WHERE block_number NOT IN (SELECT number FROM raw.blocks) - ORDER BY block_number ASC LIMIT $1`, + `SELECT DISTINCT(block_number) FROM ${tableName} LEFT JOIN raw.blocks ON ${tableName}.block_number = raw.blocks.number WHERE number IS NULL LIMIT $1;`, [MAX_BLOCKS_PER_QUERY], )) as MissingBlocksResponse[]; const blockNumberStrings = R.pluck('block_number', response); @@ -86,4 +86,5 @@ async function getAndSaveBlocksAsync( const blocks = R.map(parseBlock, rawBlocks); console.log(`Saving ${blocks.length} blocks...`); await blocksRepository.save(blocks, { chunk: Math.ceil(blocks.length / BATCH_SAVE_SIZE) }); + console.log('Done saving this batch of blocks'); } diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts index e02b42a40..5f2121807 100644 --- a/packages/pipeline/src/types.ts +++ b/packages/pipeline/src/types.ts @@ -1,2 +1,9 @@ -export type AssetType = 'erc20' | 'erc721'; -export type OrderType = 'bid' | 'ask'; +export enum AssetType { + ERC20 = 'erc20', + ERC721 = 'erc721', + MultiAsset = 'multiAsset', +} +export enum OrderType { + Bid = 'bid', + Ask = 'ask', +} diff --git a/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts new file mode 100644 index 000000000..2cd05a616 --- /dev/null +++ b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts @@ -0,0 +1,20 @@ +import { AssetProxyId } from '@0x/types'; + +import { AssetType } from '../../types'; + +/** + * Converts an assetProxyId to its string equivalent + * @param assetProxyId Id of AssetProxy + */ +export function convertAssetProxyIdToType(assetProxyId: AssetProxyId): AssetType { + switch (assetProxyId) { + case AssetProxyId.ERC20: + return AssetType.ERC20; + case AssetProxyId.ERC721: + return AssetType.ERC721; + case AssetProxyId.MultiAsset: + return AssetType.MultiAsset; + default: + throw new Error(`${assetProxyId} not a supported assetProxyId`); + } +} diff --git a/packages/pipeline/src/utils/transformers/index.ts b/packages/pipeline/src/utils/transformers/index.ts index 232c1c5de..31a4c9223 100644 --- a/packages/pipeline/src/utils/transformers/index.ts +++ b/packages/pipeline/src/utils/transformers/index.ts @@ -1,2 +1,3 @@ export * from './big_number'; export * from './number_to_bigint'; +export * from './asset_proxy_id_types'; diff --git a/packages/pipeline/src/utils/transformers/number_to_bigint.ts b/packages/pipeline/src/utils/transformers/number_to_bigint.ts index 85560c1f0..9736d7c18 100644 --- a/packages/pipeline/src/utils/transformers/number_to_bigint.ts +++ b/packages/pipeline/src/utils/transformers/number_to_bigint.ts @@ -9,8 +9,12 @@ const decimalRadix = 10; // https://github.com/typeorm/typeorm/issues/2400 for more information. export class NumberToBigIntTransformer implements ValueTransformer { // tslint:disable-next-line:prefer-function-over-method - public to(value: number): string { - return value.toString(); + public to(value: number): string | null { + if (value === null || value === undefined) { + return null; + } else { + return value.toString(); + } } // tslint:disable-next-line:prefer-function-over-method diff --git a/packages/pipeline/test/entities/copper_test.ts b/packages/pipeline/test/entities/copper_test.ts new file mode 100644 index 000000000..2543364e6 --- /dev/null +++ b/packages/pipeline/test/entities/copper_test.ts @@ -0,0 +1,54 @@ +import 'mocha'; +import 'reflect-metadata'; + +import { + CopperActivity, + CopperActivityType, + CopperCustomField, + CopperLead, + CopperOpportunity, +} from '../../src/entities'; +import { createDbConnectionOnceAsync } from '../db_setup'; +import { + ParsedActivities, + ParsedActivityTypes, + ParsedCustomFields, + ParsedLeads, + ParsedOpportunities, +} from '../fixtures/copper/parsed_entities'; +import { chaiSetup } from '../utils/chai_setup'; + +import { testSaveAndFindEntityAsync } from './util'; + +chaiSetup.configure(); + +describe('Copper entities', () => { + describe('save and find', async () => { + it('Copper lead', async () => { + const connection = await createDbConnectionOnceAsync(); + const repository = connection.getRepository(CopperLead); + ParsedLeads.forEach(async entity => testSaveAndFindEntityAsync(repository, entity)); + }); + it('Copper activity', async () => { + const connection = await createDbConnectionOnceAsync(); + const repository = connection.getRepository(CopperActivity); + ParsedActivities.forEach(async entity => testSaveAndFindEntityAsync(repository, entity)); + }); + // searching on jsonb fields is broken in typeorm + it.skip('Copper opportunity', async () => { + const connection = await createDbConnectionOnceAsync(); + const repository = connection.getRepository(CopperOpportunity); + ParsedOpportunities.forEach(async entity => testSaveAndFindEntityAsync(repository, entity)); + }); + it('Copper activity type', async () => { + const connection = await createDbConnectionOnceAsync(); + const repository = connection.getRepository(CopperActivityType); + ParsedActivityTypes.forEach(async entity => testSaveAndFindEntityAsync(repository, entity)); + }); + it('Copper custom field', async () => { + const connection = await createDbConnectionOnceAsync(); + const repository = connection.getRepository(CopperCustomField); + ParsedCustomFields.forEach(async entity => testSaveAndFindEntityAsync(repository, entity)); + }); + }); +}); diff --git a/packages/pipeline/test/entities/util.ts b/packages/pipeline/test/entities/util.ts index 043a3b15d..42df23a4a 100644 --- a/packages/pipeline/test/entities/util.ts +++ b/packages/pipeline/test/entities/util.ts @@ -15,9 +15,9 @@ const expect = chai.expect; * @param entity An instance of a TypeORM entity which will be saved/retrieved from the database. */ export async function testSaveAndFindEntityAsync<T>(repository: Repository<T>, entity: T): Promise<void> { - // Note(albrow): We are forced to use an 'as any' hack here because + // Note(albrow): We are forced to use an 'any' hack here because // TypeScript complains about stack depth when checking the types. - await repository.save(entity as any); + await repository.save<any>(entity); const gotEntity = await repository.findOneOrFail({ where: entity, }); diff --git a/packages/pipeline/test/fixtures/copper/api_v1_activity_types.json b/packages/pipeline/test/fixtures/copper/api_v1_activity_types.json new file mode 100644 index 000000000..dbd39c31b --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_activity_types.json @@ -0,0 +1,24 @@ +{ + "user": [ + { "id": 0, "category": "user", "name": "Note", "is_disabled": false, "count_as_interaction": false }, + { "id": 660496, "category": "user", "name": "To Do", "is_disabled": false, "count_as_interaction": false }, + { "id": 660495, "category": "user", "name": "Meeting", "is_disabled": false, "count_as_interaction": true }, + { "id": 660494, "category": "user", "name": "Phone Call", "is_disabled": false, "count_as_interaction": true } + ], + "system": [ + { + "id": 1, + "category": "system", + "name": "Property Changed", + "is_disabled": false, + "count_as_interaction": false + }, + { + "id": 3, + "category": "system", + "name": "Pipeline Stage Changed", + "is_disabled": false, + "count_as_interaction": false + } + ] +} diff --git a/packages/pipeline/test/fixtures/copper/api_v1_activity_types.ts b/packages/pipeline/test/fixtures/copper/api_v1_activity_types.ts new file mode 100644 index 000000000..fd2d62a6c --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_activity_types.ts @@ -0,0 +1,16 @@ +import { CopperActivityType } from '../../../src/entities'; +const ParsedActivityTypes: CopperActivityType[] = [ + { id: 0, name: 'Note', category: 'user', isDisabled: false, countAsInteraction: false }, + { id: 660496, name: 'To Do', category: 'user', isDisabled: false, countAsInteraction: false }, + { id: 660495, name: 'Meeting', category: 'user', isDisabled: false, countAsInteraction: true }, + { id: 660494, name: 'Phone Call', category: 'user', isDisabled: false, countAsInteraction: true }, + { id: 1, name: 'Property Changed', category: 'system', isDisabled: false, countAsInteraction: false }, + { + id: 3, + name: 'Pipeline Stage Changed', + category: 'system', + isDisabled: false, + countAsInteraction: false, + }, +]; +export { ParsedActivityTypes }; diff --git a/packages/pipeline/test/fixtures/copper/api_v1_custom_field_definitions.json b/packages/pipeline/test/fixtures/copper/api_v1_custom_field_definitions.json new file mode 100644 index 000000000..c6665cb0f --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_custom_field_definitions.json @@ -0,0 +1,38 @@ +[ + { + "id": 261066, + "name": "Integration Type", + "canonical_name": null, + "data_type": "MultiSelect", + "available_on": ["opportunity", "company", "person"], + "options": [ + { "id": 394020, "name": "Strategic Relationship", "rank": 7 }, + { "id": 394013, "name": "ERC-20 Exchange", "rank": 0 }, + { "id": 394014, "name": "ERC-721 Marketplace", "rank": 1 }, + { "id": 394015, "name": "Trade Widget", "rank": 2 }, + { "id": 394016, "name": "Prediction Market Exchange", "rank": 3 }, + { "id": 394017, "name": "Security Token Exchange", "rank": 4 }, + { "id": 394018, "name": "Complementary Company", "rank": 5 }, + { "id": 394019, "name": "Service Provider", "rank": 6 } + ] + }, + { + "id": 261067, + "name": "Company Type", + "canonical_name": null, + "data_type": "Dropdown", + "available_on": ["company", "opportunity", "person"], + "options": [ + { "id": 394129, "name": "Market Maker", "rank": 6 }, + { "id": 394130, "name": "Events", "rank": 2 }, + { "id": 394023, "name": "Exchange", "rank": 3 }, + { "id": 394024, "name": "Investor", "rank": 5 }, + { "id": 394026, "name": "Service Provider", "rank": 8 }, + { "id": 394027, "name": "Wallet", "rank": 9 }, + { "id": 394134, "name": "Game", "rank": 4 }, + { "id": 394025, "name": "OTC", "rank": 7 }, + { "id": 394021, "name": "Blockchain/Protocol", "rank": 0 }, + { "id": 394022, "name": "dApp", "rank": 1 } + ] + } +] diff --git a/packages/pipeline/test/fixtures/copper/api_v1_custom_field_definitions.ts b/packages/pipeline/test/fixtures/copper/api_v1_custom_field_definitions.ts new file mode 100644 index 000000000..a44bbd2c3 --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_custom_field_definitions.ts @@ -0,0 +1,39 @@ +import { CopperCustomField } from '../../../src/entities'; +const ParsedCustomFields: CopperCustomField[] = [ + { + id: 394020, + name: 'Strategic Relationship', + dataType: 'Integration Type', + fieldType: 'option', + }, + { id: 394013, name: 'ERC-20 Exchange', dataType: 'Integration Type', fieldType: 'option' }, + { id: 394014, name: 'ERC-721 Marketplace', dataType: 'Integration Type', fieldType: 'option' }, + { id: 394015, name: 'Trade Widget', dataType: 'Integration Type', fieldType: 'option' }, + { + id: 394016, + name: 'Prediction Market Exchange', + dataType: 'Integration Type', + fieldType: 'option', + }, + { + id: 394017, + name: 'Security Token Exchange', + dataType: 'Integration Type', + fieldType: 'option', + }, + { id: 394018, name: 'Complementary Company', dataType: 'Integration Type', fieldType: 'option' }, + { id: 394019, name: 'Service Provider', dataType: 'Integration Type', fieldType: 'option' }, + { id: 261066, name: 'Integration Type', dataType: 'MultiSelect' }, + { id: 394129, name: 'Market Maker', dataType: 'Company Type', fieldType: 'option' }, + { id: 394130, name: 'Events', dataType: 'Company Type', fieldType: 'option' }, + { id: 394023, name: 'Exchange', dataType: 'Company Type', fieldType: 'option' }, + { id: 394024, name: 'Investor', dataType: 'Company Type', fieldType: 'option' }, + { id: 394026, name: 'Service Provider', dataType: 'Company Type', fieldType: 'option' }, + { id: 394027, name: 'Wallet', dataType: 'Company Type', fieldType: 'option' }, + { id: 394134, name: 'Game', dataType: 'Company Type', fieldType: 'option' }, + { id: 394025, name: 'OTC', dataType: 'Company Type', fieldType: 'option' }, + { id: 394021, name: 'Blockchain/Protocol', dataType: 'Company Type', fieldType: 'option' }, + { id: 394022, name: 'dApp', dataType: 'Company Type', fieldType: 'option' }, + { id: 261067, name: 'Company Type', dataType: 'Dropdown' }, +]; +export { ParsedCustomFields }; diff --git a/packages/pipeline/test/fixtures/copper/api_v1_list_activities.json b/packages/pipeline/test/fixtures/copper/api_v1_list_activities.json new file mode 100644 index 000000000..a726111ac --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_list_activities.json @@ -0,0 +1,242 @@ +[ + { + "id": 5015299552, + "parent": { "id": 14667512, "type": "opportunity" }, + "type": { "id": 3, "category": "system", "name": "Stage Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1545329595, + "old_value": { "id": 2392929, "name": "Evaluation" }, + "new_value": { "id": 2392931, "name": "Integration Started" }, + "date_created": 1545329595, + "date_modified": 1545329595 + }, + { + "id": 5010214065, + "parent": { "id": 14978865, "type": "opportunity" }, + "type": { "id": 3, "category": "system", "name": "Stage Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1545245706, + "old_value": { "id": 2392928, "name": "Intro" }, + "new_value": { "id": 2392929, "name": "Evaluation" }, + "date_created": 1545245706, + "date_modified": 1545245706 + }, + { + "id": 5006149111, + "parent": { "id": 70430977, "type": "person" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1545166908, + "old_value": null, + "new_value": null, + "date_created": 1545168280, + "date_modified": 1545166908 + }, + { + "id": 5005314622, + "parent": { "id": 27778968, "type": "company" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1545080504, + "old_value": null, + "new_value": null, + "date_created": 1545160479, + "date_modified": 1545080504 + }, + { + "id": 5000006802, + "parent": { "id": 14956518, "type": "opportunity" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1545071374, + "old_value": null, + "new_value": null, + "date_created": 1545071500, + "date_modified": 1545071374 + }, + { + "id": 4985504199, + "parent": { "id": 14912790, "type": "opportunity" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544644058, + "old_value": null, + "new_value": null, + "date_created": 1544644661, + "date_modified": 1544644058 + }, + { + "id": 4985456147, + "parent": { "id": 14912790, "type": "opportunity" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544644048, + "old_value": null, + "new_value": null, + "date_created": 1544644053, + "date_modified": 1544644048 + }, + { + "id": 4980975996, + "parent": { "id": 14902828, "type": "opportunity" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544563171, + "old_value": null, + "new_value": null, + "date_created": 1544563224, + "date_modified": 1544563171 + }, + { + "id": 4980910331, + "parent": { "id": 14902828, "type": "opportunity" }, + "type": { "id": 3, "category": "system", "name": "Stage Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544562495, + "old_value": { "id": 2392928, "name": "Intro" }, + "new_value": { "id": 2392931, "name": "Integration Started" }, + "date_created": 1544562495, + "date_modified": 1544562495 + }, + { + "id": 4980872220, + "parent": { "id": 14888910, "type": "opportunity" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544559279, + "old_value": null, + "new_value": null, + "date_created": 1544562118, + "date_modified": 1544559279 + }, + { + "id": 4980508097, + "parent": { "id": 14050167, "type": "opportunity" }, + "type": { "id": 1, "category": "system", "name": "Status Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544558077, + "old_value": "Open", + "new_value": "Won", + "date_created": 1544558077, + "date_modified": 1544558077 + }, + { + "id": 4980508095, + "parent": { "id": 66538237, "type": "person" }, + "type": { "id": 1, "category": "system" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544558077, + "old_value": null, + "new_value": null, + "date_created": 1544558077, + "date_modified": 1544558077 + }, + { + "id": 4980508092, + "parent": { "id": 27779020, "type": "company" }, + "type": { "id": 1, "category": "system" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544558077, + "old_value": null, + "new_value": null, + "date_created": 1544558077, + "date_modified": 1544558077 + }, + { + "id": 4980507507, + "parent": { "id": 14050167, "type": "opportunity" }, + "type": { "id": 3, "category": "system", "name": "Stage Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544558071, + "old_value": { "id": 2392931, "name": "Integration Started" }, + "new_value": { "id": 2405442, "name": "Integration Complete" }, + "date_created": 1544558071, + "date_modified": 1544558071 + }, + { + "id": 4980479684, + "parent": { "id": 14901232, "type": "opportunity" }, + "type": { "id": 3, "category": "system", "name": "Stage Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544557777, + "old_value": { "id": 2392928, "name": "Intro" }, + "new_value": { "id": 2392929, "name": "Evaluation" }, + "date_created": 1544557777, + "date_modified": 1544557777 + }, + { + "id": 4980327164, + "parent": { "id": 14901232, "type": "opportunity" }, + "type": { "id": 660495, "category": "user" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544554864, + "old_value": null, + "new_value": null, + "date_created": 1544556132, + "date_modified": 1544554864 + }, + { + "id": 4975270470, + "parent": { "id": 14888744, "type": "opportunity" }, + "type": { "id": 3, "category": "system", "name": "Stage Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544469501, + "old_value": { "id": 2392928, "name": "Intro" }, + "new_value": { "id": 2392931, "name": "Integration Started" }, + "date_created": 1544469501, + "date_modified": 1544469501 + }, + { + "id": 4975255523, + "parent": { "id": 64713448, "type": "person" }, + "type": { "id": 1, "category": "system" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544469389, + "old_value": null, + "new_value": null, + "date_created": 1544469389, + "date_modified": 1544469389 + }, + { + "id": 4975255519, + "parent": { "id": 13735617, "type": "opportunity" }, + "type": { "id": 1, "category": "system", "name": "Status Change" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544469388, + "old_value": "Open", + "new_value": "Won", + "date_created": 1544469388, + "date_modified": 1544469388 + }, + { + "id": 4975255514, + "parent": { "id": 27778968, "type": "company" }, + "type": { "id": 1, "category": "system" }, + "user_id": 680302, + "details": "blah blah", + "activity_date": 1544469388, + "old_value": null, + "new_value": null, + "date_created": 1544469388, + "date_modified": 1544469388 + } +] diff --git a/packages/pipeline/test/fixtures/copper/api_v1_list_activities.ts b/packages/pipeline/test/fixtures/copper/api_v1_list_activities.ts new file mode 100644 index 000000000..51ee9ced3 --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_list_activities.ts @@ -0,0 +1,305 @@ +import { CopperActivity } from '../../../src/entities'; + +const ParsedActivities: CopperActivity[] = [ + { + id: 5015299552, + parentId: 14667512, + parentType: 'opportunity', + typeId: 3, + typeCategory: 'system', + typeName: 'Stage Change', + userId: 680302, + dateCreated: 1545329595000, + dateModified: 1545329595000, + oldValueId: 2392929, + oldValueName: 'Evaluation', + newValueId: 2392931, + newValueName: 'Integration Started', + }, + { + id: 5010214065, + parentId: 14978865, + parentType: 'opportunity', + typeId: 3, + typeCategory: 'system', + typeName: 'Stage Change', + userId: 680302, + dateCreated: 1545245706000, + dateModified: 1545245706000, + oldValueId: 2392928, + oldValueName: 'Intro', + newValueId: 2392929, + newValueName: 'Evaluation', + }, + { + id: 5006149111, + parentId: 70430977, + parentType: 'person', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1545168280000, + dateModified: 1545166908000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 5005314622, + parentId: 27778968, + parentType: 'company', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1545160479000, + dateModified: 1545080504000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 5000006802, + parentId: 14956518, + parentType: 'opportunity', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1545071500000, + dateModified: 1545071374000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4985504199, + parentId: 14912790, + parentType: 'opportunity', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1544644661000, + dateModified: 1544644058000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4985456147, + parentId: 14912790, + parentType: 'opportunity', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1544644053000, + dateModified: 1544644048000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4980975996, + parentId: 14902828, + parentType: 'opportunity', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1544563224000, + dateModified: 1544563171000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4980910331, + parentId: 14902828, + parentType: 'opportunity', + typeId: 3, + typeCategory: 'system', + typeName: 'Stage Change', + userId: 680302, + dateCreated: 1544562495000, + dateModified: 1544562495000, + oldValueId: 2392928, + oldValueName: 'Intro', + newValueId: 2392931, + newValueName: 'Integration Started', + }, + { + id: 4980872220, + parentId: 14888910, + parentType: 'opportunity', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1544562118000, + dateModified: 1544559279000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4980508097, + parentId: 14050167, + parentType: 'opportunity', + typeId: 1, + typeCategory: 'system', + typeName: 'Status Change', + userId: 680302, + dateCreated: 1544558077000, + dateModified: 1544558077000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4980508095, + parentId: 66538237, + parentType: 'person', + typeId: 1, + typeCategory: 'system', + typeName: undefined, + userId: 680302, + dateCreated: 1544558077000, + dateModified: 1544558077000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4980508092, + parentId: 27779020, + parentType: 'company', + typeId: 1, + typeCategory: 'system', + typeName: undefined, + userId: 680302, + dateCreated: 1544558077000, + dateModified: 1544558077000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4980507507, + parentId: 14050167, + parentType: 'opportunity', + typeId: 3, + typeCategory: 'system', + typeName: 'Stage Change', + userId: 680302, + dateCreated: 1544558071000, + dateModified: 1544558071000, + oldValueId: 2392931, + oldValueName: 'Integration Started', + newValueId: 2405442, + newValueName: 'Integration Complete', + }, + { + id: 4980479684, + parentId: 14901232, + parentType: 'opportunity', + typeId: 3, + typeCategory: 'system', + typeName: 'Stage Change', + userId: 680302, + dateCreated: 1544557777000, + dateModified: 1544557777000, + oldValueId: 2392928, + oldValueName: 'Intro', + newValueId: 2392929, + newValueName: 'Evaluation', + }, + { + id: 4980327164, + parentId: 14901232, + parentType: 'opportunity', + typeId: 660495, + typeCategory: 'user', + typeName: undefined, + userId: 680302, + dateCreated: 1544556132000, + dateModified: 1544554864000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4975270470, + parentId: 14888744, + parentType: 'opportunity', + typeId: 3, + typeCategory: 'system', + typeName: 'Stage Change', + userId: 680302, + dateCreated: 1544469501000, + dateModified: 1544469501000, + oldValueId: 2392928, + oldValueName: 'Intro', + newValueId: 2392931, + newValueName: 'Integration Started', + }, + { + id: 4975255523, + parentId: 64713448, + parentType: 'person', + typeId: 1, + typeCategory: 'system', + typeName: undefined, + userId: 680302, + dateCreated: 1544469389000, + dateModified: 1544469389000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4975255519, + parentId: 13735617, + parentType: 'opportunity', + typeId: 1, + typeCategory: 'system', + typeName: 'Status Change', + userId: 680302, + dateCreated: 1544469388000, + dateModified: 1544469388000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, + { + id: 4975255514, + parentId: 27778968, + parentType: 'company', + typeId: 1, + typeCategory: 'system', + typeName: undefined, + userId: 680302, + dateCreated: 1544469388000, + dateModified: 1544469388000, + oldValueId: undefined, + oldValueName: undefined, + newValueId: undefined, + newValueName: undefined, + }, +]; +export { ParsedActivities }; diff --git a/packages/pipeline/test/fixtures/copper/api_v1_list_leads.json b/packages/pipeline/test/fixtures/copper/api_v1_list_leads.json new file mode 100644 index 000000000..5223976f9 --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_list_leads.json @@ -0,0 +1,583 @@ +[ + { + "id": 9150547, + "name": "My Contact", + "prefix": null, + "first_name": "My", + "last_name": "Contact", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mycontact@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": null + }, + { + "custom_field_definition_id": 103481, + "value": null + } + ], + "date_created": 1490045162, + "date_modified": 1490045162 + }, + { + "id": 9150552, + "name": "My Contact", + "prefix": null, + "first_name": "My", + "last_name": "Contact", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": null, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [ + { + "number": "415-123-45678", + "category": "mobile" + } + ], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": null + }, + { + "custom_field_definition_id": 103481, + "value": null + } + ], + "date_created": 1490045237, + "date_modified": 1490045237 + }, + { + "id": 9150578, + "name": "My Contact", + "prefix": null, + "first_name": "My", + "last_name": "Contact", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": null, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [ + { + "number": "415-123-45678", + "category": "mobile" + } + ], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": null + }, + { + "custom_field_definition_id": 103481, + "value": null + } + ], + "date_created": 1490045279, + "date_modified": 1490045279 + }, + { + "id": 8982554, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": null + }, + { + "custom_field_definition_id": 103481, + "value": null + } + ], + "date_created": 1489528899, + "date_modified": 1489528899 + }, + { + "id": 8982702, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@gmail.test", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": null + }, + { + "custom_field_definition_id": 103481, + "value": null + } + ], + "date_created": 1489531171, + "date_modified": 1489531171 + }, + { + "id": 9094361, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": null + }, + { + "custom_field_definition_id": 103481, + "value": null + } + ], + "date_created": 1489791225, + "date_modified": 1489791225 + }, + { + "id": 9094364, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": "123456789012345678901234567890" + }, + { + "custom_field_definition_id": 103481, + "value": "123456789012345678901234567890" + } + ], + "date_created": 1489791283, + "date_modified": 1489791283 + }, + { + "id": 9094371, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": + "|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------" + }, + { + "custom_field_definition_id": 103481, + "value": "123456789012345678901234567890" + } + ], + "date_created": 1489791417, + "date_modified": 1489791417 + }, + { + "id": 9094372, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": + "|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5-----" + }, + { + "custom_field_definition_id": 103481, + "value": "123456789012345678901234567890" + } + ], + "date_created": 1489791453, + "date_modified": 1489791453 + }, + { + "id": 9094373, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": + "|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5-----" + }, + { + "custom_field_definition_id": 103481, + "value": + "|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------" + } + ], + "date_created": 1489791470, + "date_modified": 1489791470 + }, + { + "id": 9094383, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": + "|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5-----" + }, + { + "custom_field_definition_id": 103481, + "value": + "|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------|--------1---------2---------3---------4---------5---------6---------7---------8---------9---------" + } + ], + "date_created": 1489791672, + "date_modified": 1489791672 + }, + { + "id": 9174441, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": "Text fields are 255 chars or less!" + }, + { + "custom_field_definition_id": 103481, + "value": "text \n text" + } + ], + "date_created": 1490112942, + "date_modified": 1490112942 + }, + { + "id": 9174443, + "name": "My Lead", + "prefix": null, + "first_name": "My", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": null, + "assignee_id": null, + "company_name": null, + "customer_source_id": null, + "details": null, + "email": { + "email": "mylead@noemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": null, + "socials": [], + "status": "New", + "status_id": 208231, + "tags": [], + "title": null, + "websites": [], + "phone_numbers": [], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": "Text fields are 255 chars or less!" + }, + { + "custom_field_definition_id": 103481, + "value": "text /n text" + } + ], + "date_created": 1490112953, + "date_modified": 1490112953 + }, + { + "id": 8894157, + "name": "Test Lead", + "prefix": null, + "first_name": "Test", + "last_name": "Lead", + "middle_name": null, + "suffix": null, + "address": { + "street": "301 Howard St Ste 600", + "city": "San Francisco", + "state": "CA", + "postal_code": "94105", + "country": "US" + }, + "assignee_id": 137658, + "company_name": "Lead's Company", + "customer_source_id": 331241, + "details": "This is an update", + "email": { + "email": "address@workemail.com", + "category": "work" + }, + "interaction_count": 0, + "monetary_value": 100, + "socials": [ + { + "url": "facebook.com/test_lead", + "category": "facebook" + } + ], + "status": "New", + "status_id": 208231, + "tags": ["tag 1", "tag 2"], + "title": "Title", + "websites": [ + { + "url": "www.workwebsite.com", + "category": "work" + } + ], + "phone_numbers": [ + { + "number": "415-999-4321", + "category": "mobile" + }, + { + "number": "415-555-1234", + "category": "work" + } + ], + "custom_fields": [ + { + "custom_field_definition_id": 100764, + "value": null + }, + { + "custom_field_definition_id": 103481, + "value": null + } + ], + "date_created": 1489018784, + "date_modified": 1496692911 + } +] diff --git a/packages/pipeline/test/fixtures/copper/api_v1_list_leads.ts b/packages/pipeline/test/fixtures/copper/api_v1_list_leads.ts new file mode 100644 index 000000000..b1f00cba7 --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_list_leads.ts @@ -0,0 +1,229 @@ +import { CopperLead } from '../../../src/entities'; +const ParsedLeads: CopperLead[] = [ + { + id: 9150547, + name: 'My Contact', + firstName: 'My', + lastName: 'Contact', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1490045162000, + dateModified: 1490045162000, + }, + { + id: 9150552, + name: 'My Contact', + firstName: 'My', + lastName: 'Contact', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1490045237000, + dateModified: 1490045237000, + }, + { + id: 9150578, + name: 'My Contact', + firstName: 'My', + lastName: 'Contact', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1490045279000, + dateModified: 1490045279000, + }, + { + id: 8982554, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489528899000, + dateModified: 1489528899000, + }, + { + id: 8982702, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489531171000, + dateModified: 1489531171000, + }, + { + id: 9094361, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489791225000, + dateModified: 1489791225000, + }, + { + id: 9094364, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489791283000, + dateModified: 1489791283000, + }, + { + id: 9094371, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489791417000, + dateModified: 1489791417000, + }, + { + id: 9094372, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489791453000, + dateModified: 1489791453000, + }, + { + id: 9094373, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489791470000, + dateModified: 1489791470000, + }, + { + id: 9094383, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1489791672000, + dateModified: 1489791672000, + }, + { + id: 9174441, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1490112942000, + dateModified: 1490112942000, + }, + { + id: 9174443, + name: 'My Lead', + firstName: 'My', + lastName: 'Lead', + middleName: undefined, + assigneeId: undefined, + companyName: undefined, + customerSourceId: undefined, + monetaryValue: undefined, + status: 'New', + statusId: 208231, + title: undefined, + dateCreated: 1490112953000, + dateModified: 1490112953000, + }, + { + id: 8894157, + name: 'Test Lead', + firstName: 'Test', + lastName: 'Lead', + middleName: undefined, + assigneeId: 137658, + companyName: "Lead's Company", + customerSourceId: 331241, + monetaryValue: 100, + status: 'New', + statusId: 208231, + title: 'Title', + dateCreated: 1489018784000, + dateModified: 1496692911000, + }, +]; + +export { ParsedLeads }; diff --git a/packages/pipeline/test/fixtures/copper/api_v1_list_opportunities.json b/packages/pipeline/test/fixtures/copper/api_v1_list_opportunities.json new file mode 100644 index 000000000..34ac58c30 --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_list_opportunities.json @@ -0,0 +1,662 @@ +[ + { + "id": 14050269, + "name": "8Base RaaS", + "assignee_id": 680302, + "close_date": "11/19/2018", + "company_id": 27778962, + "company_name": "8base", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405442, + "primary_contact_id": 66088850, + "priority": "None", + "status": "Won", + "tags": [], + "interaction_count": 81, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542653860, + "date_last_contacted": 1544757550, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1538414159, + "date_modified": 1544769562, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013, 394018] }, + { "custom_field_definition_id": 261067, "value": 394026 } + ] + }, + { + "id": 14631430, + "name": "Alice.si TW + ERC 20 Marketplace", + "assignee_id": 680302, + "close_date": "12/15/2018", + "company_id": 30238847, + "company_name": "Alice SI", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 69354024, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 4, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542304481, + "date_last_contacted": 1542304800, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542304481, + "date_modified": 1542304943, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013, 394015] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14632057, + "name": "Altcoin.io Relayer", + "assignee_id": 680302, + "close_date": "12/15/2018", + "company_id": 29936486, + "company_name": "Altcoin.io", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 68724646, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 22, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542310909, + "date_last_contacted": 1543864597, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542306827, + "date_modified": 1543864667, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013, 394017] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14667523, + "name": "Altcoin.io Relayer", + "assignee_id": 680302, + "close_date": "12/19/2018", + "company_id": 29936486, + "company_name": "Altcoin.io", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 68724646, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 21, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542657437, + "date_last_contacted": 1543864597, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542657437, + "date_modified": 1543864667, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013, 394017] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14666706, + "name": "Amadeus Relayer", + "assignee_id": 680302, + "close_date": "11/19/2018", + "company_id": 29243209, + "company_name": "Amadeus", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405442, + "primary_contact_id": 66912020, + "priority": "None", + "status": "Won", + "tags": [], + "interaction_count": 11, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542654284, + "date_last_contacted": 1543264254, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542654284, + "date_modified": 1543277520, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14666718, + "name": "Ambo Relayer", + "assignee_id": 680302, + "close_date": "11/19/2018", + "company_id": 29249190, + "company_name": "Ambo", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405442, + "primary_contact_id": 66927869, + "priority": "None", + "status": "Won", + "tags": [], + "interaction_count": 126, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542654352, + "date_last_contacted": 1545252349, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542654352, + "date_modified": 1545253761, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14164318, + "name": "Augur TW", + "assignee_id": 680302, + "close_date": "12/10/2018", + "company_id": 27778967, + "company_name": "Augur", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405442, + "primary_contact_id": 67248692, + "priority": "None", + "status": "Won", + "tags": [], + "interaction_count": 22, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1544469362, + "date_last_contacted": 1544491567, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1539204858, + "date_modified": 1544653867, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394015] }, + { "custom_field_definition_id": 261067, "value": 394021 } + ] + }, + { + "id": 14666626, + "name": "Autonio", + "assignee_id": 680302, + "close_date": "12/19/2018", + "company_id": 27920701, + "company_name": "Auton", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392931, + "primary_contact_id": 64742640, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 54, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542653834, + "date_last_contacted": 1542658568, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542653834, + "date_modified": 1542658808, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013, 394019] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14050921, + "name": "Axie Infinity 721 Marketplace", + "assignee_id": 680302, + "close_date": "11/1/2018", + "company_id": 27779033, + "company_name": "Axie Infinity", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392931, + "primary_contact_id": 66499254, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 4, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1543861025, + "date_last_contacted": 1539024738, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1538416687, + "date_modified": 1543861025, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394014] }, + { "custom_field_definition_id": 261067, "value": 394134 } + ] + }, + { + "id": 13735617, + "name": "Balance TW", + "assignee_id": 680302, + "close_date": "12/10/2018", + "company_id": 27778968, + "company_name": "Balance", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405442, + "primary_contact_id": 64713448, + "priority": "None", + "status": "Won", + "tags": [], + "interaction_count": 34, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1544469382, + "date_last_contacted": 1545082200, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1535668009, + "date_modified": 1545082454, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394015] }, + { "custom_field_definition_id": 261067, "value": 394027 } + ] + }, + { + "id": 14667112, + "name": "Bamboo Relayer", + "assignee_id": 680302, + "close_date": "11/19/2018", + "company_id": 29243795, + "company_name": "Bamboo Relay", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405442, + "primary_contact_id": 66914687, + "priority": "None", + "status": "Won", + "tags": [], + "interaction_count": 46, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542655143, + "date_last_contacted": 1545252349, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542655143, + "date_modified": 1545253761, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 13627309, + "name": "Ben TW", + "assignee_id": 680302, + "close_date": "1/1/2019", + "company_id": 27702348, + "company_name": "Ben", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 64262622, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 64, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1541527279, + "date_last_contacted": 1541639882, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1534887789, + "date_modified": 1541651395, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394015] }, + { "custom_field_definition_id": 261067, "value": 394027 } + ] + }, + { + "id": 14808512, + "name": "Bit2Me Relayer", + "assignee_id": 680302, + "close_date": "12/3/2018", + "company_id": 30793050, + "company_name": "Bit2Me", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405442, + "primary_contact_id": 70267217, + "priority": "None", + "status": "Won", + "tags": [], + "interaction_count": 0, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1543861167, + "date_last_contacted": null, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1543861167, + "date_modified": 1543861189, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394013] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14050312, + "name": "Bitcoin.tax Reporting Integration", + "assignee_id": 680302, + "close_date": "11/1/2018", + "company_id": 27957614, + "company_name": "Bitcoin", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392928, + "primary_contact_id": 66539479, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 5, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1538414308, + "date_last_contacted": 1536766098, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1538414308, + "date_modified": 1538414314, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394019] }, + { "custom_field_definition_id": 261067, "value": 394026 } + ] + }, + { + "id": 14331463, + "name": "Bitpie TW", + "assignee_id": 680302, + "close_date": "11/19/2018", + "company_id": 27779026, + "company_name": "Bitpie", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 67700943, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 9, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1539984566, + "date_last_contacted": 1541529947, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1539984566, + "date_modified": 1541530233, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394015] }, + { "custom_field_definition_id": 261067, "value": 394027 } + ] + }, + { + "id": 14331481, + "name": "Bitski Wallet SDK TW", + "assignee_id": 680302, + "close_date": "11/19/2018", + "company_id": 29489300, + "company_name": "Bitski", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 67697528, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 23, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1539984735, + "date_last_contacted": 1544811399, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1539984735, + "date_modified": 1544818605, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394015] }, + { "custom_field_definition_id": 261067, "value": 394026 } + ] + }, + { + "id": 14531554, + "name": "BitUniverse TW", + "assignee_id": 680302, + "close_date": "12/6/2018", + "company_id": 29901805, + "company_name": "BitUniverse Co., Ltd (Cryptocurrency Portfolio)", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 68692107, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 15, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1543861104, + "date_last_contacted": 1544803276, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1541527110, + "date_modified": 1544812979, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394015] }, + { "custom_field_definition_id": 261067, "value": 394026 } + ] + }, + { + "id": 14050895, + "name": "BlitzPredict PMR", + "assignee_id": 680302, + "close_date": "11/1/2018", + "company_id": 28758258, + "company_name": "BlitzPredict", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 66378659, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 32, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1539985501, + "date_last_contacted": 1544830560, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1538416597, + "date_modified": 1544830709, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394016] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + }, + { + "id": 14209841, + "name": "Blockfolio TW", + "assignee_id": 680302, + "close_date": "11/15/2018", + "company_id": 29332516, + "company_name": "Blockfolio", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2405443, + "primary_contact_id": 67247027, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 20, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1539984098, + "date_last_contacted": 1539977661, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1539624801, + "date_modified": 1539984098, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394015] }, + { "custom_field_definition_id": 261067, "value": 394026 } + ] + }, + { + "id": 14633220, + "name": "BlockSwap 721 / 1155 Conversational Marketplace", + "assignee_id": 680302, + "close_date": "12/15/2018", + "company_id": 30210921, + "company_name": "BlockSwap", + "customer_source_id": null, + "details": "blah blah", + "loss_reason_id": null, + "pipeline_id": 512676, + "pipeline_stage_id": 2392929, + "primary_contact_id": 69296220, + "priority": "None", + "status": "Open", + "tags": [], + "interaction_count": 82, + "monetary_unit": null, + "monetary_value": null, + "converted_unit": null, + "converted_value": null, + "win_probability": 0, + "date_stage_changed": 1542311056, + "date_last_contacted": 1543536442, + "leads_converted_from": [], + "date_lead_created": null, + "date_created": 1542311056, + "date_modified": 1543557877, + "custom_fields": [ + { "custom_field_definition_id": 261066, "value": [394014] }, + { "custom_field_definition_id": 261067, "value": 394023 } + ] + } +] diff --git a/packages/pipeline/test/fixtures/copper/api_v1_list_opportunities.ts b/packages/pipeline/test/fixtures/copper/api_v1_list_opportunities.ts new file mode 100644 index 000000000..3c2d4ae5e --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/api_v1_list_opportunities.ts @@ -0,0 +1,425 @@ +// tslint:disable:custom-no-magic-numbers +import { CopperOpportunity } from '../../../src/entities'; +const ParsedOpportunities: CopperOpportunity[] = [ + { + id: 14050269, + name: '8Base RaaS', + assigneeId: 680302, + closeDate: '11/19/2018', + companyId: 27778962, + companyName: '8base', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405442, + primaryContactId: 66088850, + priority: 'None', + status: 'Won', + interactionCount: 81, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1538414159000, + dateModified: 1544769562000, + customFields: { '261066': 394018, '261067': 394026 }, + }, + { + id: 14631430, + name: 'Alice.si TW + ERC 20 Marketplace', + assigneeId: 680302, + closeDate: '12/15/2018', + companyId: 30238847, + companyName: 'Alice SI', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 69354024, + priority: 'None', + status: 'Open', + interactionCount: 4, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542304481000, + dateModified: 1542304943000, + customFields: { '261066': 394015, '261067': 394023 }, + }, + { + id: 14632057, + name: 'Altcoin.io Relayer', + assigneeId: 680302, + closeDate: '12/15/2018', + companyId: 29936486, + companyName: 'Altcoin.io', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 68724646, + priority: 'None', + status: 'Open', + interactionCount: 22, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542306827000, + dateModified: 1543864667000, + customFields: { '261066': 394017, '261067': 394023 }, + }, + { + id: 14667523, + name: 'Altcoin.io Relayer', + assigneeId: 680302, + closeDate: '12/19/2018', + companyId: 29936486, + companyName: 'Altcoin.io', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 68724646, + priority: 'None', + status: 'Open', + interactionCount: 21, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542657437000, + dateModified: 1543864667000, + customFields: { '261066': 394017, '261067': 394023 }, + }, + { + id: 14666706, + name: 'Amadeus Relayer', + assigneeId: 680302, + closeDate: '11/19/2018', + companyId: 29243209, + companyName: 'Amadeus', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405442, + primaryContactId: 66912020, + priority: 'None', + status: 'Won', + interactionCount: 11, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542654284000, + dateModified: 1543277520000, + customFields: { '261066': 394013, '261067': 394023 }, + }, + { + id: 14666718, + name: 'Ambo Relayer', + assigneeId: 680302, + closeDate: '11/19/2018', + companyId: 29249190, + companyName: 'Ambo', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405442, + primaryContactId: 66927869, + priority: 'None', + status: 'Won', + interactionCount: 126, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542654352000, + dateModified: 1545253761000, + customFields: { '261066': 394013, '261067': 394023 }, + }, + { + id: 14164318, + name: 'Augur TW', + assigneeId: 680302, + closeDate: '12/10/2018', + companyId: 27778967, + companyName: 'Augur', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405442, + primaryContactId: 67248692, + priority: 'None', + status: 'Won', + interactionCount: 22, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1539204858000, + dateModified: 1544653867000, + customFields: { '261066': 394015, '261067': 394021 }, + }, + { + id: 14666626, + name: 'Autonio', + assigneeId: 680302, + closeDate: '12/19/2018', + companyId: 27920701, + companyName: 'Auton', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392931, + primaryContactId: 64742640, + priority: 'None', + status: 'Open', + interactionCount: 54, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542653834000, + dateModified: 1542658808000, + customFields: { '261066': 394019, '261067': 394023 }, + }, + { + id: 14050921, + name: 'Axie Infinity 721 Marketplace', + assigneeId: 680302, + closeDate: '11/1/2018', + companyId: 27779033, + companyName: 'Axie Infinity', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392931, + primaryContactId: 66499254, + priority: 'None', + status: 'Open', + interactionCount: 4, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1538416687000, + dateModified: 1543861025000, + customFields: { '261066': 394014, '261067': 394134 }, + }, + { + id: 13735617, + name: 'Balance TW', + assigneeId: 680302, + closeDate: '12/10/2018', + companyId: 27778968, + companyName: 'Balance', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405442, + primaryContactId: 64713448, + priority: 'None', + status: 'Won', + interactionCount: 34, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1535668009000, + dateModified: 1545082454000, + customFields: { '261066': 394015, '261067': 394027 }, + }, + { + id: 14667112, + name: 'Bamboo Relayer', + assigneeId: 680302, + closeDate: '11/19/2018', + companyId: 29243795, + companyName: 'Bamboo Relay', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405442, + primaryContactId: 66914687, + priority: 'None', + status: 'Won', + interactionCount: 46, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542655143000, + dateModified: 1545253761000, + customFields: { '261066': 394013, '261067': 394023 }, + }, + { + id: 13627309, + name: 'Ben TW', + assigneeId: 680302, + closeDate: '1/1/2019', + companyId: 27702348, + companyName: 'Ben', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 64262622, + priority: 'None', + status: 'Open', + interactionCount: 64, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1534887789000, + dateModified: 1541651395000, + customFields: { '261066': 394015, '261067': 394027 }, + }, + { + id: 14808512, + name: 'Bit2Me Relayer', + assigneeId: 680302, + closeDate: '12/3/2018', + companyId: 30793050, + companyName: 'Bit2Me', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405442, + primaryContactId: 70267217, + priority: 'None', + status: 'Won', + interactionCount: 0, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1543861167000, + dateModified: 1543861189000, + customFields: { '261066': 394013, '261067': 394023 }, + }, + { + id: 14050312, + name: 'Bitcoin.tax Reporting Integration', + assigneeId: 680302, + closeDate: '11/1/2018', + companyId: 27957614, + companyName: 'Bitcoin', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392928, + primaryContactId: 66539479, + priority: 'None', + status: 'Open', + interactionCount: 5, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1538414308000, + dateModified: 1538414314000, + customFields: { '261066': 394019, '261067': 394026 }, + }, + { + id: 14331463, + name: 'Bitpie TW', + assigneeId: 680302, + closeDate: '11/19/2018', + companyId: 27779026, + companyName: 'Bitpie', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 67700943, + priority: 'None', + status: 'Open', + interactionCount: 9, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1539984566000, + dateModified: 1541530233000, + customFields: { '261066': 394015, '261067': 394027 }, + }, + { + id: 14331481, + name: 'Bitski Wallet SDK TW', + assigneeId: 680302, + closeDate: '11/19/2018', + companyId: 29489300, + companyName: 'Bitski', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 67697528, + priority: 'None', + status: 'Open', + interactionCount: 23, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1539984735000, + dateModified: 1544818605000, + customFields: { '261066': 394015, '261067': 394026 }, + }, + { + id: 14531554, + name: 'BitUniverse TW', + assigneeId: 680302, + closeDate: '12/6/2018', + companyId: 29901805, + companyName: 'BitUniverse Co., Ltd (Cryptocurrency Portfolio)', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 68692107, + priority: 'None', + status: 'Open', + interactionCount: 15, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1541527110000, + dateModified: 1544812979000, + customFields: { '261066': 394015, '261067': 394026 }, + }, + { + id: 14050895, + name: 'BlitzPredict PMR', + assigneeId: 680302, + closeDate: '11/1/2018', + companyId: 28758258, + companyName: 'BlitzPredict', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 66378659, + priority: 'None', + status: 'Open', + interactionCount: 32, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1538416597000, + dateModified: 1544830709000, + customFields: { '261066': 394016, '261067': 394023 }, + }, + { + id: 14209841, + name: 'Blockfolio TW', + assigneeId: 680302, + closeDate: '11/15/2018', + companyId: 29332516, + companyName: 'Blockfolio', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2405443, + primaryContactId: 67247027, + priority: 'None', + status: 'Open', + interactionCount: 20, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1539624801000, + dateModified: 1539984098000, + customFields: { '261066': 394015, '261067': 394026 }, + }, + { + id: 14633220, + name: 'BlockSwap 721 / 1155 Conversational Marketplace', + assigneeId: 680302, + closeDate: '12/15/2018', + companyId: 30210921, + companyName: 'BlockSwap', + customerSourceId: undefined, + lossReasonId: undefined, + pipelineId: 512676, + pipelineStageId: 2392929, + primaryContactId: 69296220, + priority: 'None', + status: 'Open', + interactionCount: 82, + monetaryValue: undefined, + winProbability: 0, + dateCreated: 1542311056000, + dateModified: 1543557877000, + customFields: { '261066': 394014, '261067': 394023 }, + }, +]; +export { ParsedOpportunities }; diff --git a/packages/pipeline/test/fixtures/copper/parsed_entities.ts b/packages/pipeline/test/fixtures/copper/parsed_entities.ts new file mode 100644 index 000000000..1f49d38ed --- /dev/null +++ b/packages/pipeline/test/fixtures/copper/parsed_entities.ts @@ -0,0 +1,5 @@ +export { ParsedActivityTypes } from './api_v1_activity_types'; +export { ParsedCustomFields } from './api_v1_custom_field_definitions'; +export { ParsedActivities } from './api_v1_list_activities'; +export { ParsedLeads } from './api_v1_list_leads'; +export { ParsedOpportunities } from './api_v1_list_opportunities'; diff --git a/packages/pipeline/test/parsers/copper/index_test.ts b/packages/pipeline/test/parsers/copper/index_test.ts new file mode 100644 index 000000000..bb8e70da1 --- /dev/null +++ b/packages/pipeline/test/parsers/copper/index_test.ts @@ -0,0 +1,87 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { + CopperActivity, + CopperActivityType, + CopperCustomField, + CopperLead, + CopperOpportunity, +} from '../../../src/entities'; +import { + CopperActivityResponse, + CopperActivityTypeCategory, + CopperActivityTypeResponse, + CopperCustomFieldResponse, + CopperSearchResponse, + parseActivities, + parseActivityTypes, + parseCustomFields, + parseLeads, + parseOpportunities, +} from '../../../src/parsers/copper'; +import { chaiSetup } from '../../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +type CopperResponse = CopperSearchResponse | CopperCustomFieldResponse; +type CopperEntity = CopperLead | CopperActivity | CopperOpportunity | CopperActivityType | CopperCustomField; + +import * as activityTypesApiResponse from '../../fixtures/copper/api_v1_activity_types.json'; +import * as customFieldsApiResponse from '../../fixtures/copper/api_v1_custom_field_definitions.json'; +import * as listActivitiesApiResponse from '../../fixtures/copper/api_v1_list_activities.json'; +import * as listLeadsApiResponse from '../../fixtures/copper/api_v1_list_leads.json'; +import * as listOpportunitiesApiResponse from '../../fixtures/copper/api_v1_list_opportunities.json'; +import { + ParsedActivities, + ParsedActivityTypes, + ParsedCustomFields, + ParsedLeads, + ParsedOpportunities, +} from '../../fixtures/copper/parsed_entities'; + +interface TestCase { + input: CopperResponse[]; + expected: CopperEntity[]; + parseFn(input: CopperResponse[]): CopperEntity[]; +} +const testCases: TestCase[] = [ + { + input: listLeadsApiResponse, + expected: ParsedLeads, + parseFn: parseLeads, + }, + { + input: (listActivitiesApiResponse as unknown) as CopperActivityResponse[], + expected: ParsedActivities, + parseFn: parseActivities, + }, + { + input: listOpportunitiesApiResponse, + expected: ParsedOpportunities, + parseFn: parseOpportunities, + }, + { + input: customFieldsApiResponse, + expected: ParsedCustomFields, + parseFn: parseCustomFields, + }, +]; +describe('Copper parser', () => { + it('parses API responses', () => { + testCases.forEach(testCase => { + const actual: CopperEntity[] = testCase.parseFn(testCase.input); + expect(actual).deep.equal(testCase.expected); + }); + }); + + // special case because the API response is not an array + it('parses activity types API response', () => { + const actual: CopperActivityType[] = parseActivityTypes((activityTypesApiResponse as unknown) as Map< + CopperActivityTypeCategory, + CopperActivityTypeResponse[] + >); + expect(actual).deep.equal(ParsedActivityTypes); + }); +}); diff --git a/packages/pipeline/test/parsers/ddex_orders/index_test.ts b/packages/pipeline/test/parsers/ddex_orders/index_test.ts index 4a4a86bf8..f30e86b02 100644 --- a/packages/pipeline/test/parsers/ddex_orders/index_test.ts +++ b/packages/pipeline/test/parsers/ddex_orders/index_test.ts @@ -31,13 +31,13 @@ describe('ddex_orders', () => { amountDecimals: 0, }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'ddex'; const expected = new TokenOrder(); expected.source = 'ddex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.5); // ddex currently confuses base and quote assets. // Switch them to maintain our internal consistency. diff --git a/packages/pipeline/test/parsers/events/exchange_events_test.ts b/packages/pipeline/test/parsers/events/exchange_events_test.ts index 5d4b185a5..956ad9ef8 100644 --- a/packages/pipeline/test/parsers/events/exchange_events_test.ts +++ b/packages/pipeline/test/parsers/events/exchange_events_test.ts @@ -6,6 +6,7 @@ import 'mocha'; import { ExchangeFillEvent } from '../../../src/entities'; import { _convertToExchangeFillEvent } from '../../../src/parsers/events/exchange_events'; +import { AssetType } from '../../../src/types'; import { chaiSetup } from '../../utils/chai_setup'; chaiSetup.configure(); @@ -62,12 +63,12 @@ describe('exchange_events', () => { expected.takerFeePaid = new BigNumber('12345'); expected.orderHash = '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a'; expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerAssetType = 'erc20'; + expected.makerAssetType = AssetType.ERC20; expected.makerAssetProxyId = '0xf47261b0'; expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; expected.makerTokenId = null; expected.rawTakerAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - expected.takerAssetType = 'erc20'; + expected.takerAssetType = AssetType.ERC20; expected.takerAssetProxyId = '0xf47261b0'; expected.takerTokenAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; expected.takerTokenId = null; diff --git a/packages/pipeline/test/parsers/idex_orders/index_test.ts b/packages/pipeline/test/parsers/idex_orders/index_test.ts index d54ecb9a8..48b019732 100644 --- a/packages/pipeline/test/parsers/idex_orders/index_test.ts +++ b/packages/pipeline/test/parsers/idex_orders/index_test.ts @@ -31,13 +31,13 @@ describe('idex_orders', () => { user: '0x212345667543456435324564345643453453333', }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'idex'; const expected = new TokenOrder(); expected.source = 'idex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.5); expected.baseAssetSymbol = 'ABC'; expected.baseAssetAddress = '0x0000000000000000000000000000000000000000'; @@ -65,13 +65,13 @@ describe('idex_orders', () => { user: '0x212345667543456435324564345643453453333', }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'ask'; + const orderType: OrderType = OrderType.Ask; const source: string = 'idex'; const expected = new TokenOrder(); expected.source = 'idex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'ask'; + expected.orderType = OrderType.Ask; expected.price = new BigNumber(0.5); expected.baseAssetSymbol = 'ABC'; expected.baseAssetAddress = '0x0000000000000000000000000000000000000000'; diff --git a/packages/pipeline/test/parsers/oasis_orders/index_test.ts b/packages/pipeline/test/parsers/oasis_orders/index_test.ts index 433bfb665..401fedff8 100644 --- a/packages/pipeline/test/parsers/oasis_orders/index_test.ts +++ b/packages/pipeline/test/parsers/oasis_orders/index_test.ts @@ -27,13 +27,13 @@ describe('oasis_orders', () => { low: 0, }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'oasis'; const expected = new TokenOrder(); expected.source = 'oasis'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.5); expected.baseAssetSymbol = 'DEF'; expected.baseAssetAddress = null; diff --git a/packages/pipeline/test/parsers/paradex_orders/index_test.ts b/packages/pipeline/test/parsers/paradex_orders/index_test.ts index 6b811b90d..c5dd8751b 100644 --- a/packages/pipeline/test/parsers/paradex_orders/index_test.ts +++ b/packages/pipeline/test/parsers/paradex_orders/index_test.ts @@ -32,13 +32,13 @@ describe('paradex_orders', () => { quoteTokenAddress: '0x0000000000000000000000000000000000000000', }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'paradex'; const expected = new TokenOrder(); expected.source = 'paradex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.1245); expected.baseAssetSymbol = 'DEF'; expected.baseAssetAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts index ee2842ef3..838171a72 100644 --- a/packages/pipeline/test/parsers/sra_orders/index_test.ts +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -5,6 +5,7 @@ import 'mocha'; import { SraOrder } from '../../../src/entities'; import { _convertToEntity } from '../../../src/parsers/sra_orders'; +import { AssetType } from '../../../src/types'; import { chaiSetup } from '../../utils/chai_setup'; chaiSetup.configure(); @@ -50,12 +51,12 @@ describe('sra_orders', () => { expected.signature = '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03'; expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerAssetType = 'erc20'; + expected.makerAssetType = AssetType.ERC20; expected.makerAssetProxyId = '0xf47261b0'; expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; expected.makerTokenId = null; expected.rawTakerAssetData = '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18'; - expected.takerAssetType = 'erc20'; + expected.takerAssetType = AssetType.ERC20; expected.takerAssetProxyId = '0xf47261b0'; expected.takerTokenAddress = '0x42d6622dece394b54999fbd73d108123806f6a18'; expected.takerTokenId = null; diff --git a/packages/pipeline/tsconfig.json b/packages/pipeline/tsconfig.json index 6f138f260..45e07374c 100644 --- a/packages/pipeline/tsconfig.json +++ b/packages/pipeline/tsconfig.json @@ -4,7 +4,15 @@ "outDir": "lib", "rootDir": ".", "emitDecoratorMetadata": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "resolveJsonModule": true }, - "include": ["./src/**/*", "./test/**/*", "./migrations/**/*"] + "include": ["./src/**/*", "./test/**/*", "./migrations/**/*"], + "files": [ + "./test/fixtures/copper/api_v1_activity_types.json", + "./test/fixtures/copper/api_v1_custom_field_definitions.json", + "./test/fixtures/copper/api_v1_list_activities.json", + "./test/fixtures/copper/api_v1_list_leads.json", + "./test/fixtures/copper/api_v1_list_opportunities.json" + ] } diff --git a/packages/react-docs/CHANGELOG.json b/packages/react-docs/CHANGELOG.json index 9d8b5bc88..13109e613 100644 --- a/packages/react-docs/CHANGELOG.json +++ b/packages/react-docs/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "1.0.23", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "1.0.22", "changes": [ { diff --git a/packages/react-docs/CHANGELOG.md b/packages/react-docs/CHANGELOG.md index 5c702d562..01f987645 100644 --- a/packages/react-docs/CHANGELOG.md +++ b/packages/react-docs/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.23 - _January 9, 2019_ + + * Dependencies updated + ## v1.0.22 - _December 13, 2018_ * Dependencies updated diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index f58a51c90..7cd0ae55a 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -1,6 +1,6 @@ { "name": "@0x/react-docs", - "version": "1.0.22", + "version": "1.0.23", "engines": { "node": ">=6.12" }, @@ -24,19 +24,19 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0x/dev-utils": "^1.0.21", + "@0x/dev-utils": "^1.0.22", "@0x/tslint-config": "^2.0.0", "@types/compare-versions": "^3.0.0", - "@types/styled-components": "^4.0.0", + "@types/styled-components": "4.0.0", "make-promises-safe": "^1.1.0", "shx": "^0.2.2", "tslint": "^5.9.1", "typescript": "3.0.1" }, "dependencies": { - "@0x/react-shared": "^1.0.25", - "@0x/types": "^1.4.1", - "@0x/utils": "^2.0.8", + "@0x/react-shared": "^1.1.0", + "@0x/types": "^1.5.0", + "@0x/utils": "^2.1.1", "@types/lodash": "4.14.104", "@types/material-ui": "^0.20.0", "@types/node": "*", diff --git a/packages/react-shared/CHANGELOG.json b/packages/react-shared/CHANGELOG.json index 9ef0d079f..aaf2485d1 100644 --- a/packages/react-shared/CHANGELOG.json +++ b/packages/react-shared/CHANGELOG.json @@ -7,7 +7,8 @@ "Change implementation to use react-router-dom NavLink instead of Link. Expose `activeStyle` prop.", "pr": 1448 } - ] + ], + "timestamp": 1547040760 }, { "version": "1.0.25", diff --git a/packages/react-shared/CHANGELOG.md b/packages/react-shared/CHANGELOG.md index 8afa94e04..a8f2764dd 100644 --- a/packages/react-shared/CHANGELOG.md +++ b/packages/react-shared/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.0 - _January 9, 2019_ + + * Change implementation to use react-router-dom NavLink instead of Link. Expose `activeStyle` prop. (#1448) + ## v1.0.25 - _December 13, 2018_ * Dependencies updated diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json index 17defe35a..5754e238b 100644 --- a/packages/react-shared/package.json +++ b/packages/react-shared/package.json @@ -1,6 +1,6 @@ { "name": "@0x/react-shared", - "version": "1.0.25", + "version": "1.1.0", "engines": { "node": ">=6.12" }, @@ -25,7 +25,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0x/dev-utils": "^1.0.21", + "@0x/dev-utils": "^1.0.22", "@0x/tslint-config": "^2.0.0", "make-promises-safe": "^1.1.0", "shx": "^0.2.2", @@ -33,7 +33,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/types": "^1.4.1", + "@0x/types": "^1.5.0", "@material-ui/core": "^3.0.1", "@types/is-mobile": "0.3.0", "@types/lodash": "4.14.104", @@ -43,7 +43,7 @@ "@types/react-dom": "*", "@types/react-router-dom": "^4.0.4", "@types/react-scroll": "1.5.3", - "@types/styled-components": "^4.0.0", + "@types/styled-components": "4.0.0", "@types/valid-url": "^1.0.2", "basscss": "^8.0.3", "change-case": "^3.0.2", diff --git a/packages/sol-compiler/CHANGELOG.json b/packages/sol-compiler/CHANGELOG.json index 8548fd73f..3c88bf4e9 100644 --- a/packages/sol-compiler/CHANGELOG.json +++ b/packages/sol-compiler/CHANGELOG.json @@ -9,8 +9,13 @@ { "note": "Make error and warning colouring more visually pleasant and consistent with other compilers", "pr": 1461 + }, + { + "note": "Add newest solidity versions from 0.4.25 to 0.5.2", + "pr": 1496 } - ] + ], + "timestamp": 1547040760 }, { "version": "1.1.16", diff --git a/packages/sol-compiler/CHANGELOG.md b/packages/sol-compiler/CHANGELOG.md index b2066448d..6ec804c3a 100644 --- a/packages/sol-compiler/CHANGELOG.md +++ b/packages/sol-compiler/CHANGELOG.md @@ -5,6 +5,12 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.0 - _January 9, 2019_ + + * Add sol-compiler watch mode with -w flag (#1461) + * Make error and warning colouring more visually pleasant and consistent with other compilers (#1461) + * Add newest solidity versions from 0.4.25 to 0.5.2 (#1496) + ## v1.1.16 - _December 13, 2018_ * Dependencies updated diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json index 86167a603..6c9435bcd 100644 --- a/packages/sol-compiler/package.json +++ b/packages/sol-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-compiler", - "version": "1.1.16", + "version": "2.0.0", "engines": { "node": ">=6.12" }, @@ -42,7 +42,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-compiler/README.md", "devDependencies": { - "@0x/dev-utils": "^1.0.21", + "@0x/dev-utils": "^1.0.22", "@0x/tslint-config": "^2.0.0", "@types/chokidar": "^1.7.5", "@types/mkdirp": "^0.5.2", @@ -67,13 +67,13 @@ "zeppelin-solidity": "1.8.0" }, "dependencies": { - "@0x/assert": "^1.0.20", - "@0x/json-schemas": "^2.1.4", - "@0x/sol-resolver": "^1.1.1", - "@0x/types": "^1.4.1", + "@0x/assert": "^1.0.21", + "@0x/json-schemas": "^2.1.5", + "@0x/sol-resolver": "^1.2.1", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@types/yargs": "^11.0.0", "chalk": "^2.3.0", "chokidar": "^2.0.4", @@ -84,7 +84,7 @@ "pluralize": "^7.0.0", "require-from-string": "^2.0.1", "semver": "5.5.0", - "solc": "^0.4.23", + "solc": "^0.5.2", "source-map-support": "^0.5.0", "web3-eth-abi": "^1.0.0-beta.24", "yargs": "^10.0.3" diff --git a/packages/sol-compiler/src/solc/bin_paths.ts b/packages/sol-compiler/src/solc/bin_paths.ts index a75255dc6..b653c0926 100644 --- a/packages/sol-compiler/src/solc/bin_paths.ts +++ b/packages/sol-compiler/src/solc/bin_paths.ts @@ -3,19 +3,50 @@ export interface BinaryPaths { } export const binPaths: BinaryPaths = { - '0.4.10': 'soljson-v0.4.10+commit.f0d539ae.js', - '0.4.11': 'soljson-v0.4.11+commit.68ef5810.js', - '0.4.12': 'soljson-v0.4.12+commit.194ff033.js', - '0.4.13': 'soljson-v0.4.13+commit.fb4cb1a.js', - '0.4.14': 'soljson-v0.4.14+commit.c2215d46.js', - '0.4.15': 'soljson-v0.4.15+commit.bbb8e64f.js', - '0.4.16': 'soljson-v0.4.16+commit.d7661dd9.js', - '0.4.17': 'soljson-v0.4.17+commit.bdeb9e52.js', - '0.4.18': 'soljson-v0.4.18+commit.9cf6e910.js', - '0.4.19': 'soljson-v0.4.19+commit.c4cbbb05.js', - '0.4.20': 'soljson-v0.4.20+commit.3155dd80.js', - '0.4.21': 'soljson-v0.4.21+commit.dfe3193c.js', - '0.4.22': 'soljson-v0.4.22+commit.4cb486ee.js', - '0.4.23': 'soljson-v0.4.23+commit.124ca40d.js', + '0.5.2': 'soljson-v0.5.2+commit.1df8f40c.js', + '0.5.1': 'soljson-v0.5.1+commit.c8a2cb62.js', + '0.5.0': 'soljson-v0.5.0+commit.1d4f565a.js', + '0.4.25': 'soljson-v0.4.25+commit.59dbf8f1.js', '0.4.24': 'soljson-v0.4.24+commit.e67f0147.js', + '0.4.23': 'soljson-v0.4.23+commit.124ca40d.js', + '0.4.22': 'soljson-v0.4.22+commit.4cb486ee.js', + '0.4.21': 'soljson-v0.4.21+commit.dfe3193c.js', + '0.4.20': 'soljson-v0.4.20+commit.3155dd80.js', + '0.4.19': 'soljson-v0.4.19+commit.c4cbbb05.js', + '0.4.18': 'soljson-v0.4.18+commit.9cf6e910.js', + '0.4.17': 'soljson-v0.4.17+commit.bdeb9e52.js', + '0.4.16': 'soljson-v0.4.16+commit.d7661dd9.js', + '0.4.15': 'soljson-v0.4.15+commit.bbb8e64f.js', + '0.4.14': 'soljson-v0.4.14+commit.c2215d46.js', + '0.4.13': 'soljson-v0.4.13+commit.fb4cb1a.js', + '0.4.12': 'soljson-v0.4.12+commit.194ff033.js', + '0.4.11': 'soljson-v0.4.11+commit.68ef5810.js', + '0.4.10': 'soljson-v0.4.10+commit.f0d539ae.js', + '0.4.9': 'soljson-v0.4.9+commit.364da425.js', + '0.4.8': 'soljson-v0.4.8+commit.60cc1668.js', + '0.4.7': 'soljson-v0.4.7+commit.822622cf.js', + '0.4.6': 'soljson-v0.4.6+commit.2dabbdf0.js', + '0.4.5': 'soljson-v0.4.5+commit.b318366e.js', + '0.4.4': 'soljson-v0.4.4+commit.4633f3de.js', + '0.4.3': 'soljson-v0.4.3+commit.2353da71.js', + '0.4.2': 'soljson-v0.4.2+commit.af6afb04.js', + '0.4.1': 'soljson-v0.4.1+commit.4fc6fc2c.js', + '0.4.0': 'soljson-v0.4.0+commit.acd334c9.js', + '0.3.6': 'soljson-v0.3.6+commit.3fc68da.js', + '0.3.5': 'soljson-v0.3.5+commit.5f97274.js', + '0.3.4': 'soljson-v0.3.4+commit.7dab890.js', + '0.3.3': 'soljson-v0.3.3+commit.4dc1cb1.js', + '0.3.2': 'soljson-v0.3.2+commit.81ae2a7.js', + '0.3.1': 'soljson-v0.3.1+commit.c492d9b.js', + '0.3.0': 'soljson-v0.3.0+commit.11d6736.js', + '0.2.2': 'soljson-v0.2.2+commit.ef92f56.js', + '0.2.1': 'soljson-v0.2.1+commit.91a6b35.js', + '0.2.0': 'soljson-v0.2.0+commit.4dc2445.js', + '0.1.7': 'soljson-v0.1.7+commit.b4e666c.js', + '0.1.6': 'soljson-v0.1.6+commit.d41f8b7.js', + '0.1.5': 'soljson-v0.1.5+commit.23865e3.js', + '0.1.4': 'soljson-v0.1.4+commit.5f6c3cd.js', + '0.1.3': 'soljson-v0.1.3+commit.28f561.js', + '0.1.2': 'soljson-v0.1.2+commit.d0d36e3.js', + '0.1.1': 'soljson-v0.1.1+commit.6ff4cd6.js', }; diff --git a/packages/sol-cov/CHANGELOG.json b/packages/sol-cov/CHANGELOG.json index b7973c135..3781dd39e 100644 --- a/packages/sol-cov/CHANGELOG.json +++ b/packages/sol-cov/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "2.1.17", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "2.1.16", "changes": [ { diff --git a/packages/sol-cov/CHANGELOG.md b/packages/sol-cov/CHANGELOG.md index 879ef9c95..c2bc3cd01 100644 --- a/packages/sol-cov/CHANGELOG.md +++ b/packages/sol-cov/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.17 - _January 9, 2019_ + + * Dependencies updated + ## v2.1.16 - _December 13, 2018_ * Dependencies updated diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index 3ade51c80..8542948b1 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-cov", - "version": "2.1.16", + "version": "2.1.17", "engines": { "node": ">=6.12" }, @@ -42,12 +42,12 @@ }, "homepage": "https://github.com/0xProject/0x.js/packages/sol-cov/README.md", "dependencies": { - "@0x/dev-utils": "^1.0.21", - "@0x/sol-compiler": "^1.1.16", - "@0x/subproviders": "^2.1.8", + "@0x/dev-utils": "^1.0.22", + "@0x/sol-compiler": "^2.0.0", + "@0x/subproviders": "^2.1.9", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@types/solidity-parser-antlr": "^0.2.0", "ethereum-types": "^1.1.4", "ethereumjs-util": "^5.1.1", diff --git a/packages/sol-doc/CHANGELOG.json b/packages/sol-doc/CHANGELOG.json index e8fef746e..1be3b2121 100644 --- a/packages/sol-doc/CHANGELOG.json +++ b/packages/sol-doc/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "1.0.12", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "1.0.11", "changes": [ { diff --git a/packages/sol-doc/CHANGELOG.md b/packages/sol-doc/CHANGELOG.md index 1b4a938af..3416662e5 100644 --- a/packages/sol-doc/CHANGELOG.md +++ b/packages/sol-doc/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.12 - _January 9, 2019_ + + * Dependencies updated + ## v1.0.11 - _December 13, 2018_ * Dependencies updated diff --git a/packages/sol-doc/package.json b/packages/sol-doc/package.json index c83c122df..f93abfe49 100644 --- a/packages/sol-doc/package.json +++ b/packages/sol-doc/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-doc", - "version": "1.0.11", + "version": "1.0.12", "description": "Solidity documentation generator", "main": "lib/src/index.js", "types": "lib/src/index.d.js", @@ -25,9 +25,9 @@ "author": "F. Eugene Aumson", "license": "Apache-2.0", "dependencies": { - "@0x/sol-compiler": "^1.1.16", - "@0x/types": "^1.4.1", - "@0x/utils": "^2.0.8", + "@0x/sol-compiler": "^2.0.0", + "@0x/types": "^1.5.0", + "@0x/utils": "^2.1.1", "ethereum-types": "^1.1.4", "ethereumjs-util": "^5.1.1", "lodash": "^4.17.10", diff --git a/packages/sol-resolver/CHANGELOG.json b/packages/sol-resolver/CHANGELOG.json index 74c4d39c5..d541dc320 100644 --- a/packages/sol-resolver/CHANGELOG.json +++ b/packages/sol-resolver/CHANGELOG.json @@ -10,7 +10,8 @@ "note": "Add `SpyResolver` that records all resolved contracts data", "pr": 1461 } - ] + ], + "timestamp": 1547040760 }, { "version": "1.1.1", diff --git a/packages/sol-resolver/CHANGELOG.md b/packages/sol-resolver/CHANGELOG.md index 98435be19..b03f21b79 100644 --- a/packages/sol-resolver/CHANGELOG.md +++ b/packages/sol-resolver/CHANGELOG.md @@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.2.1 - _January 9, 2019_ + + * Add `absolutePath` to `ContractSource` type (#1461) + * Add `SpyResolver` that records all resolved contracts data (#1461) + ## v1.1.1 - _December 13, 2018_ * Dependencies updated diff --git a/packages/sol-resolver/package.json b/packages/sol-resolver/package.json index 0163765d9..b33a4f494 100644 --- a/packages/sol-resolver/package.json +++ b/packages/sol-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-resolver", - "version": "1.1.1", + "version": "1.2.1", "engines": { "node": ">=6.12" }, @@ -30,7 +30,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/types": "^1.4.1", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", "lodash": "^4.17.5" }, diff --git a/packages/sra-spec/CHANGELOG.json b/packages/sra-spec/CHANGELOG.json index 393054465..fe52a8111 100644 --- a/packages/sra-spec/CHANGELOG.json +++ b/packages/sra-spec/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "1.0.14", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "1.0.13", "changes": [ { diff --git a/packages/sra-spec/CHANGELOG.md b/packages/sra-spec/CHANGELOG.md index fd673b837..36b6b1fb0 100644 --- a/packages/sra-spec/CHANGELOG.md +++ b/packages/sra-spec/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.14 - _January 9, 2019_ + + * Dependencies updated + ## v1.0.13 - _December 13, 2018_ * Dependencies updated diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index 3a6ee96d3..a0caea0f4 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sra-spec", - "version": "1.0.13", + "version": "1.0.14", "engines": { "node": ">=6.12" }, @@ -35,7 +35,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-spec/README.md", "dependencies": { - "@0x/json-schemas": "^2.1.4", + "@0x/json-schemas": "^2.1.5", "@loopback/openapi-v3-types": "^0.8.2" }, "devDependencies": { diff --git a/packages/sra-spec/public/index.html b/packages/sra-spec/public/index.html index e75ae7f04..5271b50c6 100644 --- a/packages/sra-spec/public/index.html +++ b/packages/sra-spec/public/index.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <head> - <title>ReDoc</title> + <title>0x Standard Relayer API</title> <!-- needed for adaptive design --> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"> @@ -21,4 +21,4 @@ <redoc spec-url='http://sra-spec.s3-website-us-east-1.amazonaws.com/api.json'></redoc> <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index 938b2a717..cc0e0414a 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "2.1.9", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "2.1.8", "changes": [ { diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md index 002c76395..076cce935 100644 --- a/packages/subproviders/CHANGELOG.md +++ b/packages/subproviders/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.9 - _January 9, 2019_ + + * Dependencies updated + ## v2.1.8 - _December 13, 2018_ * Dependencies updated diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 90ef6b677..178e7a99c 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -1,6 +1,6 @@ { "name": "@0x/subproviders", - "version": "2.1.8", + "version": "2.1.9", "engines": { "node": ">=6.12" }, @@ -29,11 +29,11 @@ } }, "dependencies": { - "@0x/assert": "^1.0.20", - "@0x/types": "^1.4.1", + "@0x/assert": "^1.0.21", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-transport-u2f": "4.24.0", "@types/eth-lightwallet": "^3.0.0", diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index 70d785c74..b01ef4b8e 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@0x/testnet-faucets", - "version": "1.0.60", + "version": "1.0.61", "engines": { "node": ">=6.12" }, @@ -18,11 +18,11 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "0x.js": "^2.0.8", - "@0x/subproviders": "^2.1.8", + "0x.js": "^3.0.0", + "@0x/subproviders": "^2.1.9", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", - "@0x/web3-wrapper": "^3.2.1", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "body-parser": "^1.17.1", "ethereum-types": "^1.1.4", "ethereumjs-tx": "^1.3.5", diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index f1cd2f18e..754cd29dc 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "1.5.0", + "changes": [ + { + "note": "Added types for Dutch Auction contract", + "pr": 1465 + } + ], + "timestamp": 1547040760 + }, + { "version": "1.4.1", "changes": [ { @@ -18,6 +28,10 @@ { "note": "Add RevertReasons for DutchAuction contract", "pr": 1225 + }, + { + "note": "Add MultiAsset types", + "pr": 1363 } ], "timestamp": 1544570656 diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 5170eb4db..a1745cf94 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.5.0 - _January 9, 2019_ + + * Added types for Dutch Auction contract (#1465) + ## v1.4.1 - _December 13, 2018_ * Dependencies updated @@ -13,6 +17,7 @@ CHANGELOG * Add `LengthMismatch` and `LengthGreaterThan3Required` revert reasons (#1224) * Add RevertReasons for DutchAuction contract (#1225) + * Add MultiAsset types (#1363) ## v1.3.0 - _November 21, 2018_ diff --git a/packages/types/package.json b/packages/types/package.json index 3c4bb6fe6..75c3b7c63 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@0x/types", - "version": "1.4.1", + "version": "1.5.0", "engines": { "node": ">=6.12" }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 4470dd501..49f788fb0 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -110,7 +110,9 @@ export type DoneCallback = (err?: Error) => void; export interface OrderRelevantState { makerBalance: BigNumber; + makerIndividualBalances: ObjectMap<BigNumber>; makerProxyAllowance: BigNumber; + makerIndividualProxyAllowances: ObjectMap<BigNumber>; makerFeeBalance: BigNumber; makerFeeProxyAllowance: BigNumber; filledTakerAssetAmount: BigNumber; @@ -155,6 +157,7 @@ export enum SignatureType { export enum AssetProxyId { ERC20 = '0xf47261b0', ERC721 = '0x02571792', + MultiAsset = '0x94cfcdd7', } export interface ERC20AssetData { @@ -168,7 +171,21 @@ export interface ERC721AssetData { tokenId: BigNumber; } -export type AssetData = ERC20AssetData | ERC721AssetData; +export type SingleAssetData = ERC20AssetData | ERC721AssetData; + +export interface MultiAssetData { + assetProxyId: string; + amounts: BigNumber[]; + nestedAssetData: string[]; +} + +export interface MultiAssetDataWithRecursiveDecoding { + assetProxyId: string; + amounts: BigNumber[]; + nestedAssetData: SingleAssetData[]; +} + +export type AssetData = SingleAssetData | MultiAssetData | MultiAssetDataWithRecursiveDecoding; // TODO: DRY. These should be extracted from contract code. export enum RevertReason { @@ -659,3 +676,12 @@ export interface SimpleEvmOutput { export interface SimpleEvmBytecodeOutput { object: string; } + +export interface DutchAuctionDetails { + beginTimeSeconds: BigNumber; + endTimeSeconds: BigNumber; + beginAmount: BigNumber; + endAmount: BigNumber; + currentAmount: BigNumber; + currentTimeSeconds: BigNumber; +} diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 605151fb6..fb85c4174 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "2.1.1", + "changes": [ + { + "note": "Add `should` prefix to names of properties in EncodingRules and DecodingRules", + "pr": 1363 + } + ], + "timestamp": 1547040760 + }, + { "version": "2.1.0", "changes": [ { diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index c0437392d..15d78e321 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.1 - _January 9, 2019_ + + * Add `should` prefix to names of properties in EncodingRules and DecodingRules (#1363) + +## v2.1.0 - _Invalid date_ + + * Add `logWithTime` to `logUtils` (#1461) + ## v2.0.8 - _December 13, 2018_ * Dependencies updated diff --git a/packages/utils/package.json b/packages/utils/package.json index 5ffec049a..49d6e3013 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/utils", - "version": "2.0.8", + "version": "2.1.1", "engines": { "node": ">=6.12" }, @@ -44,7 +44,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/types": "^1.4.1", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.6", "@types/node": "*", "abortcontroller-polyfill": "^1.1.9", diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts index 089d04659..00059a4b6 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts @@ -62,7 +62,7 @@ export abstract class AbstractSetDataType extends DataType { // Create a new scope in the calldata, before descending into the members of this set. calldata.startScope(); let value: any[] | object; - if (rules.structsAsObjects && !this._isArray) { + if (rules.shouldConvertStructsToObjects && !this._isArray) { // Construct an object with values for each member of the set. value = {}; _.each(this._memberIndexByName, (idx: number, key: string) => { diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 5f3eee94a..b08fb71ce 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -49,7 +49,7 @@ export class Calldata { throw new Error('expected root'); } // Optimize, if flag set - if (this._rules.optimize) { + if (this._rules.shouldOptimize) { this._optimize(); } // Set offsets @@ -60,7 +60,9 @@ export class Calldata { offset += block.getSizeInBytes(); } // Generate hex string - const hexString = this._rules.annotate ? this._toHumanReadableCallData() : this._toEvmCompatibeCallDataHex(); + const hexString = this._rules.shouldAnnotate + ? this._toHumanReadableCallData() + : this._toEvmCompatibeCallDataHex(); return hexString; } /** diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts index 2f43ba04d..36de2dd4f 100644 --- a/packages/utils/src/abi_encoder/utils/constants.ts +++ b/packages/utils/src/abi_encoder/utils/constants.ts @@ -11,7 +11,7 @@ export const constants = { HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA: 0, // Disable no-object-literal-type-assertion so we can enforce cast /* tslint:disable no-object-literal-type-assertion */ - DEFAULT_DECODING_RULES: { structsAsObjects: false } as DecodingRules, - DEFAULT_ENCODING_RULES: { optimize: true, annotate: false } as EncodingRules, + DEFAULT_DECODING_RULES: { shouldConvertStructsToObjects: false } as DecodingRules, + DEFAULT_ENCODING_RULES: { shouldOptimize: true, shouldAnnotate: false } as EncodingRules, /* tslint:enable no-object-literal-type-assertion */ }; diff --git a/packages/utils/src/abi_encoder/utils/rules.ts b/packages/utils/src/abi_encoder/utils/rules.ts index 31471e97a..c8d83c3ba 100644 --- a/packages/utils/src/abi_encoder/utils/rules.ts +++ b/packages/utils/src/abi_encoder/utils/rules.ts @@ -1,8 +1,8 @@ export interface DecodingRules { - structsAsObjects: boolean; + shouldConvertStructsToObjects: boolean; } export interface EncodingRules { - optimize?: boolean; - annotate?: boolean; + shouldOptimize?: boolean; + shouldAnnotate?: boolean; } diff --git a/packages/utils/test/abi_encoder/evm_data_types_test.ts b/packages/utils/test/abi_encoder/evm_data_types_test.ts index 7185851a8..55d582d10 100644 --- a/packages/utils/test/abi_encoder/evm_data_types_test.ts +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately. + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. describe('Array', () => { it('Fixed size; Static elements', async () => { // Create DataType object @@ -194,7 +194,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -214,7 +214,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -234,7 +234,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -254,7 +254,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -276,7 +276,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -298,7 +298,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -328,7 +328,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); diff --git a/packages/utils/test/abi_encoder/methods_test.ts b/packages/utils/test/abi_encoder/methods_test.ts index 837020883..a0525967e 100644 --- a/packages/utils/test/abi_encoder/methods_test.ts +++ b/packages/utils/test/abi_encoder/methods_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Method Encoding / Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately. + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. it('Types with default widths', async () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); @@ -360,7 +360,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const decodedValue = method.decode(calldata, { structsAsObjects: true }); + const decodedValue = method.decode(calldata, { shouldConvertStructsToObjects: true }); expect(decodedValue).to.be.deep.equal(args); }); }); diff --git a/packages/utils/test/abi_encoder/optimizer_test.ts b/packages/utils/test/abi_encoder/optimizer_test.ts index 18aa6549a..ee0654ec3 100644 --- a/packages/utils/test/abi_encoder/optimizer_test.ts +++ b/packages/utils/test/abi_encoder/optimizer_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: true }; + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: true }; it('Duplicate Dynamic Arrays with Static Elements', async () => { // Generate calldata const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements); @@ -206,7 +206,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const twoDimArray2 = twoDimArray1; const args = [twoDimArray1, twoDimArray2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: false }); + const optimizedCalldata = method.encode(args, { shouldOptimize: false }); const expectedOptimizedCalldata = '0x0d28c4f9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); diff --git a/packages/utils/test/abi_encoder/return_values_test.ts b/packages/utils/test/abi_encoder/return_values_test.ts index a8cdd6ca3..104c7f5db 100644 --- a/packages/utils/test/abi_encoder/return_values_test.ts +++ b/packages/utils/test/abi_encoder/return_values_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Return Value Encoding/Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately. + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. it('No Return Value', async () => { // Decode return value const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues); diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index 6b76626a9..65f811028 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "timestamp": 1547040760, + "version": "3.2.2", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, + { "version": "3.2.1", "changes": [ { diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md index 76ca80e69..4242fe0cc 100644 --- a/packages/web3-wrapper/CHANGELOG.md +++ b/packages/web3-wrapper/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.2.2 - _January 9, 2019_ + + * Dependencies updated + ## v3.2.1 - _December 13, 2018_ * Dependencies updated diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index e5af24965..b67472a8a 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -1,6 +1,6 @@ { "name": "@0x/web3-wrapper", - "version": "3.2.1", + "version": "3.2.2", "engines": { "node": ">=6.12" }, @@ -54,10 +54,10 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/assert": "^1.0.20", - "@0x/json-schemas": "^2.1.4", + "@0x/assert": "^1.0.21", + "@0x/json-schemas": "^2.1.5", "@0x/typescript-typings": "^3.0.6", - "@0x/utils": "^2.0.8", + "@0x/utils": "^2.1.1", "ethereum-types": "^1.1.4", "ethereumjs-util": "^5.1.1", "ethers": "~4.0.4", diff --git a/packages/website/package.json b/packages/website/package.json index bb97ea4e8..af43fabf9 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@0x/website", - "version": "0.0.63", + "version": "0.0.64", "engines": { "node": ">=6.12" }, @@ -20,23 +20,23 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "@0x/asset-buyer": "^3.0.4", - "@0x/contract-addresses": "^2.0.0", - "@0x/contract-wrappers": "^4.1.1", - "@0x/json-schemas": "^2.1.2", - "@0x/order-utils": "^3.0.4", - "@0x/react-docs": "^1.0.20", - "@0x/react-shared": "^1.0.23", - "@0x/subproviders": "^2.1.6", - "@0x/types": "^1.3.0", + "@0x/asset-buyer": "^3.0.5", + "@0x/contract-addresses": "^2.1.0", + "@0x/contract-wrappers": "^4.2.0", + "@0x/json-schemas": "^2.1.5", + "@0x/order-utils": "^3.1.0", + "@0x/react-docs": "^1.0.23", + "@0x/react-shared": "^1.1.0", + "@0x/subproviders": "^2.1.9", + "@0x/types": "^1.5.0", "@0x/typescript-typings": "^3.0.4", - "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.6", + "@0x/utils": "^2.1.1", + "@0x/web3-wrapper": "^3.2.2", "@reach/dialog": "^0.1.2", "@types/react-lazyload": "^2.3.1", "@types/react-loadable": "^5.4.2", "@types/react-syntax-highlighter": "^0.0.8", - "@types/styled-components": "^4.1.1", + "@types/styled-components": "4.1.1", "accounting": "^0.4.1", "basscss": "^8.0.3", "blockies": "^0.0.2", diff --git a/packages/website/translations/chinese.json b/packages/website/translations/chinese.json index b99a3cdcb..88193a181 100644 --- a/packages/website/translations/chinese.json +++ b/packages/website/translations/chinese.json @@ -83,7 +83,7 @@ "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", - "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", + "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications on top of Ethereum", "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", diff --git a/packages/website/translations/english.json b/packages/website/translations/english.json index 2914ffead..a1d8ecc43 100644 --- a/packages/website/translations/english.json +++ b/packages/website/translations/english.json @@ -87,7 +87,7 @@ "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", - "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", + "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications on top of Ethereum", "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", diff --git a/packages/website/translations/korean.json b/packages/website/translations/korean.json index a421ffb94..539b81470 100644 --- a/packages/website/translations/korean.json +++ b/packages/website/translations/korean.json @@ -83,7 +83,7 @@ "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", - "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", + "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications on top of Ethereum", "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", diff --git a/packages/website/translations/russian.json b/packages/website/translations/russian.json index b3ea29cf3..feb5df02e 100644 --- a/packages/website/translations/russian.json +++ b/packages/website/translations/russian.json @@ -83,7 +83,7 @@ "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", - "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", + "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications on top of Ethereum", "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", diff --git a/packages/website/translations/spanish.json b/packages/website/translations/spanish.json index db75312c5..f4762a67e 100644 --- a/packages/website/translations/spanish.json +++ b/packages/website/translations/spanish.json @@ -84,7 +84,7 @@ "BUILD_A_RELAYER": "build a relayer", "BUILD_A_RELAYER_DESCRIPTION": "Learn how to build your own 0x relayer from scratch", "DEVELOP_ON_ETHEREUM": "develop on Ethereum", - "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications ontop of Ethereum", + "DEVELOP_ON_ETHEREUM_DESCRIPTION": "Learn more about building applications on top of Ethereum", "ORDER_BASICS": "Make & take orders", "ORDER_BASICS_DESCRIPTION": "Tutorial on how to create, validate and fill an order using 0x", "USE_NETWORKED_LIQUIDITY": "use networked liquidity", diff --git a/packages/website/ts/components/button.tsx b/packages/website/ts/components/button.tsx index c9785e48c..569038ae0 100644 --- a/packages/website/ts/components/button.tsx +++ b/packages/website/ts/components/button.tsx @@ -8,6 +8,7 @@ import { colors } from 'ts/style/colors'; interface ButtonInterface { bgColor?: string; + borderColor?: string; color?: string; children?: React.ReactNode | string; isTransparent?: boolean; @@ -26,7 +27,7 @@ interface ButtonInterface { shouldUseAnchorTag?: boolean; } -export const Button = (props: ButtonInterface) => { +export const Button: React.StatelessComponent<ButtonInterface> = (props: ButtonInterface) => { const { children, href, isWithArrow, to, shouldUseAnchorTag, target } = props; let linkElem; @@ -53,6 +54,10 @@ export const Button = (props: ButtonInterface) => { ); }; +Button.defaultProps = { + borderColor: 'rgba(255, 255, 255, .4)', +}; + const ButtonBase = styled.button < ButtonInterface > @@ -62,7 +67,7 @@ const ButtonBase = display: inline-block; background-color: ${props => props.bgColor || colors.brandLight}; background-color: ${props => (props.isTransparent || props.isWithArrow) && 'transparent'}; - border-color: ${props => props.isTransparent && !props.isWithArrow && 'rgba(255, 255, 255, .4)'}; + border-color: ${props => props.isTransparent && !props.isWithArrow && props.borderColor}; color: ${props => (props.isAccentColor ? props.theme.linkColor : props.color || props.theme.textColor)}; padding: ${props => !props.isNoPadding && !props.isWithArrow && '18px 30px'}; white-space: ${props => props.isWithArrow && 'nowrap'}; diff --git a/packages/website/ts/components/definition.tsx b/packages/website/ts/components/definition.tsx index c7fac5177..92fe54f38 100644 --- a/packages/website/ts/components/definition.tsx +++ b/packages/website/ts/components/definition.tsx @@ -5,7 +5,7 @@ import { Button } from 'ts/components/button'; import { Icon } from 'ts/components/icon'; import { Heading, Paragraph } from 'ts/components/text'; -interface Action { +export interface Action { label: string; url?: string; onClick?: () => void; diff --git a/packages/website/ts/components/documentation/sidebar_header.tsx b/packages/website/ts/components/documentation/sidebar_header.tsx index 0ab24ab5e..d158ab926 100644 --- a/packages/website/ts/components/documentation/sidebar_header.tsx +++ b/packages/website/ts/components/documentation/sidebar_header.tsx @@ -24,7 +24,7 @@ export const SidebarHeader: React.StatelessComponent<SidebarHeaderProps> = ({ return ( <Container> <Container className="flex justify-bottom"> - <Container className="col col-7 pl1"> + <Container className="col col-8 pl1"> <Text fontColor={colors.lightLinkBlue} fontSize={screenWidth === ScreenWidths.Sm ? '20px' : '22px'} @@ -37,7 +37,7 @@ export const SidebarHeader: React.StatelessComponent<SidebarHeaderProps> = ({ {!_.isUndefined(docsVersion) && !_.isUndefined(availableDocVersions) && !_.isUndefined(onVersionSelected) && ( - <div className="col col-5 pl1" style={{ alignSelf: 'flex-end', paddingBottom: 4 }}> + <div className="col col-4 pl1" style={{ alignSelf: 'flex-end', paddingBottom: 4 }}> <Container className="right"> <VersionDropDown selectedVersion={docsVersion} diff --git a/packages/website/ts/components/dropdowns/dropdown_developers.tsx b/packages/website/ts/components/dropdowns/dropdown_developers.tsx index dc6b70d21..590d2ead9 100644 --- a/packages/website/ts/components/dropdowns/dropdown_developers.tsx +++ b/packages/website/ts/components/dropdowns/dropdown_developers.tsx @@ -37,6 +37,10 @@ const introData: LinkConfig[] = [ label: 'Use networked liquidity', url: `${WebsitePaths.Wiki}#Find,-Submit,-Fill-Order-From-Relayer`, }, + { + label: 'Market making', + url: `${WebsitePaths.MarketMaker}`, + }, ]; const docsData: LinkConfig[] = [ diff --git a/packages/website/ts/components/modals/modal_contact.tsx b/packages/website/ts/components/modals/modal_contact.tsx index d9c276584..7414df7d9 100644 --- a/packages/website/ts/components/modals/modal_contact.tsx +++ b/packages/website/ts/components/modals/modal_contact.tsx @@ -12,11 +12,18 @@ import { Icon } from 'ts/components/icon'; import { Input, InputWidth } from 'ts/components/modals/input'; import { Heading, Paragraph } from 'ts/components/text'; import { GlobalStyle } from 'ts/constants/globalStyle'; +import { utils } from 'ts/utils/utils'; + +export enum ModalContactType { + General = 'GENERAL', + MarketMaker = 'MARKET_MAKER', +} interface Props { theme?: GlobalStyle; isOpen?: boolean; onDismiss?: () => void; + modalContactType: ModalContactType; } interface FormProps { @@ -39,23 +46,30 @@ interface ErrorProps { } export class ModalContact extends React.Component<Props> { + public static defaultProps = { + modalContactType: ModalContactType.General, + }; public state = { isSubmitting: false, isSuccessful: false, errors: {}, }; + // shared fields public nameRef: React.RefObject<HTMLInputElement> = React.createRef(); public emailRef: React.RefObject<HTMLInputElement> = React.createRef(); public companyProjectRef: React.RefObject<HTMLInputElement> = React.createRef(); - public linkRef: React.RefObject<HTMLInputElement> = React.createRef(); public commentsRef: React.RefObject<HTMLInputElement> = React.createRef(); + // general lead fields + public linkRef: React.RefObject<HTMLInputElement> = React.createRef(); + // market maker lead fields + public countryRef: React.RefObject<HTMLInputElement> = React.createRef(); + public fundSizeRef: React.RefObject<HTMLInputElement> = React.createRef(); public constructor(props: Props) { super(props); } public render(): React.ReactNode { const { isOpen, onDismiss } = this.props; const { isSuccessful, errors } = this.state; - return ( <> <DialogOverlay @@ -68,58 +82,7 @@ export class ModalContact extends React.Component<Props> { <Heading color={colors.textDarkPrimary} size={34} asElement="h2"> Contact the 0x Core Team </Heading> - <Paragraph isMuted={true} color={colors.textDarkPrimary}> - If you're considering building on 0x, we're happy to answer your questions. Fill out the - form so we can connect you with the right person to help you get started. - </Paragraph> - <InputRow> - <Input - name="name" - label="Your name" - type="text" - width={InputWidth.Half} - ref={this.nameRef} - required={true} - errors={errors} - /> - <Input - name="email" - label="Your email" - type="email" - ref={this.emailRef} - required={true} - errors={errors} - width={InputWidth.Half} - /> - </InputRow> - <InputRow> - <Input - name="companyOrProject" - label="Name of your project / company" - type="text" - ref={this.companyProjectRef} - required={true} - errors={errors} - /> - </InputRow> - <InputRow> - <Input - name="link" - label="Do you have any documentation or a website?" - type="text" - ref={this.linkRef} - errors={errors} - /> - </InputRow> - <InputRow> - <Input - name="comments" - label="Anything else?" - type="textarea" - ref={this.commentsRef} - errors={errors} - /> - </InputRow> + {this._renderFormContent(errors)} <ButtonRow> <Button color="#5C5C5C" @@ -149,28 +112,182 @@ export class ModalContact extends React.Component<Props> { </> ); } + public _renderFormContent(errors: ErrorProps): React.ReactNode { + switch (this.props.modalContactType) { + case ModalContactType.MarketMaker: + return this._renderMarketMakerFormContent(errors); + case ModalContactType.General: + default: + return this._renderGeneralFormContent(errors); + } + } + private _renderMarketMakerFormContent(errors: ErrorProps): React.ReactNode { + return ( + <> + <Paragraph isMuted={true} color={colors.textDarkPrimary}> + If you’re considering market making on 0x, we’re happy to answer your questions. Fill out the form + so we can connect you with the right person to help you get started. + </Paragraph> + <InputRow> + <Input + name="name" + label="Your name" + type="text" + width={InputWidth.Half} + ref={this.nameRef} + required={true} + errors={errors} + /> + <Input + name="email" + label="Your email" + type="email" + ref={this.emailRef} + required={true} + errors={errors} + width={InputWidth.Half} + /> + </InputRow> + <InputRow> + <Input + name="country" + label="Country of Location" + type="text" + ref={this.countryRef} + required={true} + errors={errors} + /> + </InputRow> + <InputRow> + <Input + name="fundSize" + label="Fund Size" + type="text" + ref={this.fundSizeRef} + required={true} + errors={errors} + /> + </InputRow> + <InputRow> + <Input + name="companyOrProject" + label="Name of your project / company" + type="text" + ref={this.companyProjectRef} + required={false} + errors={errors} + /> + </InputRow> + <InputRow> + <Input + name="comments" + label="What is prompting you to reach out?" + type="textarea" + ref={this.commentsRef} + required={false} + errors={errors} + /> + </InputRow> + </> + ); + } + private _renderGeneralFormContent(errors: ErrorProps): React.ReactNode { + return ( + <> + <Paragraph isMuted={true} color={colors.textDarkPrimary}> + If you're considering building on 0x, we're happy to answer your questions. Fill out the form so we + can connect you with the right person to help you get started. + </Paragraph> + <InputRow> + <Input + name="name" + label="Your name" + type="text" + width={InputWidth.Half} + ref={this.nameRef} + required={true} + errors={errors} + /> + <Input + name="email" + label="Your email" + type="email" + ref={this.emailRef} + required={true} + errors={errors} + width={InputWidth.Half} + /> + </InputRow> + <InputRow> + <Input + name="companyOrProject" + label="Name of your project / company" + type="text" + ref={this.companyProjectRef} + required={true} + errors={errors} + /> + </InputRow> + <InputRow> + <Input + name="link" + label="Do you have any documentation or a website?" + type="text" + ref={this.linkRef} + errors={errors} + /> + </InputRow> + <InputRow> + <Input + name="comments" + label="Anything else?" + type="textarea" + ref={this.commentsRef} + errors={errors} + /> + </InputRow> + </> + ); + } private async _onSubmitAsync(e: Event): Promise<void> { e.preventDefault(); - const name = this.nameRef.current.value; - const email = this.emailRef.current.value; - const projectOrCompany = this.companyProjectRef.current.value; - const link = this.linkRef.current.value; - const comments = this.commentsRef.current.value; + let jsonBody; + if (this.props.modalContactType === ModalContactType.MarketMaker) { + jsonBody = { + name: this.nameRef.current.value, + email: this.emailRef.current.value, + country: this.countryRef.current.value, + fundSize: this.fundSizeRef.current.value, + projectOrCompany: this.companyProjectRef.current.value, + comments: this.commentsRef.current.value, + }; + } else { + jsonBody = { + name: this.nameRef.current.value, + email: this.emailRef.current.value, + projectOrCompany: this.companyProjectRef.current.value, + link: this.linkRef.current.value, + comments: this.commentsRef.current.value, + }; + } this.setState({ ...this.state, errors: [], isSubmitting: true }); + const endpoint = + this.props.modalContactType === ModalContactType.MarketMaker ? '/market_maker_leads' : '/leads'; + try { // Disabling no-unbound method b/c no reason for _.isEmpty to be bound // tslint:disable:no-unbound-method - const response = await fetch('https://website-api.0xproject.com/leads', { + const response = await fetch(`${utils.getBackendBaseUrl()}${endpoint}`, { method: 'post', mode: 'cors', credentials: 'same-origin', headers: { 'content-type': 'application/json; charset=utf-8', }, - body: JSON.stringify(_.omitBy({ name, email, projectOrCompany, link, comments }, _.isEmpty)), + body: JSON.stringify(_.omitBy(jsonBody, _.isEmpty)), }); if (!response.ok) { @@ -201,6 +318,7 @@ export class ModalContact extends React.Component<Props> { ); } } + // Handle errors: {"errors":[{"location":"body","param":"name","msg":"Invalid value"},{"location":"body","param":"email","msg":"Invalid value"}]} const InputRow = styled.div` diff --git a/packages/website/ts/components/newsletter_form.tsx b/packages/website/ts/components/newsletter_form.tsx index 4a7abb7ec..bd61e3f4d 100644 --- a/packages/website/ts/components/newsletter_form.tsx +++ b/packages/website/ts/components/newsletter_form.tsx @@ -4,6 +4,7 @@ import styled, { withTheme } from 'styled-components'; import { ThemeValuesInterface } from 'ts/components/siteWrap'; import { colors } from 'ts/style/colors'; import { errorReporter } from 'ts/utils/error_reporter'; +import { utils } from 'ts/utils/utils'; interface FormProps { theme: ThemeValuesInterface; @@ -92,7 +93,7 @@ class Form extends React.Component<FormProps> { } try { - await fetch('https://website-api.0x.org/newsletter_subscriber/substack', { + await fetch(`${utils.getBackendBaseUrl()}/newsletter_subscriber/substack`, { method: 'post', mode: 'cors', headers: { diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index df77e4b76..45054772c 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -24,6 +24,7 @@ import { NextEcosystem } from 'ts/pages/ecosystem'; import { Next0xInstant } from 'ts/pages/instant'; import { NextLanding } from 'ts/pages/landing'; import { NextLaunchKit } from 'ts/pages/launch_kit'; +import { NextMarketMaker } from 'ts/pages/market_maker'; import { NextWhy } from 'ts/pages/why'; // Check if we've introduced an update that requires us to clear the tradeHistory local storage entries @@ -99,6 +100,11 @@ render( {/* Next (new site) routes */} <Route exact={true} path="/" component={NextLanding as any} /> <Route exact={true} path={WebsitePaths.Why} component={NextWhy as any} /> + <Route + exact={true} + path={WebsitePaths.MarketMaker} + component={NextMarketMaker as any} + /> <Route exact={true} path={WebsitePaths.Instant} component={Next0xInstant as any} /> <Route exact={true} path={WebsitePaths.LaunchKit} component={NextLaunchKit as any} /> <Route exact={true} path={WebsitePaths.Ecosystem} component={NextEcosystem as any} /> diff --git a/packages/website/ts/pages/documentation/developers_page.tsx b/packages/website/ts/pages/documentation/developers_page.tsx index fcca2b6ad..78376a0f2 100644 --- a/packages/website/ts/pages/documentation/developers_page.tsx +++ b/packages/website/ts/pages/documentation/developers_page.tsx @@ -153,7 +153,7 @@ export class DevelopersPage extends React.Component<DevelopersPageProps, Develop <Container className="flex mx-auto" height="100vh"> <Container className="sm-hide xs-hide relative" - width={234} + width={270} paddingLeft={22} paddingRight={22} paddingTop={0} diff --git a/packages/website/ts/pages/market_maker.tsx b/packages/website/ts/pages/market_maker.tsx index 55566c798..854870358 100644 --- a/packages/website/ts/pages/market_maker.tsx +++ b/packages/website/ts/pages/market_maker.tsx @@ -1,43 +1,68 @@ import * as _ from 'lodash'; +import { opacify } from 'polished'; import * as React from 'react'; import { Banner } from 'ts/components/banner'; import { Button } from 'ts/components/button'; -import { Definition } from 'ts/components/definition'; +import { Action, Definition } from 'ts/components/definition'; import { Hero } from 'ts/components/hero'; -import { ModalContact } from 'ts/components/modals/modal_contact'; +import { ModalContact, ModalContactType } from 'ts/components/modals/modal_contact'; import { Section } from 'ts/components/newLayout'; import { SiteWrap } from 'ts/components/siteWrap'; +import { colors } from 'ts/style/colors'; +import { WebsitePaths } from 'ts/types'; -const offersData = [ - { - icon: 'supportForAllEthereumStandards', - title: 'Comprehensive Tutorials', - description: - 'Stay on the bleeding edge of crypto by learning how to market make on decentralized exchanges. The network of 0x relayers provides market makers a first-mover advantage to capture larger spreads, arbitrage markets, and access a long-tail of new tokens not currently listed on centralized exchanges.', - }, - { - icon: 'generateRevenueForYourBusiness-large', - title: 'Market Making Compensation', - description: ( - <ul> - <li>Receive an infrastructure grant of $20,000+ for completing onboarding*</li> - <li>Earn an additional $5,000 by referring other market makers to the Program*</li> - </ul> - ), - }, - { - icon: 'getInTouch', - title: 'Personalized Support', - description: - 'The 0x MM Success Manager will walk you through how to read 0x order types, spin up an Ethereum node, set up your MM bot, and execute trades on the blockchain. We are more than happy to promptly answer your questions and give you complete onboarding assistance.', - }, -]; +interface OfferData { + icon: string; + title: string; + description: string; + links?: Action[]; +} +export interface NextMarketMakerProps {} -export class NextMarketMaker extends React.Component { +export class NextMarketMaker extends React.Component<NextMarketMakerProps> { public state = { isContactModalOpen: false, }; + + private readonly _offersData: OfferData[]; + + constructor(props: NextMarketMakerProps) { + super(props); + this._offersData = [ + { + icon: 'supportForAllEthereumStandards', + title: 'Comprehensive Tutorials', + description: + 'Stay on the bleeding edge of crypto by learning how to market make on decentralized exchanges. The network of 0x relayers provides market makers a first-mover advantage to capture larger spreads, find arbitrage opportunities, and trade on new types of exchanges like prediction markets and non-fungible token marketplaces.', + links: [ + { + label: 'Explore the Docs', + url: `${WebsitePaths.Wiki}#Market-Making-on-0x`, + }, + ], + }, + { + icon: 'generateRevenueForYourBusiness-large', + title: 'Market Making Compensation', + description: 'Accepted applicants can receive up to $15,000 for completing onboarding', + }, + { + icon: 'getInTouch', + title: 'Dedicated Support', + description: + 'The 0x team will provide 1:1 onboarding assistance and promptly answer all your questions. They will walk you through the tutorials so that you know how to read 0x order types, spin up an Ethereum node, and execute trades on the blockchain.', + links: [ + { + label: 'Contact Us', + onClick: this._onOpenContactModal, + shouldUseAnchorTag: true, + }, + ], + }, + ]; + } + public render(): React.ReactNode { return ( <SiteWrap theme="light"> @@ -47,14 +72,14 @@ export class NextMarketMaker extends React.Component { isLargeTitle={false} isFullWidth={false} isCenteredMobile={false} - title="Bring liquidity to the exchanges of the future" - description="Market makers (MMs) are important stakeholders in the 0x ecosystem. The Market Making Program provides a set of resources that help onboard MMs bring liquidity to the 0x network. The program includes tutorials, a robust data platform, trade compensation, and 1:1 support from our MM Success Manager." - actions={<HeroActions />} + title="Bring liquidity to the markets of the future" + description="Market makers (MMs) are important stakeholders in the 0x ecosystem. The Market Making Program provides a set of resources that help onboard MMs to bring liquidity to the 0x network. The Program includes tutorials, monetary incentives, and 1:1 support from the 0x team." + actions={this._renderHeroActions()} /> <Section bgColor="light" isFlex={true} maxWidth="1170px"> <Definition - title="Secure" + title="Secure Trading" titleSize="small" description="Take full custody of your assets to eliminate counterparty risk" icon="secureTrading" @@ -74,7 +99,7 @@ export class NextMarketMaker extends React.Component { <Definition title="Low Cost" titleSize="small" - description="Pay no fees on orders except for bulk cancellations" + description="Pay no gas fees to make 0x orders" icon="low-cost" iconSize="medium" isInline={true} @@ -82,7 +107,7 @@ export class NextMarketMaker extends React.Component { </Section> <Section> - {_.map(offersData, (item, index) => ( + {_.map(this._offersData, (item, index) => ( <Definition key={`offers-${index}`} icon={item.icon} @@ -91,34 +116,47 @@ export class NextMarketMaker extends React.Component { isInlineIcon={true} iconSize={240} fontSize="medium" + actions={item.links} /> ))} </Section> <Banner - heading="Need more flexibility?" - subline="Dive into our docs, or contact us if needed" - mainCta={{ text: 'Explore the Docs', href: '/docs' }} - secondaryCta={{ text: 'Get in Touch', onClick: this._onOpenContactModal.bind(this) }} + heading="Start trading today." + subline="Check out our Market Making tutorials to get started" + mainCta={{ text: 'Tutorials', href: `${WebsitePaths.Wiki}#Market-Making-on-0x` }} + secondaryCta={{ text: 'Apply Now', onClick: this._onOpenContactModal }} + /> + <ModalContact + isOpen={this.state.isContactModalOpen} + onDismiss={this._onDismissContactModal} + modalContactType={ModalContactType.MarketMaker} /> - <ModalContact isOpen={this.state.isContactModalOpen} onDismiss={this._onDismissContactModal} /> </SiteWrap> ); } - public _onOpenContactModal = (): void => { + private readonly _onOpenContactModal = (): void => { this.setState({ isContactModalOpen: true }); }; - public _onDismissContactModal = (): void => { + private readonly _onDismissContactModal = (): void => { this.setState({ isContactModalOpen: false }); }; -} -const HeroActions = () => ( - <> - <Button href="https://github.com/0xProject/0x-launch-kit" bgColor="dark" isInline={true}> - Get Started - </Button> - </> -); + private readonly _renderHeroActions = () => ( + <> + <Button href={`${WebsitePaths.Wiki}#Market-Making-on-0x`} bgColor="dark" isInline={true}> + Get Started + </Button> + <Button + onClick={this._onOpenContactModal} + borderColor={opacify(0.4)(colors.brandDark)} + isTransparent={true} + isInline={true} + > + Apply Now + </Button> + </> + ); +} diff --git a/packages/website/ts/utils/configs.ts b/packages/website/ts/utils/configs.ts index fab382b07..7cc854ca0 100644 --- a/packages/website/ts/utils/configs.ts +++ b/packages/website/ts/utils/configs.ts @@ -5,8 +5,8 @@ const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs'; export const configs = { AMOUNT_DISPLAY_PRECSION: 5, - BACKEND_BASE_PROD_URL: 'https://website-api.0xproject.com', - BACKEND_BASE_STAGING_URL: 'https://staging-website-api.0xproject.com', + BACKEND_BASE_PROD_URL: 'https://website-api.0x.org', + BACKEND_BASE_STAGING_URL: 'https://staging-website-api.0x.org', BASE_URL, BITLY_ACCESS_TOKEN: 'ffc4c1a31e5143848fb7c523b39f91b9b213d208', DEFAULT_DERIVATION_PATH: `44'/60'/0'`, @@ -39,8 +39,8 @@ export const configs = { ] as OutdatedWrappedEtherByNetworkId[], // The order matters. We first try first node and only then fall back to others. PUBLIC_NODE_URLS_BY_NETWORK_ID: { - [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`, 'https://mainnet.0xproject.com'], - [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`, 'https://kovan.0xproject.com'], + [1]: [`https://mainnet.infura.io/${INFURA_API_KEY}`, 'https://mainnet.0x.org'], + [42]: [`https://kovan.infura.io/${INFURA_API_KEY}`, 'https://kovan.0x.org'], [3]: [`https://ropsten.infura.io/${INFURA_API_KEY}`], [4]: [`https://rinkeby.infura.io/${INFURA_API_KEY}`], } as PublicNodeUrlsByNetworkId, |