diff options
69 files changed, 1752 insertions, 84 deletions
diff --git a/contracts/core/compiler.json b/contracts/core/compiler.json index ea9e43f12..1ffc26b05 100644 --- a/contracts/core/compiler.json +++ b/contracts/core/compiler.json @@ -25,6 +25,7 @@ "DummyERC721Token", "DummyMultipleReturnERC20Token", "DummyNoReturnERC20Token", + "DutchAuction", "ERC20Proxy", "ERC20Token", "ERC721Token", diff --git a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol new file mode 100644 index 000000000..abe8309cf --- /dev/null +++ b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol @@ -0,0 +1,205 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "../../protocol/Exchange/libs/LibOrder.sol"; +import "../../tokens/ERC20Token/IERC20Token.sol"; +import "../../utils/LibBytes/LibBytes.sol"; +import "../../utils/SafeMath/SafeMath.sol"; + + +contract DutchAuction is + SafeMath +{ + using LibBytes for bytes; + + // solhint-disable var-name-mixedcase + IExchange internal EXCHANGE; + + struct AuctionDetails { + uint256 beginTimeSeconds; // Auction begin unix timestamp: sellOrder.makerAssetData + uint256 endTimeSeconds; // Auction end unix timestamp: sellOrder.expiryTimeSeconds + uint256 beginAmount; // Auction begin amount: sellOrder.makerAssetData + uint256 endAmount; // Auction end amount: sellOrder.takerAssetAmount + uint256 currentAmount; // Calculated amount given block.timestamp + uint256 currentTimeSeconds; // block.timestamp + } + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + } + + /// @dev 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. + /// sellOrder.expiryTimeSeconds is the end time of the auction. + /// sellOrder.takerAssetAmount is the end amount of the auction (lowest possible amount). + /// sellOrder.makerAssetData is the ABI encoded Asset Proxy data with the following data appended + /// buyOrder.makerAssetData is the buyers bid on the auction, must meet the amount for the current block timestamp + /// (uint256 beginTimeSeconds, uint256 beginAmount). + /// This function reverts in the following scenarios: + /// * Auction has not started (auctionDetails.currentTimeSeconds < auctionDetails.beginTimeSeconds) + /// * Auction has expired (auctionDetails.endTimeSeconds < auctionDetails.currentTimeSeconds) + /// * Amount is invalid: Buy order amount is too low (buyOrder.makerAssetAmount < auctionDetails.currentAmount) + /// * Amount is invalid: Invalid begin amount (auctionDetails.beginAmount > auctionDetails.endAmount) + /// * Any failure in the 0x Match Orders + /// @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 buySignature Proof that order was created by the buyer. + /// @param sellSignature Proof that order was created by the seller. + /// @return matchedFillResults amounts filled and fees paid by maker and taker of matched orders. + function matchOrders( + LibOrder.Order memory buyOrder, + LibOrder.Order memory sellOrder, + bytes memory buySignature, + bytes memory sellSignature + ) + public + returns (LibFillResults.MatchedFillResults memory matchedFillResults) + { + AuctionDetails memory auctionDetails = getAuctionDetails(sellOrder); + // Ensure the auction has not yet started + require( + auctionDetails.currentTimeSeconds >= auctionDetails.beginTimeSeconds, + "AUCTION_NOT_STARTED" + ); + // Ensure the auction has not expired. This will fail later in 0x but we can save gas by failing early + require( + sellOrder.expirationTimeSeconds > auctionDetails.currentTimeSeconds, + "AUCTION_EXPIRED" + ); + // Validate the buyer amount is greater than the current auction amount + require( + buyOrder.makerAssetAmount >= auctionDetails.currentAmount, + "INVALID_AMOUNT" + ); + // Match orders, maximally filling `buyOrder` + matchedFillResults = EXCHANGE.matchOrders( + buyOrder, + sellOrder, + buySignature, + sellSignature + ); + // The difference in sellOrder.takerAssetAmount and current amount is given as spread to the matcher + // This may include additional spread from the buyOrder.makerAssetAmount and the currentAmount. + // e.g currentAmount is 30, sellOrder.takerAssetAmount is 10 and buyOrder.makerAssetamount is 40. + // 10 (40-30) is returned to the buyer, 20 (30-10) sent to the seller and 10 has previously + // been transferred to the seller during matchOrders + uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; + if (leftMakerAssetSpreadAmount > 0) { + // ERC20 Asset data itself is encoded as follows: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 1 * 32 | function parameters: | + // | | 4 | 12 | 1. token address padding | + // | | 16 | 20 | 2. token address | + bytes memory assetData = sellOrder.takerAssetData; + address token = assetData.readAddress(16); + // Calculate the excess from the buy order. This can occur if the buyer sends in a higher + // amount than the calculated current amount + uint256 buyerExcessAmount = safeSub(buyOrder.makerAssetAmount, auctionDetails.currentAmount); + uint256 sellerExcessAmount = safeSub(leftMakerAssetSpreadAmount, buyerExcessAmount); + // Return the difference between auctionDetails.currentAmount and sellOrder.takerAssetAmount + // to the seller + if (sellerExcessAmount > 0) { + IERC20Token(token).transfer(sellOrder.makerAddress, sellerExcessAmount); + } + // Return the difference between buyOrder.makerAssetAmount and auctionDetails.currentAmount + // to the buyer + if (buyerExcessAmount > 0) { + IERC20Token(token).transfer(buyOrder.makerAddress, buyerExcessAmount); + } + } + return matchedFillResults; + } + + /// @dev Calculates the Auction Details for the given order + /// @param order The sell order + /// @return AuctionDetails + function getAuctionDetails( + LibOrder.Order memory order + ) + public + returns (AuctionDetails memory auctionDetails) + { + uint256 makerAssetDataLength = order.makerAssetData.length; + // It is unknown the encoded data of makerAssetData, we assume the last 64 bytes + // are the Auction Details encoding. + // Auction Details is encoded as follows: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Params | | 2 * 32 | parameters: | + // | | -64 | 32 | 1. auction begin unix timestamp | + // | | -32 | 32 | 2. auction begin begin amount | + // ERC20 asset data length is 4+32, 64 for auction details results in min length 100 + require( + makerAssetDataLength >= 100, + "INVALID_ASSET_DATA" + ); + uint256 auctionBeginTimeSeconds = order.makerAssetData.readUint256(makerAssetDataLength - 64); + uint256 auctionBeginAmount = order.makerAssetData.readUint256(makerAssetDataLength - 32); + // Ensure the auction has a valid begin time + require( + order.expirationTimeSeconds > auctionBeginTimeSeconds, + "INVALID_BEGIN_TIME" + ); + uint256 auctionDurationSeconds = order.expirationTimeSeconds-auctionBeginTimeSeconds; + // Ensure the auction goes from high to low + uint256 minAmount = order.takerAssetAmount; + require( + auctionBeginAmount > minAmount, + "INVALID_AMOUNT" + ); + uint256 amountDelta = auctionBeginAmount-minAmount; + // solhint-disable-next-line not-rely-on-time + uint256 timestamp = block.timestamp; + auctionDetails.beginTimeSeconds = auctionBeginTimeSeconds; + auctionDetails.endTimeSeconds = order.expirationTimeSeconds; + auctionDetails.beginAmount = auctionBeginAmount; + auctionDetails.endAmount = minAmount; + auctionDetails.currentTimeSeconds = timestamp; + + uint256 remainingDurationSeconds = order.expirationTimeSeconds-timestamp; + if (timestamp < auctionBeginTimeSeconds) { + // If the auction has not yet begun the current amount is the auctionBeginAmount + auctionDetails.currentAmount = auctionBeginAmount; + } else if (timestamp >= order.expirationTimeSeconds) { + // If the auction has ended the current amount is the minAmount. + // Auction end time is guaranteed by 0x Exchange due to the order expiration + auctionDetails.currentAmount = minAmount; + } else { + auctionDetails.currentAmount = safeAdd( + minAmount, + safeDiv( + safeMul(remainingDurationSeconds, amountDelta), + auctionDurationSeconds + ) + ); + } + return auctionDetails; + } +} diff --git a/contracts/core/package.json b/contracts/core/package.json index b1249a4b0..504f479bf 100644 --- a/contracts/core/package.json +++ b/contracts/core/package.json @@ -33,7 +33,7 @@ "lint-contracts": "solhint contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiAssetProxy|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json" + "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|DutchAuction|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiAssetProxy|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", diff --git a/contracts/core/src/artifacts/index.ts b/contracts/core/src/artifacts/index.ts index e2161f3f9..f217adb8e 100644 --- a/contracts/core/src/artifacts/index.ts +++ b/contracts/core/src/artifacts/index.ts @@ -6,6 +6,7 @@ import * as DummyERC721Receiver from '../../generated-artifacts/DummyERC721Recei import * as DummyERC721Token from '../../generated-artifacts/DummyERC721Token.json'; import * as DummyMultipleReturnERC20Token from '../../generated-artifacts/DummyMultipleReturnERC20Token.json'; import * as DummyNoReturnERC20Token from '../../generated-artifacts/DummyNoReturnERC20Token.json'; +import * as DutchAuction from '../../generated-artifacts/DutchAuction.json'; import * as ERC20Proxy from '../../generated-artifacts/ERC20Proxy.json'; import * as ERC20Token from '../../generated-artifacts/ERC20Token.json'; import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; @@ -43,6 +44,7 @@ export const artifacts = { DummyERC721Token: DummyERC721Token as ContractArtifact, DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact, DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact, + DutchAuction: DutchAuction as ContractArtifact, ERC20Proxy: ERC20Proxy as ContractArtifact, ERC20Token: ERC20Token as ContractArtifact, ERC721Proxy: ERC721Proxy as ContractArtifact, diff --git a/contracts/core/src/wrappers/index.ts b/contracts/core/src/wrappers/index.ts index 2a73b37ff..06a139209 100644 --- a/contracts/core/src/wrappers/index.ts +++ b/contracts/core/src/wrappers/index.ts @@ -4,6 +4,7 @@ export * from '../../generated-wrappers/dummy_erc721_receiver'; export * from '../../generated-wrappers/dummy_erc721_token'; export * from '../../generated-wrappers/dummy_multiple_return_erc20_token'; export * from '../../generated-wrappers/dummy_no_return_erc20_token'; +export * from '../../generated-wrappers/dutch_auction'; export * from '../../generated-wrappers/erc20_proxy'; export * from '../../generated-wrappers/erc721_proxy'; export * from '../../generated-wrappers/erc20_token'; diff --git a/contracts/core/test/extensions/dutch_auction.ts b/contracts/core/test/extensions/dutch_auction.ts new file mode 100644 index 000000000..c133d8c60 --- /dev/null +++ b/contracts/core/test/extensions/dutch_auction.ts @@ -0,0 +1,486 @@ +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils'; +import { RevertReason, SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; +import ethAbi = require('ethereumjs-abi'); +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; +import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; +import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction'; +import { ExchangeContract } from '../../generated-wrappers/exchange'; +import { WETH9Contract } from '../../generated-wrappers/weth9'; +import { artifacts } from '../../src/artifacts'; +import { expectTransactionFailedAsync } from '../utils/assertions'; +import { getLatestBlockTimestampAsync } from '../utils/block_timestamp'; +import { chaiSetup } from '../utils/chai_setup'; +import { constants } from '../utils/constants'; +import { ERC20Wrapper } from '../utils/erc20_wrapper'; +import { ERC721Wrapper } from '../utils/erc721_wrapper'; +import { ExchangeWrapper } from '../utils/exchange_wrapper'; +import { OrderFactory } from '../utils/order_factory'; +import { ContractName, ERC20BalancesByOwner } from '../utils/types'; +import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +const DECIMALS_DEFAULT = 18; + +describe(ContractName.DutchAuction, () => { + let makerAddress: string; + let owner: string; + let takerAddress: string; + let feeRecipientAddress: string; + let defaultMakerAssetAddress: string; + + let zrxToken: DummyERC20TokenContract; + let erc20TokenA: DummyERC20TokenContract; + let erc721Token: DummyERC721TokenContract; + let dutchAuctionContract: DutchAuctionContract; + let wethContract: WETH9Contract; + + let sellerOrderFactory: OrderFactory; + let buyerOrderFactory: OrderFactory; + let erc20Wrapper: ERC20Wrapper; + let erc20Balances: ERC20BalancesByOwner; + let currentBlockTimestamp: number; + let auctionBeginTimeSeconds: BigNumber; + let auctionEndTimeSeconds: BigNumber; + let auctionBeginAmount: BigNumber; + let auctionEndAmount: BigNumber; + let sellOrder: SignedOrder; + let buyOrder: SignedOrder; + let erc721MakerAssetIds: BigNumber[]; + const tenMinutesInSeconds = 10 * 60; + + async function increaseTimeAsync(): Promise<void> { + const timestampBefore = await getLatestBlockTimestampAsync(); + await web3Wrapper.increaseTimeAsync(5); + const timestampAfter = await getLatestBlockTimestampAsync(); + // HACK send some transactions when a time increase isn't supported + if (timestampAfter === timestampBefore) { + await web3Wrapper.sendTransactionAsync({ to: makerAddress, from: makerAddress, value: new BigNumber(1) }); + } + } + + function extendMakerAssetData(makerAssetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string { + return ethUtil.bufferToHex( + Buffer.concat([ + ethUtil.toBuffer(makerAssetData), + ethUtil.toBuffer( + (ethAbi as any).rawEncode( + ['uint256', 'uint256'], + [beginTimeSeconds.toString(), beginAmount.toString()], + ), + ), + ]), + ); + } + + before(async () => { + await blockchainLifecycle.startAsync(); + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts); + + erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); + + const numDummyErc20ToDeploy = 2; + [erc20TokenA, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + numDummyErc20ToDeploy, + constants.DUMMY_TOKEN_DECIMALS, + ); + const erc20Proxy = await erc20Wrapper.deployProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + + const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); + [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); + const erc721Proxy = await erc721Wrapper.deployProxyAsync(); + await erc721Wrapper.setBalancesAndAllowancesAsync(); + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; + + wethContract = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults); + erc20Wrapper.addDummyTokenContract(wethContract as any); + + const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); + const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( + artifacts.Exchange, + provider, + txDefaults, + zrxAssetData, + ); + const exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider); + await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, { + from: owner, + }); + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, { + from: owner, + }); + + const dutchAuctionInstance = await DutchAuctionContract.deployFrom0xArtifactAsync( + artifacts.DutchAuction, + provider, + txDefaults, + exchangeInstance.address, + ); + dutchAuctionContract = new DutchAuctionContract( + dutchAuctionInstance.abi, + dutchAuctionInstance.address, + provider, + ); + + defaultMakerAssetAddress = erc20TokenA.address; + const defaultTakerAssetAddress = wethContract.address; + + // Set up taker WETH balance and allowance + await web3Wrapper.awaitTransactionSuccessAsync( + await wethContract.deposit.sendTransactionAsync({ + from: takerAddress, + value: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), DECIMALS_DEFAULT), + }), + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await wethContract.approve.sendTransactionAsync( + erc20Proxy.address, + constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + { from: takerAddress }, + ), + ); + web3Wrapper.abiDecoder.addABI(exchangeInstance.abi); + web3Wrapper.abiDecoder.addABI(zrxToken.abi); + erc20Wrapper.addTokenOwnerAddress(dutchAuctionContract.address); + + currentBlockTimestamp = await getLatestBlockTimestampAsync(); + // Default auction begins 10 minutes ago + auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp).minus(tenMinutesInSeconds); + // Default auction ends 10 from now + auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp).plus(tenMinutesInSeconds); + auctionBeginAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT); + auctionEndAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT); + + // Default sell order and buy order are exact mirrors + const sellerDefaultOrderParams = { + salt: generatePseudoRandomSalt(), + exchangeAddress: exchangeInstance.address, + makerAddress, + feeRecipientAddress, + // taker address or sender address should be set to the ducth auction contract + takerAddress: dutchAuctionContract.address, + makerAssetData: extendMakerAssetData( + assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + auctionBeginTimeSeconds, + auctionBeginAmount, + ), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT), + takerAssetAmount: auctionEndAmount, + expirationTimeSeconds: auctionEndTimeSeconds, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }; + // Default buy order is for the auction begin price + const buyerDefaultOrderParams = { + ...sellerDefaultOrderParams, + makerAddress: takerAddress, + makerAssetData: sellerDefaultOrderParams.takerAssetData, + takerAssetData: sellerDefaultOrderParams.makerAssetData, + makerAssetAmount: auctionBeginAmount, + takerAssetAmount: sellerDefaultOrderParams.makerAssetAmount, + }; + const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; + const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; + sellerOrderFactory = new OrderFactory(makerPrivateKey, sellerDefaultOrderParams); + buyerOrderFactory = new OrderFactory(takerPrivateKey, buyerDefaultOrderParams); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + erc20Balances = await erc20Wrapper.getBalancesAsync(); + sellOrder = await sellerOrderFactory.newSignedOrderAsync(); + buyOrder = await buyerOrderFactory.newSignedOrderAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('matchOrders', () => { + it('should be worth the begin price at the begining of the auction', async () => { + auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2); + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + makerAssetData: extendMakerAssetData( + assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + auctionBeginTimeSeconds, + auctionBeginAmount, + ), + }); + const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount); + expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); + }); + it('should be be worth the end price at the end of the auction', async () => { + auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); + auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + makerAssetData: extendMakerAssetData( + assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + auctionBeginTimeSeconds, + auctionBeginAmount, + ), + expirationTimeSeconds: auctionEndTimeSeconds, + }); + const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount); + expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); + }); + it('should match orders at current amount and send excess to buyer', async () => { + const beforeAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + buyOrder = await buyerOrderFactory.newSignedOrderAsync({ + makerAssetAmount: beforeAuctionDetails.currentAmount.times(2), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ), + ); + const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal( + constants.ZERO_AMOUNT, + ); + // HACK gte used here due to a bug in ganache where the timestamp can change + // between multiple calls to the same block. Which can move the amount in our case + // ref: https://github.com/trufflesuite/ganache-core/issues/111 + expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( + erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), + ); + expect(newBalances[takerAddress][wethContract.address]).to.be.bignumber.gte( + erc20Balances[takerAddress][wethContract.address].minus(beforeAuctionDetails.currentAmount), + ); + }); + it('should have valid getAuctionDetails at some block in the future', async () => { + let auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const beforeAmount = auctionDetails.currentAmount; + await increaseTimeAsync(); + auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const currentAmount = auctionDetails.currentAmount; + expect(beforeAmount).to.be.bignumber.greaterThan(currentAmount); + + buyOrder = await buyerOrderFactory.newSignedOrderAsync({ + makerAssetAmount: currentAmount, + }); + const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + // HACK geth seems to miscalculate the gas required intermittently + gas: 400000, + }, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.equal( + erc20Balances[makerAddress][wethContract.address].plus(currentAmount), + ); + }); + it('maker fees on sellOrder are paid to the fee receipient', async () => { + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + makerFee: new BigNumber(1), + }); + const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( + erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), + ); + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].plus(sellOrder.makerFee), + ); + }); + it('maker fees on buyOrder are paid to the fee receipient', async () => { + buyOrder = await buyerOrderFactory.newSignedOrderAsync({ + makerFee: new BigNumber(1), + }); + const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + const newBalances = await erc20Wrapper.getBalancesAsync(); + const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( + erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), + ); + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].plus(buyOrder.makerFee), + ); + }); + it('should revert when auction expires', async () => { + auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); + auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + expirationTimeSeconds: auctionEndTimeSeconds, + makerAssetData: extendMakerAssetData( + assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + auctionBeginTimeSeconds, + auctionBeginAmount, + ), + }); + return expectTransactionFailedAsync( + dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ), + RevertReason.AuctionExpired, + ); + }); + it('cannot be filled for less than the current price', async () => { + await increaseTimeAsync(); + buyOrder = await buyerOrderFactory.newSignedOrderAsync({ + makerAssetAmount: sellOrder.takerAssetAmount, + }); + return expectTransactionFailedAsync( + dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ), + RevertReason.AuctionInvalidAmount, + ); + }); + it('auction begin amount must be higher than final amount ', async () => { + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + takerAssetAmount: auctionBeginAmount.plus(1), + }); + return expectTransactionFailedAsync( + dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ), + RevertReason.AuctionInvalidAmount, + ); + }); + it('begin time is less than end time', async () => { + auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds); + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + expirationTimeSeconds: auctionEndTimeSeconds, + makerAssetData: extendMakerAssetData( + assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + auctionBeginTimeSeconds, + auctionBeginAmount, + ), + }); + return expectTransactionFailedAsync( + dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ), + RevertReason.AuctionInvalidBeginTime, + ); + }); + it('asset data contains auction parameters', async () => { + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + }); + return expectTransactionFailedAsync( + dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ), + RevertReason.InvalidAssetData, + ); + }); + describe('ERC721', () => { + it('should match orders when ERC721', async () => { + const makerAssetId = erc721MakerAssetIds[0]; + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ + makerAssetAmount: new BigNumber(1), + makerAssetData: extendMakerAssetData( + assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + auctionBeginTimeSeconds, + auctionBeginAmount, + ), + }); + buyOrder = await buyerOrderFactory.newSignedOrderAsync({ + takerAssetAmount: new BigNumber(1), + takerAssetData: sellOrder.makerAssetData, + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from: takerAddress, + }, + ), + ); + const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const newBalances = await erc20Wrapper.getBalancesAsync(); + // HACK gte used here due to a bug in ganache where the timestamp can change + // between multiple calls to the same block. Which can move the amount in our case + // ref: https://github.com/trufflesuite/ganache-core/issues/111 + expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( + erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), + ); + const newOwner = await erc721Token.ownerOf.callAsync(makerAssetId); + expect(newOwner).to.be.bignumber.equal(takerAddress); + }); + }); + }); +}); diff --git a/contracts/core/tsconfig.json b/contracts/core/tsconfig.json index be552e5a3..57ab35732 100644 --- a/contracts/core/tsconfig.json +++ b/contracts/core/tsconfig.json @@ -13,6 +13,7 @@ "./generated-artifacts/DummyERC721Token.json", "./generated-artifacts/DummyMultipleReturnERC20Token.json", "./generated-artifacts/DummyNoReturnERC20Token.json", + "./generated-artifacts/DutchAuction.json", "./generated-artifacts/ERC20Proxy.json", "./generated-artifacts/ERC20Token.json", "./generated-artifacts/ERC721Proxy.json", diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts index 9fc9e1570..d738fcd4e 100644 --- a/contracts/test-utils/src/types.ts +++ b/contracts/test-utils/src/types.ts @@ -86,6 +86,7 @@ export enum ContractName { ZRXToken = 'ZRXToken', DummyERC20Token = 'DummyERC20Token', EtherToken = 'WETH9', + DutchAuction = 'DutchAuction', AssetProxyOwner = 'AssetProxyOwner', AccountLevels = 'AccountLevels', EtherDelta = 'EtherDelta', diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 28d3270e8..4ff83018e 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,5 +1,13 @@ [ { + "version": "3.0.3", + "changes": [ + { + "note": "Update SRA order provider to include Dai" + } + ] + }, + { "timestamp": 1543401373, "version": "3.0.2", "changes": [ diff --git a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts index be1fc55d6..813c9923b 100644 --- a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts +++ b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts @@ -100,6 +100,12 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider { } catch (err) { throw new Error(AssetBuyerError.StandardRelayerApiError); } - return _.map(response.records, item => item.assetDataB.assetData); + return _.map(response.records, item => { + if (item.assetDataA.assetData === takerAssetData) { + return item.assetDataB.assetData; + } else { + return item.assetDataA.assetData; + } + }); } } diff --git a/packages/instant/.dogfood.discharge.json b/packages/instant/.dogfood.discharge.json index b0e4edaff..5d6a09a16 100644 --- a/packages/instant/.dogfood.discharge.json +++ b/packages/instant/.dogfood.discharge.json @@ -1,6 +1,6 @@ { "domain": "0x-instant-dogfood", - "build_command": "WEBPACK_OUTPUT_PATH=public yarn build --env.discharge_target=dogfood", + "build_command": "WEBPACK_OUTPUT_PATH=public dotenv yarn build --env.discharge_target=dogfood", "upload_directory": "public", "index_key": "index.html", "error_key": "index.html", diff --git a/packages/instant/.env_example b/packages/instant/.env_example new file mode 100644 index 000000000..234e64bbe --- /dev/null +++ b/packages/instant/.env_example @@ -0,0 +1,7 @@ +INSTANT_ROLLBAR_PUBLISH_TOKEN= +INSTANT_ROLLBAR_CLIENT_TOKEN= +INSTANT_HEAP_ANALYTICS_ID_PRODUCTION= +INSTANT_HEAP_ANALYTICS_ID_DEVELOPMENT= +# if you want to report to heap or rollbar when building in development mode, you can use the following: +# INSTANT_HEAP_FORCE_DEVELOPMENT=true +# INSTANT_ROLLBAR_FORCE_DEVELOPMENT=true
\ No newline at end of file diff --git a/packages/instant/.gitignore b/packages/instant/.gitignore index a99cea187..2e65f192d 100644 --- a/packages/instant/.gitignore +++ b/packages/instant/.gitignore @@ -1,3 +1,4 @@ public/instant.js public/instant.js.map -umd/*
\ No newline at end of file +umd/* +.env
\ No newline at end of file diff --git a/packages/instant/.npmignore b/packages/instant/.npmignore index a4f7810c0..563923652 100644 --- a/packages/instant/.npmignore +++ b/packages/instant/.npmignore @@ -2,4 +2,5 @@ * */ !lib/**/* -!umd/**/*
\ No newline at end of file +!umd/**/* +.env
\ No newline at end of file diff --git a/packages/instant/.production.discharge.json b/packages/instant/.production.discharge.json index 4aa5337ba..947f68b1a 100644 --- a/packages/instant/.production.discharge.json +++ b/packages/instant/.production.discharge.json @@ -1,6 +1,6 @@ { "domain": "instant.0xproject.com", - "build_command": "yarn build --env.discharge_target=production", + "build_command": "dotenv yarn build --env.discharge_target=production", "upload_directory": "umd", "index_key": "instant.js", "error_key": "404.html", diff --git a/packages/instant/.staging.discharge.json b/packages/instant/.staging.discharge.json index 56ffee4e9..bd5f28ba8 100644 --- a/packages/instant/.staging.discharge.json +++ b/packages/instant/.staging.discharge.json @@ -1,6 +1,6 @@ { "domain": "0x-instant-staging", - "build_command": "WEBPACK_OUTPUT_PATH=public yarn build --env.discharge_target=staging", + "build_command": "dotenv WEBPACK_OUTPUT_PATH=public yarn build --env.discharge_target=staging", "upload_directory": "public", "index_key": "index.html", "error_key": "index.html", diff --git a/packages/instant/README.md b/packages/instant/README.md index 45a871124..2092b45d9 100644 --- a/packages/instant/README.md +++ b/packages/instant/README.md @@ -20,6 +20,8 @@ The package is available as a UMD module named `zeroExInstant` at https://instan ## Deploying +To run any of the following commands you need to configure your `.env` file. There is an example `.env_example` file to show you what values are required. + You can deploy a work-in-progress version of 0x Instant at http://0x-instant-dogfood.s3-website-us-east-1.amazonaws.com/instant.js for easy sharing. To build and deploy the bundle run diff --git a/packages/instant/package.json b/packages/instant/package.json index 62904949b..7d0bf6bec 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -7,7 +7,6 @@ "private": true, "description": "0x Instant React Component", "main": "umd/instant.js", - "private": true, "scripts": { "build": "webpack --mode production", "build:ci": "yarn build", @@ -25,7 +24,10 @@ }, "config": { "postpublish": { - "assets": ["packages/instant/umd/instant.js", "packages/instant/umd/instant.js.map"] + "assets": [ + "packages/instant/umd/instant.js", + "packages/instant/umd/instant.js.map" + ] } }, "repository": { @@ -58,6 +60,7 @@ "react-redux": "^5.0.7", "redux": "^4.0.0", "redux-devtools-extension": "^2.13.5", + "rollbar": "^2.5.0", "styled-components": "^4.0.2", "ts-optchain": "^0.1.1" }, @@ -75,6 +78,7 @@ "@types/redux": "^3.6.0", "@types/styled-components": "^4.0.1", "awesome-typescript-loader": "^5.2.1", + "dotenv-cli": "^1.4.0", "enzyme": "^3.6.0", "enzyme-adapter-react-16": "^1.5.0", "ip": "^1.1.5", @@ -82,7 +86,9 @@ "make-promises-safe": "^1.1.0", "npm-run-all": "^4.1.2", "nyc": "^11.0.1", + "rollbar-sourcemap-webpack-plugin": "^2.4.0", "shx": "^0.2.2", + "source-map-loader": "^0.2.4", "svg-react-loader": "^0.4.6", "ts-jest": "^23.10.3", "tslint": "5.11.0", diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html index df39994ef..d10618c58 100644 --- a/packages/instant/public/index.html +++ b/packages/instant/public/index.html @@ -175,6 +175,7 @@ defaultSelectedAssetData: queryParams.getQueryParamValue('defaultSelectedAssetData'), affiliateInfo: affiliateInfoOverride, shouldDisablePushToHistory: !!queryParams.getQueryParamValue('shouldDisablePushToHistory'), + walletDisplayName: queryParams.getQueryParamValue('walletDisplayName') || undefined, }; return renderOptionsOverrides; }; diff --git a/packages/instant/src/assets/icons/zrx.svg b/packages/instant/src/assets/icons/zrx.svg index 07518f551..da623710b 100644 --- a/packages/instant/src/assets/icons/zrx.svg +++ b/packages/instant/src/assets/icons/zrx.svg @@ -1,3 +1,6 @@ -<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M22.6726 18.5002L22.6787 18.5063L22.625 18.5868C22.641 18.558 22.6569 18.5291 22.6726 18.5002V18.5002H22.6726ZM22.7893 18.5002L21.3866 17.0053L17.9692 12.9131L19.6779 11.0919L17.1249 7.89955L18.8337 6.07835L20.0599 4.74999C20.663 5.26419 21.2058 5.83552 21.6882 6.46394C22.1707 7.09242 22.5828 7.77444 22.9245 8.5101C23.2663 9.2457 23.5309 10.0206 23.7186 10.8347C23.9062 11.6489 24 12.4916 24 13.3628C24 14.3055 23.8928 15.216 23.6784 16.0945C23.4703 16.9468 23.174 17.7487 22.7893 18.5L22.7893 18.5002ZM6.74427 15.3604L8.87512 18.1019L7.20654 19.8795L5.94009 21.2502L5.91999 21.2288C5.3169 20.7291 4.77415 20.1651 4.29169 19.5368C3.80923 18.9086 3.39714 18.2268 3.05539 17.4915C2.71365 16.7562 2.45567 15.9816 2.28144 15.1677C2.09382 14.3539 2 13.5114 2 12.6405C2 11.6981 2.10721 10.7913 2.32164 9.92041C2.53608 9.04943 2.83091 8.24276 3.20615 7.50025L4.61334 8.99943L8.45293 13.54L6.7442 15.3605L6.74427 15.3604ZM7.89849 8.87512L6.12088 7.20654L4.75015 5.94009L4.77157 5.91999C5.27132 5.3169 5.83531 4.77415 6.46352 4.29169C7.09178 3.80923 7.77357 3.39714 8.50886 3.05539C9.2442 2.71365 10.0188 2.45567 10.8326 2.28144C11.6465 2.09382 12.489 2 13.3599 2C14.3023 2 15.209 2.10721 16.08 2.32164C16.9509 2.53608 17.7576 2.83091 18.5001 3.20615L17.0866 4.55302L12.9101 8.0307L11.0896 6.32197L7.89835 8.87496L7.89849 8.87512ZM18.1019 17.1252L19.8795 18.7938L21.2503 20.0602L21.2288 20.0803C20.7291 20.6834 20.1651 21.2262 19.5369 21.7086C18.9086 22.1911 18.2268 22.6032 17.4916 22.9449C16.7562 23.2867 15.9817 23.5447 15.1678 23.7189C14.3539 23.9065 13.5114 24.0003 12.6405 24.0003C11.6981 24.0003 10.7914 23.8931 9.92046 23.6787C9.04952 23.4643 8.2428 23.1694 7.50029 22.7942L8.91381 21.4473L13.54 17.5474L15.3606 19.6782L18.1021 17.1252L18.1019 17.1252Z" fill="white"/> +<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M4.62099 13.9044L6.3287 12.1375L4.20565 9.27256L1.50251 5.44771C0.547534 7.07749 0 8.97462 0 11C0 14.3552 1.50251 17.3593 3.8722 19.3768L7.30341 16.9518C6.13632 16.2248 5.19614 15.1662 4.62099 13.9044Z" fill="white"/> +<path d="M8.09561 4.62099L9.86251 6.3287L12.7274 4.20565L16.5523 1.50251C14.9225 0.547534 13.0254 0 11 0C7.64475 0 4.64072 1.50251 2.62323 3.8722L5.04816 7.30341C5.77525 6.13632 6.83381 5.19614 8.09561 4.62099Z" fill="white"/> +<path d="M15.6713 9.86251L17.7943 12.7274L20.4975 16.5523C21.4525 14.9225 22 13.0254 22 11C22 7.64475 20.4975 4.64072 18.1278 2.62323L14.6966 5.04816C15.8637 5.77525 16.8039 6.83381 17.379 8.09561L15.6713 9.86251Z" fill="white"/> +<path d="M19.3768 18.1278L16.9518 14.6966C16.2248 15.8637 15.1662 16.8039 13.9044 17.379L12.1375 15.6713L9.27256 17.7943L5.44771 20.4975C7.07749 21.4525 8.97462 22 11 22C14.3552 22 17.3593 20.4975 19.3768 18.1278Z" fill="white"/> </svg> diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx index eeb42d6fc..1489b94d4 100644 --- a/packages/instant/src/components/buy_button.tsx +++ b/packages/instant/src/components/buy_button.tsx @@ -1,4 +1,5 @@ import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; +import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; @@ -7,7 +8,7 @@ import { oc } from 'ts-optchain'; import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants'; import { ColorOption } from '../style/theme'; -import { AffiliateInfo, ZeroExInstantError } from '../types'; +import { AffiliateInfo, Asset, ZeroExInstantError } from '../types'; import { analytics } from '../util/analytics'; import { gasPriceEstimator } from '../util/gas_price_estimator'; import { util } from '../util/util'; @@ -21,6 +22,7 @@ export interface BuyButtonProps { assetBuyer: AssetBuyer; web3Wrapper: Web3Wrapper; affiliateInfo?: AffiliateInfo; + selectedAsset?: Asset; onValidationPending: (buyQuote: BuyQuote) => void; onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void; onSignatureDenied: (buyQuote: BuyQuote) => void; @@ -36,8 +38,12 @@ export class BuyButton extends React.Component<BuyButtonProps> { onBuyFailure: util.boundNoop, }; public render(): React.ReactNode { - const { buyQuote, accountAddress } = this.props; + const { buyQuote, accountAddress, selectedAsset } = this.props; const shouldDisableButton = _.isUndefined(buyQuote) || _.isUndefined(accountAddress); + const buttonText = + !_.isUndefined(selectedAsset) && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20 + ? `Buy ${selectedAsset.metaData.symbol.toUpperCase()}` + : 'Buy Now'; return ( <Button width="100%" @@ -45,7 +51,7 @@ export class BuyButton extends React.Component<BuyButtonProps> { isDisabled={shouldDisableButton} fontColor={ColorOption.white} > - Buy + {buttonText} </Button> ); } diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx index e563bec73..833818900 100644 --- a/packages/instant/src/components/buy_order_state_buttons.tsx +++ b/packages/instant/src/components/buy_order_state_buttons.tsx @@ -4,7 +4,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper'; import * as React from 'react'; import { ColorOption } from '../style/theme'; -import { AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types'; +import { AffiliateInfo, Asset, OrderProcessState, ZeroExInstantError } from '../types'; import { BuyButton } from './buy_button'; import { PlacingOrderButton } from './placing_order_button'; @@ -21,6 +21,7 @@ export interface BuyOrderStateButtonProps { assetBuyer: AssetBuyer; web3Wrapper: Web3Wrapper; affiliateInfo?: AffiliateInfo; + selectedAsset?: Asset; onViewTransaction: () => void; onValidationPending: (buyQuote: BuyQuote) => void; onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void; @@ -60,6 +61,7 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP assetBuyer={props.assetBuyer} web3Wrapper={props.web3Wrapper} affiliateInfo={props.affiliateInfo} + selectedAsset={props.selectedAsset} onValidationPending={props.onValidationPending} onValidationFail={props.onValidationFail} onSignatureDenied={props.onSignatureDenied} diff --git a/packages/instant/src/components/erc20_asset_amount_input.tsx b/packages/instant/src/components/erc20_asset_amount_input.tsx index ff900842a..4da82eb73 100644 --- a/packages/instant/src/components/erc20_asset_amount_input.tsx +++ b/packages/instant/src/components/erc20_asset_amount_input.tsx @@ -113,7 +113,7 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput ); }; private readonly _renderChevronIcon = (): React.ReactNode => { - if (!this._areMultipleAssetsAvailable()) { + if (!this._areAnyAssetsAvailable()) { return null; } return ( @@ -134,14 +134,14 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput // We don't want to allow opening the token selection panel if there are no assets. // Since styles are inferred from the presence of a click handler, we want to return undefined // instead of providing a noop. - if (!this._areMultipleAssetsAvailable() || _.isUndefined(this.props.onSelectAssetClick)) { + if (!this._areAnyAssetsAvailable() || _.isUndefined(this.props.onSelectAssetClick)) { return undefined; } return this._handleSelectAssetClick; }; - private readonly _areMultipleAssetsAvailable = (): boolean => { + private readonly _areAnyAssetsAvailable = (): boolean => { const { numberOfAssetsAvailable } = this.props; - return !_.isUndefined(numberOfAssetsAvailable) && numberOfAssetsAvailable > 1; + return !_.isUndefined(numberOfAssetsAvailable) && numberOfAssetsAvailable > 0; }; private readonly _handleSelectAssetClick = (): void => { if (this.props.onSelectAssetClick) { diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx index c23b43267..4efe5b28e 100644 --- a/packages/instant/src/components/payment_method.tsx +++ b/packages/instant/src/components/payment_method.tsx @@ -18,7 +18,7 @@ import { WalletPrompt } from './wallet_prompt'; export interface PaymentMethodProps { account: Account; network: Network; - walletName: string; + walletDisplayName: string; onInstallWalletClick: () => void; onUnlockWalletClick: () => void; } @@ -62,11 +62,11 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> { if (account.state === AccountState.Ready || account.state === AccountState.Locked) { const circleColor: ColorOption = account.state === AccountState.Ready ? ColorOption.green : ColorOption.red; return ( - <Flex> + <Flex align="center"> <Circle diameter={8} color={circleColor} /> - <Container marginLeft="3px"> - <Text fontColor={ColorOption.darkGrey} fontSize="12px"> - {this.props.walletName} + <Container marginLeft="5px"> + <Text fontColor={ColorOption.darkGrey} fontSize="12px" lineHeight="30px"> + {this.props.walletDisplayName} </Text> </Container> </Flex> @@ -91,7 +91,7 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> { image={<Icon width={13} icon="lock" color={ColorOption.black} />} {...colors} > - Please Unlock {this.props.walletName} + Please Unlock {this.props.walletDisplayName} </WalletPrompt> ); case AccountState.None: diff --git a/packages/instant/src/components/search_input.tsx b/packages/instant/src/components/search_input.tsx index 3a693b9f8..71bc18915 100644 --- a/packages/instant/src/components/search_input.tsx +++ b/packages/instant/src/components/search_input.tsx @@ -13,10 +13,10 @@ export interface SearchInputProps extends InputProps { } export const SearchInput: React.StatelessComponent<SearchInputProps> = props => ( - <Container backgroundColor={props.backgroundColor} borderRadius="3px" padding=".5em .3em"> - <Flex justify="flex-start" align="flex-end"> - <Icon width={14} height={14} icon="search" color={ColorOption.lightGrey} padding="0px 12px" /> - <Input {...props} fontSize="14px" fontColor={props.fontColor} /> + <Container backgroundColor={props.backgroundColor} borderRadius="3px" padding=".5em .5em"> + <Flex justify="flex-start" align="center"> + <Icon width={14} height={14} icon="search" color={ColorOption.lightGrey} padding="2px 12px" /> + <Input {...props} type="search" fontSize="16px" fontColor={props.fontColor} /> </Flex> </Container> ); diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx index e7d909d92..636eb8fc9 100644 --- a/packages/instant/src/components/ui/container.tsx +++ b/packages/instant/src/components/ui/container.tsx @@ -77,6 +77,7 @@ export const Container = ${props => cssRuleIfExists(props, 'opacity')} ${props => cssRuleIfExists(props, 'cursor')} ${props => cssRuleIfExists(props, 'overflow')} + ${props => (props.overflow === 'scroll' ? `-webkit-overflow-scrolling: touch` : '')}; ${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')}; ${props => props.display && stylesForMedia<string>('display', props.display)} ${props => props.width && stylesForMedia<string>('width', props.width)} diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx index 1ea5d8fe1..863c970ef 100644 --- a/packages/instant/src/components/ui/input.tsx +++ b/packages/instant/src/components/ui/input.tsx @@ -10,6 +10,7 @@ export interface InputProps { fontSize?: string; fontColor?: ColorOption; placeholder?: string; + type?: string; onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; } diff --git a/packages/instant/src/components/ui/overlay.tsx b/packages/instant/src/components/ui/overlay.tsx index f67d6fb2f..7d311dc2f 100644 --- a/packages/instant/src/components/ui/overlay.tsx +++ b/packages/instant/src/components/ui/overlay.tsx @@ -33,7 +33,7 @@ export const Overlay = Overlay.defaultProps = { zIndex: zIndex.overlayDefault, - backgroundColor: generateOverlayBlack(0.6), + backgroundColor: generateOverlayBlack(0.9), }; Overlay.displayName = 'Overlay'; diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index a4a03bbf4..dae9124c6 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -15,6 +15,7 @@ import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource, Quote import { analytics, disableAnalytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; +import { setupRollbar } from '../util/error_reporter'; import { gasPriceEstimator } from '../util/gas_price_estimator'; import { Heartbeater } from '../util/heartbeater'; import { generateAccountHeartbeater, generateBuyQuoteHeartbeater } from '../util/heartbeater_factory'; @@ -29,6 +30,7 @@ export interface ZeroExInstantProviderRequiredProps { export interface ZeroExInstantProviderOptionalProps { provider: Provider; + walletDisplayName: string; availableAssetDatas: string[]; defaultAssetBuyAmount: number; defaultSelectedAssetData: string; @@ -66,6 +68,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider ...defaultState, providerState, network: networkId, + walletDisplayName: props.walletDisplayName, selectedAsset: _.isUndefined(props.defaultSelectedAssetData) ? undefined : assetUtils.createAssetFromAssetDataOrThrow( @@ -86,6 +89,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider } constructor(props: ZeroExInstantProviderProps) { super(props); + setupRollbar(); fonts.include(); const initialAppState = ZeroExInstantProvider._mergeDefaultStateWithProps(this.props); this._store = store.create(initialAppState); diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 0dd770ec6..2439c7349 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -19,9 +19,20 @@ export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6); export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2; export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info'; export const HEAP_ANALYTICS_ID = process.env.HEAP_ANALYTICS_ID; +export const HEAP_ENABLED = process.env.HEAP_ENABLED; export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2'; export const PROGRESS_STALL_AT_WIDTH = '95%'; export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200; +export const HOST_DOMAINS = [ + '0x-instant-staging.s3-website-us-east-1.amazonaws.com', + '0x-instant-dogfood.s3-website-us-east-1.amazonaws.com', + 'localhost', + '127.0.0.1', + '0.0.0.0', + 'instant.0xproject.com', +]; +export const ROLLBAR_CLIENT_TOKEN = process.env.ROLLBAR_CLIENT_TOKEN; +export const ROLLBAR_ENABLED = process.env.ROLLBAR_ENABLED; export const INSTANT_DISCHARGE_TARGET = process.env.INSTANT_DISCHARGE_TARGET as | 'production' | 'dogfood' diff --git a/packages/instant/src/containers/connected_account_payment_method.ts b/packages/instant/src/containers/connected_account_payment_method.ts index e9327a288..bb68fdd57 100644 --- a/packages/instant/src/containers/connected_account_payment_method.ts +++ b/packages/instant/src/containers/connected_account_payment_method.ts @@ -20,6 +20,7 @@ export interface ConnectedAccountPaymentMethodProps {} interface ConnectedState { network: Network; providerState: ProviderState; + walletDisplayName?: string; } interface ConnectedDispatch { @@ -34,6 +35,7 @@ type FinalProps = ConnectedProps & ConnectedAccountPaymentMethodProps; const mapStateToProps = (state: State, _ownProps: ConnectedAccountPaymentMethodProps): ConnectedState => ({ network: state.network, providerState: state.providerState, + walletDisplayName: state.walletDisplayName, }); const mapDispatchToProps = ( @@ -56,7 +58,7 @@ const mergeProps = ( ...ownProps, network: connectedState.network, account: connectedState.providerState.account, - walletName: connectedState.providerState.name, + walletDisplayName: connectedState.walletDisplayName || connectedState.providerState.name, onUnlockWalletClick: () => connectedDispatch.unlockWalletAndDispatchToStore(connectedState.providerState), onInstallWalletClick: () => { const isMobile = envUtil.isMobileOperatingSystem(); diff --git a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts index 0d49edc57..80943a96f 100644 --- a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts +++ b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts @@ -9,7 +9,7 @@ import { Dispatch } from 'redux'; import { BuyOrderStateButtons } from '../components/buy_order_state_buttons'; import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; -import { AccountState, AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types'; +import { AccountState, AffiliateInfo, Asset, OrderProcessState, ZeroExInstantError } from '../types'; import { analytics } from '../util/analytics'; import { errorFlasher } from '../util/error_flasher'; import { etherscanUtil } from '../util/etherscan'; @@ -22,6 +22,7 @@ interface ConnectedState { assetBuyer: AssetBuyer; web3Wrapper: Web3Wrapper; affiliateInfo?: AffiliateInfo; + selectedAsset?: Asset; onViewTransaction: () => void; } @@ -41,6 +42,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButt const account = state.providerState.account; const accountAddress = account.state === AccountState.Ready ? account.address : undefined; const accountEthBalanceInWei = account.state === AccountState.Ready ? account.ethBalanceInWei : undefined; + const selectedAsset = state.selectedAsset; return { accountAddress, accountEthBalanceInWei, @@ -49,6 +51,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButt web3Wrapper, buyQuote: state.latestBuyQuote, affiliateInfo: state.affiliateInfo, + selectedAsset, onViewTransaction: () => { if ( state.buyOrderState.processState === OrderProcessState.Processing || diff --git a/packages/instant/src/data/asset_meta_data_map.ts b/packages/instant/src/data/asset_meta_data_map.ts index b24c9c83d..88611a8c0 100644 --- a/packages/instant/src/data/asset_meta_data_map.ts +++ b/packages/instant/src/data/asset_meta_data_map.ts @@ -83,14 +83,14 @@ export const assetMetaDataMap: ObjectMap<AssetMetaData> = { '0xf47261b0000000000000000000000000e0b7927c4af23765cb51314a0e0521a9645f0e2a': { assetProxyId: AssetProxyId.ERC20, decimals: 9, - primaryColor: '#DEB564', + primaryColor: '#E1AA3E', symbol: 'dgd', name: 'DigixDao', }, '0xf47261b00000000000000000000000004f3afec4e5a3f2a6a1a411def7d7dfe50ee057bf': { assetProxyId: AssetProxyId.ERC20, decimals: 9, - primaryColor: '#DEB564', + primaryColor: '#E1AA3E', symbol: 'dgx', name: 'Digix Gold Token', }, @@ -195,7 +195,7 @@ export const assetMetaDataMap: ObjectMap<AssetMetaData> = { '0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359': { assetProxyId: AssetProxyId.ERC20, decimals: 18, - primaryColor: '#F2B350', + primaryColor: '#DEA349', symbol: 'dai', name: 'Dai Stablecoin', }, diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts index 7391e2844..b92fa3a7c 100644 --- a/packages/instant/src/index.umd.ts +++ b/packages/instant/src/index.umd.ts @@ -39,6 +39,9 @@ const validateInstantRenderConfig = (config: ZeroExInstantConfig, selector: stri if (!_.isUndefined(config.provider)) { assert.isWeb3Provider('provider', config.provider); } + if (!_.isUndefined(config.walletDisplayName)) { + assert.isString('walletDisplayName', config.walletDisplayName); + } if (!_.isUndefined(config.shouldDisablePushToHistory)) { assert.isBoolean('shouldDisablePushToHistory', config.shouldDisablePushToHistory); } diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index 9fdcea3ca..c67b222d1 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -10,6 +10,7 @@ import { assetUtils } from '../util/asset'; import { buyQuoteUpdater } from '../util/buy_quote_updater'; import { coinbaseApi } from '../util/coinbase_api'; import { errorFlasher } from '../util/error_flasher'; +import { errorReporter } from '../util/error_reporter'; import { actions } from './actions'; import { State } from './reducer'; @@ -23,6 +24,7 @@ export const asyncData = { const errorMessage = 'Error fetching ETH/USD price'; errorFlasher.flashNewErrorMessage(dispatch, errorMessage); dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO)); + errorReporter.report(e); } }, fetchAvailableAssetDatasAndDispatchToStore: async (state: State, dispatch: Dispatch) => { @@ -30,13 +32,15 @@ export const asyncData = { const assetBuyer = providerState.assetBuyer; try { const assetDatas = await assetBuyer.getAvailableAssetDatasAsync(); - const assets = assetUtils.createAssetsFromAssetDatas(assetDatas, assetMetaDataMap, network); + const deduplicatedAssetDatas = _.uniq(assetDatas); + const assets = assetUtils.createAssetsFromAssetDatas(deduplicatedAssetDatas, assetMetaDataMap, network); dispatch(actions.setAvailableAssets(assets)); } catch (e) { const errorMessage = 'Could not find any assets'; errorFlasher.flashNewErrorMessage(dispatch, errorMessage); // On error, just specify that none are available dispatch(actions.setAvailableAssets([])); + errorReporter.report(e); } }, fetchAccountInfoAndDispatchToStore: async ( @@ -77,6 +81,7 @@ export const asyncData = { const ethBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(address); dispatch(actions.updateAccountEthBalance({ address, ethBalanceInWei })); } catch (e) { + errorReporter.report(e); // leave balance as is return; } diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index dfc2b89f3..a9a407b7d 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -49,6 +49,7 @@ interface OptionalState { latestBuyQuote: BuyQuote; latestErrorMessage: string; affiliateInfo: AffiliateInfo; + walletDisplayName: string; } export type State = DefaultState & PropsDerivedState & Partial<OptionalState>; diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index e625824ef..760ec8b5c 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -2,7 +2,7 @@ import { BuyQuote } from '@0x/asset-buyer'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; -import { INSTANT_DISCHARGE_TARGET } from '../constants'; +import { HEAP_ENABLED, INSTANT_DISCHARGE_TARGET } from '../constants'; import { AffiliateInfo, Asset, @@ -16,15 +16,17 @@ import { import { EventProperties, heapUtil } from './heap'; -let isDisabled = false; +let isDisabledViaConfig = false; export const disableAnalytics = (shouldDisableAnalytics: boolean) => { - isDisabled = shouldDisableAnalytics; + isDisabledViaConfig = shouldDisableAnalytics; }; export const evaluateIfEnabled = (fnCall: () => void) => { - if (isDisabled) { + if (isDisabledViaConfig) { return; } - fnCall(); + if (HEAP_ENABLED) { + fnCall(); + } }; enum EventNames { diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index 40560d3eb..08f3642e3 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -1,3 +1,4 @@ +import { AssetBuyerError } from '@0x/asset-buyer'; import { AssetProxyId, ObjectMap } from '@0x/types'; import * as _ from 'lodash'; @@ -106,4 +107,20 @@ export const assetUtils = { ); return _.compact(erc20sOrUndefined); }, + assetBuyerErrorMessage: (asset: ERC20Asset, error: Error): string | undefined => { + if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { + const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); + return `Not enough ${assetName} available`; + } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { + return 'Not enough ZRX available'; + } else if ( + error.message === AssetBuyerError.StandardRelayerApiError || + error.message.startsWith(AssetBuyerError.AssetUnavailable) + ) { + const assetName = assetUtils.bestNameForAsset(asset, 'This asset'); + return `${assetName} is currently unavailable`; + } + + return undefined; + }, }; diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index c1899f8c1..4229f2735 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -1,4 +1,4 @@ -import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer'; +import { AssetBuyer, BuyQuote } from '@0x/asset-buyer'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; @@ -10,6 +10,7 @@ import { AffiliateInfo, ERC20Asset, QuoteFetchOrigin } from '../types'; import { analytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; +import { errorReporter } from '../util/error_reporter'; export const buyQuoteUpdater = { updateBuyQuoteAsync: async ( @@ -35,30 +36,18 @@ export const buyQuoteUpdater = { try { newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage }); } catch (error) { + const errorMessage = assetUtils.assetBuyerErrorMessage(asset, error); + + if (_.isUndefined(errorMessage)) { + // This is an unknown error, report it to rollbar + errorReporter.report(error); + } + if (options.dispatchErrors) { dispatch(actions.setQuoteRequestStateFailure()); analytics.trackQuoteError(error.message ? error.message : 'other', baseUnitValue, fetchOrigin); - let errorMessage; - if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { - const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); - errorMessage = `Not enough ${assetName} available`; - } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) { - errorMessage = 'Not enough ZRX available'; - } else if ( - error.message === AssetBuyerError.StandardRelayerApiError || - error.message.startsWith(AssetBuyerError.AssetUnavailable) - ) { - const assetName = assetUtils.bestNameForAsset(asset, 'This asset'); - errorMessage = `${assetName} is currently unavailable`; - } - if (!_.isUndefined(errorMessage)) { - errorFlasher.flashNewErrorMessage(dispatch, errorMessage); - } else { - throw error; - } + errorFlasher.flashNewErrorMessage(dispatch, errorMessage || 'Error fetching price, please try again'); } - // TODO: report to error reporter on else - return; } // We have a successful new buy quote diff --git a/packages/instant/src/util/error_reporter.ts b/packages/instant/src/util/error_reporter.ts new file mode 100644 index 000000000..3ec7b6daa --- /dev/null +++ b/packages/instant/src/util/error_reporter.ts @@ -0,0 +1,62 @@ +import { logUtils } from '@0x/utils'; +import * as _ from 'lodash'; + +import { HOST_DOMAINS, INSTANT_DISCHARGE_TARGET, ROLLBAR_CLIENT_TOKEN, ROLLBAR_ENABLED } from '../constants'; + +// Import version of Rollbar designed for embedded components +// See https://docs.rollbar.com/docs/using-rollbarjs-inside-an-embedded-component +// tslint:disable-next-line:no-var-requires +const Rollbar = require('rollbar/dist/rollbar.noconflict.umd'); + +let rollbar: any; +// Configures rollbar and sets up error catching +export const setupRollbar = (): any => { + if (_.isUndefined(rollbar) && ROLLBAR_CLIENT_TOKEN && ROLLBAR_ENABLED) { + rollbar = new Rollbar({ + accessToken: ROLLBAR_CLIENT_TOKEN, + captureUncaught: true, + captureUnhandledRejections: true, + enabled: true, + itemsPerMinute: 10, + maxItems: 500, + payload: { + environment: INSTANT_DISCHARGE_TARGET || `Local ${process.env.NODE_ENV}`, + client: { + javascript: { + source_map_enabled: true, + code_version: process.env.GIT_SHA, + guess_uncaught_frames: true, + }, + }, + }, + hostWhiteList: HOST_DOMAINS, + uncaughtErrorLevel: 'error', + ignoredMessages: [ + // Errors from the third-party scripts + 'Script error', + // Network errors or ad-blockers + 'TypeError: Failed to fetch', + 'Exchange has not been deployed to detected network (network/artifact mismatch)', + // Source: https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/7VU0_VvC7mE + "undefined is not an object (evaluating '__gCrWeb.autofill.extractForms')", + // Source: http://stackoverflow.com/questions/43399818/securityerror-from-facebook-and-cross-domain-messaging + 'SecurityError (DOM Exception 18)', + ], + }); + } +}; + +export const errorReporter = { + report(err: Error): void { + if (!rollbar) { + logUtils.log('Not reporting to rollbar because not configured', err); + return; + } + + rollbar.error(err, (rollbarErr: Error) => { + if (rollbarErr) { + logUtils.log(`Error reporting to rollbar, ignoring: ${rollbarErr}`); + } + }); + }, +}; diff --git a/packages/instant/src/util/gas_price_estimator.ts b/packages/instant/src/util/gas_price_estimator.ts index 6b15809a3..332c8d00a 100644 --- a/packages/instant/src/util/gas_price_estimator.ts +++ b/packages/instant/src/util/gas_price_estimator.ts @@ -7,6 +7,8 @@ import { GWEI_IN_WEI, } from '../constants'; +import { errorReporter } from './error_reporter'; + interface EthGasStationResult { average: number; fastestWait: number; @@ -42,8 +44,9 @@ export class GasPriceEstimator { let fetchedAmount: GasInfo | undefined; try { fetchedAmount = await fetchFastAmountInWeiAsync(); - } catch { + } catch (e) { fetchedAmount = undefined; + errorReporter.report(e); } if (fetchedAmount) { diff --git a/packages/instant/src/util/heap.ts b/packages/instant/src/util/heap.ts index 7c53c9918..279ff3059 100644 --- a/packages/instant/src/util/heap.ts +++ b/packages/instant/src/util/heap.ts @@ -5,6 +5,7 @@ import * as _ from 'lodash'; import { HEAP_ANALYTICS_ID } from '../constants'; import { AnalyticsEventOptions, AnalyticsUserOptions } from './analytics'; +import { errorReporter } from './error_reporter'; export type EventProperties = ObjectMap<string | number>; @@ -107,8 +108,8 @@ export const heapUtil = { heapFunctionCall(curHeap); } catch (e) { // We never want analytics to crash our React component - // TODO(sk): error reporter here logUtils.log('Analytics error', e); + errorReporter.report(e); } } }, diff --git a/packages/instant/test/util/asset.test.ts b/packages/instant/test/util/asset.test.ts index 4229b24ed..fc4e4e2e4 100644 --- a/packages/instant/test/util/asset.test.ts +++ b/packages/instant/test/util/asset.test.ts @@ -1,6 +1,7 @@ +import { AssetBuyerError } from '@0x/asset-buyer'; import { AssetProxyId, ObjectMap } from '@0x/types'; -import { Asset, AssetMetaData, ERC20AssetMetaData, Network, ZeroExInstantError } from '../../src/types'; +import { Asset, AssetMetaData, ERC20Asset, ERC20AssetMetaData, Network, ZeroExInstantError } from '../../src/types'; import { assetUtils } from '../../src/util/asset'; const ZRX_ASSET_DATA = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; @@ -11,7 +12,7 @@ const ZRX_META_DATA: ERC20AssetMetaData = { decimals: 18, name: '0x', }; -const ZRX_ASSET: Asset = { +const ZRX_ASSET: ERC20Asset = { assetData: ZRX_ASSET_DATA, metaData: ZRX_META_DATA, }; @@ -45,4 +46,32 @@ describe('assetDataUtil', () => { ).toThrowError(ZeroExInstantError.AssetMetaDataNotAvailable); }); }); + describe('assetBuyerErrorMessage', () => { + it('should return message for InsufficientAssetLiquidity', () => { + const insufficientAssetError = new Error(AssetBuyerError.InsufficientAssetLiquidity); + expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientAssetError)).toEqual( + 'Not enough ZRX available', + ); + }); + it('should return message for InsufficientAssetLiquidity', () => { + const insufficientZrxError = new Error(AssetBuyerError.InsufficientZrxLiquidity); + expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientZrxError)).toEqual( + 'Not enough ZRX available', + ); + }); + it('should message for StandardRelayerApiError', () => { + const standardRelayerError = new Error(AssetBuyerError.StandardRelayerApiError); + expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, standardRelayerError)).toEqual( + 'ZRX is currently unavailable', + ); + }); + it('should return error for AssetUnavailable error', () => { + const assetUnavailableError = new Error( + `${AssetBuyerError.AssetUnavailable}: For assetData ${ZRX_ASSET_DATA}`, + ); + expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, assetUnavailableError)).toEqual( + 'ZRX is currently unavailable', + ); + }); + }); }); diff --git a/packages/instant/webpack.config.js b/packages/instant/webpack.config.js index 803240e76..e74cf36d9 100644 --- a/packages/instant/webpack.config.js +++ b/packages/instant/webpack.config.js @@ -1,10 +1,16 @@ const childProcess = require('child_process'); const ip = require('ip'); const path = require('path'); +const RollbarSourceMapPlugin = require('rollbar-sourcemap-webpack-plugin'); const webpack = require('webpack'); +const GIT_SHA = childProcess + .execSync('git rev-parse HEAD') + .toString() + .trim(); + const DISCHARGE_TARGETS_THAT_REQUIRED_HEAP = ['production', 'staging', 'dogfood']; -const getConfigForDischargeTarget = dischargeTarget => { +const getHeapConfigForDischargeTarget = dischargeTarget => { return { heapAnalyticsIdEnvName: dischargeTarget === 'production' @@ -14,24 +20,80 @@ const getConfigForDischargeTarget = dischargeTarget => { }; }; -const GIT_SHA = childProcess - .execSync('git rev-parse HEAD') - .toString() - .trim(); -const generateConfig = (dischargeTarget, configOptions) => { +const DISCHARGE_TARGETS_THAT_REQUIRE_ROLLBAR = ['production', 'staging', 'dogfood']; +const getRollbarConfigForDischargeTarget = dischargeTarget => { + if (DISCHARGE_TARGETS_THAT_REQUIRE_ROLLBAR.includes(dischargeTarget)) { + const rollbarSourceMapPublicPath = + dischargeTarget === 'production' + ? 'https://instant.0xproject.com' + : `http://0x-instant-${dischargeTarget}.s3-website-us-east-1.amazonaws.com`; + + return { + rollbarSourceMapPublicPath, + rollbarRequired: true, + }; + } + + return { + rollbarRequired: false, + }; +}; + +const ROLLBAR_CLIENT_TOKEN_ENV_VAR_NAME = 'INSTANT_ROLLBAR_CLIENT_TOKEN'; +const ROLLBAR_PUBLISH_TOKEN_ENV_VAR_NAME = 'INSTANT_ROLLBAR_PUBLISH_TOKEN'; +const getRollbarTokens = (dischargeTarget, rollbarRequired) => { + const clientToken = process.env[ROLLBAR_CLIENT_TOKEN_ENV_VAR_NAME]; + const publishToken = process.env[ROLLBAR_PUBLISH_TOKEN_ENV_VAR_NAME]; + + if (rollbarRequired) { + if (!clientToken) { + throw new Error( + `Rollbar client token required for ${dischargeTarget}, please set env var ${ROLLBAR_CLIENT_TOKEN_ENV_VAR_NAME}`, + ); + } + if (!publishToken) { + throw new Error( + `Rollbar publish token required for ${dischargeTarget}, please set env var ${ROLLBAR_PUBLISH_TOKEN_ENV_VAR_NAME}`, + ); + } + } + + return { clientToken, publishToken }; +}; + +const generateConfig = (dischargeTarget, heapConfigOptions, rollbarConfigOptions, nodeEnv) => { const outputPath = process.env.WEBPACK_OUTPUT_PATH || 'umd'; - const { heapAnalyticsIdEnvName, heapAnalyticsIdRequired } = configOptions; + const { heapAnalyticsIdEnvName, heapAnalyticsIdRequired } = heapConfigOptions; const heapAnalyticsId = process.env[heapAnalyticsIdEnvName]; if (heapAnalyticsIdRequired && !heapAnalyticsId) { throw new Error( `Must define heap analytics id in ENV var ${heapAnalyticsIdEnvName} when building for ${dischargeTarget}`, ); } + const heapEnabled = heapAnalyticsId && (nodeEnv !== 'development' || process.env.INSTANT_HEAP_FORCE_DEVELOPMENT); + + const rollbarTokens = getRollbarTokens(dischargeTarget, rollbarConfigOptions.rollbarRequired); + const rollbarEnabled = + rollbarTokens.clientToken && (nodeEnv !== 'development' || process.env.INSTANT_ROLLBAR_FORCE_DEVELOPMENT); + + let rollbarPlugin; + if (rollbarConfigOptions.rollbarRequired) { + if (!rollbarEnabled || !rollbarTokens.publishToken || !rollbarConfigOptions.rollbarSourceMapPublicPath) { + throw new Error(`Rollbar required for ${dischargeTarget} but not configured`); + } + rollbarPlugin = new RollbarSourceMapPlugin({ + accessToken: rollbarTokens.publishToken, + version: GIT_SHA, + publicPath: rollbarConfigOptions.rollbarSourceMapPublicPath, + }); + } const envVars = { GIT_SHA: JSON.stringify(GIT_SHA), NPM_PACKAGE_VERSION: JSON.stringify(process.env.npm_package_version), + ROLLBAR_ENABLED: rollbarEnabled, + HEAP_ENABLED: heapEnabled }; if (dischargeTarget) { envVars.INSTANT_DISCHARGE_TARGET = JSON.stringify(dischargeTarget); @@ -39,6 +101,18 @@ const generateConfig = (dischargeTarget, configOptions) => { if (heapAnalyticsId) { envVars.HEAP_ANALYTICS_ID = JSON.stringify(heapAnalyticsId); } + if (rollbarTokens.clientToken) { + envVars.ROLLBAR_CLIENT_TOKEN = JSON.stringify(rollbarTokens.clientToken); + } + + const plugins = [ + new webpack.DefinePlugin({ + 'process.env': envVars, + }), + ]; + if (rollbarPlugin) { + plugins.push(rollbarPlugin); + } const config = { entry: { @@ -50,11 +124,7 @@ const generateConfig = (dischargeTarget, configOptions) => { library: 'zeroExInstant', libraryTarget: 'umd', }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': envVars, - }), - ], + plugins, devtool: 'source-map', resolve: { extensions: ['.js', '.json', '.ts', '.tsx'], @@ -69,6 +139,15 @@ const generateConfig = (dischargeTarget, configOptions) => { test: /\.svg$/, loader: 'svg-react-loader', }, + { + test: /\.js$/, + loader: 'source-map-loader', + exclude: [ + // instead of /\/node_modules\// + path.join(process.cwd(), 'node_modules'), + path.join(process.cwd(), '../..', 'node_modules'), + ], + }, ], }, devServer: { @@ -89,8 +168,9 @@ const generateConfig = (dischargeTarget, configOptions) => { return config; }; -module.exports = (env, _argv) => { +module.exports = (env, argv) => { const dischargeTarget = env ? env.discharge_target : undefined; - const configOptions = getConfigForDischargeTarget(dischargeTarget); - return generateConfig(dischargeTarget, configOptions); + const heapConfigOptions = getHeapConfigForDischargeTarget(dischargeTarget); + const rollbarConfigOptions = getRollbarConfigForDischargeTarget(dischargeTarget); + return generateConfig(dischargeTarget, heapConfigOptions, rollbarConfigOptions, argv.mode); }; diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 53b24aff0..b09859101 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Add `LengthMismatch` and `LengthGreaterThan3Required` revert reasons", "pr": 1224 + }, + { + "note": "Add RevertReasons for DutchAuction contract", + "pr": 1225 } ] }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 26d8f8e22..6b728af71 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -237,6 +237,12 @@ export enum RevertReason { TxFullyConfirmed = 'TX_FULLY_CONFIRMED', TxNotFullyConfirmed = 'TX_NOT_FULLY_CONFIRMED', TimeLockIncomplete = 'TIME_LOCK_INCOMPLETE', + // DutchAuction + AuctionInvalidAmount = 'INVALID_AMOUNT', + AuctionExpired = 'AUCTION_EXPIRED', + AuctionNotStarted = 'AUCTION_NOT_STARTED', + AuctionInvalidBeginTime = 'INVALID_BEGIN_TIME', + InvalidAssetData = 'INVALID_ASSET_DATA', } export enum StatusCodes { diff --git a/packages/website/public/images/instant/dai_screenshot.png b/packages/website/public/images/instant/dai_screenshot.png Binary files differnew file mode 100644 index 000000000..02aefc909 --- /dev/null +++ b/packages/website/public/images/instant/dai_screenshot.png diff --git a/packages/website/public/images/instant/feature_1.svg b/packages/website/public/images/instant/feature_1.svg new file mode 100644 index 000000000..cca58d9b9 --- /dev/null +++ b/packages/website/public/images/instant/feature_1.svg @@ -0,0 +1,33 @@ +<svg width="343" height="127" viewBox="0 0 343 127" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="22" cy="101" r="22" fill="#0057FF"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9276 102.3C16.7747 102.3 17.5795 102.47 18.3419 102.81C19.0873 103.133 19.7396 103.576 20.2986 104.137C20.8577 104.698 21.2982 105.353 21.6201 106.101C21.959 106.867 22.1284 107.675 22.1284 108.525C22.1284 109.392 21.9675 110.2 21.6455 110.949C21.3067 111.697 20.8577 112.356 20.2986 112.926C19.7395 113.496 19.0873 113.942 18.3419 114.265C17.5795 114.588 16.7747 114.75 15.9276 114.75C15.0635 114.75 14.2589 114.588 13.5133 114.265C12.7679 113.942 12.1114 113.496 11.5438 112.926C10.9763 112.356 10.5315 111.697 10.2096 110.949C9.88772 110.2 9.72677 109.392 9.72677 108.525C9.72677 107.675 9.88772 106.867 10.2096 106.101C10.5315 105.353 10.9763 104.698 11.5438 104.137C12.1114 103.576 12.7679 103.125 13.5133 102.785C14.2588 102.462 15.0635 102.3 15.9276 102.3ZM15.9276 112.582C17.0289 112.582 17.9734 112.186 18.7612 111.395C19.549 110.604 19.9429 109.648 19.9429 108.525C19.9429 107.42 19.549 106.471 18.7612 105.68C17.9734 104.89 17.0288 104.494 15.9276 104.494C14.8094 104.494 13.8564 104.89 13.0686 105.68C12.2807 106.471 11.8868 107.42 11.8868 108.525C11.8868 109.648 12.2807 110.604 13.0686 111.395C13.8564 112.186 14.8093 112.582 15.9276 112.582ZM29.5492 88.625C30.4132 88.625 31.2179 88.7866 31.9634 89.1097C32.7089 89.4329 33.3654 89.8794 33.9329 90.4491C34.5005 91.0189 34.9452 91.678 35.2671 92.4263C35.5891 93.1747 35.75 93.9826 35.75 94.85C35.75 95.7004 35.5891 96.5084 35.2671 97.2737C34.9452 98.0221 34.5005 98.6769 33.9329 99.2381C33.3654 99.7994 32.7089 100.25 31.9634 100.59C31.218 100.913 30.4132 101.075 29.5492 101.075C28.7021 101.075 27.8973 100.913 27.1349 100.59C26.3894 100.25 25.7372 99.7994 25.1781 99.2381C24.6191 98.6768 24.1701 98.022 23.8312 97.2737C23.5093 96.5083 23.3484 95.7004 23.3484 94.85C23.3484 93.9826 23.5093 93.1748 23.8312 92.4263C24.1701 91.678 24.619 91.0189 25.1781 90.4491C25.7372 89.8794 26.3895 89.4329 27.1349 89.1097C27.8973 88.7866 28.7021 88.625 29.5492 88.625ZM29.5492 98.881C30.6674 98.881 31.6204 98.4856 32.4082 97.6947C33.1961 96.9038 33.59 95.9555 33.59 94.85C33.59 93.7275 33.1961 92.7707 32.4082 91.9798C31.6204 91.189 30.6675 90.7935 29.5492 90.7935C28.4479 90.7935 27.5034 91.1889 26.7156 91.9798C25.9278 92.7707 25.5339 93.7274 25.5339 94.85C25.5339 95.9556 25.9278 96.9038 26.7156 97.6947C27.5034 98.4856 28.448 98.881 29.5492 98.881ZM15.1398 88.625H22.2046V90.7936H20.629C21.0864 91.3379 21.4465 91.9502 21.7091 92.6305C21.9717 93.3108 22.103 94.0337 22.103 94.7991V94.8502C22.103 95.7176 21.9336 96.5254 21.5947 97.2738C21.2728 98.0392 20.8281 98.7068 20.2605 99.2766C19.6929 99.8464 19.0364 100.293 18.291 100.616C17.5286 100.939 16.7154 101.101 15.8513 101.101C14.9872 101.101 14.1825 100.939 13.437 100.616C12.6746 100.293 12.0139 99.8464 11.4548 99.2766C10.8957 98.7068 10.4467 98.0392 10.1079 97.2738C9.78595 96.5255 9.625 95.7176 9.625 94.8502C9.625 94.0337 9.78595 93.2684 10.1079 92.554C10.4298 91.8396 10.8491 91.2061 11.3658 90.6534C11.8826 90.1006 12.4713 89.6456 13.132 89.2885C13.7928 88.9313 14.462 88.7187 15.1397 88.6506L15.1398 88.625ZM19.9429 94.8501C19.9429 93.7276 19.549 92.7708 18.7611 91.9799C17.9733 91.189 17.0287 90.7936 15.9275 90.7936C14.8093 90.7936 13.8563 91.189 13.0685 91.9799C12.2807 92.7708 11.8868 93.7274 11.8868 94.8501C11.8868 95.9557 12.2807 96.9038 13.0685 97.6947C13.8563 98.4856 14.8092 98.8811 15.9275 98.8811C17.0288 98.8811 17.9733 98.4856 18.7611 97.6947C19.5489 96.9039 19.9429 95.9556 19.9429 94.8501Z" fill="white"/> +<circle cx="75.5" cy="25.5" r="22.5" fill="#F08839"/> +<path d="M56.8066 31.7309C61.6528 31.5578 71.2413 31.7309 72.9028 33.8078C74.9797 36.404 62.5182 39.5193 72.9028 40.5578C80.4452 41.312 83.4605 43.327 83.8066 44.1924" stroke="white" stroke-width="1.73077"/> +<circle cx="75.4997" cy="25.5002" r="19.9038" stroke="white" stroke-width="1.73077"/> +<circle cx="72.039" cy="13.3847" r="4.32692" stroke="white" stroke-width="1.73077"/> +<path d="M56.2891 26.0186C56.6352 26.3647 57.4314 26.7455 57.8468 25.4993C58.2621 24.2532 60.0968 21.5186 60.9621 20.307H64.0775L66.1544 26.0186C67.1929 25.8455 69.3737 25.4993 69.7891 25.4993C70.3083 25.4993 71.3468 26.0186 71.866 26.0186M71.866 26.0186C72.3852 26.0186 73.9429 23.9416 74.9814 23.9416C76.0198 23.9416 79.1352 26.0186 79.1352 25.4993C79.1352 24.9801 81.7314 22.3839 82.2506 21.3455C82.666 20.5147 83.8083 19.9609 84.3275 19.7878C84.8468 19.9609 85.8852 20.2032 85.8852 19.7878C85.8852 19.2686 86.4044 18.7493 87.4429 20.307C88.4814 21.8647 90.0391 23.9416 90.5583 23.9416C91.0775 23.9416 91.5968 23.9416 92.116 24.4609C92.5314 24.8762 94.0198 26.3647 94.7121 27.057L95.5 28M71.866 26.0186L70.3083 29.1339" stroke="white" stroke-width="1.73077"/> +<path d="M85.885 33.9376C85.885 35.2995 84.9901 35.7159 83.7215 35.8374C83.3207 35.8758 82.8825 35.8847 82.4235 35.8847C81.9644 35.8847 81.5262 35.8758 81.1254 35.8374C79.8568 35.7159 78.9619 35.2995 78.9619 33.9376C78.9619 32.1453 80.5117 30.6924 82.4235 30.6924C84.3352 30.6924 85.885 32.1453 85.885 33.9376Z" stroke="white" stroke-width="1.73077"/> +<path d="M91.0771 36.3173C91.0771 37.2252 90.4059 37.5028 89.4545 37.5838C89.1538 37.6094 88.8252 37.6153 88.4809 37.6153C88.1366 37.6153 87.808 37.6094 87.5074 37.5838C86.5559 37.5028 85.8848 37.2252 85.8848 36.3173C85.8848 35.1224 87.0471 34.1538 88.4809 34.1538C89.9147 34.1538 91.0771 35.1224 91.0771 36.3173Z" stroke="white" stroke-width="1.73077"/> +<circle cx="322.5" cy="98.5" r="20.5" fill="#F2B350"/> +<rect x="322.499" y="88.1144" width="14.6871" height="14.6871" transform="rotate(45 322.499 88.1144)" stroke="white" stroke-width="3.15385"/> +<rect x="322.499" y="94.2949" width="5.94697" height="5.94697" transform="rotate(45 322.499 94.2949)" fill="white"/> +<path d="M335.115 98.4998L322.499 111.115L309.884 98.4998H335.115Z" fill="white"/> +<circle cx="274" cy="26" r="21" fill="#9E19A0"/> +<path d="M268.973 27.3501C268.829 25.8594 270.406 23.8901 272.06 23.1905C273.715 22.4909 274.124 20.8111 274.124 20.8111L273.999 12.269" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/> +<path d="M274.125 40.027V36.6055C274.125 36.6055 273.637 34.5659 275.292 33.8663C276.253 33.4599 277.23 33.8663 278.547 31.6665" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/> +<path d="M262.567 32.8566L265.863 30.707C265.863 30.707 268.146 29.9997 269.476 31.197C270.368 32.0011 270.625 32.9673 272.061 33.1987" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/> +<path d="M285.929 32.3543C285.929 32.3543 282.656 31.2176 281.559 30.5071C280.461 29.7966 279.769 29.6631 279.169 27.1944C278.751 25.4729 277.422 24.0452 275.938 23.9409" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M275.494 12.3748C274.88 11.2696 273.293 11.2649 272.673 12.3664L261.859 31.589C261.426 32.3596 261.692 33.3356 262.457 33.7788L273.307 40.0648C273.812 40.3574 274.436 40.3548 274.939 40.0579L285.57 33.7788C286.323 33.3338 286.585 32.3696 286.161 31.6043L275.494 12.3748Z" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/> +<circle cx="119.5" cy="102.5" r="19.5" fill="#68CCBB"/> +<path d="M131.499 109.423V96.4901C131.499 95.8739 130.797 95.5206 130.302 95.8878L122.469 101.698C122.278 101.84 122.166 102.063 122.166 102.301V110" stroke="white" stroke-width="2.25" stroke-linecap="round"/> +<path d="M107.501 109.423V96.4901C107.501 95.8739 108.203 95.5206 108.698 95.8878L116.531 101.698C116.722 101.84 116.834 102.063 116.834 102.301V110" stroke="white" stroke-width="2.25" stroke-linecap="round"/> +<path d="M222.5 125.557C224.047 126.45 225.953 126.45 227.5 125.557L245.883 114.943C247.43 114.05 248.383 112.4 248.383 110.613V89.3867C248.383 87.6004 247.43 85.9498 245.883 85.0566L227.5 74.4434C225.953 73.5502 224.047 73.5502 222.5 74.4434L204.117 85.0566C202.57 85.9498 201.617 87.6004 201.617 89.3867V110.613C201.617 112.4 202.57 114.05 204.117 114.943L222.5 125.557Z" fill="#FF5D3A"/> +<path d="M213.397 96.7922L213.829 88L219.337 91.1811C223.063 89.3156 227.01 89.296 230.751 91.132L236.195 88V96.4241C236.985 97.9655 237.397 99.669 237.397 101.402C237.378 107.828 231.781 113.037 224.874 113.037C217.967 113.037 212.371 107.813 212.371 101.402C212.371 99.8064 212.719 98.2355 213.397 96.7922Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M232.003 99.3155C231.551 98.1177 229.632 98.2993 229.151 99.3155" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M221.738 99.3155C221.287 98.1177 219.367 98.2993 218.886 99.3155" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M226.951 103.307H223.942" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M225.443 103.307V104.809" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M226.734 108.471C226.327 108.746 225.34 109.203 223.941 108.52" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M174.169 1.04473C172.798 0.384526 171.202 0.384527 169.831 1.04473L153.06 9.12104C151.689 9.78124 150.693 11.0298 150.355 12.5133L146.213 30.6606C145.874 32.144 146.23 33.701 147.178 34.8906L158.784 49.4436C159.733 50.6333 161.171 51.3262 162.693 51.3262L181.307 51.3262C182.829 51.3262 184.267 50.6333 185.216 49.4436L196.822 34.8906C197.77 33.701 198.126 32.144 197.787 30.6606L193.645 12.5133C193.307 11.0298 192.311 9.78125 190.94 9.12104L174.169 1.04473Z" fill="#3AC4FF"/> +<path d="M161 22.12V37H169.186M161 22.12H183M161 22.12L171.744 13L183 22.12M183 22.12V37H175.326M175.326 37V29.32H169.186V37M175.326 37H169.186" stroke="white" stroke-width="2"/> +</svg> diff --git a/packages/website/public/images/instant/feature_2.svg b/packages/website/public/images/instant/feature_2.svg new file mode 100644 index 000000000..a313872b2 --- /dev/null +++ b/packages/website/public/images/instant/feature_2.svg @@ -0,0 +1,15 @@ +<svg width="209" height="112" viewBox="0 0 209 112" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="2" y="32" width="178.035" height="77.5613" rx="3.29952" fill="#8397DE" stroke="#5265AA" stroke-width="4"/> +<path d="M121.491 69.9311C121.491 88.7841 107.718 103.915 90.9058 103.915C74.0938 103.915 60.3203 88.7841 60.3203 69.9311C60.3203 51.0782 74.0938 35.9473 90.9058 35.9473C107.718 35.9473 121.491 51.0782 121.491 69.9311Z" fill="#6E80BF" stroke="#6E80BF" stroke-width="1.69919"/> +<path d="M172.468 50.39C172.468 55.6225 168.596 59.7356 163.972 59.7356C159.347 59.7356 155.476 55.6225 155.476 50.39C155.476 45.1575 159.347 41.0444 163.972 41.0444C168.596 41.0444 172.468 45.1575 172.468 50.39Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/> +<path d="M172.468 93.1903C172.468 98.4228 168.596 102.536 163.972 102.536C159.347 102.536 155.476 98.4228 155.476 93.1903C155.476 87.9578 159.347 83.8447 163.972 83.8447C168.596 83.8447 172.468 87.9578 172.468 93.1903Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/> +<path d="M26.3171 49.4691C26.3171 54.7016 22.4453 58.8147 17.8212 58.8147C13.197 58.8147 9.32518 54.7016 9.32518 49.4691C9.32518 44.2366 13.197 40.1235 17.8212 40.1235C22.4453 40.1235 26.3171 44.2366 26.3171 49.4691Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/> +<path d="M26.3367 92.87C26.3367 98.1025 22.4648 102.216 17.8407 102.216C13.2166 102.216 9.34471 98.1025 9.34471 92.87C9.34471 87.6375 13.2166 83.5244 17.8407 83.5244C22.4648 83.5244 26.3367 87.6375 26.3367 92.87Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/> +<path d="M105.329 70.7255L105.329 70.7256L93.111 77.9472L93.1106 77.9474C92.199 78.4868 91.0649 78.4871 90.1516 77.9472L90.1515 77.9471L77.9287 70.7256C76.537 69.903 76.0828 68.1043 76.9171 66.721L76.9172 66.7208L89.1402 46.4409L89.1402 46.4408C90.2706 44.5646 92.9907 44.5646 94.1211 46.4408L106.339 66.7207L106.339 66.7208C107.174 68.1058 106.719 69.9044 105.329 70.7255Z" fill="#5265AA" stroke="#5265AA" stroke-width="1.69919"/> +<path d="M93.4911 93.863L93.4911 93.863C92.5828 95.1423 90.6834 95.1423 89.7751 93.863L77.2355 76.1985L90.7948 84.2098C90.7952 84.21 90.7955 84.2102 90.7959 84.2104C91.3117 84.5159 91.9534 84.5158 92.4693 84.2103C92.4696 84.2101 92.4699 84.2099 92.4702 84.2098L106.035 76.1981L93.4911 93.863Z" fill="#5265AA" stroke="#5265AA" stroke-width="1.69919"/> +<circle cx="177" cy="32" r="32" fill="#9AA7D7"/> +<g opacity="0.7"> +<line x1="195.064" y1="32.5152" x2="158.935" y2="32.5152" stroke="white" stroke-width="5.16129" stroke-linecap="round"/> +<line x1="177.516" y1="50.0644" x2="177.516" y2="13.9353" stroke="white" stroke-width="5.16129" stroke-linecap="round"/> +</g> +</svg> diff --git a/packages/website/public/images/instant/feature_3.svg b/packages/website/public/images/instant/feature_3.svg new file mode 100644 index 000000000..83aa259c6 --- /dev/null +++ b/packages/website/public/images/instant/feature_3.svg @@ -0,0 +1,195 @@ +<svg width="312" height="238" viewBox="0 0 312 238" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0)"> +<path d="M4.54049 64.6748L115.492 0.617544C117.998 -0.826333 120.032 0.345114 120.032 3.24195V162.804C120.032 165.701 117.998 169.215 115.492 170.668L4.54049 234.716C2.03414 236.16 0 234.989 0 232.092V72.539C0 69.6421 2.03414 66.1278 4.54049 64.6748Z" fill="#363636"/> +<path d="M4.54049 64.6748L115.492 0.617544C117.998 -0.826333 120.032 0.345114 120.032 3.24195V43.6342L0 112.931V72.539C0 69.6421 2.03414 66.1278 4.54049 64.6748Z" fill="#363636"/> +<path d="M14.8197 76.8164L13.5393 77.552C13.4667 77.7064 12.9854 79.577 11.4053 80.4851V81.8745C12.4768 81.257 13.2124 80.3943 13.3668 79.9675L13.4394 79.9221V88.6126L14.8106 87.8226V76.8164H14.8197ZM24.0551 78.9414L22.7111 79.7133V72.2578L21.2037 73.1295L17.0627 83.3366V84.4354L21.4034 81.929V84.0176L22.7111 83.2639V81.1753L24.0551 80.4034V78.9414ZM18.7245 82.0198L21.2037 75.763L21.4034 75.1001V80.467L18.7245 82.0198ZM31.7104 71.84C31.7104 68.6526 30.5026 67.6174 28.4775 68.7797C26.4343 69.9603 25.2175 72.4031 25.2175 75.5905V76.8891C25.2175 80.1401 26.4525 81.2661 28.4957 80.0856C30.5208 78.9141 31.7194 76.3896 31.7194 73.1386V71.84H31.7104ZM30.2483 74.2465C30.2483 76.2352 29.6399 77.9606 28.4866 78.6326C27.3061 79.3137 26.6704 78.3057 26.6704 76.317V74.4826C26.6704 72.5393 27.297 70.9138 28.4775 70.2327C29.6399 69.5607 30.2574 70.4597 30.2574 72.4121V74.2465H30.2483ZM33.8353 76.9526C34.3529 76.653 34.7797 75.9265 34.7797 75.3181C34.7797 74.7278 34.3529 74.4917 33.8353 74.7914C33.3268 75.082 32.8909 75.7993 32.8909 76.4078C32.9 77.0162 33.3177 77.2614 33.8353 76.9526ZM42.3079 65.7195C42.3079 62.532 41.1001 61.4968 39.075 62.6592C37.0318 63.8397 35.815 66.2825 35.815 69.4699V70.7685C35.815 74.0195 37.05 75.1455 39.0932 73.965C41.1183 72.7935 42.317 70.269 42.317 67.018V65.7195H42.3079ZM40.8549 68.1259C40.8549 70.1146 40.2465 71.84 39.0932 72.512C37.9127 73.1931 37.277 72.1851 37.277 70.1964V68.362C37.277 66.4187 37.9036 64.7932 39.0841 64.1121C40.2465 63.4401 40.864 64.3391 40.864 66.2916V68.1259H40.8549ZM50.1811 61.1699C50.1811 57.9825 48.9733 56.9472 46.9483 58.1096C44.905 59.2901 43.6882 61.7329 43.6882 64.9203V66.2189C43.6882 69.4699 44.9232 70.5959 46.9664 69.4154C48.9915 68.244 50.1902 65.7195 50.1902 62.4685V61.1699H50.1811ZM48.7281 63.5763C48.7281 65.5651 48.1197 67.2905 46.9664 67.9625C45.7859 68.6435 45.1502 67.6355 45.1502 65.6468V63.8124C45.1502 61.8691 45.7768 60.2436 46.9573 59.5625C48.1197 58.8905 48.7372 59.7896 48.7372 61.742V63.5763H48.7281ZM62.0772 59.0812L57.0826 61.969L62.0409 50.6813V49.5553L55.2574 53.4692V54.9312L60.2519 52.0526L55.2937 63.3402V64.4663L62.0772 60.5524V59.0812ZM69.1059 56.4841L70.7768 55.5215L68.7517 52.1434C69.7869 51.0082 70.4226 49.637 70.4226 48.1205C70.4226 46.1862 69.4146 45.3054 67.1535 46.6131L63.8116 48.5382V59.5535L65.2919 58.6998V54.4863L67.2806 53.3421L69.1059 56.4841ZM65.2828 49.1466L67.1444 48.0751C68.5428 47.2669 68.9606 47.9752 68.9606 48.9832C68.9606 50.1274 68.443 51.1808 67.0536 51.9799L65.2828 53.0061V49.1466ZM73.2105 43.1078L71.5033 44.0885L74.4818 47.8753L71.4851 55.1129L73.2014 54.123L75.4353 48.62L75.508 48.5745L77.7419 51.4986L79.4582 50.5088L76.5069 46.7039L79.44 39.5117L77.7328 40.4925L75.508 46.041L75.4353 46.0864L73.2105 43.1078Z" fill="#363636"/> +<path d="M10.724 199.291L109.716 142.145C110.833 141.5 111.732 142.027 111.732 143.307V160.443C111.732 161.732 110.833 163.294 109.716 163.939L10.724 221.086C9.60703 221.73 8.70801 221.204 8.70801 219.923V202.788C8.70801 201.498 9.61611 199.936 10.724 199.291Z" fill="#363636"/> +<path opacity="0.2" d="M10.5881 121.159L109.435 64.0936C109.989 63.7758 110.443 64.0301 110.443 64.6748V78.6505C110.443 79.2952 109.989 80.0762 109.435 80.394L10.5881 137.468C10.0341 137.786 9.58008 137.532 9.58008 136.887V122.911C9.58008 122.266 10.0341 121.485 10.5881 121.159Z" fill="#363636"/> +<path opacity="0.2" d="M9.7168 183.272L107.583 126.771" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path opacity="0.2" d="M9.7168 167.835L107.583 111.333" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path opacity="0.2" d="M9.7168 153.305L107.583 96.8032" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path d="M6.56588 66.4912L117.517 2.43395C120.024 0.990073 122.058 2.16152 122.058 5.05836V164.62C122.058 167.517 120.024 171.032 117.517 172.485L6.56588 236.533C4.05953 237.977 2.02539 236.805 2.02539 233.908V74.3554C2.02539 71.4585 4.05953 67.9442 6.56588 66.4912Z" fill="#424242"/> +<path d="M85.7878 33.0822C85.7878 35.1708 85.1159 36.7237 83.9081 37.4229C82.7003 38.1222 82.0283 37.3503 82.0283 35.2526C82.0283 33.1549 82.7003 31.602 83.9081 30.9028C85.1159 30.2035 85.7878 30.9845 85.7878 33.0822ZM84.8888 32.2377C84.7254 31.5929 84.3894 31.4022 83.9081 31.6837C83.1816 32.1015 82.7911 33.1912 82.7911 34.8167C82.7911 35.0074 82.7911 35.1799 82.8093 35.3434L84.8888 32.2377ZM82.9183 36.1153C83.0908 36.7509 83.4177 36.9416 83.9081 36.6601C84.6346 36.2424 85.016 35.1527 85.016 33.5272C85.016 33.3365 85.016 33.1549 84.9978 32.9914L82.9183 36.1153Z" fill="#FF602E"/> +<path d="M87.9219 34.1629C87.9219 33.6998 88.2397 33.1277 88.6393 32.9006C89.0388 32.6736 89.3567 32.8643 89.3567 33.3365C89.3567 33.7997 89.0388 34.3718 88.6393 34.5988C88.2397 34.8258 87.9219 34.626 87.9219 34.1629Z" fill="#FF602E"/> +<path d="M93.4165 31.9291C92.3086 32.5738 91.5186 32.1289 91.5186 30.8485C91.5186 30.1129 91.7365 29.3138 92.2632 28.1423C92.3449 27.9425 93.3802 25.6905 93.4528 25.5452L94.3427 25.0366L92.9079 28.1332C92.7445 28.4965 92.7445 28.5056 92.7082 28.6054L92.7717 28.5964C92.9443 28.2513 93.3166 27.8608 93.6617 27.661C94.6243 27.1071 95.3326 27.543 95.3326 28.6781C95.3144 29.9131 94.5153 31.2934 93.4165 31.9291ZM94.5516 29.1594C94.5516 28.3966 94.0612 28.106 93.4165 28.4783C92.7717 28.8506 92.2814 29.7133 92.2814 30.4761C92.2814 31.2389 92.7717 31.5295 93.4165 31.1572C94.0612 30.7758 94.5516 29.9131 94.5516 29.1594Z" fill="#FF602E"/> +<path d="M100.017 24.8641C100.017 26.9527 99.3453 28.5056 98.1376 29.2048C96.9298 29.904 96.2578 29.1321 96.2578 27.0344C96.2578 24.9367 96.9298 23.3839 98.1376 22.6846C99.3453 21.9945 100.017 22.7664 100.017 24.8641ZM99.1183 24.0195C98.9549 23.3748 98.6189 23.1841 98.1285 23.4656C97.402 23.8833 97.0115 24.973 97.0115 26.5985C97.0115 26.7892 97.0115 26.9618 97.0297 27.1252L99.1183 24.0195ZM97.1478 27.8971C97.3203 28.5328 97.6472 28.7235 98.1376 28.442C98.8641 28.0243 99.2455 26.9345 99.2455 25.309C99.2455 25.1183 99.2455 24.9367 99.2273 24.7733L97.1478 27.8971Z" fill="#FF602E"/> +<path d="M85.7878 44.3337C85.7878 46.4223 85.1159 47.9751 83.9081 48.6744C82.7003 49.3736 82.0283 48.6017 82.0283 46.504C82.0283 44.4063 82.7003 42.8535 83.9081 42.1542C85.1159 41.455 85.7878 42.236 85.7878 44.3337ZM84.8888 43.4801C84.7254 42.8353 84.3894 42.6446 83.9081 42.9261C83.1816 43.3438 82.7911 44.4336 82.7911 46.0591C82.7911 46.2498 82.7911 46.4223 82.8093 46.5858L84.8888 43.4801ZM82.9183 47.3576C83.0908 47.9933 83.4177 48.184 83.9081 47.9025C84.6346 47.4848 85.016 46.3951 85.016 44.7696C85.016 44.5789 85.016 44.3972 84.9978 44.2338L82.9183 47.3576Z" fill="#FF602E"/> +<path d="M87.9219 45.4051C87.9219 44.942 88.2397 44.3699 88.6393 44.1428C89.0388 43.9158 89.3567 44.1065 89.3567 44.5787C89.3567 45.0419 89.0388 45.614 88.6393 45.841C88.2397 46.068 87.9219 45.8682 87.9219 45.4051Z" fill="#FF602E"/> +<path d="M91.5273 42.4452L92.272 42.0093C92.3356 42.5814 92.7805 42.7448 93.3799 42.3997C94.0428 42.0183 94.4877 41.2283 94.4877 40.4201C94.4877 39.6119 94.0428 39.3213 93.3799 39.7027C92.9258 39.966 92.5535 40.4201 92.3446 40.965L91.6 41.3918L91.8633 37.6958L94.9145 35.9341V36.715L92.4808 38.1226L92.3356 40.1386L92.3991 40.1023C92.6534 39.5756 93.0439 39.1578 93.5252 38.8763C94.5241 38.2951 95.2233 38.7492 95.2233 39.9751C95.2233 41.2555 94.4605 42.5632 93.3345 43.217C92.3083 43.8073 91.5727 43.4985 91.5273 42.4452Z" fill="#FF602E"/> +<path d="M98.1284 33.9542C99.2363 33.3094 100.026 33.7544 100.026 35.0348C100.026 35.7522 99.8084 36.5514 99.2726 37.75C99.1818 37.9589 98.1466 40.211 98.083 40.3472L97.1931 40.8557L98.6278 37.7591C98.755 37.4776 98.7913 37.3868 98.8276 37.296L98.7641 37.3051C98.5915 37.6502 98.2283 38.0316 97.8923 38.2313C96.9297 38.7853 96.2305 38.3585 96.2305 37.2052C96.2305 35.9611 97.0296 34.5899 98.1284 33.9542ZM96.9933 36.7239C96.9933 37.4867 97.4836 37.7773 98.1284 37.405C98.7731 37.0326 99.2635 36.17 99.2635 35.4071C99.2635 34.6443 98.7731 34.3538 98.1284 34.7261C97.4836 35.0984 96.9933 35.9611 96.9933 36.7239Z" fill="#FF602E"/> +<path d="M85.7878 56.5112C85.7878 58.5998 85.1159 60.1527 83.9081 60.8519C82.7003 61.5511 82.0283 60.7793 82.0283 58.6816C82.0283 56.5838 82.7003 55.031 83.9081 54.3318C85.1159 53.6325 85.7878 54.4226 85.7878 56.5112ZM84.8888 55.6667C84.7254 55.0219 84.3894 54.8312 83.9081 55.1127C83.1816 55.5304 82.7911 56.6202 82.7911 58.2457C82.7911 58.4364 82.7911 58.6089 82.8093 58.7724L84.8888 55.6667ZM82.9183 59.5442C83.0908 60.1799 83.4177 60.3706 83.9081 60.0891C84.6346 59.6714 85.016 58.5817 85.016 56.9562C85.016 56.7655 85.016 56.5838 84.9978 56.4204L82.9183 59.5442Z" fill="#FF602E"/> +<path d="M87.9219 57.5916C87.9219 57.1285 88.2397 56.5564 88.6393 56.3294C89.0388 56.1023 89.3567 56.293 89.3567 56.7652C89.3567 57.2284 89.0388 57.8005 88.6393 58.0275C88.2397 58.2545 87.9219 58.0547 87.9219 57.5916Z" fill="#FF602E"/> +<path d="M91.5273 54.6317L92.272 54.1958C92.3356 54.7679 92.7805 54.9313 93.3799 54.5863C94.0428 54.2049 94.4877 53.4148 94.4877 52.6066C94.4877 51.7984 94.0428 51.5078 93.3799 51.8892C92.9258 52.1526 92.5535 52.6066 92.3446 53.1515L91.6091 53.5783L91.8724 49.8823L94.9236 48.1206V48.9016L92.4899 50.3091L92.3446 52.3251L92.4082 52.2888C92.6625 51.7621 93.0529 51.3444 93.5342 51.0628C94.5331 50.4817 95.2324 50.9357 95.2324 52.1616C95.2324 53.4421 94.4696 54.7497 93.3435 55.4036C92.3083 55.9938 91.5727 55.6851 91.5273 54.6317Z" fill="#FF602E"/> +<path d="M96.1768 51.9617C96.1768 51.1898 96.6126 50.3089 97.2937 49.7277V49.6551C96.7398 49.8004 96.3947 49.4644 96.3947 48.7924C96.3947 47.8116 97.1212 46.7038 98.1473 46.1135C99.1735 45.5232 99.9 45.7957 99.9 46.7673C99.9 47.4393 99.5549 48.1658 98.9919 48.6653V48.7379C99.6639 48.5109 100.118 48.9014 100.118 49.6732C100.118 50.7448 99.3097 51.9526 98.1473 52.6246C96.985 53.3056 96.1768 53.0332 96.1768 51.9617ZM99.3279 50.0728C99.3279 49.3917 98.8466 49.201 98.1564 49.6097C97.4663 50.0183 96.985 50.7539 96.985 51.435C96.985 52.116 97.4663 52.3067 98.1564 51.8981C98.8466 51.4894 99.3279 50.7448 99.3279 50.0728ZM99.1372 47.294C99.1372 46.6856 98.7376 46.5131 98.1473 46.8491C97.548 47.1941 97.1575 47.8298 97.1575 48.4291C97.1575 49.0376 97.548 49.2192 98.1473 48.8741C98.7376 48.5381 99.1372 47.9025 99.1372 47.294Z" fill="#FF602E"/> +<path d="M85.7878 68.2712C85.7878 70.3598 85.1159 71.9126 83.9081 72.6119C82.7003 73.3111 82.0283 72.5392 82.0283 70.4415C82.0283 68.3438 82.7003 66.791 83.9081 66.0917C85.1159 65.3925 85.7878 66.1735 85.7878 68.2712ZM84.8888 67.4266C84.7254 66.7819 84.3894 66.5912 83.9081 66.8727C83.1816 67.2904 82.7911 68.3801 82.7911 70.0056C82.7911 70.1963 82.7911 70.3689 82.8093 70.5323L84.8888 67.4266ZM82.9183 71.2951C83.0908 71.9308 83.4177 72.1215 83.9081 71.84C84.6346 71.4223 85.016 70.3326 85.016 68.7071C85.016 68.5164 85.016 68.3347 84.9978 68.1713L82.9183 71.2951Z" fill="#FF602E"/> +<path d="M87.9219 69.3519C87.9219 68.8887 88.2397 68.3166 88.6393 68.0896C89.0388 67.8626 89.3567 68.0533 89.3567 68.5255C89.3567 68.9886 89.0388 69.5607 88.6393 69.7878C88.2397 70.0148 87.9219 69.815 87.9219 69.3519Z" fill="#FF602E"/> +<path d="M91.5273 66.3822L92.272 65.9463C92.3356 66.5184 92.7805 66.6818 93.3799 66.3368C94.0428 65.9554 94.4877 65.1653 94.4877 64.3571C94.4877 63.5489 94.0428 63.2583 93.3799 63.6397C92.9258 63.9031 92.5535 64.3571 92.3446 64.902L91.6 65.3288L91.8633 61.6328L94.9145 59.8711V60.6521L92.4808 62.0596L92.3356 64.0756L92.3991 64.0393C92.6534 63.5126 93.0439 63.0948 93.5252 62.8133C94.5241 62.2321 95.2233 62.6862 95.2233 63.9121C95.2233 65.1926 94.4605 66.5002 93.3345 67.154C92.3083 67.7443 91.5727 67.4356 91.5273 66.3822Z" fill="#FF602E"/> +<path d="M99.2097 58.2366V58.173L96.3311 59.8348V59.0629L100.009 56.938V57.728L97.6206 64.5569L96.7942 65.0291L99.2097 58.2366Z" fill="#FF602E"/> +<path d="M85.7878 79.5226C85.7878 81.6113 85.1159 83.1641 83.9081 83.8633C82.7003 84.5626 82.0283 83.7907 82.0283 81.693C82.0283 79.5953 82.7003 78.0424 83.9081 77.3432C85.1159 76.644 85.7878 77.4249 85.7878 79.5226ZM84.8888 78.669C84.7254 78.0243 84.3894 77.8336 83.9081 78.1151C83.1816 78.5328 82.7911 79.6225 82.7911 81.248C82.7911 81.4387 82.7911 81.6113 82.8093 81.7747L84.8888 78.669ZM82.9183 82.5466C83.0908 83.1823 83.4177 83.373 83.9081 83.0915C84.6346 82.6737 85.016 81.584 85.016 79.9585C85.016 79.7678 85.016 79.5862 84.9978 79.4227L82.9183 82.5466Z" fill="#FF602E"/> +<path d="M87.9219 80.5936C87.9219 80.1304 88.2397 79.5583 88.6393 79.3313C89.0388 79.1043 89.3567 79.295 89.3567 79.7672C89.3567 80.2303 89.0388 80.8024 88.6393 81.0295C88.2397 81.2565 87.9219 81.0567 87.9219 80.5936Z" fill="#FF602E"/> +<path d="M91.5273 77.6336L92.272 77.1977C92.3356 77.7698 92.7805 77.9333 93.3799 77.5882C94.0428 77.2068 94.4877 76.4168 94.4877 75.6086C94.4877 74.8004 94.0428 74.5098 93.3799 74.8912C92.9258 75.1545 92.5535 75.6086 92.3446 76.1534L91.6 76.5802L91.8633 72.8843L94.9145 71.1226V71.9035L92.4808 73.3111L92.3356 75.3271L92.3991 75.2907C92.6534 74.764 93.0439 74.3463 93.5252 74.0648C94.5241 73.4836 95.2233 73.9377 95.2233 75.1636C95.2233 76.444 94.4605 77.7517 93.3345 78.4055C92.3083 78.9958 91.5727 78.687 91.5273 77.6336Z" fill="#FF602E"/> +<path d="M98.1645 75.6176C97.0566 76.2623 96.2666 75.8174 96.2666 74.5369C96.2666 73.8014 96.4845 73.0022 97.0112 71.8308C97.1021 71.631 98.1282 69.3789 98.2008 69.2336L99.0908 68.7251L97.656 71.8217C97.4925 72.185 97.4925 72.194 97.4562 72.2939L97.5198 72.2848C97.6923 71.9398 98.0646 71.5493 98.4097 71.3495C99.3723 70.7956 100.081 71.2315 100.081 72.3666C100.053 73.6107 99.2542 74.991 98.1645 75.6176ZM99.2906 72.8479C99.2906 72.0851 98.8002 71.7945 98.1554 72.1668C97.5107 72.5391 97.0203 73.4018 97.0203 74.1646C97.0203 74.9274 97.5107 75.218 98.1554 74.8457C98.8002 74.4734 99.2906 73.6107 99.2906 72.8479Z" fill="#FF602E"/> +<path d="M85.7878 91.6999C85.7878 93.7885 85.1159 95.3414 83.9081 96.0406C82.7003 96.7398 82.0283 95.9679 82.0283 93.8702C82.0283 91.7725 82.7003 90.2197 83.9081 89.5204C85.1159 88.8212 85.7878 89.6022 85.7878 91.6999ZM84.8888 90.8553C84.7254 90.2106 84.3894 90.0199 83.9081 90.3014C83.1816 90.7191 82.7911 91.8088 82.7911 93.4343C82.7911 93.625 82.7911 93.7976 82.8093 93.961L84.8888 90.8553ZM82.9183 94.7329C83.0908 95.3686 83.4177 95.5593 83.9081 95.2778C84.6346 94.8601 85.016 93.7703 85.016 92.1448C85.016 91.9541 85.016 91.7725 84.9978 91.6091L82.9183 94.7329Z" fill="#FF602E"/> +<path d="M87.9219 92.7806C87.9219 92.3174 88.2397 91.7453 88.6393 91.5183C89.0388 91.2913 89.3567 91.482 89.3567 91.9542C89.3567 92.4173 89.0388 92.9894 88.6393 93.2165C88.2397 93.4435 87.9219 93.2437 87.9219 92.7806Z" fill="#FF602E"/> +<path d="M91.5273 89.8202L92.272 89.3843C92.3356 89.9564 92.7805 90.1198 93.3799 89.7747C94.0428 89.3933 94.4877 88.6033 94.4877 87.7951C94.4877 86.9869 94.0428 86.6963 93.3799 87.0777C92.9258 87.341 92.5535 87.7951 92.3446 88.34L91.6 88.7668L91.8633 85.0708L94.9145 83.3091V84.09L92.4808 85.4976L92.3356 87.5136L92.3991 87.4773C92.6534 86.9506 93.0439 86.5328 93.5252 86.2513C94.5241 85.6701 95.2233 86.1242 95.2233 87.3501C95.2233 88.6305 94.4605 89.9382 93.3345 90.592C92.3083 91.1823 91.5727 90.8736 91.5273 89.8202Z" fill="#FF602E"/> +<path d="M96.2666 87.0775L97.0112 86.6416C97.0748 87.2137 97.5198 87.3771 98.1191 87.0321C98.782 86.6507 99.227 85.8606 99.227 85.0524C99.227 84.2442 98.782 83.9536 98.1191 84.335C97.6651 84.5984 97.2928 85.0524 97.0839 85.5973L96.3392 86.0241L96.6026 82.3281L99.6538 80.5664V81.3474L97.2201 82.7549L97.0748 84.7709L97.1384 84.7346C97.3926 84.2079 97.7831 83.7902 98.2644 83.5086C99.2633 82.9275 99.9626 83.3815 99.9626 84.6074C99.9626 85.8879 99.1998 87.1955 98.0737 87.8494C97.0476 88.4396 96.312 88.1309 96.2666 87.0775Z" fill="#FF602E"/> +<path d="M85.7878 114.238C85.7878 116.327 85.1159 117.88 83.9081 118.579C82.7003 119.278 82.0283 118.507 82.0283 116.409C82.0283 114.311 82.7003 112.758 83.9081 112.059C85.1159 111.36 85.7878 112.141 85.7878 114.238ZM84.8888 113.394C84.7254 112.749 84.3894 112.558 83.9081 112.84C83.1816 113.258 82.7911 114.347 82.7911 115.973C82.7911 116.164 82.7911 116.336 82.8093 116.5L84.8888 113.394ZM82.9183 117.271C83.0908 117.907 83.4177 118.098 83.9081 117.816C84.6346 117.399 85.016 116.309 85.016 114.683C85.016 114.493 85.016 114.311 84.9978 114.148L82.9183 117.271Z" fill="#23C49E"/> +<path d="M87.9219 115.319C87.9219 114.856 88.2397 114.284 88.6393 114.057C89.0388 113.83 89.3567 114.021 89.3567 114.493C89.3567 114.956 89.0388 115.528 88.6393 115.755C88.2397 115.982 87.9219 115.782 87.9219 115.319Z" fill="#23C49E"/> +<path d="M91.5273 112.349L92.272 111.914C92.3356 112.486 92.7805 112.649 93.3799 112.304C94.0428 111.923 94.4877 111.133 94.4877 110.324C94.4877 109.516 94.0428 109.226 93.3799 109.607C92.9258 109.87 92.5535 110.324 92.3446 110.869L91.6 111.296L91.8633 107.6L94.9145 105.838V106.619L92.4808 108.027L92.3356 110.043L92.3991 110.007C92.6534 109.48 93.0439 109.062 93.5252 108.781C94.5241 108.199 95.2233 108.653 95.2233 109.879C95.2233 111.16 94.4605 112.467 93.3345 113.121C92.3083 113.721 91.5727 113.412 91.5273 112.349Z" fill="#23C49E"/> +<path d="M98.7006 108.617L96.2578 110.025V109.307L98.0377 104.04L98.8277 103.586L97.0842 108.735V108.799L98.7278 107.854V106.129L99.4271 105.72V107.446L100.226 106.983V107.736L99.4362 108.19V109.471L98.7188 109.889V108.617H98.7006Z" fill="#23C49E"/> +<path d="M85.7878 125.49C85.7878 127.579 85.1159 129.132 83.9081 129.831C82.7003 130.53 82.0283 129.758 82.0283 127.661C82.0283 125.563 82.7003 124.01 83.9081 123.311C85.1159 122.612 85.7878 123.393 85.7878 125.49ZM84.8888 124.646C84.7254 124.001 84.3894 123.81 83.9081 124.092C83.1816 124.51 82.7911 125.599 82.7911 127.225C82.7911 127.416 82.7911 127.588 82.8093 127.752L84.8888 124.646ZM82.9183 128.514C83.0908 129.15 83.4177 129.341 83.9081 129.059C84.6346 128.642 85.016 127.552 85.016 125.926C85.016 125.736 85.016 125.554 84.9978 125.391L82.9183 128.514Z" fill="#23C49E"/> +<path d="M87.9219 126.571C87.9219 126.108 88.2397 125.536 88.6393 125.309C89.0388 125.082 89.3567 125.273 89.3567 125.745C89.3567 126.208 89.0388 126.78 88.6393 127.007C88.2397 127.234 87.9219 127.025 87.9219 126.571Z" fill="#23C49E"/> +<path d="M91.5273 123.601L92.272 123.166C92.3356 123.738 92.7805 123.901 93.3799 123.556C94.0428 123.175 94.4877 122.385 94.4877 121.576C94.4877 120.768 94.0428 120.478 93.3799 120.859C92.9258 121.122 92.5535 121.576 92.3446 122.121L91.6 122.548L91.8633 118.852L94.9145 117.09V117.871L92.4808 119.279L92.3356 121.295L92.3991 121.259C92.6534 120.732 93.0439 120.314 93.5252 120.033C94.5241 119.451 95.2233 119.905 95.2233 121.131C95.2233 122.412 94.4605 123.719 93.3345 124.373C92.3083 124.964 91.5727 124.655 91.5273 123.601Z" fill="#23C49E"/> +<path d="M97.4751 118.289L98.0745 117.944C98.6557 117.608 99.0643 116.945 99.0643 116.327C99.0643 115.719 98.6738 115.546 98.0836 115.892C97.4933 116.228 97.1119 116.845 97.0574 117.517L96.3309 117.935C96.3854 116.8 97.0665 115.719 98.1017 115.12C99.1097 114.538 99.809 114.802 99.809 115.764C99.809 116.509 99.4639 117.254 98.8736 117.744V117.817C99.6001 117.508 100.027 117.835 100.027 118.679C100.027 119.76 99.2369 120.968 98.1199 121.613C97.0211 122.248 96.2674 121.967 96.2129 120.886L96.9394 120.468C96.9939 121.077 97.4479 121.222 98.0927 120.841C98.801 120.432 99.2369 119.76 99.2369 119.097C99.2369 118.416 98.7828 118.244 98.0745 118.661L97.4479 119.024V118.289H97.4751Z" fill="#23C49E"/> +<path d="M85.7878 137.677C85.7878 139.766 85.1159 141.319 83.9081 142.018C82.7003 142.717 82.0283 141.945 82.0283 139.848C82.0283 137.75 82.7003 136.197 83.9081 135.498C85.1159 134.799 85.7878 135.58 85.7878 137.677ZM84.8888 136.824C84.7254 136.179 84.3894 135.988 83.9081 136.27C83.1816 136.688 82.7911 137.777 82.7911 139.403C82.7911 139.593 82.7911 139.766 82.8093 139.929L84.8888 136.824ZM82.9183 140.701C83.0908 141.337 83.4177 141.528 83.9081 141.246C84.6346 140.829 85.016 139.739 85.016 138.113C85.016 137.923 85.016 137.741 84.9978 137.578L82.9183 140.701Z" fill="#23C49E"/> +<path d="M87.9219 138.748C87.9219 138.285 88.2397 137.713 88.6393 137.486C89.0388 137.259 89.3567 137.45 89.3567 137.922C89.3567 138.385 89.0388 138.957 88.6393 139.184C88.2397 139.411 87.9219 139.211 87.9219 138.748Z" fill="#23C49E"/> +<path d="M91.5273 135.788L92.272 135.353C92.3356 135.925 92.7805 136.088 93.3799 135.743C94.0428 135.362 94.4877 134.572 94.4877 133.763C94.4877 132.955 94.0428 132.665 93.3799 133.046C92.9258 133.309 92.5535 133.763 92.3446 134.308L91.6 134.735L91.8633 131.039L94.9145 129.277V130.058L92.4808 131.466L92.3356 133.482L92.3991 133.446C92.6534 132.919 93.0439 132.501 93.5252 132.22C94.5241 131.638 95.2233 132.092 95.2233 133.318C95.2233 134.599 94.4605 135.906 93.3345 136.56C92.3083 137.151 91.5727 136.842 91.5273 135.788Z" fill="#23C49E"/> +<path d="M98.1016 127.307C99.1277 126.716 99.8269 127.025 99.8269 128.078C99.8269 128.732 99.5545 129.432 98.6737 131.003L97.4659 133.182V133.255L99.9087 131.847V132.637L96.3762 134.68V134.063L98.1833 130.757C98.8825 129.477 99.0551 129.05 99.0551 128.578C99.0551 127.933 98.6646 127.752 98.0743 128.088C97.475 128.433 97.0754 129.123 97.0754 129.822V129.849L96.3398 130.276V130.249C96.3489 129.123 97.0754 127.906 98.1016 127.307Z" fill="#23C49E"/> +<path d="M85.7878 149.428C85.7878 151.517 85.1159 153.069 83.9081 153.769C82.7003 154.468 82.0283 153.696 82.0283 151.598C82.0283 149.501 82.7003 147.948 83.9081 147.248C85.1159 146.549 85.7878 147.33 85.7878 149.428ZM84.8888 148.583C84.7254 147.939 84.3894 147.748 83.9081 148.029C83.1816 148.447 82.7911 149.537 82.7911 151.162C82.7911 151.353 82.7911 151.526 82.8093 151.689L84.8888 148.583ZM82.9183 152.461C83.0908 153.097 83.4177 153.287 83.9081 153.006C84.6346 152.588 85.016 151.498 85.016 149.873C85.016 149.682 85.016 149.501 84.9978 149.337L82.9183 152.461Z" fill="#23C49E"/> +<path d="M87.9219 150.509C87.9219 150.045 88.2397 149.473 88.6393 149.246C89.0388 149.019 89.3567 149.21 89.3567 149.682C89.3567 150.145 89.0388 150.717 88.6393 150.944C88.2397 151.172 87.9219 150.972 87.9219 150.509Z" fill="#23C49E"/> +<path d="M91.5273 147.539L92.272 147.103C92.3356 147.675 92.7805 147.839 93.3799 147.493C94.0428 147.112 94.4877 146.322 94.4877 145.514C94.4877 144.706 94.0428 144.415 93.3799 144.796C92.9258 145.06 92.5535 145.514 92.3446 146.059L91.6 146.486L91.8633 142.79L94.9145 141.028V141.809L92.4808 143.216L92.3356 145.232L92.3991 145.196C92.6534 144.669 93.0439 144.252 93.5252 143.97C94.5241 143.389 95.2233 143.843 95.2233 145.069C95.2233 146.349 94.4605 147.657 93.3345 148.311C92.3083 148.91 91.5727 148.601 91.5273 147.539Z" fill="#23C49E"/> +<path d="M100.19 143.462V144.224L96.3938 146.413V145.65L97.9467 144.751V140.247L97.8831 140.283L96.3848 142.281V141.373L97.9467 139.284L98.7095 138.839V144.315L100.19 143.462Z" fill="#23C49E"/> +<path d="M85.7878 160.678C85.7878 162.767 85.1159 164.32 83.9081 165.019C82.7003 165.718 82.0283 164.946 82.0283 162.849C82.0283 160.751 82.7003 159.198 83.9081 158.499C85.1159 157.8 85.7878 158.581 85.7878 160.678ZM84.8888 159.825C84.7254 159.18 84.3894 158.989 83.9081 159.271C83.1816 159.689 82.7911 160.778 82.7911 162.404C82.7911 162.594 82.7911 162.767 82.8093 162.93L84.8888 159.825ZM82.9183 163.702C83.0908 164.338 83.4177 164.529 83.9081 164.247C84.6346 163.829 85.016 162.74 85.016 161.114C85.016 160.924 85.016 160.742 84.9978 160.579L82.9183 163.702Z" fill="#23C49E"/> +<path d="M87.9219 161.75C87.9219 161.287 88.2397 160.715 88.6393 160.488C89.0388 160.261 89.3567 160.451 89.3567 160.923C89.3567 161.387 89.0388 161.959 88.6393 162.186C88.2397 162.413 87.9219 162.213 87.9219 161.75Z" fill="#23C49E"/> +<path d="M91.5273 158.79L92.272 158.354C92.3356 158.926 92.7805 159.09 93.3799 158.744C94.0428 158.363 94.4877 157.573 94.4877 156.765C94.4877 155.957 94.0428 155.666 93.3799 156.047C92.9258 156.311 92.5535 156.765 92.3446 157.31L91.6 157.736L91.8633 154.041L94.9145 152.279V153.06L92.4808 154.467L92.3356 156.483L92.3991 156.447C92.6534 155.92 93.0439 155.503 93.5252 155.221C94.5241 154.64 95.2233 155.094 95.2233 156.32C95.2233 157.6 94.4605 158.908 93.3345 159.562C92.3083 160.152 91.5727 159.843 91.5273 158.79Z" fill="#23C49E"/> +<path d="M100.017 152.46C100.017 154.549 99.3453 156.102 98.1376 156.801C96.9298 157.5 96.2578 156.728 96.2578 154.631C96.2578 152.533 96.9298 150.98 98.1376 150.281C99.3453 149.591 100.017 150.363 100.017 152.46ZM99.1183 151.607C98.9549 150.962 98.6189 150.771 98.1285 151.053C97.402 151.47 97.0115 152.56 97.0115 154.186C97.0115 154.376 97.0115 154.549 97.0297 154.712L99.1183 151.607ZM97.1478 155.484C97.3203 156.12 97.6472 156.311 98.1376 156.029C98.8641 155.611 99.2455 154.522 99.2455 152.896C99.2455 152.705 99.2455 152.524 99.2273 152.36L97.1478 155.484Z" fill="#23C49E"/> +<path d="M85.7878 172.856C85.7878 174.945 85.1159 176.498 83.9081 177.197C82.7003 177.896 82.0283 177.124 82.0283 175.027C82.0283 172.929 82.7003 171.376 83.9081 170.677C85.1159 169.978 85.7878 170.768 85.7878 172.856ZM84.8888 172.012C84.7254 171.367 84.3894 171.176 83.9081 171.458C83.1816 171.876 82.7911 172.965 82.7911 174.591C82.7911 174.782 82.7911 174.954 82.8093 175.118L84.8888 172.012ZM82.9183 175.889C83.0908 176.525 83.4177 176.716 83.9081 176.434C84.6346 176.017 85.016 174.927 85.016 173.301C85.016 173.111 85.016 172.929 84.9978 172.766L82.9183 175.889Z" fill="#23C49E"/> +<path d="M87.9219 173.937C87.9219 173.474 88.2397 172.902 88.6393 172.675C89.0388 172.448 89.3567 172.638 89.3567 173.11C89.3567 173.574 89.0388 174.146 88.6393 174.373C88.2397 174.6 87.9219 174.4 87.9219 173.937Z" fill="#23C49E"/> +<path d="M93.9613 169.978L91.5186 171.385V170.668L93.2984 165.401L94.0885 164.947L92.3449 170.096V170.159L93.9886 169.215V167.489L94.6878 167.081V168.806L95.4869 168.343V169.097L94.6969 169.551V170.831L93.9704 171.249V169.978H93.9613Z" fill="#23C49E"/> +<path d="M98.1284 162.486C99.2363 161.841 100.026 162.286 100.026 163.567C100.026 164.284 99.8084 165.083 99.2726 166.282C99.1818 166.491 98.1466 168.743 98.083 168.879L97.1931 169.387L98.6278 166.291C98.755 166.009 98.7913 165.919 98.8276 165.828L98.7641 165.837C98.5915 166.182 98.2283 166.563 97.8923 166.763C96.9297 167.317 96.2305 166.89 96.2305 165.737C96.2305 164.493 97.0296 163.113 98.1284 162.486ZM96.9933 165.256C96.9933 166.018 97.4836 166.309 98.1284 165.937C98.7731 165.564 99.2635 164.702 99.2635 163.939C99.2635 163.176 98.7731 162.885 98.1284 163.258C97.4836 163.63 96.9933 164.493 96.9933 165.256Z" fill="#23C49E"/> +<g opacity="0.6"> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 70.1598V70.9226L21.8489 73.1111V72.3483L23.4018 71.4493V66.9452L23.3382 66.9815L21.8398 68.9793V68.0712L23.4018 65.9826L24.1646 65.5376V71.0134L25.6448 70.1598Z" fill="white"/> +<path opacity="0.6" d="M28.9047 67.7628L26.4619 69.1704V68.453L28.2418 63.186L29.0318 62.7319L27.2883 67.8809V67.9444L28.9229 67V65.2746L29.6221 64.866V66.5914L30.4212 66.1282V66.8819L29.6312 67.336V68.6164L28.9047 69.0341V67.7628Z" fill="white"/> +<path opacity="0.6" d="M34.9617 62.4232C34.9617 64.5118 34.2897 66.0646 33.0819 66.7639C31.8741 67.4631 31.2021 66.6912 31.2021 64.5935C31.2021 62.4958 31.8741 60.943 33.0819 60.2437C34.2897 59.5536 34.9617 60.3255 34.9617 62.4232ZM34.0627 61.5786C33.8992 60.9339 33.5632 60.7432 33.0819 61.0247C32.3554 61.4424 31.965 62.5321 31.965 64.1576C31.965 64.3483 31.965 64.5209 31.9831 64.6843L34.0627 61.5786ZM32.0921 65.4562C32.2555 66.0919 32.5915 66.2826 33.0819 66.0011C33.8084 65.5833 34.1898 64.4936 34.1898 62.8681C34.1898 62.6774 34.1898 62.4958 34.1716 62.3324L32.0921 65.4562Z" fill="white"/> +<path opacity="0.6" d="M37.1045 63.5037C37.1045 63.0406 37.4223 62.4685 37.8219 62.2415C38.2215 62.0144 38.5393 62.2051 38.5393 62.6774C38.5393 63.1405 38.2215 63.7126 37.8219 63.9396C37.4223 64.1666 37.1045 63.9669 37.1045 63.5037Z" fill="white"/> +<path opacity="0.6" d="M44.4509 56.9476C44.4509 59.0362 43.7789 60.589 42.5712 61.2883C41.3634 61.9875 40.6914 61.2156 40.6914 59.1179C40.6914 57.0202 41.3634 55.4674 42.5712 54.7681C43.7789 54.078 44.4509 54.8499 44.4509 56.9476ZM43.5519 56.103C43.3885 55.4583 43.0525 55.2676 42.5712 55.5491C41.8447 55.9668 41.4542 57.0565 41.4542 58.682C41.4542 58.8727 41.4542 59.0453 41.4724 59.2087L43.5519 56.103ZM41.5813 59.9806C41.7539 60.6163 42.0808 60.807 42.5712 60.5255C43.2976 60.1078 43.679 59.018 43.679 57.3925C43.679 57.2018 43.67 57.0202 43.6609 56.8568L41.5813 59.9806Z" fill="white"/> +<path opacity="0.6" d="M49.2009 54.2049C49.2009 56.2935 48.5289 57.8464 47.3212 58.5456C46.1134 59.2448 45.4414 58.473 45.4414 56.3753C45.4414 54.2775 46.1134 52.7247 47.3212 52.0255C48.5199 51.3353 49.2009 52.1072 49.2009 54.2049ZM48.3019 53.3604C48.1385 52.7156 47.8025 52.5249 47.3212 52.8064C46.5947 53.2241 46.2042 54.3139 46.2042 55.9394C46.2042 56.1301 46.2042 56.3026 46.2224 56.4661L48.3019 53.3604ZM46.3223 57.2379C46.4948 57.8736 46.8217 58.0643 47.3121 57.7828C48.0386 57.3651 48.42 56.2754 48.42 54.6499C48.42 54.4592 48.4109 54.2775 48.4018 54.1141L46.3223 57.2379Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M30.1213 78.8234V79.5862L26.3255 81.7747V81.0119L27.8783 80.1129V75.6087L27.8148 75.6451L26.3164 77.6429V76.7348L27.8783 74.6461L28.6411 74.2012V79.677L30.1213 78.8234Z" fill="white"/> +<path opacity="0.6" d="M32.7815 71.686C33.8077 71.0958 34.5069 71.4045 34.5069 72.4579C34.5069 73.1117 34.2345 73.811 33.3536 75.382L32.1459 77.5614V77.6341L34.5887 76.2265V77.0166L31.0561 79.0598V78.4423L32.8633 75.1368C33.5625 73.8564 33.735 73.4296 33.735 72.9573C33.735 72.3126 33.3446 72.131 32.7452 72.467C32.1459 72.8121 31.7463 73.5022 31.7463 74.2014V74.2287L31.0107 74.6555V74.6283C31.0198 73.4931 31.7463 72.2763 32.7815 71.686Z" fill="white"/> +<path opacity="0.6" d="M36.832 74.9095C36.832 74.4464 37.1499 73.8743 37.5494 73.6472C37.949 73.4202 38.2668 73.6109 38.2668 74.0831C38.2668 74.5462 37.9399 75.1183 37.5494 75.3454C37.1589 75.5724 36.832 75.3726 36.832 74.9095Z" fill="white"/> +<path opacity="0.6" d="M44.1882 68.3527C44.1882 70.4413 43.5162 71.9942 42.3085 72.6934C41.1007 73.3927 40.4287 72.6208 40.4287 70.5231C40.4287 68.4254 41.1007 66.8725 42.3085 66.1733C43.5162 65.474 44.1882 66.255 44.1882 68.3527ZM43.2892 67.4991C43.1258 66.8543 42.7898 66.6636 42.3085 66.9452C41.582 67.3629 41.1915 68.4526 41.1915 70.0781C41.1915 70.2688 41.1915 70.4413 41.2097 70.6048L43.2892 67.4991ZM41.3186 71.3767C41.4912 72.0123 41.8181 72.203 42.3085 71.9215C43.035 71.5038 43.4164 70.4141 43.4164 68.7886C43.4164 68.5979 43.4073 68.4163 43.3982 68.2528L41.3186 71.3767Z" fill="white"/> +<path opacity="0.6" d="M48.9275 65.6107C48.9275 67.6993 48.2555 69.2521 47.0477 69.9514C45.84 70.6506 45.168 69.8787 45.168 67.781C45.168 65.6833 45.84 64.1305 47.0477 63.4312C48.2555 62.7411 48.9275 63.513 48.9275 65.6107ZM48.0285 64.7661C47.865 64.1214 47.529 63.9307 47.0477 64.2122C46.3213 64.6299 45.9308 65.7196 45.9308 67.3451C45.9308 67.5358 45.9308 67.7084 45.9489 67.8718L48.0285 64.7661ZM46.0579 68.6437C46.2304 69.2794 46.5574 69.4701 47.0477 69.1886C47.7742 68.7708 48.1556 67.6811 48.1556 66.0556C48.1556 65.8649 48.1465 65.6833 48.1374 65.5199L46.0579 68.6437Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 93.5895V94.3523L21.8489 96.5408V95.778L23.4018 94.879V90.3748L23.3382 90.4112L21.8398 92.409V91.5009L23.4018 89.4123L24.1646 88.9673V94.4431L25.6448 93.5895Z" fill="white"/> +<path opacity="0.6" d="M27.6705 89.6209L28.2698 89.2759C28.851 88.9399 29.2596 88.277 29.2596 87.6594C29.2596 87.051 28.8692 86.8785 28.2789 87.2236C27.6886 87.5686 27.3072 88.1771 27.2527 88.8491L26.5263 89.2668C26.5807 88.1317 27.2618 87.051 28.297 86.4517C29.305 85.8705 30.0043 86.1338 30.0043 87.0964C30.0043 87.8411 29.6592 88.5857 29.0689 89.0761V89.1487C29.7954 88.84 30.2222 89.1669 30.2222 90.0114C30.2222 91.0921 29.4322 92.2998 28.3152 92.9446C27.2164 93.5802 26.4627 93.2987 26.4082 92.2181L27.1347 91.8004C27.1892 92.4088 27.6432 92.5541 28.288 92.1727C28.9872 91.7641 29.4322 91.0921 29.4322 90.4291C29.4322 89.7481 28.9781 89.5755 28.2698 89.9933L27.6432 90.3565V89.6209H27.6705Z" fill="white"/> +<path opacity="0.6" d="M33.0452 83.7187C34.0714 83.1285 34.7706 83.4372 34.7706 84.4906C34.7706 85.1444 34.4982 85.8437 33.6173 87.4147L32.4095 89.5941V89.6668L34.8523 88.2592V89.0493L31.3198 91.0925V90.475L33.1269 87.1695C33.8262 85.8891 33.9987 85.4623 33.9987 84.9901C33.9987 84.3453 33.6082 84.1637 33.0089 84.4997C32.4095 84.8448 32.01 85.5349 32.01 86.2342V86.2614L31.2744 86.6882V86.661C31.2926 85.5258 32.0191 84.309 33.0452 83.7187Z" fill="white"/> +<path opacity="0.6" d="M37.1045 86.9329C37.1045 86.4698 37.4223 85.8977 37.8219 85.6707C38.2215 85.4436 38.5393 85.6343 38.5393 86.1066C38.5393 86.5697 38.2215 87.1418 37.8219 87.3688C37.4223 87.5958 37.1045 87.3961 37.1045 86.9329Z" fill="white"/> +<path opacity="0.6" d="M44.4509 80.3766C44.4509 82.4652 43.7789 84.018 42.5712 84.7173C41.3634 85.4165 40.6914 84.6446 40.6914 82.5469C40.6914 80.4492 41.3634 78.8964 42.5712 78.1971C43.7789 77.507 44.4509 78.2879 44.4509 80.3766ZM43.5519 79.532C43.3885 78.8873 43.0525 78.6966 42.5712 78.9781C41.8447 79.3958 41.4542 80.4855 41.4542 82.111C41.4542 82.3017 41.4542 82.4743 41.4724 82.6377L43.5519 79.532ZM41.5813 83.4096C41.7539 84.0453 42.0808 84.236 42.5712 83.9545C43.2976 83.5367 43.679 82.447 43.679 80.8215C43.679 80.6308 43.67 80.4492 43.6609 80.2858L41.5813 83.4096Z" fill="white"/> +<path opacity="0.6" d="M49.2009 77.6434C49.2009 79.732 48.5289 81.2848 47.3212 81.9841C46.1134 82.6833 45.4414 81.9114 45.4414 79.8137C45.4414 77.716 46.1134 76.1632 47.3212 75.4639C48.5199 74.7738 49.2009 75.5457 49.2009 77.6434ZM48.3019 76.7898C48.1385 76.145 47.8025 75.9543 47.3212 76.2358C46.5947 76.6535 46.2042 77.7433 46.2042 79.3688C46.2042 79.5595 46.2042 79.732 46.2224 79.8955L48.3019 76.7898ZM46.3223 80.6673C46.4948 81.303 46.8217 81.4937 47.3121 81.2122C48.0386 80.7945 48.42 79.7048 48.42 78.0793C48.42 77.8886 48.4109 77.7069 48.4018 77.5435L46.3223 80.6673Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 105.349V106.112L21.8489 108.3V107.537L23.4018 106.638V102.134L23.3382 102.17L21.8398 104.168V103.26L23.4018 101.172L24.1646 100.727V106.202L25.6448 105.349Z" fill="white"/> +<path opacity="0.6" d="M28.9047 102.952L26.4619 104.359V103.642L28.2418 98.3749L29.0318 97.9209L27.2883 103.07V103.133L28.9229 102.189V100.464L29.6221 100.055V101.78L30.4212 101.317V102.071L29.6312 102.525V103.805L28.9047 104.223V102.952Z" fill="white"/> +<path opacity="0.6" d="M34.9617 97.6121C34.9617 99.7008 34.2897 101.254 33.0819 101.953C31.8741 102.652 31.2021 101.88 31.2021 99.7825C31.2021 97.6848 31.8741 96.1319 33.0819 95.4327C34.2897 94.7425 34.9617 95.5144 34.9617 97.6121ZM34.0627 96.7676C33.8992 96.1228 33.5632 95.9321 33.0819 96.2137C32.3554 96.6314 31.965 97.7211 31.965 99.3466C31.965 99.5373 31.965 99.7098 31.9831 99.8733L34.0627 96.7676ZM32.0921 100.645C32.2555 101.281 32.5915 101.472 33.0819 101.19C33.8084 100.772 34.1898 99.6826 34.1898 98.0571C34.1898 97.8664 34.1898 97.6848 34.1716 97.5213L32.0921 100.645Z" fill="white"/> +<path opacity="0.6" d="M37.1045 98.6927C37.1045 98.2296 37.4223 97.6575 37.8219 97.4304C38.2215 97.2034 38.5393 97.3941 38.5393 97.8663C38.5393 98.3294 38.2215 98.9016 37.8219 99.1286C37.4223 99.3556 37.1045 99.1558 37.1045 98.6927Z" fill="white"/> +<path opacity="0.6" d="M44.4509 92.1365C44.4509 94.2252 43.7789 95.778 42.5712 96.4772C41.3634 97.1765 40.6914 96.4046 40.6914 94.3069C40.6914 92.2092 41.3634 90.6563 42.5712 89.9571C43.7789 89.2669 44.4509 90.0388 44.4509 92.1365ZM43.5519 91.292C43.3885 90.6473 43.0525 90.4566 42.5712 90.7381C41.8447 91.1558 41.4542 92.2455 41.4542 93.871C41.4542 94.0617 41.4542 94.2342 41.4724 94.3977L43.5519 91.292ZM41.5813 95.1605C41.7539 95.7962 42.0808 95.9869 42.5712 95.7054C43.2976 95.2876 43.679 94.1979 43.679 92.5724C43.679 92.3817 43.67 92.2001 43.6609 92.0366L41.5813 95.1605Z" fill="white"/> +<path opacity="0.6" d="M49.2009 89.3939C49.2009 91.4825 48.5289 93.0353 47.3212 93.7346C46.1134 94.4338 45.4414 93.6619 45.4414 91.5642C45.4414 89.4665 46.1134 87.9137 47.3212 87.2144C48.5199 86.5243 49.2009 87.2962 49.2009 89.3939ZM48.3019 88.5493C48.1385 87.9046 47.8025 87.7139 47.3212 87.9954C46.5947 88.4131 46.2042 89.5028 46.2042 91.1283C46.2042 91.319 46.2042 91.4916 46.2224 91.655L48.3019 88.5493ZM46.3223 92.4269C46.4948 93.0626 46.8217 93.2533 47.3121 92.9718C48.0386 92.554 48.42 91.4643 48.42 89.8388C48.42 89.6481 48.4109 89.4665 48.4018 89.303L46.3223 92.4269Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M30.1213 114.012V114.775L26.3255 116.964V116.201L27.8783 115.302V110.798L27.8148 110.834L26.3164 112.832V111.924L27.8783 109.835L28.6411 109.39V114.866L30.1213 114.012Z" fill="white"/> +<path opacity="0.6" d="M32.7815 106.875C33.8077 106.285 34.5069 106.593 34.5069 107.647C34.5069 108.301 34.2345 109 33.3536 110.571L32.1459 112.75V112.823L34.5887 111.415V112.206L31.0561 114.249V113.631L32.8633 110.326C33.5625 109.045 33.735 108.619 33.735 108.146C33.735 107.502 33.3446 107.32 32.7452 107.656C32.1459 108.001 31.7463 108.691 31.7463 109.39V109.418L31.0107 109.844V109.817C31.0198 108.682 31.7463 107.465 32.7815 106.875Z" fill="white"/> +<path opacity="0.6" d="M36.832 110.089C36.832 109.626 37.1499 109.054 37.5494 108.827C37.949 108.6 38.2668 108.791 38.2668 109.263C38.2668 109.726 37.9399 110.298 37.5494 110.525C37.1589 110.752 36.832 110.552 36.832 110.089Z" fill="white"/> +<path opacity="0.6" d="M44.1882 103.533C44.1882 105.621 43.5162 107.174 42.3085 107.874C41.1007 108.573 40.4287 107.801 40.4287 105.703C40.4287 103.605 41.1007 102.053 42.3085 101.353C43.5162 100.663 44.1882 101.444 44.1882 103.533ZM43.2892 102.688C43.1258 102.044 42.7898 101.853 42.3085 102.134C41.582 102.552 41.1915 103.642 41.1915 105.267C41.1915 105.458 41.1915 105.631 41.2097 105.794L43.2892 102.688ZM41.3186 106.566C41.4912 107.202 41.8181 107.392 42.3085 107.111C43.035 106.693 43.4164 105.603 43.4164 103.978C43.4164 103.787 43.4073 103.605 43.3982 103.442L41.3186 106.566Z" fill="white"/> +<path opacity="0.6" d="M48.9275 100.8C48.9275 102.888 48.2555 104.441 47.0477 105.14C45.84 105.84 45.168 105.068 45.168 102.97C45.168 100.872 45.84 99.3194 47.0477 98.6202C48.2555 97.93 48.9275 98.7019 48.9275 100.8ZM48.0285 99.9551C47.865 99.3103 47.529 99.1196 47.0477 99.4012C46.3213 99.8189 45.9308 100.909 45.9308 102.534C45.9308 102.725 45.9308 102.897 45.9489 103.061L48.0285 99.9551ZM46.0579 103.824C46.2304 104.459 46.5574 104.65 47.0477 104.368C47.7742 103.951 48.1556 102.861 48.1556 101.236C48.1556 101.045 48.1465 100.863 48.1374 100.7L46.0579 103.824Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 128.777V129.54L21.8489 131.729V130.966L23.4018 130.067V125.563L23.3382 125.599L21.8398 127.597V126.689L23.4018 124.6L24.1646 124.155V129.631L25.6448 128.777Z" fill="white"/> +<path opacity="0.6" d="M27.6705 124.809L28.2698 124.464C28.851 124.128 29.2596 123.465 29.2596 122.848C29.2596 122.239 28.8692 122.067 28.2789 122.412C27.6886 122.757 27.3072 123.366 27.2527 124.038L26.5263 124.455C26.5807 123.32 27.2618 122.239 28.297 121.64C29.305 121.059 30.0043 121.322 30.0043 122.285C30.0043 123.03 29.6592 123.774 29.0689 124.265V124.337C29.7954 124.028 30.2222 124.355 30.2222 125.2C30.2222 126.281 29.4322 127.488 28.3152 128.133C27.2164 128.769 26.4627 128.487 26.4082 127.407L27.1347 126.989C27.1892 127.597 27.6432 127.743 28.288 127.361C28.9872 126.953 29.4322 126.281 29.4322 125.618C29.4322 124.937 28.9781 124.764 28.2698 125.182L27.6432 125.545V124.809H27.6705Z" fill="white"/> +<path opacity="0.6" d="M33.0452 118.898C34.0714 118.308 34.7706 118.616 34.7706 119.67C34.7706 120.324 34.4982 121.023 33.6173 122.594L32.4095 124.773V124.846L34.8523 123.438V124.228L31.3198 126.272V125.654L33.1269 122.349C33.8262 121.068 33.9987 120.641 33.9987 120.169C33.9987 119.525 33.6082 119.343 33.0089 119.679C32.4095 120.024 32.01 120.714 32.01 121.413V121.441L31.2744 121.867V121.84C31.2926 120.714 32.0191 119.497 33.0452 118.898Z" fill="white"/> +<path opacity="0.6" d="M37.1045 122.121C37.1045 121.658 37.4223 121.086 37.8219 120.859C38.2215 120.632 38.5393 120.823 38.5393 121.295C38.5393 121.758 38.2215 122.33 37.8219 122.557C37.4223 122.784 37.1045 122.585 37.1045 122.121Z" fill="white"/> +<path opacity="0.6" d="M44.4509 115.565C44.4509 117.654 43.7789 119.207 42.5712 119.906C41.3634 120.605 40.6914 119.833 40.6914 117.736C40.6914 115.638 41.3634 114.085 42.5712 113.386C43.7789 112.696 44.4509 113.468 44.4509 115.565ZM43.5519 114.721C43.3885 114.076 43.0525 113.885 42.5712 114.167C41.8447 114.585 41.4542 115.674 41.4542 117.3C41.4542 117.49 41.4542 117.663 41.4724 117.826L43.5519 114.721ZM41.5813 118.598C41.7539 119.234 42.0808 119.425 42.5712 119.143C43.2976 118.725 43.679 117.636 43.679 116.01C43.679 115.82 43.67 115.638 43.6609 115.474L41.5813 118.598Z" fill="white"/> +<path opacity="0.6" d="M49.2009 112.832C49.2009 114.92 48.5289 116.473 47.3212 117.172C46.1134 117.872 45.4414 117.1 45.4414 115.002C45.4414 112.904 46.1134 111.352 47.3212 110.652C48.5199 109.953 49.2009 110.734 49.2009 112.832ZM48.3019 111.978C48.1385 111.333 47.8025 111.143 47.3212 111.424C46.5947 111.842 46.2042 112.932 46.2042 114.557C46.2042 114.748 46.2042 114.92 46.2224 115.084L48.3019 111.978ZM46.3223 115.856C46.4948 116.491 46.8217 116.682 47.3121 116.401C48.0386 115.983 48.42 114.893 48.42 113.268C48.42 113.077 48.4109 112.895 48.4018 112.732L46.3223 115.856Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 151.318V152.08L21.8489 154.269V153.506L23.4018 152.607V148.103L23.3382 148.139L21.8398 150.137V149.229L23.4018 147.14L24.1646 146.695V152.171L25.6448 151.318Z" fill="white"/> +<path opacity="0.6" d="M28.9047 148.92L26.4619 150.327V149.61L28.2418 144.343L29.0318 143.889L27.2883 149.038V149.101L28.9229 148.157V146.431L29.6221 146.023V147.748L30.4212 147.285V148.039L29.6312 148.493V149.773L28.9047 150.191V148.92Z" fill="white"/> +<path opacity="0.6" d="M34.9617 143.58C34.9617 145.669 34.2897 147.222 33.0819 147.921C31.8741 148.62 31.2021 147.848 31.2021 145.751C31.2021 143.653 31.8741 142.1 33.0819 141.401C34.2897 140.711 34.9617 141.492 34.9617 143.58ZM34.0627 142.736C33.8992 142.091 33.5632 141.9 33.0819 142.182C32.3554 142.599 31.965 143.689 31.965 145.315C31.965 145.505 31.965 145.678 31.9831 145.841L34.0627 142.736ZM32.0921 146.613C32.2555 147.249 32.5915 147.44 33.0819 147.158C33.8084 146.74 34.1898 145.651 34.1898 144.025C34.1898 143.834 34.1898 143.653 34.1716 143.489L32.0921 146.613Z" fill="white"/> +<path opacity="0.6" d="M37.1045 144.661C37.1045 144.198 37.4223 143.626 37.8219 143.399C38.2215 143.172 38.5393 143.362 38.5393 143.835C38.5393 144.298 38.2215 144.87 37.8219 145.097C37.4223 145.324 37.1045 145.124 37.1045 144.661Z" fill="white"/> +<path opacity="0.6" d="M44.4509 138.104C44.4509 140.193 43.7789 141.746 42.5712 142.445C41.3634 143.144 40.6914 142.372 40.6914 140.275C40.6914 138.177 41.3634 136.624 42.5712 135.925C43.7789 135.235 44.4509 136.007 44.4509 138.104ZM43.5519 137.26C43.3885 136.615 43.0525 136.424 42.5712 136.706C41.8447 137.124 41.4542 138.213 41.4542 139.839C41.4542 140.029 41.4542 140.202 41.4724 140.365L43.5519 137.26ZM41.5813 141.137C41.7539 141.773 42.0808 141.964 42.5712 141.682C43.2976 141.265 43.679 140.175 43.679 138.549C43.679 138.359 43.67 138.177 43.6609 138.014L41.5813 141.137Z" fill="white"/> +<path opacity="0.6" d="M49.2009 135.362C49.2009 137.451 48.5289 139.003 47.3212 139.703C46.1134 140.402 45.4414 139.63 45.4414 137.532C45.4414 135.435 46.1134 133.882 47.3212 133.182C48.5199 132.492 49.2009 133.273 49.2009 135.362ZM48.3019 134.517C48.1385 133.873 47.8025 133.682 47.3212 133.963C46.5947 134.381 46.2042 135.471 46.2042 137.096C46.2042 137.287 46.2042 137.46 46.2224 137.623L48.3019 134.517ZM46.3223 138.395C46.4948 139.031 46.8217 139.221 47.3121 138.94C48.0386 138.522 48.42 137.432 48.42 135.807C48.42 135.616 48.4109 135.435 48.4018 135.271L46.3223 138.395Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M30.1213 159.98V160.743L26.3255 162.931V162.169L27.8783 161.27V156.765L27.8148 156.802L26.3164 158.8V157.891L27.8783 155.803L28.6411 155.358V160.834L30.1213 159.98Z" fill="white"/> +<path opacity="0.6" d="M32.7815 152.843C33.8077 152.252 34.5069 152.561 34.5069 153.615C34.5069 154.268 34.2345 154.968 33.3536 156.539L32.1459 158.718V158.791L34.5887 157.383V158.173L31.0561 160.217V159.599L32.8633 156.294C33.5625 155.013 33.735 154.586 33.735 154.114C33.735 153.469 33.3446 153.288 32.7452 153.624C32.1459 153.969 31.7463 154.659 31.7463 155.358V155.385L31.0107 155.812V155.785C31.0198 154.65 31.7463 153.433 32.7815 152.843Z" fill="white"/> +<path opacity="0.6" d="M36.832 156.066C36.832 155.603 37.1499 155.031 37.5494 154.804C37.949 154.577 38.2668 154.768 38.2668 155.24C38.2668 155.703 37.9399 156.275 37.5494 156.502C37.1589 156.729 36.832 156.529 36.832 156.066Z" fill="white"/> +<path opacity="0.6" d="M44.1882 149.51C44.1882 151.599 43.5162 153.152 42.3085 153.851C41.1007 154.55 40.4287 153.778 40.4287 151.68C40.4287 149.583 41.1007 148.03 42.3085 147.331C43.5162 146.64 44.1882 147.412 44.1882 149.51ZM43.2892 148.656C43.1258 148.012 42.7898 147.821 42.3085 148.103C41.582 148.52 41.1915 149.61 41.1915 151.235C41.1915 151.426 41.1915 151.599 41.2097 151.762L43.2892 148.656ZM41.3186 152.534C41.4912 153.17 41.8181 153.36 42.3085 153.079C43.035 152.661 43.4164 151.571 43.4164 149.946C43.4164 149.755 43.4073 149.574 43.3982 149.41L41.3186 152.534Z" fill="white"/> +<path opacity="0.6" d="M48.9275 146.767C48.9275 148.856 48.2555 150.409 47.0477 151.108C45.84 151.807 45.168 151.035 45.168 148.938C45.168 146.84 45.84 145.287 47.0477 144.588C48.2555 143.898 48.9275 144.67 48.9275 146.767ZM48.0285 145.923C47.865 145.278 47.529 145.087 47.0477 145.369C46.3213 145.787 45.9308 146.876 45.9308 148.502C45.9308 148.693 45.9308 148.865 45.9489 149.029L48.0285 145.923ZM46.0579 149.8C46.2304 150.436 46.5574 150.627 47.0477 150.345C47.7742 149.928 48.1556 148.838 48.1556 147.212C48.1556 147.022 48.1465 146.84 48.1374 146.677L46.0579 149.8Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 174.746V175.509L21.8489 177.697V176.934L23.4018 176.035V171.531L23.3382 171.567L21.8398 173.565V172.657L23.4018 170.569L24.1646 170.124V175.599L25.6448 174.746Z" fill="white"/> +<path opacity="0.6" d="M27.6705 170.777L28.2698 170.432C28.851 170.096 29.2596 169.433 29.2596 168.816C29.2596 168.207 28.8692 168.035 28.2789 168.38C27.6886 168.725 27.3072 169.333 27.2527 170.005L26.5263 170.423C26.5807 169.288 27.2618 168.207 28.297 167.608C29.305 167.027 30.0043 167.29 30.0043 168.253C30.0043 168.997 29.6592 169.742 29.0689 170.232V170.305C29.7954 169.996 30.2222 170.323 30.2222 171.168C30.2222 172.248 29.4322 173.456 28.3152 174.101C27.2164 174.736 26.4627 174.455 26.4082 173.374L27.1347 172.957C27.1892 173.565 27.6432 173.71 28.288 173.329C28.9872 172.92 29.4322 172.248 29.4322 171.585C29.4322 170.904 28.9781 170.732 28.2698 171.15L27.6432 171.513V170.777H27.6705Z" fill="white"/> +<path opacity="0.6" d="M33.0452 164.875C34.0714 164.285 34.7706 164.593 34.7706 165.647C34.7706 166.301 34.4982 167 33.6173 168.571L32.4095 170.75V170.823L34.8523 169.415V170.206L31.3198 172.249V171.631L33.1269 168.326C33.8262 167.045 33.9987 166.619 33.9987 166.146C33.9987 165.502 33.6082 165.32 33.0089 165.656C32.4095 166.001 32.01 166.691 32.01 167.39V167.418L31.2744 167.844V167.817C31.2926 166.682 32.0191 165.465 33.0452 164.875Z" fill="white"/> +<path opacity="0.6" d="M37.1045 168.098C37.1045 167.635 37.4223 167.063 37.8219 166.836C38.2215 166.609 38.5393 166.8 38.5393 167.272C38.5393 167.735 38.2215 168.307 37.8219 168.534C37.4223 168.752 37.1045 168.562 37.1045 168.098Z" fill="white"/> +<path opacity="0.6" d="M44.4509 161.542C44.4509 163.63 43.7789 165.183 42.5712 165.882C41.3634 166.582 40.6914 165.81 40.6914 163.712C40.6914 161.614 41.3634 160.061 42.5712 159.362C43.7789 158.663 44.4509 159.444 44.4509 161.542ZM43.5519 160.688C43.3885 160.043 43.0525 159.853 42.5712 160.134C41.8447 160.552 41.4542 161.642 41.4542 163.267C41.4542 163.458 41.4542 163.63 41.4724 163.794L43.5519 160.688ZM41.5813 164.566C41.7539 165.201 42.0808 165.392 42.5712 165.111C43.2976 164.693 43.679 163.603 43.679 161.978C43.679 161.787 43.67 161.605 43.6609 161.442L41.5813 164.566Z" fill="white"/> +<path opacity="0.6" d="M49.2009 158.8C49.2009 160.888 48.5289 162.441 47.3212 163.14C46.1134 163.84 45.4414 163.068 45.4414 160.97C45.4414 158.872 46.1134 157.319 47.3212 156.62C48.5199 155.93 49.2009 156.702 49.2009 158.8ZM48.3019 157.955C48.1385 157.31 47.8025 157.12 47.3212 157.401C46.5947 157.819 46.2042 158.909 46.2042 160.534C46.2042 160.725 46.2042 160.897 46.2224 161.061L48.3019 157.955ZM46.3223 161.824C46.4948 162.459 46.8217 162.65 47.3121 162.368C48.0386 161.951 48.42 160.861 48.42 159.236C48.42 159.045 48.4109 158.863 48.4018 158.7L46.3223 161.824Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 186.506V187.268L21.8489 189.457V188.694L23.4018 187.795V183.291L23.3382 183.327L21.8398 185.325V184.417L23.4018 182.328L24.1646 181.883V187.359L25.6448 186.506Z" fill="white"/> +<path opacity="0.6" d="M28.9047 184.109L26.4619 185.516V184.799L28.2418 179.532L29.0318 179.078L27.2883 184.227V184.29L28.9229 183.346V181.62L29.6221 181.212V182.937L30.4212 182.474V183.228L29.6312 183.682V184.962L28.9047 185.38V184.109Z" fill="white"/> +<path opacity="0.6" d="M34.9617 178.769C34.9617 180.857 34.2897 182.41 33.0819 183.11C31.8741 183.809 31.2021 183.037 31.2021 180.939C31.2021 178.842 31.8741 177.289 33.0819 176.589C34.2897 175.899 34.9617 176.671 34.9617 178.769ZM34.0627 177.924C33.8992 177.28 33.5632 177.089 33.0819 177.37C32.3554 177.788 31.965 178.878 31.965 180.503C31.965 180.694 31.965 180.867 31.9831 181.03L34.0627 177.924ZM32.0921 181.802C32.2555 182.438 32.5915 182.628 33.0819 182.347C33.8084 181.929 34.1898 180.839 34.1898 179.214C34.1898 179.023 34.1898 178.841 34.1716 178.678L32.0921 181.802Z" fill="white"/> +<path opacity="0.6" d="M37.1045 179.849C37.1045 179.386 37.4223 178.814 37.8219 178.587C38.2215 178.36 38.5393 178.551 38.5393 179.023C38.5393 179.486 38.2215 180.058 37.8219 180.285C37.4223 180.512 37.1045 180.313 37.1045 179.849Z" fill="white"/> +<path opacity="0.6" d="M44.4509 173.293C44.4509 175.382 43.7789 176.935 42.5712 177.634C41.3634 178.333 40.6914 177.561 40.6914 175.464C40.6914 173.366 41.3634 171.813 42.5712 171.114C43.7789 170.424 44.4509 171.196 44.4509 173.293ZM43.5519 172.449C43.3885 171.804 43.0525 171.613 42.5712 171.895C41.8447 172.313 41.4542 173.402 41.4542 175.028C41.4542 175.218 41.4542 175.391 41.4724 175.554L43.5519 172.449ZM41.5813 176.326C41.7539 176.962 42.0808 177.153 42.5712 176.871C43.2976 176.453 43.679 175.364 43.679 173.738C43.679 173.548 43.67 173.366 43.6609 173.202L41.5813 176.326Z" fill="white"/> +<path opacity="0.6" d="M49.2009 170.551C49.2009 172.639 48.5289 174.192 47.3212 174.891C46.1134 175.591 45.4414 174.819 45.4414 172.721C45.4414 170.623 46.1134 169.07 47.3212 168.371C48.5199 167.681 49.2009 168.453 49.2009 170.551ZM48.3019 169.706C48.1385 169.061 47.8025 168.871 47.3212 169.152C46.5947 169.57 46.2042 170.66 46.2042 172.285C46.2042 172.476 46.2042 172.648 46.2224 172.812L48.3019 169.706ZM46.3223 173.584C46.4948 174.219 46.8217 174.41 47.3121 174.129C48.0386 173.711 48.42 172.621 48.42 170.996C48.42 170.805 48.4109 170.623 48.4018 170.46L46.3223 173.584Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M30.1213 195.169V195.932L26.3255 198.12V197.358L27.8783 196.459V191.954L27.8148 191.991L26.3164 193.989V193.08L27.8783 190.992L28.6411 190.547V196.023L30.1213 195.169Z" fill="white"/> +<path opacity="0.6" d="M32.7815 188.032C33.8077 187.441 34.5069 187.75 34.5069 188.804C34.5069 189.457 34.2345 190.157 33.3536 191.728L32.1459 193.907V193.98L34.5887 192.572V193.362L31.0561 195.405V194.788L32.8633 191.482C33.5625 190.202 33.735 189.775 33.735 189.303C33.735 188.658 33.3446 188.477 32.7452 188.813C32.1459 189.158 31.7463 189.848 31.7463 190.547V190.574L31.0107 191.001V190.974C31.0198 189.839 31.7463 188.622 32.7815 188.032Z" fill="white"/> +<path opacity="0.6" d="M36.832 191.255C36.832 190.792 37.1499 190.22 37.5494 189.993C37.949 189.766 38.2668 189.957 38.2668 190.429C38.2668 190.892 37.9399 191.464 37.5494 191.691C37.1589 191.918 36.832 191.718 36.832 191.255Z" fill="white"/> +<path opacity="0.6" d="M44.1882 184.699C44.1882 186.788 43.5162 188.341 42.3085 189.04C41.1007 189.739 40.4287 188.967 40.4287 186.869C40.4287 184.772 41.1007 183.219 42.3085 182.52C43.5162 181.829 44.1882 182.601 44.1882 184.699ZM43.2892 183.845C43.1258 183.201 42.7898 183.01 42.3085 183.291C41.582 183.709 41.1915 184.799 41.1915 186.424C41.1915 186.615 41.1915 186.788 41.2097 186.951L43.2892 183.845ZM41.3186 187.723C41.4912 188.359 41.8181 188.549 42.3085 188.268C43.035 187.85 43.4164 186.76 43.4164 185.135C43.4164 184.944 43.4073 184.763 43.3982 184.599L41.3186 187.723Z" fill="white"/> +<path opacity="0.6" d="M48.9275 181.956C48.9275 184.045 48.2555 185.598 47.0477 186.297C45.84 186.996 45.168 186.224 45.168 184.127C45.168 182.029 45.84 180.476 47.0477 179.777C48.2555 179.087 48.9275 179.859 48.9275 181.956ZM48.0285 181.112C47.865 180.467 47.529 180.276 47.0477 180.558C46.3213 180.976 45.9308 182.065 45.9308 183.691C45.9308 183.882 45.9308 184.054 45.9489 184.218L48.0285 181.112ZM46.0579 184.989C46.2304 185.625 46.5574 185.816 47.0477 185.534C47.7742 185.117 48.1556 184.027 48.1556 182.401C48.1556 182.211 48.1465 182.029 48.1374 181.866L46.0579 184.989Z" fill="white"/> +</g> +<g opacity="0.6"> +<path opacity="0.6" d="M25.6448 209.935V210.698L21.8489 212.887V212.124L23.4018 211.225V206.721L23.3382 206.757L21.8398 208.755V207.847L23.4018 205.758L24.1646 205.313V210.789L25.6448 209.935Z" fill="white"/> +<path opacity="0.6" d="M27.6705 205.967L28.2698 205.622C28.851 205.286 29.2596 204.623 29.2596 204.005C29.2596 203.397 28.8692 203.224 28.2789 203.569C27.6886 203.914 27.3072 204.523 27.2527 205.195L26.5263 205.612C26.5807 204.477 27.2618 203.397 28.297 202.797C29.305 202.216 30.0043 202.48 30.0043 203.442C30.0043 204.187 29.6592 204.931 29.0689 205.422V205.494C29.7954 205.186 30.2222 205.513 30.2222 206.357C30.2222 207.438 29.4322 208.646 28.3152 209.29C27.2164 209.926 26.4627 209.644 26.4082 208.564L27.1347 208.146C27.1892 208.755 27.6432 208.9 28.288 208.518C28.9872 208.11 29.4322 207.438 29.4322 206.775C29.4322 206.094 28.9781 205.921 28.2698 206.339L27.6432 206.702V205.967H27.6705Z" fill="white"/> +<path opacity="0.6" d="M33.0452 200.064C34.0714 199.474 34.7706 199.783 34.7706 200.836C34.7706 201.49 34.4982 202.189 33.6173 203.76L32.4095 205.94V206.012L34.8523 204.605V205.395L31.3198 207.438V206.821L33.1269 203.515C33.8262 202.235 33.9987 201.808 33.9987 201.336C33.9987 200.691 33.6082 200.509 33.0089 200.845C32.4095 201.19 32.01 201.881 32.01 202.58V202.607L31.2744 203.034V203.007C31.2926 201.872 32.0191 200.655 33.0452 200.064Z" fill="white"/> +<path opacity="0.6" d="M37.1045 203.279C37.1045 202.815 37.4223 202.243 37.8219 202.016C38.2215 201.789 38.5393 201.98 38.5393 202.452C38.5393 202.915 38.2215 203.487 37.8219 203.715C37.4223 203.942 37.1045 203.742 37.1045 203.279Z" fill="white"/> +<path opacity="0.6" d="M44.4509 196.722C44.4509 198.811 43.7789 200.364 42.5712 201.063C41.3634 201.762 40.6914 200.99 40.6914 198.893C40.6914 196.795 41.3634 195.242 42.5712 194.543C43.7789 193.853 44.4509 194.634 44.4509 196.722ZM43.5519 195.878C43.3885 195.233 43.0525 195.042 42.5712 195.324C41.8447 195.742 41.4542 196.831 41.4542 198.457C41.4542 198.647 41.4542 198.82 41.4724 198.983L43.5519 195.878ZM41.5813 199.755C41.7539 200.391 42.0808 200.582 42.5712 200.3C43.2976 199.882 43.679 198.793 43.679 197.167C43.679 196.977 43.67 196.795 43.6609 196.631L41.5813 199.755Z" fill="white"/> +<path opacity="0.6" d="M49.2009 193.989C49.2009 196.078 48.5289 197.631 47.3212 198.33C46.1134 199.029 45.4414 198.257 45.4414 196.159C45.4414 194.062 46.1134 192.509 47.3212 191.81C48.5199 191.119 49.2009 191.891 49.2009 193.989ZM48.3019 193.135C48.1385 192.491 47.8025 192.3 47.3212 192.582C46.5947 192.999 46.2042 194.089 46.2042 195.714C46.2042 195.905 46.2042 196.078 46.2224 196.241L48.3019 193.135ZM46.3223 197.013C46.4948 197.649 46.8217 197.839 47.3121 197.558C48.0386 197.14 48.42 196.05 48.42 194.425C48.42 194.234 48.4109 194.053 48.4018 193.889L46.3223 197.013Z" fill="white"/> +</g> +</g> +<path d="M98.4106 64.6748L209.362 0.617544C211.868 -0.826333 213.903 0.345114 213.903 3.24195V162.804C213.903 165.701 211.868 169.215 209.362 170.668L98.4106 234.725C95.9043 236.169 93.8701 234.998 93.8701 232.101V72.539C93.8701 69.6421 95.9043 66.1278 98.4106 64.6748Z" fill="#363636"/> +<path d="M104.604 199.291L203.596 142.145C204.713 141.5 205.612 142.027 205.612 143.307V160.443C205.612 161.732 204.713 163.294 203.596 163.939L104.604 221.086C103.487 221.73 102.588 221.204 102.588 219.923V202.788C102.588 201.498 103.487 199.936 104.604 199.291Z" fill="#424242"/> +<path d="M100.444 66.4912L211.387 2.43395C213.893 0.990073 215.927 2.16152 215.927 5.05836V164.62C215.927 167.517 213.893 171.032 211.387 172.485L100.435 236.542C97.9287 237.986 95.8945 236.814 95.8945 233.917V74.3554C95.9036 71.4585 97.9378 67.9442 100.444 66.4912Z" fill="#424242"/> +<path d="M100.444 66.4912L211.386 2.43395C213.893 0.990073 215.927 2.16152 215.927 5.05836V45.4506L95.9033 114.748V74.3554C95.9033 71.4585 97.9375 67.9442 100.444 66.4912Z" fill="#424242"/> +<path d="M193.679 64.6748L304.631 0.617544C307.137 -0.826333 309.171 0.345114 309.171 3.24195V162.804C309.171 165.701 307.137 169.215 304.631 170.668L193.679 234.725C191.173 236.169 189.139 234.998 189.139 232.101V72.539C189.139 69.6421 191.173 66.1278 193.679 64.6748Z" fill="#424242"/> +<path d="M193.679 64.6748L304.63 0.617544C307.137 -0.826333 309.171 0.345114 309.171 3.24195V43.6342L189.147 112.931V72.539C189.138 69.6421 191.173 66.1278 193.679 64.6748Z" fill="#424242"/> +<path d="M203.968 76.8164L202.688 77.552C202.615 77.7064 202.134 79.577 200.554 80.4851V81.8745C201.625 81.257 202.361 80.3943 202.515 79.9675L202.588 79.9221V88.6126L203.959 87.8226V76.8164H203.968ZM213.194 78.9414L211.85 79.7133V72.2578L210.343 73.1295L206.202 83.3366V84.4354L210.543 81.929V84.0176L211.85 83.2639V81.1753L213.194 80.4034V78.9414ZM207.864 82.0198L210.343 75.763L210.543 75.1001V80.467L207.864 82.0198ZM220.85 71.84C220.85 68.6526 219.642 67.6174 217.617 68.7797C215.574 69.9603 214.357 72.4031 214.357 75.5905V76.8891C214.357 80.1401 215.592 81.2661 217.635 80.0856C219.66 78.9141 220.859 76.3896 220.859 73.1386V71.84H220.85ZM219.388 74.2465C219.388 76.2352 218.779 77.9606 217.626 78.6326C216.445 79.3137 215.81 78.3057 215.81 76.317V74.4826C215.81 72.5393 216.436 70.9138 217.617 70.2327C218.779 69.5607 219.397 70.4597 219.397 72.4121V74.2465H219.388ZM222.984 76.9526C223.501 76.653 223.928 75.9265 223.928 75.3181C223.928 74.7278 223.501 74.4917 222.984 74.7914C222.475 75.082 222.039 75.7993 222.039 76.4078C222.039 77.0162 222.457 77.2614 222.984 76.9526ZM231.447 65.7195C231.447 62.532 230.239 61.4968 228.214 62.6592C226.171 63.8397 224.954 66.2825 224.954 69.4699V70.7685C224.954 74.0195 226.189 75.1455 228.233 73.965C230.258 72.7935 231.456 70.269 231.456 67.018V65.7195H231.447ZM229.994 68.1259C229.994 70.1146 229.386 71.84 228.233 72.512C227.052 73.1931 226.416 72.1851 226.416 70.1964V68.362C226.416 66.4187 227.043 64.7932 228.223 64.1121C229.386 63.4401 230.003 64.3391 230.003 66.2916V68.1259H229.994ZM239.33 61.1699C239.33 57.9825 238.122 56.9472 236.097 58.1096C234.053 59.2901 232.837 61.7329 232.837 64.9203V66.2189C232.837 69.4699 234.072 70.5959 236.115 69.4154C238.14 68.244 239.339 65.7195 239.339 62.4685V61.1699H239.33ZM237.867 63.5763C237.867 65.5651 237.259 67.2905 236.106 67.9625C234.925 68.6435 234.29 67.6355 234.29 65.6468V63.8124C234.29 61.8691 234.916 60.2436 236.097 59.5625C237.259 58.8905 237.877 59.7896 237.877 61.742V63.5763H237.867ZM251.226 59.0812L246.231 61.969L251.189 50.6813V49.5553L244.406 53.4692V54.9312L249.4 52.0526L244.442 63.3402V64.4663L251.226 60.5524V59.0812ZM258.254 56.4841L259.925 55.5215L257.9 52.1434C258.935 51.0082 259.571 49.637 259.571 48.1205C259.571 46.1862 258.563 45.3054 256.302 46.6131L252.96 48.5382V59.5535L254.44 58.6998V54.4863L256.429 53.3421L258.254 56.4841ZM254.431 49.1466L256.293 48.0751C257.691 47.2669 258.109 47.9752 258.109 48.9832C258.109 50.1274 257.591 51.1808 256.202 51.9799L254.431 53.0061V49.1466ZM262.35 43.1078L260.643 44.0885L263.621 47.8753L260.624 55.1129L262.341 54.123L264.575 48.62L264.647 48.5745L266.881 51.4986L268.598 50.5088L265.646 46.7039L268.579 39.5117L266.872 40.4925L264.647 46.041L264.575 46.0864L262.35 43.1078Z" fill="#424242"/> +<path d="M199.872 199.291L298.864 142.145C299.981 141.5 300.88 142.027 300.88 143.307V160.443C300.88 161.732 299.981 163.294 298.864 163.939L199.872 221.086C198.755 221.73 197.856 221.204 197.856 219.923V202.788C197.856 201.498 198.755 199.936 199.872 199.291Z" fill="#424242"/> +<path opacity="0.2" d="M199.736 121.159L298.582 64.0936C299.136 63.7758 299.59 64.0301 299.59 64.6748V78.6505C299.59 79.2952 299.136 80.0762 298.582 80.394L199.736 137.459C199.182 137.777 198.728 137.522 198.728 136.878V122.902C198.728 122.266 199.173 121.485 199.736 121.159Z" fill="#424242"/> +<path opacity="0.2" d="M198.864 183.272L296.721 126.771" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path opacity="0.2" d="M198.864 167.835L296.721 111.333" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path opacity="0.2" d="M198.864 153.305L296.721 96.8032" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path d="M115.601 82.0923L112.804 83.7087L112.123 86.7781L110.897 87.4864L113.549 76.2351L114.902 75.4541L117.554 83.6452L116.264 84.3898L115.601 82.0923ZM115.32 81.1206L114.23 77.4065L114.194 77.4338L113.095 82.4101L115.32 81.1206Z" fill="white"/> +<path d="M121.64 73.8013C123.03 73.0022 123.938 73.2383 124.047 74.4551L122.93 75.0999C122.821 74.5641 122.33 74.4733 121.631 74.8819C120.887 75.3087 120.396 75.9989 120.396 76.6073C120.396 77.134 120.705 77.243 121.45 77.0069L122.376 76.7072C123.629 76.3076 124.192 76.5982 124.192 77.6789C124.192 79.0773 123.193 80.5394 121.64 81.4384C120.142 82.3011 119.17 82.0922 119.07 80.8572L120.251 80.1761C120.378 80.7301 120.905 80.7846 121.677 80.3396C122.476 79.8765 122.993 79.1682 122.993 78.5325C122.993 77.9967 122.712 77.8786 121.94 78.1238L120.986 78.4235C119.824 78.7867 119.252 78.4508 119.252 77.3792C119.225 76.0534 120.178 74.6458 121.64 73.8013Z" fill="white"/> +<path d="M129.023 69.5332C130.413 68.7341 131.321 68.9702 131.43 70.187L130.304 70.8318C130.195 70.296 129.704 70.2052 129.005 70.6139C128.261 71.0407 127.77 71.7308 127.77 72.3392C127.77 72.8659 128.079 72.9749 128.824 72.7388L129.75 72.4391C131.003 72.0396 131.566 72.3302 131.566 73.4108C131.566 74.8093 130.567 76.2713 129.014 77.1703C127.516 78.033 126.544 77.8242 126.444 76.5891L127.625 75.9081C127.752 76.462 128.279 76.5165 129.051 76.0715C129.85 75.6084 130.367 74.9001 130.367 74.2644C130.367 73.7286 130.086 73.6106 129.314 73.8558L128.36 74.1554C127.198 74.5187 126.626 74.1827 126.626 73.1111C126.617 71.7853 127.561 70.3777 129.023 69.5332Z" fill="white"/> +<path d="M139.13 69.2515C138.958 70.5864 137.914 72.0394 136.542 72.8294C134.753 73.8646 133.736 73.2381 133.736 71.1494V70.0325C133.736 68.0982 134.826 66.2003 136.479 65.2468C138.141 64.2842 139.149 64.9744 139.149 67.063V67.8621L134.881 70.3231V70.65C134.881 71.8941 135.525 72.3118 136.533 71.7306C137.287 71.2947 137.85 70.5955 138.013 69.9053L139.13 69.2515ZM134.881 69.3605L137.995 67.5624C137.995 66.2548 137.423 65.8007 136.461 66.3547C135.48 66.9177 134.881 68.0437 134.881 69.3605Z" fill="white"/> +<path d="M143.78 59.1538V61.2061L146.295 59.7532V60.861L143.798 62.3049V66.2733C143.798 66.9998 144.234 67.1451 145.042 66.6819C145.342 66.5094 146.114 66.0463 146.259 65.9464V67.0543C146.132 67.1542 145.206 67.7172 144.96 67.8534C143.335 68.7887 142.617 68.5345 142.617 66.9544V62.986L140.938 63.9577V62.8407L142.617 61.869V59.8167L143.78 59.1538Z" fill="white"/> +<path d="M157.174 59.6169L157.065 59.6805V60.8429L155.885 61.5239V51.3078L157.083 50.6177V54.6496L157.202 54.5861C157.456 53.6144 158.128 52.7699 159.036 52.2341C160.498 51.3896 161.433 52.0525 161.433 53.9776V55.3125C161.433 57.2286 160.498 58.9813 159.036 59.8258C158.128 60.3434 157.474 60.2708 157.174 59.6169ZM160.253 55.8665V54.7677C160.253 53.5054 159.635 53.0605 158.654 53.6326C157.683 54.1956 157.056 55.3489 157.056 56.6202V57.719C157.056 58.9813 157.683 59.4171 158.654 58.8541C159.635 58.2729 160.253 57.1197 160.253 55.8665Z" fill="white"/> +<path d="M168.607 54.1683L167.472 54.8221V53.5508L167.391 53.5962C167.055 54.695 166.337 55.6213 165.393 56.1661C164.013 56.9652 163.259 56.4385 163.259 54.6768V49.9093L164.43 49.2282V53.6598C164.43 54.9129 164.866 55.2489 165.811 54.7132C166.782 54.1501 167.436 52.9878 167.436 51.7982V47.4938L168.598 46.8218V54.1683H168.607Z" fill="white"/> +<path d="M171.032 55.3849V54.2498C171.095 54.2226 171.286 54.1227 171.386 54.0682C171.994 53.714 172.33 53.2237 172.566 52.3156L172.666 51.8978L170.351 45.8408L171.631 45.0962L173.302 50.1361L173.384 50.0907L175.045 43.1256L176.29 42.4082L173.956 51.3257C173.402 53.4688 172.802 54.3951 171.513 55.1397C171.331 55.2306 171.104 55.3395 171.032 55.3849Z" fill="white"/> +<path d="M183.446 43.6617C183.273 44.9966 182.229 46.4495 180.858 47.2396C179.069 48.2748 178.052 47.6482 178.052 45.5596V44.4426C178.052 42.5084 179.141 40.6105 180.794 39.657C182.456 38.6944 183.464 39.3845 183.464 41.4731V42.2723L179.196 44.7332V45.0601C179.196 46.3042 179.841 46.722 180.849 46.1408C181.602 45.7049 182.165 45.0057 182.329 44.3155L183.446 43.6617ZM179.196 43.7706L182.311 41.9726C182.311 40.6649 181.739 40.2109 180.776 40.7648C179.795 41.3369 179.196 42.463 179.196 43.7706Z" fill="white"/> +<path d="M188.04 37.4773C188.294 35.9517 189.03 34.8438 190.256 34.1355C190.619 33.9266 190.964 33.7813 191.173 33.7632V35.2434C190.891 35.2888 190.474 35.4432 190.119 35.6429C188.685 36.4693 187.886 37.9677 187.886 39.7657V41.9996L189.82 40.8827V41.9179L185.488 44.4152V43.38L186.75 42.6535V37.3865L185.37 38.1856V37.1504L187.886 35.6974V37.5681L188.04 37.4773Z" fill="white"/> +<path d="M161.914 168.47L164.348 164.248C165.419 162.386 165.565 160.352 164.693 159.426L162.323 156.892C163.24 154.531 163.948 152.161 164.42 149.836L168.48 146.667C169.987 145.514 171.086 143.371 171.095 141.609V137.586C171.086 135.824 169.987 134.962 168.48 135.543L164.42 137.059C163.948 135.288 163.24 133.736 162.323 132.428L164.693 127.206C165.565 125.272 165.419 123.401 164.348 122.784L161.914 121.376C160.843 120.759 159.153 121.567 157.909 123.283L154.531 127.969C152.942 127.824 151.244 127.996 149.473 128.469L148.756 124.191C148.501 122.602 147.203 122.076 145.686 122.947L142.199 124.954C140.674 125.844 139.375 127.86 139.13 129.749L138.412 134.843C136.642 136.415 134.944 138.213 133.354 140.183L130.022 139.375C128.778 139.084 127.088 140.229 126.017 142.09L123.583 146.313C122.521 148.174 122.385 150.199 123.265 151.126L125.636 153.659C124.718 156.02 124.01 158.39 123.538 160.715L119.479 163.884C117.971 165.038 116.872 167.181 116.863 168.943V172.965C116.872 174.727 117.971 175.59 119.479 175.009L123.538 173.492C124.01 175.263 124.718 176.816 125.636 178.123L123.265 183.345C122.394 185.279 122.539 187.15 123.61 187.767L126.044 189.175C127.116 189.793 128.805 188.984 130.049 187.268L133.427 182.582C135.016 182.728 136.714 182.555 138.485 182.083L139.203 186.36C139.457 187.949 140.755 188.476 142.272 187.604L145.759 185.597C147.285 184.707 148.583 182.691 148.828 180.802L149.546 175.708C151.317 174.137 153.015 172.339 154.604 170.368L157.909 171.158C159.144 171.458 160.833 170.332 161.914 168.47ZM198.946 76.0623C198.547 74.6002 197.166 74.2824 195.659 75.3085L191.963 77.8149C190.674 76.3165 189.012 75.3539 187.068 74.9907L187.386 70.541C187.522 68.7157 186.551 67.6714 185.08 68.0619L181.756 68.9791C180.285 69.3877 178.741 71.1222 178.115 73.0928L176.544 77.8149C174.219 79.3133 171.949 81.3292 169.869 83.7357L166.827 83.0274C165.583 82.7368 163.894 83.881 162.822 85.7426L160.388 89.9652C159.317 91.8268 159.172 93.861 160.043 94.7872L162.205 97.0484C161.16 100.054 160.552 103.024 160.416 105.793L157.019 109.607C155.63 111.133 154.894 113.331 155.276 114.82L156.148 118.162C156.538 119.633 157.928 119.951 159.435 118.925L163.131 116.418C164.42 117.917 166.082 118.879 168.026 119.242L167.708 123.692C167.599 125.499 168.58 126.516 170.042 126.108L173.365 125.19C174.836 124.782 176.38 123.047 177.007 121.077L178.578 116.282C180.911 114.793 183.2 112.786 185.289 110.388L188.331 111.124C189.575 111.415 191.264 110.27 192.335 108.409L194.769 104.186C195.841 102.324 195.986 100.29 195.114 99.3641L192.98 97.0847C194.024 94.0789 194.633 91.1094 194.769 88.3397L198.138 84.5802C199.528 83.0546 200.263 80.857 199.882 79.3677L198.946 76.0623ZM177.642 94.2151L171.894 100.554L177.642 101.153L183.391 93.9064L177.642 94.2151ZM171.894 100.554L177.642 101.153V86.2148L171.894 100.554ZM177.642 86.2148V101.162L183.391 93.9155L177.642 86.2148ZM171.894 101.816L177.642 107.855V102.424L171.894 101.816ZM177.642 102.415V107.846L183.4 95.1687L177.642 102.415Z" stroke="white" stroke-width="1.18552" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M137.731 171.949C139.275 172.094 141.037 171.731 142.935 170.777C145.559 169.461 148.038 167.218 150.1 164.511C151.126 163.167 152.043 161.696 152.824 160.171C152.179 159.508 151.489 158.827 150.763 158.155C150.563 157.973 150.363 157.791 150.163 157.61C149.382 159.49 148.256 161.279 146.93 162.759L144.896 161.633L137.731 171.949Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/> +<path d="M133.381 155.757C132.501 158.028 131.947 160.361 131.829 162.595C131.656 165.719 132.355 168.189 133.681 169.806C134.335 170.605 135.143 171.195 136.069 171.567C136.968 170.296 137.904 168.961 138.848 167.572C139.102 167.199 139.357 166.818 139.62 166.437C138.385 166.246 137.404 165.519 136.787 164.348L138.794 160.825L133.381 155.757Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/> +<path d="M149.681 141.3C148.138 141.155 146.376 141.518 144.469 142.472C141.845 143.789 139.365 146.032 137.304 148.747C136.287 150.091 135.37 151.544 134.589 153.069C135.234 153.732 135.924 154.413 136.65 155.085C136.85 155.267 137.05 155.449 137.25 155.63C138.031 153.76 139.157 151.971 140.482 150.49L142.399 151.544L149.681 141.3Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/> +<path d="M154.05 157.419C154.913 155.176 155.458 152.87 155.576 150.654C155.748 147.53 155.049 145.069 153.732 143.453C153.078 142.644 152.27 142.054 151.335 141.691C150.436 142.962 149.5 144.297 148.556 145.677C148.302 146.05 148.048 146.431 147.784 146.813C149.019 147.003 150.009 147.739 150.627 148.91L150.636 148.938L148.71 152.361L154.05 157.419Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/> +<path d="M195.713 66.4912L306.655 2.43395C309.161 0.990073 311.196 2.16152 311.196 5.05836V164.62C311.196 167.517 309.161 171.032 306.655 172.485L195.704 236.542C193.197 237.986 191.163 236.814 191.163 233.917V74.3554C191.172 71.4585 193.206 67.9442 195.713 66.4912Z" fill="#B0B0B0"/> +<path d="M195.712 66.4912L306.655 2.43395C309.161 0.990073 311.195 2.16152 311.195 5.05836V45.4506L191.172 114.748V74.3554C191.172 71.4585 193.206 67.9442 195.712 66.4912Z" fill="#4F4F4F"/> +<path opacity="0.3" d="M205.993 78.6323L204.712 79.3679C204.64 79.5223 204.158 81.393 202.578 82.3011V83.6905C203.65 83.0729 204.385 82.2102 204.54 81.7834L204.612 81.738V90.4285L205.983 89.6385V78.6323H205.993ZM215.219 80.7573L213.875 81.5292V74.0737L212.367 74.9455L208.227 85.1525V86.2513L212.567 83.7449V85.8336L213.875 85.0798V82.9912L215.219 82.2193V80.7573ZM209.888 83.8357L212.367 77.5789L212.567 76.916V82.2829L209.888 83.8357ZM222.874 73.656C222.874 70.4685 221.666 69.4333 219.641 70.5957C217.598 71.7762 216.381 74.219 216.381 77.4064V78.705C216.381 81.956 217.616 83.082 219.659 81.9015C221.685 80.73 222.883 78.2055 222.883 74.9545V73.656H222.874ZM221.421 76.0624C221.421 78.0512 220.813 79.7765 219.659 80.4485C218.479 81.1296 217.843 80.1216 217.843 78.1329V76.2985C217.843 74.3552 218.47 72.7297 219.65 72.0486C220.813 71.3766 221.43 72.2756 221.43 74.2281V76.0624H221.421ZM225.008 78.7686C225.526 78.4689 225.953 77.7424 225.953 77.134C225.953 76.5437 225.526 76.3076 225.008 76.6073C224.5 76.8979 224.064 77.6153 224.064 78.2237C224.073 78.8321 224.491 79.0773 225.008 78.7686ZM233.481 67.5354C233.481 64.3479 232.273 63.3127 230.248 64.4751C228.205 65.6556 226.988 68.0984 226.988 71.2858V72.5844C226.988 75.8354 228.223 76.9614 230.266 75.7809C232.291 74.6095 233.49 72.0849 233.49 68.834V67.5354H233.481ZM232.019 69.9418C232.019 71.9306 231.41 73.656 230.257 74.3279C229.076 75.009 228.441 74.001 228.441 72.0123V70.1779C228.441 68.2346 229.067 66.6091 230.248 65.928C231.41 65.256 232.028 66.1551 232.028 68.1075V69.9418H232.019ZM241.354 62.9858C241.354 59.7984 240.146 58.7631 238.121 59.9255C236.078 61.106 234.861 63.5488 234.861 66.7362V68.0348C234.861 71.2858 236.096 72.4119 238.139 71.2313C240.164 70.0599 241.363 67.5354 241.363 64.2844V62.9858H241.354ZM239.901 65.3923C239.901 67.381 239.293 69.1064 238.139 69.7784C236.959 70.4594 236.323 69.4515 236.323 67.4627V65.6284C236.323 63.685 236.95 62.0595 238.13 61.3785C239.293 60.7065 239.91 61.6055 239.91 63.5579V65.3923H239.901ZM253.25 60.8972L248.255 63.7849L253.214 52.4973V51.3712L246.43 55.2851V56.7472L251.425 53.8685L246.467 65.1562V66.2822L253.25 62.3683V60.8972ZM260.279 58.3L261.95 57.3374L259.925 53.9593C260.96 52.8242 261.595 51.4529 261.595 49.9364C261.595 48.0022 260.587 47.1213 258.326 48.429L254.985 50.3541V61.3694L256.465 60.5158V56.3022L258.453 55.158L260.279 58.3ZM256.456 50.9626L258.317 49.891C259.716 49.0828 260.133 49.7911 260.133 50.7991C260.133 51.9433 259.616 52.9967 258.226 53.7958L256.456 54.822V50.9626ZM264.383 44.9237L262.676 45.9045L265.655 49.6912L262.658 56.9288L264.374 55.9389L266.608 50.4359L266.681 50.3905L268.915 53.3145L270.631 52.3247L267.68 48.5198L270.613 41.3276L268.906 42.3084L266.681 47.8569L266.608 47.9023L264.383 44.9237Z" fill="white"/> +<path d="M201.897 201.108L300.889 143.961C302.006 143.316 302.905 143.843 302.905 145.123V162.259C302.905 163.549 302.006 165.111 300.889 165.755L201.897 222.902C200.78 223.547 199.881 223.02 199.881 221.74V204.604C199.881 203.314 200.78 201.753 201.897 201.108Z" fill="#4F4F4F"/> +<path opacity="0.2" d="M201.761 122.975L300.607 65.91C301.161 65.5922 301.615 65.8465 301.615 66.4912V80.4669C301.615 81.1116 301.161 81.8926 300.607 82.2104L201.761 139.275C201.207 139.593 200.753 139.339 200.753 138.694V124.719C200.753 124.083 201.207 123.302 201.761 122.975Z" fill="white"/> +<path opacity="0.2" d="M200.889 185.089L298.745 128.587" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path opacity="0.2" d="M200.889 169.651L298.745 113.149" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +<path opacity="0.2" d="M200.889 155.122L298.745 98.6196" stroke="white" stroke-width="2.16763" stroke-linecap="round"/> +</g> +<defs> +<clipPath id="clip0"> +<rect width="311.196" height="237.15" fill="white"/> +</clipPath> +</defs> +</svg> diff --git a/packages/website/public/images/instant/gnt_screenshot.png b/packages/website/public/images/instant/gnt_screenshot.png Binary files differnew file mode 100644 index 000000000..595c92703 --- /dev/null +++ b/packages/website/public/images/instant/gnt_screenshot.png diff --git a/packages/website/public/images/instant/gods_screenshot.png b/packages/website/public/images/instant/gods_screenshot.png Binary files differnew file mode 100644 index 000000000..07dcd3b7c --- /dev/null +++ b/packages/website/public/images/instant/gods_screenshot.png diff --git a/packages/website/public/images/instant/kitty_screenshot.png b/packages/website/public/images/instant/kitty_screenshot.png Binary files differnew file mode 100644 index 000000000..cf201f8d9 --- /dev/null +++ b/packages/website/public/images/instant/kitty_screenshot.png diff --git a/packages/website/public/images/instant/nmr_screenshot.png b/packages/website/public/images/instant/nmr_screenshot.png Binary files differnew file mode 100644 index 000000000..9b13e59d7 --- /dev/null +++ b/packages/website/public/images/instant/nmr_screenshot.png diff --git a/packages/website/public/images/instant/rep_screenshot.png b/packages/website/public/images/instant/rep_screenshot.png Binary files differnew file mode 100644 index 000000000..813787c16 --- /dev/null +++ b/packages/website/public/images/instant/rep_screenshot.png diff --git a/packages/website/ts/components/ui/image.tsx b/packages/website/ts/components/ui/image.tsx index c8d39352b..d698ddaa0 100644 --- a/packages/website/ts/components/ui/image.tsx +++ b/packages/website/ts/components/ui/image.tsx @@ -10,6 +10,7 @@ export interface ImageProps { height?: string | number; maxWidth?: string | number; maxHeight?: string | number; + additionalStyle?: React.CSSProperties; } interface ImageState { imageLoadFailed: boolean; @@ -30,6 +31,7 @@ export class Image extends React.Component<ImageProps, ImageState> { onError={this._onError.bind(this)} src={src} style={{ + ...this.props.additionalStyle, borderRadius: this.props.borderRadius, maxWidth: this.props.maxWidth, maxHeight: this.props.maxHeight, diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx index c13e21913..87239a021 100644 --- a/packages/website/ts/components/ui/text.tsx +++ b/packages/website/ts/components/ui/text.tsx @@ -3,7 +3,7 @@ import { darken } from 'polished'; import * as React from 'react'; import { styled } from 'ts/style/theme'; -export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i'; +export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i' | 'span'; export interface TextProps { className?: string; diff --git a/packages/website/ts/containers/instant.ts b/packages/website/ts/containers/instant.ts new file mode 100644 index 000000000..12ae7454e --- /dev/null +++ b/packages/website/ts/containers/instant.ts @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Instant as InstantComponent, InstantProps } from 'ts/pages/instant/instant'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; +import { ScreenWidths } from 'ts/types'; +import { Translate } from 'ts/utils/translate'; + +interface ConnectedState { + translate: Translate; + screenWidth: ScreenWidths; +} + +interface ConnectedDispatch { + dispatcher: Dispatcher; +} + +const mapStateToProps = (state: State, _ownProps: InstantProps): ConnectedState => ({ + translate: state.translate, + screenWidth: state.screenWidth, +}); + +const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); + +export const Instant: React.ComponentClass<InstantProps> = connect(mapStateToProps, mapDispatchToProps)( + InstantComponent, +); diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index faf7d8c87..050c201a3 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -7,6 +7,7 @@ import { MetaTags } from 'ts/components/meta_tags'; import { About } from 'ts/containers/about'; import { DocsHome } from 'ts/containers/docs_home'; import { FAQ } from 'ts/containers/faq'; +import { Instant } from 'ts/containers/instant'; import { Jobs } from 'ts/containers/jobs'; import { Landing } from 'ts/containers/landing'; import { LaunchKit } from 'ts/containers/launch_kit'; @@ -92,6 +93,7 @@ render( <Route exact={true} path="/" component={Landing as any} /> <Redirect from="/otc" to={`${WebsitePaths.Portal}`} /> <Route path={WebsitePaths.LaunchKit} component={LaunchKit as any} /> + <Route path={WebsitePaths.Instant} component={Instant as any} /> <Route path={WebsitePaths.Careers} component={Jobs as any} /> <Route path={WebsitePaths.Portal} component={LazyPortal} /> <Route path={WebsitePaths.FAQ} component={FAQ as any} /> diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx new file mode 100644 index 000000000..c836739bb --- /dev/null +++ b/packages/website/ts/pages/instant/configurator.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +import { Container } from 'ts/components/ui/container'; +import { colors } from 'ts/style/colors'; + +export interface ConfiguratorProps { + hash: string; +} + +export const Configurator = (props: ConfiguratorProps) => ( + <Container id={props.hash} height="400px" backgroundColor={colors.instantTertiaryBackground} /> +); diff --git a/packages/website/ts/pages/instant/features.tsx b/packages/website/ts/pages/instant/features.tsx new file mode 100644 index 000000000..9c1668c1c --- /dev/null +++ b/packages/website/ts/pages/instant/features.tsx @@ -0,0 +1,146 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { Container } from 'ts/components/ui/container'; +import { Image } from 'ts/components/ui/image'; +import { Text } from 'ts/components/ui/text'; +import { colors } from 'ts/style/colors'; +import { ScreenWidths } from 'ts/types'; +import { utils } from 'ts/utils/utils'; + +export interface FeatureProps { + screenWidth: ScreenWidths; + onGetStartedClick: () => void; +} + +export const Features = (props: FeatureProps) => { + const isSmallScreen = props.screenWidth === ScreenWidths.Sm; + const getStartedLinkInfo = { + displayText: 'Get started', + onClick: props.onGetStartedClick, + }; + const exploreTheDocsLinkInfo = { + displayText: 'Explore the docs', + linkSrc: `${utils.getCurrentBaseUrl()}/wiki#Get-Started`, + }; + const tokenLinkInfos = isSmallScreen ? [getStartedLinkInfo] : [getStartedLinkInfo, exploreTheDocsLinkInfo]; + return ( + <Container backgroundColor={colors.instantSecondaryBackground} className="py3 flex flex-column px3"> + <FeatureItem + imgSrc="images/instant/feature_1.svg" + title="Support ERC-20 and ERC-721 tokens" + description="Seamlessly integrate token purchasing into your product experience by offering digital assets ranging from in-game items to stablecoins." + linkInfos={tokenLinkInfos} + screenWidth={props.screenWidth} + /> + <FeatureItem + imgSrc="images/instant/feature_2.svg" + title="Generate revenue for your business" + description="With just a few lines of code, you can earn up to 5% in affiliate fees on every transaction from your crypto wallet or dApp." + linkInfos={[ + { + displayText: 'Learn about affiliate fees', + linkSrc: `${utils.getCurrentBaseUrl()}/wiki#Learn-About-Affiliate-Fees`, + }, + ]} + screenWidth={props.screenWidth} + /> + <FeatureItem + imgSrc="images/instant/feature_3.svg" + title="Easy and Flexible Integration" + description="Use our out-of-the-box design or customize the user interface by integrating the AssetBuyer engine. You can also tap into 0x networked liquidity or choose your own liquidity pool." + linkInfos={[ + { + displayText: 'Explore AssetBuyer', + linkSrc: `${utils.getCurrentBaseUrl()}/docs/asset-buyer`, + }, + ]} + screenWidth={props.screenWidth} + /> + </Container> + ); +}; + +interface LinkInfo { + displayText: string; + linkSrc?: string; + onClick?: () => void; +} + +interface FeatureItemProps { + imgSrc: string; + title: string; + description: string; + linkInfos: LinkInfo[]; + screenWidth: ScreenWidths; +} + +const FeatureItem = (props: FeatureItemProps) => { + const { imgSrc, title, description, linkInfos, screenWidth } = props; + const isLargeScreen = screenWidth === ScreenWidths.Lg; + const maxWidth = isLargeScreen ? '500px' : undefined; + const image = ( + <Container className="center" minWidth="435px" maxHeight="225px"> + <Image src={imgSrc} additionalStyle={{ filter: 'drop-shadow(0px 4px 4px rgba(0,0,0,.25))' }} /> + </Container> + ); + const info = ( + <Container maxWidth={maxWidth}> + <Text fontSize="24px" lineHeight="34px" fontColor={colors.white} fontWeight={500}> + {title} + </Text> + <Container marginTop="28px"> + <Text fontFamily="Roboto Mono" fontSize="14px" lineHeight="2em" fontColor={colors.grey500}> + {description} + </Text> + </Container> + <Container className="flex" marginTop="28px"> + {_.map(linkInfos, linkInfo => { + const onClick = (event: React.MouseEvent<HTMLElement>) => { + if (!_.isUndefined(linkInfo.onClick)) { + linkInfo.onClick(); + } else if (!_.isUndefined(linkInfo.linkSrc)) { + utils.openUrl(linkInfo.linkSrc); + } + }; + return ( + <Container + key={linkInfo.linkSrc} + className="flex items-center" + marginRight="32px" + onClick={onClick} + cursor="pointer" + > + <Container> + <Text fontSize="16px" fontColor={colors.white}> + {linkInfo.displayText} + </Text> + </Container> + <Container paddingTop="1px" paddingLeft="6px"> + <i + className="zmdi zmdi-chevron-right bold" + style={{ fontSize: 16, color: colors.white }} + /> + </Container> + </Container> + ); + })} + </Container> + </Container> + ); + return ( + <Container className="flex flex-column items-center py4 px3"> + {isLargeScreen ? ( + <Container className="flex"> + {image} + <Container marginLeft="115px">{info}</Container> + </Container> + ) : ( + <Container className="flex flex-column items-center" width="100%"> + {image} + <Container marginTop="48px">{info}</Container> + </Container> + )} + </Container> + ); +}; diff --git a/packages/website/ts/pages/instant/instant.tsx b/packages/website/ts/pages/instant/instant.tsx new file mode 100644 index 000000000..fa6bd1c33 --- /dev/null +++ b/packages/website/ts/pages/instant/instant.tsx @@ -0,0 +1,87 @@ +import { utils as sharedUtils } from '@0x/react-shared'; +import * as _ from 'lodash'; +import * as React from 'react'; +import * as DocumentTitle from 'react-document-title'; + +import { Footer } from 'ts/components/footer'; +import { MetaTags } from 'ts/components/meta_tags'; +import { TopBar } from 'ts/components/top_bar/top_bar'; +import { Container } from 'ts/components/ui/container'; +import { Configurator } from 'ts/pages/instant/configurator'; +import { Features } from 'ts/pages/instant/features'; +import { Introducing0xInstant } from 'ts/pages/instant/introducing_0x_instant'; +import { NeedMore } from 'ts/pages/instant/need_more'; +import { Screenshots } from 'ts/pages/instant/screenshots'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { colors } from 'ts/style/colors'; +import { ScreenWidths } from 'ts/types'; +import { Translate } from 'ts/utils/translate'; +import { utils } from 'ts/utils/utils'; + +export interface InstantProps { + location: Location; + translate: Translate; + dispatcher: Dispatcher; + screenWidth: ScreenWidths; +} + +export interface InstantState {} + +const CONFIGURATOR_HASH = 'configure'; +const THROTTLE_TIMEOUT = 100; +const DOCUMENT_TITLE = '0x Instant'; +const DOCUMENT_DESCRIPTION = '0x Instant'; + +export class Instant extends React.Component<InstantProps, InstantState> { + // TODO: consolidate this small screen scaffolding into one place (its being used in portal and docs as well) + private readonly _throttledScreenWidthUpdate: () => void; + public constructor(props: InstantProps) { + super(props); + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + } + public componentDidMount(): void { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + } + public render(): React.ReactNode { + return ( + <Container overflowX="hidden"> + <MetaTags title={DOCUMENT_TITLE} description={DOCUMENT_DESCRIPTION} /> + <DocumentTitle title={DOCUMENT_TITLE} /> + <TopBar + blockchainIsLoaded={false} + location={this.props.location} + style={{ backgroundColor: colors.instantPrimaryBackground, position: 'relative' }} + translate={this.props.translate} + isNightVersion={true} + /> + <Container backgroundColor={colors.instantPrimaryBackground} /> + <Introducing0xInstant screenWidth={this.props.screenWidth} onCTAClick={this._onGetStartedClick} /> + <Screenshots screenWidth={this.props.screenWidth} /> + <Features screenWidth={this.props.screenWidth} onGetStartedClick={this._onGetStartedClick} /> + {!this._isSmallScreen() && <Configurator hash={CONFIGURATOR_HASH} />} + <NeedMore screenWidth={this.props.screenWidth} /> + <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} /> + </Container> + ); + } + private readonly _onGetStartedClick = () => { + if (this._isSmallScreen()) { + utils.openUrl(`${utils.getCurrentBaseUrl()}/wiki#Get-Started`); + } else { + this._scrollToConfigurator(); + } + }; + private _isSmallScreen(): boolean { + const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm; + return isSmallScreen; + } + private _scrollToConfigurator(): void { + sharedUtils.setUrlHash(CONFIGURATOR_HASH); + sharedUtils.scrollToHash(CONFIGURATOR_HASH, ''); + } + private _updateScreenWidth(): void { + const newScreenWidth = utils.getScreenWidth(); + this.props.dispatcher.updateScreenWidth(newScreenWidth); + } +} diff --git a/packages/website/ts/pages/instant/introducing_0x_instant.tsx b/packages/website/ts/pages/instant/introducing_0x_instant.tsx new file mode 100644 index 000000000..da3f09faa --- /dev/null +++ b/packages/website/ts/pages/instant/introducing_0x_instant.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; + +import { Button } from 'ts/components/ui/button'; +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { colors } from 'ts/style/colors'; +import { ScreenWidths } from 'ts/types'; + +export interface Introducing0xInstantProps { + screenWidth: ScreenWidths; + onCTAClick: () => void; +} + +export const Introducing0xInstant = (props: Introducing0xInstantProps) => { + const isSmallScreen = props.screenWidth === ScreenWidths.Sm; + const zero = ( + <Text fontColor={colors.white} fontSize="42px" fontWeight="600" fontFamily="Roboto Mono" Tag="span"> + 0 + </Text> + ); + const title = isSmallScreen ? ( + <div> + Introducing<br /> + {zero}x Instant + </div> + ) : ( + <div>Introducing {zero}x Instant</div> + ); + return ( + <div className="clearfix center lg-pt4 md-pt4" style={{ backgroundColor: colors.instantPrimaryBackground }}> + <div className="mx-auto inline-block align-middle py4" style={{ lineHeight: '44px', textAlign: 'center' }}> + <Container className="sm-center sm-pt3"> + <Text fontColor={colors.white} fontSize="42px" lineHeight="52px" fontWeight="600"> + {title} + </Text> + </Container> + <Container className="pb2 lg-pt2 md-pt2 sm-pt3 sm-px3 sm-center" maxWidth="600px"> + <Text fontColor={colors.grey500} fontSize="20px" lineHeight="32px" fontFamily="Roboto Mono"> + A free and flexible way to offer simple crypto + <br /> purchasing in any app or website. + </Text> + </Container> + <div className="py3"> + <Button + type="button" + backgroundColor={colors.mediumBlue} + fontColor={colors.white} + fontSize="18px" + onClick={props.onCTAClick} + > + Get Started + </Button> + </div> + </div> + </div> + ); +}; diff --git a/packages/website/ts/pages/instant/need_more.tsx b/packages/website/ts/pages/instant/need_more.tsx new file mode 100644 index 000000000..e6d5c3694 --- /dev/null +++ b/packages/website/ts/pages/instant/need_more.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; + +import { Button } from 'ts/components/ui/button'; +import { Container } from 'ts/components/ui/container'; +import { Text } from 'ts/components/ui/text'; +import { colors } from 'ts/style/colors'; +import { ScreenWidths } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { utils } from 'ts/utils/utils'; + +export interface NeedMoreProps { + screenWidth: ScreenWidths; +} +export const NeedMore = (props: NeedMoreProps) => { + const isSmallScreen = props.screenWidth === ScreenWidths.Sm; + const backgroundColor = isSmallScreen ? colors.instantTertiaryBackground : colors.instantSecondaryBackground; + const className = isSmallScreen ? 'flex flex-column items-center' : 'flex'; + const marginRight = isSmallScreen ? undefined : '200px'; + return ( + <Container className="flex flex-column items-center py4 px3" backgroundColor={backgroundColor}> + <Container className={className}> + <Container className="sm-center" marginRight={marginRight}> + <Text fontColor={colors.white} fontSize="32px" lineHeight="45px"> + Need more flexibility? + </Text> + <Text fontColor={colors.grey500} fontSize="18px" lineHeight="27px"> + View our full documentation or reach out if you have any questions. + </Text> + </Container> + <Container className="py3 flex"> + <Container marginRight="20px"> + <Button + type="button" + backgroundColor={colors.white} + fontColor={backgroundColor} + fontSize="18px" + onClick={onGetInTouchClick} + > + Get in Touch + </Button> + </Container> + <Button + type="button" + backgroundColor={colors.mediumBlue} + fontColor={colors.white} + fontSize="18px" + onClick={onDocsClick} + > + Explore the Docs + </Button> + </Container> + </Container> + </Container> + ); +}; + +const onGetInTouchClick = () => { + utils.openUrl(constants.URL_ZEROEX_CHAT); +}; +const onDocsClick = () => { + utils.openUrl(`${utils.getCurrentBaseUrl()}/wiki#Get-Started`); +}; diff --git a/packages/website/ts/pages/instant/screenshots.tsx b/packages/website/ts/pages/instant/screenshots.tsx new file mode 100644 index 000000000..7dcf17fd1 --- /dev/null +++ b/packages/website/ts/pages/instant/screenshots.tsx @@ -0,0 +1,35 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { Container } from 'ts/components/ui/container'; +import { colors } from 'ts/style/colors'; +import { ScreenWidths } from 'ts/types'; + +export interface ScreenshotsProps { + screenWidth: ScreenWidths; +} + +export const Screenshots = (props: ScreenshotsProps) => { + const isSmallScreen = props.screenWidth === ScreenWidths.Sm; + const images = isSmallScreen + ? [ + 'images/instant/rep_screenshot.png', + 'images/instant/dai_screenshot.png', + 'images/instant/gods_screenshot.png', + ] + : [ + 'images/instant/nmr_screenshot.png', + 'images/instant/kitty_screenshot.png', + 'images/instant/rep_screenshot.png', + 'images/instant/dai_screenshot.png', + 'images/instant/gods_screenshot.png', + 'images/instant/gnt_screenshot.png', + ]; + return ( + <Container backgroundColor={colors.instantPrimaryBackground} className="py3 flex justify-center"> + {_.map(images, image => { + return <img className="px1 flex-none" width="300px" height="420px" src={image} key={image} />; + })} + </Container> + ); +}; diff --git a/packages/website/ts/style/colors.ts b/packages/website/ts/style/colors.ts index 0620bae0f..f89dfc86e 100644 --- a/packages/website/ts/style/colors.ts +++ b/packages/website/ts/style/colors.ts @@ -13,6 +13,9 @@ const appColors = { jobsPageOpenPositionRow: sharedColors.grey100, metaMaskOrange: '#f68c24', metaMaskTransparentOrange: 'rgba(255, 248, 242, 0.8)', + instantPrimaryBackground: '#222222', + instantSecondaryBackground: '#333333', + instantTertiaryBackground: '#444444', }; export const colors = { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 89c477085..9c4b8a018 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -353,6 +353,7 @@ export enum WebsitePaths { FAQ = '/faq', About = '/about', LaunchKit = '/launch-kit', + Instant = '/instant', Whitepaper = '/pdfs/0x_white_paper.pdf', SmartContracts = '/docs/contracts', Connect = '/docs/connect', diff --git a/packages/website/tslint.json b/packages/website/tslint.json index 50f5be4f6..3022b2c84 100644 --- a/packages/website/tslint.json +++ b/packages/website/tslint.json @@ -5,6 +5,7 @@ "no-object-literal-type-assertion": false, "completed-docs": false, "prefer-function-over-method": false, - "custom-no-magic-numbers": false + "custom-no-magic-numbers": false, + "semicolon": [true, "always", "ignore-bound-class-methods"] } } @@ -4520,7 +4520,7 @@ cross-fetch@^2.1.0: node-fetch "2.1.1" whatwg-fetch "2.0.3" -cross-spawn@^4: +cross-spawn@^4, cross-spawn@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" dependencies: @@ -5279,6 +5279,14 @@ dot-prop@^4.1.0, dot-prop@^4.2.0: dependencies: is-obj "^1.0.0" +dotenv-cli@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-1.4.0.tgz#e8e80830ed88b48a03b5eb7ec26147ca717f7409" + dependencies: + cross-spawn "^4.0.0" + dotenv "^4.0.0" + minimist "^1.1.3" + dotenv@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" @@ -13431,6 +13439,21 @@ rollbar@^2.4.7: optionalDependencies: decache "^3.0.5" +rollbar@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.5.0.tgz#dbb513eea34f1e3bae57611810c3e6df0ef50a12" + dependencies: + async "~1.2.1" + console-polyfill "0.3.0" + debug "2.6.9" + error-stack-parser "1.3.3" + json-stringify-safe "~5.0.0" + lru-cache "~2.2.1" + request-ip "~2.0.1" + uuid "3.0.x" + optionalDependencies: + decache "^3.0.5" + rst-selector-parser@^2.2.3: version "2.2.3" resolved "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" |