Repository: Loopring/loopring-web-v2 Branch: released Commit: 84d19a96c2ae Files: 1323 Total size: 10.0 MB Directory structure: gitextract_p30q3z9e/ ├── .claude/ │ └── settings.local.json ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .yarnclean ├── .yarnrc ├── LICENSE ├── README.md ├── craco.config.cjs ├── lerna.json ├── package.json ├── packages/ │ ├── common-resources/ │ │ ├── .babelrc │ │ ├── assets/ │ │ │ └── coin/ │ │ │ └── loopring.json │ │ ├── index.ts │ │ ├── mail.html │ │ ├── package.json │ │ ├── scripts/ │ │ │ ├── .gitignore │ │ │ ├── READMD.md │ │ │ ├── get_imgs.py │ │ │ └── requirements.txt │ │ ├── static-resources/ │ │ │ ├── index.ts │ │ │ ├── src/ │ │ │ │ ├── constant/ │ │ │ │ │ ├── account.ts │ │ │ │ │ ├── chart.ts │ │ │ │ │ ├── firebase.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loopring.ts │ │ │ │ │ ├── market.ts │ │ │ │ │ ├── miningOuterLinks.ts │ │ │ │ │ ├── networks.ts │ │ │ │ │ ├── notification.ts │ │ │ │ │ ├── proLayout.ts │ │ │ │ │ ├── router.ts │ │ │ │ │ ├── sagaStatus.ts │ │ │ │ │ ├── setting.ts │ │ │ │ │ ├── social.ts │ │ │ │ │ ├── table.ts │ │ │ │ │ ├── tokenConfig.ts │ │ │ │ │ ├── trade.ts │ │ │ │ │ ├── vendor.ts │ │ │ │ │ └── walletConnector.ts │ │ │ │ ├── error/ │ │ │ │ │ ├── errorMap.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en_US/ │ │ │ │ │ │ ├── common.ts │ │ │ │ │ │ ├── error.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── landPage.ts │ │ │ │ │ │ ├── layout.ts │ │ │ │ │ │ ├── tables.ts │ │ │ │ │ │ └── webEarn.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── zh_CN/ │ │ │ │ │ ├── common.ts │ │ │ │ │ ├── error.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── landPage.ts │ │ │ │ │ ├── layout.ts │ │ │ │ │ ├── tables.ts │ │ │ │ │ └── webEarn.ts │ │ │ │ ├── loopring-interface/ │ │ │ │ │ ├── CoinInterface.ts │ │ │ │ │ ├── FooterInterface.ts │ │ │ │ │ ├── HeaderInterface.ts │ │ │ │ │ ├── Investment.ts │ │ │ │ │ ├── VendorInterface.ts │ │ │ │ │ ├── WallectInterface.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── svg/ │ │ │ │ │ ├── Icon.tsx │ │ │ │ │ ├── dropdownLogo.tsx │ │ │ │ │ ├── earnLogo.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── network.tsx │ │ │ │ │ ├── redPacketScope.tsx │ │ │ │ │ ├── redPacketSvg.tsx │ │ │ │ │ └── shareReferral.tsx │ │ │ │ ├── themes/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ ├── color-lib.ts │ │ │ │ │ │ ├── global.tsx │ │ │ │ │ │ └── reset.tsx │ │ │ │ │ ├── fonts/ │ │ │ │ │ │ └── english/ │ │ │ │ │ │ └── LICENSE │ │ │ │ │ ├── globalSetup.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── overrides/ │ │ │ │ │ ├── muTheme.ts │ │ │ │ │ ├── overrides-date-pick.ts │ │ │ │ │ ├── overrides-mui.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── utils/ │ │ │ │ ├── format_tools.ts │ │ │ │ ├── index.ts │ │ │ │ ├── log_tools.ts │ │ │ │ ├── makeDom.ts │ │ │ │ ├── obj_tools.ts │ │ │ │ ├── types.tsx │ │ │ │ └── util.ts │ │ │ └── types.d.ts │ │ ├── tsconfig.json │ │ └── tsconfig.tsbuildinfo │ ├── component-lib/ │ │ ├── .babelrc │ │ ├── .eslintignore │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .storybook/ │ │ │ ├── main.ts │ │ │ └── preview.tsx │ │ ├── .travis.yml │ │ ├── README.md │ │ ├── craco.config.cjs │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── basic-lib/ │ │ │ │ │ ├── Icon.stories.tsx │ │ │ │ │ ├── btns/ │ │ │ │ │ │ ├── BtnPercentage.tsx │ │ │ │ │ │ ├── Button.stories.tsx │ │ │ │ │ │ ├── Button.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── checkbox/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── color.stories.tsx │ │ │ │ │ ├── display/ │ │ │ │ │ │ ├── SpaceBetweenBox.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── empty/ │ │ │ │ │ │ ├── Empty.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── form/ │ │ │ │ │ │ ├── Form.stories.tsx │ │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── useFocusRef.ts │ │ │ │ │ │ │ └── usePanelRef.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── input/ │ │ │ │ │ │ ├── CollectionInput.tsx │ │ │ │ │ │ ├── DatePicker.tsx │ │ │ │ │ │ ├── Default.tsx │ │ │ │ │ │ ├── InputButton.tsx │ │ │ │ │ │ ├── InputCode.tsx │ │ │ │ │ │ ├── InputCoin.tsx │ │ │ │ │ │ ├── InputMaxButton.tsx │ │ │ │ │ │ ├── InputMaxCoin.tsx │ │ │ │ │ │ ├── InputSearch.tsx │ │ │ │ │ │ ├── InputSelect.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── TextareaWithCount.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── style.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── lists/ │ │ │ │ │ │ ├── CoinList.tsx │ │ │ │ │ │ ├── CollectionItem.tsx │ │ │ │ │ │ ├── FileListItem.tsx │ │ │ │ │ │ ├── HeadMenuItem.tsx │ │ │ │ │ │ ├── HeadToolbar.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── List.stories.tsx │ │ │ │ │ │ ├── NFTList.tsx │ │ │ │ │ │ ├── Notification.tsx │ │ │ │ │ │ ├── SubMenuList.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── loading/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── media/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── nftImage.tsx │ │ │ │ │ ├── panel/ │ │ │ │ │ │ ├── IPFSSourceUpload.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── Panel.stories.tsx │ │ │ │ │ │ ├── QRCodeUpload.tsx │ │ │ │ │ │ ├── SubMenu.tsx │ │ │ │ │ │ ├── SwitchPanel.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── popover/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── Popover.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── popover.stories.tsx │ │ │ │ │ ├── resource/ │ │ │ │ │ │ ├── hook/ │ │ │ │ │ │ │ └── useImage.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── table-pagination/ │ │ │ │ │ │ ├── TablePagination.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tables/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── Table.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── Formatters/ │ │ │ │ │ │ │ │ ├── CellActionsFormatter.tsx │ │ │ │ │ │ │ │ ├── CellDepthFormatter.tsx │ │ │ │ │ │ │ │ ├── CellExpanderFormatter.tsx │ │ │ │ │ │ │ │ ├── ChildRowDeleteButton.tsx │ │ │ │ │ │ │ │ ├── ImageFormatter.tsx │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── HeaderRenderers/ │ │ │ │ │ │ │ │ ├── SortableHeaderCell.tsx │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ └── RowRenders/ │ │ │ │ │ │ │ ├── RowDepthFormatter.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── hook/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── useClickOutside.ts │ │ │ │ │ │ │ ├── useCombinedRefs.ts │ │ │ │ │ │ │ ├── useFocusRef.ts │ │ │ │ │ │ │ └── useLatestFunc.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── table.stories.tsx │ │ │ │ │ └── tags/ │ │ │ │ │ ├── Tags.stories.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── block/ │ │ │ │ │ ├── AmmCard.tsx │ │ │ │ │ ├── AmmPairDetail.tsx │ │ │ │ │ ├── AssetTitle.tsx │ │ │ │ │ ├── Block.stories.tsx │ │ │ │ │ ├── CollectionDetailView.tsx │ │ │ │ │ ├── CollectionMedia.tsx │ │ │ │ │ ├── DepthRaw.tsx │ │ │ │ │ ├── DownloadPanel.tsx │ │ │ │ │ ├── ETHStakingDetail.tsx │ │ │ │ │ ├── ErrorBlock.tsx │ │ │ │ │ ├── Interface.ts │ │ │ │ │ ├── LoadingBlock.tsx │ │ │ │ │ ├── MarketBlock.tsx │ │ │ │ │ ├── NotificationPanel.tsx │ │ │ │ │ ├── RedPacket.tsx │ │ │ │ │ ├── Referral.tsx │ │ │ │ │ ├── SettingPanel.tsx │ │ │ │ │ ├── ShareSocialPanel.tsx │ │ │ │ │ ├── TagIconList.tsx │ │ │ │ │ ├── TradeRace.tsx │ │ │ │ │ ├── TradeTitle.tsx │ │ │ │ │ ├── Vip.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── nftMedia.tsx │ │ │ │ ├── bottomRule/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── carousel/ │ │ │ │ │ ├── Carousel.tsx │ │ │ │ │ ├── Interface.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── charts/ │ │ │ │ │ ├── constant.ts │ │ │ │ │ ├── doughnutChart/ │ │ │ │ │ │ ├── DoughnutChart.stories.tsx │ │ │ │ │ │ ├── DoughnutChart.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── scaleAreaChart/ │ │ │ │ │ ├── APRChart/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── DepthChart/ │ │ │ │ │ │ ├── ScaleAreaChart.stories.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── KlineChart/ │ │ │ │ │ │ ├── KlineChart.stories.tsx │ │ │ │ │ │ ├── KlineChart.tsx │ │ │ │ │ │ ├── KlineChart_min.stories.tsx │ │ │ │ │ │ ├── data.ts │ │ │ │ │ │ ├── data_min.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ScaleAreaChart.tsx │ │ │ │ │ ├── TrendChart/ │ │ │ │ │ │ ├── ScaleAreaChart.stories.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── data.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── datetimerangepicker/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── footer/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── header/ │ │ │ │ │ ├── Header.tsx │ │ │ │ │ ├── Interface.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── toolbar/ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ ├── WalletConnect.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── layout/ │ │ │ │ │ ├── AmmDetail.stories.tsx │ │ │ │ │ ├── Error.stories.tsx │ │ │ │ │ ├── layer2.stories.tsx │ │ │ │ │ └── trade.stories.tsx │ │ │ │ ├── modal/ │ │ │ │ │ ├── ClosureAnnouncementModal.tsx │ │ │ │ │ ├── ModalPanelBase.tsx │ │ │ │ │ ├── ModalPanels/ │ │ │ │ │ │ ├── AccountBase.tsx │ │ │ │ │ │ ├── AddAsset.tsx │ │ │ │ │ │ ├── BasicPanel.tsx │ │ │ │ │ │ ├── BridgePanel.tsx │ │ │ │ │ │ ├── CheckActiveStatus.tsx │ │ │ │ │ │ ├── CheckImportCollection.tsx │ │ │ │ │ │ ├── ClaimWithdraw.tsx │ │ │ │ │ │ ├── ClaimWithdrawPanel.tsx │ │ │ │ │ │ ├── CoinbaseSmartWalletModal.tsx │ │ │ │ │ │ ├── Connect.tsx │ │ │ │ │ │ ├── CreateAccount.tsx │ │ │ │ │ │ ├── CreateRedPacketPanel.tsx │ │ │ │ │ │ ├── DeployNFT.tsx │ │ │ │ │ │ ├── Deposit.tsx │ │ │ │ │ │ ├── DepositNFT.tsx │ │ │ │ │ │ ├── DepositPanel.tsx │ │ │ │ │ │ ├── DepositRecorder.tsx │ │ │ │ │ │ ├── Dual.tsx │ │ │ │ │ │ ├── EditContact.tsx │ │ │ │ │ │ ├── ExportAccount.tsx │ │ │ │ │ │ ├── FeeSelect.tsx │ │ │ │ │ │ ├── ForceWithdraw.tsx │ │ │ │ │ │ ├── ForceWithdrawPanel.tsx │ │ │ │ │ │ ├── HadAccount.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── MintNFT.tsx │ │ │ │ │ │ ├── ModalAccount.tsx │ │ │ │ │ │ ├── NoAccount.tsx │ │ │ │ │ │ ├── QRAddressPanel.tsx │ │ │ │ │ │ ├── RedPacket.tsx │ │ │ │ │ │ ├── SendAsset.tsx │ │ │ │ │ │ ├── SendNFTAsset.tsx │ │ │ │ │ │ ├── ThirdPanelReturn.tsx │ │ │ │ │ │ ├── Transfer.tsx │ │ │ │ │ │ ├── TransferPanel.tsx │ │ │ │ │ │ ├── TransferToTaikoAccount.tsx │ │ │ │ │ │ ├── TransferToTaikoAccountPanel.tsx │ │ │ │ │ │ ├── UnlockAccount.tsx │ │ │ │ │ │ ├── UpdateAccount.tsx │ │ │ │ │ │ ├── Vault.tsx │ │ │ │ │ │ ├── Withdraw.tsx │ │ │ │ │ │ ├── WithdrawPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── QRCode/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── QRCode.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── RedPacketPanels/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── RedPacketModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Vault/ │ │ │ │ │ │ ├── VaultBorrowPanel.tsx │ │ │ │ │ │ ├── VaultExitPanel.tsx │ │ │ │ │ │ ├── VaultJoinPanel.tsx │ │ │ │ │ │ ├── VaultLoanPanel.tsx │ │ │ │ │ │ ├── VaultRepayPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── WalletConnect/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── ModalWalletConnect.tsx │ │ │ │ │ │ ├── ProviderMenu.tsx │ │ │ │ │ │ ├── WalletConnectQRCode.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── WalletPanels/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── LockWallet.tsx │ │ │ │ │ │ ├── ModalWallet.tsx │ │ │ │ │ │ ├── WalletApprove.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── accountList_new.stories.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── setting/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── SettingFee.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── walletList.stories.tsx │ │ │ │ ├── panel/ │ │ │ │ │ ├── FilterMarketsPanel.tsx │ │ │ │ │ ├── Panel.stories.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── provider.tsx │ │ │ │ ├── share/ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ ├── components/ │ │ │ │ │ │ └── SocialButton.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── styled.ts │ │ │ │ ├── tableList/ │ │ │ │ │ ├── QuoteTable/ │ │ │ │ │ │ ├── MarketDetail.tsx │ │ │ │ │ │ ├── MarketTable.tsx │ │ │ │ │ │ ├── QuoteTable.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ammRecordTable/ │ │ │ │ │ │ ├── AmmRecordTable.stories.tsx │ │ │ │ │ │ ├── AmmRecordTable.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ammTable/ │ │ │ │ │ │ ├── AmmTable.stories.tsx │ │ │ │ │ │ ├── AmmTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ └── Filter.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── interface.ts │ │ │ │ │ ├── assetsTable/ │ │ │ │ │ │ ├── AssetsDefiTable.tsx │ │ │ │ │ │ ├── AssetsTable.tsx │ │ │ │ │ │ ├── VaultAssetsTable.tsx │ │ │ │ │ │ ├── VaultPositionsTable.tsx │ │ │ │ │ │ ├── assetsTable.stories.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── ActionMemo.tsx │ │ │ │ │ │ │ ├── CoinIcons.tsx │ │ │ │ │ │ │ ├── Filter.tsx │ │ │ │ │ │ │ └── modal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── btradeSwapTable/ │ │ │ │ │ │ ├── BtradeSwapTable.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── defiTxsTable/ │ │ │ │ │ │ ├── DefiStakingTable.tsx │ │ │ │ │ │ ├── DefiStakingTxTable.tsx │ │ │ │ │ │ ├── DefiTxsTable.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ └── ActionMemo.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── dualTable/ │ │ │ │ │ │ ├── DualTable.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── dualTxsTable/ │ │ │ │ │ │ ├── DualAssetTable.tsx │ │ │ │ │ │ ├── DualTxsTable.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── investOverviewTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── InvestOverviewTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── Filter.tsx │ │ │ │ │ │ │ └── expends.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── myPoolTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── MyPoolTable.stories.tsx │ │ │ │ │ │ ├── MyPoolTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── ActionPop.tsx │ │ │ │ │ │ │ └── Filter.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── nftTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── TsNFTTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── Filter.tsx │ │ │ │ │ │ │ └── modal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── orderHistoryTable/ │ │ │ │ │ │ ├── OrderHistoryTable.tsx │ │ │ │ │ │ ├── SingleOrderHistoryTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── Filter.tsx │ │ │ │ │ │ │ └── modal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── poolsTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── PoolsTable.stories.tsx │ │ │ │ │ │ ├── PoolsTable.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── redPacketTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── RedPacketBlindBoxReceiveTable.tsx │ │ │ │ │ │ ├── RedPacketClaimTable.tsx │ │ │ │ │ │ ├── RedPacketReceiveTable.tsx │ │ │ │ │ │ ├── RedPacketRecodTable.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── textTooltip.tsx │ │ │ │ │ ├── rewardTable/ │ │ │ │ │ │ ├── ReferralsTable.tsx │ │ │ │ │ │ ├── RefundTable.tsx │ │ │ │ │ │ ├── RewardTable.tsx │ │ │ │ │ │ ├── RewardsTable.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tableList.stories.tsx │ │ │ │ │ ├── taikoFarmingTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── TaikoFarmingPortfolioTable.tsx │ │ │ │ │ │ ├── TaikoTarmingTxRecordsTable.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tradeNFTTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── TradeNFTTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ └── Filter.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tradeRaceTable/ │ │ │ │ │ │ ├── TradeRaceTable.tsx │ │ │ │ │ │ ├── TradeRaceTableConfig.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── tradeRroTable/ │ │ │ │ │ │ ├── TradePro.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── tradeTable.stories.tsx │ │ │ │ │ ├── tradeTable/ │ │ │ │ │ │ ├── TradeTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ └── Filter.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── tradeTable.stories.tsx │ │ │ │ │ ├── transactionsTable/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── TransactionTable.tsx │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── Filter.tsx │ │ │ │ │ │ │ └── modal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── vaultTable/ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ ├── VaultTxTable.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── text-tooltip/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── toast/ │ │ │ │ │ └── index.tsx │ │ │ │ └── tradePanel/ │ │ │ │ ├── Amm/ │ │ │ │ │ ├── AmmPanel.tsx │ │ │ │ │ ├── Interface.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Deposit/ │ │ │ │ │ ├── DepositGroup.tsx │ │ │ │ │ ├── DepositTitle.tsx │ │ │ │ │ ├── Interface.tsx │ │ │ │ │ ├── VendorMenu.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ExportAccount/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── Interface.ts │ │ │ │ ├── ModalPanel.tsx │ │ │ │ ├── Panel.stories.tsx │ │ │ │ ├── Reset/ │ │ │ │ │ ├── ActiveAccountPanel.tsx │ │ │ │ │ ├── ConfirmationPanel.tsx │ │ │ │ │ ├── ResetPanel.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── Swap/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── components/ │ │ │ │ │ ├── AddressType.tsx │ │ │ │ │ ├── AmmWrap/ │ │ │ │ │ │ ├── AmmDeposit.tsx │ │ │ │ │ │ ├── AmmWithdraw.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── styled.ts │ │ │ │ │ ├── BanxaConfirm.tsx │ │ │ │ │ ├── BasicACoinInput.tsx │ │ │ │ │ ├── BasicACoinTrade.tsx │ │ │ │ │ ├── BasicANFTTrade.tsx │ │ │ │ │ ├── CollectionAdvanceWrap.tsx │ │ │ │ │ ├── CollectionManageWrap.tsx │ │ │ │ │ ├── ContactSelection.tsx │ │ │ │ │ ├── CreateCollectionWrap.tsx │ │ │ │ │ ├── CreateRedPacketWrap.tsx │ │ │ │ │ ├── DeFiWrap/ │ │ │ │ │ │ ├── DeFiStackOneSideWrap.tsx │ │ │ │ │ │ ├── DeFiStackRedeemWrap.tsx │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── deFiWrap.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── DeployNFTWrap.tsx │ │ │ │ │ ├── DepositConfirm.tsx │ │ │ │ │ ├── DepositNFTWrap.tsx │ │ │ │ │ ├── DepositWrap.tsx │ │ │ │ │ ├── DualWrap/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── ModifyParameter.tsx │ │ │ │ │ │ ├── ModifySetting.tsx │ │ │ │ │ │ ├── dualDetail.tsx │ │ │ │ │ │ ├── dualWrap.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ExportAccountWrap.tsx │ │ │ │ │ ├── ForceWithdrawConfirm.tsx │ │ │ │ │ ├── ForceWithdrawWrap.tsx │ │ │ │ │ ├── ImportCollectionWrap.tsx │ │ │ │ │ ├── ImportRedPacketWrap.tsx │ │ │ │ │ ├── Interface.ts │ │ │ │ │ ├── MintAdvanceNFTWrap.tsx │ │ │ │ │ ├── MintNFTBlock.tsx │ │ │ │ │ ├── MintNFTConfirm.tsx │ │ │ │ │ ├── RampConfirm.tsx │ │ │ │ │ ├── ResetWrap.tsx │ │ │ │ │ ├── Styled.ts │ │ │ │ │ ├── SwapWrap/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── SwapMenuList.tsx │ │ │ │ │ │ ├── SwapTradeWrap.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TargetRedpacketWrap.tsx │ │ │ │ │ ├── TransferConfirm.tsx │ │ │ │ │ ├── TransferNFTBurn.tsx │ │ │ │ │ ├── TransferWrap.tsx │ │ │ │ │ ├── VaultWrap/ │ │ │ │ │ │ ├── Interface.ts │ │ │ │ │ │ ├── VaultBorrow.tsx │ │ │ │ │ │ ├── VaultJoin.tsx │ │ │ │ │ │ ├── VaultRepay.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── styled.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── WithdrawConfirm.tsx │ │ │ │ │ ├── WithdrawWrap.tsx │ │ │ │ │ ├── hook/ │ │ │ │ │ │ ├── BasicACoinPanelHook.tsx │ │ │ │ │ │ └── useAddressType.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── tool/ │ │ │ │ │ ├── Dialogs.tsx │ │ │ │ │ ├── FeeList.tsx │ │ │ │ │ ├── Property.tsx │ │ │ │ │ ├── Refresh.tsx │ │ │ │ │ ├── SlippagePanel.tsx │ │ │ │ │ ├── ToolBarItems.tsx │ │ │ │ │ ├── TradeMenuList.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── tradePro/ │ │ │ │ ├── Interface.ts │ │ │ │ ├── hookCommon.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── limitTrade.tsx │ │ │ │ ├── marketTrade.tsx │ │ │ │ └── stopLimitTrade.tsx │ │ │ ├── index.ts │ │ │ ├── react-app-env.d.ts │ │ │ ├── reportWebVitals.ts │ │ │ ├── setupTests.ts │ │ │ ├── sharedPages/ │ │ │ │ ├── VaultPage/ │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── DashBoardPanel.tsx │ │ │ │ │ │ ├── HomePanel.tsx │ │ │ │ │ │ ├── ModalWrap.tsx │ │ │ │ │ │ ├── TradePanel.tsx │ │ │ │ │ │ ├── VaultPageUI.tsx │ │ │ │ │ │ ├── VaultSwapView.tsx │ │ │ │ │ │ ├── modals.tsx │ │ │ │ │ │ └── useVaultSwapExtends.tsx │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ ├── useModals.ts │ │ │ │ │ │ ├── useVaultDashBoard.tsx │ │ │ │ │ │ ├── useVaultMarket.ts │ │ │ │ │ │ ├── useVaultPage.ts │ │ │ │ │ │ └── useVaultSwap.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── index.ts │ │ │ ├── static.ts │ │ │ ├── stores/ │ │ │ │ ├── index.ts │ │ │ │ └── reducer/ │ │ │ │ ├── modals/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── reducer.ts │ │ │ │ ├── settings/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── reducer.ts │ │ │ │ └── toggle/ │ │ │ │ ├── hook.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ └── reducer.ts │ │ │ ├── types/ │ │ │ │ └── lib.ts │ │ │ └── utils/ │ │ │ └── closureAnnouncementUtils.ts │ │ ├── tsconfig.json │ │ └── tsconfig.test.json │ ├── core/ │ │ ├── .babelrc │ │ ├── .eslintignore │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── README.md │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src/ │ │ │ ├── TimeoutCheckProvider.tsx │ │ │ ├── abi/ │ │ │ │ ├── erc20ABI.ts │ │ │ │ ├── index.ts │ │ │ │ └── taikoDepositABI.ts │ │ │ ├── api_wrapper/ │ │ │ │ ├── index.ts │ │ │ │ └── wallet.ts │ │ │ ├── component/ │ │ │ │ ├── BtnConnect.tsx │ │ │ │ ├── NotificationItem.tsx │ │ │ │ ├── index.ts │ │ │ │ └── styled.ts │ │ │ ├── defs/ │ │ │ │ └── index.ts │ │ │ ├── hookConnect.tsx │ │ │ ├── hooks/ │ │ │ │ ├── common/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useAddrCheck.ts │ │ │ │ │ ├── useAllowances.ts │ │ │ │ │ ├── useBtnStatus.ts │ │ │ │ │ ├── useCollectionImport.ts │ │ │ │ │ ├── useCollectionManage.ts │ │ │ │ │ ├── useDebounce.ts │ │ │ │ │ ├── useGetOrderHistorys.ts │ │ │ │ │ ├── useHookBtn.ts │ │ │ │ │ ├── useInjectWeb3Modal.ts │ │ │ │ │ ├── useIsHebao.ts │ │ │ │ │ ├── useMarket.ts │ │ │ │ │ ├── useMyCollection.ts │ │ │ │ │ ├── useMyNFTCollection.ts │ │ │ │ │ ├── useNFT.tsx │ │ │ │ │ ├── useNotification.ts │ │ │ │ │ ├── usePairMatch.ts │ │ │ │ │ ├── useThrottle.ts │ │ │ │ │ ├── useToast.ts │ │ │ │ │ ├── useTrade.ts │ │ │ │ │ └── useUserWallets.ts │ │ │ │ ├── help/ │ │ │ │ │ ├── coinPairInit.ts │ │ │ │ │ ├── connectStatusCallback.tsx │ │ │ │ │ ├── formatPrice.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── makeCache.ts │ │ │ │ │ ├── makeDefiSideStaking.ts │ │ │ │ │ ├── makeDual.ts │ │ │ │ │ ├── makeInvest.ts │ │ │ │ │ ├── makeMarketTrend.ts │ │ │ │ │ ├── makeTickView.ts │ │ │ │ │ ├── makeUIAmmActivityMap.ts │ │ │ │ │ ├── makeWallet.ts │ │ │ │ │ ├── marketPairHelp.ts │ │ │ │ │ ├── marketRedPacket.ts │ │ │ │ │ ├── marketTable.ts │ │ │ │ │ ├── parseRabbitConfig.ts │ │ │ │ │ ├── providorConnect.ts │ │ │ │ │ ├── useAmmTotalValue.ts │ │ │ │ │ └── volumeToCount.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rooters/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useLocationChange.ts │ │ │ │ └── useractions/ │ │ │ │ ├── hookAmmCommon.ts │ │ │ │ ├── hookAmmExit.ts │ │ │ │ ├── hookAmmJoin.ts │ │ │ │ ├── hookSwap.ts │ │ │ │ ├── index.ts │ │ │ │ ├── useAccountModal.ts │ │ │ │ ├── useActiveAccount.ts │ │ │ │ ├── useBanxaConfirm.ts │ │ │ │ ├── useBtrade.ts │ │ │ │ ├── useCheckAccStatus.tsx │ │ │ │ ├── useClaimConfirm.ts │ │ │ │ ├── useCoinbaseWalletPassword.ts │ │ │ │ ├── useCollectionAdvanceMeta.ts │ │ │ │ ├── useContact.ts │ │ │ │ ├── useContactAdd.tsx │ │ │ │ ├── useCreateRedPacket.ts │ │ │ │ ├── useDefiTrade.ts │ │ │ │ ├── useDeposit.ts │ │ │ │ ├── useDualEdit.ts │ │ │ │ ├── useDualTrade.ts │ │ │ │ ├── useEditCollection.tsx │ │ │ │ ├── useExportAccount.ts │ │ │ │ ├── useForceWithdraw.ts │ │ │ │ ├── useNFTBurn.ts │ │ │ │ ├── useNFTDeploy.ts │ │ │ │ ├── useNFTDeposit.ts │ │ │ │ ├── useNFTMintAdvance.ts │ │ │ │ ├── useNFTTransfer.ts │ │ │ │ ├── useNFTWithdraw.ts │ │ │ │ ├── useRampConfirm.ts │ │ │ │ ├── useRedpacket.ts │ │ │ │ ├── useReset.ts │ │ │ │ ├── useStakeTrade.ts │ │ │ │ ├── useStakeTradeExit.ts │ │ │ │ ├── useStakingAprTrend.ts │ │ │ │ ├── useTaikoLock.ts │ │ │ │ ├── useTransfer.ts │ │ │ │ ├── useTransferToTaikoAccount.ts │ │ │ │ ├── useUpdateAccount.ts │ │ │ │ ├── useVendor.ts │ │ │ │ ├── useWithdraw.ts │ │ │ │ └── vault/ │ │ │ │ ├── index.ts │ │ │ │ ├── useAccountInfo.ts │ │ │ │ ├── useVaultBorrow.ts │ │ │ │ ├── useVaultJoin.ts │ │ │ │ ├── useVaultLoan.ts │ │ │ │ ├── useVaultRedeem.ts │ │ │ │ ├── useVaultRepay.ts │ │ │ │ └── utils.ts │ │ │ ├── index.ts │ │ │ ├── modal/ │ │ │ │ ├── AccountL1Modal/ │ │ │ │ │ ├── hook.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── AccountModal/ │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── NFTDetail.tsx │ │ │ │ │ │ └── QRCodeScanner.tsx │ │ │ │ │ ├── hook.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── AmmPoolModal/ │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── ammPanel.tsx │ │ │ │ │ │ ├── ammRecordPanel.tsx │ │ │ │ │ │ └── chartAndInfo.tsx │ │ │ │ │ ├── hooks.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── DualModal/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── RedPacketModal/ │ │ │ │ │ ├── hook.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── WalletModal/ │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ ├── react-app-env.d.ts │ │ │ ├── reportWebVitals.ts │ │ │ ├── services/ │ │ │ │ ├── account/ │ │ │ │ │ ├── accountServices.ts │ │ │ │ │ ├── activateAccount.ts │ │ │ │ │ ├── checkAccount.ts │ │ │ │ │ ├── command.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── lockAccount.ts │ │ │ │ │ ├── networkUpdate.ts │ │ │ │ │ ├── resetAccount.ts │ │ │ │ │ ├── unlockAccount.ts │ │ │ │ │ └── useAccountHook.ts │ │ │ │ ├── banxa/ │ │ │ │ │ ├── banxaService.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── offFaitService.ts │ │ │ │ ├── claimServices/ │ │ │ │ │ ├── claimServices.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── collectionServices/ │ │ │ │ │ ├── collectionService.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── connect/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useConnectHook.ts │ │ │ │ ├── depositServices/ │ │ │ │ │ ├── depositServices.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── fee/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useChargeFees.ts │ │ │ │ ├── index.ts │ │ │ │ ├── ipfs/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── ipfsService.ts │ │ │ │ │ └── useIpfs.ts │ │ │ │ ├── mintServices/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mintService.ts │ │ │ │ │ ├── useNFTMeta.ts │ │ │ │ │ └── useNFTMint.ts │ │ │ │ ├── qrcodeServices/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── qrCodeService.ts │ │ │ │ │ └── useQrcodeScan.ts │ │ │ │ ├── redpacket/ │ │ │ │ │ └── index.ts │ │ │ │ └── socket/ │ │ │ │ ├── hook/ │ │ │ │ │ ├── useL2CommonSocket.ts │ │ │ │ │ ├── useNotificationSocket.ts │ │ │ │ │ ├── useWalletLayer2Socket.ts │ │ │ │ │ └── useWalletLayer2WithNFTSocket.ts │ │ │ │ ├── index.ts │ │ │ │ ├── services/ │ │ │ │ │ ├── ammPoolService.ts │ │ │ │ │ ├── bookService.ts │ │ │ │ │ ├── btradeOrderbookService.ts │ │ │ │ │ ├── l2CommonService.ts │ │ │ │ │ ├── mixorderService.ts │ │ │ │ │ ├── mixtradeService.ts │ │ │ │ │ ├── notificationService.ts │ │ │ │ │ ├── orderbookService.ts │ │ │ │ │ ├── tickerService.ts │ │ │ │ │ ├── tradeService.ts │ │ │ │ │ └── walletLayer2Service.ts │ │ │ │ └── socketUtil.ts │ │ │ ├── storage/ │ │ │ │ └── index.ts │ │ │ ├── stores/ │ │ │ │ ├── Amm/ │ │ │ │ │ ├── AmmActivityMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── AmmMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── account/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── amount/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── reducer.ts │ │ │ │ ├── contacts/ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── global/ │ │ │ │ │ └── actions.ts │ │ │ │ ├── index.ts │ │ │ │ ├── invest/ │ │ │ │ │ ├── BtradeMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── DefiMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── DualMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── InvestTokenTypeMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── StakingMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── VaultMap/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── VaultTicker/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── localStore/ │ │ │ │ │ ├── coinbaseSmartWalletPersist/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── confirmation/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── favoriteMarket/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── favoriteVaultMarket/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── layer1Store/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── reducer.ts │ │ │ │ │ │ └── saga.ts │ │ │ │ │ ├── nftRefresh/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── offRamp/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── onchainHashInfo/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── redPacket/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── tradeProSettings/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ └── walletInfo/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── reducer.ts │ │ │ │ ├── notify/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── redPacket/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── rootSaga.ts │ │ │ │ ├── router/ │ │ │ │ │ ├── amm/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── modals/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── tradeBtrade/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── tradeDefi/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── tradeDual/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── tradeLite/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── tradePro/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ ├── tradeStake/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── reducer.ts │ │ │ │ │ └── tradeVault/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── reducer.ts │ │ │ │ ├── socket/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── system/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── targetRedpackt/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── ticker/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── token/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── tokenPrices/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── userRewards/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── vaultLayer2/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── walletL2Collection/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── walletL2NFTCollection/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── walletLayer1/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ ├── walletLayer2/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── reducer.ts │ │ │ │ │ └── saga.ts │ │ │ │ └── walletLayer2NFT/ │ │ │ │ ├── hook.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── reducer.ts │ │ │ │ └── saga.ts │ │ │ ├── types.d.ts │ │ │ └── utils/ │ │ │ ├── .gitignore │ │ │ ├── AESMd5.ts │ │ │ ├── address.ts │ │ │ ├── addressTypeMap.ts │ │ │ ├── calculation.ts │ │ │ ├── coinbaseSmartWallet.ts │ │ │ ├── decimal.ts │ │ │ ├── dt_tools.ts │ │ │ ├── feeInfo.ts │ │ │ ├── formatter_tool.ts │ │ │ ├── genAvatar.ts │ │ │ ├── getStateFnState.ts │ │ │ ├── index.ts │ │ │ ├── makeMeta.ts │ │ │ ├── numberFormat.ts │ │ │ ├── promise.ts │ │ │ ├── readFileQrcode.ts │ │ │ ├── retry.ts │ │ │ ├── swap_utils.ts │ │ │ ├── tryFn.ts │ │ │ ├── validation.ts │ │ │ ├── waitForTx.ts │ │ │ └── web3_tools.ts │ │ ├── tsconfig.json │ │ └── tsconfig.test.json │ ├── web-bridge/ │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── README.md │ │ ├── craco.config.js │ │ ├── electron-builder.env │ │ ├── generate-react-cli.json │ │ ├── jest.config.js │ │ ├── jest.setup.js │ │ ├── package.json │ │ ├── public/ │ │ │ ├── electron.js │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── hook.ts │ │ │ ├── hookAccountInit.ts │ │ │ ├── index.tsx │ │ │ ├── layouts/ │ │ │ │ ├── footer/ │ │ │ │ │ └── index.tsx │ │ │ │ └── header/ │ │ │ │ ├── hook.tsx │ │ │ │ └── index.tsx │ │ │ ├── pages/ │ │ │ │ ├── DepositPage/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── ErrorPage/ │ │ │ │ │ └── index.tsx │ │ │ │ └── LoadingPage/ │ │ │ │ └── index.tsx │ │ │ ├── react-app-env.d.ts │ │ │ ├── reportWebVitals.ts │ │ │ ├── routers/ │ │ │ │ └── index.tsx │ │ │ ├── setupTests.ts │ │ │ └── types.d.ts │ │ └── tsconfig.json │ ├── web-developer/ │ │ └── public/ │ │ ├── badge.xml │ │ ├── browserconfig.xml │ │ ├── dark.css │ │ ├── developer.css │ │ ├── developer.js │ │ ├── developer.scss │ │ ├── footer.jsx │ │ ├── index.html │ │ ├── light.css │ │ ├── manifest.json │ │ ├── reset.css │ │ ├── robots.txt │ │ └── vercel.json │ ├── web-earn/ │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── ADD_NEW_CHAIN.md │ │ ├── README.md │ │ ├── craco.config.js │ │ ├── electron-builder.env │ │ ├── generate-react-cli.json │ │ ├── jest.config.js │ │ ├── jest.setup.js │ │ ├── package.json │ │ ├── public/ │ │ │ ├── badge.xml │ │ │ ├── browserconfig.xml │ │ │ ├── electron.js │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── constant/ │ │ │ │ ├── index.ts │ │ │ │ ├── router.ts │ │ │ │ └── setting.ts │ │ │ ├── hook.ts │ │ │ ├── hookAccountInit.ts │ │ │ ├── index.tsx │ │ │ ├── layouts/ │ │ │ │ ├── footer/ │ │ │ │ │ └── index.tsx │ │ │ │ └── header/ │ │ │ │ ├── hook.tsx │ │ │ │ └── index.tsx │ │ │ ├── pages/ │ │ │ │ ├── AssetPage/ │ │ │ │ │ ├── AssetPanel/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── HistoryPanel/ │ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── useDualAsset.tsx │ │ │ │ │ ├── RewardsPanel/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── BtradeSwapPage/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── EarnPage/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── ErrorPage/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── IntroPage/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── InvestPage/ │ │ │ │ │ ├── DeFiPanel/ │ │ │ │ │ │ ├── DeFiTradePanel.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── DualPanel/ │ │ │ │ │ │ ├── BeginnerMode.tsx │ │ │ │ │ │ ├── ChooseDualType.tsx │ │ │ │ │ │ ├── DualListPanel.tsx │ │ │ │ │ │ └── hook.ts │ │ │ │ │ ├── LeverageETHPanel/ │ │ │ │ │ │ ├── TradePanel.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── MyLiquidityPanel/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── interface.ts │ │ │ │ │ ├── OverviewPanel/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── PoolsPanel/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Layer2Page/ │ │ │ │ │ ├── ContactPanel/ │ │ │ │ │ │ ├── delete.tsx │ │ │ │ │ │ ├── history.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ForcewithdrawPanel/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ReferralRewardsPanel/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── SecurityPanel/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── VipPanel/ │ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── LoadingPage/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── MarkdownPage/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── investMarkdown.tsx │ │ │ │ │ └── notifyMarkdown.tsx │ │ │ │ ├── QuotePage/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useMaket.ts │ │ │ │ ├── TaikoLockPage/ │ │ │ │ │ ├── BannerPage.tsx │ │ │ │ │ ├── LogInToCleanLrTaiko.tsx │ │ │ │ │ ├── MintRedeemModal.tsx │ │ │ │ │ ├── PendingTxsModal.tsx │ │ │ │ │ ├── TaikoLockInput.tsx │ │ │ │ │ ├── TxSubmitModal.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── TradeRacePage/ │ │ │ │ ├── hook.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── interface.ts │ │ │ │ ├── rank.tsx │ │ │ │ └── snow.css │ │ │ ├── react-app-env.d.ts │ │ │ ├── reportWebVitals.ts │ │ │ ├── routers/ │ │ │ │ └── index.tsx │ │ │ ├── setupTests.ts │ │ │ └── types.d.ts │ │ └── tsconfig.json │ ├── web-guardian/ │ │ ├── .babelrc │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── README.md │ │ ├── craco.config.js │ │ ├── electron-builder.env │ │ ├── generate-react-cli.json │ │ ├── jest.config.js │ │ ├── jest.setup.js │ │ ├── package.json │ │ ├── public/ │ │ │ ├── electron.js │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── src/ │ │ │ ├── App.tsx │ │ │ ├── common.ts │ │ │ ├── hook.ts │ │ │ ├── hookAccountInit.ts │ │ │ ├── index.tsx │ │ │ ├── layouts/ │ │ │ │ ├── footer/ │ │ │ │ │ └── index.tsx │ │ │ │ └── header/ │ │ │ │ ├── hook.tsx │ │ │ │ └── index.tsx │ │ │ ├── pages/ │ │ │ │ ├── ErrorPage/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── LoadingPage/ │ │ │ │ │ └── index.tsx │ │ │ │ └── WalletPage/ │ │ │ │ ├── GuardianModal.tsx │ │ │ │ ├── WalletHistory.tsx │ │ │ │ ├── WalletProtector.tsx │ │ │ │ ├── WalletValidationInfo.tsx │ │ │ │ ├── hook.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── modal/ │ │ │ │ │ └── index.tsx │ │ │ │ └── utils.ts │ │ │ ├── react-app-env.d.ts │ │ │ ├── reportWebVitals.ts │ │ │ ├── routers/ │ │ │ │ └── index.tsx │ │ │ ├── setupTests.ts │ │ │ └── types.d.ts │ │ └── tsconfig.json │ ├── web-wallet/ │ │ └── public/ │ │ ├── badge.xml │ │ ├── browserconfig.xml │ │ ├── electron.js │ │ ├── footer.jsx │ │ ├── index.html │ │ ├── manifest.json │ │ ├── reset.css │ │ ├── robots.txt │ │ ├── wallet.css │ │ ├── wallet.js │ │ ├── wallet.scss │ │ ├── wallet_dark.css │ │ └── wallet_light.css │ └── webapp/ │ ├── .babelrc │ ├── .eslintrc.json │ ├── .gitignore │ ├── ADD_NEW_CHAIN.md │ ├── README.md │ ├── craco.config.js │ ├── electron-builder.env │ ├── generate-react-cli.json │ ├── jest.config.js │ ├── jest.setup.js │ ├── package.json │ ├── public/ │ │ ├── badge.xml │ │ ├── browserconfig.xml │ │ ├── electron.js │ │ ├── index.html │ │ ├── manifest.json │ │ └── robots.txt │ ├── src/ │ │ ├── App.tsx │ │ ├── hook.ts │ │ ├── hookAccountInit.ts │ │ ├── index.tsx │ │ ├── layouts/ │ │ │ ├── footer/ │ │ │ │ └── index.tsx │ │ │ └── header/ │ │ │ ├── hook.tsx │ │ │ └── index.tsx │ │ ├── pages/ │ │ │ ├── AssetPage/ │ │ │ │ ├── AssetPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── HistoryPanel/ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useDualAsset.tsx │ │ │ │ ├── RewardsPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ ├── BtradeSwapPage/ │ │ │ │ └── index.tsx │ │ │ ├── ErrorPage/ │ │ │ │ └── index.tsx │ │ │ ├── FiatPage/ │ │ │ │ └── index.tsx │ │ │ ├── InvestPage/ │ │ │ │ ├── DeFiPanel/ │ │ │ │ │ ├── DeFiTradePanel.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── DualPanel/ │ │ │ │ │ ├── BeginnerMode.tsx │ │ │ │ │ ├── ChooseDualType.tsx │ │ │ │ │ ├── DualListPanel.tsx │ │ │ │ │ └── hook.tsx │ │ │ │ ├── LeverageETHPanel/ │ │ │ │ │ ├── TradePanel.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── MyLiquidityPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── interface.ts │ │ │ │ ├── OverviewPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── PoolsPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── StakePanel/ │ │ │ │ │ └── StackTradePanel.tsx │ │ │ │ └── index.tsx │ │ │ ├── LandPage/ │ │ │ │ ├── Card.tsx │ │ │ │ ├── HomePage.tsx │ │ │ │ ├── LandPage.tsx │ │ │ │ ├── WalletPage.tsx │ │ │ │ ├── coins.json │ │ │ │ ├── index.ts │ │ │ │ └── style.tsx │ │ │ ├── Layer2Page/ │ │ │ │ ├── ContactPanel/ │ │ │ │ │ ├── delete.tsx │ │ │ │ │ ├── history.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── ForcewithdrawPanel/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── Notification/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── ReferralRewardsPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── SecurityPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── VipPanel/ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ ├── LoadingPage/ │ │ │ │ └── index.tsx │ │ │ ├── MarkdownPage/ │ │ │ │ ├── index.tsx │ │ │ │ ├── investMarkdown.tsx │ │ │ │ └── notifyMarkdown.tsx │ │ │ ├── MiningPage/ │ │ │ │ ├── hook.ts │ │ │ │ └── index.tsx │ │ │ ├── NFTPage/ │ │ │ │ ├── CollectionPanel/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── EditCollectionPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── ImportCollectionPanel/ │ │ │ │ │ ├── CollectionManage.tsx │ │ │ │ │ ├── ImportCollection.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── MintNFTPanel/ │ │ │ │ │ ├── hook.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── metaNFTPanel.tsx │ │ │ │ ├── MyNFT/ │ │ │ │ │ ├── MyNFTCollectionList.tsx │ │ │ │ │ ├── MyNFTList.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── useMyNFT.ts │ │ │ │ ├── NFTDeposit/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── NFThistory/ │ │ │ │ │ ├── hookHistory.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── components/ │ │ │ │ │ ├── CollectionItemPanel.tsx │ │ │ │ │ ├── landingPanel.tsx │ │ │ │ │ ├── titleNFTMobile.tsx │ │ │ │ │ └── usePublicNFTs.ts │ │ │ │ └── index.tsx │ │ │ ├── ProTradePage/ │ │ │ │ ├── hookPro.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── panel/ │ │ │ │ │ ├── chart/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── klineConfig.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── market/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── orderTable/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── spot/ │ │ │ │ │ │ ├── hookLimit.ts │ │ │ │ │ │ ├── hookMarket.ts │ │ │ │ │ │ ├── hookStopLimit.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── stopLimit.tsx │ │ │ │ │ ├── toolbar/ │ │ │ │ │ │ ├── hook.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── walletInfo/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── stopLimitInfo.tsx │ │ │ │ ├── proService.ts │ │ │ │ └── stopLimtPage.tsx │ │ │ ├── QuotePage/ │ │ │ │ ├── hook.ts │ │ │ │ ├── index.tsx │ │ │ │ └── useMaket.ts │ │ │ ├── RedPacketPage/ │ │ │ │ ├── CreateRedPacketPanel/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── MyRedPacketPanel/ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── RedPacketClaimPanel/ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ └── index.tsx │ │ │ │ ├── RedPacketMarketPanel/ │ │ │ │ │ ├── hooks.ts │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ ├── SwapPage/ │ │ │ │ └── index.tsx │ │ │ └── TradeRacePage/ │ │ │ ├── hook.ts │ │ │ ├── index.tsx │ │ │ ├── interface.ts │ │ │ ├── rank.tsx │ │ │ └── snow.css │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ ├── routers/ │ │ │ └── index.tsx │ │ ├── setupTests.ts │ │ └── types.d.ts │ └── tsconfig.json ├── tsconfig.build.json └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .claude/settings.local.json ================================================ { "permissions": { "allow": [ "Bash(rg:*)", "Bash(grep:*)" ], "deny": [] } } ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # parcel-bundler cache (https://parceljs.org/) .cache # next.js build output .next # nuxt.js build output .nuxt # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # production **/build /out /dist # misc .DS_Store .env.local .env.test.local **/.env.development # .env.prod_for_dev npm-debug.log* yarn-debug.log* yarn-error.log* *.code-workspace .vscode openapitools.json *.tgz .idea/ .yarn/ testkey.txt test_datadir #yalc .yalc yalc.lock .windsurfrules ================================================ FILE: .prettierignore ================================================ # Ignore artifacts: .idea/** .vscode/** /node_modules **/dist **/build **/node_modules ================================================ FILE: .prettierrc.json ================================================ { "semi": false, "tabWidth": 2, "printWidth": 100, "singleQuote": true, "trailingComma": "all", "jsxSingleQuote": true, "bracketSpacing": true } ================================================ FILE: .yarnclean ================================================ # test directories __tests__ test tests powered-test # test exceptions !**/viem/**/test !**/viem/**/test/** # asset directories docs doc website images # assets # examples example examples # code coverage directories coverage .nyc_output # build scripts Makefile Gulpfile.js Gruntfile.js # configs appveyor.yml circle.yml codeship-services.yml codeship-steps.yml wercker.yml .tern-project .gitattributes .editorconfig .*ignore .eslintrc .jshintrc .flowconfig .documentup.json .yarn-metadata.json .travis.yml # misc *.md !@storybook/react/dist/esm/client/docs ================================================ FILE: .yarnrc ================================================ sass_binary_site "https://npm.taobao.org/mirrors/node-sass/" phantomjs_cdnurl "https://npm.taobao.org/mirrors/phantomjs/" electron_mirror "https://npm.taobao.org/mirrors/electron/" electron_builder_binaries_mirror "https://mirrors.huaweicloud.com/electron-builder-binaries/" #registry "https://registry.npm.taobao.org" ================================================ FILE: LICENSE ================================================ Licensor: Loopring Technology Limited Licensed Work: Loopring-web/loopring-sdk The Licensed Work is (c) 2022 Loopring Technology Limited ----------------------------------------------------------------------------- Terms The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make limited-production use of the Licensed Work, on condition that Loopring L2 protocol/technology is supported on your platform that this Licensed Work is being used. If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must acquire a full licensing agreement from the Licensor, or its affiliated entities, or you must refrain from using the Licensed Work. All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License. This License applies separately for each version of the Licensed Work. You must conspicuously display this License on each original or modified copy of the Licensed Work. If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work. Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work. This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License). TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. ----------------------------------------------------------------------------- ================================================ FILE: README.md ================================================

Loopring-website

Loopring Application

Ethereum’s First zkRollup Layer2

Fast, Secure, and 100x Lower Fees

[![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Loopring/loopring-web-v2/master/LICENSE) [![type-badge](https://img.shields.io/npm/types/react-data-grid)](https://www.npmjs.com/package/react-data-grid)
## 🚀 Quick Start ```bash // with yarn yarn install yarn up cd ./packages/webapp npm run dev ``` ## 📚 Loopring UI component [StoryBook](https://static.loopring.io/storybook-static/) ```bash cd ./packages/component-lib npm run storybook ``` ## 🏗 Framework Design ![](https://static.loopring.io/Loopring%20framwork.png) ## 👉 [What is Loopring?](https://loopring.org/#/) ## 🫂 Community - [Loopring Website](https://loopring.org/) - [Loopring Exchange](https://loopring.io/#/layer2) - [Loopring Reddit](https://www.reddit.com/r/loopringorg/) - [Loopring Medium](https://medium.com/loopring-protocol) - [Loopring Twitter](https://twitter.com/loopringorg) - [Loopring Telegram](https://t.me/loopring_en) ## 👺 For Developer - We appreciate any improvements or initiatives for Loopring Layer2 website, please view the source code in `./packages/component-lib`. - The project contains a separate lib "web3-provider", which is a third-party ETH web3 wallet provider service ( WalletConnect & metamask), - You are welcome to reuse it or integrate your provider service with our website. - Feel free to leave suggestions or ideas. ### 📒 API & Dependency - [Web3-Provider](https://www.npmjs.com/package/@loopring-web/web3-provider) - [Loopring-sdk](https://www.npmjs.com/package/@loopring-web/loopring-sdk) - [APIs](https://docs.loopring.io/) ## 🙋 Protocol & Architecture - [Whitepaper](https://loopring.org/resources/en_whitepaper.pdf) - [Design Docs](https://github.com/LoopringSecondary/docs/wiki/Loopring3_Design) ## ❓[Help](https://desk.zoho.com/portal/loopring/en/home) ## 🔑 Security - [Wallet](https://security.loopring.io/) - [Protocol Audit](https://loopring.org/resources/loopring1.0_audit.pdf) ## Release Process loopring.io, wallet.loopring.io, docs.loopring.io, earn.loopring.io, static.loopring.io, and loopring.io are now auto deployed using Vercel. ================================================ FILE: craco.config.cjs ================================================ const webpack = require('webpack') const {addBeforeLoader, loaderByName, getLoader, addPlugins} = require('@craco/craco'); const CopyWebpackPlugin = require('copy-webpack-plugin') // Try the environment variable, otherwise use root const ASSET_PATH = process.env.ASSET_PATH || '/' // const rewireLess = require('react-app-rewire-less') // const { alias } = require('react-app-rewire-alias') const path = require('path') // const GitRevisionPlugin = require('git-revision-webpack-plugin') // const gitRevisionPlugin = new GitRevisionPlugin() const direct = './' const packagesPath = './packages' module.exports = function ({env}) { const dev = env === 'development' const prod = env === 'production' return { babel: { presets: [ /* ... */], plugins: [ [ "import", { libraryName: 'antd', libraryDirectory: 'es', style: true, } ], ], loaderOptions: { /* ... */}, loaderOptions: (babelLoaderOptions, {env, paths}) => { const dev = env === 'development' const prod = env === 'production' // console.log(babelLoaderOptions) return babelLoaderOptions; }, }, webpack: { alias: { '@material-ui/core/Menu': '@mui/material/Menu', '@material-ui/core': '@mui/material', '@material-ui/core/Popover': '@mui/material/Popover', process: "process/browser" }, plugins: { add: [ new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, packagesPath, 'common-resources', 'assets'), to: './static', toType: 'dir', }, ], }), new webpack.DefinePlugin({ 'process.env.TIME': '"' + new Date().toLocaleString('en-US', {timeZone: 'Asia/Shanghai'}) + '/SH"', }), new webpack.ProvidePlugin({ process: 'process/browser', }), new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], }), ], }, configure: (config, {env, paths}) => { const dev = env === 'development' const prod = env === 'production' config.ignoreWarnings = [/Failed to parse source map/]; config.resolve.fallback = Object.assign(config.resolve.fallback ?? {}, { "crypto": require.resolve("crypto-browserify"), "crypto-js": require.resolve('crypto-js'), "crypto-js/sha256": require.resolve('crypto-js/sha256'), "stream": require.resolve("stream-browserify"), "assert": require.resolve("assert/"), "http": require.resolve("stream-http"), "https": require.resolve("https-browserify"), "os": require.resolve("os-browserify"), "url": require.resolve("url/"), "util": require.resolve("util"), "buffer": require.resolve("buffer"), "timers": require.resolve("timers-browserify"), 'process/browser': require.resolve('process/browser'), "fs":false, "path":false }) config.node = {global: true} console.log(config.node) // node: { global: true, fs: 'empty'}, // config.packages({ // packages: [ // { // name: 'crypto-js', // location: 'path-to/bower_components/crypto-js', // main: 'index' // } // ] // }); var {isFound, match} = getLoader(config, loaderByName('babel-loader')); if (isFound) { console.log(match.loader) match.loader.include = [ path.resolve(__dirname, packagesPath), ...[ path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@web3modal`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@walletconnect`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@metamask`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@scure`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@noble`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@ethereumjs`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/micro-ftch`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/react-spring`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@react-spring`, ), path.resolve( __dirname, `${process.env.NODE_ENV === 'development' ? direct : direct}`, `node_modules/@loopring-web/loopring-sdk`, ), ], ] } config.resolve.extensions.push('.html'); config.resolve.extensions.push('.md'); addBeforeLoader(config, loaderByName('babel-loader'), { loader: 'html-loader', test: /\.html$/i, exclude: [/node_modules/, /index.html/i], options: { attrs: [':data-src'], minimize: true, removeComments: false, collapseWhitespace: false, }, }); addBeforeLoader(config, loaderByName('babel-loader'), { test: /\.md$/, use: 'raw-loader', }); // } return config; }, }, }; }; ================================================ FILE: lerna.json ================================================ { "version": "independent", "npmClient": "yarn", "command": { "publish": { "ignoreChanges": [ "*.md" ] } }, "useWorkspaces": true, "packages": [ "packages/*" ] } ================================================ FILE: package.json ================================================ { "name": "loopring-web", "version": "1.3.0", "author": "Loopring Dex Frontend Team", "description": "dex web app new version", "private": true, "license": "SEE LICENSE IN LICENSE", "engines": { "node": "22.x" }, "dependencies": { "@coinbase/onchainkit": "^0.38.7", "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", "@loopring-web/loopring-sdk": "3.9.23", "@loopring-web/recharts": "^2.0.10", "@loopring-web/web3-provider": "1.4.16", "@mui/icons-material": "^5.16.6", "@mui/lab": "5.0.0-alpha.45", "@mui/material": "5.6.2", "@ramp-network/ramp-instant-sdk": "^4.0.4", "@react-buddy/ide-toolbox": "^2.3.1", "@react-buddy/palette-mui": "^5.0.1", "@react-spring/parallax": "^9.4.4", "@reduxjs/toolkit": "^1.8.1", "@reown/appkit": "1.7.2", "@reown/appkit-adapter-ethers5": "1.7.2", "@reown/appkit-adapter-wagmi": "^1.7.2", "assert": "^2.0.0", "bignumber.js": "9.1.1", "bn.js": "^5.2.1", "cids": "^1.1.9", "clsx": "^1.1.1", "crypto-browserify": "^3.12.0", "crypto-js": "^4.1.1", "d3-format": "2.0.0", "d3-time-format": "3.0.0", "decimal.js": "^10.4.3", "dompurify": "^2.4.0", "dotenv": "^8.2.0", "dotenv-webpack": "^7.0.3", "easy-template-string": "^1.0.1", "echarts": "^5.2.2", "echarts-for-react": "^3.0.2", "ethers": "^5.5.4", "ffjavascript": "^0.1.0", "firebase": "^9.23.0", "github-markdown-css": "^5.2.0", "html5-qrcode": "2.3.4", "i18next": "^23.2.3", "immutable": "^4.0.0-rc.12", "ipfs-http-client": "^56.0.2", "jsqr": "^1.4.0", "lottie-react": "^2.1.0", "material-ui-popup-state": "^1.9.3", "moment": "^2.29.1", "polished": "^4.1.1", "qr-code-styling": "^1.6.0-rc.1", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", "react-currency-input-field": "^3.3.1", "react-data-grid": "^7.0.0-canary.49", "react-dom": "^18.2.0", "react-dropzone": "^12.0.4", "react-financial-charts": "^2.0.1", "react-grid-layout": "^1.3.0", "react-i18next": "^11.8.12", "react-markdown": "^8.0.7", "react-redux": "^7.2.3", "react-redux-firebase": "^3.11.0", "react-resizable": "^3.0.5", "react-router-dom": "^5.3.4", "react-scripts": "5.0.1", "react-scripts-rewired": "3.1.1", "react-share": "^4.4.1", "react-spring": "^9.7.1", "react-swipeable-views": "^0.13.9", "react-use": "^17.4.0", "react-virtualized": "^9.22.5", "react-virtuoso": "^1.8.6", "redux-actions": "^2.6.5", "redux-observable": "^1.2.0", "redux-persist": "^6.0.0", "redux-saga": "^1.2.3", "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.0", "rxjs": "^7.8.1", "three": "^0.149.0", "timers": "^0.1.1", "timers-browserify": "^2.0.12", "util": "^0.12.5", "vconsole": "^3.15.1", "viem": "^2.27.0", "voca": "^1.4.1", "wagmi": "^2.14.16", "web-vitals": "^3.4.0" }, "build": { "files": [ "build/**/*", "node_modules/**/*" ], "publish": { "provider": "custom", "repo": "ssh://git@github.com/loopring/dexwebappv2", "owner": "loopring dev" } }, "scripts": { "clean": "yarn autoclean --force", "docker": "cd ./packages/webapp; yarn build; cd devops docker-compose build", "boot": "echo y | lerna clean; sleep 1; lerna bootstrap --force-local", "up": "echo y | lerna clean; yarn", "up2": "yarn unlink \"loopring-sdk'\"; echo y | lerna clean; yarn", "reset": "npm config delete proxy; rm -rf node_modules/ yarn.lock; echo y | lerna clean; yarn", "reset_proxy": "npm config set proxy=http://127.0.0.1:1087; yarn reset", "dev": "cd ./packages/webapp; yarn dev", "dev-bridge": "cd packages/web-bridge; yarn dev", "dev-guardian": "cd packages/web-guardian; yarn dev", "dev_earn": "cd packages/web-earn; yarn dev", "build": "cd ./packages/webapp; yarn build", "build-sb": "cd ./packages/component-lib; npm run build", "build-bridge": "cd packages/web-bridge; yarn build", "build-guardian": "cd packages/web-guardian; yarn build", "build_dev": "cd ./packages/webapp; yarn build_dev", "build_earn": "cd packages/web-earn; yarn build", "deploy": "cd ./packages/webapp; yarn deploy", "deploy:ipfs": "cd ./packages/webapp; yarn deploy:ipfs", "sass": "node-sass packages/web-wallet/public/*.scss packages/web-wallet/public/*.css" }, "browserslist": { "production": [ "last 2 chrome version", "last 2 firefox version", "last 2 safari version" ], "development": [ "last 2 chrome version", "last 2 firefox version", "last 2 safari version" ] }, "workspaces": { "packages": [ "packages/*" ], "nohoist": [ "**/babel-loader", "**/babel-eslint" ] }, "prettier": { "semi": false, "tabWidth": 2, "printWidth": 100, "singleQuote": true, "trailingComma": "all", "jsxSingleQuote": true, "bracketSpacing": true }, "devDependencies": { "@babel/core": "^7.13.14", "@babel/eslint-parser": "^7.13.14", "@babel/helper-builder-react-jsx": "^7.10.4", "@babel/helper-builder-react-jsx-experimental": "^7.12.11", "@babel/helper-explode-assignable-expression": "^7.18.6", "@babel/helper-function-name": "^7.24.7", "@babel/helper-split-export-declaration": "^7.24.7", "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-transform-exponentiation-operator": "^7.22.5", "@babel/plugin-transform-typescript": "^7.22.5", "@babel/preset-env": "^7.13.12", "@babel/preset-react": "^7.13.13", "@babel/preset-typescript": "^7.13.0", "@craco/craco": "^7.1.0", "@craco/types": "^7.1.0", "@manaflair/redux-batch": "^1.0.0", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/react-hooks": "^5.1.0", "@testing-library/user-event": "^12.1.10", "@types/crypto-js": "^4.1.1", "@types/dompurify": "^2.3.4", "@types/ethereumjs-abi": "^0.6.3", "@types/ethereumjs-tx": "^2.0.0", "@types/jest": "^26.0.15", "@types/lodash": "^4.14.168", "@types/node": "^12.0.0", "@types/qrcode-svg": "^1.1.1", "@types/react": "^18.2.19", "@types/react-beautiful-dnd": "^13.1.2", "@types/react-dom": "^18.2.7", "@types/react-grid-layout": "^1.1.2", "@types/react-router-dom": "^5.1.7", "@types/react-swipeable-views": "^0.13.0", "@types/react-virtualized": "^9.21.11", "@types/webpack": "^5.28.1", "@typescript-eslint/eslint-plugin": "^5.59.9", "@typescript-eslint/parser": "^4.33.0", "JSONStream": "^1.3.5", "babel-plugin-import": "^1.13.3", "babel-plugin-named-exports-order": "^0.0.2", "browserify-fs": "^1.0.0", "buffer": "^6.0.3", "copy-webpack-plugin": "6", "craco": "^0.0.3", "cross-env": "^7.0.3", "crypto-browserify": "^3.12.0", "csp-html-webpack-plugin": "^5.1.0", "csv-parse": "^5.1.0", "customize-cra": "^1.0.0", "decimal.js": "^10.4.3", "dotenv-cli": "^4.0.0", "eslint": "^7.23.0", "eslint-config-react-app": "^6.0.0", "eslint-plugin-flowtype": "^5.4.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^1.7.0", "generate-react-cli": "^5.2.3", "git-revision-webpack-plugin": "^3.0.6", "html-loader": "^0.5.5", "html5-qrcode": "2.3.4", "https-browserify": "^1.0.0", "lerna": "^7.1.5", "prettier": "2.8.8", "process": "^0.11.10", "prop-types": "^15.8.1", "react-app-rewire-alias": "^0.2.0", "react-app-rewired": "^2.2.1", "react-router": "^5.2.1", "storybook-addon-styled-component-theme": "^1.3.0", "stream-browserify": "^3.0.0", "truffle-privatekey-provider": "1.3.0", "typescript": "^4.1.2", "url": "^0.11.1", "webpack": "5.88.2" }, "resolutions": { "bufferutil": "npm:-@0.0.1" } } ================================================ FILE: packages/common-resources/.babelrc ================================================ { "presets": [ "@babel/typescript", [ "@babel/env", { "modules": false } ] ], "plugins": [ "@babel/plugin-proposal-class-properties" ] } ================================================ FILE: packages/common-resources/assets/coin/loopring.json ================================================ {"file":"loopring_431f2ed4.png","frames":{"0XBTC":{"x":0,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"1INCH":{"x":72,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"AAVE":{"x":144,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"AC":{"x":216,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ADX":{"x":288,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ALCX":{"x":360,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ALEND":{"x":432,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ALINK":{"x":504,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"AMP":{"x":576,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ANT":{"x":648,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"AUC":{"x":720,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":73,"sourceH":73},"AUSDC":{"x":793,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BADGER":{"x":865,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BAL":{"x":937,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BAND":{"x":1009,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BAT":{"x":1081,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BCDT":{"x":1153,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BCP":{"x":1225,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BEL":{"x":1297,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BERA":{"x":1369,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"BIT":{"x":1441,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BKT":{"x":1513,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BNB":{"x":1585,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"BNT":{"x":1657,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BOME":{"x":1729,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"BOR":{"x":1801,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BTC":{"x":1873,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"BTC2XFLI":{"x":1945,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":74,"sourceH":73},"BTU":{"x":2019,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BUSD":{"x":2091,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"BZRX":{"x":2163,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"CBBTC":{"x":2235,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"CDAI":{"x":2307,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"CEL":{"x":2379,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"CETH":{"x":2451,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"CIETH":{"x":2523,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"COMP":{"x":2595,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"CRV":{"x":2667,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"CUSDC":{"x":2739,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"CVT":{"x":2811,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"DAI":{"x":2883,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"DEFIL":{"x":2955,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"DEP":{"x":3027,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"DOGE":{"x":3099,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"DOUGH":{"x":3171,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"DPI":{"x":3243,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"DPR":{"x":3315,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"DXD":{"x":3387,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ENJ":{"x":3459,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ENS":{"x":3531,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ENTRP":{"x":3603,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ETH-1":{"x":3675,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ETH":{"x":3747,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ETHFI":{"x":3819,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"FARM":{"x":3891,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"FIN":{"x":3963,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"FLI":{"x":4035,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"FLX":{"x":4107,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":79,"sourceH":73},"FST":{"x":4186,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"FUSE":{"x":4258,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"GNO":{"x":4330,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"GRG":{"x":4402,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"GRID":{"x":4474,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"GRT":{"x":4546,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"HBTC":{"x":4618,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":73,"sourceH":73},"HEX":{"x":4691,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"HT":{"x":4763,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ICHI":{"x":4835,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"IDLE":{"x":4907,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"IMX":{"x":4979,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"INDEX":{"x":5051,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"JRT":{"x":5123,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"KAI":{"x":5195,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"KEEP":{"x":5267,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"KNC":{"x":5339,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"KP3R":{"x":5411,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"LDO":{"x":5483,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"LINK":{"x":5555,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"LQTY":{"x":5627,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"LRC":{"x":5699,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"LRTAIKO":{"x":5771,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"MANA":{"x":5843,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"MASK":{"x":5915,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"MATIC":{"x":5987,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"MCB":{"x":6059,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"MKR":{"x":6131,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"MOVD":{"x":6203,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"MTA":{"x":6275,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"NEC":{"x":6347,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"NEST":{"x":6419,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"NIOX":{"x":6491,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":75,"sourceH":73},"NMR":{"x":6566,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"OBTC":{"x":6638,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"OGN":{"x":6710,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"OMG":{"x":6782,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ONG":{"x":6854,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ORDI":{"x":6926,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"OVR":{"x":6998,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"PAX":{"x":7070,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"PBTC":{"x":7142,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"PEPE":{"x":7214,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"PLTC":{"x":7286,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"PNK":{"x":7358,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"PNT":{"x":7430,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"QCAD":{"x":7502,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RAI":{"x":7574,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":73,"sourceH":73},"REN":{"x":7647,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RENBTC":{"x":7719,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RENDOGE":{"x":7791,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"REP":{"x":7863,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"REPT":{"x":7935,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RETH":{"x":8007,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RFOX":{"x":8079,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RGT":{"x":8151,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RICE":{"x":8223,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RPL":{"x":8295,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RSPT":{"x":8367,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"RSR":{"x":8439,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SAND":{"x":8511,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SENT":{"x":8583,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SMARTCREDIT":{"x":8655,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SNT":{"x":8727,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SNX":{"x":8799,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SOL":{"x":8871,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"STAKE":{"x":8943,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SUSD":{"x":9015,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SUSHI":{"x":9087,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"SX":{"x":9159,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"TAIKO":{"x":9231,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"TEL":{"x":9303,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"TLOS":{"x":9375,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"TRB":{"x":9447,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"TRUMP":{"x":9519,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":53,"sourceH":72},"TRYB":{"x":9572,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":73,"sourceH":73},"TUSD":{"x":9645,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":73,"sourceH":73},"UBT":{"x":9718,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"UMA":{"x":9790,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"UNI":{"x":9862,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"USDC":{"x":9934,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"USDT":{"x":10006,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"VBZRX":{"x":10078,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"VETH":{"x":10150,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"VSP":{"x":10222,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"WBTC":{"x":10294,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"WETH":{"x":10366,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"WIF":{"x":10438,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"WNXM":{"x":10510,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"WOO":{"x":10582,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"WSTETH":{"x":10654,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"XRP":{"x":10726,"y":72,"w":72,"h":72,"offX":0,"offY":0,"sourceW":72,"sourceH":72},"YFI":{"x":10798,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"YFII":{"x":10870,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"YPIE":{"x":10942,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":72,"sourceH":73},"ZRX":{"x":11014,"y":72,"w":72,"h":73,"offX":0,"offY":0,"sourceW":73,"sourceH":73}}} ================================================ FILE: packages/common-resources/index.ts ================================================ export * from './static-resources' ================================================ FILE: packages/common-resources/mail.html ================================================
Loopring Help Center

Attach files
Each of your file(s) can be up to 20MB in size.
Loading...

   
powered by
================================================ FILE: packages/common-resources/package.json ================================================ { "name": "@loopring-web/common-resources", "version": "1.0.0", "description": "Common package for code sharing", "main": "index.ts", "build": { "files": [ "build/**/*", "node_modules/**/*" ], "publish": { "provider": "custom", "repo": "https://github.com/Loopring/loopring-web-v2", "owner": "Loopring Dev Team" } }, "scripts": { "compile": "tsc -p tsconfig.json", "build": "tsdx build", "prepublishOnly": "NODE_ENV=production tsdx build" }, "devDependencies": { "tsc": "^2.0.4" } } ================================================ FILE: packages/common-resources/scripts/.gitignore ================================================ output* *.zip *.rar *.tar* ================================================ FILE: packages/common-resources/scripts/READMD.md ================================================ require python 3.7+ pip install -r requirements.txt ================================================ FILE: packages/common-resources/scripts/get_imgs.py ================================================ #!/usr/bin/python # -- encoding= utf8 -- import requests import json from eth_utils import to_checksum_address import os from PIL import Image output_folder = 'output' output_folder2 = 'output_resize' output_folder_zapper = 'output_zapper' output_folder2_zapper = 'output_resize_zapper' SIZE = (36, 36) def gen_imgs(tokenMap): tokens = requests.get('https://api3.loopring.io/api/v3/exchange/tokens') print(tokens.status_code) tokenObjs = json.loads(tokens.text) if not os.path.exists(output_folder): os.makedirs(output_folder) if not os.path.exists(output_folder2): os.makedirs(output_folder2) if not os.path.exists(output_folder_zapper): os.makedirs(output_folder_zapper) if not os.path.exists(output_folder2_zapper): os.makedirs(output_folder2_zapper) totalNum = len(tokenObjs) count = 1 for tokenInfo in tokenObjs: symbol = str(tokenInfo['symbol']) address = str(tokenInfo['address']) if not symbol.find('LP-') >= 0: fileName = '{}.png'.format(symbol) address = to_checksum_address(tokenInfo['address']) lower_addr = address.lower() filePath = os.path.join(output_folder, fileName) filePath2 = os.path.join(output_folder_zapper, fileName) try: if not os.path.exists(filePath): url = 'https://exchange.loopring.io/assets/images/ethereum/assets/{}/logo.png'.format(address) r = requests.get(url) if r.status_code == 200: with open(filePath, "wb") as code: code.write(r.content) print('handling: {}/{}. {}'.format(count, totalNum, url)) else: print('handling: {}/{}. {}. errorFile: {}'.format(count, totalNum, url, fileName)) else: print('{}/{}. {} already existed!'.format(count, totalNum, fileName)) except: print('handling: {}/{}. {} got error!'.format(count, totalNum, fileName)) #zapper try: if not os.path.exists(filePath2) and lower_addr in tokenMap: tokenInfo = tokenMap[lower_addr] url = tokenInfo['logoURI'] r = requests.get(url) if r.status_code == 200: with open(filePath2, "wb") as code: code.write(r.content) print('handling: {}/{}. {}'.format(count, totalNum, url)) else: print('handling: {}/{}. {}. errorFile: {}'.format(count, totalNum, url, fileName)) else: print('{}/{}. {} already existed!'.format(count, totalNum, fileName)) except: print('handling: {}/{}. {} got error!'.format(count, totalNum, fileName)) else: print('{}/{}. {} is a LP Token!'.format(count, totalNum, fileName)) count += 1 list = os.listdir(output_folder) lst = [] for i in range(0, len(list)): path = os.path.join(output_folder, list[i]) path2 = os.path.join(output_folder2, list[i]) im = Image.open(path) if im.width != im.height: lst.append(path) im = im.resize(SIZE) im.save(path2, 'PNG') print('w!=h list:', lst) def fetch_zapper(): zapperInfo = requests.get('https://zapper.fi/api/token-list') print(zapperInfo.status_code) print(zapperInfo.text) tokenObjs = json.loads(zapperInfo.text) tokens = tokenObjs['tokens'] tokenMap = {} for token in tokens: tokenMap[token['address']] = token return tokenMap if __name__ == '__main__': tokenMap = fetch_zapper() gen_imgs(tokenMap) ================================================ FILE: packages/common-resources/scripts/requirements.txt ================================================ requests==2.24.0 eth_utils==1.10.0 pillow==8.3.2 ================================================ FILE: packages/common-resources/static-resources/index.ts ================================================ import i18n from './src/i18n' export { i18n } export * from './src/svg' export * from './src/i18n' export * from './src/error' export * from './src/themes' export * from './src/constant' export * from './src/loopring-interface' export * from './src/utils' ================================================ FILE: packages/common-resources/static-resources/src/constant/account.ts ================================================ import { StateBase } from './sagaStatus' import { ConnectProviders } from '@loopring-web/web3-provider' import { TokenType } from '../loopring-interface' export enum AccountStatus { UN_CONNECT = 'UN_CONNECT', // CONNECT = 'CONNECT', NO_ACCOUNT = 'NO_ACCOUNT', DEPOSITING = 'DEPOSITING', NOT_ACTIVE = 'NOT_ACTIVE', LOCKED = 'LOCKED', ACTIVATED = 'ACTIVATED', ERROR_NETWORK = 'ERROR_NETWORK', } export enum fnType { UN_CONNECT = 'UN_CONNECT', NO_ACCOUNT = 'NO_ACCOUNT', NOT_ACTIVE = 'NOT_ACTIVE', LOCKED = 'LOCKED', ACTIVATED = 'ACTIVATED', DEFAULT = 'DEFAULT', DEPOSITING = 'DEPOSITING', CONNECT = 'CONNECT', ERROR_NETWORK = 'ERROR_NETWORK', } export type Account = { accAddress: string qrCodeUrl: string readyState: keyof typeof AccountStatus | 'unknown' accountId: number level: string apiKey: string frozen: boolean | undefined eddsaKey: any publicKey: any keySeed: string nonce: number | undefined keyNonce: number | undefined connectName: ConnectProviders wrongChain?: boolean | undefined isInCounterFactualStatus?: boolean isContract1XAddress?: boolean isContractAddress?: boolean isCFAddress?: boolean isContract?: boolean _chainId?: 1 | 5 | 'unknown' _accountIdNotActive?: number _userOnModel?: boolean | undefined __timer__: NodeJS.Timer | -1 hasUnknownCollection: undefined | boolean } export type AccountState = Account & StateBase export type AccountFull = { account: Account resetAccount: () => void updateAccount: (account: Partial) => void } & StateBase export type AssetsRawDataItem = { token: { type: TokenType value: string } amount: string available: string locked: string smallBalance: boolean tokenValueDollar: number name: string withdrawAmount?: string depositAmount?: string precision: number hideDepositButton?: boolean } export const ContactLimit = 1500 ================================================ FILE: packages/common-resources/static-resources/src/constant/chart.ts ================================================ export enum ChartType { Depth = 'Depth', Trend = 'Trend', Kline = 'Kline', } export enum ChartUnit { W1 = '1W', H1 = '1H', D1 = '1D', } ================================================ FILE: packages/common-resources/static-resources/src/constant/firebase.ts ================================================ export const firebaseIOConfig = { apiKey: 'AIzaSyC484SNh-OZWco7o1xUC4UGVGVf0yZU__s', authDomain: 'loopring-d0829.firebaseapp.com', projectId: 'loopring-d0829', storageBucket: 'loopring-d0829.appspot.com', messagingSenderId: '372617031978', appId: '1:372617031978:web:15a3f35c4adbd679dc6e53', measurementId: 'G-5TWTDQ8J7J', } export const firebaseBridgeConfig = { apiKey: 'AIzaSyC484SNh-OZWco7o1xUC4UGVGVf0yZU__s', authDomain: 'loopring-d0829.firebaseapp.com', projectId: 'loopring-d0829', storageBucket: 'loopring-d0829.appspot.com', messagingSenderId: '372617031978', appId: '1:372617031978:web:bcc50f63d79ef868dc6e53', measurementId: 'G-P4XEJ3CY0J', } ================================================ FILE: packages/common-resources/static-resources/src/constant/index.ts ================================================ export * from './walletConnector' export * from './setting' export * from './market' export * from './chart' export * from './trade' export * from './router' export * from './table' export * from './loopring' export * from './account' export * from './sagaStatus' export * from './proLayout' export * from './miningOuterLinks' export * from './notification' export * from './vendor' export * from './firebase' export * from './social' export { SUPPORTING_NETWORKS } from './networks' export { WITHDRAW_TOKEN_FILTER_LIST, mapSpecialTokenName } from './tokenConfig' ================================================ FILE: packages/common-resources/static-resources/src/constant/loopring.ts ================================================ export const imgConfig = { file: 'loopring.png', frames: { '0XBTC': { x: 248, y: 282, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, '1INCH': { x: 207, y: 322, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, AAVE: { x: 166, y: 362, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, AC: { x: 125, y: 402, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ADX: { x: 84, y: 442, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ALCX: { x: 453, y: 82, w: 36, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ALEND: { x: 453, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ALINK: { x: 412, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, AMP: { x: 371, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ANT: { x: 248, y: 322, w: 36, h: 35, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, AUC: { x: 166, y: 478, w: 33, h: 32, offX: 3, offY: 2, sourceW: 37, sourceH: 36 }, AUSDC: { x: 330, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BADGER: { x: 125, y: 442, w: 37, h: 35, offX: 0, offY: 1, sourceW: 37, sourceH: 36 }, BAL: { x: 289, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BAND: { x: 100, y: 482, w: 19, h: 26, offX: 9, offY: 5, sourceW: 37, sourceH: 36 }, BAT: { x: 248, y: 242, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BCDT: { x: 205, y: 442, w: 27, h: 36, offX: 5, offY: 0, sourceW: 37, sourceH: 36 }, BCP: { x: 207, y: 282, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BEL: { x: 166, y: 322, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BKT: { x: 125, y: 362, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BNT: { x: 84, y: 402, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BOR: { x: 43, y: 442, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BTC2XFLI: { x: 289, y: 242, w: 36, h: 36, offX: 1, offY: 0, sourceW: 38, sourceH: 36 }, BTU: { x: 456, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BUSD: { x: 412, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, BZRX: { x: 371, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, CDAI: { x: 330, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, CEL: { x: 289, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, CETH: { x: 248, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, COMP: { x: 207, y: 242, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, CRV: { x: 166, y: 282, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, CUSDC: { x: 125, y: 322, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, CVT: { x: 409, y: 202, w: 33, h: 30, offX: 2, offY: 3, sourceW: 37, sourceH: 36 }, DAI: { x: 84, y: 362, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, DEP: { x: 370, y: 240, w: 31, h: 34, offX: 3, offY: 1, sourceW: 37, sourceH: 36 }, DOUGH: { x: 43, y: 402, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, DPI: { x: 2, y: 442, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, DPR: { x: 415, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, DXD: { x: 371, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ENJ: { x: 330, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ENTRP: { x: 236, y: 442, w: 21, h: 30, offX: 8, offY: 3, sourceW: 37, sourceH: 36 }, 'ETH-1': { x: 289, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ETH: { x: 248, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, FARM: { x: 166, y: 442, w: 35, h: 32, offX: 1, offY: 2, sourceW: 37, sourceH: 36 }, FIN: { x: 207, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, FLI: { x: 166, y: 242, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, FLX: { x: 2, y: 2, w: 40, h: 36, offX: 0, offY: 0, sourceW: 40, sourceH: 36 }, FUSE: { x: 207, y: 362, w: 35, h: 36, offX: 1, offY: 0, sourceW: 37, sourceH: 36 }, GNO: { x: 330, y: 202, w: 36, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, GRG: { x: 84, y: 322, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, GRID: { x: 43, y: 362, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, GRT: { x: 2, y: 402, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, HBTC: { x: 205, y: 402, w: 32, h: 36, offX: 2, offY: 0, sourceW: 37, sourceH: 36 }, HEX: { x: 452, y: 122, w: 37, h: 32, offX: 0, offY: 2, sourceW: 37, sourceH: 36 }, HT: { x: 374, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ICHI: { x: 330, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, IDLE: { x: 289, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, INDEX: { x: 241, y: 402, w: 24, h: 36, offX: 7, offY: 0, sourceW: 37, sourceH: 36 }, JRT: { x: 248, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, KAI: { x: 125, y: 481, w: 29, h: 29, offX: 4, offY: 3, sourceW: 37, sourceH: 36 }, KEEP: { x: 207, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, KNC: { x: 203, y: 482, w: 19, h: 26, offX: 9, offY: 5, sourceW: 37, sourceH: 36 }, KP3R: { x: 166, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, LINK: { x: 125, y: 242, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, LRC: { x: 412, y: 122, w: 36, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, MASK: { x: 84, y: 282, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, MCB: { x: 63, y: 482, w: 33, h: 18, offX: 2, offY: 9, sourceW: 37, sourceH: 36 }, MKR: { x: 43, y: 322, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, MTA: { x: 2, y: 362, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, NEC: { x: 333, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, NEST: { x: 289, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, NIOX: { x: 411, y: 162, w: 33, h: 36, offX: 2, offY: 0, sourceW: 38, sourceH: 36 }, NMR: { x: 248, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, OBTC: { x: 207, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, OGN: { x: 166, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, OMG: { x: 125, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ONG: { x: 84, y: 242, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, OVR: { x: 43, y: 282, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, PAX: { x: 2, y: 322, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, PBTC: { x: 292, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, PLTC: { x: 248, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, PNK: { x: 329, y: 242, w: 37, h: 34, offX: 0, offY: 1, sourceW: 37, sourceH: 36 }, PNT: { x: 207, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, QCAD: { x: 166, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RAI: { x: 166, y: 402, w: 35, h: 36, offX: 1, offY: 0, sourceW: 37, sourceH: 36 }, REN: { x: 125, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RENBTC: { x: 84, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RENDOGE: { x: 43, y: 242, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, REP: { x: 2, y: 482, w: 23, h: 28, offX: 7, offY: 4, sourceW: 37, sourceH: 36 }, REPT: { x: 2, y: 282, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RFOX: { x: 251, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RGT: { x: 207, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RICE: { x: 166, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RPL: { x: 125, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RSPT: { x: 84, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, RSR: { x: 43, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, SMARTCREDIT: { x: 2, y: 242, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, SNT: { x: 210, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, SNX: { x: 166, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, STAKE: { x: 125, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, SUSD: { x: 84, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, SUSHI: { x: 370, y: 202, w: 35, h: 34, offX: 1, offY: 1, sourceW: 37, sourceH: 36 }, SX: { x: 289, y: 282, w: 35, h: 34, offX: 1, offY: 1, sourceW: 37, sourceH: 36 }, TEL: { x: 43, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, TRB: { x: 2, y: 202, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, TRYB: { x: 169, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, TUSD: { x: 125, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, UBT: { x: 84, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, UMA: { x: 43, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, UNI: { x: 2, y: 162, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, USDC: { x: 371, y: 162, w: 36, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, USDT: { x: 128, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, VBZRX: { x: 84, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, VETH: { x: 43, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, VSP: { x: 2, y: 122, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, WBTC: { x: 87, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, WNXM: { x: 43, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, WOO: { x: 29, y: 482, w: 30, h: 21, offX: 3, offY: 8, sourceW: 37, sourceH: 36 }, YFI: { x: 2, y: 82, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, YFII: { x: 46, y: 2, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, YPIE: { x: 2, y: 42, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, ZRX: { x: 125, y: 282, w: 37, h: 36, offX: 0, offY: 0, sourceW: 37, sourceH: 36 }, }, } ================================================ FILE: packages/common-resources/static-resources/src/constant/market.ts ================================================ export enum FloatTag { increase = 'increase', decrease = 'decrease', none = 'none', } export type MarketType = `${string}-${string}` export type AMMMarketType = `AMM-${string}-${string}` export type LPTokenType = `LP-${string}-${string}` export const PrecisionTree = { 1: '0.1', 2: '0.01', 3: '0.001', 4: '0.0001', 5: '0.00001', 6: '0.000001', 7: '0.0000001', 8: '0.00000001', 9: '0.000000001', 10: '0.0000000001', 11: '0.00000000001', 12: '0.000000000001', } ================================================ FILE: packages/common-resources/static-resources/src/constant/miningOuterLinks.ts ================================================ export const getMiningLinkList = (lan: string) => { if (lan === 'cn') { lan = 'zh' } return { 'BCDT-ETH': `https://loopring.io/#/embed/amm_mining_14_${lan}`, 'USDC-USDT': `https://loopring.io/#/embed/orderbook_mining_24_${lan}`, 'BKT-USDT': `https://loopring.io/#/embed/orderbook_mining_24_${lan}`, } } ================================================ FILE: packages/common-resources/static-resources/src/constant/networks.ts ================================================ export const SUPPORTING_NETWORKS = process.env.REACT_APP_CHAIN_IDS!.split(',') ================================================ FILE: packages/common-resources/static-resources/src/constant/notification.ts ================================================ /* import { AMMMarketType, MarketType } from "./market"; */ // import { CoinKey } from "../loopring-interface"; import { Account } from './account' import { InvestAdvice } from '../loopring-interface' import * as sdk from '@loopring-web/loopring-sdk' /** * export enum RuleType { * AMM_MINING = "AMM_MINING", * SWAP_VOLUME_RANKING = "SWAP_VOLUME_RANKING", * ORDERBOOK_MINING = "ORDERBOOK_MINING", * } */ export enum NOTIFY_COLOR { default = 'default', primary = 'primary', secondary = 'secondary', tertiary = 'tertiary', } export type INVEST_ITEM = { name: string version: string type: string bannerMobile: string bannerLaptop: string linkRule: string startShow: number endShow: number link: string } export type NOTIFICATION_ITEM = { version: string //localStore for visited should be unique name: string title: string description1: string description2: string type: string link: `#race-event/${number}/${number}/activities.${string}.json` | string linkIos: string linkAndroid: string startShow: number endShow: number color: NOTIFY_COLOR banner?: string bannerDark?: string webRouter?: string webFlag: boolean versionIosMin: string versionIosMax: string versionAndroidMin: string versionAndroidMax: string linkParam: string } export type ACTIVITY = NOTIFICATION_ITEM export type CAMPAIGN_TAG = { name: string type?: 'activity' | 'protocol' | '' //options for Amm , activity| startShow: number endShow: number iconSource: string symbols: Array behavior: 'tooltips' | 'link' content: string webFlag: boolean versionIosMin: string versionIosMax: string versionAndroidMin: string versionAndroidMax: string } export enum SCENARIO { ORDERBOOK = 'ORDERBOOK', MARKET = 'MARKET', AMM = 'AMM', FIAT = 'FIAT', SWAP = 'SWAP', VAULT = 'VAULT', } export type CAMPAIGNTAGCONFIG = { [key in SCENARIO]: CAMPAIGN_TAG[] } export type RedPacketConfig = { timeRangeMaxInSecondsToken: number timeRangeMaxInSecondsNFT: number showNFT: boolean showERC20Blindbox: boolean } export type NOTIFICATION = { activities: ACTIVITY[] activitiesInvest: ACTIVITY[] notifications: NOTIFICATION_ITEM[] invest: { investAdvice: InvestAdvice[] STAKE: InvestAdvice[] } account?: Account chainId: sdk.ChainId prev?: { endDate: number // prevMonth: string; } campaignTagConfig?: CAMPAIGNTAGCONFIG redPacket: RedPacketConfig } export type Notify = Omit export type NOTIFICATIONHEADER = { notifyMap: Notify myNotifyMap: { items: N[] total: number unReads: number } } // export enum SCENARIO { // orderbook = "orderbook", // market = "market", // Amm = "Amm", // Fiat = "Fiat", // swap = "swap", // } // export type CAMPAIGNTAGCONFIG ={ // [key in SCENARIO]: CAMPAIGN_TAG[]; // } ================================================ FILE: packages/common-resources/static-resources/src/constant/proLayout.ts ================================================ import { Layouts } from 'react-grid-layout' export enum LayoutConfig { basicLayout, layout1, layout2, } export const MarketRowHeight = 20 export enum BreakPoint { xlg = 'xlg', lg = 'lg', md = 'md', sm = 'sm', xs = 'xs', xxs = 'xxs', } export type ConfigLayout

= { breakpoints: { [key in keyof P]: number } cols: { [key in keyof P]: number } layouts: { [key in keyof P]: Array<{ i: string x: number y: number w: number h: number minW: number minH: number }> } } const basicLayout: ConfigLayout = { breakpoints: { xlg: 1920, lg: 1600, md: 1200, sm: 960, xs: 768, xxs: 320 }, cols: { xlg: 24, lg: 24, md: 24, sm: 12, xs: 12, xxs: 6 }, layouts: { xlg: [ { i: 'toolbar', x: 0, y: 0, w: 24, h: 9, minW: 24, minH: 9 }, { i: 'walletInfo', x: 0, y: 10, w: 4, h: 28, minW: 4, minH: 26 }, { i: 'spot', x: 0, y: 14, w: 4, h: 115, minW: 4, minH: 70 }, { i: 'market', x: 4, y: 10, w: 4, h: 88, minW: 4, minH: 58 }, { i: 'chart', x: 8, y: 10, w: 12, h: 88, minW: 6, minH: 32 }, { i: 'market2', x: 20, y: 10, w: 4, h: 88, minW: 4, minH: 36 }, { i: 'orderTable', x: 4, y: 64, w: 20, h: 55, minW: 6, minH: 36 }, ], lg: [ { i: 'toolbar', x: 0, y: 0, w: 24, h: 9, minW: 24, minH: 9 }, { i: 'walletInfo', x: 0, y: 10, w: 4, h: 28, minW: 4, minH: 26 }, { i: 'spot', x: 0, y: 26, w: 4, h: 89, minW: 4, minH: 70 }, { i: 'market', x: 4, y: 10, w: 4, h: 73, minW: 4, minH: 58 }, { i: 'chart', x: 8, y: 10, w: 12, h: 73, minW: 6, minH: 32 }, { i: 'market2', x: 20, y: 10, w: 4, h: 73, minW: 4, minH: 58 }, { i: 'orderTable', x: 4, y: 64, w: 20, h: 44, minW: 6, minH: 36 }, ], md: [ { i: 'toolbar', x: 0, y: 0, w: 24, h: 9, minW: 24, minH: 9 }, { i: 'walletInfo', x: 0, y: 10, w: 5, h: 28, minW: 4, minH: 26 }, { i: 'spot', x: 0, y: 9, w: 5, h: 68, minW: 4, minH: 68 }, { i: 'market', x: 5, y: 10, w: 5, h: 58, minW: 4, minH: 58 }, { i: 'market2', x: 0, y: 0, w: 0, h: 0, minW: 0, minH: 0 }, { i: 'chart', x: 10, y: 10, w: 14, h: 58, minW: 6, minH: 32 }, { i: 'orderTable', x: 5, y: 64, w: 19, h: 38, minW: 6, minH: 36 }, ], sm: [ { i: 'toolbar', x: 0, y: 0, w: 12, h: 9, minW: 12, minH: 9 }, { i: 'walletInfo', x: 0, y: 10, w: 3, h: 28, minW: 3, minH: 22 }, { i: 'spot', x: 0, y: 14, w: 3, h: 66, minW: 3, minH: 66 }, { i: 'market', x: 3, y: 10, w: 3, h: 58, minW: 3, minH: 58 }, { i: 'market2', x: 0, y: 0, w: 0, h: 0, minW: 0, minH: 0 }, { i: 'chart', x: 6, y: 10, w: 6, h: 58, minW: 6, minH: 32 }, { i: 'orderTable', x: 3, y: 64, w: 9, h: 36, minW: 6, minH: 36 }, ], xs: [ { i: 'toolbar', w: 12, h: 17, x: 0, y: 0, minW: 6, minH: 9 }, { i: 'walletInfo', w: 4, h: 48, x: 0, y: 121, minW: 3, minH: 22 }, { i: 'spot', w: 4, h: 104, x: 0, y: 17, minW: 3, minH: 54 }, { i: 'market', w: 4, h: 151, x: 4, y: 17, minW: 3, minH: 58 }, { i: 'market2', w: 4, x: 8, y: 17, h: 151, minW: 3, minH: 58 }, { i: 'chart', w: 12, h: 80, x: 0, y: 239, minW: 3, minH: 32 }, { i: 'orderTable', w: 12, h: 71, x: 0, y: 168, minW: 5, minH: 36 }, ], xxs: [ { i: 'toolbar', x: 0, y: 0, w: 6, h: 14, minW: 6, minH: 9 }, { i: 'walletInfo', x: 0, y: 63, w: 3, h: 51, minW: 3, minH: 22 }, { i: 'spot', x: 0, y: 9, w: 3, h: 103, minW: 3, minH: 54 }, { i: 'market', x: 3, y: 9, w: 3, h: 154, minW: 3, minH: 58 }, { i: 'market2', x: 0, y: 0, w: 0, h: 0, minW: 0, minH: 0 }, { i: 'chart', x: 3, y: 125, w: 6, h: 80, minW: 3, minH: 32 }, { i: 'orderTable', x: 0, y: 88, w: 6, h: 80, minW: 5, minH: 36 }, ], }, } const stopLimitLayout: ConfigLayout = { breakpoints: { xlg: 1920, lg: 1600, md: 1200, sm: 960, xs: 768, xxs: 320 }, cols: { xlg: 24, lg: 24, md: 24, sm: 12, xs: 12, xxs: 6 }, layouts: { xlg: [ { w: 24, h: 9, x: 0, y: 0, i: 'toolbar', minW: 24, minH: 9 }, { w: 5, h: 79, x: 0, y: 9, i: 'spot', minW: 4, minH: 68 }, { w: 7, h: 79, x: 5, y: 9, i: 'markdown', minW: 4, minH: 20 }, { w: 12, h: 78, x: 12, y: 9, i: 'chart', minW: 6, minH: 32 }, { w: 24, h: 36, x: 0, y: 88, i: 'orderTable', minW: 6, minH: 36 }, ], lg: [ { w: 24, h: 9, x: 0, y: 0, i: 'toolbar', minW: 24, minH: 9 }, { w: 5, h: 80, x: 0, y: 9, i: 'spot', minW: 4, minH: 68 }, { w: 7, h: 44, x: 5, y: 9, i: 'markdown', minW: 4, minH: 20 }, { w: 12, h: 44, x: 12, y: 9, i: 'chart', minW: 6, minH: 32 }, { w: 19, h: 36, x: 5, y: 53, i: 'orderTable', minW: 6, minH: 36 }, ], md: [ { i: 'toolbar', x: 0, y: 0, w: 24, h: 9, minW: 24, minH: 9 }, { i: 'spot', x: 0, y: 10, w: 5, h: 81, minW: 4, minH: 68 }, { i: 'markdown', x: 5, y: 10, w: 7, h: 44, minW: 4, minH: 20 }, { i: 'chart', x: 12, y: 10, w: 12, h: 44, minW: 6, minH: 32 }, { i: 'orderTable', x: 5, y: 53, w: 19, h: 37, minW: 6, minH: 32 }, ], sm: [ { i: 'toolbar', x: 0, y: 0, w: 12, h: 9, minW: 12, minH: 9 }, // { i: "walletInfo", x: 0, y: 10, w: 3, h: 28, minW: 3, minH: 22 }, { i: 'spot', x: 0, y: 10, w: 3, h: 94, minW: 3, minH: 66 }, { i: 'markdown', x: 3, y: 10, w: 3, h: 58, minW: 3, minH: 20 }, // { i: "market2", x: 0, y: 0, w: 0, h: 0, minW: 0, minH: 0 }, { i: 'chart', x: 6, y: 10, w: 6, h: 58, minW: 6, minH: 32 }, { i: 'orderTable', x: 3, y: 64, w: 9, h: 36, minW: 6, minH: 36 }, ], xs: [ { i: 'toolbar', w: 12, h: 17, x: 0, y: 0, minW: 6, minH: 9 }, { i: 'spot', w: 3, h: 72, x: 0, y: 17, minW: 3, minH: 54 }, { i: 'markdown', w: 3, h: 72, x: 3, y: 17, minW: 3, minH: 20 }, { i: 'chart', w: 6, h: 72, x: 6, y: 17, minW: 3, minH: 32 }, { i: 'orderTable', w: 12, h: 71, x: 0, y: 89, minW: 5, minH: 36 }, ], xxs: [ { i: 'toolbar', w: 6, h: 14, x: 0, y: 0, minW: 6, minH: 9 }, { i: 'spot', w: 3, h: 73, x: 0, y: 14, minW: 3, minH: 54 }, { i: 'markdown', w: 3, h: 73, x: 3, y: 14, minW: 3, minH: 58 }, { i: 'chart', w: 6, h: 80, x: 0, y: 167, minW: 3, minH: 32 }, { i: 'orderTable', w: 6, h: 80, x: 0, y: 87, minW: 5, minH: 36 }, ], }, } export const layoutConfigs: Array = [basicLayout] export const stopLimitLayoutConfigs: Array = [stopLimitLayout] export type LayoutConfigInfo = { currentBreakpoint: BreakPoint mounted: boolean layouts: Layouts compactType: 'vertical' | 'horizontal' | null | undefined } ================================================ FILE: packages/common-resources/static-resources/src/constant/router.ts ================================================ import AutorenewIcon from '@mui/icons-material/Autorenew'; import { AssetsIcon, ContactIcon, ImageIcon, L2MyLiquidityIcon, MintIcon, ProfileIcon, RewardIcon, SecurityIcon, VipIcon, AmmIcon, BlockTradeIcon, CreateNFTIcon, DualInvestIcon, ETHStakingIcon, FiatIcon, LRCStakingIcon, LeverageETHIcon, MyCollectionIcon, MyNFTIcon, OrderBookIcon, OverviewIcon, StopLimitIcon, SwapIcon, VaultHomeIcon, VaultDashboardIcon, VaultTradeIcon2, } from '../svg' import { HeaderMenuItemInterface, HeaderMenuTabStatus, InvestAdvice } from '../loopring-interface' import { AddAssetList, InvestAssetRouter, InvestMapType, SendAssetList } from './trade' import { Earnlite, ExchangePro, WalletSite, LOOPRING_DOC, hideDefiEntry } from './setting' import { ChainId } from '@loopring-web/loopring-sdk' export const FEED_BACK_LINK = 'https://desk.zoho.com/portal/loopring/en/home' export const headerRoot = 'Landing-page' export const SoursURL = 'https://static.loopring.io/assets/' export const GUARDIAN_URL = 'https://guardian.loopring.io' export const LoopringIPFSSite = 'ipfs.loopring.io' export const LoopringIPFSSiteProtocol = 'https' export const IPFS_LOOPRING_URL = `${LoopringIPFSSiteProtocol}://${LoopringIPFSSite}` export const IPFS_HEAD_URL = 'ipfs://' export const IPFS_HEAD_URL_REG = /^ipfs:\/\/(ipfs\/)?/i export const IPFS_LOOPRING_SITE = 'https://ipfs.loopring.io/ipfs/' // sdk.LOOPRING_URLs.IPFS_META_URL; //`${IPFS_LOOPRING_URL}/ipfs/`; export const BANXA_URLS = { 1: 'https://loopring.banxa.com', 11155111: 'https://loopring.banxa-sandbox.com', } export const LOOPRING_DOCUMENT = 'https://loopring.io/#/document/' export enum RouterPath { trade = '/trade', lite = '/trade/lite', pro = '/trade/pro', stoplimit = '/trade/stoplimit', btrade = '/trade/btrade', fiat = '/trade/fiat', markets = '/markets', mining = '/mining', redPacket = '/redPacket', l2assets = '/l2assets', l2records = '/l2assets/history', l2assetsDetail = '/l2assets/assets', layer2 = '/layer2', nft = '/nft', invest = '/invest', investBalance = '/invest/balance', vault = '/portal', //404? loading loading = '/loading', } export enum InvestType { MyBalance = 0, AmmPool = 1, DeFi = 2, Overview = 3, Dual = 4, Stack = 5, LeverageETH = 6, } export const InvestRouter = [ 'balance', 'ammpool', 'defi', 'overview', 'dual', 'stakelrc', 'leverageETH', ] // // export enum Layer2RouterID { security = 'security', vip = 'vip', contact = 'contact', referralrewards = 'referralrewards', forcewithdraw = 'forcewithdraw', notification = 'notification', } // export enum ProfileKey { // security = 'security', // vip = 'vip', // contact = 'contact', // referralrewards = 'referralrewards', // forcewithdraw = 'forcewithdraw', // notification = 'notification', // } export const Profile = { security: [ { icon: SecurityIcon, router: { path: `${RouterPath.layer2}/${Layer2RouterID.security}` }, label: { id: 'security', i18nKey: 'labelSecurity', }, }, ], vip: [ { icon: VipIcon, router: { path: `${RouterPath.layer2}/${Layer2RouterID.vip}` }, label: { id: 'vip', i18nKey: 'labelVipPanel', }, }, ], contact: [ { icon: ContactIcon, router: { path: `${RouterPath.layer2}/${Layer2RouterID.contact}` }, label: { id: 'contact', i18nKey: 'labelContactsPanel', }, }, ], referralrewards: [ { icon: RewardIcon, router: { path: `${RouterPath.layer2}/${Layer2RouterID.referralrewards}` }, label: { id: 'referralrewards', i18nKey: 'labelReferralReward', }, }, ], } export enum ButtonComponentsMap { Download = 'Download', Notification = 'Notification', Setting = 'Setting', ProfileMenu = 'ProfileMenu', WalletConnect = 'WalletConnect', TestNet = 'TestNet', ColorSwitch = 'ColorSwitch', } export const toolBarAvailableItem: ButtonComponentsMap[] = [ ButtonComponentsMap.Notification, ButtonComponentsMap.Setting, ButtonComponentsMap.ProfileMenu, ButtonComponentsMap.WalletConnect, ButtonComponentsMap.TestNet, ] // export enum GuardianToolBarComponentsMap { // Notification, // TestNet, // WalletConnect, // } export const GuardianToolBarAvailableItem: ButtonComponentsMap[] = [ ButtonComponentsMap.Notification, ButtonComponentsMap.TestNet, ButtonComponentsMap.WalletConnect, ] export let headerGuardianToolBarData: { [key in ButtonComponentsMap]?: { buttonComponent: ButtonComponentsMap handleClick?: (props: any) => void [key: string]: any } } = { [ButtonComponentsMap.Notification]: { buttonComponent: ButtonComponentsMap.Notification, label: 'labelNotification', }, [ButtonComponentsMap.TestNet]: { buttonComponent: ButtonComponentsMap.TestNet }, [ButtonComponentsMap.WalletConnect]: { buttonComponent: ButtonComponentsMap.WalletConnect, label: 'labelConnectWallet', accountState: undefined, handleClick: undefined, }, } export let headerToolBarData: { [key in ButtonComponentsMap]?: { buttonComponent: ButtonComponentsMap handleClick?: (props: any) => void [key: string]: any } } = { [ButtonComponentsMap.Notification]: { buttonComponent: ButtonComponentsMap.Notification, label: 'labelNotification', }, [ButtonComponentsMap.Setting]: { buttonComponent: ButtonComponentsMap.Setting, label: 'labelSetting', }, [ButtonComponentsMap.ProfileMenu]: { buttonComponent: ButtonComponentsMap.ProfileMenu, i18nDescription: 'labelProfile', readyState: undefined, }, [ButtonComponentsMap.WalletConnect]: { buttonComponent: ButtonComponentsMap.WalletConnect, label: 'labelConnectWallet', accountState: undefined, handleClick: undefined, }, } export let headerToolBarDataMobile: { [key in ButtonComponentsMap]?: { buttonComponent: ButtonComponentsMap handleClick?: (props: any) => void [key: string]: any } } = { // [ButtonComponentsMap.Download]: { // buttonComponent: ButtonComponentsMap.Download, // url: WalletSite, // }, // [ButtonComponentsMap.Notification]: { // buttonComponent: ButtonComponentsMap.Notification, // label: 'labelNotification', // }, // [ButtonComponentsMap.Setting]: { // buttonComponent: ButtonComponentsMap.Setting, // label: 'labelSetting', // }, [ButtonComponentsMap.ProfileMenu]: { buttonComponent: ButtonComponentsMap.ProfileMenu, i18nDescription: 'labelProfile', readyState: undefined, }, [ButtonComponentsMap.WalletConnect]: { buttonComponent: ButtonComponentsMap.WalletConnect, label: 'labelConnectWallet', accountState: undefined, handleClick: undefined, }, } // export const toolBarMobileAvailableItem = [ // ButtonComponentsMap.Download, // ButtonComponentsMap.Notification, // ButtonComponentsMap.Setting, // ButtonComponentsMap.WalletConnect, // ] export enum RouterMainKey { lite = 'lite', pro = 'pro', stoplimit = 'stoplimit', btrade = 'btrade', fiat = 'fiat', markets = 'markets', mining = 'mining', redPacket = 'redPacket', l2assets = 'l2assets', layer2 = 'layer2', nft = 'nft', invest = 'invest', earn = 'earn', vault = 'portal', } export enum NFTSubRouter { transactionNFT = 'transactionNFT', mintNFTLanding = 'mintNFTLanding', mintNFT = 'mintNFT', mintNFTAdvance = 'mintNFTAdvance', depositNFT = 'depositNFT', myCollection = 'myCollection', addCollection = 'addCollection', editCollection = 'editCollection', addLegacyCollection = 'addLegacyCollection', importLegacyCollection = 'importLegacyCollection', assetsNFT = 'assetsNFT', } export let layer2ItemData: Array = [ { label: { id: 'lite', i18nKey: 'labelClassic', description: 'labelClassicDescription', icon: SwapIcon, }, router: { path: RouterPath.lite + '/${pair}' }, }, { label: { id: 'pro', i18nKey: 'labelAdvanced', description: 'labelAdvancedDescription', icon: OrderBookIcon, }, router: { path: RouterPath.pro + '/${pair}' }, }, { label: { id: 'stopLimit', i18nKey: 'labelStopLimit', description: 'labelStopLimitDescription', icon: StopLimitIcon, }, router: { path: RouterPath.stoplimit + '/${pair}' }, }, // { // label: { // id: 'btrade', // i18nKey: 'labelBtradeTrade', // description: 'labelBtradeTradeDescription', // icon: BlockTradeIcon, // }, // router: { path: RouterPath.btrade + '/${pair}' }, // }, // { // label: { // id: 'fiat', // i18nKey: 'labelFiat', // description: 'labelFiatDescription', // icon: FiatIcon, // }, // router: { path: RouterPath.fiat }, // }, ] export enum VaultKey { VAULT_HOME = 'portalHome', VAULT_DASHBOARD = 'portalDashboard', VAULT_TRADE = 'portalTrade', } export let vaultItemData: Array = [ { label: { id: VaultKey.VAULT_DASHBOARD, i18nKey: 'labelVaultDashboard', description: 'labelVaultDashboardDes', icon: VaultDashboardIcon, }, router: { path: RouterPath.vault + `/${VaultKey.VAULT_DASHBOARD}` }, }, { label: { id: VaultKey.VAULT_HOME, i18nKey: 'labelVaultHome', description: 'labelVaultHomeDes', icon: VaultHomeIcon, }, router: { path: RouterPath.vault + `/${VaultKey.VAULT_HOME}` }, }, { label: { id: VaultKey.VAULT_TRADE, i18nKey: 'labelVaultTradeTabTitle', description: 'labelVaultTradeTabDes', icon: AutorenewIcon, }, router: { path: RouterPath.vault + `/${VaultKey.VAULT_TRADE}` }, }, ] export const orderDisableList = ['Liquidity', 'Markets', 'Trading', 'Mining'] export const ammDisableList = ['Liquidity'] export const headerMenuLandingData: Array = [ ...(!hideDefiEntry ? [{ label: { id: 'loopringLite', i18nKey: 'labelNavEarn', description: 'labelNavEarnDes', }, router: { path: Earnlite }, logo: { dark: SoursURL + 'images/landing_page_nav_earn_dark.png', light: SoursURL + 'images/landing_page_nav_earn_light.png', } }] : []), { label: { id: 'loopringPro', i18nKey: 'labelNavPro', description: 'labelNavProDes', }, router: { path: ExchangePro }, logo: { dark: SoursURL + 'images/landing_page_nav_pro_dark.png', light: SoursURL + 'images/landing_page_nav_pro_light.png', } }, ...(!hideDefiEntry ? [{ label: { id: 'wallet', i18nKey: 'labelNavWallet', description: 'labelNavWalletDes', }, router: { path: WalletSite }, logo: { dark: SoursURL + 'images/landing_page_nav_wallet_dark.png', light: SoursURL + 'images/landing_page_nav_wallet_light.png', } }] : []), { label: { id: 'doc', i18nKey: 'labelNavDoc', description: 'labelNavDocDes', }, router: { path: LOOPRING_DOC }, logo: { dark: SoursURL + 'images/landing_page_nav_doc_dark.png', light: SoursURL + 'images/landing_page_nav_doc_light.png', } }, ] export const subMenuLayer2 = { assetsGroup: [ { icon: AssetsIcon, router: { path: RouterPath.l2assetsDetail }, label: { id: 'assets', i18nKey: 'labelAssets', }, }, ], profileGroup: [ { icon: ProfileIcon, router: { path: `${RouterPath.layer2}/${Layer2RouterID.security}` }, label: { id: 'security', i18nKey: 'labelSecurity', }, }, { icon: VipIcon, router: { path: `${RouterPath.layer2}/${Layer2RouterID.vip}` }, label: { id: 'vip', i18nKey: 'labelVipPanel', }, }, ], } export const subMenuInvest = [ // { // icon: L2MyLiquidityIcon, // router: { path: `${RouterPath.invest}/${InvestRouter[InvestType.Overview]}` }, // label: { // id: 'overview', // i18nKey: 'labelInvestOverview', // description: 'labelInvestOverviewDes', // icon: OverviewIcon, // }, // }, // { // icon: L2MyLiquidityIcon, // router: { path: `${RouterPath.invest}/${InvestAssetRouter.DUAL}` }, // label: { // id: 'dual', // i18nKey: 'labelInvestDual', // description: 'labelInvestDualDes', // icon: DualInvestIcon, // }, // }, { icon: L2MyLiquidityIcon, router: { path: `${RouterPath.invest}/${InvestAssetRouter.STAKE}` }, label: { id: 'defi', i18nKey: 'labelInvestDefi', description: 'labelInvestDefiDes', icon: ETHStakingIcon, }, }, { icon: L2MyLiquidityIcon, router: { path: `${RouterPath.invest}/${InvestAssetRouter.LEVERAGEETH}` }, label: { id: 'leverageeth', i18nKey: 'labelInvestLeverageETH', description: 'labelInvestLeverageETHDes', icon: LeverageETHIcon, }, }, { icon: L2MyLiquidityIcon, router: { path: `${RouterPath.invest}/${InvestAssetRouter.AMM}` }, label: { id: 'ammpool', i18nKey: 'labelInvestAmm', description: 'labelInvestAmmDes', icon: AmmIcon, }, }, // { // icon: L2MyLiquidityIcon, // router: { path: `${RouterPath.invest}/${InvestAssetRouter.STAKELRC}` }, // label: { // id: 'stackonesided', // i18nKey: 'labelInvestStakeLRC', // description: 'labelInvestStakeLRCDes', // icon: LRCStakingIcon, // }, // }, ] // export enum INVEST_TAB { // pools = 'pools', // lido = 'lido', // staking = 'staking', // dual = 'dual', // leverageETH = 'leverageETH', // } export const INVEST_TABS = [ { tab: InvestAssetRouter.DUAL, label: 'labelInvestDualTitle' }, { tab: InvestAssetRouter.STAKE, label: 'labelInvestDefiTitle' }, { tab: InvestAssetRouter.LEVERAGEETH, label: 'labelLeverageETHTitle' }, { tab: InvestAssetRouter.AMM, label: 'labelLiquidityPageTitle' }, { tab: InvestAssetRouter.STAKELRC, label: 'labelInvestLRCTitle' }, ] export const DEFI_CONFIG = { products: { TAIKOHEKLA: [] as string[], TAIKO: [] as string[], ETHEREUM: ['LIDO', 'ROCKETPOOL'], GOERLI: ['ROCKETPOOL'], SEPOLIA: ['LIDO'], ARBGOERLI: ['ROCKETPOOL'], BASE: [], BASESEPOLIA: [], }, MARKETS: { TAIKOHEKLA: [] as string[], TAIKO: [] as string[], ETHEREUM: ['RETH-ETH', 'WSTETH-ETH'], GOERLI: ['RETH-ETH'], SEPOLIA: ['WSTETH-ETH'], ARBGOERLI: ['RETH-ETH'], BASE: [], BASESEPOLIA: [], }, } export const LEVERAGE_ETH_CONFIG = { coins: { TAIKOHEKLA: [] as string[], TAIKO: [] as string[], ETHEREUM: ['CIETH'], GOERLI: ['WSTETH'], SEPOLIA: ['WSTETH'], ARBGOERLI: ['WSTETH'], BASE: ['WSTETH'], BASESEPOLIA: ['WSTETH'], }, types: { TAIKOHEKLA: [] as string[], TAIKO: [] as string[], ETHEREUM: ['cian'], GOERLI: ['lido'], SEPOLIA: ['lido'], ARBGOERLI: ['lido'], BASE: [], BASESEPOLIA: [], }, products: { TAIKOHEKLA: [] as string[], TAIKO: [] as string[], ETHEREUM: ['CIAN'], GOERLI: ['LIDO'], SEPOLIA: ['LIDO'], ARBGOERLI: ['LIDO'], BASE: ['LIDO'], BASESEPOLIA: ['LIDO'], }, MARKETS: { TAIKOHEKLA: [] as string[], TAIKO: [] as string[], ETHEREUM: ['CIETH-ETH'], GOERLI: ['WSTETH-ETH'], SEPOLIA: ['WSTETH-ETH'], ARBGOERLI: ['WSTETH-ETH'], BASE: [], BASESEPOLIA: [], }, // ['LIDO,ROCKETPOOL', 'CIAN'] : ['ROCKETPOOL', 'LIDO'] } export const DUAL_CONFIG = { products: { TAIKOHEKLA: ['PIONEX'] as string[], TAIKO: ['PIONEX'] as string[], ETHEREUM: ['PIONEX'], GOERLI: ['PIONEX'], SEPOLIA: ['PIONEX'], ARBGOERLI: ['PIONEX'], BASE: ['PIONEX'], BASESEPOLIA: ['PIONEX'], }, } export const subMenuNFT = { NFTGroup: [ { icon: AssetsIcon, router: { path: `${RouterPath.nft}/${NFTSubRouter.assetsNFT}` }, label: { id: 'assetsNFT', i18nKey: 'labelMyAssetsNFT', description: 'labelMyAssetsNFTDes', icon: MyNFTIcon, }, }, // { // icon: MintIcon, // router: { path: `${RouterPath.nft}/${NFTSubRouter.mintNFTLanding}` }, // label: { // id: 'mintNFT', // i18nKey: 'labelMintNFT', // description: 'labelMintNFTDes', // icon: CreateNFTIcon, // }, // }, { icon: ImageIcon, router: { path: `${RouterPath.nft}/${NFTSubRouter.myCollection}` }, label: { id: 'collection', i18nKey: 'labelMyCollection', description: 'labelMyCollectionDes', icon: MyCollectionIcon, }, }, ], } export const FOOTER_LIST_MAP = { About: [ { linkName: 'Org', // loopring.org linkHref: 'https://loopring.org', }, { linkName: 'Terms', //Terms of service linkHref: 'https://loopring.io/#/document/terms_en.md', }, { linkName: 'Privacy', //Privacy policy linkHref: LOOPRING_DOCUMENT + 'privacy_en.md', }, { linkName: 'Risks', //Risks Disclosure linkHref: LOOPRING_DOCUMENT + 'risks_en.md', }, ], Platform: [ { linkName: 'Fees', //Fees linkHref: LOOPRING_DOCUMENT + 'dex_fees_en.md', }, { linkName: 'VIP', //VIP linkHref: 'https://medium.loopring.io/introducing-loopring-vip-tiers-c6f73d753bac', }, { linkName: 'Referrals', //Referrals linkHref: 'https://medium.loopring.io/loopring-exchange-launches-referral-program-c61777f072d1', }, ], } export const MEDIA_LIST = [ { linkName: 'Discord', //color={"inherit"} fontSize={"large"} linkHref: 'https://discord.com/invite/KkYccYp', }, { linkName: 'Twitter', linkHref: 'https://twitter.com/loopringorg', }, { linkName: 'Youtube', linkHref: 'https://www.youtube.com/c/Loopring', }, { linkName: 'Medium', linkHref: 'https://medium.com/loopring-protocol', }, ] export const headerMenuData: Array = [ { label: { id: 'home', i18nKey: 'labelHome', }, router: { path: `/pro` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'L2Assets', i18nKey: 'labelAssets', }, router: { path: `${RouterPath.l2assets}` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'Markets', i18nKey: 'labelMarkets', }, router: { path: `${RouterPath.markets}` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'Trade', i18nKey: 'labelTrade', }, status: HeaderMenuTabStatus.default, child: layer2ItemData, }, { label: { id: 'Invest', i18nKey: 'labelInvest', }, router: { path: `${RouterPath.invest}/${InvestRouter[InvestType.Overview]}` }, status: HeaderMenuTabStatus.default, child: subMenuInvest, }, // { // label: { // id: 'vault', // i18nKey: 'labelVault', // description: 'labelVaultDescription', // }, // router: { path: `${RouterPath.vault}` }, // status: HeaderMenuTabStatus.default, // child: vaultItemData, // }, { label: { id: 'NFT', i18nKey: 'labelNFT', }, router: { path: `${RouterPath.nft}` }, status: HeaderMenuTabStatus.default, child: subMenuNFT, }, ] export const ammAdvice: InvestAdvice = { type: InvestMapType.AMM, router: `${RouterPath.invest}/${InvestAssetRouter.AMM}`, banner: SoursURL + 'images/icon-amm.svg', titleI18n: 'labelInvestAmm', desI18n: 'labelInvestAmmDes', notification: '', enable: true, } export const defiAdvice: InvestAdvice = { type: InvestMapType.STAKE, router: `${RouterPath.invest}/${InvestAssetRouter.STAKE}`, notification: '', banner: SoursURL + 'images/icon-lido.svg', titleI18n: 'labelInvestDefi', desI18n: 'labelInvestDefiDes', enable: true, } export const defiWSTETHAdvice: InvestAdvice = { type: InvestMapType.STAKE, router: `${RouterPath.invest}/${InvestAssetRouter.STAKE}/WSTETH`, notification: '', banner: SoursURL + 'images/icon-lido2.svg', titleI18n: 'labelInvestWSTETH', desI18n: 'labelInvestWSTETHDes', enable: true, project: 'Lido', market: 'WSTETH-ETH', } export const defiRETHAdvice: InvestAdvice = { type: InvestMapType.STAKE, router: `${RouterPath.invest}/${InvestAssetRouter.STAKE}/RETH`, notification: '', banner: SoursURL + 'images/icon-pocket.svg', titleI18n: 'labelInvestRETH', desI18n: 'labelInvestRETHDes', enable: true, project: 'Rocket Pool', market: 'RETH-ETH', } export const dualAdvice: InvestAdvice = { type: InvestMapType.DUAL, router: `${RouterPath.invest}/${InvestAssetRouter.DUAL}`, notification: '', banner: SoursURL + 'images/icon-dual.svg', titleI18n: 'labelInvestDual', desI18n: 'labelInvestDualDes', enable: true, } export const stakeAdvice: InvestAdvice = { type: InvestMapType.STAKELRC, router: `${RouterPath.invest}/${InvestAssetRouter.STAKELRC}`, notification: '', banner: SoursURL + 'images/icon-stake-lrc.svg', titleI18n: 'labelInvestStakeLRC', desI18n: 'labelInvestStakeLRCDes', enable: true, } export const leverageETHAdvice: InvestAdvice = { type: InvestMapType.LEVERAGEETH, router: `${RouterPath.invest}/${InvestAssetRouter.LEVERAGEETH}`, notification: '', banner: SoursURL + 'images/icon-leverage-ETH.svg', titleI18n: 'labelInvestLeverageETH', desI18n: 'labelInvestLeverageETHDes', enable: true, project: 'Leveraged ETH Staking', market: 'CIETH-ETH', } export const DEFI_ADVICE_MAP = { WSTETH: defiWSTETHAdvice, RETH: defiRETHAdvice, CIETH: leverageETHAdvice, } export enum RecordTabIndex { Transactions = 'Transactions', Trades = 'Trades', AmmRecords = 'AmmRecords', Orders = 'Orders', DefiRecords = 'DefiRecords', DualRecords = 'DualRecords', SideStakingRecords = 'SideStakingRecords', BtradeSwapRecords = 'BtradeSwapRecords', StopLimitRecords = 'StopLimitRecords', leverageETHRecords = 'leverageETHRecords', VaultRecords = 'VaultRecords', TaikoLockRecords = 'TaikoLockRecords', } export enum AssetTabIndex { Tokens = 'Tokens', Invests = 'Invests', RedPacket = 'RedPacket', Rewards = 'Rewards', } export enum RedPacketRouterIndex { create = 'create', records = 'records', markets = 'markets', } export enum RedPacketRecordsTabIndex { Received = 'Received', Send = 'Send', NFTReceived = 'NFTReceived', NFTSend = 'NFTSend', BlindBoxReceived = 'BlindBoxReceived', BlindBoxSend = 'BlindBoxSend', NFTsUnClaimed = 'NFTsUnClaimed', BlindBoxUnClaimed = 'BlindBoxUnClaimed', } export enum TabOrderIndex { orderOpenTable = 'orderOpenTable', orderHistoryTable = 'orderHistoryTable', } export const headerMenuDataMap: { [key: string]: HeaderMenuItemInterface[] } = { TAIKOHEKLA:[ { label: { id: 'home', i18nKey: 'labelHome', }, router: { path: `/pro` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'L2Assets', i18nKey: 'labelAssets', }, router: { path: `${RouterPath.l2assets}` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'Markets', i18nKey: 'labelMarkets', }, router: { path: `${RouterPath.markets}` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'Trade', i18nKey: 'labelTrade', }, status: HeaderMenuTabStatus.default, child: [ { label: { id: 'lite', i18nKey: 'labelClassic', description: 'labelClassicDescription', icon: SwapIcon, }, router: { path: RouterPath.lite + '/${pair}' }, }, { label: { id: 'pro', i18nKey: 'labelAdvanced', description: 'labelAdvancedDescription', icon: OrderBookIcon, }, router: { path: RouterPath.pro + '/${pair}' }, }, { label: { id: 'stopLimit', i18nKey: 'labelStopLimit', description: 'labelStopLimitDescription', icon: StopLimitIcon, }, router: { path: RouterPath.stoplimit + '/${pair}' }, }, // { // label: { // id: 'btrade', // i18nKey: 'labelBtradeTrade', // description: 'labelBtradeTradeDescription', // icon: BlockTradeIcon, // }, // router: { path: RouterPath.btrade + '/${pair}' }, // }, ] }, { label: { id: 'vault', i18nKey: 'labelVault', description: 'labelVaultDescription', }, router: { path: `${RouterPath.vault}` }, status: HeaderMenuTabStatus.default, child: vaultItemData, }, { label: { id: 'Invest', i18nKey: 'labelInvest', }, router: { path: `${RouterPath.invest}/${InvestRouter[InvestType.Overview]}` }, status: HeaderMenuTabStatus.default, child: [ // { // icon: L2MyLiquidityIcon, // router: { path: `${RouterPath.invest}/${InvestRouter[InvestType.Overview]}` }, // label: { // id: 'overview', // i18nKey: 'labelInvestOverview', // description: 'labelInvestOverviewDes', // icon: OverviewIcon, // }, // }, { icon: L2MyLiquidityIcon, router: { path: `${RouterPath.invest}/${InvestAssetRouter.DUAL}` }, label: { id: 'dual', i18nKey: 'labelInvestDual', description: 'labelInvestDualDes', icon: DualInvestIcon, }, }, { icon: L2MyLiquidityIcon, router: { path: `${RouterPath.invest}/${InvestAssetRouter.AMM}` }, label: { id: 'ammpool', i18nKey: 'labelInvestAmm', description: 'labelInvestAmmDes', icon: AmmIcon, }, }, ] as HeaderMenuItemInterface[] }, ], TAIKO:[ { label: { id: 'home', i18nKey: 'labelHome', }, router: { path: `/pro` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'L2Assets', i18nKey: 'labelAssets', }, router: { path: `${RouterPath.l2assets}` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'Markets', i18nKey: 'labelMarkets', }, router: { path: `${RouterPath.markets}` }, status: HeaderMenuTabStatus.default, }, { label: { id: 'Trade', i18nKey: 'labelTrade', }, status: HeaderMenuTabStatus.default, child: [ { label: { id: 'lite', i18nKey: 'labelClassic', description: 'labelClassicDescription', icon: SwapIcon, }, router: { path: RouterPath.lite + '/${pair}' }, }, { label: { id: 'pro', i18nKey: 'labelAdvanced', description: 'labelAdvancedDescription', icon: OrderBookIcon, }, router: { path: RouterPath.pro + '/${pair}' }, }, { label: { id: 'stopLimit', i18nKey: 'labelStopLimit', description: 'labelStopLimitDescription', icon: StopLimitIcon, }, router: { path: RouterPath.stoplimit + '/${pair}' }, }, // { // label: { // id: 'btrade', // i18nKey: 'labelBtradeTrade', // description: 'labelBtradeTradeDescription', // icon: BlockTradeIcon, // }, // router: { path: RouterPath.btrade + '/${pair}' }, // }, ] }, { label: { id: 'vault', i18nKey: 'labelVault', description: 'labelVaultDescription', }, router: { path: `${RouterPath.vault}` }, status: HeaderMenuTabStatus.default, child: vaultItemData, }, { label: { id: 'Invest', i18nKey: 'labelInvest', }, router: { path: `${RouterPath.invest}/${InvestRouter[InvestType.Overview]}` }, status: HeaderMenuTabStatus.default, child: [ // { // icon: L2MyLiquidityIcon, // router: { path: `${RouterPath.invest}/${InvestRouter[InvestType.Overview]}` }, // label: { // id: 'overview', // i18nKey: 'labelInvestOverview', // description: 'labelInvestOverviewDes', // icon: OverviewIcon, // }, // }, { icon: L2MyLiquidityIcon, router: { path: `${RouterPath.invest}/${InvestAssetRouter.DUAL}` }, label: { id: 'dual', i18nKey: 'labelInvestDual', description: 'labelInvestDualDes', icon: DualInvestIcon, }, }, { icon: L2MyLiquidityIcon, router: { path: `${RouterPath.invest}/${InvestAssetRouter.AMM}` }, label: { id: 'ammpool', i18nKey: 'labelInvestAmm', description: 'labelInvestAmmDes', icon: AmmIcon, }, }, ] as HeaderMenuItemInterface[] }, ], ETHEREUM: headerMenuData, GOERLI: headerMenuData, SEPOLIA: headerMenuData, ARBGOERLI: headerMenuData, BASE: headerMenuData, BASESEPOLIA: headerMenuData, } export const TokenPriceBase = { TAIKOHEKLA: '0x931c7ada32c20b9d565cab616fe9976154e29809', TAIKO: '0x07d83526730c7438048d55a4fc0b850e2aab6f0b', ETHEREUM: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', GOERLI: '0xd4e71c4bb48850f5971ce40aa428b09f242d3e8a', SEPOLIA: '0xa7bc5a2731803be668090125b5074555f91cbc9d', ARBGOERLI: '0xd4e71c4bb48850f5971ce40aa428b09f242d3e8a', BASE: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', BASESEPOLIA: '0x5cc6b635bb68976e4ae3d0546ba0f20f66872a72', } export const RecordMap: { [key: string]: RecordTabIndex[] } = { TAIKOHEKLA: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.AmmRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], TAIKO: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.AmmRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], ETHEREUM: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.AmmRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], GOERLI: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.AmmRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], SEPOLIA: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.AmmRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], ARBGOERLI: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.AmmRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], BASE: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], BASESEPOLIA: [ RecordTabIndex.Transactions, RecordTabIndex.Trades, RecordTabIndex.Orders, RecordTabIndex.StopLimitRecords, RecordTabIndex.DefiRecords, RecordTabIndex.DualRecords, RecordTabIndex.SideStakingRecords, RecordTabIndex.BtradeSwapRecords, RecordTabIndex.leverageETHRecords, RecordTabIndex.VaultRecords, ], } export const AddAssetListMap = { TAIKOHEKLA: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, AddAssetList.FromExchange.key, ], TAIKO: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, AddAssetList.FromExchange.key, ], ETHEREUM: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, ], GOERLI: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, ], SEPOLIA: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, ], ARBGOERLI: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, ], BASE: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, ], BASESEPOLIA: [ AddAssetList.FromMyL1.key, AddAssetList.BuyWithCard.key, AddAssetList.FromOtherL2.key, AddAssetList.FromOtherL1.key, AddAssetList.FromExchange.key, AddAssetList.FromAnotherNet.key, ], } export const SendAssetListMap = { TAIKOHEKLA: [ SendAssetList.SendAssetToMyL1.key, SendAssetList.SendAssetToL2.key, SendAssetList.SendAssetToOtherL1.key, ], TAIKO: [ SendAssetList.SendAssetToMyL1.key, SendAssetList.SendAssetToL2.key, SendAssetList.SendAssetToOtherL1.key, ], ETHEREUM: [ SendAssetList.SendAssetToMyL1.key, SendAssetList.SendAssetToL2.key, SendAssetList.SendAssetToOtherL1.key, SendAssetList.SendAssetToTaikoAccount.key, SendAssetList.SendAssetToAnotherNet.key, ], SEPOLIA: [ SendAssetList.SendAssetToMyL1.key, SendAssetList.SendAssetToL2.key, SendAssetList.SendAssetToOtherL1.key, SendAssetList.SendAssetToTaikoAccount.key, SendAssetList.SendAssetToAnotherNet.key, ], BASE: [ SendAssetList.SendAssetToMyL1.key, SendAssetList.SendAssetToL2.key, SendAssetList.SendAssetToOtherL1.key, SendAssetList.SendAssetToTaikoAccount.key, SendAssetList.SendAssetToAnotherNet.key, ], BASESEPOLIA: [ SendAssetList.SendAssetToMyL1.key, SendAssetList.SendAssetToL2.key, SendAssetList.SendAssetToOtherL1.key, SendAssetList.SendAssetToTaikoAccount.key, SendAssetList.SendAssetToAnotherNet.key, ], } export const AssetL2TabIndex = { TAIKOHEKLA: [AssetTabIndex.Tokens], TAIKO: [AssetTabIndex.Tokens], ETHEREUM: [ AssetTabIndex.Tokens, AssetTabIndex.Invests, // AssetTabIndex.RedPacket, AssetTabIndex.Rewards, ], GOERLI: [ AssetTabIndex.Tokens, AssetTabIndex.Invests, // AssetTabIndex.RedPacket, AssetTabIndex.Rewards, ], SEPOLIA: [ AssetTabIndex.Tokens, AssetTabIndex.Invests, // AssetTabIndex.RedPacket, AssetTabIndex.Rewards, ], ARBGOERLI: [ AssetTabIndex.Tokens, AssetTabIndex.Invests, // AssetTabIndex.RedPacket, AssetTabIndex.Rewards, ], BASE: [ AssetTabIndex.Tokens, AssetTabIndex.Invests, // AssetTabIndex.RedPacket, AssetTabIndex.Rewards, ], BASESEPOLIA: [ AssetTabIndex.Tokens, AssetTabIndex.Invests, // AssetTabIndex.RedPacket, AssetTabIndex.Rewards, ], } export const RouterAllowIndex = { TAIKOHEKLA: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, RouterMainKey.vault, ], TAIKO: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, RouterMainKey.vault, ], ETHEREUM: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, RouterMainKey.vault, ], GOERLI: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, RouterMainKey.vault, ], SEPOLIA: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, RouterMainKey.vault, ], ARBGOERLI: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, ], BASE: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, RouterMainKey.vault, ], BASESEPOLIA: [ RouterMainKey.lite, RouterMainKey.pro, RouterMainKey.stoplimit, RouterMainKey.btrade, RouterMainKey.fiat, RouterMainKey.markets, RouterMainKey.mining, RouterMainKey.redPacket, RouterMainKey.l2assets, RouterMainKey.layer2, RouterMainKey.nft, RouterMainKey.invest, RouterMainKey.vault, ], } export const ProfileIndex = { TAIKOHEKLA: [ Layer2RouterID.security, ], TAIKO: [ Layer2RouterID.security, ], ETHEREUM: [ Layer2RouterID.security, Layer2RouterID.forcewithdraw, Layer2RouterID.vip, Layer2RouterID.contact, Layer2RouterID.referralrewards, Layer2RouterID.notification, ], GOERLI: [ Layer2RouterID.security, Layer2RouterID.forcewithdraw, Layer2RouterID.vip, Layer2RouterID.contact, Layer2RouterID.referralrewards, Layer2RouterID.notification, ], SEPOLIA: [ Layer2RouterID.security, Layer2RouterID.forcewithdraw, Layer2RouterID.vip, Layer2RouterID.contact, Layer2RouterID.referralrewards, Layer2RouterID.notification, ], ARBGOERLI: [ Layer2RouterID.security, Layer2RouterID.forcewithdraw, Layer2RouterID.vip, Layer2RouterID.contact, Layer2RouterID.referralrewards, ], BASE: [ Layer2RouterID.security, ], BASESEPOLIA: [ Layer2RouterID.security, ], } export const L1L2_NAME_DEFINED = { TAIKOHEKLA: { layer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Layer 3', l1ChainName: 'Taiko Hekla', loopringL2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring L3', l2Symbol: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'L3', l1Symbol: 'Taiko Hekla', ethereumL1: 'Taiko Hekla', loopringLayer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring Layer 3', L1Token: 'ETH', L2Token: 'TAIKO', }, TAIKO: { layer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Layer 3', l1ChainName: 'Taiko', loopringL2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring L3', l2Symbol: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'L3', l1Symbol: 'Taiko', ethereumL1: 'Taiko', loopringLayer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring Layer 3', L1Token: 'ETH', L2Token: 'TAIKO', }, ETHEREUM: { layer2: 'Layer 2', l1ChainName: 'Ethereum', loopringL2: 'Loopring L2', l2Symbol: 'L2', l1Symbol: 'L1', ethereumL1: 'Ethereum L1', loopringLayer2: 'Loopring Layer 2', L1Token: 'ETH', L2Token: 'LRC', }, GOERLI: { layer2: 'Layer 2', l1ChainName: 'Ethereum', loopringL2: 'Loopring L2', l2Symbol: 'L2', l1Symbol: 'L1', ethereumL1: 'Ethereum L1', loopringLayer2: 'Loopring Layer 2', L1Token: 'ETH', L2Token: 'LRC', }, SEPOLIA: { layer2: 'Layer 2', l1ChainName: 'Ethereum', loopringL2: 'Loopring L2', l2Symbol: 'L2', l1Symbol: 'L1', ethereumL1: 'Ethereum L1', loopringLayer2: 'Loopring Layer 2', L1Token: 'ETH', L2Token: 'LRC', }, ARBGOERLI: { layer2: 'Layer 2', l1ChainName: 'Ethereum', loopringL2: 'Loopring L2', l2Symbol: 'L2', l1Symbol: 'L1', ethereumL1: 'Ethereum L1', loopringLayer2: 'Loopring Layer 2', L1Token: 'ETH', L2Token: 'LRC', }, BASE: { layer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Layer 3', l1ChainName: 'BASE', loopringL2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring L3', l2Symbol: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'L3', l1Symbol: 'L1', ethereumL1: 'BASE', loopringLayer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring Layer 3', L1Token: 'ETH', L2Token: 'ETH', }, BASESEPOLIA: { layer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Layer 3', l1ChainName: 'BASE', loopringL2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring L3', l2Symbol: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'L3', l1Symbol: 'L1', ethereumL1: 'BASE', loopringLayer2: process.env.REACT_APP_NAME === 'loopring defi' ? 'Loopring DeFi' : 'Loopring Layer 3', L1Token: 'ETH', L2Token: 'ETH', }, } export const SPECIAL_ACTIVATION_NETWORKS = [ ChainId.TAIKO, ChainId.TAIKOHEKLA, ChainId.BASE, ChainId.SEPOLIA, ChainId.BASESEPOLIA, ] ================================================ FILE: packages/common-resources/static-resources/src/constant/sagaStatus.ts ================================================ import { ErrorObject } from '../error' export enum SagaStatus { UNSET = 'UNSET', PENDING = 'PENDING', ERROR = 'ERROR', // success failed timeout is Done DONE = 'DONE', // success failed timeout is Done } export type StateBase = { status: SagaStatus errorMessage?: ErrorObject | null } ================================================ FILE: packages/common-resources/static-resources/src/constant/setting.ts ================================================ import { IsMobile } from '../utils' import { NetworkItemInfo } from '../loopring-interface' import * as sdk from '@loopring-web/loopring-sdk' import { mainnet, sepolia, base, baseSepolia, taiko, taikoHekla, Chain } from 'viem/chains' export enum UpColor { green = 'green', red = 'red', } export const SlippageTolerance: Array<0.1 | 0.5 | 1 | string> = [0.1, 0.5, 1] export const SlippageBtradeTolerance: Array<0.1 | 0.5 | 1 | string> = [0.1, 0.5, 1] export type RowConfigType = { rowHeight?: number rowHeaderHeight?: number minHeight?: number } export const RowConfig = { rowHeight: IsMobile.any() ? 48 : 44, rowHeaderHeight: IsMobile.any() ? 48 : 44, minHeight: 350, } export const RowInvestConfig = { rowHeight: IsMobile.any() ? 48 : 56, rowHeaderHeight: IsMobile.any() ? 48 : 56, minHeight: 350, } export const RowDualInvestConfig = { rowHeight: IsMobile.any() ? 48 : 72, rowHeaderHeight: IsMobile.any() ? 48 : 72, minHeight: 350, } export const DirectionTag = '\u2192' export const FeeChargeOrderDefaultMap = new Map([ [sdk.ChainId.MAINNET, ['ETH', 'USDT', 'LRC', 'DAI', 'USDC']], [sdk.ChainId.TAIKO, ['ETH', 'USDT', 'LRC', 'USDC', 'TAIKO']], [sdk.ChainId.SEPOLIA, ['ETH', 'USDT', 'LRC', 'DAI']], [sdk.ChainId.TAIKOHEKLA, ['ETH', 'USDT', 'LRC', 'TAIKO']], [sdk.ChainId.BASE, ['ETH', 'USDT', 'LRC', 'USDC']], [sdk.ChainId.BASESEPOLIA, ['ETH', 'USDT', 'LRC', 'USDC']], ]) export const HEADER_HEIGHT = 64 export const LandPageHeightConfig = { headerHeight: 64, whiteHeight: 32, maxHeight: 836, minHeight: 800, } export const Lang = { en_US: 'en', zh_CN: 'zh', } export const Explorer = 'https://explorer.loopring.io/' export const Bridge = 'https://bridge.loopring.io/#/' export const ExchangeIO = 'https://loopring.io' export const Exchange = 'https://loopring.io/#/' export const ExchangePro = window.location.origin + '/#/pro' export const Earnlite = 'https://defi.loopring.io/#/' export const WalletSite = 'https://wallet.loopring.io' export const LOOPRING_DOC = 'https://docs.loopring.io' export const YEAR_FROMATE = 'YYYY' export const DAY_FORMAT = 'MM/DD' export const MINUTE_FORMAT = 'HH:mm' export const DAY_MINUTE_FORMAT = `${DAY_FORMAT} ${MINUTE_FORMAT}` export const DAT_STRING_FORMAT = 'MMM DD [UTC]Z' export const DAT_STRING_FORMAT_S = 'MMM DD' export const SECOND_FORMAT = `${MINUTE_FORMAT}:ss` export const YEAR_DAY_FORMAT = `${YEAR_FROMATE}/${DAY_FORMAT}` export const YEAR_DAY_MINUTE_FORMAT = `${YEAR_DAY_FORMAT} ${MINUTE_FORMAT}` export const YEAR_DAY_SECOND_FORMAT = `${YEAR_DAY_FORMAT} ${SECOND_FORMAT}` export const MINT_STRING_FORMAT = `${MINUTE_FORMAT} ${DAT_STRING_FORMAT}` export const UNIX_TIMESTAMP_FORMAT = 'x' export const sizeNFTConfig = (size: 'large' | 'medium' | 'small') => { switch (size) { case 'large': return { wrap_xs: 12, wrap_md: 4, wrap_lg: 4, avatar: 40, contentHeight: 80, } break case 'small': return { wrap_xs: 6, wrap_md: 3, wrap_lg: 2, avatar: 28, contentHeight: 60, } break case 'medium': return { wrap_xs: 6, wrap_md: 3, wrap_lg: 3, avatar: 38, contentHeight: 72, } break } } export enum TradeBtnStatus { AVAILABLE = 'AVAILABLE', DISABLED = 'DISABLED', LOADING = 'LOADING', } export const { NetworkMap, ChainTests, MapChainId, ChainIdExtends } = ( process.env.REACT_APP_RPC_OTHERS?.split(',') ?? [] ).reduce( ({ NetworkMap, ChainTests, MapChainId, ChainIdExtends }, item: string, index: number) => { let [_name, isTest] = process.env[`REACT_APP_RPC_CHAINNAME_${item}`]?.split('|') ?? [ `unknown${item}`, ] const name = _name.toUpperCase() ChainIdExtends[name] = Number(item) MapChainId[item] = name // MapChainIdMap.set(Number(item), name); // myLog("MapChainIdMap", item, MapChainIdMap); if (isTest) { ChainTests.push(Number(item)) } NetworkMap[Number(item)] = { label: _name, chainId: index.toString(), // RPC: process.env[`REACT_APP_RPC_URL_${item}`] ?? "", isTest: isTest ? true : false, walletType: name, } return { NetworkMap, ChainTests, MapChainId, ChainIdExtends } }, { MapChainId: { 1: 'ETHEREUM', 5: 'GOERLI', 421613: 'ARBGOERLI', 11155111: 'SEPOLIA', 167009: 'TAIKOHEKLA', 167000: 'TAIKO', 8453: 'BASE', 84532: 'BASESEPOLIA', }, NetworkMap: { 1: { label: 'Ethereum', chainId: '1', isTest: false, walletType: 'ETHEREUM', }, 5: { label: 'Görli', chainId: '5', isTest: true, walletType: 'GOERLI', }, 11155111: { label: 'Sepolia', chainId: '11155111', isTest: true, walletType: 'SEPOLIA', }, 167009: { label: 'Taiko Hekla', chainId: '167009', isTest: true, walletType: 'Taiko Hekla', }, 167000: { label: 'Taiko', chainId: '167000', isTest: false, walletType: 'TAIKO', }, 8453: { label: 'Base', chainId: '8453', isTest: false, walletType: 'BASE', }, 84532: { label: 'Base Sepolia', chainId: '84532', isTest: true, walletType: 'BASESEPOLIA', }, }, ChainTests: [11155111, 5, 167009, 84532], ChainIdExtends: { NONETWORK: 'unknown', }, } as { ChainTests: number[] MapChainId: { [key: string]: string } NetworkMap: { [key: number]: NetworkItemInfo } ChainIdExtends: { [key: string]: number | string } }, ) if (window) { // @ts-ignore window.__ChainIdExtends = ChainIdExtends // @ts-ignore window.__MapChainId = MapChainId } export const HEBAO_CONTRACT_MAP = [ ['V3_0_0', sdk.AddressType.LOOPRING_HEBAO_CONTRACT_3_0_0], ['V2_2_0', sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_2_0], ['V2_1_0', sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_1_0], ['V2_0_0', sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_0_0], ['V1_2_0', sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_2_0], ['V1_1_6', sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_1_6], ] export type ContactType = Pick['contacts'][0] export type CoinSource = { x: number y: number w: number h: number offX: number offY: number sourceW: number sourceH: number simpleName?: string } export enum TableFilterParams { all = 'all', favourite = 'favourite', ranking = 'ranking', } export type Contact = { contactName: string // name?: string address?: string addressType?: (typeof sdk.AddressType)[sdk.AddressTypeKeys] ens?: string } & Partial export enum ToastType { success = 'success', error = 'error', warning = 'warning', info = 'info', } export const SEND_TO_TAIKO_NETWORK_MAP = new Map([ [sdk.ChainId.SEPOLIA, sdk.ChainId.TAIKOHEKLA], [sdk.ChainId.MAINNET, sdk.ChainId.TAIKO], ]) export const CHAIN_ID_TO_VIEW_CHAIN = new Map([ [sdk.ChainId.SEPOLIA, sepolia], [sdk.ChainId.MAINNET, mainnet], [sdk.ChainId.TAIKOHEKLA, taikoHekla], [sdk.ChainId.TAIKO, taiko], [sdk.ChainId.BASE, base], [sdk.ChainId.BASESEPOLIA, baseSepolia], ]) export const coinbaseSmartWalletChains = [sdk.ChainId.BASE, sdk.ChainId.BASESEPOLIA] export const hideDefiEntry = process.env.REACT_APP_HIDE_DEFI_ENTRY === 'true' ================================================ FILE: packages/common-resources/static-resources/src/constant/social.ts ================================================ import * as Social from 'react-share' import { DiscordSvg } from '../svg'; import { createElement } from 'react'; import { ExchangeIO } from './setting'; import { IPFSHTTPClient } from 'ipfs-http-client'; import { IPFS_LOOPRING_SITE } from './router'; import { myLog } from '../utils'; export enum SOCIAL_NAME_KEYS { Facebook = 'Facebook', WhatsApp = 'WhatsApp', Twitter = 'Twitter', Telegram = 'Telegram', Email = 'Email', Pinterest = 'Pinterest', Discord = 'Discord' } // Object.values(SOCIAL_NAME_KEYS) export const SOCIAL_COMPONENT_MAP = { [ SOCIAL_NAME_KEYS.Facebook ]: { SocialNetworkName: SOCIAL_NAME_KEYS.Facebook, SocialComponent: Social.FacebookShareButton, SocialIcon: Social.FacebookIcon, }, [ SOCIAL_NAME_KEYS.WhatsApp ]: { SocialNetworkName: SOCIAL_NAME_KEYS.WhatsApp, SocialComponent: Social.WhatsappShareButton, SocialIcon: Social.WhatsappIcon, }, [ SOCIAL_NAME_KEYS.Twitter ]: { SocialNetworkName: SOCIAL_NAME_KEYS.Twitter, SocialComponent: Social.TwitterShareButton, SocialIcon: Social.TwitterIcon, }, [ SOCIAL_NAME_KEYS.Telegram ]: { SocialNetworkName: SOCIAL_NAME_KEYS.Telegram, SocialComponent: Social.TelegramShareButton, SocialIcon: Social.TelegramIcon, }, [ SOCIAL_NAME_KEYS.Email ]: { SocialNetworkName: SOCIAL_NAME_KEYS.Email, SocialComponent: Social.EmailShareButton, SocialIcon: Social.EmailIcon, }, [ SOCIAL_NAME_KEYS.Pinterest ]: { SocialNetworkName: SOCIAL_NAME_KEYS.Pinterest, SocialComponent: Social.PinterestShareButton, SocialIcon: Social.PinterestIcon, }, [ SOCIAL_NAME_KEYS.Discord ]: { SocialNetworkName: SOCIAL_NAME_KEYS.Discord, SocialComponent: createElement(''), SocialIcon: DiscordSvg, } } export const SOCIAL_LIST = [ SOCIAL_COMPONENT_MAP[ SOCIAL_NAME_KEYS.Facebook ], SOCIAL_COMPONENT_MAP[ SOCIAL_NAME_KEYS.Twitter ], SOCIAL_COMPONENT_MAP[ SOCIAL_NAME_KEYS.Email ], ] // export const SOCIAL_WITH_TITLE = new Set([ // SOCIAL_NAME_KEYS.Facebook, // // SOCIAL_NAME_KEYS.Discord, // SOCIAL_NAME_KEYS.Twitter, // SOCIAL_NAME_KEYS.Email, // // SOCIAL_NAME_KEYS. // // SOCIAL_NAME_KEYS.telegram, // // SOCIAL_NAME_KEYS.pinterest, // ]) export const shareOnTwitter = async (message: string, image: string, ipfs?: IPFSHTTPClient, url: string = ExchangeIO) => { let ipfsUrl = '' if (ipfs) { const {cid} = await ipfs.add(Buffer.from(image.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''), 'base64')) ipfsUrl = `${IPFS_LOOPRING_SITE}${cid}`; } myLog('ipfsUrl', ipfsUrl) const tweetText = encodeURIComponent(`${message}\n\nImage: ${ipfsUrl}\n\nWebsite: ${url}`); const twitterUrl = `https://twitter.com/intent/tweet?text=${tweetText}`; window.open(twitterUrl, '_blank'); }; export const shareOnFacebook = async (message: string, image: string, ipfs?: IPFSHTTPClient, url: string = ExchangeIO) => { let ipfsUrl = '' if (ipfs) { const {cid} = await ipfs.add(Buffer.from(image.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''), 'base64')) ipfsUrl = `${IPFS_LOOPRING_SITE}${cid}`; } myLog('ipfsUrl', ipfsUrl) const facebookUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent( url )}"e=${encodeURIComponent(message)}&picture=${encodeURIComponent( `${ipfsUrl}` )}`; window.open(facebookUrl, '_blank'); } export const shareViaEmail = async (message: string, image: string, ipfs?: IPFSHTTPClient, _width = '315', _height = '440') => { const emailSubject = encodeURIComponent("Join me at Loopring and earn exclusive rewards"); // let ipfsUrl = '' // if (ipfs) { // const {cid} = await ipfs.add(Buffer.from(image.replace(/^data:image\/(png|jpeg|jpg);base64,/, ''), 'base64')) // ipfsUrl = `${IPFS_LOOPRING_SITE}${cid}`; // } // myLog('ipfsUrl', ipfsUrl) //
const emailBody = encodeURIComponent( `${message}` ); const mailtoUrl = `mailto:?subject=${emailSubject}&body=${emailBody}`; window.open(mailtoUrl, '_blank'); }; export const shareDownload = (name: string, url: Blob | string) => { const link = document.createElement('a'); link.href = typeof url === "string" ? url : URL.createObjectURL(url); link.download = name; link.click(); // const facebookUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent( // window.location.href // )}"e=${encodeURIComponent(message)}&picture=${encodeURIComponent( // `${image}` // )}`; // window.open(facebookUrl, '_blank'); } export const shareOnDiscord = (message: string, image: string) => { const discordShareUrl = `https://discord.com/api/oauth2/authorize?client_id=YOUR_BOT_CLIENT_ID&scope=bot&permissions=0&response_type=code&redirect_uri=${encodeURIComponent( window.location.href )}&state=${encodeURIComponent( JSON.stringify({message, image: `${image}`}) )}`; window.open(discordShareUrl, '_blank'); }; ================================================ FILE: packages/common-resources/static-resources/src/constant/table.ts ================================================ export enum TableType { filter = 'filter', page = 'page', } ================================================ FILE: packages/common-resources/static-resources/src/constant/tokenConfig.ts ================================================ const SPECIAL_TOKEN_NAME_MAP = new Map( [ ['LRTAIKO', 'lrTAIKO'], ] ); export const WITHDRAW_TOKEN_FILTER_LIST = ['LRTAIKO'] export const mapSpecialTokenName = (original: string) => { return SPECIAL_TOKEN_NAME_MAP.get(original) || original } ================================================ FILE: packages/common-resources/static-resources/src/constant/trade.ts ================================================ import { CollectionMeta, DeFiCalcData, DeFiSideCalcData, DeFiSideRedeemCalcData, FeeInfo, IBData, LuckyRedPacketItem, } from '../loopring-interface' import * as sdk from '@loopring-web/loopring-sdk' import { MarketType } from './market' import { VendorProviders } from './vendor' export enum DeFiChgType { coinSell = 'coinSell', coinBuy = 'coinBuy', exchange = 'exchange', } export type WithdrawType = | sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL | sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL | sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL export type WithdrawTypes = { // @ts-ignore [sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL]: 'Fast' // @ts-ignore [sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL]: 'Standard' } // export type PriceTagType = '$' | '¥' export enum CurrencyToTag { USD = 'USD', CNY = 'CNY', JPY = 'JPY', AUD = 'AUD', CAD = 'CAD', HKD = 'HKD', EUR = 'EUR', KRW = 'KRW', GBP = 'GBP', } export enum PriceTag { USD = '$', CNY = '¥', JPY = '¥', AUD = 'A$', CAD = 'C$', HKD = 'HKD', EUR = '€', KRW = '₩', GBP = '£', } export enum TradeTypes { Buy = 'Buy', Sell = 'Sell', } export enum TradeStatus { // Filled = 'Filled', // Cancelled = 'Cancelled', // Succeeded = 'Succeeded', Processing = 'processing', Processed = 'processed', Cancelling = 'cancelling', Cancelled = 'cancelled', Expired = 'expired', Waiting = 'waiting', } export type TxInfo = { hash: string timestamp?: number | undefined status?: 'pending' | 'success' | 'failed' | undefined [key: string]: any } export interface AccountHashInfo { depositHashes: { [key: string]: TxInfo[] } vaultBorrowHashes: { [key: string]: TxInfo[] } showHadUnknownCollection: { [key: string]: boolean } } export interface NFTHashInfo { nftDataHashes: { [key: string]: Required } } export enum Layer1Action { GuardianLock = 'GuardianLock', NFTDeploy = 'NFTDeploy', } // GuardianLock export type Layer1ActionHistory = { [key: string]: { [key in keyof typeof Layer1Action]?: { [key: string]: number } // NFTDeploy?: { [key: string]: number }; } } export type ChainHashInfos = { [key in sdk.ChainId extends string ? string : string]: AccountHashInfo } export type NFTHashInfos = { [key in sdk.ChainId extends string ? string : string]: NFTHashInfo } export type LAYER1_ACTION_HISTORY = { [key in sdk.ChainId extends string ? string : string]: Layer1ActionHistory } & { __timer__: -1 | NodeJS.Timeout } export type MetaProperty = { key: string value: string } export type MetaDataProperty = { [key: string]: string } export type AttributesProperty = { trait_type: string value: string } export type NFTMETA = { image: string name: string royaltyPercentage: number // 0 - 10 for UI description: string collection_metadata: string properties?: Array animationUrl?: string attributes?: AttributesProperty[] } export enum Media { Audio = 'Audio', Image = 'Image', Video = 'Video', Media3D = 'Media3D', } export type NFTWholeINFO = sdk.NFTTokenInfo & sdk.UserNFTBalanceInfo & NFTMETA & { nftBalance?: number nftIdView?: string pendingOnSync: boolean fee?: FeeInfo isFailedLoadMeta?: boolean etherscanBaseUrl: string __mediaType__?: Media // collectionMeta?: Partial; preference?: { favourite: boolean hide: boolean } collectionInfo?: Co } export type MintTradeNFT = { balance?: number fee?: FeeInfo cid?: string nftId?: string nftBalance?: number nftIdView?: string royaltyPercentage?: number // tokenAddress?:string; } & Partial> & Partial> export type MintReadTradeNFT = { balance?: number fee?: FeeInfo readonly cid?: string readonly nftId?: string readonly nftIdView?: string readonly nftBalance?: number readonly royaltyPercentage?: number } & Partial> & Partial> export type TradeNFT = MintTradeNFT & Partial> & { isApproved?: boolean collectionMeta?: CollectionMeta } export enum NFT_TYPE_STRING { ERC721 = 'ERC721', ERC1155 = 'ERC1155', } export const EmptyValueTag = '--' export const HiddenTag = '*****' export const DEAULT_NFTID_STRING = '0x0000000000000000000000000000000000000000000000000000000000000000' export const MINT_LIMIT = 100000 export const SUBMIT_PANEL_CHECK = 1000 export const SUBMIT_PANEL_AUTO_CLOSE = 8000 export const SUBMIT_PANEL_QUICK_AUTO_CLOSE = 3000 export const SUBMIT_PANEL_DOUBLE_QUICK_AUTO_CLOSE = 1000 export const TOAST_TIME = 3000 export const PROPERTY_LIMIT = 64 export const PROPERTY_KET_LIMIT = 20 export const STAKING_INVEST_LIMIT = 5 export const PROPERTY_Value_LIMIT = 40 export const REDPACKET_ORDER_LIMIT = 10000 export const REDPACKET_ORDER_NFT_LIMIT = 20000 export const EXCLUSIVE_REDPACKET_ORDER_LIMIT_WHITELIST = 10000 export const EXCLUSIVE_REDPACKET_ORDER_LIMIT = 50 export const BLINDBOX_REDPACKET_LIMIT = 10000 export const VAULT_MARKET_REFRESH = 60000 export const LOOPRING_TAKE_NFT_META_KET = { name: 'name', image: 'image', royaltyPercentage: 'royaltyPercentage', description: 'description', properties: 'properties', } export type LOOPRING_NFT_METADATA = { [key in keyof typeof LOOPRING_TAKE_NFT_META_KET]?: string | undefined } export const NFTLimit = 12, CollectionLimit = 12, RedPacketLimit = 12 export const AddAssetList = { FromMyL1: { key: 'FromMyL1', svgIcon: 'IncomingIcon', enableKey: 'deposit', }, BuyWithCard: { key: 'BuyWithCard', svgIcon: 'CardIcon', enableKey: 'legal', }, FromOtherL2: { key: 'FromOtherL2', svgIcon: 'L2l2Icon', enableKey: null, }, FromOtherL1: { key: 'FromOtherL1', svgIcon: 'OutputIcon', enableKey: null, }, FromExchange: { key: 'FromExchange', svgIcon: 'ExchangeAIcon', enableKey: null, }, FromAnotherNet: { key: 'FromAnotherNet', svgIcon: 'AnotherIcon', enableKey: null, }, } type SendAssetListType = Record< string, { key: string svgIcon: string enableKey?: string | null type: 'sameLayer' | 'crossLayer' | 'crossChain' cornerTag?: 'Loopring' | '3rd party' description?: string } > export const SendAssetList: SendAssetListType = { SendAssetToMyL1: { key: 'SendToMyL1', svgIcon: 'IncomingIcon', enableKey: 'withdraw', type: 'crossLayer', }, SendAssetToL2: { key: 'SendTOL2', svgIcon: 'L2l2Icon', enableKey: 'transfer', type: 'sameLayer', }, SendAssetToOtherL1: { key: 'SendToOtherL1', svgIcon: 'L1l2Icon', enableKey: 'withdraw', type: 'crossLayer', }, SendAssetToAnotherNet: { key: 'SendAssetToAnotherNet', svgIcon: 'AnotherIcon', enableKey: 'withdraw', type: 'crossChain', cornerTag: '3rd party' }, SendAssetToTaikoAccount: { key: 'SendAssetToTaikoAccount', svgIcon: 'AnotherIcon', enableKey: 'transferToTaikoAccount', type: 'crossChain', cornerTag: 'Loopring', }, } export const SendNFTAssetList: SendAssetListType = { SendAssetToMyL1: { key: 'SendToMyL1', svgIcon: 'IncomingIcon', enableKey: 'withdrawNFT', type: 'crossLayer', }, SendAssetToL2: { key: 'SendTOL2', svgIcon: 'L2l2Icon', enableKey: 'transferNFT', type: 'sameLayer', }, SendAssetToOtherL1: { key: 'SendToOtherL1', svgIcon: 'L1l2Icon', enableKey: 'withdrawNFT', type: 'crossLayer', }, } export enum AddressError { NoError = 'NoError', EmptyAddr = 'EmptyAddr', InvalidAddr = 'InvalidAddr', ENSResolveFailed = 'ENSResolveFailed', IsNotLoopringContract = 'IsNotLoopringContract', TimeOut = 'TimeOut', } export enum WALLET_TYPE { EOA = 'EOA', Loopring = 'Loopring', OtherSmart = 'OtherSmart', Exchange = 'Exchange', } export enum EXCHANGE_TYPE { NonExchange = 'NonExchange', Binance = 'Binance', Huobi = 'Huobi', Coinbase = 'Coinbase', Others = 'Others', } export type AddressItemType = { value: T label: string description: string disabled?: boolean maxWidth?: string | number } export const defaultSlipage = 0.1 export const defaultBlockTradeSlipage = 0.1 export type ForexMap = { [k in keyof C]?: number } export const enum InvestMapType { Token = 'Token', AMM = 'AMM', STAKE = 'STAKE', DUAL = 'DUAL', STAKELRC = 'STAKELRC', LEVERAGEETH = 'LEVERAGEETH', } export const enum InvestAssetRouter { AMM = 'ammpool', STAKE = 'defi', DUAL = 'dual', STAKELRC = 'stakelrc', LEVERAGEETH = 'leverageETH', // BTradeInvest = "BTradeInvest", } export const InvestOpenType = [ InvestMapType.AMM, InvestMapType.STAKE, InvestMapType.DUAL, InvestMapType.STAKELRC, InvestMapType.LEVERAGEETH, ] export const enum InvestDuration { Flexible = 'Flexible', Duration = 'Duration', All = 'All', } export type InvestItem = { type: InvestMapType i18nKey: `labelInvestType_${InvestMapType}` | '' apr: [start: number, end: number] durationType: InvestDuration duration: string } export type InvestDetail = { token: sdk.TokenInfo apr: [start: number, end: number] durationType: InvestDuration duration: string } export enum CreateCollectionStep { // CreateTokenAddress, // Loading, // CreateTokenAddressFailed, ChooseMethod, ChooseMintMethod, ChooseCollectionEdit, // AdvancePanel, // CommonPanel, } export type TradeDefi = { type: string market?: MarketType // eg: ETH-LRC, Pair from loopring market isStoB: boolean sellVol: string buyVol: string sellToken: sdk.TokenInfo buyToken: sdk.TokenInfo deFiCalcData?: DeFiCalcData fee: string feeRaw: string depositPrice?: string withdrawPrice?: string maxSellVol?: string maxBuyVol?: string maxFeeBips?: number miniSellVol?: string request?: sdk.DefiOrderRequest defiBalances?: { [key: string]: string } lastInput?: DeFiChgType withdrawFeeBips?: number defaultFee: string } export type TradeStake = { sellToken: sdk.TokenInfo sellVol: string deFiSideCalcData?: DeFiSideCalcData request?: { accountId: number hash: string token: sdk.TokenVolumeV3 } } export type RedeemStake = { sellToken: sdk.TokenInfo sellVol?: string deFiSideRedeemCalcData: DeFiSideRedeemCalcData request?: { accountId: number hash: string token: sdk.TokenVolumeV3 } } export type L2CollectionFilter = { isMintable?: boolean isLegacy?: boolean tokenAddress?: string owner?: string } export type MyNFTFilter = { favourite?: boolean hidden?: boolean } export enum MY_NFT_VIEW { LIST_COLLECTION = 'byCollection', LIST_NFT = 'byList', } export const LIVE_FEE_TIMES = 60000 export const L1_UPDATE = 15000 export type DualCurrentPrice = { quote: string base: string precisionForPrice: number currentPrice?: number quoteUnit: string } export type DualViewBase = { apy: `${string}%` settleRatio: string //targetPrice term: string strike: string isUp: boolean expireTime: number currentPrice: DualCurrentPrice productId: string sellSymbol: string buySymbol: string amount?: string enterTime?: number stepLength?: string quote?: string outSymbol?: string outAmount?: string side?: string status?: string statusColor?: string maxDuration?: number autoStatus?: string autoIcon?: JSX.Element autoContent?: string newStrike?: string deliveryPrice?: string | undefined __raw__?: any } export type DualViewInfo = DualViewBase & { __raw__: { info: sdk.DualProductAndPrice index: sdk.DualIndex rule: sdk.DualRulesCoinsInfo } } export type ClaimToken = sdk.UserBalanceInfo & { isNft?: boolean nftTokenInfo?: sdk.UserNFTBalanceInfo luckyTokenHash?: string } export type DualViewOrder = DualViewBase & { __raw__: { order: sdk.UserDualTxsHistory } } export enum TRADE_TYPE { TOKEN = 'TOKEN', NFT = 'NFT', } export enum CLAIM_TYPE { redPacket = 'redPacket', lrcStaking = 'lrcStaking', allToken = 'allToken', } export type BanxaOrder = { id: string account_id: string account_reference: string order_type: 'CRYPTO-SELL' payment_type: string | null ref: number | null fiat_code: string fiat_amount: number coin_code: string coin_amount: number wallet_address: string | null wallet_address_tag: string | null fee: number | null fee_tax: number | null payment_fee: number | null payment_fee_tax: number | null commission: number | null tx_hash: string | null tx_confirms: number | null created_date: string created_at: string status: string completed_at: string | null merchant_fee: number | null merchant_commission: number | null meta_data: string | null blockchain: { id: number; code: 'LRC'; description: 'Loopring ' | null } } export const LuckyRedPacketList: LuckyRedPacketItem[] = [ { labelKey: 'labelLuckyRandomToken', desKey: 'labelRedPacketsSplitLuckyDetail', tags: [ 'showInNormal', 'enableInNFTS', 'enableInERC20', 'defaultForERC20', 'defaultForNFT', 'enableInBlindBox', ], value: { value: 1, partition: sdk.LuckyTokenAmountType.RANDOM, mode: sdk.LuckyTokenClaimType.COMMON, }, }, { labelKey: 'labelLuckyRandomToken', desKey: 'labelRedPacketsSplitLuckyDetail', tags: ['defaultForBlindBox', 'enableInBlindBox', 'showInBlindBox', 'defaultForFromNFT'], value: { value: 4, partition: sdk.LuckyTokenAmountType.RANDOM, mode: sdk.LuckyTokenClaimType.BLIND_BOX, }, }, { labelKey: 'labelLuckyCommonToken', desKey: 'labelLuckyCommonTokenDes', tags: ['showInNormal', 'showInBlindBox', 'enableInNFTS', 'enableInERC20'], value: { value: 2, partition: sdk.LuckyTokenAmountType.AVERAGE, mode: sdk.LuckyTokenClaimType.COMMON, }, }, { labelKey: 'labelLuckyRelayToken', desKey: 'labelLuckyRelayTokenDes', tags: ['showInNormal', 'showInBlindBox', 'enableInERC20', 'disabledForExclusive'], value: { value: 0, partition: sdk.LuckyTokenAmountType.RANDOM, mode: sdk.LuckyTokenClaimType.RELAY, }, }, ] export const QRCODE_REGION_ID = 'qrcodeRegionId' export type ACCOUNT_ADDRESS = string export type TX_HASH = string export type RedPacketHashItems = { [key: TX_HASH]: { claim: string luckToken: sdk.LuckyTokenItemForReceive blindboxClaimed: any } } export type RedPacketHashInfo = { [key: ACCOUNT_ADDRESS]: RedPacketHashItems } export type RedPacketHashInfos = { [key in sdk.ChainId extends string ? string : string]: RedPacketHashInfo } export enum OffRampStatus { // waitingForPayment = "waitingForPayment", watingForCreateOrder = 'watingForCreateOrder', waitingForWithdraw = 'waitingForWithdraw', expired = 'expired', cancel = 'cancel', done = 'done', refund = 'refund', } export type OffRampHashItem = { orderId: string chainId: sdk.ChainId address: string product: VendorProviders status: OffRampStatus wallet_address?: string | undefined checkout_iframe?: string account_reference?: string [key: string]: any } export type OffRampHashItemObj = { pending: OffRampHashItem payments: OffRampHashItem[] } export type OffRampHashItems = { [K in keyof T]: OffRampHashItemObj } export type OffRampHashInfo = { [key: ACCOUNT_ADDRESS]: OffRampHashItems } export type OffRampHashInfos = { [key in sdk.ChainId extends string ? string : string]: OffRampHashInfo } export enum RedPacketOrderType { TOKEN = 'TOKEN', NFT = 'NFT', BlindBox = 'BlindBox', FromNFT = 'FromNFT', } export type RedPacketOrderData = { tradeType: RedPacketOrderType isNFT: boolean tradeValue?: number fee: FeeInfo | undefined __request__: any target?: { redpacketHash: string addressListString: string popupChecked: boolean maxSendCount: number sentAddresses?: string[] } showNFT: boolean type: any } & Partial> & Partial & Partial export enum TabTokenTypeIndex { ERC20 = 'ERC20', NFT = 'NFT', } export interface SnackbarMessage { message: string key: number | string svgIcon?: string } export const BTRDE_PRE = 'BTRADE-' export enum TradeProType { sell = 'sell', buy = 'buy', } export enum TradeBaseType { price = 'price', quote = 'quote', base = 'base', tab = 'tab', slippage = 'slippage', stopPrice = 'stopPrice', checkMarketPrice = 'checkMarketPrice', } export type AmmHistoryItem = { close: number timeStamp: number } export enum LocalStorageConfigKey { tokenMap = 'tokenMap', ammpools = 'ammpools', markets = 'markets', btradeMarkets = 'btradeMarkets', vaultMarkets = 'vaultMarkets', vaultTokenMap = 'vaultTokenMap', exchangeInfo = 'exchangeInfo', disableWithdrawTokenList = 'disableWithdrawTokenList', } export enum DualStep { ChooseType = 'ChooseType', ShowBase = 'ShowBase', ShowSellBuy = 'ShowSellBuy', ShowQuote = 'ShowQuote', ShowList = 'ShowList', } export enum DualViewType { DualGain = 'DualGain', DualDip = 'DualDip', DualBegin = 'DualBegin', DualBTC = 'DualBTC', All = 'All', } export const DualGain = [ // { step: DualStep.ChooseType, type: 'Card' }, { step: DualStep.ShowBase, type: 'Tab', labelKey: 'labelDualChooseTokenDUAL_BASE' }, {}, { step: DualStep.ShowQuote, type: 'Tab', labelKey: 'labelDualChooseTargetPriceDUAL_BASE' }, ] export const DualDip = [ // { step: DualStep.ChooseType, type: 'Card' }, { step: DualStep.ShowBase, type: 'Tab', labelKey: 'labelDualChooseTokenDUAL_CURRENCY' }, {}, { step: DualStep.ShowQuote, type: 'Tab', labelKey: 'labelDualChooseTargetPriceDUAL_CURRENCY' }, ] export const DualBegin = [ { step: DualStep.ShowBase, type: 'Tab', labelKey: 'labelDualBeginnerStep1Title' }, { step: DualStep.ChooseType, type: 'Tab', labelKey: 'labelDualBeginnerSellHigh' }, { step: DualStep.ShowQuote, type: 'Tab', labelKey: 'labelDualBeginnerStep3Title' }, ] export const DualBTC = [ { step: DualStep.ShowBase, type: 'Tab', labelKey: 'labelDualChooseTokenDUAL_BASE' }, {}, { step: DualStep.ShowQuote, type: 'Tab', labelKey: 'labelDualBeginnerStep3Title' }, ] export type VaultMarketExtends = { enabled: boolean | 'isFormLocal' } & Omit< sdk.VaultMarket, 'enabled' > & { vaultMarket: string originalBaseSymbol: string originalQuoteSymbol: string } export enum VaultLoanType { Borrow = 'Borrow', Repay = 'Repay', } export enum AmmPanelType { Join = 0, Exit = 1, } export enum DualInvestConfirmType { USDCOnly = 'USDCOnly', all = 'all', } export enum VaultAction { VaultJoin = 'VaultJoin', VaultExit = 'VaultExit', VaultLoan = 'VaultLoan', VaultSwap = 'VaultSwap', } export enum VaultSwapStep { Edit = 'Edit', Borrow = 'Borrow', Swap = 'Swap', Swaping = 'Swaping', } ================================================ FILE: packages/common-resources/static-resources/src/constant/vendor.ts ================================================ export enum VendorProviders { Ramp = 'Ramp', Banxa = 'Banxa', } // export const vendorList: VendorItem[] = [ // { // key: VendorProviders.Ramp, // svgIcon: "RampIcon", // // handleSelect: () => {}, // }, // { // key: VendorProviders.Banxa, // svgIcon: "BanxaIcon", // flag: { // startDate: Date.now() - 100, //1649688436000, // endDate: 1650844800000, // tag: "🔥", // highLight: "lableBanxaFeeFree", // }, // // handleSelect: () => {}, // }, // ]; export const VendorList = { Ramp: { key: VendorProviders.Ramp, svgIcon: 'RampIcon', // handleSelect: () => {}, }, Banxa: { key: VendorProviders.Banxa, svgIcon: 'BanxaIcon', flag: { startDate: 1649635200000, endDate: 1650844800000, tag: '🔥', highLight: 'labelBanxaFeeFree', }, // handleSelect: () => {}, }, } ================================================ FILE: packages/common-resources/static-resources/src/constant/walletConnector.ts ================================================ import { GatewayItem } from '../loopring-interface' import { SoursURL } from './router' import { ConnectProviders } from '@loopring-web/web3-provider' export enum GatewaySort { MetaMask, WalletConnect, GameStop, Coinbase, WalletConnectV1, } const gatewayMap = new Map() // = [ gatewayMap.set(GatewaySort.MetaMask, { key: ConnectProviders.MetaMask, keyi18n: ConnectProviders.MetaMask, imgSrc: SoursURL + 'svg/meta-mask.svg', }) gatewayMap.set(GatewaySort.WalletConnect, { key: ConnectProviders.WalletConnect, keyi18n: ConnectProviders.WalletConnect, imgSrc: SoursURL + 'svg/wallet-connect.svg', }) gatewayMap.set(GatewaySort.GameStop, { key: ConnectProviders.GameStop, keyi18n: ConnectProviders.GameStop, imgSrc: SoursURL + 'svg/gs.svg', }) gatewayMap.set(GatewaySort.Coinbase, { key: ConnectProviders.Coinbase, keyi18n: ConnectProviders.Coinbase, imgSrc: SoursURL + 'svg/coinbase-wallet.svg', }) gatewayMap.set(GatewaySort.WalletConnectV1, { key: (ConnectProviders.WalletConnectV1 + 'V1') as ConnectProviders, keyi18n: ConnectProviders.WalletConnectV1 + 'V1', imgSrc: SoursURL + 'svg/wallet-connect.svg', }) export const gatewayList: GatewayItem[] = [...gatewayMap.keys()].reduce((prev, key) => { // @ts-ignore prev[key as any] = gatewayMap.get(key as any) return prev }, [] as GatewayItem[]) ================================================ FILE: packages/common-resources/static-resources/src/error/errorMap.tsx ================================================ import { Trans } from 'react-i18next' import { Link } from '@mui/material' export const ErrorMap = { ERROR_UNKNOWN: { id: 'ERROR_UNKNOWN', messageKey: 'errorUnknown', }, ERROR_ON_FROM_SUBMIT: { id: 'ERROR_ON_FROM_SUBMIT', messageKey: 'errorOnFromSubmit', }, ERROR_WRONG_ACCOUNT: { id: 'ERROR_WRONG_ACCOUNT', messageKey: 'errorWrongAccount', }, ERROR_WRONG_TOKEN: { id: 'ERROR_WRONG_TOKEN', messageKey: 'errorWrongToken', }, ERROR_WRONG_APIKEY: { id: 'ERROR_WRONG_APIKEY', messageKey: 'errorWrongApikey', }, ERROR_WRONG_BALANCE: { id: 'ERROR_WRONG_BALANCE', messageKey: 'errorWrongBalance', }, ERROR_WRONG_MIN: { id: 'ERROR_WRONG_BALANCE', messageKey: 'errorWrongBalance', }, ERROR_MINIMUM_ORDER: { id: 'ERROR_MINIMUM_ORDER', messageKey: 'errorMinimumOrder', }, ERROR_MAXIMUM_ORDER: { id: 'ERROR_MAXIMUM_ORDER', messageKey: 'errorMaximumOrder', }, ERROR_ON_FROZEN: { id: 'ERROR_ON_FROZEN', messageKey: 'errorOnFrozen', }, ERROR_ON_FEE: { id: 'ERROR_ON_FEE', messageKey: 'errorAboutFee', }, ERROR_PROVIDER_ERROR: { id: 'ERROR_PROVIDER_ERROR', messageKey: 'errorProviderError', options: {}, }, ERROR_ON_STORAGE_ID: { id: 'ERROR_ON_STORAGE_ID', messageKey: 'errorOnStorageId', options: {}, }, ERROR_ON_NO_RECIPIENT: { id: 'ERROR_ON_NO_RECIPIENT', messageKey: 'errorOnNoRecipient', options: {}, }, ERROR_INVALID_HASH: { id: 'ERROR_INVALID_HASH', messageKey: 'errorInvalidHash', options: {}, }, ERROR_ON_CANCEL_ORDERS: { id: 'ERROR_ON_CANCEL_ORDERS', messageKey: 'errorOnCancelOrders', options: {}, }, ERROR_ON_GAS: { id: 'ERROR_ON_GAS', messageKey: 'errorOnGas', options: {}, }, ERROR_NO_MARKET: { id: 'ERROR_NO_MARKET', messageKey: 'errorNoMarket', options: {}, }, ERROR_INVALID_ORDER_ID: { id: 'ERROR_INVALID_ORDER_ID', messageKey: 'errorInvalidOrderId', options: {}, }, ERROR_ON_RATE: { id: 'ERROR_ON_RATE', messageKey: 'errorOnRate', options: {}, }, ERROR_FOR_EXIST_ORDER: { id: 'ERROR_FOR_EXIST_ORDER', messageKey: 'errorForExistOrder', options: {}, }, ERROR_ORDER_EXPIRED: { id: 'ERROR_ORDER_EXPIRED', messageKey: 'errorOrderExpired', options: {}, }, LOADING_WHOLE_SITE: { id: 'LOADING_WHOLE_SITE', messageKey: 'errorLoading', }, NO_SUPPORT_PAIR: { id: 'NO_SUPPORT_PAIR', messageKey: 'no support base/quote pair!', }, NO_SDK: { id: 'NO_SDK', messageKey: 'errorBase', }, TIME_OUT: { id: 'TIME_OUT', messageKey: 'errorTimeout', }, NO_NETWORK_ERROR: { id: 'NO_NETWORK_ERROR', messageKey: 'errorMessageNoNetwork', }, NO_TOKEN_MAP: { id: 'NO_TOKEN_MAP', messageKey: 'errorMessageTokenMapIsEmpty', }, NO_ENOUGH_BALANCE: { id: 'NO_ENOUGH_BALANCE', messageKey: 'errorTokenNotEnough', }, NO_TOKEN_KEY_LIST: { id: 'NO_TOKEN_KEY_LIST', messageKey: 'errorRequiredTokenKeyList', }, GET_X_TOKEN_TICKER_ERROR: { id: 'NO_TOKEN_KEY_LIST', messageKey: 'errorRequiredTokenKeyList', options: {}, }, BUILD_AMM_MAP_WITH_TICKER: { id: 'BUILD_AMM_MAP_WITH_TICKER', messageKey: 'errorBase', options: {}, }, TRADE_LITE_SET_PAIR_ERROR: { id: 'TRADE_LITE_SET_PAIR_ERROR', messageKey: 'errorBase', options: {}, }, SOCKET_ERROR: { id: 'SOCKET_ERROR', messageKey: 'errorBase', options: {}, }, TRADE_404: { id: '404', messageKey: 'error404', options: {}, }, NTF_ID_ENCODE_ERROR: { id: 'NTF_ID_ENCODE_ERROR', messageKey: 'NTF_ID_ENCODE_ERROR', options: {}, }, PROVIDER_ERROR: { id: 'PROVIDER_ERROR', messageKey: 'errorDisableOtherWalletForCurrent', options: {}, }, GENERATE_EDDSA: { id: 'GENERATE_EDDSA', messageKey: 'errorGenerateEddsa', options: {}, }, DATA_NOT_READY: { id: 'DATA_NOT_READY', messageKey: 'errorDataNotReady', options: {}, }, PROVIDER_NOT_INSTALL_GME: { id: 'PROVIDER_NOT_INSTALL_GME', messageKey: 'errorNotInstallGME', options: { link: ( app market ), }, }, NO_IPFS_INSTANCE: { id: 'NO_IPFS_INSTANCE', messageKey: 'errorNoIpfsInstance', options: {}, }, ADD_IPFS_ERROR: { id: 'ADD_IPFS_ERROR', messageKey: 'errorAddIpfsError', options: {}, }, CREATE_IPFS_ERROR: { id: 'CREATE_IPFS_ERROR', messageKey: 'errorCreateIpfsError', options: {}, }, NOT_SAME_IPFS_RESOURCE: { id: 'NOT_SAME_IPFS_RESOURCE', messageKey: 'errorNotSameIpfsResource', options: {}, }, IPFS_CID_TO_NFTID_ERROR: { id: 'IPFS_CID_TO_NFTID_ERROR', messageKey: 'errorIpfsDidToNftidError', options: {}, }, ERROR_MINT_OVERLAP: { id: 'ERROR_MINT_OVERLAP', messageKey: 'errorMintOverlap', options: {}, }, ERROR_JSON_STRINGIFY: { id: 'ERROR_JSON_STRINGIFY', messageKey: 'errorJSONStringify', options: {}, }, ERROR_COLLECTION_METADATA_NO_TILEURI: { id: 'ERROR_COLLECTION_METADATA_NO_TILEURI', messageKey: 'errorCollectionMetadataNoTileUri', options: {}, }, ERROR_COLLECTION_NO_NAME: { id: 'ERROR_COLLECTION_NO_NAME', messageKey: 'errorCollectionNoName', options: {}, }, ERROR_COLLECTION_SAME_NAME: { id: 'ERROR_COLLECTION_SAME_NAME', messageKey: 'errorCollectionSameName', options: {}, }, ERROR_COLLECTION_EMPTY: { id: 'ERROR_COLLECTION_EMPTY', messageKey: 'errorCollectionEmpty', options: {}, }, ERROR_COLLECTION_NOT_READABLE: { id: 'ERROR_COLLECTION_NOT_READABLE', messageKey: 'errorCollectionNotReadable', options: {}, }, ERROR_COLLECTION_INFO: { id: 'ERROR_COLLECTION_INFO', messageKey: 'errorCollectionInfo', options: {}, }, ERROR_COLLECTION_NO_SUPPORT: { id: 'ERROR_COLLECTION_NO_SUPPORT', messageKey: 'errorCollectionNoSupport', options: {}, }, ERROR_ON_REFRESH: { id: 'ERROR_ON_REFRESH', messageKey: 'errorOnRefresh', options: {}, }, IPFS_TIME_OUT: { id: 'IPFS_TIME_OUT', messageKey: 'errorIpfsTimeout', }, ERROR_RAMP_NO_INSTANCE: { id: 'ERROR_RAMP_NO_INSTANCE', messageKey: 'errorRampNoInstance', options: {}, }, ERROR_DUAL_EXPIRED: { id: 'ERROR_DUAL_EXPIRED', messageKey: 'errorDualExpired', options: {}, }, ERROR_PRIVATE_KEY: { id: 'ERROR_PRIVATE_KEY', messageKey: 'errorPrivateKey', options: {}, }, ERROR_NO_RESPONSE: { id: 'ERROR_NO_RESPONSE', messageKey: 'errorNoResponse', options: {}, }, ERROR_REDPACKET_EMPTY: { id: 'ERROR_REDPACKET_EMPTY', messageKey: 'errorRedpacketEmpty', options: {}, }, ERROR_REDPACKET_CLAIMED: { id: 'ERROR_REDPACKET_CLAIMED', messageKey: 'errorRedpacketClaimed', options: {}, }, ERROR_REDPACKET_CLAIM_OUT: { id: 'ERROR_REDPACKET_CLAIM_OUT', messageKey: 'errorRedpacketClaimOut', options: {}, }, ERROR_REDPACKET_CLAIM_TIMEOUT: { id: 'ERROR_REDPACKET_CLAIM_TIMEOUT', messageKey: 'errorRedpacketClaimTimeOut', options: {}, }, ERROR_OFF_RAMP_EXPIRED: { id: 'ERROR_OFF_RAMP_EXPIRED', messageKey: 'errorOffRampExpired', }, PROVIDER_ERROR_UNKNOWN: { id: 'PROVIDER_ERROR_UNKNOWN', messageKey: 'errorProviderErrorUnknown', }, GUARDIAN_ROUTER_ERROR: { id: 'GUARDIAN_ROUTER_ERROR', messageKey: 'errorGuardianRouterError', }, ERROR_SWITCH_ETHEREUM: { id: 'ERROR_SWITCH_ETHEREUM', messageKey: 'errorSwitchEthereum', }, ERROR_NO_GAMESTOP_EXTENSION: { id: 'ERROR_NO_GAMESTOP_EXTENSION', messageKey: 'errorNoGamestopExtension', }, ERROR_ETHEREUM_NOT_METAMASK: { id: 'ERROR_ETHEREUM_NOT_METAMASK', messageKey: 'errorEthereumNotMetamask', }, ERROR_GAMESTOP_NO_CHAIN_CHANGE: { id: 'ERROR_GAMESTOP_NO_CHAIN_CHANGE', messageKey: 'errorGamestopNoChainChange', }, ERROR_ADDRESS_CHECK_ERROR: { id: 'ERROR_ADDRESS_CHECK_ERROR', messageKey: 'errorAddressCheckError', }, ERROR_CONTACT_EXISTED: { id: 'ERROR_CONTACT_EXISTED', messageKey: 'errorContactExisted' }, ERROR_CONTACT_NAME_EXISTED: { id: 'ERROR_CONTACT_NAME_EXISTED', messageKey: 'errorContactNameExisted', }, ERROR_CONTACT_LIMIT_REACHED: { id: 'ERROR_CONTACT_LIMIT_REACHED', messageKey: 'errorContactLimit', }, ERROR_CONTACT_NAME_OVER_LIMIT: { id: 'ERROR_CONTACT_NAME_OVER_LIMIT', messageKey: 'errorContactOverLimit', }, ERROR_ORDER_FAILED: { id: 'ERROR_ORDER_FAILED', messageKey: 'errorOrderFailed', }, ERROR_ADDRESS_BURN_NFT:{ id: 'ERROR_ADDRESS_BURN_NFT', messageKey: 'errorAddressBurnNft', } } export enum UIERROR_CODE { NO_NETWORK_ERROR = 700000, UNKNOWN = 700001, PROVIDER_ERROR = 700002, PROVIDER_ERROR_UNKNOWN = 700003, PROVIDER_NOT_INSTALL_GME = 700004, DATA_NOT_READY = 700005, GENERATE_EDDSA = 700006, NO_IPFS_INSTANCE = 700007, ADD_IPFS_ERROR = 700008, CREATE_IPFS_ERROR = 700009, NOT_SAME_IPFS_RESOURCE = 700010, IPFS_CID_TO_NFTID_ERROR = 700011, TIME_OUT = 700012, ERROR_JSON_STRINGIFY = 700013, ERROR_COLLECTION_METADATA_NO_TILEURI = 700014, ERROR_COLLECTION_NO_NAME = 700015, ERROR_COLLECTION_INFO = 700016, ERROR_COLLECTION_EMPTY = 700017, ERROR_COLLECTION_NO_SUPPORT = 700018, ERROR_COLLECTION_NOT_READABLE = 700019, IPFS_TIME_OUT = 700020, ERROR_ON_FEE_UI = 700021, ERROR_PRIVATE_KEY = 700022, ERROR_NO_RESPONSE = 700023, ERROR_REDPACKET_EMPTY = 700024, ERROR_ORDER_FAILED = 700205, ERROR_RAMP_NO_INSTANCE = 700100, ERROR_OFF_RAMP_EXPIRED = 700101, ERROR_ADDRESS_CHECK_ERROR = 700102, ERROR_ADDRESS_BURN_NFT = 700103, ERROR_WALLECTCONNECT_MANUALLY_CLOSE = 700201, ERROR_GAMESTOP_NO_CHAIN_CHANGE = 700202, ERROR_DUAL_EXPIRED = 115003, ERROR_REDPACKET_CLAIMED = 113002, ERROR_REDPACKET_CLAIM_OUT = 113006, ERROR_REDPACKET_CLAIM_TIMEOUT = 113000, ERROR_SWITCH_ETHEREUM = 700202, ERROR_NO_GAMESTOP_EXTENSION = 700203, ERROR_ETHEREUM_NOT_METAMASK = 700204, ERROR_CONTACT_EXISTED = 124001, ERROR_CONTACT_NAME_EXISTED = 124002, ERROR_CONTACT_LIMIT_REACHED = 124003, ERROR_CONTACT_NAME_OVER_LIMIT = 124004, } export type ErrorObject = { from?: string timestamp?: number messageKey: string [key: string]: any } export const SDK_ERROR_MAP_TO_UI = { 700000: ErrorMap.NO_NETWORK_ERROR, 700001: ErrorMap.ERROR_UNKNOWN, //UI Unknown error => 700002: ErrorMap.ERROR_PROVIDER_ERROR, 700003: ErrorMap.PROVIDER_ERROR_UNKNOWN, 700004: ErrorMap.PROVIDER_NOT_INSTALL_GME, 700005: ErrorMap.DATA_NOT_READY, 700006: ErrorMap.GENERATE_EDDSA, 700007: ErrorMap.NO_IPFS_INSTANCE, 700008: ErrorMap.ADD_IPFS_ERROR, 700009: ErrorMap.CREATE_IPFS_ERROR, 700010: ErrorMap.NOT_SAME_IPFS_RESOURCE, 700011: ErrorMap.IPFS_CID_TO_NFTID_ERROR, 700012: ErrorMap.TIME_OUT, 700013: ErrorMap.ERROR_JSON_STRINGIFY, 700014: ErrorMap.ERROR_COLLECTION_METADATA_NO_TILEURI, 700015: ErrorMap.ERROR_COLLECTION_NO_NAME, 700016: ErrorMap.ERROR_COLLECTION_INFO, 700017: ErrorMap.ERROR_COLLECTION_EMPTY, 700018: ErrorMap.ERROR_COLLECTION_NO_SUPPORT, 700019: ErrorMap.ERROR_COLLECTION_NOT_READABLE, 700020: ErrorMap.IPFS_TIME_OUT, 700021: ErrorMap.ERROR_ON_FEE, 700022: ErrorMap.ERROR_PRIVATE_KEY, 700023: ErrorMap.ERROR_NO_RESPONSE, 700024: ErrorMap.ERROR_REDPACKET_EMPTY, 700205: ErrorMap.ERROR_ORDER_FAILED, 700100: ErrorMap.ERROR_RAMP_NO_INSTANCE, 700101: ErrorMap.ERROR_OFF_RAMP_EXPIRED, 700202: ErrorMap.ERROR_SWITCH_ETHEREUM, 700103: ErrorMap.ERROR_ADDRESS_BURN_NFT, 700203: ErrorMap.ERROR_NO_GAMESTOP_EXTENSION, 700204: ErrorMap.ERROR_ETHEREUM_NOT_METAMASK, 700102: ErrorMap.ERROR_ADDRESS_CHECK_ERROR, 100000: ErrorMap.ERROR_UNKNOWN, //Unknown error => 100001: ErrorMap.ERROR_ON_FROM_SUBMIT, //Invalid argument 101001: ErrorMap.ERROR_WRONG_ACCOUNT, //The address was not found 101002: ErrorMap.ERROR_WRONG_ACCOUNT, //User not found 102001: ErrorMap.ERROR_ON_FROM_SUBMIT, //Exchange ID is incorrect 102002: ErrorMap.ERROR_WRONG_TOKEN, //Unsupported TokenId in the order 102003: ErrorMap.ERROR_WRONG_ACCOUNT, //Invalid account ID 102004: ErrorMap.ERROR_INVALID_ORDER_ID, //Invalid order ID 102005: ErrorMap.ERROR_NO_MARKET, //Market does not support 102006: ErrorMap.ERROR_ON_RATE, //Illegal rate field 102007: ErrorMap.ERROR_FOR_EXIST_ORDER, //Order already exists 102008: ErrorMap.ERROR_ORDER_EXPIRED, //Order has expired 102010: ErrorMap.ERROR_WRONG_APIKEY, //Order is missing signature information 102011: ErrorMap.ERROR_WRONG_BALANCE, //Insufficient user balance 102012: ErrorMap.ERROR_MINIMUM_ORDER, //The order amount is too small 102014: ErrorMap.ERROR_ON_FROZEN, //Failed to freeze the amount, please try again later 102020: ErrorMap.ERROR_MAXIMUM_ORDER, //Exceeded the maximum order amount 102021: ErrorMap.ERROR_ON_FROM_SUBMIT, // Nonce is invalid 102022: ErrorMap.ERROR_ON_FROM_SUBMIT, //Transfer sender is invalid 102023: ErrorMap.ERROR_ON_FROM_SUBMIT, //Transfer receiver is invalid 102024: ErrorMap.ERROR_ON_FEE, //Fee token is unsupported 102025: ErrorMap.ERROR_ON_FEE, //Fee token is unsupported//Transfer token isnt consistent with fee token 102027: ErrorMap.ERROR_ON_FROM_SUBMIT, //Submit order failed 102028: ErrorMap.ERROR_ON_STORAGE_ID, //No Available storage id 102030: ErrorMap.ERROR_ON_STORAGE_ID, //Invalid storage id 102032: ErrorMap.ERROR_ON_NO_RECIPIENT, //Invalid recipient 102117: ErrorMap.ERROR_ON_CANCEL_ORDERS, //No orders to cancel 102118: ErrorMap.ERROR_ON_CANCEL_ORDERS, //Failed to cancel orders, please try again later 102119: ErrorMap.ERROR_INVALID_HASH, //Invalid hash 102120: ErrorMap.ERROR_ON_FROM_SUBMIT, //Order is not valid 102122: ErrorMap.ERROR_ON_CANCEL_ORDERS, //Order already in cancel 104001: ErrorMap.ERROR_WRONG_APIKEY, //Empty ApiKey 102040: ErrorMap.ERROR_MINT_OVERLAP, //mint record with the same nft token address and token id already exists 104002: ErrorMap.ERROR_WRONG_APIKEY, //Invalid ApiKey 104003: ErrorMap.ERROR_WRONG_ACCOUNT, //Invalid Account ID 104004: ErrorMap.ERROR_ON_FROM_SUBMIT, //No signature information provided 104005: ErrorMap.ERROR_ON_FROM_SUBMIT, //Wrong signature information 104208: ErrorMap.ERROR_UNKNOWN, //Unknown error in Ethereum node 104209: ErrorMap.ERROR_UNKNOWN, //Partial batch operation failed 105001: ErrorMap.ERROR_ON_GAS, //Failed to get recommended gas 107001: ErrorMap.ERROR_WRONG_ACCOUNT, //User ID cannot be empty 107002: ErrorMap.ERROR_INVALID_HASH, //Order Hash cannot be empty 107003: ErrorMap.ERROR_ON_FROM_SUBMIT, //Order does not exist 108000: ErrorMap.ERROR_NO_MARKET, //Unsupported market 102127: ErrorMap.ERROR_COLLECTION_SAME_NAME, 108001: ErrorMap.ERROR_ON_FROM_SUBMIT, //Unsupported depth level 113002: ErrorMap.ERROR_REDPACKET_CLAIMED, // REDPACKET_CLAIMED 113006: ErrorMap.ERROR_REDPACKET_CLAIM_OUT, // REDPACKET_COUNT_OUT 113000: ErrorMap.ERROR_REDPACKET_CLAIM_TIMEOUT, // REDPACKET_CLAIM_TIMEOUT 114001: ErrorMap.ERROR_ON_FEE, //Fee token not support 114002: ErrorMap.ERROR_ON_FEE, //Fee amount invalid, need refresh the fee. App need refresh fee less than every 15 mins 122001: ErrorMap.ERROR_ON_REFRESH, 124001: ErrorMap.ERROR_CONTACT_EXISTED, 124002: ErrorMap.ERROR_CONTACT_NAME_EXISTED, 124003: ErrorMap.ERROR_CONTACT_LIMIT_REACHED, 124004: ErrorMap.ERROR_CONTACT_NAME_OVER_LIMIT, 115003: ErrorMap.ERROR_DUAL_EXPIRED, } ================================================ FILE: packages/common-resources/static-resources/src/error/index.ts ================================================ export * from './errorMap' export type ErrorType = { id: string messageKey: string message?: string options?: any } export type ErrorWithCodeType = { id: string code: number message: string messageKey?: string options?: any } export class CustomError extends Error { constructor(error: ErrorType) { // Pass remaining arguments (including vendor specific ones) to parent constructor super(error.id) // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.name = error.id this.message = error?.message ?? error.id this._date = Date.now() this._messageKey = error.messageKey this._options = error.options // Custom debugging information // this.foo = foo // this.date = new Date() } private _options: any get options(): any { return this._options } private _date: number get date(): number { return this._date } private _messageKey: string get messageKey(): string { return this._messageKey } } export class CustomErrorWithCode extends Error { constructor(error: ErrorWithCodeType) { // Pass remaining arguments (including vendor specific ones) to parent constructor super(error.id) // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.name = error.id this._code = error.code this.message = error.message this._date = Date.now() this._messageKey = error.messageKey ?? error.message // this._options = error.options; // Custom debugging information ' // this.foo = foo // this.date = new Date() } private _code: number get code(): number { return this._code } private _options: any get options(): any { return this._options } private _date: number get date(): number { return this._date } private _messageKey: string get messageKey(): string { return this._messageKey } } ================================================ FILE: packages/common-resources/static-resources/src/i18n/en_US/common.ts ================================================ /* eslint-disable max-len */ export default { labelErrorTitle: 'Error Detail: ', labelNoContent: 'No Content', labelError: 'Error', tokenEnter: 'Enter Token', tokenEnterPaymentToken: '', tokenMax: 'Available: ', tokenNFTMaxMINT: 'Max:', tokenHave: 'Available:', tokenEnterReceiveToken: '', tokenSelectToken: 'Select Token', tokenExchange: 'exchange', tokenNotEnough: 'Insufficient {{belong}} balance', tokenSearchCoin: 'Search Token Symbol', swapTitle: 'Swap', swapTolerance: 'Slippage Tolerance', labelSwapToleranceTooltips: 'Your trade will revert if the price changes unfavorably by more than this percentage.', swapPriceImpact: 'Price Impact', labelSwapPriceImpactTooltips: 'The difference between market price and estimated price due to trade size', swapMinReceive: 'Minimum Received', swapMinReceiveS: 'Min. Received', labelSwapMinReceiveTooltips: 'The pool price changes dynamically, the price you see when placing an order may be inconsistent with the final transaction price; also the received amount needs to deduct the fees from converted amount. The protocol can guarantee that the received token is at least this amount.', swapFee: 'Trading Fee', swapFeeS: 'Est. Fee', labelSwapFeeTooltips: 'The trading fee is determined by your VIP level and the size of your trade. Small trades (below ~$100) incur a higher fee. Please review the fee before confirming.', swapBtn: 'swap', goBack: 'go back', resetTitle: 'Reset {{layer2}} Keypair', restLabelEnterToken: 'Select Reset cause token', labelResetDescription: 'Each account on {{loopringL2}} needs an EdDSA private key (the account key) to sign off-chain (aka {{layer2}}) requests. You can reset the EdDSA keypair at any time.', resetFee: 'Fee {{count}} GAS ≈ ${{price}}', resetLabelBtn: 'Reset', labelActiveEnterToken: 'Select payment token', labelActiveAccountDescription: 'You need to have enough balance for {{layer2}} creation as below.', labelActiveAccountFee: 'Fee {{count}} GAS ≈ ${{price}}', labelActiveAccountBtn: 'Activate Account', depositLabelEnterToken: 'Select Layer 1 Token', labelDepositDescription: 'Once your deposit is confirmed on {{l1ChainName}}, \n it will be added to your balance within 2 minutes.', labelDepositAndActiveDescription: 'Make a deposit to activate your {{loopringL2}} account. Once your deposit is <1>confirmed on {{l1ChainName}}, it will be added to your balance within <3>2 minutes.', depositLabelRefer: 'Referral address, Account ID or ENS. (Optional)', depositLabelPlaceholder: 'address, Account ID or ENS', withdrawDescription: 'Your withdrawal will be processed in the next batch,\n which usually takes 30 minutes to 2 hours.\n (There will be a large delay if the {{l1ChainName}} gas price exceeds 500 GWei.)', withdrawTypeLabelFast: 'Fast ~15 seconds', withdrawTypeLabelStandard: 'Standard ~25 minutes', labelConnectWallet: 'Connect wallet', labelCustomer: 'Custom', labelChange24h: '{{timeUnit}} Change', labelDepth: 'Depth', labelTrend: 'Price', label1W: 'W1', label1H: 'H1', label1D: 'D1', labelCopyAddress: 'Copy', labelDisconnect: 'Disconnect', labelLockLayer2: 'Lock', labelUnLockLayer2: 'Sign in First', labelSwitchAccount: 'Switch', labelViewEth: 'View on Etherscan', labelQRCode: 'View QR Code', labelShowAccountInfo: 'Show account information', labelAssetTitle: '{{loopringL2}} Total Assets', labelAssetMobileTitle: '{{l2Symbol}} Assets', labelShowAccount: 'Show or Hide Assets', labelLevel: 'VIP Level', labelOrderbook: 'Order book', labelSetPublicKey: 'Set EdDSA Public Key', labelTitleSecurity: 'Security', labelTitleResetL2Keypair: 'Reset {{loopringL2}} Keypair', labelBtnReset: 'Reset', labelHadChangPassword: 'You changed your keypair {{passDay}} ago.', labelTitleForceWithdraw: 'Force Withdraw', labelBtnForceWithdraw: 'Force Withdraw', labelTitleExportAccount: 'Export Account', labelDescriptionExportAccount: 'In order to access the {{loopringL2}} APIs, you will need to export a security key.', labelBtnExportAccount: 'Export Account', labelDownloadViewMore: 'View More', labelTitlePreferences: 'Preferences', labelTitleLayout: 'Custom Setting', whichColorIsUp: '<0>{{up}} up and <2>{{down}} down ', labelTradeFeeLevel: 'Your Trading Fee Level:', labelLanguage: 'Language', labelCurrency: 'Currency', currencySetting: 'Currency', labelColors: 'Colors', labelTheme: 'Dark Theme', labelthemeLight: 'Light theme', labelthemeDark: 'Dark theme', labelgreen: 'green', labelred: 'red', langZH: 'Chinese', langEN: 'English', labelUSDollar: 'USD', labelCNYYuan: 'CNY', labelMaker: 'Maker', labelTaker: 'Taker', labelAssetsTitle: 'Assets', labelVolume: 'volume', labelAmount: 'Amount', labelLiquidityDeposit: 'Subscribe', labelLiquidityWithdraw: 'Redeem', labelAvailable: 'Available:', labelTokenAmount: 'Amount', labelRemoveLiquidityBtn: 'Remove Liquidity', labelAddLiquidityBtn: 'Add Liquidity', labelEndLiquidityBtn: 'Ended', labelTradePanelHideOtherPairs: 'Hide other pairs', labelLPTokens: 'LP Tokens', labelMyLPToken: 'My Liquidity Token', labelMyLPAToken: 'My {{symbol}}', labelMyLPBToken: 'My {{symbol}}', labelMyLPAmountFor: 'My Ratio', labelTrade: 'Trade', labelAmmList: 'AMM List', labelMyPoolShare: 'My Pool Share', labelFee: 'Fee', labelLPTotal: 'Total ', labelReward: 'Rewards', labelMyReward: 'My Rewards', labelDate: 'Date', labelBack: 'Back', labelAPR: 'APR', label24Volume: '24h Volume', label24VolumeSimple: '24h Vol', labelTVL: 'TVL', labelAmmTotalToken: 'Tokens in AMM', labelNoActiveEvent: 'No event', labelNew: 'New', labelAccount: 'Account', labelAll: 'All', labelMe: 'My Liquidity', labelMyTrade: 'My Trades', labelRecent: 'Market Trades', labelMyAmm: 'My AMM Liquidity', labelMyAmmRecord: 'My AMM Records', labelCurrentActivities: 'Current Activities', labelPastActivities: 'Past Activities', labelTotalPositionValue: 'Total Investment', labelFeeRewards: 'Fee Rewards', labelMiningRewards: 'Mining Rewards', labelLiquidityValue: 'Liquidity Value', labelCopyAddClip: 'Address Copied to Clipboard!', labelPleaseInputWalletAddress: 'Please input address / ENS / Account ID', labelEmptyDefault: 'No data to display', labelUnlockAccount: 'Unlock Account', labelLockWallet: 'Lock Wallet', labelAssetsDistribution: 'Assets Distribution', labelTotalAssets: 'Assets Trend', labelTxnPageTitle: 'Deposit/Withdraw', labelTradePageTitle: 'Trade History', labelAmmPageTitle: 'AMM Records', labelSwapSuccess: 'Swap successful!', labelOrderProcessing: 'Order Placed!', labelSwapFailed: 'Swap failed!', labelJoinAmmSuccess: 'Successfully joined AMM Pool!', labelJoinAmmFailed: 'Failed to join AMM Pool!', labelExitAmmSuccess: 'Successfully exited the AMM Pool!', labelExitAmmFailed: 'Failed to exit the AMM Pool!', labelConnectBy: 'Connected with <1>{{connectBy}}', labelWrongNetwork: 'Wrong network', labelActivatedAccountDeposit: 'Please deposit to activate your {{layer2}} account', labelActivatedAccountNotSupport: 'Your wallet does not support {{loopringL2}}', labelActivatedAccountNotSupportDes: 'Please connect with a different wallet or download the Loopring Wallet mobile app.', labelNotAllowForSmartWalletTitle: 'Apologize', labelProcessing: 'Processing', labelProviderProcessing: 'Connect Wallet with {{name}}...', labelProviderCommonProcessDescribe: 'Please click the ‘Approve’ button on the Wallet Extension popup window. If the wallet popup window is dismissed, please manually click <5> on your browser toolbar.', labelWalletConnectProcessDescribe: 'Please wait for WalletConnect provider to confirm processing', labelWalletConnectQRCode: 'Please scan the QR code with a WalletConnect compatible application', labelSuccessConnect: 'Successfully Connected with {{providerName}}', labelSuccessConnectDescribe: 'Congratulations, Connection Successful!', labelCopyClipBoard: 'Copy', labelCopyManually: 'Manually Selected & Copy:', labelRejectOrError: 'Request was rejected or some unknown error occurred, please retry', labelWalletConnectProcessDescribe2: 'Please click ‘Approve’ on your device.', labelUnlockProcessing: 'Unlocking {{layer2}}...', labelFailedConnect: 'Connection Failed', labelRejectConnect: 'Connection request was rejected', // labelTokenAccess:'Waiting to approve {{symbol}} access!', labelTokenAccess: 'Waiting for approval', labelFailedTokenAccess: 'Failed to approve {{symbol}} access!', labelNFTTokenFailedAccess: 'Failed to approve NFT access!', labelSuccessTokenAccess: 'Congratulations, you have {{symbol}} access!', labelSuccessUnlockDescribe: 'Congratulations, Successfully Unlocked!', labelSuccessUnlock: 'Unlock Successful!', labelActivateAccount: 'Activate Account', labelClose: 'Close', labelRetry: 'Retry', labelTryNext: 'Sign Again', labelQuotePageFavourite: 'Favourites', labelQuotePageAll: 'All', labelQuotePageTradeRanking: 'Tournaments', labelFailedUnlock: 'Unlock Failed', labelFailedUpdateAcc: 'Update Account Failed', labelUpdateAccSigWarning: 'Your Wallet does not support current sig function, it will try another one.', labelUpdateAccUserDenied: 'Signature request was rejected!', labelCreateLayer2Title: 'Create {{layer2}} Account', labelCreateAccount: 'Create {{layer2}} Account', labelUpdateAccount: 'Sign in Account', labelTryAnother: 'Try Another Sig Method', labelCancel: 'Cancel', describeTitleNoAccount: "As {{l1ChainName}}'s first ever zkRollup, Loopring {{layer2}} allows you to avoid costly gas fees and network congestion with the same security as mainnet - 100x cheaper and faster.", describeTitleOpenAccounting: 'Your deposit has been submitted to {{l1ChainName}}.\n Please wait...', describeTitleOnErrorNetwork: 'Your current network is not supported by Loopring!\n Please change network via {{connectName}}.', describeTitleNotActive: "As {{l1ChainName}}'s first ever zkRollup, Loopring {{layer2}} allows you to avoid costly gas fees and network congestion with the same security as mainnet - 100x cheaper and faster.", describeTitleConnectToWallet: "As {{l1ChainName}}'s first ever zkRollup, Loopring {{layer2}} allows you to avoid costly gas fees and network congestion with the same security as mainnet - 100x cheaper and faster.", describeWhatIsGuardian: 'What is a Loopring guardian', describeTitleConnectToWalletAsGuardian: 'Connect a wallet to assign it as a guardian!', describeTitleLocked: 'Unlock your account to view your assets.', labelLiquidityPageTitle: 'AMM Pools', labelMinReceive: 'Minimum Received Amount', labelFilter: 'Search', labelMiningPageTitle: 'Loopring Liquidity Mining', labelMiningPageViceTitle: 'Earn rewards for contributing to Loopring liquidity.', labelMiningActiveDate: 'Active Date', labelMiningLiquidity: 'Liquidity', labelMiningActivityReward: 'Activity Reward', labelMiningMyShare: 'My Share', labelMiningMyReward: 'My Reward', labelMiningPlaceOrderBtn: 'Place Order', labelMiningViewDetails: 'View details', labelMiningMaxSpread: 'Max Spread', labelMiningReward: 'Reward', labelCookiesAgree: 'Agree', labelLimitMin: 'Minimum of {{arg}}', labelAmmMinAnd: 'and', labelLimitMinUnknown: 'Order too small', labelLimitMax: 'Maximum of {{arg}}', labelOrderSmall: 'Order too small (>= 100.5LRC)', labelEnterAmount: 'Enter amount', labelAgreeLoopringTxt: 'Allow Loopring to use Cookies.', labelLayer2HistoryTransactions: 'Transfer', labelLayer2HistoryTrades: 'Trades', labelLayer2HistoryAmmRecords: 'AMM Records', labelTxnDetailHeader: 'Transaction', labelDTxnDetailHeader: 'Deposit Record', labelWTxnDetailHeader: 'Withdraw Record', labelTTxnDetailHeader: 'Transfer Record', labelTxnDetailHash: '{{layer2}} Hash', labelTxnDetailHashLv1: 'Eth Hash', labelTxnDetailStatus: 'Status', labelTxnDetailTime: 'Time', labelTxnDetailFrom: 'From', labelTxnDetailTo: 'To', labelTxnDetailAmount: 'Amount', labelTxnDetailFee: 'Fee', labelTxnDetailMemo: 'Memo', labelTxnDetailProcessed: 'PROCESSED', labelTxnDetailProcessing: 'PROCESSING', labelTxnDetailFailed: 'FAILED', labelAgreeConfirm: 'Agree', labelDisAgreeConfirm: 'Disagree', labelImpactAgree: 'Type "AGREE" to confirm.', labelImpactTitle: 'Swap Requires Confirmation', labelPriceExtraGreat: 'The price you set is {{compare}} than 20% of the market price. Are you sure you want to complete this order?', labelPriceCompareGreat: 'Greater', labelPriceCompareLess: 'Less', labelImpactExtraGreat: 'Your transaction amount will affect the pool price by <1>{{value}}. Are you sure you want to swap?', labelCalculating: 'Calculating...', labelFeeCalculating: 'Calculating fee...', labelAmmMyTransactions: 'My Transactions', labelAmmAllTransactions: 'All Transactions', labelWaitForAuth: 'Waiting for signature', labelSignDenied: 'Signature request was rejected', labelFirstSignDenied: "Your wallet doesn't support this signature method", labelUpdateAccountSuccess: 'Congratulations!', labelUpdateAccountSuccess2: 'You have successfully sign in your {{loopringL2}} account!', labelResetAccountSuccess: 'Congratulations!', labelResetAccountSuccess2: 'You have successfully reset your {{loopringL2}} account keypair!', labelUpdateAccountSubmit: 'Activating Tx submitted.', labelUnlockAccountSuccess: 'Unlock successful!', labelUnlockAccountFailed: 'Unlock failed!', labelNotSupportTitle: 'Information', labelNotAllowTrade: 'Unfortunately we are unable to provide Order and AMM Deposit services due to your IP address as per our Terms of Use.', labelKnown: 'OK', labelResetAccount: 'Reset {{layer2}} Account', labelExportAccount: 'Export Account', labelExportAccountNoPhotos: 'No Photos', labelExportAccountDescription: 'Please keep your API key secure.', labelExportAccountCopy: 'Copy', labelExportAccountSuccess: 'Export Account successful!', labelExportAccountFailed: 'Export Account has failed!', // labelCreateAccountApproveWaitForAuth: 'Waiting for <1>{{symbol}} Approve...', labelCreateAccountApproveDenied: 'Signature request was rejected', labelAmmSwitch: 'switch', labelCreateAccountDepositDenied: 'Signature request was rejected', labelSlippageAlert: 'Your slippage tolerance is rather high which could result in less tokens received.', labelOrderGroup: 'Order Records', labelOrderTableOpenOrder: 'Open Order', labelOrderTableOrderHistory: 'Order History', labelResetLayout: 'Reset Layout', labelResetMobileLayout: 'Reset', labelBtnFix: 'reset', labelProSell: 'Sell', labelProBuy: 'Buy', labelProLimit: 'Limit', labelProMarket: 'Market', labelProPrice: 'Price', labelProBaseLabel: 'Amount', labelProQuoteLabel: 'Total', labelProLimitBtn: '{{tradeType}} {{symbol}}', labelProMarketBtn: '{{tradeType}} {{symbol}}', labelProOrderbook: 'Orderbook', labelProTrades: 'Trades', labelProToolbar24hChange: '24h Change', labelProToolbar24hHigh: '24h High', labelProToolbar24hLow: '24h Low', labelProToolbar24hBaseVol: '24h Volume({{symbol}})', labelProToolbar24hQuoteVol: '24h Volume({{symbol}})', labelErrorPricePrecisionLimit: '{{symbol}} price only allows {{decimal}} decimals.', labelDepthPrice: 'Price({{symbol}})', labelDepthAmount: 'Accum({{symbol}})', labelDepthTotal: 'Accumulation', labelProChartTitle: 'Chart', labelProTimeDefault: 'Time(1m)', labelProTime1m: '1m', labelProTime5m: '5m', labelProTime15m: '15m', labelProTime30m: '30m', labelProTime1H: '1H', labelProTime2H: '2H', labelProTime4H: '4H', labelProTime12H: '12H', labelProTime1D: '1D', labelProTime1W: '1W', labelProTime1M: '1M', labelProChartTradingView: 'Candlestick', labelProChartDepth: 'Depth', labelProOrderPrice: 'Order Price', labelProOrderTotalAmount: 'Accumulative Amount', labelSwapCancelled: 'Swap is cancelled.', labelSuccessfully: 'Success', labelWarning: 'Warning', labelFailure: 'Failure', labelPrompt: 'Prompt', // labelSwapCancelled: 'Swap is cancelled.', // labelWarning: 'Warning', // labelFailure: 'Failure', // labelPrompt: 'Prompt', labelComingSoon: 'Coming Soon', labelTradeProHideOtherPairs: 'Hide other trading pairs', labelCancelAllOrders: 'Confirm cancelling all orders?', labelConfirm: 'Confirm', labelSettingFee: 'Token Order for Fees', descriptionSettingFee: 'Change the token priority order to adjust which tokens will be used for fees first.', labelBtnEdit: 'Edit', labelSettingChargeFeeOrder: 'Token Order for Fees', labelDesSettingChargeFeeOrder: '{{loopringL2}} will use this token order when processing fees.', labelReset: 'Reset', labelQueryFeeOK: 'Save', depositLimit: 'Limit Orders \n Used to set the maximum or minimum price \n at which you are willing to buy or sell.', depositMarket: 'Market Orders \n Used to buy or sell immediately \n at the current market price.', labelTransactions: 'Transactions', labelMyRewards: 'My Rewards', labelClearAll: 'Clear All', labelProviderAgree: 'I have read, understand, and agree to the <1> Terms of Service .', labelNFTName: 'Name:', labelNFTDetail: 'Details', labelNFTTokenStandard: 'Token Standard:', labelNFTTokenMinted: 'Token Minted:', labelNFTDescription: 'Description:', labelNFTDate: 'Date:', labelNFTDeployContract: 'Deploy Contract', labelNFTSend: 'Send:', labelNFTDeploy: 'Deploy:', labelNFTDeploying: 'Deploying', labelNFTMyNFT: 'My NFTs - Collection: {{collection}}', labelNFTTokenID: 'ID:', labelNFTTYPE: 'Token Standard:', labelNFTRoyaltyPercentage: 'Royalty (%):', labelNFTID: 'ID:', labelNFTMinter: 'Minter:', labelNFTMetadata: 'Metadata:', labelNFTMint: 'Create NFT', labelNFTCreateCollection: '+ Create Collection', labelNFTTitleMyNFT: 'My NFTs', labelNFTTOTAL: 'Amount:', labelInformation: 'Notification', labelNoticeForProvider: 'Loopring currently supports the following wallet connections: {{name}}. Please make sure to use one of these when attempting to connect.', labelIKnow: 'OK', labelYes: 'Yes', labelNo: 'No', labelNoticeForNoMetaNFT: 'Your NFT does not contain Metadata or media information. \n Are you sure you still wish to {{ method }} this NFT?', labelAgreeConfirmNotShowAgain: 'I know & not show again', labelInvalidCID: 'Invalid CID. CIDv0 is start with `Qm`, CIDv1 only works for dag-pb', labelInvalidAddress: 'Invalid address, ENS', labelInvalidisCFAddress: 'Loopring Counterfactual wallet is disabled {{way}} {{token}}', labelInvalidisContract1XAddress: 'Loopring wallet 1.x is disabled {{way}} {{token}}', labelInvalidisContractAddress: '{{way}} of {{token}} to Contract wallet is not available', labelInvalidisLoopringAddress: 'This address does not yet have an active {{loopringL2}}, {{way}} of {{token}} is disabled!', labelInvalidisSameAddress: 'Cannot {{way}} to your own address.', labelTradeRaceRanking: 'Trading Leaderboard', labelTradeRaceYourRanking: 'Your ranking', labelTradeRaceGoTrading: 'Go to trade', labelTradeReadRule: 'Read Rules', labelTradeRaceRewards: 'Rewards', labelTradeRaceRules: 'Activity Rules', labelTradeRaceStart: 'Activity ends in:', labelTradeRaceReady: 'Activity starts in:', labelTradeRaceEnd: 'Activity has ended', labelDay: 'Days', labelHours: 'Hours', labelMinutes: 'Minutes', labelSeconds: 'Seconds', labelIsNotFeeToken: 'Please deposit {{symbols}}, or {{lastSymbol}} to activate your {{loopringL2}} account.', labelIsETHDepositAlert: 'Please reserve enough ETH in your account to pay for gas!', labelIsNotEnoughFeeToken: 'Please deposit enough token to cover the activation fee: {{fee}} {{symbol}}. Remaining token will appear in your asset after activation', depositNFTAddressLabelPlaceholder: 'please input NFT contract address...', mintNFTAddressLabelPlaceholder: '(CIDv0 or dag-pb CIDv1) eg: QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR', depositNFTIdLabelPlaceholder: 'please input NFT id...', nftDepositDescription: 'Creates a smart contract on {{ethereumL1}}, \n which requires a gas fee. NFTs minted \nhere remain on {{loopringL2}} until deployed.', labelNFTDescribe: 'Description:', labelNFTTitle: 'Amount', labelNFTDepositInputTitle: 'Amount:', labelNFTTId: 'NFT Token ID:', labelNFTCid: 'IPFS CIDv0 or dag-pb CIDv1:(Store Metadata Information)', labelNFTType: 'Token Standard:', labelNFTAccess: 'Allow Loopring to spend {{symbol}}', labelDeployDenied: 'Signature request was rejected', labelNFTTokenDeployWaitForAuth: 'Allow Loopring to deploy {{symbol}}?', labelDeployFailed: 'Deploy of {{symbol}} has failed!', labelDeploySubmit: 'Deploy of {{symbol}} has been submitted!', labelMint: 'Mint', labelMintDenied: 'Signature request was rejected', labelNFTTokenMintWaitForAuth: 'Allow Loopring to Create {{symbol}}?', labelMintFailed: 'Create of {{symbol}} has failed!', labelMintSuccess: 'Create of {{symbol}} has been submitted!', labelNFTMintBtn: 'Create NFT', labelNFTMintNoMetaBtn: 'Wrong Metadata', labelNFTMintNoMetaDetail: 'Your NFT metadata should identify <1>name, image, and royalty_percentage (integer from 0 to 10).', nftDeployDescription: 'Deploy Collection', nftDeployTitle: 'Deploy Contract', nftMintTitle: 'Create NFT', nftMintBtn: 'Create NFT', labelMintInProgress: 'Processing...', labelNFTDeployBtn: 'Deploy Contract', labelNFTDeployBroker: 'Deploy Broker:', labelDeployInProgress: 'Processing...', labelNFTDeployTitle: 'Deploy Contract', labelVendor: 'Buy with Card', labelLock: 'Lock', labelWalletToWallet: 'The connected wallet is a contract address which cannot be used (Except Recover Wallet). If you are connecting a mobile Loopring Smart Wallet, you can protect it and manage guardians within the app.', labelWalletAddAsGuardian: 'Add a guardian', labelWalletInputGuardianCode: 'Input 6 digital Code and Approve', labelWalletScanQRCode: 'Using Loopring Wallet, scan the QR code.', labelWalletInputGuardianCodeDes: 'Please contact the owner to obtain the approval code and enter it below.', labelWalletGuardianList: 'Guardian List', labelWalletRequestRecovery: 'Request for Wallet Recovery', labelWalletLoopringSmartWallet: 'The connected wallet is a Loopring Smart Wallet. Please use your Loopring Wallet mobile app to add Guardians.', labelWalletNonLoopringSmartWallet: 'The connected wallet is a non-Loopring smart contract wallet, which cannot be set as a Guardian. Please try again using a different wallet.', labelWalletGuardianHint: 'Easily add other Loopring Wallets as Guardians to secure your identity and crypto assets. After entering the wallet address, the user will receive a notification of the request directly in their Loopring Wallet app. Invite your friends and family to use the Loopring Wallet.', labelWalletLockTitle: 'Lock/unlock Wallet', labelWalletLockDes: 'Who I Protect', labelWalletValidationTitle: 'Approval Requests', labelWalletValidationDes: 'Guardian Request Handling', labelWalletHistoryTitle: 'View History', labelAddProtector: 'add Guardian', labelUnknown: 'Unknown', labelApprove: 'Approve', labelReject: 'Reject', labelWalletApprove: 'Approve Signature', labelCommonList: 'Waiting for your Approve List', labelLogList: 'Log List', labelWalletReject: 'Reject Signature', labelLockAccountSuccess: 'Lock Account Success', labelLockAccountFailed: 'Lock Account Failed', labelApproveSuccess: 'Approve Signature Success', labelApproveFailed: 'Approve Signature Failed', labelRejectSuccess: 'Reject Signature Success', labelRejectFailed: 'Reject Signature Failed', labelYourBalance: 'Your {{layer2}} have: {{balance}}', labelTxGuardianADD_GUARDIAN: 'ADD GUARDIAN', labelTxGuardianGUARDIAN_CONFIRM_ADDITION: 'GUARDIAN CONFIRM ADDITION', labelTxGuardianGUARDIAN_REJECT_ADDITION: 'GUARDIAN REJECT ADDITION', labelTxGuardianGUARDIAN_APPROVE: 'GUARDIAN APPROVE', labelTxGuardianAPPROVE_RECOVER: 'RECOVER WALLET', // RECOVER 16 labelTxGuardianAPPROVE_TRANSFER: 'OVER DAILY QUOTA TRANSFER', // APPROVE TRANSFER 18 labelTxGuardianAPPROVE_TOKEN_APPROVE: 'TOKEN ACCESS', // 23 labelTxGuardianADD_GUARDIAN_WA: 'ADD GUARDIAN', // 34 labelTxGuardianREMOVE_GUARDIAN_WA: 'REMOVE GUARDIAN', // 35 labelTxGuardianUNLOCK_WALLET_WA: 'UNLOCK WALLET', // 37 labelTxGuardianRESET_GUARDIANS_WA: 'RESET GUARDIANS', // 200 labelTxGuardianCONTACT_UPDATE_WA: 'RESET GUARDIANS', // 201 labelTxGuardianCALL_CONTRACT_WA: 'CALL CONTRACT', labelTxGuardian_recovery: 'recovery wallet', labelTxGuardian_transfer: 'over daily quota transfer', labelTxGuardian_add_guardian: 'add guardian', labelTxGuardian_remove_guardian: 'remove guardian', labelTxGuardian_unlock_wallet: 'unlock wallet', labelTxGuardian_deposit_wallet: 'deposit', labelTxGuardianApprove: 'APPROVE', labelTxGuardianReject: 'REJECT', labelReActiveAccount: 'Re-Activate Account', labelWalletSignType: 'Request for {{type}}', labelSpotTrading: 'Spot Trading Volume (30d in ETH)', labelTradeSpot: 'Trade Spot', labelBuyToken: 'Buy {{token}}', labelCurrentlyLevel: 'Currently {{value}} {{token}}', labelLRCBalance: 'LRC Balance', labelNoticeForForAccountFrozen: 'Your wallet’s L2 account is locked. While locked, you can’t perform any L2 operations. If you require further assistance, please send an email to support@loopring.io.', labelAction: 'action', labelGoExplore: 'View transactions on the <1>Loopring Block Explorer.', labelNOETH: 'Need ETH for gas', labelBanxaFeeFree: 'zero fees for a limited time', labellimit: 'limit', labelmarket: 'Market', labelswap: 'Swap', labelamm: 'Amm', labelActiveAccountTitle: 'Activate {{loopringL2}} Account', labelDepositTitle: 'Add Assets from My {{l1Symbol}}', labelDepositTitleAndActive: 'Add Asset from My {{l1Symbol}} & Activate', labelDepositAndActiveBtn: 'Activate {{loopringL2}}', labelDepositTitleActive: 'Activate {{loopringL2}}', depositLabelBtn: 'Receive', labelL2ToL1Title: 'Send to {{l1Symbol}}', labelL2ToMyL1Title: 'Send to My {{l1Symbol}}', labelL2ToOtherL1Title: 'Send to Another {{l1Symbol}}', labelL2ToL1DeployTitle: 'Deploy & Send to {{l1Symbol}}', labelL2toL1EnterToken: 'Select Token', labelSendL1Btn: 'Send', labelSendL1DeployBtn: 'Deploy & Send', labelL2toL1BtnExceed: 'Exceed Max Fast Withdraw amount: {{arg}}!', labelL2toL1BtnExceedWithFee: 'Insufficient balance (with fee)', labelL2toL1Address: '{{l1ChainName}} Address', labelL2toL1MyAddress: 'To my {{l1Symbol}}', labelL2toL1AddressInput: 'Please input the address', labelL2toL1Fee: 'Select payment token', labelL2toL1Fast: 'Fast', labelL2toL1Standard: 'Standard', labelL2toL1LinkRecent: 'Recent withdrawal history', labelL2toL2Title: 'Send to Another {{loopringL2}}', labelL2toL2EnterToken: 'Select Token', transferDescription: 'Send assets to any valid {{l1ChainName}} address instantly.\n Please make sure the recipient address accepts \n {{loopringL2}} payments before you proceed.', labelL2toL2Btn: 'Send', labelL2toL2Address: 'Recipient', labelL2toL2AddressInput: 'Please input address / ENS / Account ID', labelL2toL2Memo: 'Memo (Optional)', labelL2toL2MemoPlaceholder: 'Please input the memo', labelL2toL2FeeChoose: 'Select payment token', labelL2toL2Fee: 'Network Fee', labelL2toL2FeeNotEnough: 'Insufficient balance', labelL2toL2FeeFastNotAllowEnough: 'Please choose Standard!', labelL2toL2LinkRecent: 'Recent send history', labelL2toL2ExchangeError: 'Sending to an Exchange Address {{l2symbol}} account is not supported. {{loopringL2}} accounts cannot be activated on Exchange wallet addresses. Instead, please send to the {{l1Symbol}} account associated with this address.', labelL2toL2SmartWalletError: 'This wallet binds with smart contract that does not support {{loopringLayer2}}. You will need to send funds to the {{l1Symbol}} account. ', labelActiveLayer2: 'Activate {{loopringL2}}', labelAddAsset: 'Receive', labelAddAssetBtn: 'Receive', labelSendAsset: 'Send', labelSendAssetBtn: 'Send', labelSend: 'Send', labelReceive: 'Receive', labelWaitingRefer: 'Waiting for approval', labelL1toL2WaitForAuth: 'Please confirm to receive {{value}} {{symbol}} to {{to}} {{loopringL2}}.', labelL1toL2Denied: 'You rejected to receive {{value}} {{symbol}}.', labelL1toL2Failed: 'Add asset request of {{value}} {{symbol}} failed!', labelL1toL2Submit: 'Add asset request has been submitted. <1>', labelL1toL2NeedApprove: 'Allow Loopring Exchange to spend {{symbol}}', labelL2toL1InProgress: 'Processing...', labelL2toL1Failed: 'Sent {{value}} {{symbol}} to {{l1Symbol}} has failed!', labelL2toL1Success: 'Sent {{value}} {{symbol}} to {{l1Symbol}} was successful!', labelL2toL2InProgress: 'Processing...', labelL2toL2Failed: 'Sent {{value}} {{symbol}} from my {{loopringL2}} to another {{loopringL2}} failed!', labelL2toL2Success: 'Sent {{value}} {{symbol}} was successful!', labelUpdateAccountFailed: 'Activate {{loopringL2}} has failed!', labelCreateAccountSubmit: "Activation of {{loopringL2}} with deposit of {{value}} {{symbol}} has been submitted! \n Approximately {{count}} minutes remaining...',", labelCreateAccountFailed: 'Activation of {{loopringL2}} with deposit of {{value}} {{symbol}} has failed!', labelL1toL2Hash: 'Recent transactions (From my {{l1Symbol}} to my {{l2Symbol}})', labelL1toL2HashEmpty: 'My {{l1Symbol}} \u2192 {{loopringL2}} transactions will show up here.', labelL1toL2Record: 'Receive {{value}} {{symbol}}', labelNFTSendL2Btn: 'To Another {{loopringL2}}', labelNFTSendMyL1Btn: 'To My {{l1Symbol}}', labelNFTSendOtherL1Btn: 'To Other {{l1Symbol}}', labelNFTDeploySendMyL1: 'To My {{l1Symbol}} & Deploy Contract', labelNFTDeploySendAnotherL1: 'To another {{l1Symbol}} & Deploy Contract', labelGuid: 'Go to Guide', labelOK: 'Ok', labelL2toL2InvalidAddr: 'Invalid address or ENS', labelL2toL2AddressNotLoopring: '<0> This address does not have an activated {{loopringL2}}. Please ensure the recipient can access {{loopringL2}} before sending.', labelL2toL2AddressType: 'Address Type', labelL2toL2OriginDesc: 'Please select the address source. Note: the following trading platforms currently do not support {{loopringL2}} transfers (Binance, Huobi, Okex…)', labelL2toL2OriginBtnExchange: 'Exchange', labelL2toL2OriginBtnWallet: 'Wallet', labelL2toL2Confirm: 'Confirm', labelL2toL2TokenAmount: 'Token Amount', labelActiveAccountFeeNotEnough: 'Insufficient balance <1>Add assets', labelNFTTransferTX: '{{l2Symbol}} \u2192 {{l2Symbol}}', labelNFTWithdrawTX: '{{l2Symbol}} \u2192 {{l1Symbol}}', labelNFTDepositTX: '{{l1Symbol}} \u2192 {{l2Symbol}}', labelNFTDeposit: 'Receive {{loopringL2}} NFT', labelNFTDepositNeedApprove: 'Allow Loopring to spend {{symbol}} and deposit it?', labelNFTDepositBtn: 'Receive NFT', labelNFTDepositTitle: 'Receive NFT from my {{l1Symbol}}', labelNFTContractAddress: 'Contract:', labelNFTAmount: 'Amount:', labelNFTTokenDepositWaitForAuth: 'Please confirm to send {{loopringL2}} {{symbol}}', nftMintDescription: 'Paste in the CID that you obtained from uploading \n the metadata.json file (point 11 above) - if successful,\n the data from the metadata.json file you created contained\n within the folder will populate the Name\n and Image below.', labelNFTMintInputTitle: 'Amount <1>\uFE61', labelL1toL2Vendor: 'Use a Loopring partner to deposit funds.\nOnce your order is confirmed by Loopring,\n it will be added to your balance within 2 minutes.', depositLabelTo: 'To address, Account ID or ENS.', labelAddressNotLoopring: "Account doesn't have an active {{loopringL2}}", labelMINTNFTTitle: 'Create NFT (ERC1155)', labelIPFSUploadTitle: 'Preview Image (Dimensions: 1:1) <1>\uFE61<2>\u2139', labelIPFSUploadTooltips: 'The file uploaded here will be used as the cover image when displaying NFT item.', labelIPFSUploadMediaTitle: 'Multimedia Content (image, audio, video and 3D)<1>\u2139', labelIPFSUploadMediaTooltips: 'If no file is uploaded here, it will use the same content as “Preview Image”.', labelLoadDes: 'Drag or click to upload files ({{types}}, max size: {{size}}MB)', labelUpload: 'upload', labelMintNoImageBtn: 'Please upload image', labelMintUserAgree: 'Please agree to the terms of service', labelMintTradeValueBtn: 'Please input amount(1 - 10,000)', labelMintNoRoyaltyPercentageBtn: 'Please input Royalty', labelMintWrongRoyaltyBtn: 'Royalty should be (0 - 10)', labelMintNoNameBtn: 'Please input name', labelNFTMetaBtn: 'Upload metadata & create', labelMintName: 'Name <1>\uFE61', labelMintCollection: 'Choose Collection <1>{{required}}<2>', labelMintCollectionTooltips: 'This is the collection where your NFT will appear.', labelMintRoyaltyPercentage: 'Royalty (%) <1>\uFE61<2>\u2139', labelMintRoyaltyPercentageRange: 'Max Int:', labelMintRoyaltyPercentageTooltips: 'Represents the percentage to be received from each subsequent resale (max 10%).', labelMintDescription: 'Description <1>\u2139', labelMintDescriptionTooltips: "The description will be included on the NFT's detail page beneath it's image.", labelMintProperty: 'Properties (Limit 64) <1>\u2139', labelMintPropertyTooltips: 'Tags can be added to the NFT for easy searchability and distinction', labelPropertyAdd: 'Add property', labelMintNFT: 'Create NFT', labelL1toL2NFT: 'Receive NFT', labelMyAssetsNFT: 'My NFTs', labelTransactionNFT: 'Transactions', labelMintPropertyKey: 'Key', labelMintPropertyValue: 'Value', labelNFTProperty: 'Properties:', labelConfirmMint: 'Confirm Metadata', labelUseIpfsMintAgree: 'I confirm that the NFT minted does not infringe on copyright laws or contain illegal, explicit, sensitive, adult themed, or any other content considered NSFW. We reserve the right to hide inappropriate content if an NFT is discovered to be harmful.', labelL1toL2TitleBridge: 'Add {{loopringL2}} Assets', labelL1toL2TitleBridgeNoConnect: 'Connect your {{ethereumL1}} Wallet to transfer assets to any {{loopringL2}} account', labelPayer: 'My Wallet:', labelL1toL2TokenAmount: 'Token Amount', labelL1toL2From: 'From', labelL1toL2TO: 'To {{loopringL2}}', labelAddAssetTitle: 'Add {{loopringL2}} {{symbol}} assets', labelSendAssetTitle: 'Send {{loopringL2}} {{symbol}} assets', labelAddAssetHowto: 'How would you like to add {{loopringL2}} assets?', labelAddAssetTitleActive: 'Add assets & Activate', labelFromMyL1: 'From my {{l1Symbol}} account', labelFromOtherL1: 'From another {{l1Symbol}} account', labelBuyWithCard: 'On-Ramp From Fiat', labelFromOtherL2: 'From another {{loopringL2}} account', labelFromExchange: 'From an exchange', labelOpenInWalletApp: 'Open in wallet app/extension', labelConnectWithDapp: 'Connect with Dapp', labelOpenInWalletTitle: 'Open in wallet', labelOpenInWalletDetail: `URL for adding funds has been copied. You can choose either way to continue:`, labelOpenInWalletDetailLi1: `Open your wallet app and paste the URL into its internal Dapp browser`, labelOpenInWalletDetailLi2: `Open your desktop Chrome browser and paste the URL in Chrome`, labelActiveL2Btn: 'Activate {{loopringL2}}', labelWrongNetworkGuideTitle: 'Wrong Network', labelWrongNetworkGuide: 'Your chosen network is not currently supported on Loopring. Please choose {{l1ChainName}} main Network or test Network Goerli', labelSenAssetTitle: 'Send {{symbol}} from {{loopringL2}}', labelSendTOL2: 'To another {{loopringL2}} account', labelSendToMyL1: 'To my {{l1Symbol}} account', labelSendToOtherL1: 'To another {{l1Symbol}} account \n(incl. exchange)', labelSendAssetHowto: 'Where would you like to send your crypto to', labelL1toL2: 'Add {{loopringL2}} assets From My {{l1Symbol}}', labelActivatedAccountChargeFeeList: 'Please make sure one of the below tokens with the minimum quantity in your {{loopringL2}} account to proceed', labelReceiveAddress: 'Receive Address', labelAssets: '{{loopringL2}} Assets', labelReceiveAddressGuide: 'Please use a {{loopringL2}} account when transferring to avoid loss of assets ({{symbol}}).', labelL2toL2: 'Send to another {{loopringL2}}', labelL2toL1: 'Send to {{l1Symbol}}', labelBenefitL2: "As {{l1ChainName}}'s first ever zkRollup, {{loopringL2}} allows you to avoid costly gas fees and network congestion with the same security as mainnet - 100x cheaper and faster.\n\nActivating your {{loopringL2}} account requires a small payment fee. ", labelNotBalancePayForActive: 'Insufficient balance in your {{loopringL2}} account', labelEnoughBalancePayForActive: 'You have enough balance to pay for {{loopringL2}} creation.', labelHaveInProcessingL1toL2: 'If you have already started the deposit, please be patient and recheck as transactions on {{l1ChainName}} can take up to 30 minutes.', labelWaitingL1toL2: 'Please wait', labelAddAssetGateBtn: 'Add assets', labelActiveLayer2Btn: 'Activate {{loopringL2}}', labelActiveLayer2PayBtn: 'Pay Activation Fee', labelBalanceActiveAccountFee: '{{symbol}}: <2>Fee {{fee}};<3>My {{loopringL2}} balance: {{count}}', labelToAddressShouldLoopring: 'To address is no {{loopringL2}}', labelBridgeSendTo: 'Send to (address, Account ID or ENS)', labelInvalidAddressClick: 'Invalid Wallet Address, {{way}} of {{token}} is disabled! <1>Click to input another receive address ', labelENSShouldConnect: 'Receive address is an ENS, please connect wallet to check real address', labelToken: 'Token', labelMinRequirement: 'Min Requirement', labelAvailability: 'Availability', labelWhatProvider: 'Which provider would you like to use?', labelMemo: 'Memo', labelAdvanceMint: 'Advance Create NFT', labelWalletTypeDes: 'Please confirm the address type again to ensure the assets are not mistakenly sent to the exchange address. ', labelWalletTypeOptions: '{{type}} Wallet', labelWalletTypeOtherSmart: 'Other Smart', labelWalletTypeLoopring: 'Loopring', labelWalletTypeEOA: 'EOA', labelWalletTypeExchange: 'Exchange', labelEOADes: 'There is no smart contract binds with this wallet address. (e.g. MetaMask, imtoken, Ledger, Trezor, etc....) ', labelLoopringDes: 'This wallet is created using Loopring Wallet mobile app and binds with Loopring smart contract.', labelOtherSmartDes: 'This wallet binds with smart contract that does not support {{loopringLayer2}}. You will need to send funds to the {{l1Symbol}} account. ', labelExchangeDes: 'The following trading platforms currently do not support {{loopringL2}} transfers (Binance, Coinbase, etc...). You will need to send funds to the {{l1Symbol}} account. ', labelExchangeTypeDes: 'Please select the address source:', labelNonExchangeTypeDes: 'eg: Loopring Wallet, Metamask, Coinbase Wallet, imtoken, Ledger, Trezor... EOA wallet', labelNonExchangeType: 'Non-Exchange Wallet', labelExchangeType: 'Exchange', labelExchangeBinance: 'Binance', labelExchangeBinanceDes: '', labelExchangeHuobi: 'Huobi', labelExchangeHuobiDes: 'Transactions need to wait 24 hours', labelExchangeCoinbase: 'Coinbase', labelExchangeOthers: 'Other Exchanges', labelExchangeOthersDes: '', labelL2toL1AddressType: 'Address Type', labelConfirmBtrade: 'Confirm CEX Support', labelConfirmDetail: '<0>Before withdrawing, please confirm with your CEX support that they accept deposits from smart contracts.' + '<1>{{l2Symbol}} to {{l1Symbol}} withdrawing is performed via a smart contract. The CEX depositing address may not be able to automatically acknowledge the deposit.' + '<2>If the deposit does not appear at the CEX address within 24 hours, please contact your CEX support and ask they manually acknowledge the transaction.', labelBtradeUnderstand: 'I understand and acknowledge the risk', labelMintFee: 'Create Fee', labelMintFeeNotEnough: 'Insufficient balance', labelMintFeeChoose: 'Select payment token', labelLayerSwapUnderstand: 'I understand and acknowledge the risk', labelIUnderStand: 'I Understand', labelLayerSwapUnderstandDes: 'LayerSwap is a 3rd party App service provider to help move tokens from exchange to {{loopringL2}} directly. If you have any concerns regarding their service, please check out their <1>TOS.', labelInvestAmmTitle: 'AMM Pools', labelInvestBalanceTitle: 'My Investments', labelInvestDualRefreshErrorTitle: 'Subscription Failed', labelInvestDualRefreshError: 'The subscription of {{token1}}/{{token2}} Dual Investment failed.', labelTransactionsLink: 'Transactions', labelAMMTransactionsLink: 'View Pool Transactions', labelNFTMintWrongCIDBtn: 'Wrong MetaData format', labelWithdrawBtn: 'Withdraw', labelFWithdrawFee: 'Fee', labelFWithdrawNotEnough: 'Insufficient balance', labelForceWithdrawTitle: 'Force Withdraw', labelForceWithdrawWaitForAuth: 'Please confirm to force withdraw {{symbol}}', labelForceWithdrawDenied: 'You rejected to force withdraw {{symbol}}.', labelForceWithdrawInProgress: 'Processing...', labelForceWithdrawFailed: 'Force withdraw has failed!', labelForceWithdrawSubmit: 'Force withdraw has been submitted', labelForceWithdrawToken: 'Token Amount', labelForceWithdrawFee: 'Network Fee', labelForceWithdrawEnterToken: 'Select Token', labelPleaseForceWithdrawAddress: 'Please enter the address you wish to withdraw from', labelForceWithdrawAddress: 'The address you wish to withdraw from', labelForceWithdrawDes: "If the recipient doesn't have an active {{loopringL2}} account, you will be able to withdraw the token from {{l2Symbol}} to {{l1ChainName}} {{l1Symbol}}. This process is usually only needed when tokens were sent to a CEX address using {{loopringL2}}. Since the CEX does not have access to the {{l2Symbol}} account, you will need to perform this action to reclaim the tokens.", labelForceWithdrawConfirm: 'This feature allows a user to move their {{l2Symbol}} tokens to the {{l1Symbol}} address. The target address must either be a wallet or exchange address', labelForceWithdrawConfirm1: 'This operation usually requires more than 30 minutes to take effect, as it needs to interact with {{l1ChainName}} Mainnet. Please be patient.', labelNFTSendBtn: 'Send', labelNFTProperties: 'Properties', labelNFTDescription2: 'Description', labelForceWithdrawNotAvailable: '{{loopringL2}} account is activated in this address. For security reason, Loopring would not allow other user to force withdraw token from its {{l2Symbol}} to {{l1symbol}} anymore', labelForceWithdrawNoToken: 'No token is detected from this address to operate', labelForceWithdrawBtn: 'Force Withdraw', labelInvestDefiTitle: 'ETH Staking', labelInvestDefDeposit: 'Subscribe', labelInvestDefWithdraw: 'Redeem', labelNFTDepositLabel: 'Receive NFT', labelDefiFee: 'Fee', labelDefiMin: 'Minimum of {{arg}}', labelDefiNoEnough: 'Insufficient balance', labelDefiMaxBalance: 'It is not possible for the Loopring pool to fulfil your complete request at the moment. You can only redeem {{maxValue}} now.\n' + 'You can choose one of the following approaches for the remaining amount:', labelDefiMaxBalance1: '

  • Withdraw {{symbol}} to {{l1Symbol}} and trade through 1Inch or {{type}}, etc...
  • ' + '
  • The Loopring pool will rebalance soon. Please come back later to redeem.
  • ', labelDefiNoBalance: 'It is not possible for the Loopring pool to fulfil your complete request at the moment.' + 'You can choose one of the following approaches for the remaining amount:', labelDefiNoBalanceList: '
  • Withdraw {{symbol}} to {{l1Symbol}} and trade through 1Inch or {{type}}, etc...
  • ' + '
  • The Loopring pool will rebalance soon. Please come back later to redeem.
  • ', labelDefiMaxBalanceJoin: "The quota is almost sold out and can't fulfil your complete order. You can only subscribe {{maxValue}} now. Loopring will setup the pool soon, please revisit for subscription later. ", labelDefiNoBalanceJoin: 'Loopring will set up the pool soon. Please come back later to subscribe.', labelInvestBtn: 'Subscribe', labelRedeemBtn: 'Redeem', labelVipTitle: 'VIP', labelSecurity: 'Security', labelFeeTitleList: 'Fee', labelInvestOverviewTitle: 'Overview', labelTitleOverviewToken: 'Total Investment Tokens', labelInvestType_AMM: 'AMM Pools', labelInvestType_STAKE: 'ETH Staking', labelInvestType_DUAL: 'Dual Investment', labelInvestType_STAKELRC: 'LRC Staking', labelInvestAll: 'Mixed', labelInvestFlexible: 'Flexible', labelInvestDuration: 'Duration', labelDefiOrderTable: 'ETH Staking', labelTitleMyInvestAvailable: 'My Holding Tokens', labelViewMore: 'View more', labelInvestSuccess: 'Successfully {{type}} {{symbol}}', labelInvestFailed: 'Subscribe Failed', labelWSETHDefiRiskTitle: 'What is ETH Staking via Lido?', labelRETHDefiRiskTitle: 'What is ETH Staking via Rocket Pool?', labelWSETHDefiRisk: '

    Lido is a liquid staking solution for ETH 2.0 backed by industry-leading staking providers. Lido lets users stake their ETH - without locking assets or maintaining infrastructure.

    ' + '

    When using Lido to stake your ETH on the {{l1ChainName}} beacon chain, users will receive a token (stETH), which represents their ETH on the {{l1ChainName}} beacon chain on a 1:1 basis. It effectively acts as a bridge bringing ETH 2.0’s staking rewards to ETH 1.0.

    ' + "

    wstETH is the wrapped version of stETH. The total amount of wstETH doesn't change after users receive the token. Instead, the token’s value increase over time to reflect ETH staking rewards earned.

    ", labelRETHDefiRisk: '

    Rocket Pool is the first truly decentralized {{l1ChainName}} staking pool. Rocket Pool’s liquid staking token allows anyone to earn staking rewards easily without running staking software or locking assets. Rocket Pool handles all of the {{l1ChainName}} validator operations with smart contracts on the Execution layer.

    ' + "

    Acquiring and holding rETH in your wallet means that you are staking ETH. rETH's value continuously increases relative to ETH, indicating the daily stake reward received.

    " + '

    ', labelWSETHDefiRisk2: "<0>It is important to note that users can't redeem wstETH for ETH until phase 2 of {{l1ChainName}} 2.0. However, users are able to trade wstETH for ETH on various exchanges at market prices.

    " + '<1>Loopring will provide a pool to allow users to trade wstETH for ETH directly on {{layer2}}. The pool will rebalance periodically when it reaches a specific threshold. If there is not enough inventory on {{layer2}}, user can always withdraw their wstETH tokens to Layer 1 and swap for ETH in Lido, Curve, or 1inch.

    ', labelRETHDefiRisk2: '<0>Loopring will provide a pool to allow users to trade rETH for ETH directly on {{layer2}}. The pool will rebalance periodically when it reaches a specific threshold. If there is not enough inventory on {{layer2}}, users can always withdraw their rETH tokens to Layer 1 and swap for ETH in Rocket Pool, 1Inch, etc… ' + '<1>', labelDefiAgree: 'I have read and understand the risk warning.', labelDefiInvest: 'Defi Earn', labelLRCStakingInvest: 'LRC staking', labelLRCStakingRedeemInvest: 'LRC staking Redeem', labelDefiClose: 'This service is temporarily unavailable as we set up the pool. This process may take several hours to complete. Thank you for your patience!', labelCreateCollection: 'Create Collection', labelCollectionCreateName: 'Contract address for your collection', labelCollectionCreateERC1155: 'Collection ERC-1155', labelCollectionCreateWaiting: 'Waiting for create Collection token Address', labelMintSelect: 'Choose Creation Method', labelMintSelectDes: 'Choose the most suitable approach for your needs.', labelCollectionCreateFailed: 'Create Collection token Address Failed', labelCollectionMetaTitle: 'Import Metadata from IPFS', labelAdvanceCreateCollection: 'Advance Create Collection', labelCreateCollectionSuccess: 'Collection create was successful', labelCreateCollectionFailed: 'Collection create has failed', labelCollectionAdvanceJSON: 'NFT Collection information follow this format: ', labelCopyDemo: 'Click to copy the demo', labelCollectionCreatBtn: 'Create Collection', labelEnterMeta: 'Enter Collection Metadata', labelMintGuid: 'Fill up content in GUI and let Loopring to generate necessary metadata and upload to IPFS for you, then use "Mint" to create your NFT.', labelAdMintGuid: 'Generate all the required metadata and upload to IPFS by yourself first, then use "Advanced Create NFT" to create your NFT.', labelFilterTradeNFTSell: 'Sell', labelFilterTradeNFTSelf: 'Self Trade', labelFilterTradeNFTBuy: 'Buy', labelAdMintTitle: 'Advance Create NFT', labelCopyNFTDemo: 'Copy NFT Demo', labelSelectCollection: 'Choose or Create a Collection to Create Your Own NFT', labelSelectCollectionDes: 'A NFT Collection can help you manage and group your NFTs', labelChooseCollectionBtn: 'Choose a Collection to Create NFT', labelNFTMint721Btn: 'ERC721 will coming soon', labelADMint1: 'Prepare NFT metadata', labelADMint2: 'Fill in the IPFS CID', labelADMint3: 'Preview & Create NFT', labelADMintSelect: 'Prepare NFT metadata with proper collection_metadata value', labelHasData: 'Has generated metadata with collection_metadata field', labelNoData: 'Hasn’t generated metadata with collection_metadata field', labelChooseCollection: 'Choose a collection', labelBanner: 'Banner (Dimensions: 3:1)', labelBannerDes: 'Drag or click to upload files ({{types}}, max size: {{size}}MB)', labelAvatar: 'Avatar (Dimensions: 1:1)', labelAvatarDes: 'Max size: {{size}}MB)', labelTileUri: 'Tile (Dimensions: 5:7) <1>\uFE61', labelTileUriDes: 'Drag or click to upload files ({{types}}, max size: {{size}}MB)', labelCollectionDescription: 'Description <1>\u2139', labelCollectionDescriptionTooltips: 'You can describe your collection here. 0 of 1000 characters used', labelCollectionName: 'Collection Name <1>\uFE61', labelCollectionCreateBtn: 'Create Collection', labelCollectionRequiredName: 'Please input Name', labelCollectionRequiredTileUri: 'Please input tile', labelCollectionIsUploading: 'Source is loading', labelMintNext: 'Next', labelMintCollectionInput: 'Please input contract address', labelMintCid: 'Please input IPFS CID', labelMintBack: 'Back', labelTokenAdMintBtn: 'Enter Amount', labelMintSubmitBtn: 'Create Your NFT', labelMintIPFSCIDDes: 'Fill in the IPFS CID for NFT metadata', labelNFTMintSimpleBtn: 'Create NFT', labelCollectionEditBtn: 'Edit Collection', labelCopyMetaClip: 'Metadata Copied to Clipboard', labelCollectionMetaNoNameORTileUri: 'Your Collection metadata is not setup {{type}}, please go to collection panel edit!', labelCollectionMetaMiss: 'Your NFT metadata is no not setup {{type}}.', labelCollectionMetaError: 'Your NFT metadata is no not setup {{type}}, please check and fix it from your IPFS site', labelCollectionMetaErrorType: 'correct `royalty_percentage` from 0 to 10', labelNFTServerRefresh: "Click to refresh the NFT's metadata. This process usually takes around 30 minutes.", labelNFTServerRefreshSubmit: 'Refresh command submitted', labelNFTCollection: 'Collection', labelNFTCollectionName: 'Collection Name:', labelMyCollection: 'My Collections', labelCounterFactualNFT: '{{l2Symbol}} NFT:', labelCopyUrlClip: 'URL Copied to Clipboard!', labelCollectionMetaData: 'Collection MetaData', labelViewEtherscan: 'Etherscan', labelNFTMyNFTCollection: 'View by Collection', labelNFTMyNFTList: 'View by item', labelNoCollectionCover: 'No Cover Media', labelNoNFTCover: 'No Media Resource', labelNFTAmountValue: 'Amount: {{value}}', labelNFTAmountSimpleValue: ' \u2A09 {{value}}', labelCollectionItemValue: 'Item: {{value}}', labelCollectionItemSimpleValue: ' \u2A09 {{value}}', labelMyCollectionsDes: "Legacy NFTs created in Loopring don't contain collection information. We have added the feature to allow creators to import the collection information so that those NFTs can be categorized well. <1>Go to Import Collection for Legacy NFT", labelNFTGuid: 'Please fill in the appropriate collection metadata field value in your NFT metadata with this string first, then upload it to IPFS to retrieve the CID to continue. <1>view more ', labelChooseCollectionTooltips: 'This is the collection where your NFT will appear. \n Note: NFT minted under collection will be bound with different contract address than previous created one. If you have incomplete work to finish and would like them created under previous contract address, you can still use the legacy creation method under <1>https://legacy-nft.loopring.io/.', labelMintPreview: 'Back', labelMintNoCollectionBtn: 'Please Choose Collection', labelInvestDualTitle: 'Dual Investment', labelBuy: 'Buy', labelSell: 'Sell', labelRampNoBalance: 'Insufficient {{belong}} balance', labelBanxaNoBalance: 'Insufficient {{belong}} balance', labelBanxaFeeNoBalance: 'Insufficient {{belong}} balance & fee', labelL2toRampTitle: 'Send to Ramp', labelL2toBanxaTitle: 'Send to Banxa', labelDualInvest: 'Invest {{symbol}}', labelDualBase: 'Sell High for {{symbol}}', labelDualQuote: 'Buy {{symbol}} Low', labelDualAgree: 'I have read and understand the risk warning.', labelDualRiskTitle: 'Dual Investment', labelDualInvestBaseTitle: 'Invest {{symbolA}} (Sell High for {{symbolB}})', labelDualInvestQuoteTitle: 'Invest {{symbolA}} (Buy {{symbolB}} Low)', labelDualInvestDes: 'Invest {{symbolA}} to earn more {{symbolA}} or {{symbolB}}', labelDualCurrentPriceTip: 'Current Price is based on {{symbol}} derived from some leading exchanges.', labelDualCurrentPrice: '{{symbol}} Current Price:<1>{{price}} {{baseSymbol}}', labelDualCurrentPrice2: 'Current Price:\n <1>{{price}} {{baseSymbol}}', labelDualSuccess: 'Subscription {{symbol}} Successfully', labelDualProcessing: 'Waiting for completion', labelDualProcessingDes: 'We will try to fulfill your subscription request within minutes. If your subscription cannot be fully completed within the time frame, the unfilled portion will be unlocked. You can return later to resubscribe.', labelDualFailed: 'Subscribe Failed', labelDualFee: 'Fee', labelDualMin: 'Minimum of {{arg}}', labelDualMax: 'Maximum of {{arg}}', labelDualNoEnough: 'Insufficient balance', labelDualSettleDate: 'Settlement Date', labelDualSubDate: 'Subscription Date', // labelDualCurrentPrice2: '{{symbol}} Current Price', // labelDualCurrentPrice3: '{{symbol}} current Price', labelDualCurrentAPR: 'APR <1>\u2139', labelDualCurrentAPRDes: 'APR is refreshed in real time. We will use the lastest APR at the time you complete the subscription successfully.', labelDualTargetPrice2: 'Target Price <1>\u2139', labelDualTargetPrice3: 'Target Price', labelDualTargetPriceDes: 'Target Price is a benchmark price based on USDT. On Settlement Date, the Settlement Price will be compared against this benchmark price.', labelDualRiskDes: 'Your investment will be locked up until settlement date after investing and cannot be redeemed before settlement. \n As we make profit ratio a top priority, the total opened position might vary with your initial investment.', labelDualReturn: 'Return \n {{symbol}}', labelDualReceive: 'Settlement Calculator', labelDualCalcLabel: 'If {{symbol}} {{tag}} {{target}}', labelDualReturnValue: 'Return {{value}} {{symbol}}', labelDualQuota: 'Total Quota', labelProduct: 'Product', labelDualAssetFrozen_Target: 'Invest Target', labelDualAssetPrice: 'Price', labelDualAssetSettlement_Date: 'SettlementDate', labelDualAssetAPR: 'APR', labelDualAssetAction: 'Detail', labelInvestDualTutorial: 'Tutorial', labelInvestDualTutorialContent: 'Dual Investment offers you a chance to sell cryptocurrency high or buy cryptocurrency low at your desired price on your desired date. Once subscribed, users are not able to cancel or redeem the subscription until the Settlement Date.\n You may be better off holding your cryptocurrency, and may be required to trade your cryptocurrency at a less favorable rate of exchange than the market rate on Settlement Date. Cryptocurrency trading is subject to high market risk. Please make your trades cautiously. There may be no recourse for any losses.', labelInvestDualTutorialCheck1: 'I understand that Dual Investment is NOT a principal-guaranteed products.', labelInvestDualTutorialCheck2: 'I understand that subscribed assets are locked and users aren’t able to cancel or redeem before the Settlement Date.', labelInvestDualTutorialCheck3: 'I understand that I should review the possible scenarios of settlement amount and confirmed the subscription details.', labelInvestDualTutorialCheck4: 'Please be aware that the target price in Dual Investment portfolio is USDT. If you subscribe USDC-related product with another token, that token may be converted to USDC if the target price is reached. If you want to completely avoid the USDC depegging risk, you can select USDT-related products instead.', labelInvestDualTutorialCheck5: 'I have read and understand the risk warning.', labelInvestDualBeginerMode: 'Beginner Mode', labelInvestDualBeginerModeDesLine1: 'What is Dual Investment?', labelInvestDualBeginerModeDesLine2: 'You can use the beginner mode to quickly learn.', labelDualAmount: 'Amount', labelDuaInvestmentDetails: 'Dual Investment Details', labelDualOrderTable: 'Dual Investments', labelDualBeginnerPriceSmallerThan: 'if Index Price < {{value}}', labelDualBeginnerPriceSmallerThanOrEqual: 'if Index Price ≤ {{value}}', labelDualBeginnerPriceGreaterThan: 'if Index Price > {{value}}', labelDualBeginnerPriceGreaterThanOrEqual: 'if Index Price ≥ {{value}}', labelDualBeginnerAtSettlementDay: 'At Settlement Date', labelDualBeginnerIndexPriceDes: 'Index Price is derived from some leading exchanges.', labelDualBeginnerLockingDes: 'Your token for investment will be locked until Settlement Date.', labelDualBeginnerAPR: 'APR: {{APR}}', labelDualBeginnerStep1Title: 'Step 1: Choose a token to sell or buy', labelDualBeginnerStep2Title: 'Step 2: Choose to sell or buy at desired price in the future', labelDualBeginnerSellHigh: 'Sell {{token}} High', labelDualBeginnerBuyLow: 'Buy {{token}} Low', labelDualBeginnerReceiveStable: 'You will receive {{list}} {{last}}', labelDualBeginnerInvestStable: 'You can invest {{list}} {{last}}', labelDualBeginnerLast: 'or {{last}}', labelDualBeginnerStep3Title: 'Step 3: Choose Target Price and Settlement Date', labelDualBeginnerSellHighFor: 'Sell high for {{token}}', labelDualBeginnerBuyLowWith: 'Buy low with {{token}}', labelInvestMyAmm: 'My Investments', labelInvestMyDual: 'My Investments', labelInvestMyDefi: 'My Investments', labelInvestMaxDual: 'Max {{value}}', labelDualTitle: 'Dual Investment', labelDualDesSuccess: 'Your token for investment is just locked but still in your account as Loopring is a DEX. \n When the transaction expires, if the settlement price is not reached, you will get a profit and the frozen token will also be unlocked; if the settlement price is reached, your investment and interest income will be converted into the target token at the Target price.', labelDualRefresh: 'Refresh', labelNoticeForMarketFrozen: '{{ type }} is not supported, If you believe this is indeed a bug, please contact us.', labelInvestRangeDay: '{{arg}} Days', labelAmmExit: 'Redeem', labelAmmJoin: 'Subscribe', labelDualPanelClose: 'Go to My Investments', labelDualMobilePrice: '{{symbol}} price:', labelEditCollectionSuccess: 'Collection edit was successful', labelEditCollectionFailed: 'Collection edit has failed', labelEditCollectionBtn: 'Edit', labelEditRestCollectionBtn: 'Reset', labelEditCollectionERC1155: 'Edit My Collection', labelDualSettlementCalculator: 'Settlement Calculator', labelDualSettleDateDur: 'Subscription Length (days)', labelNoInvestContent: 'You currently have no investment assets. Start earning now with AMM, ETH Staking, or Dual Investments', labelImportCollection: 'Import Collection for Legacy NFT', labelCheckImportCollectionTitle: 'Import legacy NFTs under contract address to proceed', labelContinue: 'Next', labelImportCollection1: 'Import Collection for Legacy NFT', labelImportCollection2: 'Create/Choose a collection', labelImportCollection3: 'Select NFTs to move into/out of collection', labelSelectContractAddress: 'Contract address', labelImportChooseCollection: 'The created collection here can only be used to categorize the Legacy NFT minted without collection_metadata field.\n You can freely move those NFTs into any collection you created here.', labelImportCollectionundecided: 'Undecided', labelImportCollectionoutside: 'Others', labelImportCollectioninside: 'Current Collection', labelImportCollectionall: 'All', labelImportCollectionundecidedDes: 'items under this contract not classified into a collection', labelImportCollectionoutsideDes: 'items under this contract classified into a different collection', labelImportCollectioninsideDes: 'items under this contract classified into the current collection', labelImportCollectionallDes: 'all items under this contract', labelImportCollectionTitle: 'Import Collection for Legacy NFT', labelAssetTokens: 'Tokens', labelAssetInvests: 'My Investments', labelAssetRedPacket: 'Red Packets', labelORCreateCollection: 'Or <1>Create Collection', labelCreateLegacyCollection: 'Create Legacy Collection', labelNoLegacyCollection: 'You have no Legacy Collection, please', labelLegacyCollectionTitle: 'Create Legacy Collection', labelMoveOut: 'Move out of {{symbol}}', labelMoveIn: 'Move into {{symbol}}', labelMoveInCollection: 'Collection', labelSelectAll: 'Select All', labelCancelAll: 'Cancel', labelDoneBtn: 'Done', labelDetail: 'Detail', labelNFTMyCollection: 'Collection: {{collection}}', labelZoom: 'Zoom Media', labelRefresh: 'Refresh NFT cache', labelNFTDetailTab: 'Details', labelNFTPropertiesTab: 'Properties', labelLinkMetaData: 'NFT metadata Resource link', labelCountDown: 'Count Down', labelStackingSelect: 'Choose Staking Product', labelImportCollectionMove: 'Collection', labelCollectionImportNFTBtn: 'Manage Legacy NFT', labelNFTMoveFailed: 'NFT move failed!', labelNFTMoveSuccess: 'NFT moved successful', labelLuckTokenDefaultTitle: 'Good Luck!', labelSync: 'in Sync', labelMintInSyncTooltips: 'The NFT and collection information may not be synced up timely after minting due to onChain operation. Please stay tuned and refresh the page later.', labelEstRateApr: 'Est.rate (APR)', labelStakingApr: 'APR', labelManageCollectionTitle: 'Manage Legacy NFT', labelLegacy: 'legacy', labelTitleMyNFTSAvailable: 'My Holding NFTs', labelTitleTotalAvailable: 'Total NFTs', labelEstRateAprDes: 'APR stands for annual percentage Rate. It is the actual annual rate of return, NOT taking into account the effect of compound interest.', labelCheckImportCollectionDes: 'As the creator, you will be able to generate collection information for those NFT minted earlier that belong to nowhere. And once done, the other people holding your NFT will be able to view those NFT with proper collection information via loopring.io and loopring wallet.', labelL2toL1NFTFailed: 'Sent {{value}} {{symbol}} to {{l1Symbol}} has failed!', labelL2toL1NFTSuccess: 'Sent {{value}} {{symbol}} to {{l1Symbol}} was successful!', labelL2toL2NFTFailed: 'Sent {{value}} {{symbol}} from my {{loopringL2}} to another {{loopringL2}} failed!', labelL2toL2NFTSuccess: 'Sent {{value}} {{symbol}} was successful!', labelDoAgain: '{{method}} Again', labelDepositL1: 'Receive from {{l1Symbol}}', labelDepositNFTL1: 'Receive NFT from {{l1Symbol}}', labelL2ToL1Method: 'Send {{symbol}} to {{l1Symbol}}', labelL2ToL2Method: 'Send {{symbol}} to {{l2Symbol}}', labelConfirmAgainByFailed: 'You had a failed order, please confirm information again...', labelConfirmAgainByFailedWithBalance: 'You had a failed order, please confirm information again, Balance of {{symbol}} is {{count}}', labelNFTListfav: 'Favorite', labelNFTListhide: 'Hidden', labelNFTListall: 'Owned', labelNFTHide: 'Deploy Contract', labelNFTUnHide: 'Hide NFT', labelNFTUnHideDes: 'The easiest way to trade', labelHideMethodTooltiphide: 'Hide NFT', labelHideMethodTooltipunhide: 'Show NFT', labelFavouriteMethodTooltipfavourite: 'Favorite', labelFavouriteMethodTooltipunfavourite: 'Unfavorite', labelfavourite: 'Favorite', labelunfavourite: 'Unfavorite', labelFavouriteSuccess: 'Set {{favorite}} Successful', labelFavouriteFailed: 'Set {{favorite}} Failed', labelhide: 'Hide', labelunhide: 'Show', labelHideSuccess: '{{hide}} NFT Successful', labelHideFailed: '{{hide}} NFT Failed', labelSmallOrderAlertLine1: 'Small trades (below ~$100) incur a higher fee.', labelSmallOrderAlertLine2: 'Please review the fee before confirming.', labelSmallOrderAlertLine3: 'Trading Fee:', labelSmallOrderAlertLine4: 'Fee ratio:', labelSmallOrderAlertLine5: 'Minimum Converted:', labelSwapSecondConfirmTitle: 'Confirm Swap', labelSwapSettingTitle: 'Settings', labelSwapSettingSecondConfirm: 'Second confirmation', labelSwapSettingSecondConfirmTootip: 'skip confirm screen when toggled off', labelSwapSettingToggleSuccess: 'Swap second confirmation trun {{onOrOff}}', labelFeeMin: 'Min {{fee}}', labelIKnow2: 'I know', labelAddAssetTitleBridge: 'Add Asset From Another L1', labelAddAssetTitleBridgeDesActive: 'If you have transferred tokens from another {{ethereumL1}} Symbol account, it may take some time for this transaction to execute on-chain. Once you receive the assets, you can manually activate the {{l2Symbol}} account.', labelAddAssetTitleBridgeDes: 'If you have transferred tokens from another {{ethereumL1}} account, it may take some time for this transaction to execute on-chain.', labelAddAssetTitleExchange: 'Add Asset From An Exchange', labelAddAssetTitleExchangeDes: 'If you have transferred tokens from an Exchange, please wait. ', labelAddAssetTitleExchangeDesActive: 'If you have transferred tokens from an Exchange, please wait. Once you receive the assets, you can manually activate the {{l2Symbol}} account.', labelAddAssetTitleCard: 'Add Asset With a Card', labelAddAssetTitleCardDes: 'If you have purchased crypto with a card, please wait for it to arrive in your account.', labelAddAssetTitleCardDesActive: 'If you have purchased crypto with a card, please wait for it to arrive in your account. Upon arrival, {{l2Symbol}} will be activated manually.', labelMinFeeForActive: 'Min {{fee}}', labelReceiveAddressDes: 'If you have transferred tokens from another {{loopringL2}} account, please wait.', labelReceiveAddressDesActive: 'If you have transferred tokens from other {{loopringL2}} accounts, please close this window and try to activate your {{l2Symbol}} account again.', labelDepositWaiting: 'It make take some time for this transaction to execute on-chain.', labelFrom: 'From', labelTo: 'To', labeltransfer: 'Transfer', labelwithdraw: 'Withdrawal', labelDeposit: 'Deposit', labelFiatAmount: 'Fiat Amount', labelToMyL2: 'My {{loopringL2}}', labelBanxaNotReady: 'Please waiting a while for Banxa sdk loading, if you keep on face this problem try fresh the browser or contact us', labelBanxaFailedForAPI: 'Please waiting a while, Banxa service is not available currently.', labelL2toL2AddressFeePaid: 'Active account fee had paid', labelL2toL2AddressFeeActiveFee: "Pay recipient's {{l2Symbol}} activation fee: {{value}}", labelL2toL2FeeWithActive: 'Fee (including activation fee)', labelRedPacketOpen: 'Open', labelRedPacketTitle: 'Red Packets', labelRedPacketTypeTokens: 'Choose Tokens / NFTs / Blind Box', labelRedPacketChoose: 'Choose Red Packet Type', labelRedPacketMain: 'Input Red Packet/Send', labelLuckyTokenViewTypePublic: 'Public Red Packet', labelLuckyTokenViewTypePrivate: 'Private Red Packet', labelLuckyTokenViewTypeDesPublic: 'Your Red Packet is public, and everyone can try to claim a share of it.', labelLuckyTokenViewTypeDesPrivate: 'Your Red Packet is shared privately with others via a custom QR code.', labelLuckyBlindBox: 'Blind Box Red Packet', labelLuckyBlindBoxDes: 'Each recipient will receive a sealed Red Packet which cannot be opened until the expiration date. While some recipients will receive an NFT, others will need to try their luck next time.', labelLuckyRecievedBlindBox: 'Received Blind Box {{opendBlindBoxAmount}}/{{totalBlindBoxAmount}}', labelBlindBoxExplainationNotEnded: "The outcome of the Blind Box will be revealed upon expiration. Please claim within 3 days if your Red Packet contains a gift or it will be forfeited and returned to the Sender's wallet.", labelBlindBoxExplainationEnded: "Please claim within 3 days or it will be forfeited and returned to the Sender's wallet.", labelBlindBoxExplaination2: '{{opendBlindBoxAmount}} out of {{totalBlindBoxAmount}} blind boxes have been opened.', labelBlindBoxExplaination3: '{{remainingGiftsAmount}} gifts available for grabbing.', labelBlindBoxNotStarted: 'Red Packet is available to grab after: {{time}}', labelBlindBoxStarted: 'Blind Box Reveal time after: {{time}}', labelBlindBoxTokenHint: 'Unopen tokens will be returned back to sender after: {{time}}', labelBlindBoxClaimStarted: 'Any unclaimed NFTs will be returned to the Sender after: {{time}}', labelBlindBoxRecievedNFT: 'Received NFT {{deliverdGiftsAmount}}/{{totalGiftsAmount}}', labelBlindBoxStartDate: 'Start date', labelBlindBoxStartTime: 'Start Time', labelBlindBoxEndDate: 'End date', labelBlindBoxEndDate2: 'Blindbox reveal time', labelBlindBoxEndTime: 'End Time', labelBlindBoxRedPacketWithGift: 'Count of Red Packets with gift', labelBlindBoxExpirationExplainationForToken: 'After expiration, any unopened Red Packets will be forfeited and sent back to the Sender', labelBlindBoxExpirationExplainationForNFT: "If NFT Red Packet recipients do not claim their NFT within 30 days, it will be forfeited and returned to the Sender's wallet.", labelBlindBoxPrivate: 'Private Red Packet', labelBlindBoxPrivateDes: 'Your Red Packet is shared privately with others via a custom QR code.', labelBlindBoxClaimWarning: "If the recipients of the NFT Red Packets do not claim their received NFT within 3 days, the NFT will be forfeited and sent back to the Sender's wallet.", labelBlindBoxRecievedRedPackets: 'Received NFT Red Packets', labelBlindBoxCongratulations: 'Congratulations', labelBlindBoxSorry: 'Sorry', labelBlindBoxNoRewards: 'You have not received a reward', labelBlindBoxCongratulationsBlindBox: 'Congratulations on receiving a Blind Box', labelBlindBoxSorryBlindBox: 'Sorry, you did not win a prize', labelLuckyRelayToken: 'Relay Red Packet', labelLuckyRelayTokenDes: 'If the recipient of the Red Packet also re-shares the packet, they receive half of whatever the next person receives.', labelLuckyRandomToken: 'Lucky Red Packet', labelLuckyRandomTokenDes: 'Each recipient will get a random amount of.', labelLuckyCommonToken: 'Average Red Packet', labelLuckyCommonTokenDes: 'Each recipient will receive a pre-set split of the total Red Packet shared.', labelL1toL2NFTAmount: 'NFT Amount', labelInputRedPacketBtnLabel: 'Select Token', labelCreateRedPacket: 'Send Red Packet', labelMyRedPacket: ' My Red Packet Record', labelRedPacketMarkets: 'Red Packet Plaza', labelRedPacketQRCodeImport: 'Receive Red Packet', labelLuckyTokenViewType1: 'Private Red Packet', labelLuckyTokenViewTypeDes1: 'Your Red Packet is shared privately with others via a custom QR code.', labelLuckyTokenViewType0: 'Public Red Packet', labelLuckyTokenViewTypeDes0: 'Your Red Packet is public, and everyone can try to claim a share of it.', labelSplit: 'Red Packet Count', labelRedPacketMemo: 'Memo', labelRedPacketMemoPlaceholder: 'Best wishes', labelRedPacketStart: 'Available in', labelRedPacketSendWaitForAuth: 'Please confirm to send red packet {{value}} {{symbol}}.', labelRedPacketSendDenied: 'You rejected to send {{value}} {{symbol}} red packet.', labelRedPacketRecordTitle: 'My Red Packet Record', labelRedPacketReceived: 'ERC20 Received', labelRedPacketSend: 'ERC20 Send', labelRedPacketNFTReceived: 'NFT Received', labelRedPacketNFTSend: 'NFT Send', labelImportRedPacket: 'Import QR code to receive red packet', labelCreateRedPacketTitle: 'Send Red Packet', labelClaimWithdrawFee: 'Fee', labelClaimWithdrawNotEnough: 'Insufficient balance', labelClaimWithdrawTitle: 'Claim to {{loopringL2}}', labelClaimWithdrawWaitForAuth: 'Please confirm to claim {{symbol}}', labelClaimWithdrawDenied: 'You rejected to claim {{symbol}}.', labelClaimWithdrawInProgress: 'Processing...', labelClaimWithdrawFailed: 'Claim has failed!', labelClaimWithdrawSubmit: 'Claim has been submitted', labelClaimWithdrawToken: 'Token Amount', labelRedPacketSendSubmit: 'Send red packet has been submitted.', labelRedPacketSendSuccess: 'Red packet Send Successful.', labelRedPacketSendFailed: 'Send red packet of {{value}} {{symbol}} failed!', labelRedPacketSendInProgress: 'Processing...', labelRefreshRedPacket: 'Refresh List', labelRedPacketSendCommonTitle: 'Normal Red Packet', labelRedPacketSenRandomTitle: 'Lucky Red Packet', labelAmountEach: 'Amount Each', labelRedPacketTotalAmount: 'Total Amount', labelQuantity: 'Quantity', labelAssetAmount: 'Total Asset Amounts: {{value}}', labelCreateRedPacketBtn: 'Prepare Red Packet', labelRedPacketsExpireDes: 'Unclaimed tokens remaining after the expiration will be returned within 24h', labelReserveFee: 'Insufficient {{symbol}} with fee', labelRedPacketFee: 'Insufficient fee', labelRedPacketsInsufficient: 'Insufficient {{symbol}} balance', labelRedPacketsMinRange: 'Min {{value}}', labelRedPacketsMaxRange: 'Max {{value}}', labelRedPacketsMin: 'Minimum of {{value}} {{symbol}}', labelRedPacketsMax: 'Maximum of {{value}} {{symbol}}', labelRedPacketsGiftsLargerThanPackets: 'The number of Red Packets containing gifts cannot exceed the total number of Red Packets', labelBlindBoxNumberOverMaximun: 'Number of Blind Box exceeds maximum', labelRedPacketsSplitNumber: 'The maximum number of Red Packet is {{value}}', labelRedPacketsSplitCommonDetail: 'Distribution per red packet: {{value}}', labelRedPacketsSplitLuckyDetail: 'Token amount for each Red Packet is randomized.', labelSendRedPacketTitle: 'Send Red Packet', labelSendRedPacketTitlePublic: 'Send Red Packet -- Public', labelSendRedPacketTitlePrivate: 'Send Red Packet -- Private', labelRedPacketWaitingBlock: 'Block is not ready', labelShare: 'Share', labelRelayRedPacket: 'Relay Red Packet', labelNormalRedPacket: 'Average Red Packet', labelluckyRedPacket: 'Lucky Red Packet', labelrelayRedPacket: 'Relay Red Packet', labelnormalRedPacket: 'Average Red Packet', labelLuckyRedPacket: 'Lucky Red Packet', labelLuckyRedPacketStart: 'Starts in: {{value}}', labelLuckyRedPacketTimeout: 'Red Packet has been \n taken out', labelLuckyRedPacketDetail: 'View Red Packet details >', labelRedPacketOpenInProgress: 'Processing...', labelRedPacketOpenFailed: 'Read red packet failed!', labelRedPacketShowQR: 'Share red packet', labelRedPacketReceivedRecord: 'Receive Red Packet {{value}}/{{count}}', labelAmmExitMiniOrderDisabled: 'Transaction fees will be greater than the value of the LP, which will cost you your assets.', labelAmmExitMiniOrderMini: 'The transaction fee will account for 15% of the LP value, are you sure you want to redeem it?', labelLpAmount: 'LP Amount: {{value}}', labelRedPacketMarketsBtn: 'Red Packet Plaza', labelRedPacketBtn: 'Shared', labelRedPacketViewType0: 'Public Plaza', labelRedPacketViewType1: 'Public QR', labelRedPacketViewTypeDetail0: 'public Red Packet', labelRedPacketViewTypeDetail1: 'public Red Packet', labelRedPacketStatusSUBMITTING: 'Submitting', // SUBMITTING = 0, labelRedPacketStatusNOT_EFFECTIVE: 'Not Start', // NOT_EFFECTIVE = 1, labelRedPacketStatusPENDING: 'In Processing', // PENDING = 2, labelRedPacketStatusCOMPLETED: 'Completed', // COMPLETED = 3, labelRedPacketStatusOVER_DUE: 'Over Due', // OVER_DUE = 4, labelRedPacketStatusFAILED: 'Failed', // FAILED = 5 labelRedPacketStatusNotStarted: 'Hasn’t started', labelRedPacketStatusStarted: 'Started', labelRedPacketStatusEnded: 'Ended', labelRedPacketNo: 'NO.{{value}}', labelRedPacketClaimInProgress: 'Processing...', labelRedPacketClaimFailed: 'Open red packet failed!', labelRedPacketClaimSuccess: '', labelReceived: 'Received', labelGoodLuck: 'Good Luck', labelRedPacketGrab: 'Share with Friends', labelRedPacketEnded: 'Ended', labelLuckDraw: 'Luckiest Draw', labelMyLuckReward: '(My reward)', labelRedPacketClaimTitle: 'Claim to {{loopringL2}}', labelClaimNoBalance: 'Insufficient {{belong}} balance', labelShareQRCode: 'Generate QR Code for share', labelSeal: 'Seal', labelOpenAfter: 'Open after {{time}}', labelOpenStart: 'Start', labelTotalRedPacket: 'Total Quantity: {{value}}', labelMyRedPacketReward: 'My Rewards', labelRedpacketScanDes: 'Grab this Red Packet by scanning with your Loopring Wallet or importing to loopring.io', labelLuckyRedPacketStarted: 'Red Packet is Started', labelNFTRedpacketBtn: 'Send Red Packet', labelRedpacketDurationTitle: 'Expires after', labelRedpacketDurationPlaceHold: 'Min 1 - Max 30 Days', labelRedPacketDescription: 'Red Packet Description', labelRedpacketHavePeopleHelp: '<1>{{number}} friends relayed this red packet, you extend reward: <3>{{amount}}.', labelRedPacketFrom: 'From', labelRedPacketTo: 'To {{loopringL2}}', labelRedPacketMy: 'My Red packet', labelRedpacketNotActive: 'Hide received Red Packets', labelRedpacketTokens: 'ERC20 Tokens', labelRedpacketTokensShort: 'Tokens', labelRedpacketNFTS: 'NFTs', labelRedpacketBlindBox: 'Blind Box', labelRedpacketHideInactionable: 'Hide inactionable records', labelChooseNFT: 'Choose NFT <1>{{required}}', labelChooseNFTTooltips: '', tokenSelectNFTToken: 'Select NFT', labelRedPacketClaimERC20: 'ERC20', labelRedPacketClaimNFT: 'NFT', labelRedPacketMarketERC20: 'ERC20', labelRedPacketMarketNFT: 'NFT', labelRedPacketNotSupport: 'Unfortunately Mobile Dapp does not support Red Packet feature, Please download Loopring wallet or try this feature on laptop browser.', labelRedPacketTimeRange: 'Start / End Time', labelRedPacketTimeRangeDes: 'The Red Packet expires after the end date', labelRedPacketTimeRangeBlindbox: 'Start / Reveal Time', labelRedPacketTimeRangeBlindboxDes: 'The Reveal Time is when the Red Packet ends, and recipients can open it to see if they have received an NFT', labelRedPacketStartWithTime: '{{time}} Start', labelRedPacketTabReceived: 'Received', labelRedPacketTabSent: 'Sent', labelRedPacketTabNFTs: 'NFTs', labelRedPacketTabBlindBox: 'Blind Box', labelOrderOpen: 'Continue', labelOrderCancel: 'Cancel', labelOrderBanxaIsReadyToPay: 'The crypto selling order is ready. Please continue.', labelBanxaContinuous: 'Proceed existing order', labelBanxaCreate: 'Create new order', labelBanxaTitleCreateAgain: '', labelYouAlreadyHaveAnBanxa: 'Existing <1>order detected, send token to complete order.', labelHaveAnBanxaCancel: 'Create a new order from scratch.', labelBanxaConfirmSubmit: 'Token has been sent to Banxa wallet. You can save/click below link to check the payment status anytime.', labelInvestStakeLRC: 'LRC STAKING', labelInvestStakeLRCDES: 'Earn LRC staking rewards', labelFriendsPayActivation: 'Your friend has paid for your {{l2Symbol}} activation fee.', labelLRCStakingTitle: "What's LRC Staking", labelLRCStakingRisk: '

    LRC staking is incentivized through an allocated portion of the Loopring protocol fee; the exact percentage is determined by the Loopring DAO. The APY is updated daily based on the allocated amount from previous day’s fee. Any LRC holder can participate in LRC staking via {{l2Symbol}} to accumulate daily rewards. The assets must be staked for a minimum of 90 days to receive rewards.

    ', labelLRCStakingAgree: 'I have read and understand the risk warning.', labelLRCStakingRisk2: '<0>The staked LRC will be locked in {{loopringL2}}, meaning it cannot be used for other purposes. You may redeem your LRC at any time; however, doing so before the minimum Locked Duration will forfeit any accumulated reward.', labelInvestLRCStakingTitle: 'LRC Staking', labelMyInvestLRCStaking: 'My Investments', labelInvestLRCStakingLockAlert: 'Your assets for investment will be locked until your redemption.', labelLRCStakeAPRTooltips: 'APR stands for annual percentage rate, not taking into account the effect of compound interest. The value displayed here indicates the current value, while it keeps changing dynamically. ', labelLRCStakeAPR: 'APR <1>', labelLRCStakeEarn: 'Daily Rewards (est.) <1>', labelLRCStakeEarnTooltips: 'Once funds are successfully locked for staking, rewards will begin calculating at 00:00 (UTC) the following day.', labelLRCStakeSubTime: 'Subscribe Time', labelLRCStakeDurationTooltips: 'Staking Duration refers to the minimum amount of time that staked assets must be locked in order to be entitled to claim rewards. LRC staking requires the minimum Locked Duration.', labelLRCStakeDuration: 'Lock duration to claim reward<1>', labelInvestLRCTitle: 'LRC Staking', labelLRCStakeRiskDes: 'The staked LRC will be locked in {{loopringL2}}, meaning it cannot be used for other purposes. You may redeem your LRC at any time; however, doing so before the minimum Locked Duration will forfeit any accumulated reward.', labelAgreeRedeem: 'Redeem', labelStackingAgreeRedeemTitle: 'Redeem In Advance', labelStackingAgreeRedeem: 'Redeeming staked assets before the minimum Locked Duration will forfeit the accumulated rewards. Are you sure you still want to redeem?', labelLRCStakeProduct: 'Product', labelLRCStakeRedeemDes: 'This product has meet the minimum Locked Duration. You can now redeem any portion of the subscription amount without deducting from your earnings. The remaining subscription amount will continue to generate income.', labelLRCStakeRedeemAgree: 'I acknowledge the early redemption will forfeit the accumulated reward', labelLRCStakeCurrentEarn: 'Current Total Rewards', labelLRCStakeForfeitedReward: 'Forfeited Reward', labelLRCStakeRemainingEarnings: 'Remaining Rewards', labelDeFiSideAmount: 'Amount', labelDeFiSideProduct: 'Product', labelDeFiSidePoolShare: 'Pool Share', labelDeFiSideAPR: 'APR', labelDeFiSideCumulativeEarnings: 'Cumulative Earnings', labelDeFiSidePreviousEarnings: "Previous Day's Earnings", labelDeFiSideLockDuration: 'Lock duration to claim reward', labelDeFiSideSubscribeTime: 'Subscribe Time', labelDeFiSideHoldingTime: 'Holding Time', labelDeFiSideInvestmentDetails: '{{symbol}} Staking Details', labelSideStakingTable: 'LRC Staking', labelInvestMaxDefi: 'Min {{minValue}} - Max {{maxValue}}', labelDefiMax: 'Allowable maximum is {{arg}}', labelDefiStakingDetail: 'Detail', labelDefiStakingRedeem: 'Redeem', labelDays: 'day(s)', labelRemainingAmount: 'Remaining amount should be greater than {{symbol}},\n Please redeem all.', labelRemainingBtnAmount: 'Remaining amount is insufficient', labelStakingCumulativeEarnings: 'Cumulative Rewards', labelStakingClaimableEarnings: 'Claimable Rewards', labelClaimBtn: 'Claim', labelStakeNoEnough: 'Insufficient {{arg}} balance', labelClaimBtnClaimed: 'Claimed', labelClaimBtnExpired: 'Expired', labelDefiRemindMin: 'Please redeem all Balance', labelInvestType_LRCSTAKE: 'LRC Staking', labelNFTs_one: '\u2A09{{count}} NFT', labelNFTs_other: '\u2A09{{count}} NFTs', labelTokenNFTMaxRedPack: 'Max: ', labelNFTRedPackAskClaim: 'Note: After expiration, all the unclaimed NFTs will be returned back to sender. Please claim as soon as possible if you want to hold them.', labelTransferDelayConfirm: 'Your claim request has been received. Loopring will transfer the token into your {{l2Symbol}} account soon. Please verify it.', labelClaimredPacket: 'My Red Packet', labelRedPacketMe: 'Me', labelClaimlrcStaking: 'My LRC Staking', labelExpectSettlementPrice: 'The expected settlement price from this order is {{symbolSell}}/{{symbolBuy}}={{stob}}, while the current market price from a trusted oracle is {{symbolSell}}/{{symbolBuy}}={{marketPrice}}. There is a {{marketRatePrice}}% variance observed. To proceed, tap here to confirm you understand and acknowledge the risk.', labelStakingSuccess: '{{symbol}} Staking Successful', labelStakingFailed: '{{symbol}} Staking failed', labelStakingRedeemFailed: 'Redeem {{symbol}} failed', labelStakingRedeemSuccess: 'Redeem {{symbol}} Successful', labelStakingRedeemRemaining: 'Remaining Amount', labelStakingRedeemDate: 'Redeem Time', labelContactsAddContact: 'Add Contact', labelContactsAddressTitle: 'Address', labelContactsAddressDes: 'Enter wallet address or ENS', labelContactsAddressInvalid: 'Invalid address or ENS', labelContactsNameTitle: 'Name', labelContactsNameDes: 'Enter name for the contact', labelContactsAddContactBtn: 'Add', labelContactsDeleteContact: 'Delete Contact', labelDeleteContactInfo: 'Contact', labelContactsDeleteContactBtn: 'Delete', labelContactsAddSuccess: 'Add Contact Succeed', labelContactsDeleteSuccess: 'Delete Contact Succeed', labelContactsEditSuccess: 'Edit Contact Succeed', labelContactsSendSuccess: 'Send Succeed', labelContactsCopySuccess: 'Copied to Clipboard', labelContactsAddFailed: 'Add Contact Failed', labelContactsDeleteFailed: 'Delete Contact Failed', labelContactsEditFailed: 'Edit Contact Failed', labelContactsSendFailed: 'Send Failed', labelContacts: 'Contacts', labelContactsSend: 'Send', labelContactsTransactions: 'Transactions', labelContactsNetworkChoose: 'Choose {{l2Symbol}} or {{l1Symbol}} Account', labelContactsNext: 'Next', labelContactsContactExisted: 'Contact Already Existed', labelNotExchangeEOA: 'Sending to an Exchange Address {{l2Symbol}} account is not supported. {{loopringL2}} accounts cannot be activated on Exchange wallet addresses. Instead, please send to the {{l1Symbol}} account associated with this address.', labelNotOtherSmartWallet: 'This wallet binds with smart contract that does not support {{loopringL2}}. You will need to send funds to the {{l1Symbol}} account.', labelContactsNoContact: 'No Contact', labelContactsSelectReciepient: 'Select the Recipient', labelContactsBinanceNotSupportted: 'Binance currently do not support {{loopringL2}} transfers. You will need to send funds to the {{l1Symbol}} account.', labelContactsHuobiNotSupportted: 'Huobi currently do not support {{loopringL2}} transfers. You will need to send funds to the {{l1Symbol}} account. Transactions need to wait for 24 hours.', labelContactsOtherExchangesNotSupportted: 'The trading platforms currently do not support {{loopringL2}} transfers. You will need to send funds to the {{l1Symbol}} account.', labelBtradeSwapTitle: 'Block Trade', labelBtradeSwapType: 'Type', labelBtradeSwapFilled: 'Filled', labelBtradeSwapFee: 'Fee', labelBtradeSwapTime: 'Time', labelBtradeSwapPrice: 'Price', labelBtradeSwapSettled: 'Settled', labelBtradeSwapDelivering: 'Delivering', labelBtradeSwapPanelDes: 'The Loopring pool is currently unable to swap the full requested amount. The tokens that were successfully swapped will be transferred to your account now. The unswapped tokens will be locked until they can be swapped. \n We’ll rebalance the pool shortly and swap the remaining portion.', labelBtradeSwapDeliverDes: 'The Loopring pool is currently unable to swap the full requested amount. The tokens that were successfully swapped will be transferred to your account now. The unswapped tokens will be locked until they can be swapped. \n We’ll rebalance the pool shortly and swap the remaining portion.', labelGoBtradeSwap: 'Swapping on the DEX will result in a large Price Impact (loss of assets). We recommend using the Block Trade option to help minimize potential losses.', labelBtradeSwap: 'Block Trade', labelBtrade: 'Block Trade', labelBtradeSwapFailed: 'Failed!', labelBtradeSwapTitleDes: 'What is Block Trade?', labelBtradeSwapContentDes: '

    Block Trade offers a secure and trustless way for users to swap tokens using CEX liquidity. The trades happen exclusively between designated entities, ensuring that the existing liquidity of the DEX remains unaffected. There is no price impact to other DEX users as a result of the transaction.

    ' + '

    This is similar to the traditional stock market’s Block Trade System. A block trade is a large, privately negotiated transaction, which can be made outside the open market through a private purchase agreement.

    ' + "

    The Loopring pool is currently unable to swap the fully requested amount. If you choose to continue, the unswapped tokens will be locked until they can be swapped. We'll rebalance the pool shortly.

    " + '

    Block Trade offers two options:

      ' + '
    • Prioritize Speed.
    • ' + '
    • Prioritize Quantity.
    ' + '
    Prioritize Speed
    ' + '

    This option prioritizes quick trade execution to ensure that trades are completed as soon as possible. It’s ideal for users who need to complete their trades quickly.

    ' + '
    Prioritize Quantity
    ' + '

    This option prioritizes trading as much of the asset as possible, even if it means waiting longer for the order to be fully executed. It’s ideal for users who want to maximize their trading volume and are willing to wait for the market to be favorable before completing the transaction.

    ' + '

    We’ll use the Loopring pool to swap your tokens. If your request exceeds the pool’s available balance, we’ll swap as many tokens as we can. Afterwards, we’ll rebalance the pool and then swap the remaining portion. The entire transaction should complete within 24 hours.

    ', labelRefereeRewards: 'Referee Rewards', labelReferralRewards: 'Referral Rewards', labelRewardLRC: 'Rewards LRC', labelPrice: 'Price', labelBtradeSwapMiniMax: 'Min {{minValue}} - Max {{maxValue}}', labelBtradeSwapMini: 'Min {{minValue}}', labelBtradeConfirm: 'Please check the checkbox', labelBtradeSwapBtn: 'Swap', labelType: 'Type', labelBtradeTrade: 'Block Trade', labelBtradeTitle: 'Block Trade Details', labelBtradeQuote: 'Total Quota:', labelBtradeQuoteDes: 'Total Quota is the maximum allowable trading amount.', labelBtradePoolDes: 'Loopring Pool:', labelBtradePool: 'Loopring Pool', labelBtradeToleranceTooltips: 'Your trade will revert if the price changes unfavorably by more than this percentage.', labelBtradeFeeTooltips: 'The trading fee is fixed at {{feeRate}}.', labelBtradeMinReceiveTooltips: 'The price in other liquidity source changes dynamically, the price you see when placing an order may be inconsistent with the final transaction price; also the received amount needs to deduct the fees from converted amount. The protocol can guarantee that the received token is at least this amount.', labelBtradeInsufficient: 'Insufficient', labelBtradeTime: 'Time', labelStopLimit: 'Stop-Limit {{tradeType}} {{symbol1}}', labelStopLimitDes: '

    If the last price {{from}} to or {{behavior}} {{stopPrice}} {{symbol2}}, and order to {{tradeType}} {{value1}} {{symbol1}} at a price of {{limitPrice}} {{symbol2}} will be placed.

    ', labelStopLimitFromGoesUp: 'goes up', labelStopLimitFromDropsDown: 'drops down', labelStopLimitBehaviorAbove: 'above', labelStopLimitBehaviorBelow: 'below', labelStopLimitType: 'Stop-Limit / {{tradeType}}', labelStopLimitStopPrice: 'Stop Price', labelStopLimitPriceLimitPrice: 'Limit Price', labelStopLimitAmount: 'Amount', labelStopLimitCancel: 'Cancel', labelStopLimitConfirm: 'Confirm', labelBtradeSwapPending: 'Pending', labelStopLimitTitle: 'Stop-Limit', labelStopPrice: 'Limit / Buy Price', labelStopStopPrice: 'Stop / Trigger Price', labelStopLimitWhatIs: "What's Stop-Limit?", labelStopLimitMinMax: 'Min {{minValue}} - Max {{maxValue}}', labelLimitStopPriceMinMax: 'Stop Price Range {{arg}}', labelLimitMainContent: 'A Stop-Limit order is a limit order with a limit price and a stop price. When the stop price is reached, the limit order will be placed on the order book. Once the limit price is reached, the limit order will be executed.', labelLimitStopPriceLabel: 'Stop Price / Trigger Price', labelLimitStopPriceContent: 'When the current asset price reaches the given stop price, the Stop-Limit order is executed to buy or sell the asset at the given limit price or better.', labelLimitLimitPriceLabel: 'Limit Price', labelLimitLimitPriceContent: 'The selected (or potentially better) price that the Stop-Limit order is executed at.', labelLimitAmountLabel: 'Amount', labelLimitAmountContent: 'The quantity of assets to buy or sell in the Stop-Limit order.', labelLimitDes: 'You can set the stop price and limit price at the same price. However, it’s recommended that the stop price for sell orders should be slightly higher than the limit price. This price difference will allow for a safety gap in price between the time the order is triggered and when it is fulfilled. You can set the stop price slightly lower than the limit price for buy orders. This will also reduce the risk of your order not being fulfilled.\n' + 'Please note that your order will be executed as a limit order after the market price reaches your limit price. If you set the stop-loss limit too high or the take-profit limit too low, your order may never be filled because the market price can’t reach the set limit price.', labelLimitDemoTitle: 'How does a Stop-Limit order work?', labelLimitDemoDes: 'The current price is 2,400 (A). You can set the stop price above the current price, such as 3,000 (B), or below the current price, such as 1,500 (C). Once the price goes up to 3,000 (B) or drops to 1,500 (C), the Stop-Limit order will be triggered, and the limit order will be automatically placed on the order book.\n Note:
      ' + '
    1. Limit price can be set above or below the stop price for both buy and sell orders. For example, stop price B can be placed along with a lower limit price B1 or a higher limit price B2.\n
    2. ' + '
    3. A limit order is invalid before the stop price is triggered, including when the limit price is reached ahead of the stop price.
    4. ' + '
    5. When the stop price is reached, it only indicates that a limit order is activated and will be submitted to the order book rather than the limit order being filled immediately. The limit order will be executed according to its own rules.
    ', labelLimitFailed: 'Submitted failed', labelLimitMarket: 'Market data has issue', labelStopLimitOrderGroup: 'Stop-Limit Records', labelStoplimit: 'Stop-Limit', labelStopLimitProduct: 'Product', labelStopLimitLabelType: 'Type', labelStopLimitNotSupport: 'Sorry, there is currently insufficient liquidity in this token pair to execute Stop-Limit orders. Please try again later or consider using a market / limit order instead.', labelStopLimitTriggered: 'Triggered: The limit order has been submitted to the order book.\n Time: {{time}}', labelStopLimitWaitingTrigger: 'The limit order is not placed until the stop price has been triggered.', labelStopLimitCurrentlyInsufficient: 'Currently insufficient', labelDUAL_CURRENCY: 'Dual Investment', labelDUAL_BASE: 'Dual Investment', labelBTRADE: 'Block Trade', labelL2STAKING: 'LRC Staking', labelSTOP_LIMIT: 'Stop-Limit', labelAMMPending: 'Pending', labelAMMTitle: 'AMM Investment', labelAMMChartFailed: 'Failed load data', labelExpectSettlementLimitPrice: 'The expected settlement price from this order is {{symbolBase}}/{{symbolQuote}} = {{price}}, while the current market price from a trusted oracle is {{symbolBase}}/{{symbolQuote}} = {{marketPrice}}. There is a {{marketRatePrice}}% variance observed. To proceed, tap here to confirm you understand and acknowledge the risk.', labelAMMNoEnough: 'Insufficient {{arg}} balance', labelAMMMax: 'Max {{arg}} ', labelAMMMaxAND: '{{coinA}} and {{coinB}}', labelDepositTo: 'Deposit to', labelReferTitle: 'Invite friends to join in \nLoopring and receive rewards', labelReferTitleDes: 'As referrer: will receive a commission on fees the new referred user trades. \n As referee: will enjoy a discount on transfer fees.', labelCopy: 'Copy', labelReferralRules: 'Reward rules', labelReferralMethod1: 'Method 1', labelReferralMethod2: 'Method 2', labelReferralMethod1Step1: 'Download the Loopring Wallet App', labelReferralMethod1Step2: 'Sign up with referral code', labelReferralMethod1Step3: 'Activate {{loopringL2}} Account', labelReferralMethod1Step4: 'Both of us receive rewards', labelReferralMethod2Step1: 'Access the website <1>https://loopring.io', labelReferralMethod2Step2: 'Connect Wallet', labelReferralMethod2Step3: 'Activate {{loopringL2}} Account with referral code', labelReferralMethod2Step4: 'Both of us receive rewards', labelReferralMyReferrals: 'My Referrals', labelReferralReferralsRefunds: 'Referee Refunds', labelBtradeQuantity: 'Prioritize Quantity', labelBtradeSpeed: 'Prioritize Speed', labelBtradeSettled: 'Settled', labelOrderCancelConfirm: 'Confirm to cancel this order?', labelOrderCancelOrder: 'Cancel', labelRedpacketTotalReward: 'Total {{amount}}', labelRedpacketCantOpen: 'Now is not the time to open', labelLocketInfo: '{{symbol}} Locked Detail', labelSendAssetToAnotherNet: 'To Others Via Third Party Bridge', labelFromAnotherNet: 'From another network', labelAddAssetTitleAnotherNetDes: 'If you have transferred tokens from another network, please wait. ', labelAddAssetTitleAnotherNetDesActive: 'If you have transferred tokens from another network, please wait. Once you receive the assets, you can manually activate the {{l2Symbol}} account.', labelAnotherNetworkDes: '<0>Orbiter.finance is a 3rd party service provider to help move tokens between various {{l1ChainName}} {{l1Symbol}} and {{l2Symbol}} networks. If you have any concerns regarding their service, please check out their <2>TOS.', labelAnotherNetworkUnderstand: 'I understand and acknowledge the risk', labelReferralImageDes: 'Scan code to register', labelReferralImageCode: 'Code: {{code}}', labelInvite: 'Invite Friends', labelReferralsTotalEarning: 'Total Rewards ', labelReferralsClaimEarning: 'Claimable Rewards ', labelReferralsTotalReferrals: 'Total Referrals ', labelReferralsTotalRefund: 'Total Refunds ', labelReferralsClaimRefund: 'Claimable Refunds ', labelReferralsTotalTradeNumber: 'Total Trade Number ', labelReferralCode: 'Referral Code (Optional) <1>\uFE61', labelReferralToolTip: 'Enter referral code to enjoy a discount on transfer fees.', labelBtradeRefresh: 'Refresh', labelArgNoEnough: 'Insufficient {{arg}} balance', WalletConnectV1: 'WalletConnect Legacy', labelDualInvestDesInsufficient: 'Insufficient quota', labelExplorer: 'Explorer', labelTutorial: 'Tutorial', labelRejectSwitchNetwork: 'Switch Chain was Rejected', labelAssetRewards: 'Rewards', labelClaimTypeREBATE_FEE: 'Maker Order Rewards', labelClaimTypeRECOMMENDER_FEE: 'Referral Rewards', labelClaimTypeREFERER_FEE: 'Referee Refunds', labelClaimTypePROTOCOL_FEE: 'Dedicated AMM Incentive', labelClaimTypeLRC_STAKING: 'LRC Staking Rewards', labelClaimOtherRewards: 'Other Rewards', labelAMMClaimableEarnings: 'Dedicated AMM Incentive', labelLayer2HistoryOrders: 'Order Records', labelLayer2HistoryStopLimitRecords: 'Stop-Limit Records', labelLayer2HistoryDefiRecords: 'ETH Staking', labelLayer2HistoryDualRecords: 'Dual Investment', labelLayer2HistorySideStakingRecords: 'LRC Staking', labelLayer2HistoryBtradeSwapRecords: 'Block Trade', labelPayLoopringL2: 'Pay {{loopringL2}}', labelRedPacketTimeRangeBlindboxDesERC20: 'The Reveal Time is when the Red Packet ends, and recipients can open it to see if they have received tokens', labelBlindBoxExpirationExplainationForTokenBlindbox: "If the recipients of the Tokens Red Packets do not open their received Tokens, the Tokens will be forfeited and sent back to the Sender's wallet.", labelBlindBoxRecieved: 'Received {{deliverdGiftsAmount}}/{{totalGiftsAmount}}', labelBlindBoxClaimHint: 'You can visit Assets > Red Packets to claim your rewards.', labelRedPacketBlindboxReceived1: 'ERC20 Blind Box', labelRedPacketBlindboxReceived2: 'Received', labelRedPacketsGiftsEqualsZero: "Number of gifts can't be zero", labelRedpacketStandard: 'Standard', labelCiETHDefiRiskTitle: "What's Cian Leveraged ETH?", labelCiETHDefiRisk: '

    CIAN protocol is a liquid staking derivatives (“LSD”) focused yield strategy platform, where users could earn either through joining algorithmic strategy vaults or through building their own DeFi strategies using CIAN’s advanced automation tools.

    ' + "

    The stETH/ETH leveraged staking strategy enables users to safely leverage stETH’s staking rewards. This strategy focuses on staking derivatives and protection/optimization tooling. By nature, this strategy is to use the user's asset as collateral to borrow ETH from lending platforms, then stake ETH in Lido to earn ETH staking interest. By utilizing tools like Flashloan, it actually adds leverage to users' ETH investment. If only there is a positive APY diff between the borrowing rate and ETH staking rate, there will be additional earnings from this strategy vs. standard ETH staking.

    " + '

    It is quite important to understand that, when using such leveraged strategy, it’s highly advised to intend on holding that position for a while. By doing so, users will give enough time for the high APY to cover their entry & exit costs.

    \n', labelDefiWithdrawFee: '', labelLeverageETHTitle: 'Leveraged ETH Staking', labelLayer2HistoryleverageETHRecords: 'Leveraged ETH Staking', labelSwapMinConverted: 'Minimum Converted', labelSwapMinConvertedTooltip: 'The pool price changes dynamically, the price you see when placing an order may be inconsistent with the final transaction price. The protocol can guarantee that you will receive at least this amount.', labelNetworkFee: 'Network Fee', labelTradingFee: 'Trading Fee', labelTradingFeeEst: 'Trading Fee (est.)', labelStopStopPriceDes: 'It\'s actually the trigger price for the relayer to place a valid order. When the market price reaches the "Stop Price", the system will automatically place a limit order at "Limit Price".', labelStopPriceDes: 'After the "Stop Price" is triggered, the relayer will automatically place a limit order at this price. ', labelStopPriceSell: 'Limit / Sell Price', labelClaimallToken: 'My Rewards', labelConnecting: 'Connecting', labelTitleOverviewAllPrd: 'All Products', labelInvestDefiDes: 'Earn ETH staking rewards', labelInvestChoseProduct: 'Choose the product you want', labelInvestTotalEarnings: 'Total Earnings', labelInvestLoopringEarn: 'Loopring DeFi', labelInvestLoopringEarnDes: 'Earn stable profits with professional asset management', labelInvestLRCDes: 'Earn LRC staking rewards', labelHadUnknownCollectionTitle: 'Import Collection for Legacy NFT', labelHadUnknownCollectionDes: 'As the creator, you will be able to generate collection information for those NFT minted earlier that belong to nowhere. And once done, the other people holding your NFT will be able to view those NFT with proper collection information via loopring.io and loopring wallet. ', labelGo: 'Go', labelAnotherNetworkDes2: 'Note: Please ensure to check out the "Change Account" option and input the recipient\'s address carefully. If you want to send token to network other than ethereum, the recipient address must be different than the sender address; else you will lose that asset for ever.', labelAnotherNetworkDes3: '', labelRiskReminder: 'Risk Reminder', labelDefiRedeem: 'Redeem', labelDefiSubscribe: 'Subscribe', labelDefiMaxBalance1Leverage: '
  • The Loopring pool will rebalance soon. Please come back later to redeem.
  • ', labelDefiNoBalanceLeverage: 'Loopring will set up the pool soon. Please come back later to redeem.', labelDefiMaxBalanceLeverage: 'It is not possible for the Loopring pool to fulfil your complete request at the moment. You can choose withdraw ciETH https://vault.cian.app/vaults', labelFunctionList: 'Function List:', labelSuperUserTitle: 'Super User', labelLeverageETHStaking: 'Leveraged ETH Staking', labelLeverageETHBack: 'Leveraged ETH Staking', labelInvestType_LEVERAGEETH: 'Leveraged ETH Staking', labelRewardRefresh: 'Refresh', labelToMyL2WidthAddress: '<0>My {{loopringL2}}<1>({{address}})', labelFeeAvailablePay: 'Available: {{available}}, Pay: {{pay}}', labelMarketOrderUnfilled: 'Market Order Unfilled', labelRiskAgree: 'Proceed Anyway', labelRiskCancel: 'Cancel', labelExpectedSettlementPrice: 'Expected Settlement Price', labelCurrentMarketPrice: 'Current Market Price', labelPriceVariance: 'Price Variance', labelImpactExtraNewGreat: 'This trade will result in a loss of {{value}}% of the position’s market value. To proceed, tap ‘Proceed Anyway’ to confirm you understand and acknowledge the risk.', labelPriceImpact: 'Price Impact', labelPriceImpactDes1: 'This trade will affect the pool price by more than {{value}}%,which is too high. It may result in significant slippage and potential losses. If you acknowledge the risk and wish to proceed, type the ‘AGREE’ and tap ‘Proceed Anyway’ to confirm again.', labelPriceImpactDes2: 'This trade will affect the pool price by more than {{value}}%,which is too high. It may result in significant slippage and potential losses. To proceed, tap ‘Proceed Anyway’ to confirm you understand and acknowledge the risk.', labelCopyCodeClip: 'Referral Code Copied to Clipboard!', labelDepositPending: '{{l1Symbol}} to {{l2Symbol}} Pending', labelWithDrawPending: '{{l2Symbol}} to {{l1Symbol}} Pending', labelContactsEditContactBtn: 'Edit Contact', labelLargePriceVariance: 'Large Price Variance', labelHighPriceImpacTitle: 'High Price Impact Detected', labelTimeoutAddressClick: '{{l1Symbol}} account checking request was rejected or some unknown error occurred, please retry', labelSmallOrderAlertLine: 'Small trades (below ~$100) incur a higher fee.', labelLimitImpactTitle: 'Limit taker Order Requires Confirmation', labelRedPacketViewType2: 'Exclusive', labelRedPacketPlazaPublic: 'Public Plaza', labelRedPacketPlazaPublicDes: 'Everyone in the Loopring community can participate in claiming the red packet', labelRedPacketQRPublic: 'Public QR', labelRedPacketQRPublicDes: 'Anyone that knows the QR code can participate in claiming the red packet', labelRedPacketExclusive: 'Exclusive Red Packet', labelRedPacketExclusiveDes: 'Only users that have received the red packet can claim it', labelRedPacketHaveExclusive: 'You have {{count}} exclusive Red Packets.', labelRedPacketExclusiveViewDetails: 'View Details >', labelRedPacketCongratulations: 'Congratulations!', labelExclusiveRedpacket: 'Exclusive Red Packets', labelRedpacketExclusiveReady: 'You have {{count}} exclusive Red Packets ready', labelRedpacketSentMaxLimit: 'Sent / Max Limit', labelRedpacketCreateNew: 'Create New Red Packet', labelRedpacketGiftRedPacket: 'Count of Red Packet with Gift', labelRedpacketRedPacketscount: 'Total Red Packets', labelRedpacketRevealTime: 'Reveal Time', labelRedpacketRecipients: 'Red Packet Recipients >', labelRedpacketRecipientList: 'Recipient List', labelExclusiveWhitelistDes: "For whitelisted users, each Red Packet can accommodate a maximum of 10,000 addresses, while standard users are allowed up to 50 addresses per Red Packet. Whitelisted addresses include Loopring, our partners, or other verified members. If you're interested in being whitelisted, please contact us at support@loopring.io.", labelRedpacketTextimport: 'Text import', labelRedpacketContactImport: 'Contact import', labelRedpacketNotificationDisplay: 'Notification Display', labelRedpacketRedDotDes: 'Recipients are alerted via a badge next to the Red Packets category', labelRedpacketBadge: 'Badge', labelRedpacketPopUp: 'Pop-up Notification', labelRedpacketPopUpTooltip: "Whitelisted addresses include Loopring, our partners, or other verified members. If you're interested in being whitelisted, please contact us at support@loopring.io.", labelRedpacketPopPpDes: 'Recipients are alerted via a prominent display that highlights the contents of the RedPacket. (Limited to whitelisted users)', labelRedpacketPrepareRedPacket: 'Prepare Red Packet', labelRedPacketChooseTarget: 'Select existing red packet or create a new one', labelRedPacketRecipientList: 'Recipient List', labelRedPacketPublicTooltip: 'Your Red Packet is public, and everyone can participate in claiming it.', labelRedPacketPrivateTooltip: '

    Your Red Packet is private, and only the addresses you specify can claim it.

    ' + '

    To create a new Exclusive Red Packet, please follow these steps:

    ' + '
  • 1. Select the type of Red Packet
  • ' + '
  • 2. Specify the amount of Red Packets to be sent and the date/time for delivery
  • ' + '
  • 3. Designate the recipients
  • ' + '
  • 4. Set the Notification Display
  • ' + '

    For whitelisted users, each Red Packet can accommodate a maximum of 10,000 addresses, while standard users are allowed up to 50 addresses per Red Packet.

    ' + "

    Whitelisted addresses include Loopring, our partners, or other verified members. If you're interested in being whitelisted, please contact us at support@loopring.io.

    ", labelRedpacketExclusiveEmpty: 'Your Prepared but unaddressed red packets will be displayed here!', labelRedpacketExclusiveSelected: 'Selected: {{count}}', labelRedpacketExclusiveManualEdit: 'Manual Edit', labelRedpacketValidAddresses: 'Valid Addresses: {{count}}', labelRedpacketTips: 'Tips', labelRedpacketChangeImportTips: 'If you change the import method, the previously selected addresses will be erased, are you sure you want to erase them?', labelRedpacketAddressesReview: 'Addresses Review', labelRedpacketAddressesReviewPart1: 'The list contains {{count}} valid addresses,', labelRedpacketAddressesReviewPart2: '{{count}} invalid addresses', labelRedpacketAddressesReviewPart3: '. To proceed, invalid addresses will be automatically removed from the list.', labelRedpacketExclusiveListEmpty: 'Your Prepared but unaddressed red packets will be displayed here!', labelRedpacketreceiptListEmpty: 'The addresses of the red packet you sent will be displayed here', labelRedpacketBestwishes: 'Best wishes', labelSendRedPacketTitleExclusive: 'Send Red Packet -- Exclusive', labelSendRedPacketClear: 'Clear', labelSendRedPacketMax: 'Max: {{count}}', labelRedPacketMaxValueExceeded: 'Maximum value exceeded', labelRedPacketTotal: 'Total {{count}}', labelRedPacketExclusiveTag: 'Exclusive', labelRedPacketClaiming: 'Claiming', labelRedPacketReceiptsList: 'Red Packet Receipt >', labelEOA: 'EOA', labelLoopringWallet: 'Loopring', labelOtherSmart: 'Other Smart', labelBinance: 'Binance', labelHuobi: 'Huobi', labelOtherExchange: 'Other Exchange', labelContactsEditContact: 'Edit Contact', labelLeverageETHStakingDes: 'Gain higher APY aggressively', labelDownloadShared: 'Download', labelShareReferralCode: 'Share to', labelShareMessage: 'Join me at Loopring and earn exclusive rewards with Loopring Referral Program! https://www.loopring.io/#/?referralcode={{code}}', labelInvestDualAutoTitle: "What's Auto Reinvest", labelDualAutoTitle: 'Auto Reinvest <1>', labelDualAutoTitleDes: 'Auto Reinvest will automatically reinvest your investment and earned interest into a new term with the same Target Price once the previous term expires, continuing until you successfully buy or sell crypto. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed and your investment and earned interest will be unlocked.\n', labelInvestDualAutoCheck: '

    Auto Reinvest will automatically reinvest your investment and earned interest into a new term with the same Target Price once the previous term expires, continuing until you successfully buy or sell crypto. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed and your investment and earned interest will be unlocked.

    Reinvest Target Price: The Target Price at which you want to buy or sell crypto.

    Longest Settlement Date: The maximum duration available for selecting the settlement period. Auto Reinvest will automatically match products with settlement periods that do not exceed the Longest Settlement Date.

    ', labelDualAutoDetail: 'Auto Reinvest will try to find a new product which based on the following rule at 16:00 on the settlement day.', labelDualAutoDUAL_BASEPrice: 'Sell Price <1>', labelDualAutoDUAL_CURRENCYPrice: 'Buy Price <1>', labelDualAutoDUAL_BASEPriceDes: 'The target price at which you want to sell crypto.', labelDualAutoDUAL_CURRENCYPriceDes: 'The target price at which you want to buy crypto.', labelDualModifyParameter: 'Modify Parameter', labelDayDisplay: '{{item}} Day(s)', labelDualModifyConfirm: 'confirm', labelDualAutoDurationDes: 'The maximum duration when selecting the settlement period. Auto Reinvest will automatically match products with settlement periods that do not exceed the Longest Settlement Date.', labelDualModifyBtn: 'Modify', labelTurnOffDualAutoInvest: 'Stop Auto Invest', labelDualModifyAPR: 'APR: {{value}}', labelDualModifySettlementDateDes: 'Changes will take effect after the Next Settlement Date.', labelDualModifySettlementDate: 'Next Settlement Date: {{date}}', labelDualEditSuccess: 'Update successful!', labelDualEditFailed: 'Update Failed!', labelDualEditDuration: 'Modify Longest Settlement Date <1>', labelDualEditDurationDes: 'The maximum duration when selecting the settlement period. Auto Reinvest will automatically match products with settlement periods that do not exceed the Longest Settlement Date.', labelDualInvestGuid: 'Invest', labelCoverGain: 'Covered Gain', labelCoverGainDes: 'Earn interest before taking profits', labelDip: 'Buy The Dip', labelDipDes: 'Earn interest while waiting for your price target', labelDualMerge: 'Dual Investment', labelDualMergeDes: 'Select based on Token and Settlement Date', labelDualChooseTokenDUAL_BASE: 'Step 1: Choose a token to sell', labelDualChooseTargetPriceDUAL_BASE: 'Step 2: Choose Target Price and Settlement Date', labelDualChooseTokenDUAL_CURRENCY: 'Step 1: Choose a token to buy', labelDualChooseTargetPriceDUAL_CURRENCY: 'Step 2: Choose Target Price and Settlement Date', labelDualTypeDualGain: 'Covered Gain', labelDualTypeDualDip: 'Buy The Dip', labelDualTypeDualBegin: 'Dual Investment', labelDualTypeAll: 'Dual Investment', labelDualAutoCancelConfirm: 'Disable Auto Reinvest', labelDualAutoCancelOrder: 'Cancel', labelDualAutoCancelConfirmDes: 'Are you sure about disable auto reinvest? If disabled, there will be no new orders after the next settlement.', labelDualModifySettlementDateDialog: 'Next Settlement Date', labelDefiRate: 'Rate', labelDefiLido: 'Lido', labelDefiRocketPool: 'Rocket Pool', labelDualIsHigh: 'is significantly higher', labelDualIsLow: 'is significantly lower', labelDualAutoAlert: "The current price of {{base}} is {{currentPrice}} {{quote}}, which {{method}} than the price you've set for Auto Reinvest. This may result in a lower APY for your next settlement. You can adjust the price for Auto Reinvest.", labelDualAutoDuration: 'Longest Settlement Date <1>', labelInvestDualGainTitle: 'What is Covered Gain?', labelInvestDualGainGuid: '

    Covered Gain is an investment strategy to sell digital assets at your Target Price and earn interest while waiting.

    ' + '

    On the Settlement Date, there can be 2 scenarios:

    ' + '
    1. Market Price > Target Price
    2. ' + '
    3. Market Price ≤ Target Price
    ' + '
    Market Price > Target Price
    ' + '

    Your original investment and earned interest will be sold at the target price.

    ' + '

    This order is then closed regardless of whether "Auto Reinvest" is enabled or not.

    ' + '
    Market Price ≤ Target Price
    ' + '

    Your original investment and earned interest won’t be sold.

    ' + '

    If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully sell crypto at your desired price or disable the feature.

    ' + '
    Auto Reinvest
    ' + '

    When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully sell your crypto at your Target Price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

    ' + '

    Sell Price: the Target Price at which you want to sell your crypto.

    ' + "

    Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

    ", labelInvestDualDipTitle: 'What is Buy The Dip?', labelInvestDualDipGuid: '

    Buy The Dip is an investment strategy to buy digital assets at your Target Price and earn interest while waiting.

    ' + '

    On the Settlement Date, there can be 2 scenarios:

    ' + '
    1. Market Price > Target Price
    2. ' + '
    3. Market Price ≤ Target Price
    ' + '
    Market Price > Target Price
    ' + '

    Your original investment and earned interest won’t be converted. Earned interest is in USDC or USDT.

    ' + '

    If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully buy crypto at your desired price or disable the feature.

    ' + '
    Market Price ≤ Target Price
    ' + '

    Your original investment and earned interest will be converted at the Target Price.

    ' + '

    This order is then closed regardless of whether "Auto Reinvest" is enabled or not.

    ' + '
    Auto Reinvest
    ' + '

    When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

    ' + '

    Buy Price: the Target Price at which you want to buy crypto.

    ' + '

    Sell Price: the Target Price at which you want to sell crypto.

    ' + "

    Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

    ", labelAssetDualInvests: 'Dual Investment', labelTxGuardian_upgrade_contract: 'upgrade contract', labelTxGuardian_approve_token: 'approve token', labelContactNameExisted: 'Name already exists', labelContactAddressExisted: 'Address already exists', labelL1toL2ThirdPartOn: 'On-ramp Crypto', labelL1toL2ThirdPartOff: 'Off-ramp Crypto', labelTargetRedpacketOption1: 'Option 1', labelTargetRedpacketCreateTitle: 'The following steps are required to create a new Exclusive Red Packet', labelTargetRedpacketCreateStep1: '1. Select the type of red packet to be sent.', labelTargetRedpacketCreateStep2: '2. Number of red packets to be sent/transmission time', labelTargetRedpacketCreateStep3: '3. Designated Red Packet Recipients', labelTargetRedpacketCreateStep4: '4. Notification display for red packet recipients', labelTargetRedpacketNoRedpacket: 'You do not have an existing wallet yet', labelTargetRedpacketNoRedpacketDes: 'If your prepared but unaddressed Red Packets will be displayed here !', labelTargetRedpacketOption2: 'Option 2', labelEarnVaultTitle: 'Loopring DeFi Assets', labelDualBTC: 'ETH - WBTC Dual Investment', labelDualBTCDes: 'Select based on Token and Settlement Date', labelDualTypeDualBTC: 'ETH - WBTC Dual Investment', labelDualNewPriceLessThan: 'if {{base}} < {{value}} {{quote}}', labelDualNewPriceLessOrEqualThan: 'if {{base}} ≤ {{value}} {{quote}}', labelDualNewPriceGreaterThan: 'if {{base}} > {{value}} {{quote}}', labelDualNewPriceGreaterThanOrEqual: 'if {{base}} ≥ {{value}} {{quote}}', labelInputMax: 'Max', labelTokenEnterDualToken: 'Amount', labelTokenMaxBalance: 'Available Balance', labelInvestMiniDual: 'Min {{value}}', labelDualAutoSearchingDes: 'Auto reinvesting. Searching for the product...', labelDualAutoInvestTip: 'Auto Reinvest Status:{{}}', labelDualRetryStatusSuccess: 'Auto reinvested successful. A new order has been generated for you.', labelDualRetryStatusError: 'Auto reinvest failed. Cannot find product with Buy Price of {{price}} and Longest Settlement Date of {{day}} days. ', labelDualRetryStatusRetrying: 'Auto reinvesting. Searching for the product...', labelDualRetryPending: 'Pending', labelDualRetryTerminated: 'Terminated', labelDualRetryFailed: 'Failed', labelDualRetrySuccess: 'Success', labelDualRetryStatusTerminated: 'Auto Reinvest terminated. You successfully purchased the target token.', labelInvestmentStatusSettled: 'Settled', labelInvestmentStatusDelivering: 'Delivering', labelInvestmentStatusSubscribe: 'Earning', labelDualTxsSettlement: 'Settlement', labelDualAuto: 'Auto Reinvest', labelDualAssetReInvestEnable: 'Enabled', labelDualDeliver: 'Settlement Price', labelDualAssetReInvestDisable: 'Disabled', labelUnlockErrorLine2Part1: 'If you are using an MPC wallet (such as the OKX keyless wallet), the signature may produce random results, which can lead to sign-in failures. In such cases, you may need to ', labelUnlockErrorLine2Part2: 'reset your EDDSA key', labelResetLoopringL2: 'Reset Loopring L2 keypair', labelResetlockedReset1: "Please note that if you have pending Dual Investment subscriptions, the L2 keypair reset won't take immediate effect. Your wallet's L2 account will remain locked until all subscriptions have settled. While locked, you won't be able to perform any L2 operations.", labelResetlockedReset2: 'Additionally, any pending limit orders will be canceled since they are tied to the old L2 keypair.', labelResetunlockedWithDual1: "We’ve detected that you have an active Dual Investment subscription. As a result, the L2 keypair reset won't take effect immediately. Instead, your wallet's L2 account will remain locked until all subscriptions have settled. While locked, you won't be able to perform any L2 operations.", labelResetunlockedWithDual2: 'Additionally, any pending limit orders will be canceled since they are tied to the old L2 keypair.', labelResetunlockedWithoutDual: 'Resetting the L2 keypair will cancel all pending limit orders as they tied to the old L2 keypair.', labelDualAutoReinvest: 'Auto Reinvest', labelInvestDualTutorialContent2: '
    Auto Reinvest
    ' + '

    When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

    ' + '

    Buy Price: the Target Price at which you want to buy crypto.

    ' + '

    Sell Price: the Target Price at which you want to sell crypto.

    ' + "

    Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

    ", labelNoticeTitle: 'Notifications', labelNotificationTime: '{{time}}', labelNotificationClear: 'Clear all', labelNotificationReadAll: 'Mark all as read', labelNotificationLabel: 'Notification', labelActiveL1successfulNote: 'Active {{ethereumL1}} successful', labelActiveL2successfulNote: 'Active {{loopringL2}} successful', labelActivatingL1AccountNote: 'Activating {{ethereumL1}} successful', labelL1ReceiveNote: 'Receive token in {{ethereumL1}}', labelL1SendNote: ' Send token from {{ethereumL1}}', labelL2ReceiveNote: 'Receive token in {{loopringL2}}', labelL2SendNote: 'Send token from {{loopringL2}} successful', labelL2DepositNote: 'Receive token in {{loopringL2}}', labelL2WithdrawNote: 'Send token in {{loopringL2}}', labelTotalUnRead: '{{total}} unread(s)', labelReadAll: 'View all', labelDualDefaultAutoTitle: 'Default Enable Auto Reinvest', labelDualLongestSettlementDuration: 'Modify Longest Settlement Date', labelDualLongestSettlementFixed: 'Fixed', labelDualLongestSettlementAutomatic: 'Automatic', labelDualSettingConfirm: 'Confirm', labelLuckyTokenNormal: 'Normal', labelRedPacketSendAverageTitle: 'Average Red Packet', labelBlindBoxHint: 'Each recipient will receive a sealed Red Packet which cannot be opened until the expiration date. While some recipients will receive an NFT, others will need to try their luck next time.', labelNormalRedPacketTitle: 'Normal Red Packet', labelL2DualNote: 'Dual Investment Notification', labelQuickInvest: 'Quick Invest', labelReceiveToken: 'Receive', labelETHStakingEnterPaymentToken: 'Amount', labelDefiDuration: 'Duration', labelFlexible: 'Flexible', labelEstAPR: 'Est.APR', labelTradingFeeTooltips: "The trading fee is determined by the size of your trade. Usually, it's 0.1% of your total converted amount; or the necessary fee that covers the transaction cost for small transactions.", labelHideRead: 'Hide Read(s)', labelL2NFTBurnTitle: 'Burn NFT', labelL2NFTBurnBtn: 'Burn NFT', labelENSAlert: "ENS & Address Mismatch. Before proceeding with the transfer, we recommend updating the contact's information for accuracy.", labelSendToContact: 'Send to', labelContactENSAlert: "ENS & Address Mismatch. Before proceeding with the transfer, we recommend updating the contact's information for accuracy.", labelUpdateContactsNext: 'Go to Update Contact', labelENSAddressMismatch: 'ENS & Address Mismatch', labelVault: 'Portal', labelVaultHome: 'Home', labelVaultDashboard: 'Dashboard', labelVaultRefresh: 'Refresh', labelVaultAddBtn: 'Margin Call', labelVaultJoinBtn: 'Supply Collateral', labelVaultCheckInProcessing: 'Checking Portal status', labelVaultRecord: 'Records', labelEnterToken: 'Select Token', labelVaultExitBtn: 'Settle', labelVaultLoanBtn: 'Loan', labelVaultTutorial: 'Tutorial', labelVaultTotalToken: 'Portal Token', labelVaultJoinMini: 'Minimum of {{arg}}', labelVaultJoinMax: 'Maximum of {{arg}}', labelVaultJoinNotEnough: 'Insufficient {{arg}} balance', labelVaultJoinMarginTitle: 'Add Collateral', labelVaultJoinTitle: 'Supply Collateral', labelVaultTradeBtn: 'Portal Trade', labelVaultRedeemBtn: 'Settle', labelVaultExitDes: 'Upon closing your position, all open orders will be canceled, and any outstanding debts will be automatically repaid. If the available balance of the Portal_Token is insufficient to cover the debt, Loopring will sell a portion of your assets at the market price to repay the debt. The remaining Portal_Token will be converted to the token you have staked at the current market price and returned to your {{loopringL2}} Account.', labelVaultConfirm: 'Confirm', labelVaultCancel: 'Cancel', labelVaultExitTitle: 'Settle', labelVaultSwapBtn: 'Swap', labelVaultSwap: 'Portal Trade', labelVaultType: 'Type', labelVaultTypeOpenPosition: 'Supply Collateral', labelVaultStatus: 'Status', labelVaultJoinStatus: '{{status}} {{percentage}}', labelVaultTradeStatus: '{{status}} {{percentage}}', labelVaultCollateral: 'Collateral', labelVaultReceive: 'Receive', labelVaultTime: 'Time', labelVaultExitType: 'Type', labelVaultExitTypeClose: 'Settle', labelVaultExitStatusLabel: 'Status', labelVaultExitStatus: '{{status}}', labelVaultExitStatusSuccess: 'Success', labelVaultExitTotalBalance: 'Total Balance', labelVaultExitTotalDebt: 'Total Debt', labelVaultExitTotalEquity: 'Total Equity', labelVaultExitProfit: 'Profit', labelVaultExitProfitPercentage: 'Profit Percentage', labelVaultExitTime: 'Time', labelVaultExitStatusPending: 'Pending', labelVaultExitCloseAmount: 'Redeem Token', labelVaultExitPendingDes: 'The request is being processed and is expected to take 1-2 minutes. Please be patient.', labelVaultBorrow: 'Borrow', labelVaultRepay: 'Repay', labelVaultTotalQuoteDes: 'Total Quota is the maximum amount available for you to be used as collateral.', labelVaultTokenQuoteDes: 'When provide the token as collateral, you will receive an equivalent amount of Portal Token.', labelTitleVaultDes: 'Loopring Portal can be treated as an isolated margin account allowing users to borrow/lend tokens with collateral. Other than leveraged trading, it also allows users to trade hot tokens that are not available in Loopring DEX with CEX liquidity.', labelTitleVault: 'Portal', labelVaultHomeTitle: 'Dashboard', labelVaultDashboardTitle: 'Dashboard', labelVaultTotalBalance: 'Total Balance', labelVaultOpenDate: 'Open Date:', labelVaultMarginLevel: 'Margin Level', labelVaultTotalCollateral: 'Total Collateral', labelVaultTotalDebt: 'Total Debt', labelVaultTotalEquity: 'Total Equity', labelVaultProfit: 'Profit', labelVaultCloseBtn: 'Close', labelGoVaultDashBoard: 'Go to Dashboard', labelVaultBrowserToken: 'Amount', labelVaultQuotaTooltips: 'PortalQuoteDes', labelVaultQuota: 'Total Quota', labelVaultBorrowMini: 'Minimum of {{arg}}', labelVaultBorrowNotEnough: 'Exceeded Maximum Borrowable Amount', labelVaultBorrowMax: 'Maximum of {{arg}}', labelVaultBorrowBtn: 'Borrow', labelVaultBorrowStatusLabel: 'Status', labelVaultBorrowTypeLabel: 'Type', labelVaultBorrowAmountLabel: 'Amount', labelVaultBorrowStatus: '{{status}}', labelVaultBorrowTitle: 'Borrow', labelPending: 'Pending', labelVaultRepayMini: 'Minimum of {{arg}}', labelVaultRepayNotEnough: 'Insufficient balance', labelVaultRepayMax: 'Maximum of {{arg}}', labelVaultRepayBtn: 'Repay', labelRepayQuota: 'Borrowed', labelVaultWhatis: 'What is Loopring Portal?', labelVaultDesSimple: '

    Portal: Using a third-party funds to trade assets. Traders can access additional capital, amplifying trading results.

    ', labelVaultRiskDes: '

    Loopring Portal acts as an isolated margin account, enabling users to borrow and lend tokens with collateral. Besides supporting leveraged trading, it also facilitates the trading of tokens not available on Loopring DEX by tapping into CEX liquidity depth.

    ' + "

    It's important to note that all assets in Portal are hedged on CEX, incurring an associated cost known as the Asset Holding Fee, charged on an hourly basis. We strongly recommend closing your position if it's no longer needed for trading to reduce costs.

    " + '

    In order to initiate the forced clearing of assets in the event of liquidation, Loopring will require your approval to handle the collateral asset. Additionally, after closing a position, there may be some remaining Portal tokens in your account that Loopring will need to clear before allowing you to open a new position.

      ' + '
    • There is an Asset Holding Fee for assets in Portal.
    • ' + '
    • I will grant Loopring authority to deal with my collateral in case of forced liquidation.
    • ' + '
    • I will grant Loopring the authority to clear all previously remaining Lv tokens for all new position openings in Portal.
    ', labelVaultRepayTitle: 'Repay', labelVaultRepayStatus: '{{status}}', labelVaultRepayStatusLabel: 'Status', labelVaultRepayTypeLabel: 'Type', labelVaultRepayType: 'Repay', labelVaultRepayTotalBalance: 'Amount', labelVaultVAULT_STATUS_RECEIVED: 'Received', labelVaultVAULT_STATUS_PROCESSING: 'Processing', labelVaultVAULT_STATUS_SUCCEED: 'Success', labelVaultVAULT_STATUS_FAILED: 'Failed', labelVaultVAULT_STATUS_PENDING: 'Pending', labelVaultAccountWait: 'Portal account in loading...', labelVaultInRedeemWaiting: 'Portal account is in process of settle...', labelVaultJoinSymbolNotSame: 'Only {{symbol}} allow Margin Call', labelVaultRepaySuccess: 'Success', labelVaultJoinSuccess: 'Success', labelVaultRedeemSuccess: 'Success', labelVaultBorrowSuccess: 'Success', labelVaultTradeSuccess: 'Success', labelVaultRepayInProgress: 'Processing...', labelVaultRedeemInProgress: 'Processing...', labelVaultBorrowInProgress: 'Processing...', labelVaultTradeInProgress: 'Processing...', labelVaultJoinInProgress: 'Processing...', labelVaultRepayFailed: 'Failed', labelVaultRedeemFailed: 'Failed', labelVaultBorrowFailed: 'Failed', labelVaultJoinFailed: 'Failed', labelVaultTradeFailed: 'Failed', labelLayer2HistoryVaultRecords: 'Portal', labelVaultTitleRisk: 'What is Loopring Portal?', labelProTime1h: '1h', labelProTime1d: '1d', labelProTime1w: '1w', labelStats: 'Stats', label24PriceChange: '24h Price Change', label7dPriceChange: '7d Price Change', labelVaultREDEEMPendingBtn: 'Settle in Processing', labelVaultlnredeemWaiting: 'Settle in Processing', labelJoinTitle: 'Supply Collateral', labelRedeemTitle: 'Settle', labelRedeemDesMessage: 'Please be aware that there may be a brief waiting period due to automatic liquidation. We appreciate your patience and assure you that we are working to process your request as quickly as possible. Thank you for your understanding.', labelJoinDesMessage: 'Please open a position first.', labelFailed: 'Failed', labelVAULT_COLLATERAL: 'Portal Collateral', labelTokenVaultDes: '{{symbol}} in Portal is a token backed 1:1 with {{symbol}}, bringing greater liquidity to Loopring DEX.', labelTokenWebsite: 'Website', labelTokenContractAddress: 'Contract Address', labelTokenSupply: 'Total Supply', labelMarketCap: 'Market Cap', labelTokenInfo: 'Token Info', labelTokenIntroduce: 'Introduction', labelVaultRedeemDetail: 'Details', labelCloseOutDetail: 'Settle', labelVaultTradeTitle: 'Portal Trade', labelVaultPrice: 'Price', labelVaultSell: 'Sell', labelVaultBuy: 'Buy', labelVaultFee: 'Trade Fee', labelVaultJoin: 'Supply Collateral', labelVaultMarginCall: 'Add Collateral', labelVaultMaxBrowserLabel: 'Max borrowable: ', labelVaultBorrowed: 'Borrowed', labelVaultRepayBalance: 'Total Balance', labelVaultPendingDes: 'The request is being processed and is expected to take 1-2 minutes. Please be patient.', labelVaultTotalDebtTooltips: 'Total Debt is comprised of two parts: loans and the cost incurred from utilizing the capital. The cost is determined by multiplying the Total Balance by the Funding Rate, which will accumulate on an hourly basis.', labelVaultMarginLevelTooltips: 'Margin Level provides an indication of how close your account is to experiencing a margin call or being liquidated. To avoid liquidation, you can either supply more collateral or repay borrowed positions.', labelVaultActiveLoanAlert: 'You currently have an active loan order in progress. This may affect the available tokens for trading. Please proceed with caution.', labelVaultSwapBorrowTip: 'Max is sum of Holding and Borrowable tokens. When the swap amount exceeds the available balance, it will automatically borrow tokens. This Amount is the borrowed quantity.', labelVaultSwapBorrow: 'Max:', labelVaultSwapHoldTip: 'When the swap amount exceeds the available balance, it will automatically borrow tokens. This Amount is the borrowed quantity.', labelVaultSwapHold: 'Holding:', labelTobeBorrowedtips: 'When the swap amount exceeds the available balance, it will automatically borrow tokens. This Amount is the borrowed quantity.', labelTobeBorrowed: 'To be Borrowed', labelStep1Borrow: 'STEP1: Borrow', labelStep2Swap: 'STEP2: Swap', labelVaultSwapCancel: 'You are currently borrowing tokens. \nAre you sure you want to exit the current transaction?', labelAgree: 'I know', labelTradeVaultMiniBorrow: 'The minimum borrowing amount is {{arg}}, so the amount you trade should be greater than {{arg1}}', labelVaultTradeSimpleBtn: 'Trade', labelBorrowing: 'Borrowing {{value}} {{symbol}}', labelBorrowed: 'Borrowed {{symbol}}', labelBorrowSwap: 'Borrow & Swap', labelVaultTradeInsufficient: 'Insufficient quota', labelVaultActiveLoanError: 'Due to high borrowing demand, there are currently insufficient loanable assets for {{symbol}}. You can continue trading with the tokens you hold or choose to Cancel.', labelL2NFTBurnTip: 'The NFT will be permanently sent to a burn address and unrecoverable. Please proceed with caution.', labelL2NFTBurSuccess: 'Burned {{value}} {{symbol}} successfully!', labelOpenPositionDetail: 'Supply Collateral Detail', labelMarginCallDetail: 'Add Collateral', labelBorrowDetail: 'Borrow', labelRepayDetail: 'Repay', labelTradeDetail: 'Trade', labelVaultPlacedAmount: 'Placed Amount', labelVaultExecutedAmount: 'Executed Amount', labelVaultExecutedRate: 'Executed Rate', labelVaultConvertedAmount: 'Converted Amount', labelVaultTxFee: 'Fee', labelBtradeExecutedAmount: 'Executed Amount', labelBtradeExecutedAmountTip: 'The quantity successfully processed on the CEX side.', labelBtradePlacedAmount: 'Placed Amount', labelBtradePlacedAmountTip: `The quantity you invested in 'Block Trade' that you intend to execute.`, labelBtradeExecutedRate: 'Executed Rate', labelBtradeConvertedAmount: 'Converted Amount', labelBtradeConvertedAmountTip: 'The quantity successfully converted from the CEX side', labelBtradeSettledAmount: 'Settled Amount', labelBtradeSettledAmountTip: `The quantity deposited into your Loopring L2 account. If you opt for 'Prioritize Quantity' mode and the Loopring L2 pool exhausts its quota, you may not receive the entire amount instantly. The remaining tokens will be 'swapped' at the executed price once the pool is replenished.`, labelVaultExitTypeForcedLiquidation: 'Forced Liquidation', labelVaultExecutedAmountTip: 'The quantity successfully processed on the CEX side.', labelVaultPlacedAmountTip: `The quantity you invested in 'Portal Trade' that you intend to execute.`, labelVaultConvertedAmountTip: 'The quantity successfully converted from the CEX side.', labelVaultSettledAmountTip: `The quantity deposited into your Loopring L2 account. If you opt for 'Prioritize Quantity' mode and the Loopring L2 pool exhausts its quota, you may not receive the entire amount instantly. The remaining tokens will be 'swapped' at the executed price once the pool is replenished.`, labelVaultActiveLoanError2: 'Borrowed {{value}} {{symbol}} failed.', labelVaultActiveLoanSuccessful: 'Borrowed {{value}} {{symbol}} successful.', labelVaultAmount: 'Amount', labelVaultOpenPositionDes: 'Please open a position first.' , labelHourlyInterestRate: 'Hourly Interest Rate', labelHourlyInterestRateTips: 'Interest Rate will change every hour based on current market conditions. Interest will accrue as soon as tokens are borrowed and it will continue to accrue every hour.', labelVaultMarginLevelTooltips2: 'Margin Level = (Balance + Collateral) / Total Debt', labelVaultMarginLevelTooltips3: 'Default Margin Level is 999.00', labelVaultMarginLevelTooltips4: 'Low Risk: Margin Level ≥ 1.5', labelVaultMarginLevelTooltips5: 'At Low Risk, your collateral is relatively sufficient. You should mainly focus on whether the market fluctuates violently and causes liquidation.', labelVaultMarginLevelTooltips6: 'Middle Risk: 1.15 ≤ Margin Level < 1.5', labelVaultMarginLevelTooltips7: 'At Middle Risk, it is recommended that you always pay attention to changes in market conditions and add collateral or repay borrowed tokens to reduce the risk of liquidation.', labelVaultMarginLevelTooltips8: 'High Risk: {{liqMarginLevel}} ≤ Margin Level < 1.15', labelVaultMarginLevelTooltips9: 'At High Risk, it is strongly recommended that you add collateral or repay borrowed tokens immediately to reduce the risk of liquidation.', labelVaultMarginLevelTooltips10: 'Liquidation: Margin Level < {{liqMarginLevel}}', labelVaultMarginLevelTooltips11: 'When Margin Level is lower than {{liqMarginLevel}}, the position will be liquidated.', labelVaultCollateralManagement: 'Collateral Management', labelGuardianCodeNetworkError: 'Network not match', labelGuardianCodeAccountError: 'guardian not match current account', labelGuardianCodeFormatError: 'wrong data format', labelGuardianCodeOwner: 'Owner', labelGuardianCodeRequestedWallet: 'Requested wallet', labelGuardianCodeRequestType: 'Request Type', labelGuardianCodeNetwork: 'Network', labelGuardianCodeRequester: 'Requester', labelGuardianCodeAuthorizationResult: 'Authorization Result', labelGuardianCodeApproved: 'Approved', labelGuardianCodeReject: 'Reject', labelGuardianCodeApprove: 'Approve', labelGuardianCodeShare: 'Share', labelGuardianCodeNext: 'Next', labelGuardianCodeEnterApprovalCode: 'Enter Approval Code', labelGuardianCodeEnterApprovalCodeHint: 'Please enter the Approval Code', labelGuardianCodeConfirmationDes: `Kindly approve the operation request from the wallet you are safeguarding. The approval process is fee-free. For added security, we request you enter the authorization code generated by the requester. If you haven't received this code, kindly synchronize with the requester for the necessary information.`, labelGuardianCodeSharingDes: `Please send the QR code to the requester`, labelGuardianCodeSharingApprovalRequest: 'Approval Request', labelGuardianNotWhoIProtect: 'Request not from who I protect', labelEarnAssetDefiPortfolio: 'DeFi Portfolio', labelEarnDepositDes: `There are no assets in your Loopring DeFi account yet. Please deposit a supported token to start exploring the DeFi world.`, labelEarnAssetTokens: 'All Tokens', labelBlockTradeTutorial: 'Block Trade Tutorial', labelAccountCreatedHint: 'Your Loopring DeFi account has been created. Please sign to complete the process.', labelFrozen: 'Frozen', labelBTradeTutorial: ` Block Trade offers a secure and trustless way for users to swap tokens using CEX liquidity. The trades happen exclusively between designated entities, ensuring that the existing liquidity of the DEX remains unaffected. There is no price impact to other DEX users as a result of the transaction. `, labelLearnMore: 'Learn More >', labelLearnMore2: 'Learn More', labelInvestDualDes: 'Buy the dip or sell the covered gain while earning a high yield', labelPortalDes: 'Loopring Portal can be treated as an isolated margin account allowing users to borrow/lend tokens with collateral. ', labelBtradeDes: 'Offers a secure and trustless way for users to swap tokens using CEX liquidity.', labelAPY: 'APY', labelAction2: 'Action', labelTotalBalance: 'Total Balance', labelBalance: 'Balance', depositLabelEnterTokenEarn: 'Select Token', labelLaunch: 'Launch', labelSmartWalletDepositError1: 'Currently, only EOA wallets are supported.', labelSmartWalletDepositError2: 'Loopring Smart Wallet support is coming soon ', labelSmartWalletDepositError3: 'stay tuned for updates!', labelDustCollectorDetail: 'Dust Collector', labelVaultRedeem: 'Redeem Collateral', labelVaultRedeemNotEnough: 'Exceeded the maximum {{arg}} redemption value', labelVaultJoinRedeem: 'Redeem Collateral', labelVaultCollateralDetails: 'Collateral Details', labelVaultMaximumCredit: 'Maximum Credit', labelVaultMaximumCreditDes: 'Maximum Credit means the maximum amount of money you can borrow from Portal based on your collateral.', labelVaultMaximumCreditFormula: 'Maximum Credit = Sum ( Token_MarketPrice * Token_Amount * Token_PriceFactor ) * Maximum_Leverage', labelVaultPriceFactor: 'The Price Factor of each collateral', labelVaultMaximumLeverage: 'Maximum Leverage', labelVaultLeverage: 'Leverage', labelVaultAvailableBorrow: 'Borrowing Limit', labelVaultLeverageRisk1: '· Selecting higher leverage will increase your liquidation risk.', labelVaultLeverageRisk2: '· Borrowing Limit is based on the upper limit corresponding to the leverage you choose. The total value of all your borrowed tokens will not exceed the Borrowing Limit.', labelVaultLeverageRisk3: '· Maximum Credit is the maximum amount you can borrow based on your collateral.', labelVaultDebt: 'Debt Details', labelVaultDebtDetails: 'Details', labelVaultTotalBorrowed: 'Total Borrowed', labelVaultTotalFundingFee: 'Total Funding Fee', labelVaultValueEst: 'Value (est.)', labelVaultDustCollectorDes: `The accumulated dust will be converted into corresponding USDT. If you do not have any USDT to be repaid, the converted amount will be deposited into your portal's USDT. `, labelVaultConvert: 'Convert', labelVaultMarginLevelAlert: 'Your margin level has reached a critical point. Please review your positions and considering adding additional collateral. Failure to address the margin call may result in forced liquidation.', labelVaultDetail: 'Detail', labelVaultDefaultLeverage: 'Default Leverage', labelVaultHoldingCollateral: 'Holding Collateral', labelVaultRedeemAlert: 'Collateral valued at more than twice your total debt can be withdrawn from your Portal account.', labelTotalValue: 'Total Value', labelVaultRepayment: 'Payment', labelDetails: 'Details', labelVaultErrorOccurred: 'An error has occurred. Please try again later.', labelVaultDetails: 'Details', labelVaultNoDust: 'No Dust to Collect', labelVaultJoinAdd: 'Add Collateral', labelVaultChangeLeverageFailed: 'Change Leverage Failed', labelVaultChangeLeverageFailedTooSmall: `Leverage is smaller than permitted. Insufficient Available Borrow amount.`, labelVaultDustCollectorUnavailableTitle: 'Feature Unavailable', labelVaultDustCollectorUnavailableDes: 'This feature is temporarily unavailable. Please try again later.', labelVaultMaximumCreditDesLong: 'Maximum Credit means the maximum amount of money you can borrow from Portal based on your collateral. It is calculated by taking the total value of your collateral, adjusted for price factor and the maximum leverage.', labelVaultMarginLevelDes: 'Margin Level provides an indication of how close your account is to experiencing a margin call or being liquidated. To avoid liquidation, you can either supply more collateral or repay borrowed positions.', labelLayer2HistoryTaikoLockRecords: 'Taiko Farming', labelL2TaikoFarming: 'Taiko Farming', labelTaikoFarmingNotChecked: 'Please check the above box', labelTaikoFarmingUnlock: "Unlock the Value of Your Locked TAIKO !", labelTaikoFarmingMintInfo: "Mint lrTAIKO and use it in Loopring DeFi at zero cost!", labelTaikoFarming: "Taiko Farming", labelTaikoFarmingDescription: "Earn Trailblazers points 60x faster by locking TAIKO and participating in Taiko Farming. Please note that TAIKO can only be unlocked after the end of Trailblazers Campaign Season 2.", labelLock2: "LOCK", labelEnterPaymentToken: "Enter payment token", labelMax: "Max", labelSelectToken: "Select token", labelAcknowledgeAndProceed: "I acknowledge and would like to proceed.", labelUnlockAfterSeason2: "You can unlock your TAIKO tokens only after the end of Trailblazers Season 2.", labelCurrentLockedPosition: "My Current Locked Position", labelTotalPoints: "Total Points", labelDailyEarningPoints: "Daily Earning Points", labelTaikoFarmingBannerTitle: "Taiko Farming", labelTaikoFarmingBannerDes: "Earn Trailblazers points 60x faster by locking TAIKO and participating in Taiko Farming. Please note that TAIKO can only be unlocked after the end of Trailblazers Campaign Season 2.", labelTaikoFarmingQuestion: "Worried about missing trading or earning opportunities when locking your valuable TAIKO?", labelTaikoFarmingExplanation: "Loopring lets you mint lrTAIKO at a 1:1 ratio, allowing you to freely use it across all trading and earning options within Loopring DeFi. Plus, you'll continue farming Trailblazers points.", labelTaikoFarmingComingSoon: "The lrTAIKO mint feature is coming soon!", labelTaikoFarmingPortalIntegration: "The first DeFi integration will be with Portal, allowing you to use lrTAIKO as collateral for leveraged trading on select hot tokens. You'll be able to go LONG or SHORT on your favorite tokens based on market conditions.", labelTaikoFarmingStayTuned: "Stay tuned for the upcoming release—more exciting use cases are on the way!", labelTaikoFarmingExploreUseCase: "Take a tour to explore all the current Loopring DeFi use cases ", labelTaikoFarmingHereLink: "", labelTaikoFarmingDepositAnytime: ". You can deposit assets into Loopring DeFi anytime and start enjoying the experience!", labelTotalPointsDes: 'Please note that Trailblazers points are awarded directly by the Taiko team. The Loopring dashboard provides a reference for the points you may receive from Taiko, but if there are any discrepancies, the official results from Taiko will be the final and trusted source.', labelInvestType_TAIKOFarming: 'Taiko Farming', labelTaikoFarmingTrailblazerBooster: 'Trailblazers Booster', labelTaikoFarmingPointsToBeTrackedRetroactively: 'Points to be tracked retroactively', labelTaikoFarmingPointsToBeTrackedRetroactivelyDes: 'Loopring is actively working with the Taiko team to integrate Trailblazers points collected through Taiko Farming. You are guaranteed to receive 60x Trailblazers points by participating in this campaign. However, it may take a few days for the points to be retroactively tracked in the Taiko Trailblazers Leaderboard.', labelTaikoFarmingLockSuccess: 'TAIKO Locked Successfully', labelTaikoFarmingUnlockValue: 'Unlock The Value Of Your Locked TAIKO To Keep Trading Or Earning', labelTotalPortfolio: 'Total Portfolio', labelLoopringDeFiIs: 'Loopring DeFi is expanding to various EVM-compatible networks using its trustless, time-tested ZK-Rollup protocol. The first deployment will be on Taiko. Join us for an exciting journey ahead!', labelLoopringDeFiIs21: 'Loopring DeFi is expanding to various EVM-compatible networks using its trustless, time-tested ZK-Rollup protocol. The first', labelLoopringDeFiIs22: 'deployment will be on Taiko. Join us for an exciting journey ahead!', labelLoopringDeFiAssets: 'Loopring DeFi Assets', labelSendAssetToTaikoAccount: 'To Taiko', labelL1toL2WaitForAuthTaikoEarn: 'Your {{symbol}} is being deposited into the {{loopringL2}} Protocol and may take a few moments to complete.', labelTaikoFarmingMintSuccess: 'Success', labelTime: 'Time', labelTaikoFarmingMintInfoDes: 'You can freely use lrTAIKO across all trading and earning options within Loopring DeFi.', labelTaikoFarmingMintVisitPortal: 'Visit Portal to use lrTAIKO as collateral to earn >', labelLockTAIKO: 'Lock TAIKO', labelTaikoLockEarn: 'Earn Trailblazers points with up to a 60x multiplier by locking your TAIKO.', labelTaikoLockEnjoyComplete: 'Enjoy complete control over your locking duration by choosing any period that suits you. Trailblazers points will be awarded linearly based on the length of your lock. Plus, your locked TAIKO remains usable within Loopring’s DeFi ecosystem! Use it as collateral to continue trading and earning, maximizing your capital efficiency even while it’s locked.', labelTaikoFarmingMintRiskReminder: 'Risk Reminder: You can use minted lrTAIKO as collateral across other Loopring DeFi utilities. However, if you incur any losses during your investment, those losses will be deducted from your locked TAIKO upon unlocking.', labelCompleteSignIn: 'Complete Sign in', labelSignIn: 'Sign in', labelComplete: 'Complete', labelStartMintlrTAIKO: 'Start Mint lrTAIKO', labelCompleteSignInDes: 'You need to activate Loopring DeFi account first before minting. Please sign in to proceed.', labelLocked: 'Locked', labelTAIKO_FARMING: 'Taiko Farming', labelDefiCloseCian: 'The ciETH subscription has been deprecated. We recommend redeeming your ciETH for ETH as soon as possible. You can also explore other ETH staking portfolios for alternative options.', labelUnlockErrorSupport1: 'The inability to unlock your wallet is most likely due to network conditions or browser-related issues. This can often be resolved by refreshing the page.', labelUnlockErrorSupport2: 'If the problem persists after multiple attempts, please contact us directly at support@loopring.io and provide your wallet information. Our engineers will assist you in resolving the issue as soon as possible. Thank you for your understanding.', labelVaultMarketTitle: 'Market', labelUnlockErrorLine1Part1: 'The inability to unlock your wallet is most likely due to network conditions or browser-related issues. In many cases, simply refreshing the page can resolve the issue. If the problem persists after multiple attempts, try manually ', labelUnlockErrorLine1Part2: 'resetting your Loopring EDDSA keypair ', labelUnlockErrorLine1Part3: 'to resolve the issue', labelVaultCloseShort: 'Close Short', labelVaultTradeTabTitle: 'Trade', labelVaultTradeTabDes: 'An isolated margin account', } ================================================ FILE: packages/common-resources/static-resources/src/i18n/en_US/error.ts ================================================ export default { labelConnectUsSimple: 'Please <1>contact us.', labelConnectUs: 'If you believe this is indeed a bug, please <1>  contact us. We would appreciate your feedback.', errorBase: 'Oops! Something went wrong.', errorTimeout: 'Oops! Something went wrong at network.', errorLoading: 'Loading! Please wait...', error404: "404! Page can't be reached.", errorMaintain: 'System update! Please wait...', errorMessageNoNetwork: 'Oops! Something went wrong at service.', errorUnknown: 'Unknown Error', errorOnFromSubmit: 'Submit request Info has Error!', errorWrongAccount: 'Error on account Info!', errorWrongToken: 'Error on Token Info!', errorWrongApikey: 'Error on signature or no API key.', errorWrongBalance: 'Insufficient balance', errorMinimumOrder: 'Trade amount is minimum', errorMaximumOrder: 'Trade amount is maximum', errorOnFrozen: 'Some of your assets are frozen, please try again later.', errorAboutFee: 'Fee token is not enough or incorrect.', errorProviderError: 'Global ethereum is not {{name}}, please disable your other Wallet Plugin.', errorOnStorageId: 'Error on wrong StorageId', errorOnNoRecipient: 'Error on no Receiver', errorNoMarket: 'Market is not supported.', errorOnGas: 'Error on Gas Info', errorOnCancelOrders: 'Error on cancel order', errorInvalidHash: 'Invalid Hash', errorInvalidOrderId: 'Invalid OrderId', errorOnRate: 'Error on submit rate value', errorForExistOrder: 'Order does not exist', errorOrderExpired: 'Order has expired', errorDisableOtherWalletForCurrent: 'Global ethereum is not {{name}}, please disable other Wallet Plugin.', errorGenerateEddsa: 'Generate EdDSA key has failed.', errorNotInstallGME: 'Please install the Gamestop Wallet browser extension. Then create, deposit, and activate the wallet before connecting to Loopring.io.', errorLinKWalletApp: '<0>app market', errorIpfsDidToNftidError: 'IPFS Gateway Error', errorMintOverlap: 'You have already minted this NFT. Please check your Wallet', errorJSONStringify: 'Wrong JSON format', errorCollectionMetadataNoTileUri: 'Tile uri is required', errorCollectionNoName: 'Name is required', errorCollectionSameName: 'Collection called {{name}} is existe', errorCollectionInfo: "We've detected this collection is not yours or the Contract Address don't match", errorCollectionNoSupport: 'Only {{l2symbol}} collection is allow to mint', errorCollectionNotReadable: 'Read Collection Info Failed', errorIpfsTimeout: 'IPFS Gateway timeout, please try again', errorRampNoInstance: 'Ramp Widget had out-off service, please re-order it.', errorDualExpired: 'The order has expired.', errorPrivateKey: 'Signature Wrong private key', errorNoResponse: 'No response!', errorMinError: 'Minimum of {{value}}', errorMaxError: 'Max of {{value}}', errorLengthLimit: 'Length is limit', errorRedpacketEmpty: 'Red Packet is Empty', errorRedpacketClaimed: 'You already opened Red Packet', errorRedpacketClaimOut: 'You opened the Red Packet Too Later', errorRedpacketClaimTimeOut: 'You opened the Red Packet Too Later', errorOffRampExpired: 'The order has expired. please create a neworder ', errorNFTRedPacketMaxError: 'Total Amount cannot exceeds {{value}} NTFs', errorBetaEnv: 'Oops! This site is not released yet and could contain bugs. Please use it with caution!.', errorProviderErrorUnknown: 'Unknown Error', errorNoGamestopExtension: 'User not installed GameStop extension', errorSwitchEthereum: 'wallet switch Ethereum Chain is not allowed', errorEthereumNotMetamask: 'Global ethereum is not MetaMask', errorGamestopNoChainChange: 'Gamestop extension no switch Ethereum Chain method', errorHadUnknownCollectionDes: 'As the creator, you will be able to generate collection information for those NFT minted earlier that belong to nowhere. And once done, the other people holding your NFT will be able to view those NFT with proper collection information via loopring.io and loopring wallet. <1>Go', errorGuardianRouterError: 'Oops! Guardian site moved to guardian.loopring.io.', errorAddressCheckError: 'Oops! Something went wrong on address checking processing ', errorContactExisted: 'Contact address already exists', errorContactNameExisted: 'Name already exists', errorContactLimit: 'Unable to Add Contact,You have reached the maximum limit of 1500 contacts', errorContactOverLimit: 'Unable to Add Contact,You have reached the maximum limit of 1500 contacts', labelContactsAddSuccess: 'Add Contact Succeed', labelContactsDeleteSuccess: 'Delete Contact Succeed', labelContactsEditSuccess: 'Edit Contact Succeed', labelContactsSendSuccess: 'Send Succeed', labelContactsCopySuccess: 'Copied to Clipboard', labelContactsAddFailed: 'Add Contact Failed', labelContactsDeleteFailed: 'Delete Contact Failed', labelContactsEditFailed: 'Edit Contact Failed', labelContactsSendFailed: 'Send Failed', errorOrderFailed: 'Order status Failed', errorAddressBurnNft:"Oops! Something went wrong on checking burn avaiable" } ================================================ FILE: packages/common-resources/static-resources/src/i18n/en_US/index.ts ================================================ import common from './common' import layout from './layout' import tables from './tables' import error from './error' import landPage from './landPage' import webEarn from './webEarn' export default { common, layout, tables, error, landPage, webEarn } ================================================ FILE: packages/common-resources/static-resources/src/i18n/en_US/landPage.ts ================================================ export default { labelH1Title: 'Ethereum’s First zkRollup Layer 2', labelH1TitleDetail: 'Fast, Secure, and 100x Lower Fees', labelH1TitleWallet: 'Loopring Wallet', labelH2TitleWallet: 'FREEDOM AT YOUR FINGERTIPS', labelProtocol: 'loopring offers', labelStartTrade: 'start trading', labelTitleDEX: 'Loopring Dex', labelTradeVolume: 'Volume', labelTradeUser: 'Users', labelTradeTVL: 'TVL', //Total Value Locked labelTradeNofTrades: 'Trades', labelZeroKpt: 'Powered by Zero-knowledge Proof', labelLoopringWallet: 'LOOPRING WALLET', labelBtnLearnMore: 'LEARN MORE', labelSafety: 'Ultimate Security', labelLowCost: 'Low Transaction Fees', labelFastTransfer: 'High Throughput', labelSuperpowers: 'Ready for Developers', describeSafety: 'Assets on Loopring L2 are equally secure as they are on the Ethereum mainnet.', describeLowCost: 'Transaction fees are reduced to 1/30 - 1/100 of the fees on the Ethereum mainnet.', describeFastTransfer: 'Loopring L2 can settle ~2000 transactions per second with near instant finality.', describeSuperpowers: "Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's time-tested zkRollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.", labelBtnStart: 'LAUNCH LOOPRING L2 APP', labelBtnDeveloper: 'Developer Documentation', labelFirstWallet: 'BETTER THAN BEING SMART', labelFirstWalletDetail: 'Smart wallets enable limitless extensibility over EOA wallets, but Loopring Wallet takes one step further with native zkRollup layer 2 integration. Now you can transact directly on Loopring L2, faster and cheaper.', labelWalletSecureH1: 'SECURE, at its core', labelWalletSecureH2: 'With social-recovery and guardians', labelWalletSecureDetail: 'Completely self-custodial, only you control your assets. You can choose people, institutions, and hardware that you trust to be the guardians of your wallet. You can also set quota for daily transfers, and lock it down if ever needed.', labelWalletIdentityH1: 'OWN YOUR IDENTITY', labelWalletIdentityH2: 'Your wallet is your continuous Web3 identity.', labelWalletIdentityDetail: 'The Loopring Wallet decouples identity and security. You can choose your wallet name and address, and maintain this identity forever with renewable transaction signing keys.', labelWalletUsageH1: 'EASY TO USE', labelWalletUsageH2: 'with zkRollup scaling solution', labelWalletUsageDetail: "The Loopring Wallet integrates Loopring's Layer 2 zkRollup technology to greatly increase speeds and lower fees, while inheriting the same security guarantees as the Ethereum mainnet.", labelWalletFutureH1: 'FORWARD LOOKING', labelWalletFutureH2: 'Easily upgradable to adapt future scenarios', labelWalletFutureDetail: 'Based on smart contracts, the Loopring Wallet adopts a pluggable, modular design which can be continuously upgraded under your authorization to use future standards and new technologies.', labelWalletUnleashed: 'ETHEREUM UNLEASHED', labelWalletUnleashedDetail: 'Your mobile gateway to Ethereum DeFi, collectibles, and more.', labelLaunchApp: 'View Dapps', labelLaunchMobileApp: 'Exchange', labelUpgradeHide: 'Loopring System Service Upgrade: Loopring will perform a temporary system upgrade starting at 2021/12/20 2:00pm (UTC+8), which will take approximately 2 hours.', labelUpgradeShow: 'Loopring System Service Upgrade: Loopring will perform a temporary system upgrade starting at 2021/12/20 2:00pm (UTC+8), which will take approximately 2 hours. During this period, the following relayer services will be suspended: deposits, withdrawals, trading and wallet creation. Deposits that are not completed before the start of the upgrade will be processed after the completion of the upgrade. We apologize for any inconvenience this may cause, and thank you for your patience! Please note: The 2 hours is our best estimation and may vary. As usual, we will maintain regular communication on the progress of the upgrade on our Twitter and Discord.', labelGoGuardian: 'Manage Security', labelHaveWallet: 'Already have a wallet?', labelMainDes: 'Unleash the Power of DeFi and Advanced Trading on \n Top of an App-Specific ZK-Rollup', labelEthereum: 'Ethereum ', labelGoDapps: 'Go Dapps', labelNavPro: 'Loopring pro', labelNavEarn: 'Loopring DeFi', labelNavWallet: 'Wallet', labelNavLanuch: 'View Dapps', labelLoopringSmartWallet: 'Loopring Smart Wallet', labelLoopringSmartWalletDes: "Ethereum's most secure wallet, fully integrated with advanced trading functionality and innovative Earn products, unlocking the true potential of Layer 2.", labelEthereumUnleashed: "<0>Ethereum's First zkRollup <1>Layer 2", labelGatewayToEthereum: 'Your gateway to Ethereum, DeFi, NFTs, and Financial Freedom.\n Fast, Low-cost, & Secure', labelTitleLite: 'Loopring DeFi', labelLoopringProtocol: 'Loopring Protocol', labelLoopringProtocolDes: "The world's first ZK-rollup implementation designed to scale Ethereum,\n fully optimized for trading.", labelUltimateSecurity: 'Ultimate Security', labelUltimateSecurityDes: 'Assets on Loopring L2 are equally secure as they are on the Ethereum mainnet.', labelLowTransactionFees: 'Low Transaction Fees', labelLowTransactionFeesDes: 'Loopring performs most operations, including trade and transfer settlement, off the Ethereum blockchain. This dramatically reduces gas consumption and overall transaction cost to small fractions of comparable on-chain cost.', labelHighThroughput: 'High Throughput', labelHighThroughputDes: 'Loopring L2 can settle ~2000 transactions per second with near instant finality.', labelReadyForDevelopers: 'Ready for Developers', labelReadyForDevelopersDes: "Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's battle-tested zkRollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.", labelFeatureDes8: 'Loopring Layer2', labelFeatureDes8_2: 'Crypto exchange on the go', labelGo: 'Go', labelTitleLiteDes: 'Revolutionizing Decentralized Finance with Cutting-Edge\nEarning and Trading Solutions.', labelInvestStakeLRCDes: 'Lock your LRC for a minimum duration to earn interest and redeem at any time.', labelInvestDualDes: 'Buy the dip or sell the covered gain while earning a high yield.', labelInvestLeverageETHDes: "Boost your earnings with CIAN protocol's leveraged ETH staking strategy.", labelInvestDefiDes: 'Earn steady income while holding! Stake ETH to accumulate daily rewards.', labelInvestAmmDes: 'Participate in farming pools, earn additional protocol fee incentives for HOT pools.', labelProductsTitleDes: 'A wide variety of trading tools to choose from', labelProductsTitle: 'Trading Products', labelSpotDes: 'Full access to all trading tools', labelSpot: 'Spot', labelSwapDes: 'Intuitive trading interface', labelSwap: 'Swap', labelBlockTradeDes: 'Trade via CEX liquidity', labelBlockTrade: 'Block Trade', labelFiatDes: 'On & Off Ramp Solutions', labelFiat: 'Fiat', labelNFTCollections: 'Manage and Display Your NFT Collections', labelNFTCollectionsDes: 'Collaborate on a strategic plan to integrate \n NFTs into your ecosystem', labelRedPackets: 'Red Packets', labelRedPacketsDes: 'Explore the various use cases of our revolutionary Red Packets! Send token or NFT gifts directly or gamify the experience with blind boxes.', labelEndProductTitle: 'Earn Products', labelEndProductTitleDes: 'Choose from a variety of financial products', labelSupportedNetworkDes: 'Supported networks: Ethereum & Taiko', labelLaunch: 'Launch', labelLeadingOnChainStructuredProduct: 'Leading On-Chain Structured Product', labelLeadingOnChainStructuredProductDes: 'Buy the dip or sell at a higher price—all while earning high yields.', labelTradeWithCEXLiquidity: 'Trade with CEX Liquidity', labelTradeWithCEXLiquidityDes: 'Effortlessly trade tokens with near-zero slippage, leveraging a unified liquidity pool that combines both CEX and DEX liquidity to generate the best execution price.', labelUnlockThePowerOfLeveragedTrading: 'Unlock the Power of Leveraged Trading', labelUnlockThePowerOfLeveragedTradingDes: 'Seamlessly borrow tokens against your collateral, paving the way for leveraged trading opportunities. By bridging decentralized and centralized exchanges, Loopring Portal broadens your trading horizon, granting access to tokens beyond the Ethereum network.', labelEthereumOnly: 'Ethereum-Only', labelLoopringLayer2: 'Loopring Layer 2', labelLoopringLayer2Des: 'Unleash the Power of DeFi and Advanced \n Trading on Top of an App-Specific ZK-Rollup', labelTrade: 'Trade', labelTradeDes: 'A wide variety of trading methods to choose from', labelEarn: 'Earn', labelEarnDes: 'Choose from a variety of financial products', labelNFT: 'NFTs', labelNFTDes: 'Manage and display your NFT collections', labelRedPackets2: 'Red Packets', labelRedPacketsDes2: 'Explore the possibilities with our revolutionary Red Packets', labelLoopringSmartWalletDes2: 'Trade on the go — anytime, anywhere', labelExplore: 'Explore', labelLoopringProtocolDes2: 'The world\'s first ZKRollup implementation designed to scale Ethereum fully optimized for trading', labelProvenAppSpecificZKRollup: 'Proven App-Specific ZK-Rollup', labelProvenAppSpecificZKRollupDes: `Leverage the world's first ZK-Rollup implementation to unlock the full potential of DeFi and advanced trading`, labelAuditedAndSecure: 'Bulletproof Security', labelAuditedAndSecureDes: 'Assets on the Loopring Protocol are just as secure as they are on the underlying networks where the protocol is deployed', labelBringingCEXToDeFi: 'Bringing CeFi to DeFi', labelBringingCEXToDeFiDes: 'Seamlessly bring CeFi use cases into the DeFi world in a truly trustless manner', labelReadyForDevelopersDes2: `Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's battle-tested ZK-rollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.`, } ================================================ FILE: packages/common-resources/static-resources/src/i18n/en_US/layout.ts ================================================ export default { labelMarkets: 'Markets', labelTrade: 'Trade', labelLiquidity: 'Pools', labelMining: 'Farm', labelLayer2: 'L2 Wallet', labelWallet: 'Wallet', labelBridge: 'L2 Bridge', labelBridgeDes: 'Ethereum Layer 1 to zkRollup Layer 2 Bridge', labelGuardian: 'Guardian', labelGuardianDes: 'Loopring Wallet smart contracts Guardian Security Specs', labelZkRollupLayer2: 'zkRollup Layer 2', labelSmartWallet: 'Smart Wallet', loopringDomain: 'Loopring L2 - APP ', labelClassic: 'Swap', labelClassicDescription: 'Intuitive trading interface', labelAdvanced: 'Order Book', labelFiat: 'Fiat', labelFiatDescription: 'On-ramp/Off-ramp crypto', /// Sell labelAdvancedDescription: 'Full access to all trading tools', labelDownloadApp: 'Download Loopring Wallet Mobile Application', labelDownloadAppTitle: 'Download Loopring Wallet', labelDownloadBtn: 'Go to Loopring Website', labelNotification: 'Notification Information', themeSetting: 'Theme setting', languageSetting: 'Language setting', labelConnectWallet: 'Connect Wallet', notificationApprove: 'Approving', notificationPending: 'Pending', labelMyTrades: 'My Trades', labelRecentTrades: 'Recent Trades', // layer2 submenu labelAssets: '{{l2Symbol}} Assets', labelTransactions: 'Deposit/Withdraw', labelTrades: 'Trade History', labelHistory: 'Transactions', labelOrder: 'Orders', labelReward: 'Rewards', labelRedPacket: 'RedPacket', labelAmmRecords: 'AMM records', labelAmmRecordsDes: '(Add & Remove)', rewards: 'Rewards', labelOrders: 'Orders', selectLanguage: 'select language', labelPools: 'Pools', labelAmmMining: 'AMM Mining', labelMyLiquidity: 'My Liquidity', labelOrderBookMining: 'Orderbook Mining ', labelMakerRebates: 'Maker Rebates', labelSetting: 'Setting', labelSecurity: 'Security', labelVipPanel: 'VIP', labelContactsPanel: 'Contacts', titleLoopring: 'Loopring', labelLoopringDescribe: 'zkRollup Exchange and Payment Protocol', labelWrongNetwork: 'Wrong network', labelYoutube: 'Youtube', labelWeibo: 'Weibo', // Footer labelFooterAbout: 'About', labelkeyOrg: 'Loopring.org', labelkeyTerms: 'Terms of Service', labelkeyPrivacy: 'Privacy Policy', labelkeyNews: 'Blog', labelkeyRisks: 'Risk Disclosure', labelFooterPlatform: 'Platform', labelkeyFees: 'Fees', labelkeyVIP: 'VIP Program', labelkeyReferrals: 'Referrals', labelkeyTokenListing: 'List a Token', labelkeyCreatorGrants: 'Creator Grants', labelkeyL2Explorer: 'Layer 2 Explorer', labelkeySubgraph: 'Subgraph', labelkeyBugBounty: 'Bug Bounty', labelFooterSupport: 'Support', labelkeyFeedback: '❤️ Submit a Request', labelkeyGuardian: 'Wallet Guardian', labelkeyLoopringTutorial: 'Loopring Tutorial', labelkeyCommunityDocs: 'Community Docs', labelkeySupportCenter: 'Support Center', labelFooterDevelopers: 'Developers', labelkeySmartContract: 'Smart Contracts', labelkeyLoopringApp: 'Loopring App', labelkeyLoopringSmartWallet: 'Loopring Smart Wallet', labelkeyAPIs: 'APIs', labelkeyReportIssue: 'Report Issue', labelMyNFT: 'My NFTs', labelLaunchApp: 'View Dapps', labelLaunchMobileApp: 'Exchange', labelLandingHeaderLayer2: 'zkRollup Layer 2', labelLandingHeaderWallet: 'Smart Wallet', labelCopyRight: '© 2017 Loopring Technology Limited. All rights reserved.', labelWalletProtect: 'Guardian', labelWalletValidation: 'Approval Requests', labelWalletHistory: 'History', labelNFT: 'L2 NFT', labelMyAssetsNFT: 'My NFTs', labelMyAssetsNFTDes: 'Receive/Send NFTs', labelInvest: 'Earn', labelMintNFT: 'Create NFT', labelMintNFTDes: 'Create your own NFTs', labelTransactionNFT: 'NFT Transactions', labelTransactionNFTDes: 'NFT Transactions History', labelForceWithdraw: 'Force Withdraw', labelInvestBalance: 'My Investments', labelInvestBalanceDes: 'The deposition you hold', labelInvestAmm: 'AMM Pools', labelInvestOverview: 'Overview', labelInvestOverviewDes: 'Select DeFi Investment', labelInvestAmmDes: 'Add liquidity and earn fees', labelInvestDefi: 'ETH Staking', labelInvestDefiDes: 'Earn ETH staking rewards', labelInvestWSTETH: 'Via Lido', labelInvestWSTETHDes: 'Stake ETH get WSTETH', labelInvestRETH: 'Via Rocket Pool', labelInvestRETHDes: 'Stake ETH get RETH', labelInvestDual: 'Dual Investment', labelInvestDualDes: 'Buy the Dips, Sell the Rallies', labelDepositNFT: 'Receive NFT', labelMyCollection: 'My Collections', labelMyCollectionDes: 'Create, curate, and \n manage my NFT collections', labelInvestStakeLRC: 'LRC Staking', labelInvestStakeLRCDes: 'Earn LRC staking rewards', labelBtradeTrade: 'Block Trade', labelBtradeTradeDescription: 'Trade via CEX liquidity', labelStopLimit: 'Stop-Limit', labelStopLimitDescription: 'Control trading, like a pro', labelCopyRightBeta: 'This site is not released yet and could contain bugs. Please use it with caution!', labelReferralReward: 'Referrals Reward', // labelDepositNFTDes:"Receive NFT" labelInvestLeverageETH: 'Leveraged ETH Staking', labelInvestLeverageETHDes: 'Gain higher APY aggressively', labelDualInvest: 'Dual Investment', labelVault: 'Portal', labelNavPro: 'Loopring Layer 2', labelNavEarn: 'Loopring DeFi', labelNavWallet: 'Wallet', labelDashBoard: 'Dashboard', labelRecord: 'Records', labelHome: 'Loopring Layer 2', labelVaultHome: 'Market', labelVaultHomeDes: 'Browse tradable tokens', labelVaultDashboard: 'Dashboard', labelVaultDashboardDes: 'Manage assets and positions', labelUnlockFirst: 'Unlock First', labelVault2: 'Assets', labelTaikoFarming: 'Taiko Farming', labelNavDoc: 'Developers', labelNavWalletDes: `Trade on the go — anytime, anywhere`, labelNavEarnDes: 'Revolutionizing Decentralized Finance with Cutting-Edge Earning and Trading Solutions', labelNavProDes: 'Unleash the Power of DeFi and Advanced Trading \n on Top of an App-Specific ZK-Rollup', labelNavDocDes: 'Our API,SDK, and step-by-step references to building, minting, and trading on Loopring', labelAssetsDes: 'Revolutionizing Decentralized Finance with Cutting-Edge Earning and Trading Solutions', labelDualInvestDes: 'Bring structured finance from CeFi to DeFi in a trustless manner. Place orders at your preferred price and earn high yields!', labelVaultDes: 'Trade popular tokens beyond the Ethereum chain, with leverage!', labelBtradeTradeDes: 'Swap tokens securely and trustlessly, tapping into CEX liquidity.', labelTaikoFarmingDes: 'Farm Trailblazers points at 60X while unlocking the value of your locked TAIKO to keep trading or earning.', labelVaultTradeTabTitle: 'Trade', labelVaultTradeTabDes: 'Long or Short with leverage', } ================================================ FILE: packages/common-resources/static-resources/src/i18n/en_US/tables.ts ================================================ export default { labelStatus: 'Status', labelAMM: 'AMM', labelSend: 'Send', labelSendL2: 'Send L2', labelSendL1: 'Send L1', labelL2toL1Action: 'Withdraw', labelReceive: 'Receive', labelTransfer: 'Transfer', labelWithdraw: 'Withdraw', labelWithdrawAction: 'Withdraw', labelDeposit: 'Deposit', labelTrade: 'Trade', labelToken: 'Token', labelAmount: 'Amount', labelAvailable: 'Available', labelLocked: 'Frozen', labelAssetsTableValue: 'Value', labelActions: 'Actions', labelAllToken: 'All Tokens', labelHideSmallBalances: 'Hide Small Balances', labelHideInvestToken: 'Hide Invest Tokens', labelOrderFilterAllTypes: 'All Types', labelOrderFilterBuy: 'Buy', labelOrderFilterSell: 'Sell', labelFilterReset: 'Reset', labelFilterSearch: 'Search', labelOrderSide: 'Orders', labelOrderAmount: 'Amount', labelOrderAverage: 'Average', labelOrderPrice: 'Price', labelOrderFilledAmount: 'Filled Amount', labelOrderFilledPrice: 'Filled Price', labelOrderFilterAllPairs: 'All Pairs', labelOrderFee: 'Fee', labelOrderRole: 'Role', labelOrderTime: 'Time', labelOrderStatus: 'Status', labelOrderTradingPrice: 'Trading Price', labelOrderTotal: 'Total', labelQuotaPair: 'Pair', labelQuotaLastPrice: 'Latest Price', labelQuota24hChange: '24h Change', labelQuota24hChangeLit: '24h Chg ', labelQuota24hHigh: '24h High', labelQuota24hLow: '24h Low', labelQuota24hAmount: '24h Amount', labelQuoteAction: 'Action', labelTradeFilterAllTypes: 'All Types', labelTradeFilterBuy: 'Buy', labelTradeFilterSell: 'Sell', labelTradeSide: 'Trades', labelTradeAmount: 'Amount', labelTradePrice: 'Price', labelTradeFee: 'Fee', labelTradeTime: 'Time', labelTradeRole: 'Role', labelTradeRoleMaker: 'Maker', labelTradeRoleTaker: 'Taker', labelTradeConterparty: 'Counterparty', labelTradeCounterpartyOrderbook: 'Orderbook', labelTradeCounterpartyPool: 'Pool', labelTxFilterAllTypes: 'All Types', labelTxFilterReceive: 'Receive', labelTxFilterSendL1: 'Send L1', labelTxFilterSendL2: 'Send L2', labelTxFilterDeposit: 'Deposit', labelTxFilterWithdraw: 'Withdrawal', labelTxFilterTransfer: 'Transfer', labelTxFilterAllTokens: 'All Tokens', labelTxSide: 'Transactions', labelTxToken: 'Token', labelTxFrom: 'Description', labelTxTo: 'To', labelTxAmount: 'Amount', labelTxFee: 'Fee', labelTxMemo: 'Memo', labelTxTime: 'Time', labelTxTxnHash: 'Txn Hash', labelTxStatus: 'Status', labelTransactions: 'Transactions', labelVolume: 'volume', labelTradePair: 'Trade Pair', labelPool: 'Pool', labelLiquidity: 'Liquidity', labelMyLiquidity: 'My Liquidity', label24TradeVolume: '24h Volume', label24Reward: '24h Reward', labelAPR: 'APR', labelTradePool: 'Subscribe', labelAction: 'Action', labelFilter: 'Filter', labelFilterAllPairs: 'All pairs', valueAddTOAMM: `Add <1> and <3>`, valueSwapForAMM: `Swap <1> for <3>`, valueRemoveTOAMM: `Remove <1> and <3>`, labelAmmTotalValue: 'Total Value', labelAmmTokenAmount: 'Token Amount', labelAmmTime: 'Time', labelFeeEarned: 'Fees Earned', labelBuy: 'Buy', labelSell: 'Sell', labelAmmSide: 'Subscribe/Redeem', labelAmmAmount: 'Amount', labelAmmLpTokenAmount: 'LP Token Amount', labelAmmFee: 'Fee', labelAmmRecordTime: 'Time', labelAmmExit: 'Redeem', labelAmmJoin: 'Subscribe', labelAmmFilterTypes: 'All Types', labelAmmFilterJoin: 'Subscribe', labelAmmFilterExit: 'Redeem', labelPoolTableAddLiquidity: 'Subscribe', labelPoolTableRemoveLiquidity: 'Redeem', labelEmptyDefault: 'No data to display', labelAmmTableType: 'Type', labelOrderDetailTaker: 'Taker', labelOrderDetailMaker: 'Maker', labelOrderDetailTradingVolume: 'Trading Volume', labelOrderDetailOrderId: 'Order Id', labelOrderCancelAll: 'Cancel All', labelOrderCancelOrder: 'Cancel', labelOrderProcessing: 'Processing', labelOrderProcessed: 'Processed', labelOrderCancelling: 'Cancelling', labelOrderCancelled: 'Cancelled', labelOrderExpired: 'Expired', labelOrderWaiting: 'Waiting', // labelOrderAmm: 'AMM', labelOrderLimitOrder: 'Limit', labelOrderStopLimitOrder: 'Stop-Limit', labelOrderMarketOrder: 'Market', // labelOrderMaker: 'Maker', labelOrderTaker: 'Taker', labelOrderChannelsMixed: 'Merged', // labelOrderChannelsAMM: 'AMM', labelOrderChannelsSwap: 'Swap', labelOrderChannelsOrderBook: 'Orderbook', labelOrderTypes: 'Types', labelOrderChannels: 'Channels', labelOrderCompletion: 'Completion', labelRewardTableAmount: 'Amount', labelRewardTableTime: 'Time', labelOrderCancelConfirm: 'Confirm to cancel this order?', labelOrderConfirm: 'Confirm', labelOrderCancel: 'Cancel', labelTradeProPrice: 'Price ({{symbol}})', labelTradeProAmount: 'Amount ({{symbol}})', labelVipTableLevel: 'Level', labelVipTable30dTradeVolume: '30d Volume', labelVipTableRule: 'and / or', labelVipTableBalance: 'LRC Balance', labelVipTableMaker: 'Maker', labelVipTableTaker: 'Taker', labelTradeRaceRank: 'Rank', labelTradeRaceAddress: 'Address', labelTradeRaceTradeVolume: 'Trade Volume', labelTradeRaceAward: 'Award', labelTradeRaceProject: 'Project', labelTradeRacePair: 'Pair', labelTradeRaceToken: 'Token', labelTradeRaceReward: 'Reward', labelMaker: 'Maker', labelTaker: 'Taker', labelNFTTypeDEPOSIT: 'Deposit', labelNFTTypeTRANSFER: 'Transfer', labelNFTTypeWITHDRAWAL: 'Withdraw', labelNFTTypeWITHDRAW: 'Withdraw', labelNFTTypeWITHDRAW_LUCKY_TOKEN: 'Claim Red Packet', labelNFTTypeSEND_BACK_LUCKY_TOKEN: 'Back From Red Packet', labelNFTTypeSEND_LUCKY_TOKEN: 'Send Red Packet', labelMint: 'Mint', labelNFTTypeMINT: 'Mint', labelTxNFTFilterALL: 'All Types', labelTxNFTFilterDEPOSIT: 'Deposit', labelTxNFTFilterWITHDRAW: 'Withdraw', labelTxNFTFilterWITHDRAWAL: 'Withdraw', labelTxNFTFilterRECEIVE: 'Receive', labelTxNFTFilterSEND: 'Send', labelTxNFTFilterFORCEWITHDRAW: 'Force Withdraw', labelTxNFTFilterTRANSFER: 'Transfer', labelTxFilterALL: 'All Types', labelTxFilterRECEIVE: 'Receive', labelTxFilterSEND: 'Send', labelTxFilterFORCEWITHDRAW: 'Force Withdraw', labelTxFilterDEPOSIT: 'Deposit', labelTxFilterWITHDRAW: 'Withdraw', labelTxFilterWITHDRAWAL: 'Withdraw', labelTxFilterTRANSFER: 'Transfer', labelTypeDEPOSIT: 'Receive', labelTypeWITHDRAW: 'Send', labelTypeSEND: 'Send', labelTypeReceive: 'Receive', labelTypeONCHAIN_WITHDRAWAL: 'Send', labelTypeOFFCHAIN_WITHDRAWAL: 'Send', labelTypeTRANSFER: 'Send', labelTypeWITHDRAW_LUCKY_TOKEN: 'Claim Red Packet', labelTypeSEND_BACK_LUCKY_TOKEN: 'Back From Red Packet', labelTypeSEND_LUCKY_TOKEN: 'Send Red Packet', labelTypeDUAL_INVESTMENT: 'Dual Investment Settled', labelTypeL2_STAKING: 'Claim Earnings', labelTxNFTFilterMINT: 'Mint', labelShowFilter: 'Show Filter', labelTypeDELEGATED_FORCE_WITHDRAW: 'Force Withdraw', labelForceWithdrawTotalDes: 'All {{symbol}}', labelForceWithdrawDes: '{{address}} (Address that the token is withdrawn from L2 to L1)', labelDefiInvest: 'Subscribe', labelDefiRedeem: 'Redeem', labelSelect: 'Select', labelDuration: 'Duration', labelInvestAll: 'Both', labelDefiExit: 'Redeem', labelDefiJoin: 'Subscribe', labelDefiType: 'Subscribe/Redeem', labelDefiFee: 'Fee', labelDefiTime: 'Time', labelDefiAmount: 'Amount', labelFilterTradeNFTAll: 'All', labelFilterTradeNFTSell: 'Sell', labelFilterTradeNFTSelf: 'Self Trade', labelFilterTradeNFTBuy: 'Buy', labelTradeNFTSide: 'Trade', labelFrom: 'from', labelTo: 'to', labelUPrice: 'unit price: ', labelTradeNFTUnitPrice: 'Unit Price', labelDualApy: 'APR', labelDualTerm: 'Term', labelDualPrice: 'Target Price', labelDualTPrice: 'Target', labelDualSettlement: 'Settlement Date', labelDualAction: 'Action', labelInvestmentStatusSettled: 'Settled', labelInvestmentStatusDelivering: 'Delivering', labelInvestmentStatusSubscribe: 'Earning', labelDualAssetProduct: 'Product', labelDualAssetFrozen: 'Invest', labelDualAssetPrice: 'Target Price', labelDualAssetSettlement_Date: 'Settlement Date', labelDualAssetAPR: 'APR', labelDualAssetAction: 'Actions', labelDualAssetDetail: 'Details', labelDualAssetRefresh: 'Refresh', labelDualTxsSide: 'Type', labelDualTxsProduct: 'Product', labelDualTxAPR: 'APR', labelDualTxsTargetPrice: 'Target Price', labelDualTxsSettlement_Date: 'Settlement Date', labelDualTxsSettlementPrice: 'Settlement Price', labelDualTxsSettlement: 'Settlement', labelDualTxsTime: 'Time', labelDualAssetReturn: 'Return', labelAprPool: 'Amm Pool APR:', labelAprFee: 'Protocol APR:', labelAprEvent: 'Activity APR:', label24Rewards: '24h Rewards', labelRewardFee: 'AMM Rewards:', labelRewardReward: 'Other Rewards:', labelRewardExtra: 'Campaign Rewards:', labelRecordToken: 'Token', labelRecordAmount: 'Amount', labelRecordType: 'Type', labelRecordStatus: 'Status', labelRecordNumber: 'Number', labelRecordTime: 'Time', labelReceiveTime: 'Receive Time', labelValue: 'Value', labelClaim: 'Claim', labelAddress: 'Address', labelType: 'Type', labelDefiStakingProduct: 'Product', labelDefiStakingFrozen: 'Frozen', labelDefiStakingEarn: 'Cumulative Earnings', labelDefiStakingPreviousEarn: "Previous Day's Earnings", labelDefiStakingDuration: 'Holding Time', labelDefiStakingARR: 'APR', labelDefiStakingAction: 'Action', labelDefiStakingTxType: 'Type', labelDefiStakingTxAmount: 'Amount', labelDefiStakingTxProduct: 'Product', labelDefiStakingTxLockedDuration: 'Locked Duration', labelDefiStakingTxHashId: 'HashID', labelDefiStakingTxSubscribeTime: 'Time', labelDefiStakingTxRewardsDate: 'Rewards Date', labelDefiStakingTxTxID: 'TxID', labelDefiStakingTxTime: 'Time', labelDefiStakingDetail: 'Detail', labelDefiStakingRedeem: 'Redeem', labelDays: 'day(s)', labelStakeTransactionTypesubscribe: 'subscribe', labelStakeTransactionTyperedeem: 'redeem', labelStakeTransactionTypeclaim: 'claim', labelDefiStakingAndPreviousEarn: "Cumulative & Previous Day's Earnings", labelDefiStakingTxRewardsMobileDate: 'Date', labelBlindBoxOpend: 'Opend', labelBlindBoxEndTime: 'End Time', labelBlindBoxCalim: 'Claim', labelBlindBoxExpired: 'Expired', labelBlindBoxClaimed: 'Claimed', labelBlindBoxStartTime: 'Reveal Time: {{time}}', labelBlindBoxExpiredTime: 'Expiration Time: {{time}}', labelRedPacketOpen: 'Open', labelRedPacketSenderAddress: 'Sender Address', // labelRecordAction: "Action", labelBtradeSwapType: 'Type', labelBtradeSwapFee: 'Trading Fee', labelBtradeSwapTime: 'Time', labelBtradeSwapPrice: 'Price', labelBtradeSwapSettled: 'Settled', labelBtradeSwapDelivering: 'Delivering', labelBtradeSettled: 'Settled', labelBtradeDelivering: 'Delivering', labelBtradeDeliveringDes: 'The Loopring pool is currently unable to swap the full requested amount. The tokens that were successfully swapped will be transferred to your account now. The unswapped tokens will be locked until they can be swapped. \n We’ll rebalance the pool shortly and swap the remaining portion.', labelBtradeFailed: 'Failed', labelBtradeCancelled: 'Cancelled', labelBtradePending: 'Pending', labelBtradeSwapFailed: 'Filled', labelStopLimitStopPrice: 'Trigger Condition', labelDUAL_CURRENCY: 'Dual', labelDUAL_BASE: 'Dual', labelBTRADE: 'Block Trade', labelL2STAKING: 'LRC Staking', labelSTOP_LIMIT: 'Stop-Limit', labelStopLimitTriggered: 'Triggered: The limit order has been submitted to the order book.\n Time: {{time}}', labelStopLimitWaitingTrigger: 'The limit order is not placed until the stop price has been triggered.', labelRevealTime: 'Reveal Time', labelRevealTimeTooltip: 'The Reveal time is when the Red Packet ends, and recipients can open it to see if they have received', labelExpiredTime: 'Expired Time', labelExpiredTimeTooltip: 'After expiration, all unclaimed NFTs will be returned to the Sender.', labelRedpacketFromBlindbox: 'From Blind Box', labelRedpacketCantOpen: 'It is not yet time to open, reveal time: {{time}}', labelReferralsTableReferee: 'Referee', labelReferralsTableAmount: 'Rewards', labelReferralsTableTime: 'Time', labelRefundTableAmount: 'Refunds', labelRefundTableTime: 'Time', labelTxNetworkFee: 'Network Fee', labelTxTradingFee: 'Trading Fee', labelTypeUNIFIED_CLAIM: 'Claim Rewards', labelRedPacketClaiming: 'Claiming', labelLoadingMore: 'Loading More...', labelDualAutoReinvest: 'Auto Reinvest', labelDualAssetModify: 'Modify', labelDualAssetReInvestDisable: 'Disabled', labelDualAssetReInvestEnable: 'Enabled', labelDualAuto: 'ReInvest', labelDualFailed: 'Failed', labelDualPending: 'pending', labelDualAutoInvestTip: 'Auto Reinvest Status:{{}}', labelDualRetryStatusSuccess: 'Auto reinvested successful. A new order has been generated for you.', labelDualRetryStatusError: 'Auto reinvest failed. Cannot find product with Buy Price of {{price}} and Longest Settlement Date of {{day}} days. ', labelDualRetryStatusRetrying: 'Auto reinvesting. Searching for the product...', labelDualAssetReInvestYes: 'ReInvest On', labelDualAssetReInvestNo: 'ReInvest Off', labelDualRetryPending: 'Pending', labelDualRetryTerminated: 'Terminated', labelDualRetryFailed: 'Failed', labelDualRetrySuccess: 'Successful', labelDualRetryStatusTerminated: 'Auto Reinvest terminated. You successfully purchased the target token.', labelTypeCHANGE_PWD: 'Reset Loopring L2 Keypair', labelAveragePositionCost: 'Average Position Cost <1>', labelAveragePositionCostDes: 'The average holding cost of the last 10 purchases.', labelDefiApr: 'Est.APR <1>', labelDefiAprDes: 'APR stands for annual percentage rate, not taking into account the effect of compound interest. The value displayed here indicates the current value, while it keeps changing dynamically. ', labelVaultAssetsTableValue: 'Value', labelVaultborrow: 'Borrow', labelVaultopen: 'Supply Collateral', labelVaultcloseout: 'Settle', labelVaultmargin: 'Add Collateral', labelVaultrepay: 'Repay', labelVaulttrade: 'Trade', labelVaultSellShort: 'Sell/Short', labelVaultBuyLong: 'Buy/Long', labelVaultborrowDes: 'Borrow des', labelVaultopenDes: 'Supply Collateral des', labelVaultcloseoutDes: 'Settle des', labelVaultmarginDes: 'Margin Call des', labelVaultrepayDes: 'Repay des', labelVaulttradeDes: 'Trade des', labelVaultTxType: 'Type', labelVaultTxFilled: 'Filled Amount', labelVaultTxStatus: 'Status', labelVaultTxTime: 'Time', labelVaultVAULT_STATUS_EARNING: 'Success', labelVaultVAULT_STATUS_RECEIVED: 'Received', labelVaultVAULT_STATUS_PROCESSING: 'Pending', labelVaultVAULT_STATUS_SUCCEED: 'Success', labelVaultVAULT_STATUS_FAILED: 'Failed', labelVaultVAULT_STATUS_PENDING: 'Pending', labelDetail: 'Detail', labelVaultcloseoutForced: 'Forced Liquidation', labelInvest: 'Invest', labelVaultconvert: 'Dust Collector', labelVaultredeem: 'Redeem Collateral', labelVaultJoinRedeem: 'Redeem Collateral', labelFailed: 'Failed', labelTotalValue: 'Total Value', labelVaultConvert: 'Converted', labelVaultRepayment: 'Payment', labelVaultTime: 'Time', labelVaultDetails: 'Details', labelVaultErrorOccurred: 'An error has occurred. Please try again later.', labelVaultDustCollector: 'Dust Collector', labelDetails: 'Details', labelLock: 'Lock', labelLockTime: 'Time', labelTotalLocked: 'Total/Locked', labelLocked2: 'Locked', labelTAIKO_FARMING: 'Taiko Farming', labelVaultcloseShort: 'Close Short' } ================================================ FILE: packages/common-resources/static-resources/src/i18n/en_US/webEarn.ts ================================================ /* eslint-disable max-len */ export default { labelFAQ1Question: 'What is Loopring protocol ?', labelFAQ1Answer: "As the world's first ZKRollup implementation to scale up Ethereum, Loopring Protocol has run since 2017. There have been more than 210K users and $5.88B trading volume occurred on this protocol. As an app-specific ZKRollup protocol, it has been successfully deployed not only as a Layer 2 on top of Ethereum but also as a Layer 3 on top of other EVM-compatible networks such as Arbitrum.", labelFAQ2Question: 'What is Dual Investment ?', labelFAQ2AnswerLine1: 'Dual Investment is a non-principal protected structured product. Upon purchasing, you can select the underlying asset, investment currency, investment amount, and delivery date. Your return will be denominated in the investment currency or alternate currency, depending on the below conditions.', labelFAQ2AnswerLine2: 'There are two types of Dual Investment products: “Buy Low” and “Sell High”.', labelFAQ2AnswerLine3: 'Buy Low products gives you a chance to buy your desired crypto (such as LRC) at a lower price in the future with stablecoins (USDC).', labelFAQ2AnswerLine4: 'Target Reached: On the Settlement Date, if the Market Price is at or below the Target Price, the target currency (LRC) will be bought;', labelFAQ2AnswerLine5: 'Target Not Reached: On the Settlement Date, if the Market Price is above the Target Price, then you will keep your stablecoins; In both scenarios, you will first earn interest in stablecoins. Once the Target Price is reached, your subscription amount and interest income will be used to buy LRC.', labelFAQ2AnswerLine6: 'Sell High products gives you a chance to sell your existing crypto (such as LRC) at a higher price in the future (for USDC).', labelFAQ2AnswerLine7: 'Target Reached: On the Settlement Date, the Market Price is at or above the Target Price, your LRC will be sold for USDC.', labelFAQ2AnswerLine8: 'Target Not Reached: On the Settlement Date, the Market Price is below the Target Price, then you will keep your LRC. In both scenarios, you will first earn interest in your existing currency (LRC). Once the Target Price is reached, your subscription amount and interest income will be sold for USDC.', labelFAQ2AnswerLine9: 'Your token for investment is just locked but still in your account as Loopring is a DEX.', labelFAQ2AnswerLine10: ' Each purchased product has a settlement date. We will take an average of the market price in the last 30 minutes before 16:00 (UTC+8) on the delivery date as the settlement price.', labelFAQ2AnswerLine11: 'Please make sure that you fully understand the product and the risks before investing.', labelFAQ3Question: 'What is the Dual Investment Sell covered gain ?', labelFAQ3AnswerLine1: 'Covered Gain is an investment strategy to sell digital assets at your Target Price and earn interest while waiting.', labelFAQ3AnswerLine2: 'On the Settlement Date, there can be 2 scenarios:', labelFAQ3AnswerLine3: 'Market Price > Target Price', labelFAQ3AnswerLine4: 'Market Price ≤ Target Price', labelFAQ3AnswerLine5: 'Market Price > Target Price', labelFAQ3AnswerLine6: 'Your original investment and earned interest will be sold at the target price.', labelFAQ3AnswerLine7: 'This order is then closed regardless of whether "Auto Reinvest" is enabled or not.', labelFAQ3AnswerLine8: 'Market Price ≤ Target Price', labelFAQ3AnswerLine9: 'Your original investment and earned interest won’t be sold.', labelFAQ3AnswerLine10: 'If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully sell crypto at your desired price or disable the feature.', labelFAQ3AnswerLine11: 'Auto Reinvest', labelFAQ3AnswerLine12: 'When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully sell your crypto at your Target Price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.', labelFAQ3AnswerLine13: 'Sell Price: the Target Price at which you want to sell your crypto.', labelFAQ3AnswerLine14: "Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.", labelFAQ4Question: 'What is the Dual Investment Buy the dip ?', labelFAQ4AnswerLine1: 'Buy The Dip is an investment strategy to buy digital assets at your Target Price and earn interest while waiting.\n', labelFAQ4AnswerLine2: 'On the Settlement Date, there can be 2 scenarios:', labelFAQ4AnswerLine3: 'Market Price > Target Price', labelFAQ4AnswerLine4: 'Market Price ≤ Target Price', labelFAQ4AnswerLine5: 'Market Price > Target Price', labelFAQ4AnswerLine6: 'Your original investment and earned interest won’t be converted. Earned interest is in USDC or USDT.', labelFAQ4AnswerLine7: 'If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully buy crypto at your desired price or disable the feature.', labelFAQ4AnswerLine8: 'Market Price ≤ Target Price', labelFAQ4AnswerLine9: 'Your original investment and earned interest will be converted at the Target Price.', labelFAQ4AnswerLine10: 'This order is then closed regardless of whether "Auto Reinvest" is enabled or not.', labelFAQ4AnswerLine11: 'Auto Reinvest', labelFAQ4AnswerLine12: 'When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.', labelFAQ4AnswerLine13: 'Buy Price: the Target Price at which you want to buy crypto.', labelFAQ4AnswerLine14: "Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.", labelDualEarnTitle: 'DUAL INVESTMENT', labelDualEarnSubTitle: 'The most innovative structural products brought to the DeFi world', labelDualEarnTag1: 'Buy Low or Sell High', labelDualEarnTag2: 'No Trading Fees', labelDualEarnTag3: 'High Rewards', labelSellHigh: 'Sell Hign', labelBuyLow: 'Buy Low', labelInvestSymbol: 'Invest {{symbol}}', labelInvestSymbolSellHigh: 'Sell {{symbol}} High', labelInvestSymbolBuyLow: 'Buy {{symbol}} Low', labelApy: 'APY', labelCurrentPrice: 'Current Price', labelViewDetails: 'View Details', labelLoopringEarn: 'Loopring DeFi', labelLoopringEarnDes: 'Loopring DeFi is built on top of Loopring Protocol to take full advantage of its ZKRollup technology and full-stack DEX capability to provide the most innovative DeFi products to users.', labelLoopringProtocol: 'Loopring Protocol', labelLoopringProtocolDes: "The world's first ZKRollup implementation designed to scale Ethereum, fully optimized for trading.", labelUltimateSecurity: 'Ultimate Security', labelUltimateSecurityDes: 'Assets on Loopring are equally secure as they are on the Ethereum mainnet.', labelLowTransactionFees: 'Low Transaction Fees', labelLowTransactionFeesDes: 'Loopring performs most operations, including trade and transfer settlement, off the Ethereum blockchain. This dramatically reduces gas consumption and overall transaction cost to small fractions of comparable on-chain cost.', labelHighThroughput: 'High Throughput', labelHighThroughputDes: 'Loopring can settle ~2000 transactions per second with near instant finality.', labelFAQs: 'FAQs', labelBtradeSwapTitle: 'Block Trade', labelViewMore: 'View more', labelVault: 'Portal', labelInvestDualDes1: 'Experience the Leading On-Chain Structured Product', labelInvestDualDes2: 'Buy the dip or sell at a higher price - all while earning high yields.', labelPortalDes1: 'Unlock the Power of Leveraged Trading', labelPortalDes2: 'Seamlessly borrow tokens against your collateral, paving the way for leveraged trading opportunities. By bridging decentralized and centralized exchanges, Loopring Portal broadens your trading horizon, granting access to tokens beyond the Ethereum network.', labelBtradeDes1: 'Trade with CEX Liquidity', labelBtradeDes2: 'Effortlessly trade tokens with near-zero impact, leveraging multiple liquidity sources across crypto to guarantee the best rates every time.', labelInvestDualTitle: 'Dual Investment', labelLoopringDeFi: 'Loopring DeFi', labelIntroDes1: 'Revolutionizing Decentralized Finance with Cutting-', labelIntroDes2: 'Edge Earning and Trading Solutions', labelIntroDes3: 'Delivering CeFi-like user experiences in a fully trustless environment', labelLearnMore2: 'Learn More', labelReadyForDevelopers: 'Ready for Developers', labelReadyForDevelopersDes: "Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's battle-tested zkRollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.", labelLaunch: 'Launch', labelInvestTaikoFarmingTitle: 'Taiko Farming', labelInvestTaikoFarmingDes1: 'Earn up to 60x Trailblazers Points by Locking Your TAIKO!', labelInvestTaikoFarmingDes2: 'Farm Trailblazers points at up to 60x while keeping the flexibility to trade or earn with your locked TAIKO.', } ================================================ FILE: packages/common-resources/static-resources/src/i18n/index.ts ================================================ import i18n from 'i18next' import { initReactI18next } from 'react-i18next' import enUS from './en_US' // import zhCN from './zh_CN' // import { localStore } from '../storage'; // the translations // (tip move them in a JSON file and import them) export enum LanguageType { en_US = 'en_US', zh_CN = 'zh_CN', } export enum languageMap { en_US = 'en', } export type LanguageKeys = keyof typeof LanguageType export const resources = { en_US: { ...enUS }, // zh_CN: {...zhCN}, } // const initLng = JSON.parse(localStorage.getItem('persist:settings') as string)?.language === `"${LanguageType.zh_CN}"` ? LanguageType.zh_CN : LanguageType.en_US const initLng = LanguageType.en_US i18n.use(initReactI18next).init({ resources, ns: ['common', 'layout', 'tables', 'landPage', 'error', 'webEarn'], defaultNS: 'common', lng: initLng, load: 'currentOnly', fallbackLng: LanguageType.en_US, // supportedLngs: [LanguageType.en_US, LanguageType.zh_CN], keySeparator: '.', // we do not use keys in form messages.welcome interpolation: { escapeValue: true, // react already safes from xss formatSeparator: `, `, // format: function (value, _format, lng) { // // if ( // Object().toString.call(value) === "[object Array]" && // lng === LanguageType.en_US // ) { // return value.join(", "); // } // return value; // // if (format === 'uppercase') return value.toUpperCase(); // // return value; // }, }, react: { bindI18n: 'languageChanged', // bindI18nStore: '', transEmptyNodeValue: '', transSupportBasicHtmlNodes: true, transKeepBasicHtmlNodesFor: ['br', 'strong', 'i'], useSuspense: true, }, }) export default i18n ================================================ FILE: packages/common-resources/static-resources/src/i18n/zh_CN/common.ts ================================================ /* eslint-disable max-len */ export default { labelErrorTitle: 'Error Detail:', labelNoContent: 'No Content', labelError: '错误', tokenEnter: '输入币种', tokenEnterPaymentToken: '', tokenMax: '可用: ', tokenNFTMaxMINT: 'Max:', tokenHave: '可用:', tokenEnterReceiveToken: '', tokenSelectToken: '选择币种', tokenExchange: '转换', tokenNotEnough: '{{belong}}余额不足无法交易', tokenSearchCoin: '搜索交易币种', swapTitle: '兑换', swapTolerance: '滑点范围', labelSwapToleranceTooltips: 'Your trade will revert if the price changes unfavorably by more than this percentage.', swapPriceImpact: '价格影响', labelSwapPriceImpactTooltips: 'The difference between market price and estimated price due to trade size', swapMinReceive: '最少买入', swapMinReceiveS: 'Min. Received', labelSwapMinReceiveTooltips: 'Because the pool price changes dynamically, the price you see when placing an order may be inconsistent with the final transaction price, but the protocol can guarantee that you will receive at least this amount.', swapFee: '费用', swapFeeS: 'Est. Fee', labelSwapFeeTooltips: 'The trading fee is determined by your VIP level and the size of your trade. Small trades (below ~$100) incur a higher fee. Please review the fee before confirming.', swapBtn: '兑换', goBack: '返回', resetTitle: '重置{{layer2}}账号密钥', resetLabelEnterToken: '请选择消耗代币', resetDescription: '创建一个新的{{loopringL2}}身份验证签名密钥(无需备份),该操作将会<1>取消您的所有待定指令。', resetFee: '{{count}} GAS ≈ ${{price}} 费用', resetLabelBtn: '重置', labelActiveEnterToken: 'Select payment token', labelActiveAccountDescription: 'You need to have enough balance for {{layer2}} creation as below.', labelActiveAccountFee: 'Fee {{count}} GAS ≈ ${{price}}', labelActiveAccountBtn: 'Reset', depositLabelEnterToken: '选择充值代币', depositDescription: '您的充值将会在以太坊<1>确认后<3>两分钟内到账。', depositAndActiveDescription: '完成一次充值来激活您的路印{{loopringL2}}账号。您的充值将会在以太坊<1>确认后<3>两分钟内到账。', depositLabelRefer: '请输入推荐您的ENS,地址,或者账号ID。(选填)', depositLabelPlaceholder: '以太坊地址, 账号ID或ENS', withdrawDescription: '提现操作会被提交到以太坊的下一个区块,一般会在<1>30分钟到2小时内到账。(如果以太坊的GAS价格<5>超过500GWei的话,可能会存在<3>长时间的延迟)', withdrawTypeLabelFast: '快速提现(最快15秒到账)', withdrawTypeLabelStandard: '普通提现(约25分钟)', labelConnectWallet: '连接钱包', labelCustomer: '自定义', labelChange24h: '{{timeUnit}} 涨幅', labelDepth: '深度', labelTrend: '价格', label1W: '1周', label1H: '1小时', label1D: '1日', labelCopyAddress: '复制地址', labelDisconnect: '断开', labelLockLayer2: '锁定', labelUnLockLayer2: '解锁', labelSwitchAccount: '切换钱包', labelViewEth: '跳转至Etherscan', labelQRCode: '查看二维码', labelShowAccountInfo: '查看,设置账号信息', labelAssetTitle: '总资产({{loopringL2}})', labelAssetMobileTitle: '{{l2Symbol}} Total Assets', labelShowAccount: '显示或隐藏总资产', labelLevel: 'VIP等级', labelOrderbook: '订单本', labelSetPublicKey: '设置 EdDSA 公钥', labelTitleSecurity: '安全设置', labelTitleResetL2Keypair: '重置{{loopringL2}}账号密钥', labelBtnReset: '重置', labelHadChangPassword: '您在{{passDay}}修改了密码', labelTitleForceWithdraw: '强制提现', labelBtnForceWithdraw: '强制提现', labelTitleExportAccount: '导出账号', descriptionExportAccount: '你可以导出你的{{loopringL2}}API密钥,在脚本中使用。', labelBtnExportAccount: '导出账号', labelDownloadViewMore: '查看更多', labelTitlePreferences: '偏好设置', labelTitleLayout: '个性化设置', whichColorIsUp: '<0>{{up}} 上涨 -- <2>{{down}} 下跌', labelTradeFeeLevel: '您的交易等级评定:', labelLanguage: '显示语言', labelCurrency: '结算货币', currencySetting: '货币设置', labelColors: '选择颜色', labelTheme: '暗黑模式', labelthemeLight: '明亮模式', labelthemeDark: '暗黑模式', labelgreen: '绿色', labelred: '红色', langZH: '中文', langEN: '英语', labelUSDollar: '美金结算', labelCNYYuan: '人民币结算', labelMaker: '挂单', labelTaker: '吃单', labelAssetsTitle: '资产清单', labelVolume: '总量', labelAmount: '成交量', labelLiquidityDeposit: '入金', labelLiquidityWithdraw: '出金', labelAvailable: '可用:', labelTokenAmount: '数量', labelRemoveLiquidityBtn: '立刻出金', labelAddLiquidityBtn: '立刻入金', labelEndLiquidityBtn: '活动已结束', labelTradePanelHideOtherPairs: '隐藏其他交易对', labelLPTokens: '权益代币', labelMyLPToken: '我的权益代币', labelMyLPAToken: '我的 {{symbol}}', labelMyLPBToken: '我的 {{symbol}}', labelMyLPAmountFor: '我的持有比', labelTrade: '兑换', labelAmmList: 'AMM 列表', labelMyPoolShare: '我的份额', labelFee: '费用', labelLPTotal: '总资金池', labelReward: '奖励池', labelMyReward: '我的奖励', labelDate: '日期', labelBack: '返回', labelAPR: 'APR', label24Volume: '24h 交易量', label24VolumeSimple: '24h Vol', labelTVL: '总锁仓量', labelAmmTotalToken: '池子中的代币', labelNoActiveEvent: '暂时没有活动', labelNew: '新的', labelAccount: '账户信息', labelAll: '所有', labelMe: '我的', labelMyTrade: '我的交易', labelRecent: '市场交易', labelMyAmm: '我的AMM流动性', labelMyAmmRecord: '我的AMM记录', labelCurrentActivities: '当前活动', labelPastActivities: '往期活动', labelTotalPositionValue: '我的流动性总价值', labelFeeRewards: '做市返佣', labelMiningRewards: '活动奖励', labelLiquidityValue: '流动性价值', labelCopyAddClip: '复制地址到剪切板!', labelPleaseInputWalletAddress: '请输入地址', labelEmptyDefault: '内容空空的~', labelUnlockAccount: '解锁账号', labelLockWallet: 'Lock Wallet', labelAssetsDistribution: '资产分布', labelTotalAssets: '总资产', labelTxnPageTitle: '充值提现', labelTradePageTitle: '交易记录', labelAmmPageTitle: 'AMM 出入金记录', labelSwapSuccess: '兑换成功!', labelOrderProcessing: '已下单!', labelSwapFailed: '兑换失败!', labelJoinAmmSuccess: '加入流动性池成功!', labelJoinAmmFailed: '加入流动性池失败!', labelExitAmmSuccess: '退出流动性池成功!', labelExitAmmFailed: '退出流动性池失败!', labelConnectBy: '当前连接 <1>{{connectBy}}', labelWrongNetwork: '未识别网络', labelActivatedAccountDeposit: '首次开通{{layer2}}钱包需充值并激活', labelActivatedAccountNotSupport: 'Your wallet does not support {{loopringL2}}.', labelActivatedAccountNotSupportDes: ' The wallet you have connected does not support Loopring Layer 2. Please use a different wallet to connect or download the Loopring Wallet app.', labelNotAllowForSmartWalletTitle: 'Apologize', labelProcessing: '请稍等片刻', labelProviderProcessing: '{{name}}正在连接路印钱包', labelProviderCommonProcessDescribe: '请在{{name}}的弹出窗口内点击确定按钮。 如果{{name}}插件没有主动弹出窗口, 请在浏览器工具栏中点击 <1> 图标。', labelWalletConnectProcessDescribe: '正在等待WalletConnect确认相关信息,请耐心等待片刻。', labelWalletConnectQRCode: '请用移动端路印钱包(或支持WalletConnect的应用)扫描二维码', labelSuccessConnect: '使用{{providerName}}连接成功', labelSuccessConnectDescribe: '恭喜,连接成功', labelCopyClipBoard: '复制到剪切板', labelCopyManually: 'Manually Selected & Copy:', labelRejectOrError: '用户拒绝或错误发生, 请<1>点击重试。', labelWalletConnectProcessDescribe2: '请在你的移动应用上点击确认按钮。', labelUnlockProcessing: '正在解锁{{layer2}}账号...', labelFailedConnect: '连接失败', // labelTokenAccess: '等待钱包确认{{symbol}}授权!', labelTokenAccess: '等待钱包确认授权!', labelFailedTokenAccess: '{{symbol}}授权失败!', labelSuccessTokenAccess: '恭喜, 你可以交易 {{symbol}} 了!', labelSuccessUnlockDescribe: '恭喜,解锁成功!', labelSuccessUnlock: '解锁成功', labelActivateAccount: '激活账户', labelClose: '关闭', labelRetry: '重试', labelTryNext: '尝试另一种签名', labelQuotePageFavourite: '自选', labelQuotePageAll: '全部', labelQuotePageTradeRanking: '交易竞赛', labelFailedUnlock: '解锁失败', labelFailedUpdateAcc: '更新账号失败', labelUpdateAccSigWarning: '你的钱包不支持当前签名方法,准备尝试另一种签名方式。', labelUpdateAccUserDenied: '签名被拒绝', labelCreateLayer2Title: '创建{{layer2}}账号', labelCreateAccount: '创建{{layer2}}账号', labelUpdateAccount: '激活{{layer2}}账号', labelTryAnother: '尝试另一种签名方式', labelCancel: '取消', describeTitleNoAccount: '该账户还未开通{{layer2}},点击充值开通钱包。', describeTitleOpenAccounting: '您的充值已提交以太坊,\n等待以太坊确认。。。', describeTitleOnErrorNetwork: '路印暂不支持您当前登入的网络,\n请在{{connectName}}切换网络', describeTitleNotActive: '充值钱包, 激活账户, \n开始二层之旅.', describeTitleConnectToWallet: '点击按钮,连接路印钱包,\n开始二层之旅.', describeWhatIsGuardian: 'what is Loopring guardian!', describeTitleConnectToWalletAsGuardian: '点击按钮,连接路印钱包,作为守护人', describeTitleLocked: '已连接您的钱包,\n点击解锁后查看账户信息', labelLiquidityPageTitle: 'AMM 资金池', labelMinReceive: '最少接收量', labelFilter: '搜索', labelMiningPageTitle: '路印流动性挖矿', labelMiningPageViceTitle: '提供流动性以赚取奖励', labelMiningActiveDate: '奖励时间', labelMiningLiquidity: '流动性', labelMiningActivityReward: '活动奖励', labelMiningMyShare: '我的份额', labelMiningPlaceOrderBtn: '立刻下单', labelMiningViewDetails: '查看活动详情', labelMiningMaxSpread: '最大 spread', labelMiningMyReward: '我的奖励', labelMiningReward: '活动奖励', labelCookiesAgree: '同意', labelLimitMin: '最小下单量{{arg}}', labelAmmMinAnd: 'and', labelLimitMax: '{{arg}} 最大下单量{{arg}}', labelOrderSmall: 'Order too small (>= 100.5LRC)', labelEnterAmount: '请填写兑换数', labelAgreeLoopringTxt: '允许路印使用Cookies。', labelLayer2HistoryTransactions: '充值提现', labelLayer2HistoryTrades: '成交明细', labelLayer2HistoryAmmRecords: '出入金记录', labelTxnDetailHeader: '充值提现', labelDTxnDetailHeader: '充值记录', labelWTxnDetailHeader: '提现记录', labelTTxnDetailHeader: '转账记录', labelTxnDetailHash: '{{layer2}}哈希值', labelTxnDetailHashLv1: '以太坊哈希值', labelTxnDetailStatus: '状态', labelTxnDetailTime: '时间', labelTxnDetailFrom: '付款方', labelTxnDetailTo: '收款方', labelTxnDetailAmount: '数量', labelTxnDetailFee: '费用', labelTxnDetailMemo: '备注', labelTxnDetailProcessed: '已完成', labelTxnDetailProcessing: '处理中', labelTxnDetailFailed: '失败', labelAgreeConfirm: '确认', labelDisAgreeConfirm: '取消', labelImpactAgree: '请输入大写"AGREE"再次确认。', labelImpactTitle: '兑换二次确认', labelPriceExtraGreat: '您设置的价格已经{{compare}}市价的20%,您确定执行此操作吗?', labelPriceCompareGreat: '大于', labelPriceCompareLess: '小于', labelImpactExtraGreat: '您的交易金额将影响池子价格<1> {{value}},您确定执行此操作吗?', labelCalculating: '计算中...', labelFeeCalculating: '费用计算中...', labelAmmMyTransactions: '我的交易', labelAmmAllTransactions: '所有交易', labelWaitForAuth: '等待钱包签名', labelSignDenied: '签名已被用户拒绝', labelFirstSignDenied: '您的钱包不支持当前签名方法', labelUpdateAccountSuccess: '恭喜你!', labelUpdateAccountSuccess2: '您已经成功激活路印二层账号!', labelResetAccountSuccess: '恭喜你!', labelResetAccountSuccess2: '您已经成功重置路印二层账号密钥!', labelUpdateAccountSubmit: '开户申请以提交', labelUnlockAccountSuccess: '解锁成功!', labelUnlockAccountFailed: '解锁失败!', labelNotSupportTitle: '告知', labelNotAllowTrade: '抱歉!尊敬的用户,根据我们的使用条款,我们无法为您的IP地址提供交易下单和AMM入金功能。', labelKnown: '知道了', labelResetAccount: '重置{{layer2}}账号密钥', labelExportAccount: '导出账号', labelExportAccountNoPhotos: '请勿拍照', labelExportAccountDescription: '请对以下信息严格保密,不要明文存储到任何联网的电脑中;否则,您在交易所的资产可能会因为低价下单而受损失。', labelExportAccountCopy: '复制', labelExportAccountSuccess: '导出账号成功!', labelExportAccountFailed: '导出账号失败!', // labelCreateAccountApproveWaitForAuth: 'Waiting for <1>{{symbol}} Approve...', labelCreateAccountApproveDenied: 'Signature request rejected!', labelAmmSwitch: '切换', labelCreateAccountDepositDenied: '您以拒绝充值 {{value}} {{symbol}}, 账户未激活', labelSlippageAlert: '滑点过大将会影响您出金后收到的token数量', labelOrderGroup: 'Order Records', labelOrderTableOpenOrder: '当前委托', labelOrderTableOrderHistory: '历史委托', labelResetLayout: '重置布局', labelResetMobileLayout: 'Reset', labelBtnFix: '重置', labelProSell: '卖', labelProBuy: '买', labelProLimit: '限价', labelProMarket: '市价', labelProPrice: '价格', labelProBaseLabel: '数量', labelProQuoteLabel: '成交额', labelProLimitBtn: '{{tradeType}} {{symbol}}', labelProMarketBtn: '{{tradeType}} {{symbol}}', labelProOrderbook: '订单', labelProTrades: '交易', labelProToolbar24hChange: '24h 涨跌', labelProToolbar24hHigh: '24h 最高价', labelProToolbar24hLow: '24h 最低价', labelProToolbar24hBaseVol: '24h 成交量({{symbol}})', labelProToolbar24hQuoteVol: '24h 成交量({{symbol}})', labelErrorPricePrecisionLimit: '限价 {{symbol}},最多可保留小数点后 {{decimal} 位', labelDepthPrice: '价格({{symbol}})', labelDepthAmount: '数量({{symbol}})', labelDepthTotal: '累积', labelProChartTitle: '图表', labelProTimeDefault: '分时(1分钟)', labelProTime1m: '1分钟', labelProTime5m: '5分钟', labelProTime15m: '15分钟', labelProTime30m: '30分钟', labelProTime1H: '1小时', labelProTime2H: '2小时', labelProTime4H: '4小时', labelProTime12H: '12小时', labelProTime1D: '1天', labelProTime1W: '1周', labelProTime1M: '1月', labelProChartTradingView: '蜡烛图', labelProChartDepth: '深度图', labelProOrderPrice: '委托价', labelProOrderTotalAmount: '累积', labelSwapCancelled: '交易被取消', labelSuccessfully: '成功', labelWarning: '警告', labelFailure: '失败', labelPrompt: '提示', // labelSwapCancelled: '交易被取消', // labelSuccessfully: '成功', // labelWarning: '警告', // labelFailure: '失败', // labelPrompt: '提示', labelComingSoon: '敬请期待', labelTradeProHideOtherPairs: '隐藏其他交易对', labelCancelAllOrders: '确认撤销全部订单?', labelConfirm: '确定', labelSettingFee: 'Token Order for Fees', descriptionSettingFee: 'Change the token priority order to adjust which tokens will be used for fees first.', labelBtnEdit: 'Edit', labelSettingChargeFeeOrder: 'Token Order for Fees', labelDesSettingChargeFeeOrder: '{{loopringL2}} will use this token order when processing fees.', labelReset: 'Reset', labelQueryFeeOK: 'Save', depositLimit: 'Limit Orders \n Used to set the maximum or minimum price \n at which you are willing to buy or sell.', depositMarket: 'Market Orders \n Used to buy or sell immediately \n at the current market price.', labelTransactions: 'Transactions', labelMyRewards: 'My Rewards', labelClearAll: 'Clear All', labelProviderAgree: 'I have read, understand, and agree to the <1> Terms of Service .', labelNFTName: 'Name:', labelNFTDetail: 'Details', labelNFTTokenStandard: 'Token Standard:', labelNFTTokenMinted: 'Token Minted:', labelNFTDescription: 'Description:', labelNFTDate: 'Date:', labelNFTDeployContract: 'Deploy Contract', labelNFTSend: 'Send:', labelNFTDeploy: 'Deploy:', labelNFTDeploying: 'Deploying', labelNFTMyNFT: 'My NFTs - Collection: {{collection}}', labelNFTTokenID: 'ID:', labelNFTTYPE: 'Token Standard:', labelNFTRoyaltyPercentage: 'Royalty (%):', labelNFTID: 'ID:', labelNFTMinter: 'Minter:', labelNFTMetadata: 'Metadata:', labelNFTMint: 'Create NFT', labelNFTCreateCollection: '+ Create Collection', labelNFTTitleMyNFT: 'My NFTs', labelNFTTOTAL: 'Amount:', labelInformation: 'Notification', labelNoticeForProvider: 'Loopring currently supports the following wallet connections: {{name}}. Please make sure to use one of these when attempting to connect.', labelIKnow: 'OK', labelYes: 'Yes', labelNo: 'No', labelNoticeForNoMetaNFT: 'Your NFT does not contain Metadata or media information. \n Are you sure you still wish to {{ method }} this NFT?', labelAgreeConfirmNotShowAgain: 'I know & not show again', labelInvalidCID: 'Invalid CID. CIDv0 is start with `Qm`, CIDv1 only works for dag-pb', labelInvalidAddress: 'Invalid address, ENS', labelInvalidisCFAddress: 'Loopring Counterfactual wallet is disabled {{way}} {{token}}', labelInvalidisContract1XAddress: 'Loopring wallet 1.x is disabled {{way}} {{token}}', labelInvalidisContractAddress: '{{way}} of {{token}} to Contract wallet is not available', labelInvalidisLoopringAddress: 'This address does not yet have an active {{loopringL2}}, {{way}} of {{token}} is disabled!', labelInvalidisSameAddress: 'Cannot {{way}} to your own address.', labelTradeRaceRanking: 'Trading Leaderboard', labelTradeRaceYourRanking: 'Your ranking', labelTradeRaceGoTrading: 'Go to trade', labelTradeReadRule: 'Read Rules', labelTradeRaceRewards: 'Rewards', labelTradeRaceRules: 'Activity Rules', labelTradeRaceStart: 'Activity ends in:', labelTradeRaceReady: 'Activity starts in:', labelTradeRaceEnd: 'Activity has ended', labelDay: 'Days', labelHours: 'Hours', labelMinutes: 'Minutes', labelSeconds: 'Seconds', labelIsNotFeeToken: 'Please deposit {{symbols}}, or {{lastSymbol}} to activate your {{loopringL2}} account.', labelIsETHDepositAlert: 'Please reserve enough ETH in your account to pay for gas!', labelIsNotEnoughFeeToken: 'Please deposit enough token to cover the activation fee: {{fee}} {{symbol}}. Remaining token will appear in your asset after activation', depositNFTAddressLabelPlaceholder: 'please input NFT contract address...', mintNFTAddressLabelPlaceholder: '(CIDv0 or dag-pb CIDv1) eg: QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR', depositNFTIdLabelPlaceholder: 'please input NFT id...', nftDepositDescription: 'Creates a smart contract on {{ethereumL1}}, \n which requires a gas fee. NFTs minted \nhere remain on {{loopringL2}} until deployed.', labelNFTDescribe: 'Description:', labelNFTTitle: 'Amount', labelNFTDepositInputTitle: 'Amount:', labelNFTTId: 'NFT Token ID:', labelNFTCid: 'IPFS CIDv0 or dag-pb CIDv1:(Store Metadata Information)', labelNFTType: 'Token Standard:', labelNFTAccess: 'Allow Loopring to spend {{symbol}}', labelDeployDenied: 'Signature request was rejected', labelNFTTokenDeployWaitForAuth: 'Allow Loopring to deploy {{symbol}}?', labelDeployFailed: 'Deploy of {{symbol}} has failed!', labelDeploySubmit: 'Deploy of {{symbol}} has been submitted!', labelMint: 'Mint', labelMintDenied: 'Signature request was rejected', labelNFTTokenMintWaitForAuth: 'Allow Loopring to Create {{symbol}}?', labelMintFailed: 'Create of {{symbol}} has failed!', labelMintSuccess: 'Create of {{symbol}} has been submitted!', labelNFTMintBtn: 'Create NFT', labelNFTMintNoMetaBtn: 'Wrong Metadata', labelNFTMintNoMetaDetail: 'Your NFT metadata should identify <1>name, image, and royalty_percentage (integer from 0 to 10).', nftDeployDescription: 'Deploy Collection', nftDeployTitle: 'Deploy Contract', nftMintTitle: 'Create NFT', nftMintBtn: 'Create NFT', labelMintInProgress: 'Processing...', labelNFTDeployBtn: 'Deploy Contract', labelNFTDeployBroker: 'Deploy Broker:', labelDeployInProgress: 'Processing...', labelNFTDeployTitle: 'Deploy Contract', labelVendor: 'Buy with Card', labelLock: 'Lock', labelWalletToWallet: 'The connected wallet is a contract address which cannot be used (Except Recover Wallet). If you are connecting a mobile Loopring Smart Wallet, you can protect it and manage guardians within the app.', labelWalletAddAsGuardian: 'Add a guardian', labelWalletInputGuardianCode: 'Input 6 digital Code and Approve', labelWalletScanQRCode: 'Using Loopring Wallet, scan the QR code.', labelWalletInputGuardianCodeDes: 'Please contact the owner to obtain the approval code and enter it below.', labelWalletGuardianList: 'Guardian List', labelWalletRequestRecovery: 'Request for Wallet Recovery', labelWalletLoopringSmartWallet: 'The connected wallet is a Loopring Smart Wallet. Please use your Loopring Wallet mobile app to add Guardians.', labelWalletNonLoopringSmartWallet: 'The connected wallet is a non-Loopring smart contract wallet, which cannot be set as a Guardian. Please try again using a different wallet.', labelWalletGuardianHint: 'Easily add other Loopring Wallets as Guardians to secure your identity and crypto assets. After entering the wallet address, the user will receive a notification of the request directly in their Loopring Wallet app. Invite your friends and family to use the Loopring Wallet.', labelWalletLockTitle: 'Lock/unlock Wallet', labelWalletLockDes: 'Who I Protect', labelWalletValidationTitle: 'Approval Requests', labelWalletValidationDes: 'Guardian Request Handling', labelWalletHistoryTitle: 'View History', labelAddProtector: 'add Guardian', labelUnknown: 'Unknown', labelApprove: 'Approve', labelReject: 'Reject', labelWalletApprove: 'Approve Signature', labelCommonList: 'Waiting for your Approve List', labelLogList: 'Log List', labelWalletReject: 'Reject Signature', labelLockAccountSuccess: 'Lock Account Success', labelLockAccountFailed: 'Lock Account Failed', labelApproveSuccess: 'Approve Signature Success', labelApproveFailed: 'Approve Signature Failed', labelRejectSuccess: 'Reject Signature Success', labelRejectFailed: 'Reject Signature Failed', labelYourBalance: 'Your {{layer2}} have: {{balance}}', labelTxGuardianADD_GUARDIAN: 'ADD GUARDIAN', labelTxGuardianGUARDIAN_CONFIRM_ADDITION: 'GUARDIAN CONFIRM ADDITION', labelTxGuardianGUARDIAN_REJECT_ADDITION: 'GUARDIAN REJECT ADDITION', labelTxGuardianGUARDIAN_APPROVE: 'GUARDIAN APPROVE', labelTxGuardianAPPROVE_RECOVER: 'RECOVER WALLET', // RECOVER 16 labelTxGuardianAPPROVE_TRANSFER: 'OVER DAILY QUOTA TRANSFER', // APPROVE TRANSFER 18 labelTxGuardianAPPROVE_TOKEN_APPROVE: 'TOKEN ACCESS', // 23 labelTxGuardianADD_GUARDIAN_WA: 'ADD GUARDIAN', // 34 labelTxGuardianREMOVE_GUARDIAN_WA: 'REMOVE GUARDIAN', // 35 labelTxGuardianUNLOCK_WALLET_WA: 'UNLOCK WALLET', // 37 labelTxGuardianRESET_GUARDIANS_WA: 'RESET GUARDIANS', // 200 labelTxGuardianCONTACT_UPDATE_WA: 'RESET GUARDIANS', // 201 labelTxGuardianCALL_CONTRACT_WA: 'CALL CONTRACT', labelTxGuardian_recovery: 'recovery wallet', labelTxGuardian_transfer: 'over daily quota transfer', labelTxGuardian_add_guardian: 'add guardian', labelTxGuardian_remove_guardian: 'remove guardian', labelTxGuardian_unlock_wallet: 'unlock wallet', labelTxGuardian_deposit_wallet: 'deposit', labelTxGuardianApprove: 'APPROVE', labelTxGuardianReject: 'REJECT', labelReActiveAccount: 'Re-Activate Account', labelWalletSignType: 'Request for {{type}}', labelSpotTrading: 'Spot Trading Volume (30d in ETH)', labelTradeSpot: 'Trade Spot', labelBuyToken: 'Buy {{token}}', labelCurrentlyLevel: 'Currently {{value}} {{token}}', labelLRCBalance: 'LRC Balance', labelNoticeForForAccountFrozen: 'Your wallet’s L2 account is locked. While locked, you can’t perform any L2 operations. If you require further assistance, please send an email to support@loopring.io.', labelAction: 'action', labelGoExplore: 'View transactions on the <1>Loopring Block Explorer.', labelNOETH: 'Need ETH for gas', labelBanxaFeeFree: 'zero fees for a limited time', labellimit: 'limit', labelmarket: 'Market', labelswap: 'Swap', labelamm: 'Amm', labelActiveAccountTitle: 'Activate {{loopringL2}} Account', labelDepositTitle: 'Add Assets from My {{l1Symbol}}', labelDepositTitleAndActive: 'Add Asset from My {{l1Symbol}} & Activate', labelDepositAndActiveBtn: 'Activate {{loopringL2}}', labelDepositTitleActive: 'Activate {{loopringL2}}', depositLabelBtn: 'Receive', labelL2ToL1Title: 'Send to {{l1Symbol}}', labelL2ToMyL1Title: 'Send to My {{l1Symbol}}', labelL2ToOtherL1Title: 'Send to Another {{l1Symbol}}', labelL2ToL1DeployTitle: 'Deploy & Send to {{l1Symbol}}', labelL2toL1EnterToken: 'Select Token', labelSendL1Btn: 'Send', labelSendL1DeployBtn: 'Deploy & Send', labelL2toL1BtnExceed: 'Exceed Max Fast Withdraw amount: {{arg}}!', labelL2toL1BtnExceedWithFee: 'Insufficient balance (with fee)', labelL2toL1Address: '{{l1ChainName}} Address', labelL2toL1MyAddress: 'To my {{l1Symbol}}', labelL2toL1AddressInput: 'Please input the address', labelL2toL1Fee: 'Select payment token', labelL2toL1Fast: 'Fast', labelL2toL1Standard: 'Standard', labelL2toL1LinkRecent: 'Recent withdrawal history', labelL2toL2Title: 'Send to Another {{loopringL2}}', labelL2toL2EnterToken: 'Select Token', transferDescription: 'Send assets to any valid {{l1ChainName}} address instantly.\n Please make sure the recipient address accepts \n {{loopringL2}} payments before you proceed.', labelL2toL2Btn: 'Send', labelL2toL2Address: 'Recipient', labelL2toL2AddressInput: 'Please input address / ENS / Account ID', labelL2toL2Memo: 'Memo (Optional)', labelL2toL2MemoPlaceholder: 'Please input the memo', labelL2toL2FeeChoose: 'Select payment token', labelL2toL2Fee: 'Network Fee', labelL2toL2FeeNotEnough: 'Insufficient balance', labelL2toL2FeeFastNotAllowEnough: 'Please choose Standard!', labelL2toL2LinkRecent: 'Recent send history', labelL2toL2ExchangeError: 'Sending to an Exchange Address {{l2symbol}} account is not supported. {{loopringL2}} accounts cannot be activated on Exchange wallet addresses. Instead, please send to the {{l1Symbol}} account associated with this address.', labelL2toL2SmartWalletError: 'This wallet binds with smart contract that does not support {{loopringLayer2}}. You will need to send funds to the {{l1Symbol}} account. ', labelActiveLayer2: 'Activate {{loopringL2}}', labelAddAsset: 'Receive', labelAddAssetBtn: 'Receive', labelSendAsset: 'Send', labelSendAssetBtn: 'Send', labelSend: 'Send', labelReceive: 'Receive', labelWaitingRefer: 'Waiting for approval', labelL1toL2WaitForAuth: 'Please confirm to receive {{value}} {{symbol}} to {{to}} {{loopringL2}}.', labelL1toL2Denied: 'You rejected to receive {{value}} {{symbol}}.', labelL1toL2Failed: 'Add asset request of {{value}} {{symbol}} failed!', labelL1toL2Submit: 'Add asset request has been submitted. <1>', labelL1toL2NeedApprove: 'Allow Loopring Exchange to spend {{symbol}}', labelL2toL1InProgress: 'Processing...', labelL2toL1Failed: 'Sent {{value}} {{symbol}} to {{l1Symbol}} has failed!', labelL2toL1Success: 'Sent {{value}} {{symbol}} to {{l1Symbol}} was successful!', labelL2toL2InProgress: 'Processing...', labelL2toL2Failed: 'Sent {{value}} {{symbol}} from my {{loopringL2}} to another {{loopringL2}} failed!', labelL2toL2Success: 'Sent {{value}} {{symbol}} was successful!', labelUpdateAccountFailed: 'Activate {{loopringL2}} has failed!', labelCreateAccountSubmit: "Activation of {{loopringL2}} with deposit of {{value}} {{symbol}} has been submitted! \n Approximately {{count}} minutes remaining...',", labelCreateAccountFailed: 'Activation of {{loopringL2}} with deposit of {{value}} {{symbol}} has failed!', labelL1toL2Hash: 'Recent transactions (From my {{l1Symbol}} to my {{l2Symbol}})', labelL1toL2HashEmpty: 'My {{l1Symbol}} \u2192 {{loopringL2}} transactions will show up here.', labelL1toL2Record: 'Receive {{value}} {{symbol}}', labelNFTSendL2Btn: 'To Another {{loopringL2}}', labelNFTSendMyL1Btn: 'To My {{l1Symbol}}', labelNFTSendOtherL1Btn: 'To Other {{l1Symbol}}', labelNFTDeploySendMyL1: 'To My {{l1Symbol}} & Deploy Contract', labelNFTDeploySendAnotherL1: 'To another {{l1Symbol}} & Deploy Contract', labelGuid: 'Go to Guide', labelOK: 'Ok', labelL2toL2InvalidAddr: 'Invalid address or ENS', labelL2toL2AddressNotLoopring: '<0> This address does not have an activated {{loopringL2}}. Please ensure the recipient can access {{loopringL2}} before sending.', labelL2toL2AddressType: 'Address Type', labelL2toL2OriginDesc: 'Please select the address source. Note: the following trading platforms currently do not support {{loopringL2}} transfers (Binance, Huobi, Okex…)', labelL2toL2OriginBtnExchange: 'Exchange', labelL2toL2OriginBtnWallet: 'Wallet', labelL2toL2Confirm: 'Confirm', labelL2toL2TokenAmount: 'Token Amount', labelActiveAccountFeeNotEnough: 'Insufficient balance <1>Add assets', labelNFTTransferTX: '{{l2Symbol}} \u2192 {{l2Symbol}}', labelNFTWithdrawTX: '{{l2Symbol}} \u2192 {{l1Symbol}}', labelNFTDepositTX: '{{l1Symbol}} \u2192 {{l2Symbol}}', labelNFTDeposit: 'Receive {{loopringL2}} NFT', labelNFTDepositNeedApprove: 'Allow Loopring to spend {{symbol}} and deposit it?', labelNFTDepositBtn: 'Receive NFT', labelNFTDepositTitle: 'Receive NFT from my {{l1Symbol}}', labelNFTContractAddress: 'Contract:', labelNFTAmount: 'Amount:', labelNFTTokenDepositWaitForAuth: 'Please confirm to send {{loopringL2}} {{symbol}}', nftMintDescription: 'Paste in the CID that you obtained from uploading \n the metadata.json file (point 11 above) - if successful,\n the data from the metadata.json file you created contained\n within the folder will populate the Name\n and Image below.', labelNFTMintInputTitle: 'Amount <1>\uFE61', labelL1toL2Vendor: 'Use a Loopring partner to deposit funds.\nOnce your order is confirmed by Loopring,\n it will be added to your balance within 2 minutes.', depositLabelTo: 'To address, Account ID or ENS.', labelAddressNotLoopring: "Account doesn't have an active {{loopringL2}}", labelMINTNFTTitle: 'Create NFT (ERC1155)', labelIPFSUploadTitle: 'Preview Image (Dimensions: 1:1) <1>\uFE61<2>\u2139', labelIPFSUploadTooltips: 'The file uploaded here will be used as the cover image when displaying NFT item.', labelIPFSUploadMediaTitle: 'Multimedia Content (image, audio, video and 3D)<1>\u2139', labelIPFSUploadMediaTooltips: 'If no file is uploaded here, it will use the same content as “Preview Image”.', labelLoadDes: 'Drag or click to upload files ({{types}}, max size: {{size}}MB)', labelUpload: 'upload', labelMintNoImageBtn: 'Please upload image', labelMintUserAgree: 'Please agree to the terms of service', labelMintTradeValueBtn: 'Please input amount(1 - 10,000)', labelMintNoRoyaltyPercentageBtn: 'Please input Royalty', labelMintWrongRoyaltyBtn: 'Royalty should be (0 - 10)', labelMintNoNameBtn: 'Please input name', labelNFTMetaBtn: 'Upload metadata & create', labelMintName: 'Name <1>\uFE61', labelMintCollection: 'Choose Collection <1>{{required}}<2>', labelMintCollectionTooltips: 'This is the collection where your NFT will appear.', labelMintRoyaltyPercentage: 'Royalty (%) <1>\uFE61<2>\u2139', labelMintRoyaltyPercentageRange: 'Max Int:', labelMintRoyaltyPercentageTooltips: 'Represents the percentage to be received from each subsequent resale (max 10%).', labelMintDescription: 'Description <1>\u2139', labelMintDescriptionTooltips: "The description will be included on the NFT's detail page beneath it's image.", labelMintProperty: 'Properties (Limit 64) <1>\u2139', labelMintPropertyTooltips: 'Tags can be added to the NFT for easy searchability and distinction', labelPropertyAdd: 'Add property', labelMintNFT: 'Create NFT', labelL1toL2NFT: 'Receive NFT', labelMyAssetsNFT: 'My NFTs', labelTransactionNFT: 'Transactions', labelMintPropertyKey: 'Key', labelMintPropertyValue: 'Value', labelNFTProperty: 'Properties:', labelConfirmMint: 'Confirm Metadata', labelUseIpfsMintAgree: 'I confirm that the NFT minted does not infringe on copyright laws or contain illegal, explicit, sensitive, adult themed, or any other content considered NSFW. We reserve the right to hide inappropriate content if an NFT is discovered to be harmful.', labelL1toL2TitleBridge: 'Add {{loopringL2}} Assets', labelL1toL2TitleBridgeNoConnect: 'Connect your {{ethereumL1}} Wallet to transfer assets to any {{loopringL2}} account', labelPayer: 'My Wallet:', labelL1toL2TokenAmount: 'Token Amount', labelL1toL2From: 'From', labelL1toL2TO: 'To {{loopringL2}}', labelAddAssetTitle: 'Add {{loopringL2}} {{symbol}} assets', labelSendAssetTitle: 'Send {{loopringL2}} {{symbol}} assets', labelAddAssetHowto: 'How would you like to add {{loopringL2}} assets?', labelAddAssetTitleActive: 'Add assets & Activate', labelFromMyL1: 'From my {{l1Symbol}} account', labelFromOtherL1: 'From another {{l1Symbol}} account', labelBuyWithCard: 'On-Ramp From Fiat', labelFromOtherL2: 'From another {{loopringL2}} account', labelFromExchange: 'From an exchange', labelOpenInWalletApp: 'Open in wallet app/extension', labelConnectWithDapp: 'Connect with Dapp', labelOpenInWalletTitle: 'Open in wallet', labelOpenInWalletDetail: `URL for adding funds has been copied. You can choose either way to continue:`, labelOpenInWalletDetailLi1: `Open your wallet app and paste the URL into its internal Dapp browser`, labelOpenInWalletDetailLi2: `Open your desktop Chrome browser and paste the URL in Chrome`, labelActiveL2Btn: 'Activate {{loopringL2}}', labelWrongNetworkGuideTitle: 'Wrong Network', labelWrongNetworkGuide: 'Your chosen network is not currently supported on Loopring. Please choose {{l1ChainName}} main Network or test Network Goerli', labelSenAssetTitle: 'Send {{symbol}} from {{loopringL2}}', labelSendTOL2: 'To another {{loopringL2}} account', labelSendToMyL1: 'To my {{l1Symbol}} account', labelSendToOtherL1: 'To another {{l1Symbol}} account \n(incl. exchange)', labelSendAssetHowto: 'Where would you like to send your crypto to', labelL1toL2: 'Add {{loopringL2}} assets From My {{l1Symbol}}', labelActivatedAccountChargeFeeList: 'Please make sure one of the below tokens with the minimum quantity in your {{loopringL2}} account to proceed', labelReceiveAddress: 'Receive Address', labelAssets: '{{loopringL2}} Assets', labelReceiveAddressGuide: 'Please use a {{loopringL2}} account when transferring to avoid loss of assets ({{symbol}}).', labelL2toL2: 'Send to another {{loopringL2}}', labelL2toL1: 'Send to {{l1Symbol}}', labelBenefitL2: "As {{l1ChainName}}'s first ever zkRollup, {{loopringL2}} allows you to avoid costly gas fees and network congestion with the same security as mainnet - 100x cheaper and faster.\n\nActivating your {{loopringL2}} account requires a small payment fee. ", labelNotBalancePayForActive: 'Insufficient balance in your {{loopringL2}} account', labelEnoughBalancePayForActive: 'You have enough balance to pay for {{loopringL2}} creation.', labelHaveInProcessingL1toL2: 'If you have already started the deposit, please be patient and recheck as transactions on {{l1ChainName}} can take up to 30 minutes.', labelWaitingL1toL2: 'Please wait', labelAddAssetGateBtn: 'Add assets', labelActiveLayer2Btn: 'Activate {{loopringL2}}', labelActiveLayer2PayBtn: 'Pay Activation Fee', labelBalanceActiveAccountFee: '{{symbol}}: <2>Fee {{fee}};<3>My {{loopringL2}} balance: {{count}}', labelToAddressShouldLoopring: 'To address is no {{loopringL2}}', labelBridgeSendTo: 'Send to (address, Account ID or ENS)', labelInvalidAddressClick: 'Invalid Wallet Address, {{way}} of {{token}} is disabled! <1>Click to input another receive address ', labelENSShouldConnect: 'Receive address is an ENS, please connect wallet to check real address', labelToken: 'Token', labelMinRequirement: 'Min Requirement', labelAvailability: 'Availability', labelWhatProvider: 'Which provider would you like to use?', labelMemo: 'Memo', labelAdvanceMint: 'Advance Create NFT', labelWalletTypeDes: 'Please confirm the address type again to ensure the assets are not mistakenly sent to the exchange address. ', labelWalletTypeOptions: '{{type}} Wallet', labelWalletTypeOtherSmart: 'Other Smart', labelWalletTypeLoopring: 'Loopring', labelWalletTypeEOA: 'EOA', labelWalletTypeExchange: 'Exchange', labelEOADes: 'There is no smart contract binds with this wallet address. (e.g. MetaMask, imtoken, Ledger, Trezor, etc....) ', labelLoopringDes: 'This wallet is created using Loopring Wallet mobile app and binds with Loopring smart contract.', labelOtherSmartDes: 'This wallet binds with smart contract that does not support {{loopringLayer2}}. You will need to send funds to the {{l1Symbol}} account. ', labelExchangeDes: 'The following trading platforms currently do not support {{loopringL2}} transfers (Binance, Coinbase, etc...). You will need to send funds to the {{l1Symbol}} account. ', labelExchangeTypeDes: 'Please select the address source:', labelNonExchangeTypeDes: 'eg: Loopring Wallet, Metamask, Coinbase Wallet, imtoken, Ledger, Trezor... EOA wallet', labelNonExchangeType: 'Non-Exchange Wallet', labelExchangeType: 'Exchange', labelExchangeBinance: 'Binance', labelExchangeBinanceDes: '', labelExchangeHuobi: 'Huobi', labelExchangeHuobiDes: 'Transactions need to wait 24 hours', labelExchangeCoinbase: 'Coinbase', labelExchangeOthers: 'Other Exchanges', labelExchangeOthersDes: '', labelL2toL1AddressType: 'Address Type', labelConfirmBtrade: 'Confirm CEX Support', labelConfirmDetail: '<0>Before withdrawing, please confirm with your CEX support that they accept deposits from smart contracts.' + '<1>{{l2Symbol}} to {{l1Symbol}} withdrawing is performed via a smart contract. The CEX depositing address may not be able to automatically acknowledge the deposit.' + '<2>If the deposit does not appear at the CEX address within 24 hours, please contact your CEX support and ask they manually acknowledge the transaction.', labelBtradeUnderstand: 'I understand and acknowledge the risk', labelMintFee: 'Create Fee', labelMintFeeNotEnough: 'Insufficient balance', labelMintFeeChoose: 'Select payment token', labelLayerSwapUnderstand: 'I understand and acknowledge the risk', labelIUnderStand: 'I Understand', labelLayerSwapUnderstandDes: 'LayerSwap is a 3rd party App service provider to help move tokens from exchange to {{loopringL2}} directly. If you have any concerns regarding their service, please check out their <1>TOS.', labelInvestAmmTitle: 'AMM Pools', labelInvestBalanceTitle: 'My Investments', labelInvestDualRefreshErrorTitle: 'Subscription Failed', labelInvestDualRefreshError: 'The subscription of {{token1}}/{{token2}} Dual Investment failed.', labelTransactionsLink: 'Transactions', labelAMMTransactionsLink: 'View Pool Transactions', labelNFTMintWrongCIDBtn: 'Wrong MetaData format', labelWithdrawBtn: 'Withdraw', labelFWithdrawFee: 'Fee', labelFWithdrawNotEnough: 'Insufficient balance', labelForceWithdrawTitle: 'Force Withdraw', labelForceWithdrawWaitForAuth: 'Please confirm to force withdraw {{symbol}}', labelForceWithdrawDenied: 'You rejected to force withdraw {{symbol}}.', labelForceWithdrawInProgress: 'Processing...', labelForceWithdrawFailed: 'Force withdraw has failed!', labelForceWithdrawSubmit: 'Force withdraw has been submitted', labelForceWithdrawToken: 'Token Amount', labelForceWithdrawFee: 'Network Fee', labelForceWithdrawEnterToken: 'Select Token', labelPleaseForceWithdrawAddress: 'Please enter the address you wish to withdraw from', labelForceWithdrawAddress: 'The address you wish to withdraw from', labelForceWithdrawDes: "If the recipient doesn't have an active {{loopringL2}} account, you will be able to withdraw the token from {{l2Symbol}} to {{l1ChainName}} {{l1Symbol}}. This process is usually only needed when tokens were sent to a CEX address using {{loopringL2}}. Since the CEX does not have access to the {{l2Symbol}} account, you will need to perform this action to reclaim the tokens.", labelForceWithdrawConfirm: 'This feature allows a user to move their {{l2Symbol}} tokens to the {{l1Symbol}} address. The target address must either be a wallet or exchange address', labelForceWithdrawConfirm1: 'This operation usually requires more than 30 minutes to take effect, as it needs to interact with {{l1ChainName}} Mainnet. Please be patient.', labelNFTSendBtn: 'Send', labelNFTProperties: 'Properties', labelNFTDescription2: 'Description', labelForceWithdrawNotAvailable: '{{loopringL2}} account is activated in this address. For security reason, Loopring would not allow other user to force withdraw token from its {{l2Symbol}} to {{l1symbol}} anymore', labelForceWithdrawNoToken: 'No token is detected from this address to operate', labelForceWithdrawBtn: 'Force Withdraw', labelInvestDefiTitle: 'ETH Staking', labelInvestDefDeposit: 'Subscribe', labelInvestDefWithdraw: 'Redeem', labelNFTDepositLabel: 'Receive NFT', labelDefiFee: 'Fee', labelDefiMin: 'Minimum of {{arg}}', labelDefiNoEnough: 'Insufficient balance', labelDefiMaxBalance: 'It is not possible for the Loopring pool to fulfil your complete request at the moment. You can only redeem {{maxValue}} now.\n' + 'You can choose one of the following approaches for the remaining amount:', labelDefiMaxBalance1: '
  • Withdraw {{symbol}} to {{l1Symbol}} and trade through 1Inch or {{type}}, etc...
  • ' + '
  • The Loopring pool will rebalance soon. Please come back later to redeem.
  • ', labelDefiNoBalance: 'It is not possible for the Loopring pool to fulfil your complete request at the moment.' + 'You can choose one of the following approaches for the remaining amount:', labelDefiNoBalanceList: '
  • Withdraw {{symbol}} to {{l1Symbol}} and trade through 1Inch or {{type}}, etc...
  • ' + '
  • The Loopring pool will rebalance soon. Please come back later to redeem.
  • ', labelDefiMaxBalanceJoin: "The quota is almost sold out and can't fulfil your complete order. You can only subscribe {{maxValue}} now. Loopring will setup the pool soon, please revisit for subscription later. ", labelDefiNoBalanceJoin: 'Loopring will set up the pool soon. Please come back later to subscribe.', labelInvestBtn: 'Subscribe', labelRedeemBtn: 'Redeem', labelVipTitle: 'VIP', labelSecurity: 'Security', labelFeeTitleList: 'Fee', labelInvestOverviewTitle: 'Overview', labelTitleOverviewToken: 'Total Investment Tokens', labelInvestType_AMM: 'AMM Pools', labelInvestType_STAKE: 'ETH Staking', labelInvestType_DUAL: 'Dual Investment', labelInvestType_STAKELRC: 'LRC Staking', labelInvestAll: 'Mixed', labelInvestFlexible: 'Flexible', labelInvestDuration: 'Duration', labelDefiOrderTable: 'ETH Staking', labelTitleMyInvestAvailable: 'My Holding Tokens', labelViewMore: 'View more', labelInvestSuccess: 'Successfully {{type}} {{symbol}}', labelInvestFailed: 'Subscribe Failed', labelWSETHDefiRiskTitle: 'What is ETH Staking via Lido?', labelRETHDefiRiskTitle: 'What is ETH Staking via Rocket Pool?', labelWSETHDefiRisk: '

    Lido is a liquid staking solution for ETH 2.0 backed by industry-leading staking providers. Lido lets users stake their ETH - without locking assets or maintaining infrastructure.

    ' + '

    When using Lido to stake your ETH on the {{l1ChainName}} beacon chain, users will receive a token (stETH), which represents their ETH on the {{l1ChainName}} beacon chain on a 1:1 basis. It effectively acts as a bridge bringing ETH 2.0’s staking rewards to ETH 1.0.

    ' + "

    wstETH is the wrapped version of stETH. The total amount of wstETH doesn't change after users receive the token. Instead, the token’s value increase over time to reflect ETH staking rewards earned.

    ", labelRETHDefiRisk: '

    Rocket Pool is the first truly decentralized {{l1ChainName}} staking pool. Rocket Pool’s liquid staking token allows anyone to earn staking rewards easily without running staking software or locking assets. Rocket Pool handles all of the {{l1ChainName}} validator operations with smart contracts on the Execution layer.

    ' + "

    Acquiring and holding rETH in your wallet means that you are staking ETH. rETH's value continuously increases relative to ETH, indicating the daily stake reward received.

    " + '

    ', labelWSETHDefiRisk2: "<0>It is important to note that users can't redeem wstETH for ETH until phase 2 of {{l1ChainName}} 2.0. However, users are able to trade wstETH for ETH on various exchanges at market prices.

    " + '<1>Loopring will provide a pool to allow users to trade wstETH for ETH directly on {{layer2}}. The pool will rebalance periodically when it reaches a specific threshold. If there is not enough inventory on {{layer2}}, user can always withdraw their wstETH tokens to Layer 1 and swap for ETH in Lido, Curve, or 1inch.

    ', labelRETHDefiRisk2: '<0>Loopring will provide a pool to allow users to trade rETH for ETH directly on {{layer2}}. The pool will rebalance periodically when it reaches a specific threshold. If there is not enough inventory on {{layer2}}, users can always withdraw their rETH tokens to Layer 1 and swap for ETH in Rocket Pool, 1Inch, etc… ' + '<1>', labelDefiAgree: 'I have read and understand the risk warning.', labelDefiInvest: 'Defi Earn', labelLRCStakingInvest: 'LRC staking', labelLRCStakingRedeemInvest: 'LRC staking Redeem', labelDefiClose: 'This service is temporarily unavailable as we set up the pool. This process may take several hours to complete. Thank you for your patience!', labelCreateCollection: 'Create Collection', labelCollectionCreateName: 'Contract address for your collection', labelCollectionCreateERC1155: 'Collection ERC-1155', labelCollectionCreateWaiting: 'Waiting for create Collection token Address', labelMintSelect: 'Choose Creation Method', labelMintSelectDes: 'Choose the most suitable approach for your needs.', labelCollectionCreateFailed: 'Create Collection token Address Failed', labelCollectionMetaTitle: 'Import Metadata from IPFS', labelAdvanceCreateCollection: 'Advance Create Collection', labelCreateCollectionSuccess: 'Collection create was successful', labelCreateCollectionFailed: 'Collection create has failed', labelCollectionAdvanceJSON: 'NFT Collection information follow this format: ', labelCopyDemo: 'Click to copy the demo', labelCollectionCreatBtn: 'Create Collection', labelEnterMeta: 'Enter Collection Metadata', labelMintGuid: 'Fill up content in GUI and let Loopring to generate necessary metadata and upload to IPFS for you, then use "Mint" to create your NFT.', labelAdMintGuid: 'Generate all the required metadata and upload to IPFS by yourself first, then use "Advanced Create NFT" to create your NFT.', labelFilterTradeNFTSell: 'Sell', labelFilterTradeNFTSelf: 'Self Trade', labelFilterTradeNFTBuy: 'Buy', labelAdMintTitle: 'Advance Create NFT', labelCopyNFTDemo: 'Copy NFT Demo', labelSelectCollection: 'Choose or Create a Collection to Create Your Own NFT', labelSelectCollectionDes: 'A NFT Collection can help you manage and group your NFTs', labelChooseCollectionBtn: 'Choose a Collection to Create NFT', labelNFTMint721Btn: 'ERC721 will coming soon', labelADMint1: 'Prepare NFT metadata', labelADMint2: 'Fill in the IPFS CID', labelADMint3: 'Preview & Create NFT', labelADMintSelect: 'Prepare NFT metadata with proper collection_metadata value', labelHasData: 'Has generated metadata with collection_metadata field', labelNoData: 'Hasn’t generated metadata with collection_metadata field', labelChooseCollection: 'Choose a collection', labelBanner: 'Banner (Dimensions: 3:1)', labelBannerDes: 'Drag or click to upload files ({{types}}, max size: {{size}}MB)', labelAvatar: 'Avatar (Dimensions: 1:1)', labelAvatarDes: 'Max size: {{size}}MB)', labelTileUri: 'Tile (Dimensions: 5:7) <1>\uFE61', labelTileUriDes: 'Drag or click to upload files ({{types}}, max size: {{size}}MB)', labelCollectionDescription: 'Description <1>\u2139', labelCollectionDescriptionTooltips: 'You can describe your collection here. 0 of 1000 characters used', labelCollectionName: 'Collection Name <1>\uFE61', labelCollectionCreateBtn: 'Create Collection', labelCollectionRequiredName: 'Please input Name', labelCollectionRequiredTileUri: 'Please input tile', labelCollectionIsUploading: 'Source is loading', labelMintNext: 'Next', labelMintCollectionInput: 'Please input contract address', labelMintCid: 'Please input IPFS CID', labelMintBack: 'Back', labelTokenAdMintBtn: 'Enter Amount', labelMintSubmitBtn: 'Create Your NFT', labelMintIPFSCIDDes: 'Fill in the IPFS CID for NFT metadata', labelNFTMintSimpleBtn: 'Create NFT', labelCollectionEditBtn: 'Edit Collection', labelCopyMetaClip: 'Metadata Copied to Clipboard', labelCollectionMetaNoNameORTileUri: 'Your Collection metadata is not setup {{type}}, please go to collection panel edit!', labelCollectionMetaMiss: 'Your NFT metadata is no not setup {{type}}.', labelCollectionMetaError: 'Your NFT metadata is no not setup {{type}}, please check and fix it from your IPFS site', labelCollectionMetaErrorType: 'correct `royalty_percentage` from 0 to 10', labelNFTServerRefresh: "Click to refresh the NFT's metadata. This process usually takes around 30 minutes.", labelNFTServerRefreshSubmit: 'Refresh command submitted', labelNFTCollection: 'Collection', labelNFTCollectionName: 'Collection Name:', labelMyCollection: 'My Collections', labelCounterFactualNFT: '{{l2Symbol}} NFT:', labelCopyUrlClip: 'URL Copied to Clipboard!', labelCollectionMetaData: 'Collection MetaData', labelViewEtherscan: 'Etherscan', labelNFTMyNFTCollection: 'View by Collection', labelNFTMyNFTList: 'View by item', labelNoCollectionCover: 'No Cover Media', labelNoNFTCover: 'No Media Resource', labelNFTAmountValue: 'Amount: {{value}}', labelNFTAmountSimpleValue: ' \u2A09 {{value}}', labelCollectionItemValue: 'Item: {{value}}', labelCollectionItemSimpleValue: ' \u2A09 {{value}}', labelMyCollectionsDes: "Legacy NFTs created in Loopring don't contain collection information. We have added the feature to allow creators to import the collection information so that those NFTs can be categorized well. <1>Go to Import Collection for Legacy NFT", labelNFTGuid: 'Please fill in the appropriate collection metadata field value in your NFT metadata with this string first, then upload it to IPFS to retrieve the CID to continue. <1>view more ', labelChooseCollectionTooltips: 'This is the collection where your NFT will appear. \n Note: NFT minted under collection will be bound with different contract address than previous created one. If you have incomplete work to finish and would like them created under previous contract address, you can still use the legacy creation method under <1>https://legacy-nft.loopring.io/.', labelMintPreview: 'Back', labelMintNoCollectionBtn: 'Please Choose Collection', labelInvestDualTitle: 'Dual Investment', labelBuy: 'Buy', labelSell: 'Sell', labelRampNoBalance: 'Insufficient {{belong}} balance', labelBanxaNoBalance: 'Insufficient {{belong}} balance', labelBanxaFeeNoBalance: 'Insufficient {{belong}} balance & fee', labelL2toRampTitle: 'Send to Ramp', labelL2toBanxaTitle: 'Send to Banxa', labelDualInvest: 'Invest {{symbol}}', labelDualBase: 'Sell High for {{symbol}}', labelDualQuote: 'Buy {{symbol}} Low', labelDualAgree: 'I have read and understand the risk warning.', labelDualRiskTitle: 'Dual Investment', labelDualInvestBaseTitle: 'Invest {{symbolA}} (Sell High for {{symbolB}})', labelDualInvestQuoteTitle: 'Invest {{symbolA}} (Buy {{symbolB}} Low)', labelDualInvestDes: 'Invest {{symbolA}} to earn more {{symbolA}} or {{symbolB}}', labelDualCurrentPriceTip: 'Current Price is based on {{symbol}} derived from some leading exchanges.', labelDualCurrentPrice: '{{symbol}} Current Price:<1>{{price}} {{baseSymbol}}', labelDualCurrentPrice2: 'Current Price:\n <1>{{price}} {{baseSymbol}}', labelDualSuccess: 'Subscription {{symbol}} Successfully', labelDualProcessing: 'Waiting for completion', labelDualProcessingDes: 'We will try to fulfill your subscription request within minutes. If your subscription cannot be fully completed within the time frame, the unfilled portion will be unlocked. You can return later to resubscribe.', labelDualFailed: 'Subscribe Failed', labelDualFee: 'Fee', labelDualMin: 'Minimum of {{arg}}', labelDualMax: 'Maximum of {{arg}}', labelDualNoEnough: 'Insufficient balance', labelDualSettleDate: 'Settlement Date', labelDualSubDate: 'Subscription Date', // labelDualCurrentPrice2: '{{symbol}} Current Price', // labelDualCurrentPrice3: '{{symbol}} current Price', labelDualCurrentAPR: 'APR <1>\u2139', labelDualCurrentAPRDes: 'APR is refreshed in real time. We will use the lastest APR at the time you complete the subscription successfully.', labelDualTargetPrice2: 'Target Price <1>\u2139', labelDualTargetPrice3: 'Target Price', labelDualTargetPriceDes: 'Target Price is a benchmark price based on USDT. On Settlement Date, the Settlement Price will be compared against this benchmark price.', labelDualRiskDes: 'Your investment will be locked up until settlement date after investing and cannot be redeemed before settlement. \n As we make profit ratio a top priority, the total opened position might vary with your initial investment.', labelDualReturn: 'Return \n {{symbol}}', labelDualReceive: 'Settlement Calculator', labelDualCalcLabel: 'If {{symbol}} {{tag}} {{target}}', labelDualReturnValue: 'Return {{value}} {{symbol}}', labelDualQuota: 'Total Quota', labelProduct: 'Product', labelDualAssetFrozen_Target: 'Invest Target', labelDualAssetPrice: 'Price', labelDualAssetSettlement_Date: 'SettlementDate', labelDualAssetAPR: 'APR', labelDualAssetAction: 'Detail', labelInvestDualTutorial: 'Tutorial', labelInvestDualTutorialContent: 'Dual Investment offers you a chance to sell cryptocurrency high or buy cryptocurrency low at your desired price on your desired date. Once subscribed, users are not able to cancel or redeem the subscription until the Settlement Date.\n You may be better off holding your cryptocurrency, and may be required to trade your cryptocurrency at a less favorable rate of exchange than the market rate on Settlement Date. Cryptocurrency trading is subject to high market risk. Please make your trades cautiously. There may be no recourse for any losses.', labelInvestDualTutorialCheck1: 'I understand that Dual Investment is NOT a principal-guaranteed products.', labelInvestDualTutorialCheck2: 'I understand that subscribed assets are locked and users aren’t able to cancel or redeem before the Settlement Date.', labelInvestDualTutorialCheck3: 'I understand that I should review the possible scenarios of settlement amount and confirmed the subscription details.', labelInvestDualTutorialCheck4: 'Please be aware that the target price in Dual Investment portfolio is USDT. If you subscribe USDC-related product with another token, that token may be converted to USDC if the target price is reached. If you want to completely avoid the USDC depegging risk, you can select USDT-related products instead.', labelInvestDualTutorialCheck5: 'I have read and understand the risk warning.', labelInvestDualBeginerMode: 'Beginner Mode', labelInvestDualBeginerModeDesLine1: 'What is Dual Investment?', labelInvestDualBeginerModeDesLine2: 'You can use the beginner mode to quickly learn.', labelDualAmount: 'Amount', labelDuaInvestmentDetails: 'Dual Investment Details', labelDualOrderTable: 'Dual Investments', labelDualBeginnerPriceSmallerThan: 'if Index Price < {{value}}', labelDualBeginnerPriceSmallerThanOrEqual: 'if Index Price ≤ {{value}}', labelDualBeginnerPriceGreaterThan: 'if Index Price > {{value}}', labelDualBeginnerPriceGreaterThanOrEqual: 'if Index Price ≥ {{value}}', labelDualBeginnerAtSettlementDay: 'At Settlement Date', labelDualBeginnerIndexPriceDes: 'Index Price is derived from some leading exchanges.', labelDualBeginnerLockingDes: 'Your token for investment will be locked until Settlement Date.', labelDualBeginnerAPR: 'APR: {{APR}}', labelDualBeginnerStep1Title: 'Step 1: Choose a token to sell or buy', labelDualBeginnerStep2Title: 'Step 2: Choose to sell or buy at desired price in the future', labelDualBeginnerSellHigh: 'Sell {{token}} High', labelDualBeginnerBuyLow: 'Buy {{token}} Low', labelDualBeginnerReceiveStable: 'You will receive {{list}} {{last}}', labelDualBeginnerInvestStable: 'You can invest {{list}} {{last}}', labelDualBeginnerLast: 'or {{last}}', labelDualBeginnerStep3Title: 'Step 3: Choose Target Price and Settlement Date', labelDualBeginnerSellHighFor: 'Sell high for {{token}}', labelDualBeginnerBuyLowWith: 'Buy low with {{token}}', labelInvestMyAmm: 'My Investments', labelInvestMyDual: 'My Investments', labelInvestMyDefi: 'My Investments', labelInvestMaxDual: 'Max {{value}}', labelDualTitle: 'Dual Investment', labelDualDesSuccess: 'Your token for investment is just locked but still in your account as Loopring is a DEX. \n When the transaction expires, if the settlement price is not reached, you will get a profit and the frozen token will also be unlocked; if the settlement price is reached, your investment and interest income will be converted into the target token at the Target price.', labelDualRefresh: 'Refresh', labelNoticeForMarketFrozen: '{{ type }} is not supported, If you believe this is indeed a bug, please contact us.', labelInvestRangeDay: '{{arg}} Days', labelAmmExit: 'Redeem', labelAmmJoin: 'Subscribe', labelDualPanelClose: 'Go to My Investments', labelDualMobilePrice: '{{symbol}} price:', labelEditCollectionSuccess: 'Collection edit was successful', labelEditCollectionFailed: 'Collection edit has failed', labelEditCollectionBtn: 'Edit', labelEditRestCollectionBtn: 'Reset', labelEditCollectionERC1155: 'Edit My Collection', labelDualSettlementCalculator: 'Settlement Calculator', labelDualSettleDateDur: 'Subscription Length (days)', labelNoInvestContent: 'You currently have no investment assets. Start earning now with AMM, ETH Staking, or Dual Investments', labelImportCollection: 'Import Collection for Legacy NFT', labelCheckImportCollectionTitle: 'Import legacy NFTs under contract address to proceed', labelContinue: 'Next', labelImportCollection1: 'Import Collection for Legacy NFT', labelImportCollection2: 'Create/Choose a collection', labelImportCollection3: 'Select NFTs to move into/out of collection', labelSelectContractAddress: 'Contract address', labelImportChooseCollection: 'The created collection here can only be used to categorize the Legacy NFT minted without collection_metadata field.\n You can freely move those NFTs into any collection you created here.', labelImportCollectionundecided: 'Undecided', labelImportCollectionoutside: 'Others', labelImportCollectioninside: 'Current Collection', labelImportCollectionall: 'All', labelImportCollectionundecidedDes: 'items under this contract not classified into a collection', labelImportCollectionoutsideDes: 'items under this contract classified into a different collection', labelImportCollectioninsideDes: 'items under this contract classified into the current collection', labelImportCollectionallDes: 'all items under this contract', labelImportCollectionTitle: 'Import Collection for Legacy NFT', labelAssetTokens: 'Tokens', labelAssetInvests: 'My Investments', labelAssetRedPacket: 'Red Packets', labelORCreateCollection: 'Or <1>Create Collection', labelCreateLegacyCollection: 'Create Legacy Collection', labelNoLegacyCollection: 'You have no Legacy Collection, please', labelLegacyCollectionTitle: 'Create Legacy Collection', labelMoveOut: 'Move out of {{symbol}}', labelMoveIn: 'Move into {{symbol}}', labelMoveInCollection: 'Collection', labelSelectAll: 'Select All', labelCancelAll: 'Cancel', labelDoneBtn: 'Done', labelDetail: 'Detail', labelNFTMyCollection: 'Collection: {{collection}}', labelZoom: 'Zoom Media', labelRefresh: 'Refresh NFT cache', labelNFTDetailTab: 'Details', labelNFTPropertiesTab: 'Properties', labelLinkMetaData: 'NFT metadata Resource link', labelCountDown: 'Count Down', labelStackingSelect: 'Choose Staking Product', labelImportCollectionMove: 'Collection', labelCollectionImportNFTBtn: 'Manage Legacy NFT', labelNFTMoveFailed: 'NFT move failed!', labelNFTMoveSuccess: 'NFT moved successful', labelLuckTokenDefaultTitle: 'Good Luck!', labelSync: 'in Sync', labelMintInSyncTooltips: 'The NFT and collection information may not be synced up timely after minting due to onChain operation. Please stay tuned and refresh the page later.', labelEstRateApr: 'Est.rate (APR)', labelStakingApr: 'APR', labelManageCollectionTitle: 'Manage Legacy NFT', labelLegacy: 'legacy', labelTitleMyNFTSAvailable: 'My Holding NFTs', labelTitleTotalAvailable: 'Total NFTs', labelEstRateAprDes: 'APR stands for annual percentage Rate. It is the actual annual rate of return, NOT taking into account the effect of compound interest.', labelCheckImportCollectionDes: 'As the creator, you will be able to generate collection information for those NFT minted earlier that belong to nowhere. And once done, the other people holding your NFT will be able to view those NFT with proper collection information via loopring.io and loopring wallet.', labelL2toL1NFTFailed: 'Sent {{value}} {{symbol}} to {{l1Symbol}} has failed!', labelL2toL1NFTSuccess: 'Sent {{value}} {{symbol}} to {{l1Symbol}} was successful!', labelL2toL2NFTFailed: 'Sent {{value}} {{symbol}} from my {{loopringL2}} to another {{loopringL2}} failed!', labelL2toL2NFTSuccess: 'Sent {{value}} {{symbol}} was successful!', labelDoAgain: '{{method}} Again', labelDepositL1: 'Receive from {{l1Symbol}}', labelDepositNFTL1: 'Receive NFT from {{l1Symbol}}', labelL2ToL1Method: 'Send {{symbol}} to {{l1Symbol}}', labelL2ToL2Method: 'Send {{symbol}} to {{l2Symbol}}', labelConfirmAgainByFailed: 'You had a failed order, please confirm information again...', labelConfirmAgainByFailedWithBalance: 'You had a failed order, please confirm information again, Balance of {{symbol}} is {{count}}', labelNFTListfav: 'Favorite', labelNFTListhide: 'Hidden', labelNFTListall: 'Owned', labelNFTHide: 'Deploy Contract', labelNFTUnHide: 'Hide NFT', labelNFTUnHideDes: 'The easiest way to trade', labelHideMethodTooltiphide: 'Hide NFT', labelHideMethodTooltipunhide: 'Show NFT', labelFavouriteMethodTooltipfavourite: 'Favorite', labelFavouriteMethodTooltipunfavourite: 'Unfavorite', labelfavourite: 'Favorite', labelunfavourite: 'Unfavorite', labelFavouriteSuccess: 'Set {{favorite}} Successful', labelFavouriteFailed: 'Set {{favorite}} Failed', labelhide: 'Hide', labelunhide: 'Show', labelHideSuccess: '{{hide}} NFT Successful', labelHideFailed: '{{hide}} NFT Failed', labelSmallOrderAlertLine1: 'Small trades (below ~$100) incur a higher fee.', labelSmallOrderAlertLine2: 'Please review the fee before confirming.', labelSmallOrderAlertLine3: 'Trading Fee:', labelSmallOrderAlertLine4: 'Fee ratio:', labelSmallOrderAlertLine5: 'Minimum Converted:', labelSwapSecondConfirmTitle: 'Confirm Swap', labelSwapSettingTitle: 'Settings', labelSwapSettingSecondConfirm: 'Second confirmation', labelSwapSettingSecondConfirmTootip: 'skip confirm screen when toggled off', labelSwapSettingToggleSuccess: 'Swap second confirmation trun {{onOrOff}}', labelFeeMin: 'Min {{fee}}', labelIKnow2: 'I know', labelAddAssetTitleBridge: 'Add Asset From Another L1', labelAddAssetTitleBridgeDesActive: 'If you have transferred tokens from another {{ethereumL1}} Symbol account, it may take some time for this transaction to execute on-chain. Once you receive the assets, you can manually activate the {{l2Symbol}} account.', labelAddAssetTitleBridgeDes: 'If you have transferred tokens from another {{ethereumL1}} account, it may take some time for this transaction to execute on-chain.', labelAddAssetTitleExchange: 'Add Asset From An Exchange', labelAddAssetTitleExchangeDes: 'If you have transferred tokens from an Exchange, please wait. ', labelAddAssetTitleExchangeDesActive: 'If you have transferred tokens from an Exchange, please wait. Once you receive the assets, you can manually activate the {{l2Symbol}} account.', labelAddAssetTitleCard: 'Add Asset With a Card', labelAddAssetTitleCardDes: 'If you have purchased crypto with a card, please wait for it to arrive in your account.', labelAddAssetTitleCardDesActive: 'If you have purchased crypto with a card, please wait for it to arrive in your account. Upon arrival, {{l2Symbol}} will be activated manually.', labelMinFeeForActive: 'Min {{fee}}', labelReceiveAddressDes: 'If you have transferred tokens from another {{loopringL2}} account, please wait.', labelReceiveAddressDesActive: 'If you have transferred tokens from other {{loopringL2}} accounts, please close this window and try to activate your {{l2Symbol}} account again.', labelDepositWaiting: 'It make take some time for this transaction to execute on-chain.', labelFrom: 'From', labelTo: 'To', labeltransfer: 'Transfer', labelwithdraw: 'Withdrawal', labelDeposit: 'Deposit', labelFiatAmount: 'Fiat Amount', labelToMyL2: 'My {{loopringL2}}', labelBanxaNotReady: 'Please waiting a while for Banxa sdk loading, if you keep on face this problem try fresh the browser or contact us', labelBanxaFailedForAPI: 'Please waiting a while, Banxa service is not available currently.', labelL2toL2AddressFeePaid: 'Active account fee had paid', labelL2toL2AddressFeeActiveFee: "Pay recipient's {{l2Symbol}} activation fee: {{value}}", labelL2toL2FeeWithActive: 'Fee (including activation fee)', labelRedPacketOpen: 'Open', labelRedPacketTitle: 'Red Packets', labelRedPacketTypeTokens: 'Choose Tokens / NFTs / Blind Box', labelRedPacketChoose: 'Choose Red Packet Type', labelRedPacketMain: 'Input Red Packet/Send', labelLuckyTokenViewTypePublic: 'Public Red Packet', labelLuckyTokenViewTypePrivate: 'Private Red Packet', labelLuckyTokenViewTypeDesPublic: 'Your Red Packet is public, and everyone can try to claim a share of it.', labelLuckyTokenViewTypeDesPrivate: 'Your Red Packet is shared privately with others via a custom QR code.', labelLuckyBlindBox: 'Blind Box Red Packet', labelLuckyBlindBoxDes: 'Each recipient will receive a sealed Red Packet which cannot be opened until the expiration date. While some recipients will receive an NFT, others will need to try their luck next time.', labelLuckyRecievedBlindBox: 'Received Blind Box {{opendBlindBoxAmount}}/{{totalBlindBoxAmount}}', labelBlindBoxExplainationNotEnded: "The outcome of the Blind Box will be revealed upon expiration. Please claim within 3 days if your Red Packet contains a gift or it will be forfeited and returned to the Sender's wallet.", labelBlindBoxExplainationEnded: "Please claim within 3 days or it will be forfeited and returned to the Sender's wallet.", labelBlindBoxExplaination2: '{{opendBlindBoxAmount}} out of {{totalBlindBoxAmount}} blind boxes have been opened.', labelBlindBoxExplaination3: '{{remainingGiftsAmount}} gifts available for grabbing.', labelBlindBoxNotStarted: 'Red Packet is available to grab after: {{time}}', labelBlindBoxStarted: 'Blind Box Reveal time after: {{time}}', labelBlindBoxTokenHint: 'Unopen tokens will be returned back to sender after: {{time}}', labelBlindBoxClaimStarted: 'Any unclaimed NFTs will be returned to the Sender after: {{time}}', labelBlindBoxRecievedNFT: 'Received NFT {{deliverdGiftsAmount}}/{{totalGiftsAmount}}', labelBlindBoxStartDate: 'Start date', labelBlindBoxStartTime: 'Start Time', labelBlindBoxEndDate: 'End date', labelBlindBoxEndDate2: 'Blindbox reveal time', labelBlindBoxEndTime: 'End Time', labelBlindBoxRedPacketWithGift: 'Count of Red Packets with gift', labelBlindBoxExpirationExplainationForToken: 'After expiration, any unopened Red Packets will be forfeited and sent back to the Sender', labelBlindBoxExpirationExplainationForNFT: "If NFT Red Packet recipients do not claim their NFT within 30 days, it will be forfeited and returned to the Sender's wallet.", labelBlindBoxPrivate: 'Private Red Packet', labelBlindBoxPrivateDes: 'Your Red Packet is shared privately with others via a custom QR code.', labelBlindBoxClaimWarning: "If the recipients of the NFT Red Packets do not claim their received NFT within 3 days, the NFT will be forfeited and sent back to the Sender's wallet.", labelBlindBoxRecievedRedPackets: 'Received NFT Red Packets', labelBlindBoxCongratulations: 'Congratulations', labelBlindBoxSorry: 'Sorry', labelBlindBoxNoRewards: 'You have not received a reward', labelBlindBoxCongratulationsBlindBox: 'Congratulations on receiving a Blind Box', labelBlindBoxSorryBlindBox: 'Sorry, you did not win a prize', labelLuckyRelayToken: 'Relay Red Packet', labelLuckyRelayTokenDes: 'If the recipient of the Red Packet also re-shares the packet, they receive half of whatever the next person receives.', labelLuckyRandomToken: 'Lucky Red Packet', labelLuckyRandomTokenDes: 'Each recipient will get a random amount of.', labelLuckyCommonToken: 'Average Red Packet', labelLuckyCommonTokenDes: 'Each recipient will receive a pre-set split of the total Red Packet shared.', labelL1toL2NFTAmount: 'NFT Amount', labelInputRedPacketBtnLabel: 'Select Token', labelCreateRedPacket: 'Send Red Packet', labelMyRedPacket: ' My Red Packet Record', labelRedPacketMarkets: 'Red Packet Plaza', labelRedPacketQRCodeImport: 'Receive Red Packet', labelLuckyTokenViewType1: 'Private Red Packet', labelLuckyTokenViewTypeDes1: 'Your Red Packet is shared privately with others via a custom QR code.', labelLuckyTokenViewType0: 'Public Red Packet', labelLuckyTokenViewTypeDes0: 'Your Red Packet is public, and everyone can try to claim a share of it.', labelSplit: 'Red Packet Count', labelRedPacketMemo: 'Memo', labelRedPacketMemoPlaceholder: 'Best wishes', labelRedPacketStart: 'Available in', labelRedPacketSendWaitForAuth: 'Please confirm to send red packet {{value}} {{symbol}}.', labelRedPacketSendDenied: 'You rejected to send {{value}} {{symbol}} red packet.', labelRedPacketRecordTitle: 'My Red Packet Record', labelRedPacketReceived: 'ERC20 Received', labelRedPacketSend: 'ERC20 Send', labelRedPacketNFTReceived: 'NFT Received', labelRedPacketNFTSend: 'NFT Send', labelImportRedPacket: 'Import QR code to receive red packet', labelCreateRedPacketTitle: 'Send Red Packet', labelClaimWithdrawFee: 'Fee', labelClaimWithdrawNotEnough: 'Insufficient balance', labelClaimWithdrawTitle: 'Claim to {{loopringL2}}', labelClaimWithdrawWaitForAuth: 'Please confirm to claim {{symbol}}', labelClaimWithdrawDenied: 'You rejected to claim {{symbol}}.', labelClaimWithdrawInProgress: 'Processing...', labelClaimWithdrawFailed: 'Claim has failed!', labelClaimWithdrawSubmit: 'Claim has been submitted', labelClaimWithdrawToken: 'Token Amount', labelRedPacketSendSubmit: 'Send red packet has been submitted.', labelRedPacketSendSuccess: 'Red packet Send Successful.', labelRedPacketSendFailed: 'Send red packet of {{value}} {{symbol}} failed!', labelRedPacketSendInProgress: 'Processing...', labelRefreshRedPacket: 'Refresh List', labelRedPacketSendCommonTitle: 'Normal Red Packet', labelRedPacketSenRandomTitle: 'Lucky Red Packet', labelAmountEach: 'Amount Each', labelRedPacketTotalAmount: 'Total Amount', labelQuantity: 'Quantity', labelAssetAmount: 'Total Asset Amounts: {{value}}', labelCreateRedPacketBtn: 'Prepare Red Packet', labelRedPacketsExpireDes: 'Unclaimed tokens remaining after the expiration will be returned within 24h', labelReserveFee: 'Insufficient {{symbol}} with fee', labelRedPacketFee: 'Insufficient fee', labelRedPacketsInsufficient: 'Insufficient {{symbol}} balance', labelRedPacketsMinRange: 'Min {{value}}', labelRedPacketsMaxRange: 'Max {{value}}', labelRedPacketsMin: 'Minimum of {{value}} {{symbol}}', labelRedPacketsMax: 'Maximum of {{value}} {{symbol}}', labelRedPacketsGiftsLargerThanPackets: 'The number of Red Packets containing gifts cannot exceed the total number of Red Packets', labelBlindBoxNumberOverMaximun: 'Number of Blind Box exceeds maximum', labelRedPacketsSplitNumber: 'The maximum number of Red Packet is {{value}}', labelRedPacketsSplitCommonDetail: 'Distribution per red packet: {{value}}', labelRedPacketsSplitLuckyDetail: 'Token amount for each Red Packet is randomized.', labelSendRedPacketTitle: 'Send Red Packet', labelSendRedPacketTitlePublic: 'Send Red Packet -- Public', labelSendRedPacketTitlePrivate: 'Send Red Packet -- Private', labelRedPacketWaitingBlock: 'Block is not ready', labelShare: 'Share', labelRelayRedPacket: 'Relay Red Packet', labelNormalRedPacket: 'Average Red Packet', labelluckyRedPacket: 'Lucky Red Packet', labelrelayRedPacket: 'Relay Red Packet', labelnormalRedPacket: 'Average Red Packet', labelLuckyRedPacket: 'Lucky Red Packet', labelLuckyRedPacketStart: 'Starts in: {{value}}', labelLuckyRedPacketTimeout: 'Red Packet has been \n taken out', labelLuckyRedPacketDetail: 'View Red Packet details >', labelRedPacketOpenInProgress: 'Processing...', labelRedPacketOpenFailed: 'Read red packet failed!', labelRedPacketShowQR: 'Share red packet', labelRedPacketReceivedRecord: 'Receive Red Packet {{value}}/{{count}}', labelAmmExitMiniOrderDisabled: 'Transaction fees will be greater than the value of the LP, which will cost you your assets.', labelAmmExitMiniOrderMini: 'The transaction fee will account for 15% of the LP value, are you sure you want to redeem it?', labelLpAmount: 'LP Amount: {{value}}', labelRedPacketMarketsBtn: 'Red Packet Plaza', labelRedPacketBtn: 'Shared', labelRedPacketViewType0: 'Public Plaza', labelRedPacketViewType1: 'Public QR', labelRedPacketViewTypeDetail0: 'public Red Packet', labelRedPacketViewTypeDetail1: 'public Red Packet', labelRedPacketStatusSUBMITTING: 'Submitting', // SUBMITTING = 0, labelRedPacketStatusNOT_EFFECTIVE: 'Not Start', // NOT_EFFECTIVE = 1, labelRedPacketStatusPENDING: 'In Processing', // PENDING = 2, labelRedPacketStatusCOMPLETED: 'Completed', // COMPLETED = 3, labelRedPacketStatusOVER_DUE: 'Over Due', // OVER_DUE = 4, labelRedPacketStatusFAILED: 'Failed', // FAILED = 5 labelRedPacketStatusNotStarted: 'Hasn’t started', labelRedPacketStatusStarted: 'Started', labelRedPacketStatusEnded: 'Ended', labelRedPacketNo: 'NO.{{value}}', labelRedPacketClaimInProgress: 'Processing...', labelRedPacketClaimFailed: 'Open red packet failed!', labelRedPacketClaimSuccess: '', labelReceived: 'Received', labelGoodLuck: 'Good Luck', labelRedPacketGrab: 'Share with Friends', labelRedPacketEnded: 'Ended', labelLuckDraw: 'Luckiest Draw', labelMyLuckReward: '(My reward)', labelRedPacketClaimTitle: 'Claim to {{loopringL2}}', labelClaimNoBalance: 'Insufficient {{belong}} balance', labelShareQRCode: 'Generate QR Code for share', labelSeal: 'Seal', labelOpenAfter: 'Open after {{time}}', labelOpenStart: 'Start', labelTotalRedPacket: 'Total Quantity: {{value}}', labelMyRedPacketReward: 'My Rewards', labelRedpacketScanDes: 'Grab this Red Packet by scanning with your Loopring Wallet or importing to loopring.io', labelLuckyRedPacketStarted: 'Red Packet is Started', labelNFTRedpacketBtn: 'Send Red Packet', labelRedpacketDurationTitle: 'Expires after', labelRedpacketDurationPlaceHold: 'Min 1 - Max 30 Days', labelRedPacketDescription: 'Red Packet Description', labelRedpacketHavePeopleHelp: '<1>{{number}} friends relayed this red packet, you extend reward: <3>{{amount}}.', labelRedPacketFrom: 'From', labelRedPacketTo: 'To {{loopringL2}}', labelRedPacketMy: 'My Red packet', labelRedpacketNotActive: 'Hide received Red Packets', labelRedpacketTokens: 'ERC20 Tokens', labelRedpacketTokensShort: 'Tokens', labelRedpacketNFTS: 'NFTs', labelRedpacketBlindBox: 'Blind Box', labelRedpacketHideInactionable: 'Hide inactionable records', labelChooseNFT: 'Choose NFT <1>{{required}}', labelChooseNFTTooltips: '', tokenSelectNFTToken: 'Select NFT', labelRedPacketClaimERC20: 'ERC20', labelRedPacketClaimNFT: 'NFT', labelRedPacketMarketERC20: 'ERC20', labelRedPacketMarketNFT: 'NFT', labelRedPacketNotSupport: 'Unfortunately Mobile Dapp does not support Red Packet feature, Please download Loopring wallet or try this feature on laptop browser.', labelRedPacketTimeRange: 'Start / End Time', labelRedPacketTimeRangeDes: 'The Red Packet expires after the end date', labelRedPacketTimeRangeBlindbox: 'Start / Reveal Time', labelRedPacketTimeRangeBlindboxDes: 'The Reveal Time is when the Red Packet ends, and recipients can open it to see if they have received an NFT', labelRedPacketStartWithTime: '{{time}} Start', labelRedPacketTabReceived: 'Received', labelRedPacketTabSent: 'Sent', labelRedPacketTabNFTs: 'NFTs', labelRedPacketTabBlindBox: 'Blind Box', labelOrderOpen: 'Continue', labelOrderCancel: 'Cancel', labelOrderBanxaIsReadyToPay: 'The crypto selling order is ready. Please continue.', labelBanxaContinuous: 'Proceed existing order', labelBanxaCreate: 'Create new order', labelBanxaTitleCreateAgain: '', labelYouAlreadyHaveAnBanxa: 'Existing <1>order detected, send token to complete order.', labelHaveAnBanxaCancel: 'Create a new order from scratch.', labelBanxaConfirmSubmit: 'Token has been sent to Banxa wallet. You can save/click below link to check the payment status anytime.', labelInvestStakeLRC: 'LRC STAKING', labelInvestStakeLRCDES: 'Earn LRC staking rewards', labelFriendsPayActivation: 'Your friend has paid for your {{l2Symbol}} activation fee.', labelLRCStakingTitle: "What's LRC Staking", labelLRCStakingRisk: '

    LRC staking is incentivized through an allocated portion of the Loopring protocol fee; the exact percentage is determined by the Loopring DAO. The APY is updated daily based on the allocated amount from previous day’s fee. Any LRC holder can participate in LRC staking via {{l2Symbol}} to accumulate daily rewards. The assets must be staked for a minimum of 90 days to receive rewards.

    ', labelLRCStakingAgree: 'I have read and understand the risk warning.', labelLRCStakingRisk2: '<0>The staked LRC will be locked in {{loopringL2}}, meaning it cannot be used for other purposes. You may redeem your LRC at any time; however, doing so before the minimum Locked Duration will forfeit any accumulated reward.', labelInvestLRCStakingTitle: 'LRC Staking', labelMyInvestLRCStaking: 'My Investments', labelInvestLRCStakingLockAlert: 'Your assets for investment will be locked until your redemption.', labelLRCStakeAPRTooltips: 'Apr stands for annual percentage rate, not taking into account the effect of compound interest. The value displayed here indicates the current value, while it keeps changing dynamically. ', labelLRCStakeAPR: 'APR <1>', labelLRCStakeEarn: 'Daily Rewards (est.) <1>', labelLRCStakeEarnTooltips: 'Once funds are successfully locked for staking, rewards will begin calculating at 00:00 (UTC) the following day.', labelLRCStakeSubTime: 'Subscribe Time', labelLRCStakeDurationTooltips: 'Staking Duration refers to the minimum amount of time that staked assets must be locked in order to be entitled to claim rewards. LRC staking requires the minimum Locked Duration.', labelLRCStakeDuration: 'Lock duration to claim reward<1>', labelInvestLRCTitle: 'LRC Staking', labelLRCStakeRiskDes: 'The staked LRC will be locked in {{loopringL2}}, meaning it cannot be used for other purposes. You may redeem your LRC at any time; however, doing so before the minimum Locked Duration will forfeit any accumulated reward.', labelAgreeRedeem: 'Redeem', labelStackingAgreeRedeemTitle: 'Redeem In Advance', labelStackingAgreeRedeem: 'Redeeming staked assets before the minimum Locked Duration will forfeit the accumulated rewards. Are you sure you still want to redeem?', labelLRCStakeProduct: 'Product', labelLRCStakeRedeemDes: 'This product has meet the minimum Locked Duration. You can now redeem any portion of the subscription amount without deducting from your earnings. The remaining subscription amount will continue to generate income.', labelLRCStakeRedeemAgree: 'I acknowledge the early redemption will forfeit the accumulated reward', labelLRCStakeCurrentEarn: 'Current Total Rewards', labelLRCStakeForfeitedReward: 'Forfeited Reward', labelLRCStakeRemainingEarnings: 'Remaining Rewards', labelDeFiSideAmount: 'Amount', labelDeFiSideProduct: 'Product', labelDeFiSidePoolShare: 'Pool Share', labelDeFiSideAPR: 'APR', labelDeFiSideCumulativeEarnings: 'Cumulative Earnings', labelDeFiSidePreviousEarnings: "Previous Day's Earnings", labelDeFiSideLockDuration: 'Lock duration to claim reward', labelDeFiSideSubscribeTime: 'Subscribe Time', labelDeFiSideHoldingTime: 'Holding Time', labelDeFiSideInvestmentDetails: '{{symbol}} Staking Details', labelSideStakingTable: 'LRC Staking', labelInvestMaxDefi: 'Min {{minValue}} - Max {{maxValue}}', labelDefiMax: 'Allowable maximum is {{arg}}', labelDefiStakingDetail: 'Detail', labelDefiStakingRedeem: 'Redeem', labelDays: 'day(s)', labelRemainingAmount: 'Remaining amount should be greater than {{symbol}},\n Please redeem all.', labelRemainingBtnAmount: 'Remaining amount is insufficient', labelStakingCumulativeEarnings: 'Cumulative Rewards', labelStakingClaimableEarnings: 'Claimable Rewards', labelClaimBtn: 'Claim', labelStakeNoEnough: 'Insufficient {{arg}} balance', labelClaimBtnClaimed: 'Claimed', labelClaimBtnExpired: 'Expired', labelDefiRemindMin: 'Please redeem all Balance', labelInvestType_LRCSTAKE: 'LRC Staking', labelNFTs_one: '\u2A09{{count}} NFT', labelNFTs_other: '\u2A09{{count}} NFTs', labelTokenNFTMaxRedPack: 'Max: ', labelNFTRedPackAskClaim: 'Note: After expiration, all the unclaimed NFTs will be returned back to sender. Please claim as soon as possible if you want to hold them.', labelTransferDelayConfirm: 'Your claim request has been received. Loopring will transfer the token into your {{l2Symbol}} account soon. Please verify it.', labelClaimredPacket: 'My Red Packet', labelRedPacketMe: 'Me', labelClaimlrcStaking: 'My LRC Staking', labelExpectSettlementPrice: 'The expected settlement price from this order is {{symbolSell}}/{{symbolBuy}}={{stob}}, while the current market price from a trusted oracle is {{symbolSell}}/{{symbolBuy}}={{marketPrice}}. There is a {{marketRatePrice}}% variance observed. To proceed, tap here to confirm you understand and acknowledge the risk.', labelStakingSuccess: '{{symbol}} Staking Successful', labelStakingFailed: '{{symbol}} Staking failed', labelStakingRedeemFailed: 'Redeem {{symbol}} failed', labelStakingRedeemSuccess: 'Redeem {{symbol}} Successful', labelStakingRedeemRemaining: 'Remaining Amount', labelStakingRedeemDate: 'Redeem Time', labelContactsAddContact: 'Add Contact', labelContactsAddressTitle: 'Address', labelContactsAddressDes: 'Enter wallet address or ENS', labelContactsAddressInvalid: 'Invalid address or ENS', labelContactsNameTitle: 'Name', labelContactsNameDes: 'Enter name for the contact', labelContactsAddContactBtn: 'Add', labelContactsDeleteContact: 'Delete Contact', labelDeleteContactInfo: 'Contact', labelContactsDeleteContactBtn: 'Delete', labelContactsAddSuccess: 'Add Contact Succeed', labelContactsDeleteSuccess: 'Delete Contact Succeed', labelContactsEditSuccess: 'Edit Contact Succeed', labelContactsSendSuccess: 'Send Succeed', labelContactsCopySuccess: 'Copied to Clipboard', labelContactsAddFailed: 'Add Contact Failed', labelContactsDeleteFailed: 'Delete Contact Failed', labelContactsEditFailed: 'Edit Contact Failed', labelContactsSendFailed: 'Send Failed', labelContacts: 'Contacts', labelContactsSend: 'Send', labelContactsTransactions: 'Transactions', labelContactsNetworkChoose: 'Choose {{l2Symbol}} or {{l1Symbol}} Account', labelContactsNext: 'Next', labelContactsContactExisted: 'Contact Already Existed', labelNotExchangeEOA: 'Sending to an Exchange Address {{l2Symbol}} account is not supported. {{loopringL2}} accounts cannot be activated on Exchange wallet addresses. Instead, please send to the {{l1Symbol}} account associated with this address.', labelNotOtherSmartWallet: 'This wallet binds with smart contract that does not support {{loopringL2}}. You will need to send funds to the {{l1Symbol}} account.', labelContactsNoContact: 'No Contact', labelContactsSelectReciepient: 'Select the Recipient', labelContactsBinanceNotSupportted: 'Binance currently do not support {{loopringL2}} transfers. You will need to send funds to the {{l1Symbol}} account.', labelContactsHuobiNotSupportted: 'Huobi currently do not support {{loopringL2}} transfers. You will need to send funds to the {{l1Symbol}} account. Transactions need to wait for 24 hours.', labelContactsOtherExchangesNotSupportted: 'The trading platforms currently do not support {{loopringL2}} transfers. You will need to send funds to the {{l1Symbol}} account.', labelBtradeSwapTitle: 'Block Trade', labelBtradeSwapType: 'Type', labelBtradeSwapFilled: 'Filled', labelBtradeSwapFee: 'Fee', labelBtradeSwapTime: 'Time', labelBtradeSwapPrice: 'Price', labelBtradeSwapSettled: 'Settled', labelBtradeSwapDelivering: 'Delivering', labelBtradeSwapPanelDes: 'The Loopring pool is currently unable to swap the full requested amount. The tokens that were successfully swapped will be transferred to your account now. The unswapped tokens will be locked until they can be swapped. \n We’ll rebalance the pool shortly and swap the remaining portion.', labelBtradeSwapDeliverDes: 'The Loopring pool is currently unable to swap the full requested amount. The tokens that were successfully swapped will be transferred to your account now. The unswapped tokens will be locked until they can be swapped. \n We’ll rebalance the pool shortly and swap the remaining portion.', labelGoBtradeSwap: 'Swapping on the DEX will result in a large Price Impact (loss of assets). We recommend using the Block Trade option to help minimize potential losses.', labelBtradeSwap: 'Block Trade', labelBtrade: 'Block Trade', labelBtradeSwapFailed: 'Failed!', labelBtradeSwapTitleDes: 'What is Block Trade?', labelBtradeSwapContentDes: '

    Block Trade offers a secure and trustless way for users to swap tokens using CEX liquidity. The trades happen exclusively between designated entities, ensuring that the existing liquidity of the DEX remains unaffected. There is no price impact to other DEX users as a result of the transaction.

    ' + '

    This is similar to the traditional stock market’s Block Trade System. A block trade is a large, privately negotiated transaction, which can be made outside the open market through a private purchase agreement.

    ' + "

    The Loopring pool is currently unable to swap the fully requested amount. If you choose to continue, the unswapped tokens will be locked until they can be swapped. We'll rebalance the pool shortly.

    " + '

    Block Trade offers two options:

      ' + '
    • Prioritize Speed.
    • ' + '
    • Prioritize Quantity.
    ' + '
    Prioritize Speed
    ' + '

    This option prioritizes quick trade execution to ensure that trades are completed as soon as possible. It’s ideal for users who need to complete their trades quickly.

    ' + '
    Prioritize Quantity
    ' + '

    This option prioritizes trading as much of the asset as possible, even if it means waiting longer for the order to be fully executed. It’s ideal for users who want to maximize their trading volume and are willing to wait for the market to be favorable before completing the transaction.

    ' + '

    We’ll use the Loopring pool to swap your tokens. If your request exceeds the pool’s available balance, we’ll swap as many tokens as we can. Afterwards, we’ll rebalance the pool and then swap the remaining portion. The entire transaction should complete within 24 hours.

    ', labelRefereeRewards: 'Referee Rewards', labelReferralRewards: 'Referral Rewards', labelRewardLRC: 'Rewards LRC', labelPrice: 'Price', labelBtradeSwapMiniMax: 'Min {{minValue}} - Max {{maxValue}}', labelBtradeSwapMini: 'Min {{minValue}}', labelBtradeConfirm: 'Please check the checkbox', labelBtradeSwapBtn: 'Swap', labelType: 'Type', labelBtradeTrade: 'Block Trade', labelBtradeTitle: 'Block Trade Details', labelBtradeQuote: 'Total Quota:', labelBtradeQuoteDes: 'Total Quota is the maximum allowable trading amount.', labelBtradePoolDes: 'Loopring Pool:', labelBtradePool: 'Loopring Pool', labelBtradeToleranceTooltips: 'Your trade will revert if the price changes unfavorably by more than this percentage.', labelBtradeFeeTooltips: 'The trading fee is fixed at {{feeRate}}.', labelBtradeMinReceiveTooltips: 'The price in other liquidity source changes dynamically, the price you see when placing an order may be inconsistent with the final transaction price; also the received amount needs to deduct the fees from converted amount. The protocol can guarantee that the received token is at least this amount.', labelBtradeInsufficient: 'Insufficient', labelBtradeTime: 'Time', labelStopLimit: 'Stop-Limit {{tradeType}} {{symbol1}}', labelStopLimitDes: '

    If the last price {{from}} to or {{behavior}} {{stopPrice}} {{symbol2}}, and order to {{tradeType}} {{value1}} {{symbol1}} at a price of {{limitPrice}} {{symbol2}} will be placed.

    ', labelStopLimitFromGoesUp: 'goes up', labelStopLimitFromDropsDown: 'drops down', labelStopLimitBehaviorAbove: 'above', labelStopLimitBehaviorBelow: 'below', labelStopLimitType: 'Stop-Limit / {{tradeType}}', labelStopLimitStopPrice: 'Stop Price', labelStopLimitPriceLimitPrice: 'Limit Price', labelStopLimitAmount: 'Amount', labelStopLimitCancel: 'Cancel', labelStopLimitConfirm: 'Confirm', labelBtradeSwapPending: 'Pending', labelStopLimitTitle: 'Stop-Limit', labelStopPrice: 'Limit / Buy Price', labelStopStopPrice: 'Stop / Trigger Price', labelStopLimitWhatIs: "What's Stop-Limit?", labelStopLimitMinMax: 'Min {{minValue}} - Max {{maxValue}}', labelLimitStopPriceMinMax: 'Stop Price Range {{arg}}', labelLimitMainContent: 'A Stop-Limit order is a limit order with a limit price and a stop price. When the stop price is reached, the limit order will be placed on the order book. Once the limit price is reached, the limit order will be executed.', labelLimitStopPriceLabel: 'Stop Price / Trigger Price', labelLimitStopPriceContent: 'When the current asset price reaches the given stop price, the Stop-Limit order is executed to buy or sell the asset at the given limit price or better.', labelLimitLimitPriceLabel: 'Limit Price', labelLimitLimitPriceContent: 'The selected (or potentially better) price that the Stop-Limit order is executed at.', labelLimitAmountLabel: 'Amount', labelLimitAmountContent: 'The quantity of assets to buy or sell in the Stop-Limit order.', labelLimitDes: 'You can set the stop price and limit price at the same price. However, it’s recommended that the stop price for sell orders should be slightly higher than the limit price. This price difference will allow for a safety gap in price between the time the order is triggered and when it is fulfilled. You can set the stop price slightly lower than the limit price for buy orders. This will also reduce the risk of your order not being fulfilled.\n' + 'Please note that your order will be executed as a limit order after the market price reaches your limit price. If you set the stop-loss limit too high or the take-profit limit too low, your order may never be filled because the market price can’t reach the set limit price.', labelLimitDemoTitle: 'How does a Stop-Limit order work?', labelLimitDemoDes: 'The current price is 2,400 (A). You can set the stop price above the current price, such as 3,000 (B), or below the current price, such as 1,500 (C). Once the price goes up to 3,000 (B) or drops to 1,500 (C), the Stop-Limit order will be triggered, and the limit order will be automatically placed on the order book.\n Note:
      ' + '
    1. Limit price can be set above or below the stop price for both buy and sell orders. For example, stop price B can be placed along with a lower limit price B1 or a higher limit price B2.\n
    2. ' + '
    3. A limit order is invalid before the stop price is triggered, including when the limit price is reached ahead of the stop price.
    4. ' + '
    5. When the stop price is reached, it only indicates that a limit order is activated and will be submitted to the order book rather than the limit order being filled immediately. The limit order will be executed according to its own rules.
    ', labelLimitFailed: 'Submitted failed', labelLimitMarket: 'Market data has issue', labelStopLimitOrderGroup: 'Stop-Limit Records', labelStoplimit: 'Stop-Limit', labelStopLimitProduct: 'Product', labelStopLimitLabelType: 'Type', labelStopLimitNotSupport: 'Sorry, there is currently insufficient liquidity in this token pair to execute Stop-Limit orders. Please try again later or consider using a market / limit order instead.', labelStopLimitTriggered: 'Triggered: The limit order has been submitted to the order book.\n Time: {{time}}', labelStopLimitWaitingTrigger: 'The limit order is not placed until the stop price has been triggered.', labelStopLimitCurrentlyInsufficient: 'Currently insufficient', labelDUAL_CURRENCY: 'Dual Investment', labelDUAL_BASE: 'Dual Investment', labelBTRADE: 'Block Trade', labelL2STAKING: 'LRC Staking', labelSTOP_LIMIT: 'Stop-Limit', labelAMMPending: 'Pending', labelAMMTitle: 'AMM Investment', labelAMMChartFailed: 'Failed load data', labelExpectSettlementLimitPrice: 'The expected settlement price from this order is {{symbolBase}}/{{symbolQuote}} = {{price}}, while the current market price from a trusted oracle is {{symbolBase}}/{{symbolQuote}} = {{marketPrice}}. There is a {{marketRatePrice}}% variance observed. To proceed, tap here to confirm you understand and acknowledge the risk.', labelAMMNoEnough: 'Insufficient {{arg}} balance', labelAMMMax: 'Max {{arg}} ', labelAMMMaxAND: '{{coinA}} and {{coinB}}', labelDepositTo: 'Deposit to', labelReferTitle: 'Invite friends to join in \nLoopring and receive rewards', labelReferTitleDes: 'As referrer: will receive a commission on fees the new referred user trades. \n As referee: will enjoy a discount on transfer fees.', labelCopy: 'Copy', labelReferralRules: 'Reward rules', labelReferralMethod1: 'Method 1', labelReferralMethod2: 'Method 2', labelReferralMethod1Step1: 'Download the Loopring Wallet App', labelReferralMethod1Step2: 'Sign up with referral code', labelReferralMethod1Step3: 'Activate {{loopringL2}} Account', labelReferralMethod1Step4: 'Both of us receive rewards', labelReferralMethod2Step1: 'Access the website <1>https://loopring.io', labelReferralMethod2Step2: 'Connect Wallet', labelReferralMethod2Step3: 'Activate {{loopringL2}} Account with referral code', labelReferralMethod2Step4: 'Both of us receive rewards', labelReferralMyReferrals: 'My Referrals', labelReferralReferralsRefunds: 'Referee Refunds', labelBtradeQuantity: 'Prioritize Quantity', labelBtradeSpeed: 'Prioritize Speed', labelBtradeSettled: 'Settled', labelOrderCancelConfirm: 'Confirm to cancel this order?', labelOrderCancelOrder: 'Cancel', labelRedpacketTotalReward: 'Total {{amount}}', labelRedpacketCantOpen: 'Now is not the time to open', labelLocketInfo: '{{symbol}} Locked Detail', labelSendAssetToAnotherNet: 'To Others Via Third Party Bridge', labelFromAnotherNet: 'From another network', labelAddAssetTitleAnotherNetDes: 'If you have transferred tokens from another network, please wait. ', labelAddAssetTitleAnotherNetDesActive: 'If you have transferred tokens from another network, please wait. Once you receive the assets, you can manually activate the {{l2Symbol}} account.', labelAnotherNetworkDes: '<0>Orbiter.finance is a 3rd party service provider to help move tokens between various {{l1ChainName}} {{l1Symbol}} and {{l2Symbol}} networks. If you have any concerns regarding their service, please check out their <2>TOS.', labelAnotherNetworkUnderstand: 'I understand and acknowledge the risk', labelReferralImageDes: 'Scan code to register', labelReferralImageCode: 'Code: {{code}}', labelInvite: 'Invite Friends', labelReferralsTotalEarning: 'Total Rewards ', labelReferralsClaimEarning: 'Claimable Rewards ', labelReferralsTotalReferrals: 'Total Referrals ', labelReferralsTotalRefund: 'Total Refunds ', labelReferralsClaimRefund: 'Claimable Refunds ', labelReferralsTotalTradeNumber: 'Total Trade Number ', labelReferralCode: 'Referral Code (Optional) <1>\uFE61', labelReferralToolTip: 'Enter referral code to enjoy a discount on transfer fees.', labelBtradeRefresh: 'Refresh', labelArgNoEnough: 'Insufficient {{arg}} balance', WalletConnectV1: 'WalletConnect Legacy', labelDualInvestDesInsufficient: 'Insufficient quota', labelExplorer: 'Explorer', labelTutorial: 'Tutorial', labelRejectSwitchNetwork: 'Switch Chain was Rejected', labelAssetRewards: 'Rewards', labelClaimTypeREBATE_FEE: 'Maker Order Rewards', labelClaimTypeRECOMMENDER_FEE: 'Referral Rewards', labelClaimTypeREFERER_FEE: 'Referee Refunds', labelClaimTypePROTOCOL_FEE: 'Dedicated AMM Incentive', labelClaimTypeLRC_STAKING: 'LRC Staking Rewards', labelClaimOtherRewards: 'Other Rewards', labelAMMClaimableEarnings: 'Dedicated AMM Incentive', labelLayer2HistoryOrders: 'Order Records', labelLayer2HistoryStopLimitRecords: 'Stop-Limit Records', labelLayer2HistoryDefiRecords: 'ETH Staking', labelLayer2HistoryDualRecords: 'Dual Investment', labelLayer2HistorySideStakingRecords: 'LRC Staking', labelLayer2HistoryBtradeSwapRecords: 'Block Trade', labelPayLoopringL2: 'Pay {{loopringL2}}', labelRedPacketTimeRangeBlindboxDesERC20: 'The Reveal Time is when the Red Packet ends, and recipients can open it to see if they have received tokens', labelBlindBoxExpirationExplainationForTokenBlindbox: "If the recipients of the Tokens Red Packets do not open their received Tokens, the Tokens will be forfeited and sent back to the Sender's wallet.", labelBlindBoxRecieved: 'Received {{deliverdGiftsAmount}}/{{totalGiftsAmount}}', labelBlindBoxClaimHint: 'You can visit Assets > Red Packets to claim your rewards.', labelRedPacketBlindboxReceived1: 'ERC20 Blind Box', labelRedPacketBlindboxReceived2: 'Received', labelRedPacketsGiftsEqualsZero: "Number of gifts can't be zero", labelRedpacketStandard: 'Standard', labelCiETHDefiRiskTitle: "What's Cian Leveraged ETH?", labelCiETHDefiRisk: '

    CIAN protocol is a liquid staking derivatives (“LSD”) focused yield strategy platform, where users could earn either through joining algorithmic strategy vaults or through building their own DeFi strategies using CIAN’s advanced automation tools.

    ' + "

    The stETH/ETH leveraged staking strategy enables users to safely leverage stETH’s staking rewards. This strategy focuses on staking derivatives and protection/optimization tooling. By nature, this strategy is to use the user's asset as collateral to borrow ETH from lending platforms, then stake ETH in Lido to earn ETH staking interest. By utilizing tools like Flashloan, it actually adds leverage to users' ETH investment. If only there is a positive APY diff between the borrowing rate and ETH staking rate, there will be additional earnings from this strategy vs. standard ETH staking.

    " + '

    It is quite important to understand that, when using such leveraged strategy, it’s highly advised to intend on holding that position for a while. By doing so, users will give enough time for the high APY to cover their entry & exit costs.

    \n', labelDefiWithdrawFee: '', labelLeverageETHTitle: 'Leveraged ETH Staking', labelLayer2HistoryleverageETHRecords: 'Leveraged ETH Staking', labelSwapMinConverted: 'Minimum Converted', labelSwapMinConvertedTooltip: 'The pool price changes dynamically, the price you see when placing an order may be inconsistent with the final transaction price. The protocol can guarantee that you will receive at least this amount.', labelNetworkFee: 'Network Fee', labelTradingFee: 'Trading Fee', labelTradingFeeEst: 'Trading Fee (est.)', labelStopStopPriceDes: 'It\'s actually the trigger price for the relayer to place a valid order. When the market price reaches the "Stop Price", the system will automatically place a limit order at "Limit Price".', labelStopPriceDes: 'After the "Stop Price" is triggered, the relayer will automatically place a limit order at this price. ', labelStopPriceSell: 'Limit / Sell Price', labelClaimallToken: 'My Rewards', labelConnecting: 'Connecting', labelTitleOverviewAllPrd: 'All Products', labelInvestDefiDes: 'Earn ETH staking rewards', labelInvestChoseProduct: 'Choose the product you want', labelInvestTotalEarnings: 'Total Earnings', labelInvestLoopringEarn: 'Loopring DeFi', labelInvestLoopringEarnDes: 'Earn stable profits with professional asset management', labelInvestLRCDes: 'Earn LRC staking rewards', labelHadUnknownCollectionTitle: 'Import Collection for Legacy NFT', labelHadUnknownCollectionDes: 'As the creator, you will be able to generate collection information for those NFT minted earlier that belong to nowhere. And once done, the other people holding your NFT will be able to view those NFT with proper collection information via loopring.io and loopring wallet. ', labelGo: 'Go', labelAnotherNetworkDes2: 'Note: Please ensure to check out the "Change Account" option and input the recipient\'s address carefully. If you want to send token to network other than ethereum, the recipient address must be different than the sender address; else you will lose that asset for ever.', labelAnotherNetworkDes3: '', labelRiskReminder: 'Risk Reminder', labelDefiRedeem: 'Redeem', labelDefiSubscribe: 'Subscribe', labelDefiMaxBalance1Leverage: '
  • The Loopring pool will rebalance soon. Please come back later to redeem.
  • ', labelDefiNoBalanceLeverage: 'Loopring will set up the pool soon. Please come back later to redeem.', labelDefiMaxBalanceLeverage: 'It is not possible for the Loopring pool to fulfil your complete request at the moment. You can choose withdraw ciETH https://vault.cian.app/vaults', labelFunctionList: 'Function List:', labelSuperUserTitle: 'Super User', labelLeverageETHStaking: 'Leveraged ETH Staking', labelLeverageETHBack: 'Leveraged ETH Staking', labelInvestType_LEVERAGEETH: 'Leveraged ETH Staking', labelRewardRefresh: 'Refresh', labelToMyL2WidthAddress: '<0>My {{loopringL2}}<1>({{address}})', labelFeeAvailablePay: 'Available: {{available}}, Pay: {{pay}}', labelMarketOrderUnfilled: 'Market Order Unfilled', labelRiskAgree: 'Proceed Anyway', labelRiskCancel: 'Cancel', labelExpectedSettlementPrice: 'Expected Settlement Price', labelCurrentMarketPrice: 'Current Market Price', labelPriceVariance: 'Price Variance', labelImpactExtraNewGreat: 'This trade will result in a loss of {{value}}% of the position’s market value. To proceed, tap ‘Proceed Anyway’ to confirm you understand and acknowledge the risk.', labelPriceImpact: 'Price Impact', labelPriceImpactDes1: 'This trade will affect the pool price by more than {{value}}%,which is too high. It may result in significant slippage and potential losses. If you acknowledge the risk and wish to proceed, type the ‘AGREE’ and tap ‘Proceed Anyway’ to confirm again.', labelPriceImpactDes2: 'This trade will affect the pool price by more than {{value}}%,which is too high. It may result in significant slippage and potential losses. To proceed, tap ‘Proceed Anyway’ to confirm you understand and acknowledge the risk.', labelCopyCodeClip: 'Referral Code Copied to Clipboard!', labelDepositPending: '{{l1Symbol}} to {{l2Symbol}} Pending', labelWithDrawPending: '{{l2Symbol}} to {{l1Symbol}} Pending', labelContactsEditContactBtn: 'Edit Contact', labelLargePriceVariance: 'Large Price Variance', labelHighPriceImpacTitle: 'High Price Impact Detected', labelTimeoutAddressClick: '{{l1Symbol}} account checking request was rejected or some unknown error occurred, please retry', labelSmallOrderAlertLine: 'Small trades (below ~$100) incur a higher fee.', labelLimitImpactTitle: 'Limit taker Order Requires Confirmation', labelRedPacketViewType2: 'Exclusive', labelRedPacketPlazaPublic: 'Public Plaza', labelRedPacketPlazaPublicDes: 'Everyone in the Loopring community can participate in claiming the red packet', labelRedPacketQRPublic: 'Public QR', labelRedPacketQRPublicDes: 'Anyone that knows the QR code can participate in claiming the red packet', labelRedPacketExclusive: 'Exclusive Red Packet', labelRedPacketExclusiveDes: 'Only users that have received the red packet can claim it', labelRedPacketHaveExclusive: 'You have {{count}} exclusive Red Packets.', labelRedPacketExclusiveViewDetails: 'View Details >', labelRedPacketCongratulations: 'Congratulations!', labelExclusiveRedpacket: 'Exclusive Red Packets', labelRedpacketExclusiveReady: 'You have {{count}} exclusive Red Packets ready', labelRedpacketSentMaxLimit: 'Sent / Max Limit', labelRedpacketCreateNew: 'Create New Red Packet', labelRedpacketGiftRedPacket: 'Count of Red Packet with Gift', labelRedpacketRedPacketscount: 'Total Red Packets', labelRedpacketRevealTime: 'Reveal Time', labelRedpacketRecipients: 'Red Packet Recipients >', labelRedpacketRecipientList: 'Recipient List', labelExclusiveWhitelistDes: "For whitelisted users, each Red Packet can accommodate a maximum of 10,000 addresses, while standard users are allowed up to 50 addresses per Red Packet. Whitelisted addresses include Loopring, our partners, or other verified members. If you're interested in being whitelisted, please contact us at support@loopring.io.", labelRedpacketTextimport: 'Text import', labelRedpacketContactImport: 'Contact import', labelRedpacketNotificationDisplay: 'Notification Display', labelRedpacketRedDotDes: 'Recipients are alerted via a badge next to the Red Packets category', labelRedpacketBadge: 'Badge', labelRedpacketPopUp: 'Pop-up Notification', labelRedpacketPopUpTooltip: "Whitelisted addresses include Loopring, our partners, or other verified members. If you're interested in being whitelisted, please contact us at support@loopring.io.", labelRedpacketPopPpDes: 'Recipients are alerted via a prominent display that highlights the contents of the RedPacket. (Limited to whitelisted users)', labelRedpacketPrepareRedPacket: 'Prepare Red Packet', labelRedPacketChooseTarget: 'Select existing red packet or create a new one', labelRedPacketRecipientList: 'Recipient List', labelRedPacketPublicTooltip: 'Your Red Packet is public, and everyone can participate in claiming it.', labelRedPacketPrivateTooltip: '

    Your Red Packet is private, and only the addresses you specify can claim it.

    ' + '

    To create a new Exclusive Red Packet, please follow these steps:

    ' + '
  • 1. Select the type of Red Packet
  • ' + '
  • 2. Specify the amount of Red Packets to be sent and the date/time for delivery
  • ' + '
  • 3. Designate the recipients
  • ' + '
  • 4. Set the Notification Display
  • ' + '

    For whitelisted users, each Red Packet can accommodate a maximum of 10,000 addresses, while standard users are allowed up to 50 addresses per Red Packet.

    ' + "

    Whitelisted addresses include Loopring, our partners, or other verified members. If you're interested in being whitelisted, please contact us at support@loopring.io.

    ", labelRedpacketExclusiveEmpty: 'Your Prepared but unaddressed red packets will be displayed here!', labelRedpacketExclusiveSelected: 'Selected: {{count}}', labelRedpacketExclusiveManualEdit: 'Manual Edit', labelRedpacketValidAddresses: 'Valid Addresses: {{count}}', labelRedpacketTips: 'Tips', labelRedpacketChangeImportTips: 'If you change the import method, the previously selected addresses will be erased, are you sure you want to erase them?', labelRedpacketAddressesReview: 'Addresses Review', labelRedpacketAddressesReviewPart1: 'The list contains {{count}} valid addresses,', labelRedpacketAddressesReviewPart2: '{{count}} invalid addresses', labelRedpacketAddressesReviewPart3: '. To proceed, invalid addresses will be automatically removed from the list.', labelRedpacketExclusiveListEmpty: 'Your Prepared but unaddressed red packets will be displayed here!', labelRedpacketreceiptListEmpty: 'The addresses of the red packet you sent will be displayed here', labelRedpacketBestwishes: 'Best wishes', labelSendRedPacketTitleExclusive: 'Send Red Packet -- Exclusive', labelSendRedPacketClear: 'Clear', labelSendRedPacketMax: 'Max: {{count}}', labelRedPacketMaxValueExceeded: 'Maximum value exceeded', labelRedPacketTotal: 'Total {{count}}', labelRedPacketExclusiveTag: 'Exclusive', labelRedPacketClaiming: 'Claiming', labelRedPacketReceiptsList: 'Red Packet Receipt >', labelEOA: 'EOA', labelLoopringWallet: 'Loopring', labelOtherSmart: 'Other Smart', labelBinance: 'Binance', labelHuobi: 'Huobi', labelOtherExchange: 'Other Exchange', labelContactsEditContact: 'Edit Contact', labelLeverageETHStakingDes: 'Gain higher APY aggressively', labelDownloadShared: 'Download', labelShareReferralCode: 'Share to', labelShareMessage: 'Join me at Loopring and earn exclusive rewards with Loopring Referral Program! https://www.loopring.io/#/?referralcode={{code}}', labelInvestDualAutoTitle: "What's Auto Reinvest", labelDualAutoTitle: 'Auto Reinvest <1>', labelDualAutoTitleDes: 'Auto Reinvest will automatically reinvest your investment and earned interest into a new term with the same Target Price once the previous term expires, continuing until you successfully buy or sell crypto. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed and your investment and earned interest will be unlocked.\n', labelInvestDualAutoCheck: '

    Auto Reinvest will automatically reinvest your investment and earned interest into a new term with the same Target Price once the previous term expires, continuing until you successfully buy or sell crypto. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed and your investment and earned interest will be unlocked.

    Reinvest Target Price: The Target Price at which you want to buy or sell crypto.

    Longest Settlement Date: The maximum duration available for selecting the settlement period. Auto Reinvest will automatically match products with settlement periods that do not exceed the Longest Settlement Date.

    ', labelDualAutoDetail: 'Auto Reinvest will try to find a new product which based on the following rule at 16:00 on the settlement day.', labelDualAutoDUAL_BASEPrice: 'Sell Price <1>', labelDualAutoDUAL_CURRENCYPrice: 'Buy Price <1>', labelDualAutoDUAL_BASEPriceDes: 'The target price at which you want to sell crypto.', labelDualAutoDUAL_CURRENCYPriceDes: 'The target price at which you want to buy crypto.', labelDualModifyParameter: 'Modify Parameter', labelDayDisplay: '{{item}} Day(s)', labelDualModifyConfirm: 'confirm', labelDualAutoDurationDes: 'The maximum duration when selecting the settlement period. Auto Reinvest will automatically match products with settlement periods that do not exceed the Longest Settlement Date.', labelDualModifyBtn: 'Modify', labelTurnOffDualAutoInvest: 'Stop Auto Invest', labelDualModifyAPR: 'APR: {{value}}', labelDualModifySettlementDateDes: 'Changes will take effect after the Next Settlement Date.', labelDualModifySettlementDate: 'Next Settlement Date: {{date}}', labelDualEditSuccess: 'Update successful!', labelDualEditFailed: 'Update Failed!', labelDualEditDuration: 'Modify Longest Settlement Date <1>', labelDualEditDurationDes: 'The maximum duration when selecting the settlement period. Auto Reinvest will automatically match products with settlement periods that do not exceed the Longest Settlement Date.', labelDualInvestGuid: 'Invest', labelCoverGain: 'Covered Gain', labelCoverGainDes: 'Earn interest before taking profits', labelDip: 'Buy The Dip', labelDipDes: 'Earn interest while waiting for your price target', labelDualMerge: 'Dual Investment', labelDualMergeDes: 'Select based on Token and Settlement Date', labelDualChooseTokenDUAL_BASE: 'Step 1: Choose a token to sell', labelDualChooseTargetPriceDUAL_BASE: 'Step 2: Choose Target Price and Settlement Date', labelDualChooseTokenDUAL_CURRENCY: 'Step 1: Choose a token to buy', labelDualChooseTargetPriceDUAL_CURRENCY: 'Step 2: Choose Target Price and Settlement Date', labelDualTypeDualGain: 'Covered Gain', labelDualTypeDualDip: 'Buy The Dip', labelDualTypeDualBegin: 'Dual Investment', labelDualTypeAll: 'Dual Investment', labelDualAutoCancelConfirm: 'Disable Auto Reinvest', labelDualAutoCancelOrder: 'Cancel', labelDualAutoCancelConfirmDes: 'Are you sure about disable auto reinvest? If disabled, there will be no new orders after the next settlement.', labelDualModifySettlementDateDialog: 'Next Settlement Date', labelDefiRate: 'Rate', labelDefiLido: 'Lido', labelDefiRocketPool: 'Rocket Pool', labelDualIsHigh: 'is significantly higher', labelDualIsLow: 'is significantly lower', labelDualAutoAlert: "The current price of {{base}} is {{currentPrice}} {{quote}}, which {{method}} than the price you've set for Auto Reinvest. This may result in a lower APY for your next settlement. You can adjust the price for Auto Reinvest.", labelDualAutoDuration: 'Longest Settlement Date <1>', labelInvestDualGainTitle: 'What is Covered Gain?', labelInvestDualGainGuid: '

    Covered Gain is an investment strategy to sell digital assets at your Target Price and earn interest while waiting.

    ' + '

    On the Settlement Date, there can be 2 scenarios:

    ' + '
    1. Market Price > Target Price
    2. ' + '
    3. Market Price ≤ Target Price
    ' + '
    Market Price > Target Price
    ' + '

    Your original investment and earned interest will be sold at the target price.

    ' + '

    This order is then closed regardless of whether "Auto Reinvest" is enabled or not.

    ' + '
    Market Price ≤ Target Price
    ' + '

    Your original investment and earned interest won’t be sold.

    ' + '

    If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully sell crypto at your desired price or disable the feature.

    ' + '
    Auto Reinvest
    ' + '

    When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully sell your crypto at your Target Price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

    ' + '

    Sell Price: the Target Price at which you want to sell your crypto.

    ' + "

    Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

    ", labelInvestDualDipTitle: 'What is Buy The Dip?', labelInvestDualDipGuid: '

    Buy The Dip is an investment strategy to buy digital assets at your Target Price and earn interest while waiting.

    ' + '

    On the Settlement Date, there can be 2 scenarios:

    ' + '
    1. Market Price > Target Price
    2. ' + '
    3. Market Price ≤ Target Price
    ' + '
    Market Price > Target Price
    ' + '

    Your original investment and earned interest won’t be converted. Earned interest is in USDC or USDT.

    ' + '

    If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully buy crypto at your desired price or disable the feature.

    ' + '
    Market Price ≤ Target Price
    ' + '

    Your original investment and earned interest will be converted at the Target Price.

    ' + '

    This order is then closed regardless of whether "Auto Reinvest" is enabled or not.

    ' + '
    Auto Reinvest
    ' + '

    When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

    ' + '

    Buy Price: the Target Price at which you want to buy crypto.

    ' + '

    Sell Price: the Target Price at which you want to sell crypto.

    ' + "

    Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

    ", labelAssetDualInvests: 'Dual Investment', labelTxGuardian_upgrade_contract: 'upgrade contract', labelTxGuardian_approve_token: 'approve token', labelContactNameExisted: 'Name already exists', labelContactAddressExisted: 'Address already exists', labelL1toL2ThirdPartOn: 'On-ramp Crypto', labelL1toL2ThirdPartOff: 'Off-ramp Crypto', labelTargetRedpacketOption1: 'Option 1', labelTargetRedpacketCreateTitle: 'The following steps are required to create a new Exclusive Red Packet', labelTargetRedpacketCreateStep1: '1. Select the type of red packet to be sent.', labelTargetRedpacketCreateStep2: '2. Number of red packets to be sent/transmission time', labelTargetRedpacketCreateStep3: '3. Designated Red Packet Recipients', labelTargetRedpacketCreateStep4: '4. Notification display for red packet recipients', labelTargetRedpacketNoRedpacket: 'You do not have an existing wallet yet', labelTargetRedpacketNoRedpacketDes: 'If your prepared but unaddressed Red Packets will be displayed here !', labelTargetRedpacketOption2: 'Option 2', labelEarnVaultTitle: 'Loopring DeFi Assets', labelDualBTC: 'ETH - WBTC Dual Investment', labelDualBTCDes: 'Select based on Token and Settlement Date', labelDualTypeDualBTC: 'ETH - WBTC Dual Investment', labelDualNewPriceLessThan: 'if {{base}} < {{value}} {{quote}}', labelDualNewPriceLessOrEqualThan: 'if {{base}} ≤ {{value}} {{quote}}', labelDualNewPriceGreaterThan: 'if {{base}} > {{value}} {{quote}}', labelDualNewPriceGreaterThanOrEqual: 'if {{base}} ≥ {{value}} {{quote}}', labelInputMax: 'Max', labelTokenEnterDualToken: 'Amount', labelTokenMaxBalance: 'Available Balance', labelInvestMiniDual: 'Min {{value}}', labelDualAutoSearchingDes: 'Auto reinvesting. Searching for the product...', labelDualAutoInvestTip: 'Auto Reinvest Status:{{}}', labelDualRetryStatusSuccess: 'Auto reinvested successful. A new order has been generated for you.', labelDualRetryStatusError: 'Auto reinvest failed. Cannot find product with Buy Price of {{price}} and Longest Settlement Date of {{day}} days. ', labelDualRetryStatusRetrying: 'Auto reinvesting. Searching for the product...', labelDualRetryPending: 'Pending', labelDualRetryTerminated: 'Terminated', labelDualRetryFailed: 'Failed', labelDualRetrySuccess: 'Successful', labelDualRetryStatusTerminated: 'Auto Reinvest terminated. You successfully purchased the target token.', labelInvestmentStatusSettled: 'Settled', labelInvestmentStatusDelivering: 'Delivering', labelInvestmentStatusSubscribe: 'Earning', labelDualTxsSettlement: 'Settlement', labelDualAuto: 'Auto Reinvest', labelDualAssetReInvestEnable: 'Enabled', labelDualDeliver: 'Settlement Price', labelDualAssetReInvestDisable: 'Disabled', labelUnlockErrorLine1: 'The failure to unlock your wallet most likely is due to network condition or browser issue, which may be recovered via refreshing the page or reopening it', labelUnlockErrorLine2Part1: 'As a last resort, you can ', labelUnlockErrorLine2Part2: 'Reset Loopring L2 keypair', labelUnlockErrorLine2Part3: '. This can be especially helpful if all other retry attempts fail, particularly when using hardware wallets.', labelResetLoopringL2: 'Reset Loopring L2 keypair', labelResetlockedReset1: "Please note that if you have pending Dual Investment subscriptions, the L2 keypair reset won't take immediate effect. Your wallet's L2 account will remain locked until all subscriptions have settled. While locked, you won't be able to perform any L2 operations.", labelResetlockedReset2: 'Additionally, any pending limit orders will be canceled since they are tied to the old L2 keypair.', labelResetunlockedWithDual1: "We’ve detected that you have an active Dual Investment subscription. As a result, the L2 keypair reset won't take effect immediately. Instead, your wallet's L2 account will remain locked until all subscriptions have settled. While locked, you won't be able to perform any L2 operations.", labelResetunlockedWithDual2: 'Additionally, any pending limit orders will be canceled since they are tied to the old L2 keypair.', labelResetunlockedWithoutDual: 'Resetting the L2 keypair will cancel all pending limit orders as they tied to the old L2 keypair.', labelDualAutoReinvest: 'Auto Reinvest', labelInvestDualTutorialContent2: '
    Auto Reinvest
    ' + '

    When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

    ' + '

    Buy Price: the Target Price at which you want to buy crypto.

    ' + '

    Sell Price: the Target Price at which you want to sell crypto.

    ' + "

    Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

    ", labelNoticeTitle: 'Notifications', labelNotificationTime: '{{time}}', labelNotificationClear: 'Clear all', labelNotificationReadAll: 'Mark all as read', labelNotificationLabel: 'Notification', labelActiveL1successfulNote: 'Active {{ethereumL1}} successful', labelActiveL2successfulNote: 'Active {{loopringL2}} successful', labelActivatingL1AccountNote: 'Activating {{ethereumL1}} successful', labelL1ReceiveNote: 'Receive token in {{ethereumL1}}', labelL1SendNote: ' Send token from {{ethereumL1}}', labelL2ReceiveNote: 'Receive token in {{loopringL2}}', labelL2SendNote: 'Send token from {{loopringL2}} successful', labelL2DepositNote: 'Receive token in {{loopringL2}}', labelL2WithdrawNote: 'Send token in {{loopringL2}}', labelTotalUnRead: '{{total}} unread(s)', labelReadAll: 'View all', labelLuckyTokenNormal: 'Normal', labelRedPacketSendAverageTitle: 'Average Red Packet', labelBlindBoxHint: 'Each recipient will receive a sealed Red Packet which cannot be opened until the expiration date. While some recipients will receive an NFT, others will need to try their luck next time.', labelNormalRedPacketTitle: 'Normal Red Packet', labelDualDefaultAutoTitle: 'Default Enable Auto Reinvest', labelDualLongestSettlementDuration: 'Modify Longest Settlement Date', labelDualLongestSettlementFixed: 'Fixed', labelDualLongestSettlementAutomatic: 'Automatic', labelDualSettingConfirm: 'Confirm', labelReceiveToken: 'Receive', labelETHStakingEnterPaymentToken: 'Amount', labelDefiDuration: 'Duration', labelFlexible: 'Flexible', labelEstAPR: 'Est.APR', labelTradingFeeTooltips: "The trading fee is determined by the size of your trade. Usually, it's 0.1% of your total converted amount; or the necessary fee that covers the transaction cost for small transactions.", labelENSAlert: "ENS & Address Mismatch. Before proceeding with the transfer, we recommend updating the contact's information for accuracy.", labelSendToContact: 'Send to', labelContactENSAlert: "ENS & Address Mismatch. Before proceeding with the transfer, we recommend updating the contact's information for accuracy.", labelL2DualNote: 'Dual Investment Notification', labelQuickInvest: 'Quick Invest', labelUpdateContactsNext: 'Go to Update Contact', labelENSAddressMismatch: 'ENS & Address Mismatch', labelVault: 'Portal', labelVaultHome: 'Home', labelVaultDashboard: 'Dashboard', labelVaultRefresh: 'Refresh', labelVaultAddBtn: 'Add Collateral', labelVaultJoinBtn: 'Supply Collateral', labelVaultCheckInProcessing: 'Checking Portal status', labelVaultRecord: 'Records', labelEnterToken: 'Select Token', labelVaultExitBtn: 'Settle', labelVaultLoanBtn: 'Loan', labelVaultTutorial: 'Tutorial', labelVaultTotalToken: 'Portal Token', labelVaultJoinMini: 'Minimum of {{arg}}', labelVaultJoinMax: 'Maximum of {{arg}}', labelVaultJoinNotEnough: 'Insufficient {{arg}} balance', labelVaultJoinMarginTitle: 'Add Collateral', labelVaultJoinTitle: 'Supply Collateral', labelVaultTradeBtn: 'Portal Trade', labelVaultRedeemBtn: 'Settle', labelVaultExitDes: 'Upon closing your position, all open orders will be canceled, and any outstanding debts will be automatically repaid. If the available balance of the Portal_Token is insufficient to cover the debt, Loopring will sell a portion of your assets at the market price to repay the debt. The remaining Portal_Token will be converted to the token you have staked at the current market price and returned to your {{loopringL2}} Account.', labelVaultConfirm: 'Confirm', labelVaultCancel: 'Cancel', labelVaultExitTitle: 'Settle', labelVaultSwapBtn: 'Swap', labelVaultSwap: 'Portal Trade', labelVaultType: 'Type', labelVaultTypeOpenPosition: 'Supply Collateral', labelVaultStatus: 'Status', labelVaultJoinStatus: '{{status}} {{percentage}}', labelVaultTradeStatus: '{{status}} {{percentage}}', labelVaultCollateral: 'Collateral', labelVaultReceive: 'Receive', labelVaultTime: 'Time', labelVaultExitType: 'Type', labelVaultExitTypeClose: 'Settle', labelVaultExitStatusLabel: 'Status', labelVaultExitStatus: '{{status}}', labelVaultExitStatusSuccess: 'Success', labelVaultExitTotalBalance: 'Total Balance', labelVaultExitTotalDebt: 'Total Debt', labelVaultExitTotalEquity: 'Total Equity', labelVaultExitProfit: 'Profit', labelVaultExitProfitPercentage: 'Profit Percentage', labelVaultExitTime: 'Time', labelVaultExitStatusPending: 'Pending', labelVaultExitCloseAmount: 'Redeem Token', labelVaultExitPendingDes: 'The request is being processed and is expected to take 1-2 minutes. Please be patient.', labelVaultBorrow: 'Borrow', labelVaultRepay: 'Repay', labelVaultTotalQuoteDes: 'Total Quota is the maximum amount available for you to be used as collateral.', labelVaultTokenQuoteDes: 'When provide the token as collateral, you will receive an equivalent amount of Portal Token.', labelTitleVaultDes: 'Loopring Portal can be treated as an isolated margin account allowing users to borrow/lend tokens with collateral. Other than leveraged trading, it also allows users to trade hot tokens that are not available in Loopring DEX with CEX liquidity.', labelTitleVault: 'Portal', labelVaultHomeTitle: 'Home', labelVaultDashboardTitle: 'Dashboard', labelVaultTotalBalance: 'Total Balance', labelVaultOpenDate: 'Open Date:', labelVaultMarginLevel: 'Margin Level', labelVaultTotalCollateral: 'Total Collateral', labelVaultTotalDebt: 'Total Debt', labelVaultTotalEquity: 'Total Equity', labelVaultProfit: 'Profit', labelVaultCloseBtn: 'Close', labelGoVaultDashBoard: 'Go to Dashboard', labelVaultBrowserToken: 'Amount', labelVaultQuotaTooltips: 'PortalQuoteDes', labelVaultQuota: 'Total Quota', labelVaultBorrowMini: 'Minimum of {{arg}}', labelVaultBorrowNotEnough: 'Exceeded Maximum Borrowable Amount', labelVaultBorrowMax: 'Maximum of {{arg}}', labelVaultBorrowBtn: 'Borrow', labelVaultBorrowStatusLabel: 'Status', labelVaultBorrowTypeLabel: 'Type', labelVaultBorrowAmountLabel: 'Amount', labelVaultBorrowStatus: '{{status}}', labelVaultBorrowTitle: 'Borrow', labelPending: 'Pending', labelVaultRepayMini: 'Minimum of {{arg}}', labelVaultRepayNotEnough: 'Insufficient balance', labelVaultRepayMax: 'Maximum of {{arg}}', labelVaultRepayBtn: 'Repay', labelRepayQuota: 'Borrowed', labelVaultWhatis: 'What is Loopring Portal?', labelVaultDesSimple: '

    Vault: Using a third-party funds to trade assets. Traders can access additional capital, amplifying trading results.

    ', labelVaultRiskDes: '

    Loopring Portal acts as an isolated margin account, enabling users to borrow and lend tokens with collateral. Besides supporting leveraged trading, it also facilitates the trading of tokens not available on Loopring DEX by tapping into CEX liquidity depth.

    ' + "

    It's important to note that all assets in Portal are hedged on CEX, incurring an associated cost known as the Asset Holding Fee, charged on an hourly basis. We strongly recommend closing your position if it's no longer needed for trading to reduce costs.

    " + '

    In order to initiate the forced clearing of assets in the event of liquidation, Loopring will require your approval to handle the collateral asset. Additionally, after closing a position, there may be some remaining Portal tokens in your account that Loopring will need to clear before allowing you to open a new position.

      ' + '
    • There is an Asset Holding Fee for assets in Portal.
    • ' + '
    • I will grant Loopring authority to deal with my collateral in case of forced liquidation.
    • ' + '
    • I will grant Loopring the authority to clear all previously remaining Lv tokens for all new position openings in Portal.
    ', labelVaultRepayTitle: 'Repay', labelVaultRepayStatus: '{{status}}', labelVaultRepayStatusLabel: 'Status', labelVaultRepayTypeLabel: 'Type', labelVaultRepayType: 'Repay', labelVaultRepayTotalBalance: 'Amount', labelVaultVAULT_STATUS_RECEIVED: 'Received', labelVaultVAULT_STATUS_PROCESSING: 'Processing', labelVaultVAULT_STATUS_SUCCEED: 'Success', labelVaultVAULT_STATUS_FAILED: 'Failed', labelVaultVAULT_STATUS_PENDING: 'Pending', labelVaultAccountWait: 'Portal account in loading...', labelVaultInRedeemWaiting: 'Portal account is in process of settle...', labelVaultJoinSymbolNotSame: 'Only {{symbol}} allow Margin Call', labelVaultRepaySuccess: 'Success', labelVaultJoinSuccess: 'Success', labelVaultRedeemSuccess: 'Success', labelVaultBorrowSuccess: 'Success', labelVaultTradeSuccess: 'Success', labelVaultRepayInProgress: 'Processing...', labelVaultRedeemInProgress: 'Processing...', labelVaultBorrowInProgress: 'Processing...', labelVaultTradeInProgress: 'Processing...', labelVaultJoinInProgress: 'Processing...', labelVaultRepayFailed: 'Failed', labelVaultRedeemFailed: 'Failed', labelVaultBorrowFailed: 'Failed', labelVaultJoinFailed: 'Failed', labelVaultTradeFailed: 'Failed', labelLayer2HistoryVaultRecords: 'Portal', labelVaultTitleRisk: 'What is Loopring Portal?', labelProTime1h: '1h', labelProTime1d: '1d', labelProTime1w: '1w', labelStats: 'Stats', label24PriceChange: '24h Price Change', label7dPriceChange: '7d Price Change', labelVaultREDEEMPendingBtn: 'Settle in Processing', labelVaultlnredeemWaiting: 'Settle in Processing', labelJoinTitle: 'Supply Collateral', labelRedeemTitle: 'Settle', labelRedeemDesMessage: 'Please be aware that there may be a brief waiting period due to automatic liquidation. We appreciate your patience and assure you that we are working to process your request as quickly as possible. Thank you for your understanding.', labelJoinDesMessage: 'Please open a position first.', labelFailed: 'Failed', labelVAULT_COLLATERAL: 'Portal Collateral', labelTokenVaultDes: '{{symbol}} in Portal is a token backed 1:1 with {{symbol}}, bringing greater liquidity to Loopring DEX.', labelTokenWebsite: 'Website', labelTokenContractAddress: 'Contract Address', labelTokenSupply: 'Total Supply', labelMarketCap: 'Market Cap', labelTokenInfo: 'Token Info', labelTokenIntroduce: 'Introduction', labelVaultRedeemDetail: 'Details', labelCloseOutDetail: 'Settle', labelVaultTradeTitle: 'Portal Trade', labelVaultPrice: 'Price', labelVaultSell: 'Sell', labelVaultBuy: 'Buy', labelVaultFee: 'Trade Fee', labelVaultJoin: 'Supply Collateral', labelVaultMarginCall: 'Add Collateral', labelVaultMaxBrowserLabel: 'Max borrowable: ', labelVaultBorrowed: 'Borrowed', labelVaultRepayBalance: 'Total Balance', labelVaultPendingDes: 'The request is being processed and is expected to take 1-2 minutes. Please be patient.', labelVaultTotalDebtTooltips: 'Total Debt is comprised of two parts: loans and the cost incurred from utilizing the capital. The cost is determined by multiplying the Total Balance by the Funding Rate, which will accumulate on an hourly basis.', labelVaultMarginLevelTooltips: 'Margin Level provides an indication of how close your account is to experiencing a margin call or being liquidated. To avoid liquidation, you can either supply more collateral or repay borrowed positions.', labelOpenPositionDetail: 'Supply Collateral Detail', labelMarginCallDetail: 'Margin Call', labelBorrowDetail: 'Borrow', labelRepayDetail: 'Repay', labelTradeDetail: 'Trade', labelVaultPlacedAmount: 'Placed Amount', labelVaultExecutedAmount: 'Executed Amount', labelVaultExecutedRate: 'Executed Rate', labelVaultConvertedAmount: 'Converted Amount', labelVaultTxFee: 'Fee', labelBtradeExecutedAmount: 'Executed Amount', labelBtradeExecutedAmountTip: 'The quantity successfully processed on the CEX side.', labelBtradePlacedAmount: 'Placed Amount', labelBtradePlacedAmountTip: `The quantity you invested in 'Block Trade' that you intend to execute.`, labelBtradeExecutedRate: 'Executed Rate', labelBtradeConvertedAmount: 'Converted Amount', labelBtradeConvertedAmountTip: 'The quantity successfully converted from the CEX side', labelBtradeSettledAmount: 'Settled Amount', labelBtradeSettledAmountTip: `The quantity deposited into your Loopring L2 account. If you opt for 'Prioritize Quantity' mode and the Loopring L2 pool exhausts its quota, you may not receive the entire amount instantly. The remaining tokens will be 'swapped' at the executed price once the pool is replenished.`, labelVaultExitTypeForcedLiquidation: 'Forced Liquidation', labelVaultExecutedAmountTip: 'The quantity successfully processed on the CEX side.', labelVaultPlacedAmountTip: `The quantity you invested in 'Portal Trade' that you intend to execute.`, labelVaultConvertedAmountTip: 'The quantity successfully converted from the CEX side.', labelVaultSettledAmountTip: `The quantity deposited into your Loopring L2 account. If you opt for 'Prioritize Quantity' mode and the Loopring L2 pool exhausts its quota, you may not receive the entire amount instantly. The remaining tokens will be 'swapped' at the executed price once the pool is replenished.`, labelVaultAmount: 'Amount', labelVaultOpenPositionDes: 'Please open a position first.' , labelVaultCollateralManagement: 'Collateral Management', labelGuardianCodeNetworkError: 'Network not match', labelGuardianCodeAccountError: 'guardian not match current account', labelGuardianCodeFormatError: 'wrong data format', labelGuardianCodeOwner: 'Owner', labelGuardianCodeRequestedWallet: 'Requested wallet', labelGuardianCodeRequestType: 'Request Type', labelGuardianCodeNetwork: 'Network', labelGuardianCodeRequester: 'Requester', labelGuardianCodeAuthorizationResult: 'Authorization Result', labelGuardianCodeApproved: 'Approved', labelGuardianCodeReject: 'Reject', labelGuardianCodeApprove: 'Approve', labelGuardianCodeShare: 'Share', labelGuardianCodeNext: 'Next', labelGuardianCodeEnterApprovalCode: 'Enter Approval Code', labelGuardianCodeEnterApprovalCodeHint: 'Please enter the Approval Code', labelGuardianCodeConfirmationDes: `Kindly approve the operation request from the wallet you are safeguarding. The approval process is fee-free. For added security, we request you enter the authorization code generated by the requester. If you haven't received this code, kindly synchronize with the requester for the necessary information.`, labelGuardianCodeSharingDes: `Please send the QR code to the requester`, labelGuardianCodeSharingApprovalRequest: 'Approval Request', labelGuardianNotWhoIProtect: 'Request not from who I protect', labelEarnAssetDefiPortfolio: 'DeFi Portfolio', labelEarnDepositDes: `There are no assets in your Loopring DeFi account yet. Please deposit a supported token to start exploring the DeFi world.`, labelEarnAssetTokens: 'All Tokens', labelBlockTradeTutorial: 'Block Trade Tutorial', labelAccountCreatedHint: 'Your Loopring DeFi account has been created. Please sign to complete the process.', labelFrozen: 'Frozen', labelBTradeTutorial: ` Block Trade offers a secure and trustless way for users to swap tokens using CEX liquidity. The trades happen exclusively between designated entities, ensuring that the existing liquidity of the DEX remains unaffected. There is no price impact to other DEX users as a result of the transaction. `, labelLearnMore: 'Learn More >', labelLearnMore2: 'Learn More', labelInvestDualDes: 'Buy the dip or sell the covered gain while earning a high yield', labelPortalDes: 'Loopring Portal can be treated as an isolated margin account allowing users to borrow/lend tokens with collateral. ', labelBtradeDes: 'Offers a secure and trustless way for users to swap tokens using CEX liquidity.', labelAPY: 'APY', labelAction2: 'Action', labelTotalBalance: 'Total Balance', labelBalance: 'Balance', depositLabelEnterTokenEarn: '选择充值代币', labelLaunch: 'Launch', labelSmartWalletDepositError1: 'Currently, only EOA wallets are supported.', labelSmartWalletDepositError2: 'Loopring Smart Wallet support is coming soon ', labelSmartWalletDepositError3: 'stay tuned for updates!', labelDustCollectorDetail: 'Dust Collector', labelVaultRedeem: 'Redeem Collateral', labelVaultRedeemNotEnough: 'Exceeded the maximum {{arg}} redemption value', labelVaultJoinRedeem: 'Redeem Collateral', labelVaultCollateralDetails: 'Collateral Details', labelVaultMaximumCredit: 'Maximum Credit', labelVaultMaximumCreditDes: 'Maximum Credit means the maximum amount of money you can borrow from Portal based on your collateral. It is calculated by taking the total value of your collateral, adjusted for price factor and the maximum leverage.', labelVaultMaximumCreditFormula: 'Maximum Credit = Sum ( Token_MarketPrice * Token_Amount * Token_PriceFactor ) * Maximum_Leverage', labelVaultPriceFactor: 'The Price Factor of each collateral', labelVaultMaximumLeverage: 'Maximum Leverage', labelVaultLeverage: 'Leverage', labelVaultAvailableBorrow: 'Borrowing Limit', labelVaultLeverageRisk1: '· Selecting higher leverage will increase your liquidation risk.', labelVaultLeverageRisk2: '· Borrowing Limit is based on the upper limit corresponding to the leverage you choose. The total value of all your borrowed tokens will not exceed the Borrowing Limit.', labelVaultLeverageRisk3: '· Maximum Credit is the maximum amount you can borrow based on your collateral.', labelVaultDebt: 'Debt', labelVaultDebtDetails: 'Details', labelVaultTotalBorrowed: 'Total Borrowed', labelVaultTotalFundingFee: 'Total Funding Fee', labelVaultValueEst: 'Value (est.)', labelVaultDustCollectorDes: `The accumulated dust will be converted into corresponding USDT. If you do not have any USDT to be repaid, the converted amount will be deposited into your portal's USDT. `, labelVaultConvert: 'Convert', labelVaultMarginLevelAlert: 'Your margin level has reached a critical point. Please review your positions and considering adding additional collateral. Failure to address the margin call may result in forced liquidation.', labelVaultDetail: 'Detail', labelVaultDefaultLeverage: 'Default Leverage', labelVaultHoldingCollateral: 'Holding Collateral', labelVaultRedeemAlert: 'Collateral valued at more than twice your total debt can be withdrawn from your Portal account.', labelTotalValue: 'Total Value', labelVaultRepayment: 'Payment', labelDetails: 'Details', labelVaultErrorOccurred: 'An error has occurred. Please try again later.', labelVaultDetails: 'Details', labelVaultNoDust: 'No Dust to Collect', labelVaultJoinAdd: 'Add Collateral', labelVaultChangeLeverageFailed: 'Change Leverage Failed', labelVaultChangeLeverageFailedTooSmall: `Leverage is smaller than permitted. Insufficient Available Borrow amount.`, labelVaultMaximumCreditDesLong: 'Maximum Credit means the maximum amount of money you can borrow from Portal based on your collateral. It is calculated by taking the total value of your collateral, adjusted for price factor and the maximum leverage.', labelVaultMarginLevelDes: 'Margin Level provides an indication of how close your account is to experiencing a margin call or being liquidated. To avoid liquidation, you can either supply more collateral or repay borrowed positions.', labelLayer2HistoryTaikoLockRecords: 'Taiko Farming', labelTaikoFarmingUnlock: "Unlock the Value of Your Locked TAIKO !", labelTaikoFarmingMintInfo: "Mint lrTAIKO and use it in Loopring DeFi at zero cost!", labelTaikoFarming: "Taiko Farming", labelTaikoFarmingDescription: "Earn Trailblazers points 60x faster by locking TAIKO and participating in Taiko Farming. Please note that TAIKO can only be unlocked after the end of Trailblazers Campaign Season 2.", labelLock2: "LOCK", labelEnterPaymentToken: "Enter payment token", labelMax: "Max", labelSelectToken: "Select token", labelAcknowledgeAndProceed: "I acknowledge and would like to proceed.", labelUnlockAfterSeason2: "You can unlock your TAIKO tokens only after the end of Trailblazers Season 2.", labelCurrentLockedPosition: "My Current Locked Position", labelTotalPoints: "Total Points", labelDailyEarningPoints: "Daily Earning Points", labelTaikoFarmingBannerTitle: "Taiko Farming", labelTaikoFarmingBannerDes: "Earn Trailblazers points 60x faster by locking TAIKO and participating in Taiko Farming. Please note that TAIKO can only be unlocked after the end of Trailblazers Campaign Season 2.", labelTaikoFarmingQuestion: "Worried about missing trading or earning opportunities when locking your valuable TAIKO?", labelTaikoFarmingExplanation: "Loopring lets you mint lrTAIKO at a 1:1 ratio, allowing you to freely use it across all trading and earning options within Loopring DeFi. Plus, you'll continue farming Trailblazers points.", labelTaikoFarmingComingSoon: "The lrTAIKO mint feature is coming soon!", labelTaikoFarmingPortalIntegration: "The first DeFi integration will be with Portal, allowing you to use lrTAIKO as collateral for leveraged trading on select hot tokens. You'll be able to go LONG or SHORT on your favorite tokens based on market conditions.", labelTaikoFarmingStayTuned: "Stay tuned for the upcoming release—more exciting use cases are on the way!", labelTaikoFarmingExploreUseCase: "Take a tour to explore all the current Loopring DeFi use cases ", labelTaikoFarmingHereLink: "", labelTaikoFarmingDepositAnytime: ". You can deposit assets into Loopring DeFi anytime and start enjoying the experience!", labelTotalPointsDes: 'Please note that Trailblazers points are awarded directly by the Taiko team. The Loopring dashboard provides a reference for the points you may receive from Taiko, but if there are any discrepancies, the official results from Taiko will be the final and trusted source.', labelTAIKO_FARMING: 'Taiko Farming', labelDefiCloseCian: 'The ciETH subscription has been deprecated. We recommend redeeming your ciETH for ETH as soon as possible. You can also explore other ETH staking portfolios for alternative options.', labelVaultMarketTitle: 'Market', } ================================================ FILE: packages/common-resources/static-resources/src/i18n/zh_CN/error.ts ================================================ export default { labelConnectUsSimple: 'Please <1>contact us.', labelConnectUs: '这有可能时候一个错误, 如需帮助请<1>联系我们~', errorBase: '奥哦! 遇到了一些问题', errorTimeout: 'Oops! Something went wrong at network.', errorLoading: '不要着急, 不要着急马上就好', error404: '四〇四! 这是页面不存在呢.', errorMaintain: '系统维护中...', errorMessageNoNetwork: 'Oops! Something went wrong at service.', errorUnknown: 'Unknown Error', errorOnFromSubmit: 'Submit request Info has Error!', errorWrongAccount: 'Error on account Info!', errorWrongToken: 'Error on Token Info!', errorWrongApikey: 'Error on signature or no API key.', errorWrongBalance: 'Insufficient balance', errorMinimumOrder: 'Trade amount is minimum', errorMaximumOrder: 'Trade amount is maximum', errorOnFrozen: 'Some of your assets are frozen, please try again later.', errorAboutFee: 'Fee token is not enough or incorrect.', errorProviderError: 'Global ethereum is not {{name}}, please disable your other Wallet Plugin.', errorOnStorageId: 'Error on wrong StorageId', errorOnNoRecipient: 'Error on no Receiver', errorNoMarket: 'Market is not supported.', errorOnGas: 'Error on Gas Info', errorOnCancelOrders: 'Error on cancel order', errorInvalidHash: 'Invalid Hash', errorInvalidOrderId: 'Invalid OrderId', errorOnRate: 'Error on submit rate value', errorForExistOrder: 'Order does not exist', errorOrderExpired: 'Order has expired', errorDisableOtherWalletForCurrent: 'Global ethereum is not {{name}}, please disable other Wallet Plugin.', errorGenerateEddsa: 'Generate EdDSA key has failed.', errorNotInstallGME: 'Please install the Gamestop Wallet browser extension. Then create, deposit, and activate the wallet before connecting to Loopring.io.', errorLinKWalletApp: '<0>app market', errorIpfsDidToNftidError: 'IPFS Gateway Error', errorMintOverlap: 'You have already minted this NFT. Please check your Wallet', errorJSONStringify: 'Wrong JSON format', errorCollectionMetadataNoTileUri: 'Tile uri is required', errorCollectionNoName: 'Name is required', errorCollectionSameName: 'Collection called {{name}} is existe', errorCollectionInfo: "We've detected this collection is not yours or the Contract Address don't match", errorCollectionNoSupport: 'Only {{l2symbol}} collection is allow to mint', errorCollectionNotReadable: 'Read Collection Info Failed', errorIpfsTimeout: 'IPFS Gateway timeout, please try again', errorRampNoInstance: 'Ramp Widget had out-off service, please re-order it.', errorDualExpired: 'The order has expired.', errorPrivateKey: 'Signature Wrong private key', errorNoResponse: 'No response!', errorMinError: 'Minimum of {{value}}', errorMaxError: 'Max of {{value}}', errorLengthLimit: 'Length is limit', errorRedpacketEmpty: 'Red Packet is Empty', errorRedpacketClaimed: 'You already opened Red Packet', errorRedpacketClaimOut: 'You opened the Red Packet Too Later', errorRedpacketClaimTimeOut: 'You opened the Red Packet Too Later', errorOffRampExpired: 'The order has expired. please create a neworder ', errorNFTRedPacketMaxError: 'Total Amount cannot exceeds {{value}} NTFs', errorBetaEnv: 'Oops! This site is not released yet and could contain bugs. Please use it with caution!.', errorProviderErrorUnknown: 'Unknown Error', errorNoGamestopExtension: 'User not installed GameStop extension', errorSwitchEthereum: 'wallet switch Ethereum Chain is not allowed', errorEthereumNotMetamask: 'Global ethereum is not MetaMask', errorGamestopNoChainChange: 'Gamestop extension no switch Ethereum Chain method', errorHadUnknownCollectionDes: 'As the creator, you will be able to generate collection information for those NFT minted earlier that belong to nowhere. And once done, the other people holding your NFT will be able to view those NFT with proper collection information via loopring.io and loopring wallet. <1>Go', errorGuardianRouterError: 'Oops! Guardian site moved to guardian.loopring.io.', errorAddressCheckError: 'Oops! Something went wrong on address checking processing ', errorContactExisted: 'Contact address already exists', errorContactNameExisted: 'Name already exists', errorContactLimit: 'Unable to Add Contact,You have reached the maximum limit of 1500 contacts', errorContactOverLimit: 'Unable to Add Contact,You have reached the maximum limit of 1500 contacts', labelContactsAddSuccess: 'Add Contact Succeed', labelContactsDeleteSuccess: 'Delete Contact Succeed', labelContactsEditSuccess: 'Edit Contact Succeed', labelContactsSendSuccess: 'Send Succeed', labelContactsCopySuccess: 'Copied to Clipboard', labelContactsAddFailed: 'Add Contact Failed', labelContactsDeleteFailed: 'Delete Contact Failed', labelContactsEditFailed: 'Edit Contact Failed', labelContactsSendFailed: 'Send Failed', errorOrderFailed: 'Order status Failed', } ================================================ FILE: packages/common-resources/static-resources/src/i18n/zh_CN/index.ts ================================================ import common from './common' import layout from './layout' import tables from './tables' import error from './error' import landPage from './landPage' import webEarn from './webEarn' export default { common, layout, tables, error, landPage, webEarn } ================================================ FILE: packages/common-resources/static-resources/src/i18n/zh_CN/landPage.ts ================================================ export default { labelH1Title: '路印二层,\n 第一个去中心化 \n交易平台 ', labelH1TitleDetail: 'Fast, Secure, and 100x Lower Fees', labelH1TitleWallet: 'Loopring Wallet', labelH2TitleWallet: 'FREEDOM AT YOUR FINGERTIPS', labelProtocol: 'loopring offers', labelStartTrade: 'start trading', labelTitleDEX: 'Loopring Dex', labelTradeVolume: '交易 \n 量', labelTradeUser: '注册 \n 用户', labelTradeTVL: '流动性', labelTradeNofTrades: '成交 \n 量', labelZeroKpt: '零知识证明技术', labelLoopringWallet: 'LOOPRING WALLET', labelBtnLearnMore: 'LEARN MORE', labelSafety: 'Ultimate Security', labelLowCost: 'Low Transaction Fees', labelFastTransfer: 'High Throughput', labelSuperpowers: 'Ready for Developers', describeSafety: 'Assets on Loopring L2 are equally secure as they are on the Ethereum mainnet.', describeLowCost: 'Transaction fees are reduced to 1/30 - 1/100 of the fees on the Ethereum mainnet.', describeFastTransfer: 'Loopring L2 can settle ~2000 transactions per second with near instant finality.', describeSuperpowers: "Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's time-tested zkRollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.", labelBtnStart: 'LAUNCH LOOPRING L2 APP', labelBtnDeveloper: 'Developer Documentation', labelFirstWallet: 'BETTER THAN BEING SMART', labelFirstWalletDetail: 'Smart wallets enable limitless extensibility over EOA wallets, but Loopring Wallet takes one step further with native zkRollup layer 2 integration. Now you can transact directly on Loopring L2, faster and cheaper.', labelWalletSecureH1: 'SECURE, at its core', labelWalletSecureH2: 'With social-recovery and guardians', labelWalletSecureDetail: 'Completely self-custodial, only you control your assets. You can choose people, institutions, and hardware that you trust to be the guardians of your wallet. You can also set quota for daily transfers, and lock it down if ever needed.', labelWalletIdentityH1: 'OWN YOUR IDENTITY', labelWalletIdentityH2: 'Your wallet is your continuous Web3 identity.', labelWalletIdentityDetail: 'The Loopring Wallet decouples identity and security. You can choose your wallet name and address, and maintain this identity forever with renewable transaction signing keys.', labelWalletUsageH1: 'EASY TO USE', labelWalletUsageH2: 'with zkRollup scaling solution', labelWalletUsageDetail: "The Loopring Wallet integrates Loopring's Layer 2 zkRollup technology to greatly increase speeds and lower fees, while inheriting the same security guarantees as the Ethereum mainnet.", labelWalletFutureH1: 'FORWARD LOOKING', labelWalletFutureH2: 'Easily upgradable to adapt future scenarios', labelWalletFutureDetail: 'Based on smart contracts, the Loopring Wallet adopts a pluggable, modular design which can be continuously upgraded under your authorization to use future standards and new technologies.', labelWalletUnleashed: 'ETHEREUM UNLEASHED', labelWalletUnleashedDetail: 'Your mobile gateway to Ethereum DeFi, collectibles, and more.', labelLaunchApp: 'View Dapps', labelLaunchMobileApp: 'Exchange', labelUpgradeHide: 'Loopring System Service Upgrade: Loopring will perform a temporary system upgrade starting at 2021/12/20 2:00pm (UTC+8), which will take approximately 2 hours.', labelUpgradeShow: 'Loopring System Service Upgrade: Loopring will perform a temporary system upgrade starting at 2021/12/20 2:00pm (UTC+8), which will take approximately 2 hours. During this period, the following relayer services will be suspended: deposits, withdrawals, trading and wallet creation. Deposits that are not completed before the start of the upgrade will be processed after the completion of the upgrade. We apologize for any inconvenience this may cause, and thank you for your patience! Please note: The 2 hours is our best estimation and may vary. As usual, we will maintain regular communication on the progress of the upgrade on our Twitter and Discord.', labelGoGuardian: 'Manage Security', labelHaveWallet: 'Already have a wallet?', labelMainDes: 'Unleash the Power of DeFi and Advanced Trading on \n Top of an App-Specific ZK-Rollup', labelEthereum: 'Ethereum ', labelGoDapps: 'Go Dapps', labelNavPro: 'Loopring pro', labelNavEarn: 'Loopring DeFi', labelNavWallet: 'Wallet', labelNavLanuch: 'View Dapps', labelLoopringSmartWallet: 'Loopring Smart Wallet', labelLoopringSmartWalletDes: "Ethereum's most secure wallet, fully integrated with advanced trading functionality and innovative Earn products, unlocking the true potential of Layer 2.", labelEthereumUnleashed: "<0>Ethereum's First zkRollup <1>Layer 2", labelGatewayToEthereum: 'Your gateway to Ethereum, DeFi, NFTs, and Financial Freedom.\n Fast, Low-cost, & Secure', labelTitleLite: 'Loopring DeFi', labelLoopringProtocol: 'Loopring Protocol', labelLoopringProtocolDes: "The world's first ZKRollup implementation designed to scale Ethereum, fully optimized for trading.", labelUltimateSecurity: 'Ultimate Security', labelUltimateSecurityDes: 'Assets on Loopring L2 are equally secure as they are on the Ethereum mainnet.', labelLowTransactionFees: 'Low Transaction Fees', labelLowTransactionFeesDes: 'Loopring performs most operations, including trade and transfer settlement, off the Ethereum blockchain. This dramatically reduces gas consumption and overall transaction cost to small fractions of comparable on-chain cost.', labelHighThroughput: 'High Throughput', labelHighThroughputDes: 'Loopring L2 can settle ~2000 transactions per second with near instant finality.', labelReadyForDevelopers: 'Ready for Developers', labelReadyForDevelopersDes: "Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's battle-tested zkRollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.", labelFeatureDes8: 'Loopring Layer2', labelFeatureDes8_2: 'Crypto exchange on the go', labelGo: 'Go', labelTitleLiteDes: 'Revolutionizing Decentralized Finance with Cutting-Edge Earning and Trading Solutions.', labelInvestStakeLRCDes: 'Lock your LRC for a minimum duration to earn interest and redeem at any time.', labelInvestDualDes: 'Buy the dip or sell the covered gain while earning a high yield.', labelInvestLeverageETHDes: "Boost your earnings with CIAN protocol's leveraged ETH staking strategy.", labelInvestDefiDes: 'Earn steady income while holding! Stake ETH to accumulate daily rewards.', labelInvestAmmDes: 'Participate in farming pools, earn additional protocol fee incentives for HOT pools.', labelProductsTitleDes: 'A wide variety of trading tools to choose from', labelProductsTitle: 'Trading Products', labelSpotDes: 'Full access to all trading tools', labelSpot: 'Spot', labelSwapDes: 'Intuitive trading interface', labelSwap: 'Swap', labelBlockTradeDes: 'Trade via CEX liquidity', labelBlockTrade: 'Block Trade', labelFiatDes: 'On & Off Ramp Solutions', labelFiat: 'Fiat', labelNFTCollections: 'Manage and Display Your NFT Collections', labelNFTCollectionsDes: 'Collaborate on a strategic plan to integrate \n NFTs into your ecosystem', labelRedPackets: 'Red Packets', labelRedPacketsDes: 'Explore the various use cases of our revolutionary Red Packets! Send token or NFT gifts directly or gamify the experience with blind boxes.', labelEndProductTitle: 'Earn Products', labelEndProductTitleDes: 'Choose from a variety of financial products', labelSupportedNetworkDes: 'Supported networks: Ethereum & Taiko', labelLaunch: 'Launch', labelLeadingOnChainStructuredProduct: 'Leading On-Chain Structured Product', labelLeadingOnChainStructuredProductDes: 'Buy the dip or sell at a higher price—all while earning high yields.', labelTradeWithCEXLiquidity: 'Trade with CEX Liquidity', labelTradeWithCEXLiquidityDes: 'Effortlessly trade tokens with near-zero slippage, leveraging a unified liquidity pool that combines both CEX and DEX liquidity to generate the best execution price.', labelUnlockThePowerOfLeveragedTrading: 'Unlock the Power of Leveraged Trading', labelUnlockThePowerOfLeveragedTradingDes: 'Seamlessly borrow tokens against your collateral, paving the way for leveraged trading opportunities. By bridging decentralized and centralized exchanges, Loopring Portal broadens your trading horizon, granting access to tokens beyond the Ethereum network.', labelEthereumOnly: 'Ethereum-Only', labelLoopringLayer2: 'Loopring Layer 2', labelLoopringLayer2Des: 'Unleash the Power of DeFi and Advanced \n Trading on Top of an App-Specific ZK-Rollup', labelTrade: 'Trade', labelTradeDes: 'A wide variety of trading methods to choose from', labelEarn: 'Earn', labelEarnDes: 'Choose from a variety of financial products', labelNFT: 'NFTs', labelNFTDes: 'Manage and display your NFT collections', labelRedPackets2: 'Red Packets', labelRedPacketsDes2: 'Explore the possibilities with our revolutionary Red Packets', labelLoopringSmartWalletDes2: 'Trade on the go - anytime, anywhere', labelExplore: 'Explore', labelLoopringProtocolDes2: 'The world\'s first ZKRollup implementation designed to scale Ethereum fully optimized for trading', labelProvenAppSpecificZKRollup: 'Proven App-Specific ZK-Rollup', labelProvenAppSpecificZKRollupDes: `Leverage the world's first ZK-Rollup implementation to unlock the full potential of DeFi and advanced trading`, labelAuditedAndSecure: 'Bulletproof Security', labelAuditedAndSecureDes: 'Assets on the Loopring Protocol are just as secure as they are on the underlying networks where the protocol is deployed', labelBringingCEXToDeFi: 'Bringing CeFi to DeFi', labelBringingCEXToDeFiDes: 'Seamlessly bring CeFi use cases into the DeFi world in a truly trustless manner', labelReadyForDevelopersDes2: `Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's battle-tested ZK-rollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.`, } ================================================ FILE: packages/common-resources/static-resources/src/i18n/zh_CN/layout.ts ================================================ export default { labelMarkets: '市场', labelTrade: '交易', labelLiquidity: '流动性矿池', labelMining: '挖矿', labelLayer2: '二层账号', labelWallet: '钱包', labelZkRollupLayer2: 'zkRollup Layer 2', labelSmartWallet: 'Smart Wallet', loopringDomain: '路印二层应用', labelClassic: '简洁版', labelClassicDescription: '快捷交易平台', labelAdvanced: '专业版', labelAdvancedDescription: '提供更丰富的交易模式', labelDownloadApp: '下载手机客户端', labelDownloadAppTitle: '下载钱包', labelDownloadBtn: '转去路印官网', labelNotification: '消息提醒', themeSetting: '主题模版', languageSetting: '语言设置', labelConnectWallet: '连接钱包', notificationApprove: '验证中', notificationPending: '进行中', labelMyTrades: '交易历史', labelRecentTrades: '最近交易', // layer2 submenu labelAssets: '资产信息', labelTransactions: '充值提现', labelTrades: '交易记录', labelHistory: '交易历史', labelOrder: '订单记录', labelReward: '', labelRedPacket: '红包', labelAmmRecords: 'AMM记录', labelAmmRecordsDes: '(出金和入金)', rewards: '推荐奖励', labelOrders: '订单记录', selectLanguage: '语言设置', labelPools: '流动性挖矿池', labelAmmMining: '交易大赛', labelMyLiquidity: '我的流动性', labelOrderBookMining: '订单本挖矿排名', labelMakerRebates: '做市收益', labelSetting: '高级设置', labelSecurity: '安全设置', labelVipPanel: 'VIP', labelContactsPanel: '联系人', titleLoopring: '路印协议', labelLoopringDescribe: '零知卷叠交易与支付协议', labelWrongNetwork: '未识别网络', labelYoutube: '油管', labelWeibo: '微博', labelFooterLoopring: 'Loopring', // Footer labelFooterAbout: 'About', labelkeyOrg: 'Loopring.org', labelkeyTerms: 'Terms of Service', labelkeyPrivacy: 'Privacy Policy', labelkeyNews: 'Blog', labelkeyRisks: 'Risk Disclosure', labelFooterPlatform: 'Platform', labelkeyFees: 'Fees', labelkeyVIP: 'VIP Program', labelkeyReferrals: 'Referrals', labelkeyTokenListing: 'List a Token', labelkeyCreatorGrants: 'Creator Grants', labelkeyL2Explorer: 'Layer 2 Explorer', labelkeySubgraph: 'Subgraph', labelkeyBugBounty: 'Bug Bounty', labelFooterSupport: 'Support', labelkeyFeedback: '❤️ Submit a Request', labelkeyGuardian: 'Wallet Guardian', labelkeyLoopringTutorial: 'Loopring Tutorial', labelkeyCommunityDocs: 'Community Docs', labelkeySupportCenter: 'Support Center', labelFooterDevelopers: 'Developers', labelkeySmartContract: 'Smart Contracts', labelkeyLoopringApp: 'Loopring App', labelkeyLoopringSmartWallet: 'Loopring Smart Wallet', labelkeyAPIs: 'APIs', labelkeyReportIssue: 'Report Issue', labelMyNFT: 'My NFTs', labelLaunchApp: 'View Dapps', labelLaunchMobileApp: 'Exchange', labelLandingHeaderLayer2: 'zkRollup Layer 2', labelLandingHeaderWallet: 'Smart Wallet', labelCopyRight: '© 2017 Loopring Technology Limited. All rights reserved.', labelWalletProtect: 'Guardian', labelWalletValidation: 'Approval Requests', labelWalletHistory: 'History', labelNFT: 'L2 NFT', labelMyAssetsNFT: 'My NFTs', labelMyAssetsNFTDes: 'Receive/Send NFTs', labelInvest: 'Earn', labelMintNFT: 'Create NFT', labelMintNFTDes: 'Create your own NFTs', labelTransactionNFT: 'NFT Transactions', labelTransactionNFTDes: 'NFT Transactions History', labelForceWithdraw: 'Force Withdraw', labelInvestBalance: 'My Investments', labelInvestBalanceDes: 'The deposition you hold', labelInvestAmm: 'AMM Pools', labelInvestOverview: 'Overview', labelInvestOverviewDes: 'Select DeFi Investment', labelInvestAmmDes: 'Add liquidity and earn fees', labelInvestDefi: 'ETH Staking', labelInvestDefiDes: 'Earn ETH staking rewards', labelInvestWSTETH: 'Via Lido', labelInvestWSTETHDes: 'Stake ETH get WSTETH', labelInvestRETH: 'Via Rocket Pool', labelInvestRETHDes: 'Stake ETH get RETH', labelInvestDual: 'Dual Investment', labelInvestDualDes: 'Buy the Dips, Sell the Rallies', labelDepositNFT: 'Receive NFT', labelMyCollection: 'My Collections', labelMyCollectionDes: 'Create, curate, and \n manage my NFT collections', labelInvestStakeLRC: 'LRC Staking', labelInvestStakeLRCDes: 'Earn LRC staking rewards', labelBtradeTrade: 'Block Trade', labelBtradeTradeDescription: 'Trade via CEX liquidity', labelStopLimit: 'Stop-Limit', labelStopLimitDescription: 'Control trading, like a pro', labelCopyRightBeta: 'This site is not released yet and could contain bugs. Please use it with caution!', labelReferralReward: 'Referrals Reward', // labelDepositNFTDes:"Receive NFT" labelInvestLeverageETH: 'Leveraged ETH Staking', labelInvestLeverageETHDes: 'Gain higher APY aggressively', labelDualInvest: 'Dual Investment', labelVault: 'Portal', labelNavPro: 'Loopring Layer 2', labelNavEarn: 'Loopring DeFi', labelNavWallet: 'Wallet', labelDashBoard: 'Dashboard', labelRecord: 'Records', labelHome: 'Loopring Layer 2', labelVaultDes: '', labelVaultHome: 'Home', labelVaultHomeDes: 'Browse tradable tokens', labelVaultDashboard: 'Dashboard', labelVaultDashboardDes: 'Manage assets and positions', labelVault2: 'Portfolio', labelUnlockFirst: 'Unlock First', labelVault2: 'Assets', labelTaikoFarming: 'Taiko Farming', } ================================================ FILE: packages/common-resources/static-resources/src/i18n/zh_CN/tables.ts ================================================ export default { labelStatus: '状态', labelAMM: 'AMM', labelSend: 'Send', labelSendL2: 'Send L2', labelSendL1: 'Send L1', labelL2toL1Action: 'Withdraw', labelReceive: 'Receive', labelTransfer: 'Transfer', labelWithdraw: 'Withdraw', labelWithdrawAction: 'Withdraw', labelDeposit: 'Deposit', labelTrade: '交易', labelToken: '币种', labelAmount: '总量', labelAvailable: '可用额度', labelLocked: '冻结', labelAssetsTableValue: '金额', labelActions: '操作', labelAllToken: '所有币种', labelHideSmallBalances: '隐藏小额资产', labelHideInvestToken: '隐藏流动性凭证', labelOrderFilterAllTypes: '所有类型', labelOrderFilterBuy: '买入', labelOrderFilterSell: '卖出', labelFilterReset: '重置', labelFilterSearch: '搜索', labelOrderSide: '卖/买', labelOrderAmount: '订单总量', labelOrderAverage: '均值', labelOrderPrice: '挂单价格', labelOrderFilledAmount: '成交部分', labelOrderFilledPrice: '成交价格', labelOrderFilterAllPairs: '所有交易对', labelOrderFee: '手续费', labelOrderRole: '订单类型', labelOrderTime: '挂单时间', labelOrderStatus: '状态', labelOrderTradingPrice: '交易价格', labelOrderTotal: '全部数量', labelQuotaPair: '交易对', labelQuotaLastPrice: '最新价', labelQuota24hChange: '24h 涨跌', labelQuota24hChangeLit: '24h Chg', labelQuota24hHigh: '24h 最高', labelQuota24hLow: '24h 最低', labelQuoteAction: '操作', labelQuota24hAmount: '24h 交易量', labelTradeFilterAllTypes: '所有类型', labelTradeFilterBuy: '买入', labelTradeFilterSell: '卖出', labelTradeSide: '兑换', labelTradeAmount: '交易总额', labelTradePrice: '价格', labelTradeFee: '费用', labelTradeTime: '交易时间', labelTradeRole: '交易类型', labelTradeRoleMaker: '挂单', labelTradeRoleTaker: '吃单', labelTradeConterparty: '交易对手方', labelTradeCounterpartyOrderbook: '订单本', labelTradeCounterpartyPool: '流动性矿池', labelTxFilterAllTypes: '所有类型', labelTxFilterReceive: 'Receive', labelTxFilterSendL1: 'Send L1', labelTxFilterSendL2: 'Send L2', labelTxFilterDeposit: 'Deposit', labelTxFilterWithdraw: 'Withdrawal', labelTxFilterTransfer: 'Transfer', labelTxFilterAllTokens: 'All Tokens', labelTxSide: '交易类型', labelTxToken: '币种', labelTxFrom: '起始', labelTxTo: '完结', labelTxAmount: '总额', labelTxFee: '费用', labelTxMemo: '备注', labelTxTime: '时间', labelTxTxnHash: '交易哈希', labelTxStatus: '状态', labelTransactions: 'Transactions', labelVolume: '交易量', labelTradePair: '交易对', labelPool: '资金池', labelLiquidity: '流动性', labelMyLiquidity: '我的流动性', label24TradeVolume: '24h 交易量', label24Reward: '24h 奖励', labelAPR: 'APR', labelTradePool: '入金', labelAction: '操作', labelFilter: '搜索', labelFilterAllPairs: '所有交易对', valueAddTOAMM: `添加 <1> 和 <3>`, valueSwapForAMM: `<1> 和 <3> 兑换`, valueRemoveTOAMM: `移出 <1> 和 <3>`, labelAmmTotalValue: '价值总和', labelAmmTokenAmount: '代币数量', labelAmmTime: '时间', labelFeeEarned: '赚取费用', labelBuy: '买', labelSell: '卖', labelAmmSide: '入金/出金', labelAmmAmount: '交易总额', labelAmmLpTokenAmount: '流动性资产变化', labelAmmFee: '费用', labelAmmRecordTime: '交易时间', labelAmmExit: '出金', labelAmmJoin: '入金', labelAmmFilterTypes: '所有类型', labelAmmFilterJoin: '入金', labelAmmFilterExit: '出金', labelPoolTableAddLiquidity: '入金', labelPoolTableRemoveLiquidity: '出金', labelEmptyDefault: '记录是空的', labelAmmTableType: '交易类型', labelOrderDetailTaker: '吃单', labelOrderDetailMaker: '挂单', labelOrderDetailTradingVolume: '交易量', labelOrderDetailOrderId: '订单号', labelOrderCancelAll: '撤销所有订单', labelOrderCancelOrder: '撤销订单', labelOrderProcessing: '挂单中', labelOrderProcessed: '完全成交', labelOrderCancelling: '取消中', labelOrderCancelled: '已取消', labelOrderExpired: '已过期', labelOrderWaiting: '等待队列中', // labelOrderAmm: 'AMM', labelOrderLimitOrder: '限价单', labelOrderMarketOrder: '市价单', // labelOrderMaker: '挂单', labelOrderTaker: '吃单', labelOrderChannelsMixed: '混合订单', // labelOrderChannelsAMM: 'AMM 订单', labelOrderChannelsSwap: '闪兑', labelOrderChannelsOrderBook: '订单本', labelOrderTypes: '订单类型', labelOrderChannels: '订单渠道', labelOrderCompletion: '成交比例', labelRewardTableAmount: '奖励数量', labelRewardTableTime: '时间', labelOrderCancelConfirm: '确认撤销该订单?', labelOrderConfirm: '确定', labelOrderCancel: '取消', labelTradeProPrice: '价格({{symbol}})', labelTradeProAmount: '总量', labelVipTableLevel: 'Level', labelVipTable30dTradeVolume: '30d Volume', labelVipTableRule: 'and / or', labelVipTableBalance: 'LRC Balance', labelVipTableMaker: 'Maker', labelVipTableTaker: 'Taker', labelTradeRaceRank: 'Rank', labelTradeRaceAddress: 'Address', labelTradeRaceTradeVolume: 'Trade Volume', labelTradeRaceAward: 'Award', labelTradeRaceProject: 'Project', labelTradeRacePair: 'Pair', labelTradeRaceToken: 'Token', labelTradeRaceReward: 'Reward', labelMaker: 'Maker', labelTaker: 'Taker', labelNFTTypeDEPOSIT: 'Deposit', labelNFTTypeTRANSFER: 'Transfer', labelNFTTypeWITHDRAWAL: 'Withdraw', labelNFTTypeWITHDRAW: 'Withdraw', labelNFTTypeWITHDRAW_LUCKY_TOKEN: 'Claim Red Packet', labelNFTTypeSEND_BACK_LUCKY_TOKEN: 'Back From Red Packet', labelNFTTypeSEND_LUCKY_TOKEN: 'Send Red Packet', labelMint: 'Mint', labelNFTTypeMINT: 'Mint', labelTxNFTFilterALL: 'All Types', labelTxNFTFilterDEPOSIT: 'Deposit', labelTxNFTFilterWITHDRAW: 'Withdraw', labelTxNFTFilterWITHDRAWAL: 'Withdraw', labelTxNFTFilterRECEIVE: 'Receive', labelTxNFTFilterSEND: 'Send', labelTxNFTFilterFORCEWITHDRAW: 'Force Withdraw', labelTxNFTFilterTRANSFER: 'Transfer', labelTxFilterALL: 'All Types', labelTxFilterRECEIVE: 'Receive', labelTxFilterSEND: 'Send', labelTxFilterFORCEWITHDRAW: 'Force Withdraw', labelTxFilterDEPOSIT: 'Deposit', labelTxFilterWITHDRAW: 'Withdraw', labelTxFilterWITHDRAWAL: 'Withdraw', labelTxFilterTRANSFER: 'Transfer', labelTypeDEPOSIT: 'Receive', labelTypeWITHDRAW: 'Send', labelTypeSEND: 'Send', labelTypeReceive: 'Receive', labelTypeONCHAIN_WITHDRAWAL: 'Send', labelTypeOFFCHAIN_WITHDRAWAL: 'Send', labelTypeTRANSFER: 'Send', labelTypeWITHDRAW_LUCKY_TOKEN: 'Claim Red Packet', labelTypeSEND_BACK_LUCKY_TOKEN: 'Back From Red Packet', labelTypeSEND_LUCKY_TOKEN: 'Send Red Packet', labelTypeDUAL_INVESTMENT: 'Dual Investment Settled', labelTypeL2_STAKING: 'Claim Earnings', labelTxNFTFilterMINT: 'Mint', labelShowFilter: 'Show Filter', labelTypeDELEGATED_FORCE_WITHDRAW: 'Force Withdraw', labelForceWithdrawTotalDes: 'All {{symbol}}', labelForceWithdrawDes: '{{address}} (Address that the token is withdrawn from L2 to L1)', labelDefiInvest: 'Subscribe', labelDefiRedeem: 'Redeem', labelSelect: 'Select', labelDuration: 'Duration', labelInvestAll: 'Both', labelDefiExit: 'Redeem', labelDefiJoin: 'Subscribe', labelDefiType: 'Subscribe/Redeem', labelDefiFee: 'Fee', labelDefiTime: 'Time', labelDefiAmount: 'Amount', labelFilterTradeNFTAll: 'All', labelFilterTradeNFTSell: 'Sell', labelFilterTradeNFTSelf: 'Self Trade', labelFilterTradeNFTBuy: 'Buy', labelTradeNFTSide: 'Trade', labelFrom: 'from', labelTo: 'to', labelUPrice: 'unit price: ', labelTradeNFTUnitPrice: 'Unit Price', labelDualApy: 'APR', labelDualTerm: 'Term', labelDualPrice: 'Target Price', labelDualTPrice: 'Target', labelDualSettlement: 'Settlement Date', labelDualAction: 'Action', labelInvestmentStatusSettled: 'Settled', labelInvestmentStatusDelivering: 'Delivering', labelInvestmentStatusSubscribe: 'Earning', labelDualAssetProduct: 'Product', labelDualAssetFrozen: 'Invest', labelDualAssetPrice: 'Target Price', labelDualAssetSettlement_Date: 'Settlement Date', labelDualAssetAPR: 'APR', labelDualAssetAction: 'Actions', labelDualAssetDetail: 'Details', labelDualAssetRefresh: 'Refresh', labelDualTxsSide: 'Type', labelDualTxsProduct: 'Product', labelDualTxAPR: 'APR', labelDualTxsTargetPrice: 'Target Price', labelDualTxsSettlement_Date: 'Settlement Date', labelDualTxsSettlementPrice: 'Settlement Price', labelDualTxsSettlement: 'Settlement', labelDualTxsTime: 'Time', labelDualAssetReturn: 'Return', labelAprPool: 'Amm Pool APR:', labelAprFee: 'Protocol APR:', labelAprEvent: 'Activity APR:', label24Rewards: '24h Rewards', labelRewardFee: 'AMM Rewards:', labelRewardReward: 'Other Rewards:', labelRewardExtra: 'Campaign Rewards:', labelRecordToken: 'Token', labelRecordAmount: 'Amount', labelRecordType: 'Type', labelRecordStatus: 'Status', labelRecordNumber: 'Number', labelRecordTime: 'Time', labelReceiveTime: 'Receive Time', labelValue: 'Value', labelClaim: 'Claim', labelAddress: 'Address', labelType: 'Type', labelDefiStakingProduct: 'Product', labelDefiStakingFrozen: 'Frozen', labelDefiStakingEarn: 'Cumulative Earnings', labelDefiStakingPreviousEarn: "Previous Day's Earnings", labelDefiStakingDuration: 'Holding Time', labelDefiStakingARR: 'APR', labelDefiStakingAction: 'Action', labelDefiStakingTxType: 'Type', labelDefiStakingTxAmount: 'Amount', labelDefiStakingTxProduct: 'Product', labelDefiStakingTxLockedDuration: 'Locked Duration', labelDefiStakingTxHashId: 'HashID', labelDefiStakingTxSubscribeTime: 'Time', labelDefiStakingTxRewardsDate: 'Rewards Date', labelDefiStakingTxTxID: 'TxID', labelDefiStakingTxTime: 'Time', labelDefiStakingDetail: 'Detail', labelDefiStakingRedeem: 'Redeem', labelDays: 'day(s)', labelStakeTransactionTypesubscribe: 'subscribe', labelStakeTransactionTyperedeem: 'redeem', labelStakeTransactionTypeclaim: 'claim', labelDefiStakingAndPreviousEarn: "Cumulative & Previous Day's Earnings", labelDefiStakingTxRewardsMobileDate: 'Date', labelBlindBoxOpend: 'Opend', labelBlindBoxEndTime: 'End Time', labelBlindBoxCalim: 'Claim', labelBlindBoxExpired: 'Expired', labelBlindBoxClaimed: 'Claimed', labelBlindBoxStartTime: 'Reveal Time: {{time}}', labelBlindBoxExpiredTime: 'Expiration Time: {{time}}', labelRedPacketOpen: 'Open', labelRedPacketSenderAddress: 'Sender Address', // labelRecordAction: "Action", labelBtradeSwapType: 'Type', labelBtradeSwapFee: 'Trading Fee', labelBtradeSwapTime: 'Time', labelBtradeSwapPrice: 'Price', labelBtradeSwapSettled: 'Settled', labelBtradeSwapDelivering: 'Delivering', labelBtradeSettled: 'Settled', labelBtradeDelivering: 'Delivering', labelBtradeDeliveringDes: 'The Loopring pool is currently unable to swap the full requested amount. The tokens that were successfully swapped will be transferred to your account now. The unswapped tokens will be locked until they can be swapped. \n We’ll rebalance the pool shortly and swap the remaining portion.', labelBtradeFailed: 'Failed', labelBtradeCancelled: 'Cancelled', labelBtradePending: 'Pending', labelBtradeSwapFailed: 'Filled', labelStopLimitStopPrice: 'Trigger Condition', labelDUAL_CURRENCY: 'Dual', labelDUAL_BASE: 'Dual', labelBTRADE: 'Block Trade', labelL2STAKING: 'LRC Staking', labelSTOP_LIMIT: 'Stop-Limit', labelStopLimitTriggered: 'Triggered: The limit order has been submitted to the order book.\n Time: {{time}}', labelStopLimitWaitingTrigger: 'The limit order is not placed until the stop price has been triggered.', labelRevealTime: 'Reveal Time', labelRevealTimeTooltip: 'The Reveal time is when the Red Packet ends, and recipients can open it to see if they have received', labelExpiredTime: 'Expired Time', labelExpiredTimeTooltip: 'After expiration, all unclaimed NFTs will be returned to the Sender.', labelRedpacketFromBlindbox: 'From Blind Box', labelRedpacketCantOpen: 'It is not yet time to open, reveal time: {{time}}', labelReferralsTableReferee: 'Referee', labelReferralsTableAmount: 'Rewards', labelReferralsTableTime: 'Time', labelRefundTableAmount: 'Refunds', labelRefundTableTime: 'Time', labelTxNetworkFee: 'Network Fee', labelTxTradingFee: 'Trading Fee', labelTypeUNIFIED_CLAIM: 'Claim Rewards', labelRedPacketClaiming: 'Claiming', labelLoadingMore: 'Loading More...', labelDualAutoReinvest: 'Auto Reinvest', labelDualAssetModify: 'Modify', labelDualAssetReInvestDisable: 'Disabled', labelDualAssetReInvestEnable: 'Enabled', labelDualAuto: 'ReInvest', labelDualFailed: 'Failed', labelDualPending: 'pending', labelDualAutoInvestTip: 'Auto Reinvest Status:{{}}', labelDualRetryStatusSuccess: 'Auto reinvested successful. A new order has been generated for you.', labelDualRetryStatusError: 'Auto reinvest failed. Cannot find product with Buy Price of {{price}} and Longest Settlement Date of {{day}} days. ', labelDualRetryStatusRetrying: 'Auto reinvesting. Searching for the product...', labelDualAssetReInvestYes: 'ReInvest On', labelDualAssetReInvestNo: 'ReInvest Off', labelDualRetryPending: 'Pending', labelDualRetryTerminated: 'Terminated', labelDualRetryFailed: 'Failed', labelDualRetrySuccess: 'Successful', labelDualRetryStatusTerminated: 'Auto Reinvest terminated. You successfully purchased the target token.', labelTypeCHANGE_PWD: 'Reset Loopring L2 Keypair', labelAveragePositionCost: 'Average Position Cost <1>', labelAveragePositionCostDes: 'The average holding cost of the last 10 purchases.', labelDefiApr: 'Est.APR <1>', labelDefiAprDes: 'Apr stands for annual percentage rate, not taking into account the effect of compound interest. The value displayed here indicates the current value, while it keeps changing dynamically. ', labelVaultAssetsTableValue: 'Value', labelVaultborrow: 'Borrow', labelVaultopen: 'Supply Collateral', labelVaultcloseout: 'Settle', labelVaultmargin: 'Margin Call', labelVaultrepay: 'Repay', labelVaulttrade: 'Trade', labelVaultborrowDes: 'Borrow des', labelVaultopenDes: 'Supply Collateral des', labelVaultcloseoutDes: 'Settle des', labelVaultmarginDes: 'Margin Call des', labelVaultrepayDes: 'Repay des', labelVaulttradeDes: 'Trade des', labelVaultTxType: 'Type', labelVaultTxFilled: 'Filled Amount', labelVaultTxStatus: 'Status', labelVaultTxTime: 'Time', labelVaultVAULT_STATUS_EARNING: 'Succeed', labelVaultVAULT_STATUS_RECEIVED: 'Received', labelVaultVAULT_STATUS_PROCESSING: 'Delivering', labelVaultVAULT_STATUS_SUCCEED: 'Success', labelVaultVAULT_STATUS_FAILED: 'Failed', labelVaultVAULT_STATUS_PENDING: 'Pending', labelDetail: 'Detail', labelVaultcloseoutForced: 'Forced Liquidation', labelInvest: 'Invest', labelVaultconvert: 'Dust Collector', labelVaultredeem: 'Redeem Collateral', labelVaultJoinRedeem: 'Redeem Collateral', labelFailed: 'Failed', labelTotalValue: 'Total Value', labelVaultConvert: 'Converted', labelVaultRepayment: 'Payment', labelVaultTime: 'Time', labelVaultDetails: 'Details', labelVaultErrorOccurred: 'An error has occurred. Please try again later.', labelVaultDustCollector: 'Dust Collector', labelDetails: 'Details', labelTAIKO_FARMING: 'Taiko Farming', } ================================================ FILE: packages/common-resources/static-resources/src/i18n/zh_CN/webEarn.ts ================================================ /* eslint-disable max-len */ export default { labelFAQ1Question: 'What is Loopring protocol ?', labelFAQ1Answer: "As the world's first ZKRollup implementation to scale up Ethereum, Loopring Protocol has run since 2017. There have been more than 210K users and $5.88B trading volume occurred on this protocol. As an app-specific ZKRollup protocol, it has been successfully deployed not only as a Layer 2 on top of Ethereum but also as a Layer 3 on top of other EVM-compatible networks such as Arbitrum.", labelFAQ2Question: 'What is Dual Investment ?', labelFAQ2AnswerLine1: 'Dual Investment is a non-principal protected structured product. Upon purchasing, you can select the underlying asset, investment currency, investment amount, and delivery date. Your return will be denominated in the investment currency or alternate currency, depending on the below conditions.', labelFAQ2AnswerLine2: 'There are two types of Dual Investment products: “Buy Low” and “Sell High”.', labelFAQ2AnswerLine3: 'Buy Low products gives you a chance to buy your desired crypto (such as LRC) at a lower price in the future with stablecoins (USDC).', labelFAQ2AnswerLine4: 'Target Reached: On the Settlement Date, if the Market Price is at or below the Target Price, the target currency (LRC) will be bought;', labelFAQ2AnswerLine5: 'Target Not Reached: On the Settlement Date, if the Market Price is above the Target Price, then you will keep your stablecoins; In both scenarios, you will first earn interest in stablecoins. Once the Target Price is reached, your subscription amount and interest income will be used to buy LRC.', labelFAQ2AnswerLine6: 'Sell High products gives you a chance to sell your existing crypto (such as LRC) at a higher price in the future (for USDC).', labelFAQ2AnswerLine7: 'Target Reached: On the Settlement Date, the Market Price is at or above the Target Price, your LRC will be sold for USDC.', labelFAQ2AnswerLine8: 'Target Not Reached: On the Settlement Date, the Market Price is below the Target Price, then you will keep your LRC. In both scenarios, you will first earn interest in your existing currency (LRC). Once the Target Price is reached, your subscription amount and interest income will be sold for USDC.', labelFAQ2AnswerLine9: 'Your token for investment is just locked but still in your account as Loopring is a DEX.', labelFAQ2AnswerLine10: ' Each purchased product has a settlement date. We will take an average of the market price in the last 30 minutes before 16:00 (UTC+8) on the delivery date as the settlement price.', labelFAQ2AnswerLine11: 'Please make sure that you fully understand the product and the risks before investing.', labelFAQ3Question: 'What is the Dual Investment Sell covered gain ?', labelFAQ3AnswerLine1: 'Covered Gain is an investment strategy to sell digital assets at your Target Price and earn interest while waiting.', labelFAQ3AnswerLine2: 'On the Settlement Date, there can be 2 scenarios:', labelFAQ3AnswerLine3: 'Market Price > Target Price', labelFAQ3AnswerLine4: 'Market Price ≤ Target Price', labelFAQ3AnswerLine5: 'Market Price > Target Price', labelFAQ3AnswerLine6: 'Your original investment and earned interest will be sold at the target price.', labelFAQ3AnswerLine7: 'This order is then closed regardless of whether "Auto Reinvest" is enabled or not.', labelFAQ3AnswerLine8: 'Market Price ≤ Target Price', labelFAQ3AnswerLine9: 'Your original investment and earned interest won’t be sold.', labelFAQ3AnswerLine10: 'If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully sell crypto at your desired price or disable the feature.', labelFAQ3AnswerLine11: 'Auto Reinvest', labelFAQ3AnswerLine12: 'When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully sell your crypto at your Target Price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.', labelFAQ3AnswerLine13: 'Sell Price: the Target Price at which you want to sell your crypto.', labelFAQ3AnswerLine14: "Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.", labelFAQ4Question: 'What is the Dual Investment Buy the dip ?', labelFAQ4AnswerLine1: 'Buy The Dip is an investment strategy to buy digital assets at your Target Price and earn interest while waiting.\n', labelFAQ4AnswerLine2: 'On the Settlement Date, there can be 2 scenarios:', labelFAQ4AnswerLine3: 'Market Price > Target Price', labelFAQ4AnswerLine4: 'Market Price ≤ Target Price', labelFAQ4AnswerLine5: 'Market Price > Target Price', labelFAQ4AnswerLine6: 'Your original investment and earned interest won’t be converted. Earned interest is in USDC or USDT.', labelFAQ4AnswerLine7: 'If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully buy crypto at your desired price or disable the feature.', labelFAQ4AnswerLine8: 'Market Price ≤ Target Price', labelFAQ4AnswerLine9: 'Your original investment and earned interest will be converted at the Target Price.', labelFAQ4AnswerLine10: 'This order is then closed regardless of whether "Auto Reinvest" is enabled or not.', labelFAQ4AnswerLine11: 'Auto Reinvest', labelFAQ4AnswerLine12: 'When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.', labelFAQ4AnswerLine13: 'Buy Price: the Target Price at which you want to buy crypto.', labelFAQ4AnswerLine14: "Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.", labelDualEarnTitle: 'DUAL INVESTMENT', labelDualEarnSubTitle: 'The most innovative structural products brought to the DeFi world', labelDualEarnTag1: 'Buy Low or Sell High', labelDualEarnTag2: 'No Trading Fees', labelDualEarnTag3: 'High Rewards', labelSellHigh: 'Sell Hign', labelBuyLow: 'Buy Low', labelInvestSymbol: 'Invest {{symbol}}', labelInvestSymbolSellHigh: 'Sell {{symbol}} High', labelInvestSymbolBuyLow: 'Buy {{symbol}} Low', labelApy: 'APY', labelCurrentPrice: 'Current Price', labelViewDetails: 'View Details', labelLoopringEarn: 'Loopring DeFi', labelLoopringEarnDes: 'Loopring DeFi is built on top of Loopring Protocol to take full advantage of its ZKRollup technology and full-stack DEX capability to provide the most innovative DeFi products to users.', labelLoopringProtocol: 'Loopring Protocol', labelLoopringProtocolDes: "The world's first ZKRollup implementation designed to scale Ethereum, fully optimized for trading.", labelUltimateSecurity: 'Ultimate Security', labelUltimateSecurityDes: 'Assets on Loopring are equally secure as they are on the Ethereum mainnet.', labelLowTransactionFees: 'Low Transaction Fees', labelLowTransactionFeesDes: 'Loopring performs most operations, including trade and transfer settlement, off the Ethereum blockchain. This dramatically reduces gas consumption and overall transaction cost to small fractions of comparable on-chain cost.', labelHighThroughput: 'High Throughput', labelHighThroughputDes: 'Loopring can settle ~2000 transactions per second with near instant finality.', labelFAQs: 'FAQs', labelBtradeSwapTitle: 'Block Trade', labelViewMore: 'View more', labelVault: 'Portal', labelInvestDualDes1: 'Experience the Leading On-Chain Structured Product', labelInvestDualDes2: 'Buy the dip or sell at a higher price - all while earning high yields.', labelPortalDes1: 'Unlock the Power of Leveraged Trading', labelPortalDes2: 'Seamlessly borrow tokens against your collateral, paving the way for leveraged trading opportunities. By bridging decentralized and centralized exchanges, Loopring Portal broadens your trading horizon, granting access to tokens beyond the Ethereum network.', labelBtradeDes1: 'Trade with CEX Liquidity', labelBtradeDes2: 'Effortlessly trade tokens with near-zero impact, leveraging multiple liquidity sources across crypto to guarantee the best rates every time.', labelInvestDualTitle: 'Dual Investment', labelLoopringDeFi: 'Loopring DeFi', labelIntroDes1: 'Revolutionizing Decentralized Finance with Cutting-', labelIntroDes2: 'Edge Earning and Trading Solutions', labelIntroDes3: 'Delivering CeFi-like user experiences in a fully trustless environment', labelLearnMore2: 'Learn More', labelReadyForDevelopers: 'Ready for Developers', labelReadyForDevelopersDes: "Build scalable payment apps, non-custodial exchanges, and NFT marketplaces on Ethereum with Loopring's battle-tested zkRollup technology. Get started with quick-start guides, protocol documentation, a Javascript SDK, and a fully open source codebase.", labelLaunch: 'Launch', } ================================================ FILE: packages/common-resources/static-resources/src/loopring-interface/CoinInterface.ts ================================================ import { Account, FloatTag, ForexMap, TradeStatus, TradeTypes, VaultSwapStep } from '../constant' import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' export type CoinKey = keyof R export type PairKey

    = keyof P export interface IBData { belong: CoinKey balance: number tradeValue: number | undefined } export interface IBDataMax extends IBData { max?: string } export interface CoinInfo { icon?: string name: string simpleName: CoinKey description?: string company: string belongAlice?: string [key: string]: any } export interface WalletCoin { belong: CoinKey count: number [key: string]: any } export type CoinMap> = { //[k in keyof R]: I; [K in CoinKey]?: I // [k in k extends typeof R]: I; } export interface FeeInfo { belong: string fee: number | string feeRaw?: string | number token?: string hasToken?: boolean count?: string discount?: number __raw__: { fastWithDraw: string tokenId: number feeRaw: string } } export enum TokenType { single = 'single', lp = 'lp', defi = 'defi', dual = 'dual', nft = 'nft', vault = 'vault', } export type PairMap< R extends { [key: string]: any }, P = { coinA: CoinInfo coinB: CoinInfo }, > = { [K in PairKey]?: P } export type WalletMap = WalletCoin> = { [K in CoinKey]?: I } export type TradeCalcData = { coinSell: keyof T //name coinBuy: keyof T buyPrecision: number sellPrecision: number // tokenA: sdk.TokenInfo, // tokenB: sdk.TokenInfo, StoB: string | undefined BtoS: string | undefined // marketPrecision: number, coinInfoMap?: CoinMap> sellCoinInfoMap?: CoinMap> buyCoinInfoMap?: CoinMap> walletMap?: WalletMap> slippage: number | string // slippageTolerance: Array, minimumReceived: string | undefined fee: string isReverse: boolean feeTakerRate?: number tradeCost?: string lastStepAt?: 'sell' | 'buy' totalQuota: string minimumConverted: string | undefined } & ( | { isBtrade: undefined | boolean } | { isVault: undefined | boolean } ) export type SwapTradeCalcData = TradeCalcData & { isNotMatchMarketPrice?: boolean marketPrice?: string marketRatePrice?: string isChecked?: boolean slippage: number | string priceImpact: string priceImpactColor: string feeTakerRate?: number tradeCost?: string isShowBtradeAllow?: boolean minimumConverted: string | undefined sellMaxAmtStr?: string sellMinAmtStr?: string } & ( | { isBtrade: undefined | boolean } | { isVault: undefined | boolean } ) export enum BtradeType { Quantity = 'Quantity', Speed = 'Speed', } export type BtradeTradeCalcData = TradeCalcData & { isBtrade: true maxFeeBips: number lockedNotification: true volumeSell: string | undefined volumeBuy: string | undefined sellMinAmtStr: string | undefined sellMaxL2AmtStr: string | undefined sellMaxAmtStr: string | undefined l1Pool: string l2Pool: string slippage: number | string btradeType: BtradeType // totalPool: string; } export type VaultTradeCalcData = Omit, 'btradeType' | 'isBtrade'> & { isVault: true belongBuyAlice: string belongSellAlice: string supportBorrowData: VaultBorrowData showHasBorrow: boolean isRequiredBorrow: boolean borrowVol: string borrowStr: string step: VaultSwapStep } export type TradeCalcProData = { coinBase: keyof T //name coinQuote: keyof T StoB: string BtoS: string coinInfoMap?: CoinMap> walletMap?: WalletMap> slippage: number | string priceImpact: string priceImpactColor: string minimumReceived: string fee: string feeTakerRate?: number tradeCost?: string lastStepAt?: 'base' | 'quote' stopRange?: [string | undefined, string | undefined] isNotMatchMarketPrice?: boolean marketPrice?: string marketRatePrice?: string isChecked?: boolean minimumConverted?: string } /** * @description coinA and coinB balance is different * when is deposit the balance is from wallet balance * when is withdraw the balance is from ammDeposit balance * */ export type AmmJoinData, I = any> = { coinA: C coinB: C coinLP: C slippage: number | string __cache__?: { [key: string]: any } } export type AmmExitData, I = any> = { coinA: C coinB: C coinLP: C slippage: number | string __cache__?: { [key: string]: any } } export type DeFiCalcData = { coinSell: T coinBuy: T AtoB: string BtoA: string fee: string } export type DeFiSideCalcData = { coinSell: T stakeViewInfo: R & { dalyEarn?: string maxSellAmount?: string minSellAmount?: string maxSellVol?: string minSellVol?: string } } type RedeemInfo = sdk.StakeInfoOrigin & Omit & { status_product: number maxSellAmount?: string minSellAmount?: string maxSellVol?: string minSellVol?: string minAmount: string maxAmount: string } export type DeFiSideRedeemCalcData = { coinSell: T stakeViewInfo: _R } export type DualTrade = IBData & { isRenew: boolean renewTargetPrice?: string renewDuration?: number } // { isRenew?: true; target; maxRecurseProductDuration: number } export type DualCalcData> = sdk.CalDualResult & { sellToken?: sdk.TokenInfo buyToken?: sdk.TokenInfo coinSell: B dualViewInfo: R balance: { [key: string]: sdk.DualBalance } request?: sdk.DualOrderRequest } export type AmmInData = { myCoinA: IBData myCoinB: IBData lpCoinA: IBData lpCoinB: IBData lpCoin: IBData AtoB: number BtoA: number coinInfoMap: CoinMap> // walletMap: WalletMap>, // AmmWalletMap: WalletMap>, slippage: number | string // slippageTolerance: Array, fee: string fees: any percentage: string } export type AmmDetailBase = { // name?: string, market: string coinA: CoinKey coinB: CoinKey coinAInfo: CoinInfo coinBInfo: CoinInfo address: string amountU?: string totalLPToken?: number totalA?: number totalB?: number totalAStr?: string totalBStr?: string totalAU?: number totalBU?: number rewardToken?: CoinKey rewardA?: number rewardB?: number rewardAU?: number rewardBU?: number rewardToken2?: CoinKey feeA?: number feeB?: number feeU?: number isNew?: boolean isActivity?: boolean APR?: number APRs?: { self: number event: number fee: number } } export type AmmDetail = AmmDetailBase & { exitDisable: boolean joinDisable: boolean swapDisable: boolean showDisable: boolean isRiskyMarket: boolean stob: string btos: string tradeFloat: Partial __rawConfig__: sdk.AmmPoolInfoV3 __ammPoolState__: sdk.AmmPoolStat } & sdk.AmmPoolInfoV3 export type AmmCardProps = AmmDetail & { activity: AmmActivity tradeFloat: TradeFloat handleClick: () => void account: Account forexMap: ForexMap popoverIdx: number precisionA?: number precisionB?: number coinAPriceU: number coinBPriceUr: number getLiquidityMining: (market: string, size?: number) => Promise // getMiningLinkList: (market: string) => { [key: string]: string }; setShowRewardDetail: React.Dispatch> setChosenCardInfo: React.Dispatch> ammInfo: any } export type AmmActivity = { market: string status: sdk.AmmPoolActivityStatus ruleType: string totalRewards: number myRewards: number rewardToken: CoinInfo duration: { from: Date to: Date } isPass?: boolean rewardTokenU?: number maxSpread?: number } export type Amount = { sell: { belong: T tradeValue: number } buy: { belong: T tradeValue: number } } export type TradeBasic = { side: keyof typeof TradeTypes amount: Amount time: number // timestamp fee: number // actionsStatus: object; } export type Trade = TradeBasic & { priceValue: number priceToken: T } export type AmmTrade = TradeBasic & { priceValue: number priceToken: T } export type AmmRecord = TradeBasic & { amountLP: Amount } export type OrderTrade = TradeBasic & { average: number filledAmount: Amount filledPrice: number status: keyof typeof TradeStatus } export type AmmDetailExtend = { ammCalcData: ACD } & AmmDetail export type AmmDetailExtendProps = AmmDetailExtend & { tradeFloat: TradeFloat activity?: AmmActivity } export type MyAmmLP = { smallBalance?: boolean balanceA: number | undefined balanceB: number | undefined balanceAStr: string | undefined balanceBStr: string | undefined balanceU: number | undefined feeA: number | undefined feeB: number | undefined feeU: number | undefined reward?: number | undefined rewardToken: CoinInfo | undefined reward2?: number | undefined rewardToken2?: CoinInfo | undefined rewardU?: number | undefined totalLpAmount?: number | undefined feeA24: number | undefined feeB24: number | undefined feeU24: number | undefined reward24: number | undefined reward224: number | undefined rewardU24: number | undefined extraU24: number | undefined extraRewards24: { tokenSymbol: string amount: number }[] } export type TradeFloat = { // value: number, change?: any timeUnit: '24h' | 'all' floatTag: keyof typeof FloatTag reward?: number rewardToken?: string volume?: number volumeView?: string close?: number high?: number low?: number priceU: number changeU?: number closeU?: number } export enum EXPLORE_TYPE { TRANSFER = 'transfer', DEPOSIT = 'deposit', WITHDRAW = 'withdraw', OFFCHAIN_WITHDRAWAL = 'withdraw', NFTMINT = 'nftMint', NFTWITHDRAW = 'nftWithdraw', NFTTRANSFER = 'nftTransfer', NFTSEND_BACK_LUCKY_TOKEN = 'nftTransfer', NFTSEND_LUCKY_TOKEN = 'nftTransfer', NFTWITHDRAW_LUCKY_TOKEN = 'nftWithdraw', } /** * CollectionMeta * @property name string useToCreate Collection * @property name string * @property tileUri string option * @property owner? string option * @property nftFactory? string option * @property baseUri? string option * @property collectionTitle? string option * @property description? string option * @property avatar? string option * @property banner? string option * @property thumbnail? string option * @property cid? string option * */ export type CollectionMeta = sdk.CollectionMeta & { extends: { [key: string]: any } } export type CollectionMetaJSON = { contract: string thumbnail_uri: string banner_uri: string avatar_uri: string tile_uri: string name: string description: string [key: string]: any } export enum CollectionMetaKey { name = 'name', tileUri = 'tileUri', owner = 'owner', nftFactory = 'nftFactory', baseUri = 'baseUri', collectionTitle = 'collectionTitle', description = 'description', avatar = 'avatar', banner = 'banner', thumbnail = 'thumbnail', cid = 'cid', } export type MakeMeta = (props: { collection: Co; domain: string }) => { metaDemo: any } export type GET_IPFS_STRING = (url: string | undefined, basicURl: string) => string export type RedPacketSend = { type: sdk.LuckyTokenType signerFlag: sdk.LuckyTokenSignerFlag luckyToken: sdk.LuckyTokenInfo numbers: number templateID: number memo: string validSince: number } // sdk.LuckyTokenItemForSend; export type LuckyRedPacketItem = { labelKey: string desKey: string tags?: string[], value: { value: number partition: sdk.LuckyTokenAmountType mode: sdk.LuckyTokenClaimType } } export type TickerNew = R & { timeUnit: '24h' erc20Symbol: string type: TokenType volume: string priceU: string change: string __rawTicker__: R & any rawData: R & any } export type TickerNewMap = { [key in keyof R]: TickerNew } export type Ticker = TradeFloat & { open: number high: number low: number close: number change: number volume: number | string base: string quote: string __rawTicker__: sdk.TickerData } export type NetworkItemInfo = { label: string chainId: string RPC?: string link?: string isTest?: boolean | undefined walletType: string } export const url_path = 'https://static.loopring.io/events' export const url_test_path = 'https://static.loopring.io/events/testEvents' export type VaultLoanData = { coinInfoMap: CoinMap> tradeData: T } & T export type VaultBorrowData< T = IBData & { erc20Symbol: string }, > = { borrowedAmt: string borrowedStr: string maxBorrowAmount: string maxBorrowStr: string minBorrowAmount: string minBorrowStr: string maxBorrowVol: string minBorrowVol: string maxQuote: string borrowVol: string borrowAmt: string totalQuote: string borrowAmtStr: string request: sdk.VaultBorrowRequest } & VaultLoanData export type VaultRepayData = { maxRepayAmount: string maxRepayStr: string minRepayAmount: string minRepayStr: string maxRepayVol: string minRepayVol: string maxQuote: string repayVol: string repayAmt: string repayAmtStr: string request: sdk.VaultRepayRequestV3WithPatch['request'] } & VaultLoanData export type VaultJoinData> = { walletMap: WalletMap coinMap: CoinMap & { vaultToken: string; vaultId: number } vaultLayer2Map: WalletMap vaultSymbol?: string request?: sdk.VaultJoinRequest maxShowVal: string minShowVal: string maxAmount: string minAmount: string amount: string isMerge: boolean vaultTokenInfo: sdk.TokenInfo tradeData: T // isShouldClean:boolean __request__: sdk.VaultJoinRequest } & Partial> & Partial export type VaultExitData> = { __request__: any tradeData: T } & Partial> & Partial ================================================ FILE: packages/common-resources/static-resources/src/loopring-interface/FooterInterface.ts ================================================ export type FooterInterface = { linkName: string linkHref: string } ================================================ FILE: packages/common-resources/static-resources/src/loopring-interface/HeaderInterface.ts ================================================ export enum HeaderMenuTabStatus { hidden = 'hidden', disabled = 'disabled', default = 'default', } export interface HeaderMenuItemInterface { label: { icon?: any id: string style?: any description?: string i18nKey: string } handleListKeyDown?: (props?: any) => void child?: Array | { [key: string]: Array } router?: { path: string; [key: string]: any; pathName?: string } status?: keyof typeof HeaderMenuTabStatus extender?: JSX.Element | undefined logo?: { dark: string light: string } } ================================================ FILE: packages/common-resources/static-resources/src/loopring-interface/Investment.ts ================================================ import { InvestMapType } from '../constant' export type InvestAdvice = { type: InvestMapType banner: string titleI18n: string desI18n: string router: string notification: string enable: boolean project?: string market?: string } ================================================ FILE: packages/common-resources/static-resources/src/loopring-interface/VendorInterface.ts ================================================ import { VendorProviders } from '../constant/vendor' import { TradeBtnStatus } from '../constant' export interface VendorItem { key: VendorProviders svgIcon: string flag?: { startDate: number endDate: number tag?: string highLight?: string } btnStatus?: TradeBtnStatus handleSelect?: (event?: React.MouseEvent) => void } ================================================ FILE: packages/common-resources/static-resources/src/loopring-interface/WallectInterface.ts ================================================ import { ConnectProviders } from '@loopring-web/web3-provider' export enum WalletStatus { disabled = 'disabled', loading = 'loading', unlock = 'unlock', connect = 'connect', noAccount = 'noAccount', accountPending = 'accountPending', noNetwork = 'noNetwork', default = 'default', } export interface GatewayItem { key: keyof typeof ConnectProviders keyi18n: string imgSrc: string handleSelect?: (event?: React.MouseEvent) => void } ================================================ FILE: packages/common-resources/static-resources/src/loopring-interface/index.ts ================================================ export * from './CoinInterface' export * from './HeaderInterface' export * from './WallectInterface' export * from './VendorInterface' export * from './FooterInterface' export * from './Investment' ================================================ FILE: packages/common-resources/static-resources/src/svg/Icon.tsx ================================================ import { SvgIcon, SvgIconProps } from '@mui/material' type DepthColor = { bo?: string l?: string a?: string b?: string } export const DepthHIcon = ({ bo = '#49527D', l = '#49527D', a = '#FF5677', b = '#00BBA8', ...props }: SvgIconProps & DepthColor) => { return ( ) } export const DepthFIcon = ({ bo = '#49527D', l = '#49527D', a = '#FF5677', b = '#00BBA8', ...props }: SvgIconProps & DepthColor) => { return ( ) } export const UpIcon = (props: SvgIconProps) => { return ( ) } export const DragIcon = (props: SvgIconProps) => { return ( ) } export const DragListIcon = (props: SvgIconProps) => { return ( ) } export const ResizeIcon = (props: SvgIconProps) => { return ( ) } export const AssetsIcon = (props: SvgIconProps) => { return ( ) } export const L2MyLiquidityIcon = (props: SvgIconProps) => { return ( ) } export const L2HistoryIcon = (props: SvgIconProps) => { return ( ) } export const RewardIcon = (props: SvgIconProps) => { return ( ) } // export const RedPacketIcon = (props: SvgIconProps) => { // return ( // // // // // ) // } export const SecurityIcon = (props: SvgIconProps) => { return ( ) } export const VipIcon = (props: SvgIconProps) => { return ( ) } export const L2OrderIcon = (props: SvgIconProps) => { return ( ) } export const CheckBoxIcon = (props: SvgIconProps) => { return ( ) } export const CheckedIcon = (props: SvgIconProps) => { return ( ) } export const ViewIcon = (props: SvgIconProps) => { return ( ) } export const HideIcon = (props: SvgIconProps) => { return ( ) } export const DropDownIcon = (props: SvgIconProps) => { return ( ) } export const BackIcon = (props: SvgIconProps) => { return ( ) } export const MoreIcon = (props: SvgIconProps) => { return ( ) } export const StarHollowIcon = (props: SvgIconProps) => { return ( ) } export const StarSolidIcon = (props: SvgIconProps) => { return ( ) } export const FavHollowIcon = (props: SvgIconProps) => { return ( ) } export const FavSolidIcon = (props: SvgIconProps) => { return ( ) } export const DownloadIcon = (props: SvgIconProps) => { return ( ) } export const NotificationIcon = (props: SvgIconProps) => { return ( ) } export const SettingIcon = (props: SvgIconProps) => { return ( ) } export const LinkIcon = (props: SvgIconProps) => { return ( ) } export const CopyIcon = (props: SvgIconProps) => { return ( ) } export const ReverseIcon = (props: SvgIconProps) => { return ( ) } export const HelpIcon = (props: SvgIconProps) => { return ( ) } export const CalendarIcon = (props: SvgIconProps) => { return ( ) } export const LinkedIcon = (props: SvgIconProps) => { return ( ) } export const ExchangeIcon = (props: SvgIconProps) => { return ( ) } export const CloseIcon = (props: SvgIconProps) => { return ( ) } export const SearchIcon = (props: SvgIconProps) => { return ( ) } export const MenuIcon = (props: SvgIconProps) => { return ( ) } export const QRIcon = (props: SvgIconProps) => { return ( ) } export const DoneIcon = (props: SvgIconProps) => { return ( ) } export const RefuseIcon = (props: SvgIconProps) => { return ( ) } export const SubmitIcon = (props: SvgIconProps) => { return ( ) } export const FailedIcon = (props: SvgIconProps) => { return ( ) } export const GoodIcon = (props: SvgIconProps) => { return ( ) } export const AlertIcon = (props: SvgIconProps) => { return ( ) } export const ErrorIcon = (props: SvgIconProps) => { return ( ) } export const InfoIcon = (props: SvgIconProps) => { return ( ) } export const UnConnectIcon = (props: SvgIconProps) => { return ( ) } export const LockIcon = (props: SvgIconProps) => { return ( ) } export const UnlockedIcon = (props: SvgIconProps) => { return ( ) } export const ProToLiteIcon = (props: SvgIconProps) => { return ( ) } export const CheckIcon = (props: SvgIconProps) => { return ( ) } export const LoadingIcon = (props: SvgIconProps) => { return ( ) } export const LoadingIcon2 = (props: SvgIconProps) => { return ( ) } export const ActiveIcon = (props: SvgIconProps) => { return ( ) } export const RefreshIcon = (props: SvgIconProps) => { return ( ) } export const CompleteIcon = (props: SvgIconProps) => { return ( ) } export const WaitingIcon = (props: SvgIconProps) => { return ( ) } export const WarningIcon = (props: SvgIconProps) => { return ( ) } export const EmptyIcon = (props: SvgIconProps) => { return ( ) } export const CircleIcon = (props: SvgIconProps) => { return ( ) } export const GrowIcon = (props: SvgIconProps) => { return ( ) } export const NoPhotosIcon = (props: SvgIconProps) => { return ( ) } // // export const SellBuyBg = (props: SvgIconProps) => { // return // // // } export const KLineFeaturesIcon = (props: SvgIconProps) => { return ( ) } export const LoopringDarkFooterIcon = (props: SvgIconProps) => { return ( ) } export const LoopringLightFooterIcon = (props: SvgIconProps) => { return ( ) } export const YoutubeIcon = (props: SvgIconProps) => { return ( ) } export const TwitterIcon = (props: SvgIconProps) => { return ( ) } export const MediumIcon = (props: SvgIconProps) => { return ( ) } export const DiscordIcon = (props: SvgIconProps) => { return ( ) } export const LoopringIcon = (props: SvgIconProps) => { return ( ) } export const LightIcon = (props: SvgIconProps) => { return ( ) } export const DarkIcon = (props: SvgIconProps) => { return ( ) } export const ExitIcon = (props: SvgIconProps) => { return ( ) } export const RampIcon = ({ fontColor = '#fff', ...props }: SvgIconProps & { fontColor?: string }) => ( ) export const BanxaIcon = ({ fontColor = '#fff', ...props }: SvgIconProps & { fontColor?: string }) => ( ) export const FirstPlaceIcon = (props: SvgIconProps) => { return ( ) } export const SecondPlaceIcon = (props: SvgIconProps) => { return ( ) } export const ThirdPlaceIcon = (props: SvgIconProps) => { return ( ) } export const TrophyIcon = (props: SvgIconProps) => { return ( ) } export const AmmRankIcon = (props: SvgIconProps) => { return ( ) } export const SpeakerIcon = (props: SvgIconProps) => { return ( ) } export const GoTopIcon = (props: SvgIconProps) => { return ( ) } export const NFTIcon = (props: SvgIconProps) => { return ( ) } export const RecordIcon = (props: SvgIconProps) => { return ( ) } export const WaitApproveIcon = (props: SvgIconProps) => { return ( ) } export const LoopringLogoIcon = (props: SvgIconProps) => { return ( ) } export const TransferIcon = (props: SvgIconProps) => { return ( ) } export const DepositIcon = (props: SvgIconProps) => { return ( ) } export const WithdrawIcon = (props: SvgIconProps) => { return ( ) } export const MintIcon = (props: SvgIconProps) => { return ( ) } export const AddIcon = (props: SvgIconProps) => { return ( ) } export const DeleteIcon = (props: SvgIconProps) => { return ( ) } export const ImageIcon = (props: SvgIconProps) => { return ( ) } export const Info2Icon = (props: SvgIconProps) => { return ( ) } export const IncomingIcon = (props: SvgIconProps) => { return ( ) } export const OutputIcon = (props: SvgIconProps) => { return ( ) } export const CardIcon = (props: SvgIconProps) => { return ( ) } export const L2l2Icon = (props: SvgIconProps) => { return ( ) } export const L1l2Icon = (props: SvgIconProps) => { return ( ) } export const ExchangeAIcon = (props: SvgIconProps) => { return ( ) } export const AudioIcon = (props: SvgIconProps) => { return ( ) } export const VideoIcon = (props: SvgIconProps) => { return ( ) } export const ThreeDIcon = (props: SvgIconProps) => { return ( ) } export const PlayIcon = (props: SvgIconProps) => { return ( ) } export const ProfileIcon = (props: SvgIconProps) => { return ( ) } export const OrderListIcon = (props: SvgIconProps) => { return ( ) } export const RefreshIPFSIcon = (props: SvgIconProps) => { return ( ) } export const ViewMoreIcon = (props: SvgIconProps) => { return ( ) } export const ZoomIcon = (props: SvgIconProps) => { return ( ) } export const SyncIcon = (props: SvgIconProps) => { return ( ) } export const LegacyIcon = (props: SvgIconProps) => { return ( ) } export const SwapSettingIcon = (props: SvgIconProps) => { return ( ) } export const WarningIcon2 = (props: SvgIconProps) => { return ( ) } export const RoundAddIcon = (props: SvgIconProps) => { return ( ) } export const ApprovalIcon = (props: SvgIconProps) => { return ( ) } export const LockGuardianIcon = (props: SvgIconProps) => { return ( ) } export const ViewHistoryIcon = (props: SvgIconProps) => { return ( ) } export const ScanQRIcon = (props: SvgIconProps) => { return ( ) } export const ClockIcon = (props: SvgIconProps) => { return ( ) } export const CloseRedPacketIcon = (props: SvgIconProps) => { return ( ) } export const UploadedIcon = (props: SvgIconProps) => { return ( ) } export const ContactIcon = (props: SvgIconProps) => { return ( ) } export const EditIcon = (props: SvgIconProps) => { return ( ) } export const ConvertToIcon = (props: SvgIconProps) => { return ( ) } export const LinkSharedIcon = (props: SvgIconProps) => { return ( ) } export const AnotherIcon = (props: SvgIconProps) => { return ( ) } export const RiskIcon = (props: SvgIconProps) => { return ( ) } export const RiskAlertIcon = (props: SvgIconProps) => { return ( ) } export const RoundCheckIcon = (props: SvgIconProps) => { return ( ) } export const RoundCircleIcon = (props: SvgIconProps) => { return ( ) } export const DiscordSvg = (props: SvgIconProps) => { return ( ) } export const BorderTickIcon = (props: SvgIconProps) => { return ( ) } export const MarginLevelIcon = (props: SvgIconProps) => { return ( ) } export const LoadIcon = (props: SvgIconProps) => { return ( ) } export const MarginIcon = (props: SvgIconProps) => { return ( ) } export const VaultTradeIcon = (props: SvgIconProps) => { return ( ) } export const CloseOutIcon = (props: SvgIconProps) => { return ( ) } export const SwapExchangeIcon = (props: SvgIconProps) => { return ( ) } export const BiArrow = (props: SvgIconProps) => { return ( ) } export const MessageIcon = (props: SvgIconProps) => { return ( ) } export const ReadIcon = (props: SvgIconProps) => { return ( ) } export const FiatLandIcon = (props: SvgIconProps) => { return ( ) } export const SwapLandIcon = (props: SvgIconProps) => { return ( ) } export const SpotIcon = (props: SvgIconProps) => { return ( ) } export const BlockTradeLandIcon = (props: SvgIconProps) => { return ( ) } export const GoIcon = (props: SvgIconProps) => { return ( ) } export const BlindBoxIcon = (props: SvgIconProps) => { return ( ) } export const NormalRedpacketIcon = (props: SvgIconProps) => { return ( ) } export const BurnIcon = (props: SvgIconProps) => { return ( ) } export const RedPacketIcon = (props: SvgIconProps) => { return ( ) } export const BrushIcon = (props: SvgIconProps) => { return ( ) } export const ConnectivityIcon = (props: SvgIconProps) => { return ( ) } export const AlertIcon2 = (props: SvgIconProps) => { return ( ) } ================================================ FILE: packages/common-resources/static-resources/src/svg/dropdownLogo.tsx ================================================ import { SvgIcon, SvgIconProps } from '@mui/material' export const OverviewIcon = (props: SvgIconProps) => { return ( ) } export const DualInvestIcon = (props: SvgIconProps) => { return ( ) } export const ETHStakingIcon = (props: SvgIconProps) => { return ( ) } export const LeverageETHIcon = (props: SvgIconProps) => { return ( ) } export const AmmIcon = (props: SvgIconProps) => { return ( ) } export const LRCStakingIcon = (props: SvgIconProps) => { return ( ) } export const SwapIcon = (props: SvgIconProps) => { return ( ) } export const OrderBookIcon = (props: SvgIconProps) => { return ( ) } export const StopLimitIcon = (props: SvgIconProps) => { return ( ) } export const BlockTradeIcon = (props: SvgIconProps) => { return ( ) } export const FiatIcon = (props: SvgIconProps) => { return ( ) } export const MyNFTIcon = (props: SvgIconProps) => { return ( ) } export const CreateNFTIcon = (props: SvgIconProps) => { return ( ) } export const MyCollectionIcon = (props: SvgIconProps) => { return ( ) } export const VaultHomeIcon = (props: SvgIconProps) => { return ( ) } export const VaultDashboardIcon = (props: SvgIconProps) => { return ( ) } export const VaultTradeIcon2 = (props: SvgIconProps) => { return ( ) } ================================================ FILE: packages/common-resources/static-resources/src/svg/earnLogo.tsx ================================================ import { styled } from '@mui/material' import { SvgIcon, SvgIconProps } from '@mui/material' const LargeSvgIcon = styled(SvgIcon)` && { width: 240px; height: 240px; } ` export const Overview = (props: SvgIconProps) => { return ( ) } export const AmmLogo = (props: SvgIconProps) => { return ( ) } export const SatkingLogo = (props: SvgIconProps) => { return ( ) } export const DualInvestmentLogo = (props: SvgIconProps) => { return ( ) } export const DualUpIcon = (props) => { return ( ) } export const DualDownIcon = (props) => { return ( ) } export const DualConvertIcon = (props) => { return ( ) } export const DualBTCIcon = (props) => { return ( ) } export const DualChartDD = (props) => { return ( ) } export const DualChartDH = (props) => { return ( ) } export const DualChartHD = (props) => { return ( ) } export const DualChartHH = (props) => { return ( ) } export const VaultIcon = (props) => { return ( ) } export const ToRightTopArrow = (props: SvgIconProps) => { return ( ) } ================================================ FILE: packages/common-resources/static-resources/src/svg/index.ts ================================================ export * from './Icon' export * from './dropdownLogo' export * from './earnLogo' export * from './redPacketSvg' export * from './shareReferral' export * from './network' export * from './redPacketScope' ================================================ FILE: packages/common-resources/static-resources/src/svg/network.tsx ================================================ import { SvgIcon, SvgIconProps } from '@mui/material' export const ChainGOERLIIcon = (props: SvgIconProps) => { return ( ) } export const ChainTAIKOIcon = (props: SvgIconProps) => { return ( ) } export const ChainETHEREUMIcon = (props: SvgIconProps) => { return ( ) } export const BaseIcon = (props: SvgIconProps) => { return ( ) } ================================================ FILE: packages/common-resources/static-resources/src/svg/redPacketScope.tsx ================================================ import { SVGProps } from "react" export const ScopePublic = (props: SVGProps) => { const { color = "#A4ABC1", ...rest } = props; return ( ) } export const ScopeQR = (props: SVGProps) => { const { color = "#A4ABC1", ...rest } = props; return ( ) } export const ScopeTarget = (props: SVGProps) => { const { color = "#A4ABC1", ...rest } = props; return ( ) } ================================================ FILE: packages/common-resources/static-resources/src/svg/redPacketSvg.tsx ================================================ import { SoursURL } from '../constant' import { sanitize } from 'dompurify' import React from 'react' export const RedPacketColorConfig: { default: ColorConfig official: ColorConfig } = { default: { colorTop: '#FD7659', startColor: '#FC7A5A', endColor: '#FF6151', bgColor: '#ffffff', fontColor: '#FFF7B1', btnColor: '#FD7659', qrColor: '#FD7659', }, official: { colorTop: '#FFD595', startColor: '#FFD596', endColor: '#FDBD6A', bgColor: '#ffffff', fontColor: '#A25402', btnColor: '#FD7659', qrColor: '#A25402', }, } export const RedPacketCssColorConfig: { default: ColorCssConfig official: ColorCssConfig blindbox: ColorCssConfig } = { default: { colorTop: '#FD7659', startColor: '#FC7A5A', endColor: '#FF6151', startBgColor: '#FC7A5A', endBgColor: '#930D00', startCard: '#FEF4DE', endCard: '#FED897', line: '#D4B164', highLightColor: '#A25402', highLightDisableColor: '#A25402', primaryColor: '#FFF7B1', secondaryColor: '#D09145', disableColor: '#7C3400', }, official: { colorTop: '#FFD595', startColor: '#FFD596', endColor: '#FDBD6A', startBgColor: '#FFD595', endBgColor: '#934F00', startCard: '#FEF4DE', endCard: '#FED897', line: '#D4B164', highLightColor: '#A25402', highLightDisableColor: '#A25402', primaryColor: '#A25402', secondaryColor: '#D09145', disableColor: '#7C3400', }, blindbox: { // background: linear-gradient(95.9deg, #A35388 0.7%, #FF6151 99.3%); colorTop: "url('#gradient1')", startColor: '#A35388', endColor: '#FF6151', startBgColor: '#FC7A5A', endBgColor: '#930D00', startCard: '#FEF4DE', endCard: '#FED897', line: '#D4B164', highLightColor: '#A25402', highLightDisableColor: '#A25402', primaryColor: '#FFF7B1', secondaryColor: '#D09145', disableColor: '#7C3400', }, } export const RedPacketWrapSVG = ({ colorTop, startColor, endColor, type, }: { type: 'default' | 'official' | 'blindbox' colorTop: '#FD7659' | '#FFD595' startColor: '#FC7A5A' | '#FFD596' endColor: '#FF6151' | '#FDBD6A' }) => { return ( ) } export const RedPacketOpenWrapSVG = ({ // colorTop, startColor, endColor, startBgColor, endBgColor, startCard, endCard, line, type, }: { type: 'default' | 'official' | 'blindbox' colorTop: '#FD7659' | '#FFD595' startColor: '#FC7A5A' | '#FFD596' endColor: '#FF6151' | '#FDBD6A' startBgColor: '#FC7A5A' | '#FFD595' endBgColor: '#930D00' | '#934F00' startCard: '#FEF4DE' endCard: '#FED897' line: '#D4B164' } & Partial>) => { return ( ) } export type RedPacketQRPropsExtends = { textAddress: string textContent: string amountStr: string textSendBy: string //text send by textType: string textShared: string textNo: string textDes: string imageEleUrl?: string onClickShareButton?: (e: React.MouseEvent) => void } export type ColorConfig = { colorTop: string startColor: string endColor: string bgColor: string fontColor: string btnColor: string qrColor: string } export type ColorCssConfig = { colorTop: string startColor: string endColor: '#FF6151' | '#FDBD6A' startBgColor: '#FC7A5A' | '#FFD595' endBgColor: '#930D00' | '#934F00' startCard: '#FEF4DE' endCard: '#FED897' line: '#D4B164' highLightColor: '#A25402' highLightDisableColor: '#A25402' primaryColor: '#FFF7B1' | '#A25402' secondaryColor: '#D09145' disableColor: '#7C3400' } export const RedPacketQRCodeSvg = React.memo( React.forwardRef( ( { startColor, endColor, colorTop, bgColor, fontColor, btnColor, type, qrcodeRef, textAddress, textContent, amountStr, qrCodeG, textSendBy, textType, textShared, textNo, textDes, imageEleUrl, onClickShareButton, }: ColorConfig & { type: 'default' | 'official' qrcodeRef: React.Ref // qrCodeG; } & RedPacketQRPropsExtends & { qrcodeRef: React.Ref qrCodeG: string }, ref: React.ForwardedRef, ) => { const [[textContent1, textContent2], setTextContent] = React.useState([ textContent, // "textContentdaskdjhkas jhdkjashdkjhaskjdhsakjhdkashd", '', ]) // const imageRef = React.useRef(); const [imageBase64, setImageBase64] = React.useState(imageEleUrl ?? '') React.useEffect(() => { if (imageEleUrl) { fetch(imageEleUrl) .then((result) => result.blob()) .then((result) => { const reader = new FileReader() reader.onloadend = () => { // @ts-ignore setImageBase64((state) => reader?.result ?? state) } reader.onerror = () => { console.log('reader error') } reader.readAsDataURL(result) // myLog("blob", result.stream()); // if (result) { // setImageBase64(result.toString()); // } }) } }, [imageEleUrl]) React.useEffect(() => { const [str1, str2] = textContent?.split('\n') if (textContent && str2) { setTextContent([str1, str2]) } else if (textContent && textContent.length > 12) { const value = textContent.substring(0, 12) let _textContent2 = textContent.substring(12, textContent.length) const textArray = value.split(' ') _textContent2 = (textArray.length > 2 ? textArray.pop() : '') + _textContent2 const _textContent1 = textArray.join(' ') setTextContent([_textContent1, _textContent2]) } }, [textContent]) const station = imageEleUrl ? [36, 68, 86, 188] : [56, 88, 106, 186] return ( <> {!!textSendBy && ( )} {/**/} {/*{qrCodeG}*/} {imageEleUrl && ( )} {/**/} {textAddress} 12 ? textContent2.slice(0, 12) + '...' : textContent2, ), }} /> {amountStr} {textType} {textDes .split(' ') .slice(0, Math.ceil(textDes.split(' ').length / 2)) .join(' ')} {textDes .split(' ') .slice(Math.ceil(textDes.split(' ').length / 2)) .join(' ')} {textShared} {textNo} ) }, ), ) ================================================ FILE: packages/common-resources/static-resources/src/svg/shareReferral.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import React from 'react' export type ShareReferralSvgProps = { src: string code: string label?: string height?: number width?: number bottom?: number left?: number fontColor?: string name?: string } export const ShareReferralSvg = withTranslation('common', { withRef: true })( React.memo( React.forwardRef( ( { t, code, label = t('labelReferralImageDes'), height = 880, width = 630, src, bottom = 30, left = 48, name, fontColor = '#000000', }: ShareReferralSvgProps & WithTranslation, ref: React.ForwardedRef, ) => { const lebelY = height - bottom - 100 + 10 const lebelX = left const lebelCodeY = lebelY + 60 const lebelCodeX = left const labelCode = t('labelReferralImageCode', { code }) return ( <> ) }, ), ), ) // export const ShareReferralSvg = _ShareReferralSvg) // ) as ( // props: ShareReferralSvgProps & WithTranslation & RefAttributes // ) => JSX.Element; ================================================ FILE: packages/common-resources/static-resources/src/themes/css/color-lib.ts ================================================ export const hexToRGB = (hex: string, alpha?: string | number) => { var r = parseInt(hex.slice(1, 3), 16), g = parseInt(hex.slice(3, 5), 16), b = parseInt(hex.slice(5, 7), 16) if (alpha !== undefined) { return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha.toString() + ')' } else { return 'rgb(' + r + ', ' + g + ', ' + b + ')' } } export const GrayBlack = { gray100: '#EBEEF5', gray200: '#A2A9B8', gray300: '#6C717A', gray400: '#5E6470', gray500: '#4A505C', gray600: '#393E47', gray700: '#31353D', gray800: '#25282E', gray900: '#25282E', gray1000: '#31353D', gray1100: '#292C33', } export const SystemColor = { blue: '#446EFF', green: '#00BBA8', orange: '#FBA95C', red: '#FF5677', white: '#FFFFFF', black: '#000000', optional: '#F0B90B' } export const GrayLight = { gray100: '#1A2947', gray200: '#5D667A', gray300: '#AFB7C7', gray400: '#BCC1CC', gray500: '#E1E6F0', gray600: '#EBEFF7', gray700: '#F5F7FC', gray800: '#FFFFFF', gray900: '#F5F7FC', gray1000: '#FFFFFF', gray1100: '#FFFFFF', } export const ColorDarkDefault = Object.freeze({ primary: SystemColor.blue, primaryHover: `${hexToRGB(GrayBlack.gray200, '0.2')}`, primaryPressed: '#2D49B2', secondary: SystemColor.blue, secondaryHover: '#46A6FF', secondaryPressed: '#1064B2', disable: '#343754', success: SystemColor.green, warning: SystemColor.orange, error: SystemColor.red, textPrimary: GrayBlack.gray100, textSecondary: GrayBlack.gray200, textThird: GrayBlack.gray300, textButton: SystemColor.white, textButtonSelect: GrayBlack.gray100, textButtonDisabled: GrayBlack.gray400, textDisable: `${hexToRGB(GrayBlack.gray100, '0.45')}`, border: GrayBlack.gray500, borderHover: SystemColor.blue, borderDark: GrayBlack.gray400, placeholder: GrayBlack.gray400, borderSelect: SystemColor.blue, borderDisable: GrayBlack.gray300, borderDisable2: GrayBlack.gray200, tag: '#6787FF', popBg: GrayBlack.gray1100, //ColorBlack.dark800, box: GrayBlack.gray800, //"#2D2F4B", boxSecondary: GrayBlack.gray700, boxThird: GrayBlack.gray1000, boxHover: `${hexToRGB(GrayBlack.gray100, '0.05')}`, boxLinear: GrayBlack.gray1100, globalBg: GrayBlack.gray900, // "#1F2034", globalBgOpacity: `${hexToRGB('#1F2034', '0.5')}`, fieldOpacity: GrayBlack.gray700, divide: GrayBlack.gray500, boxEnhance: `${hexToRGB(GrayBlack.gray100, '0.1')}`, mask: `${hexToRGB('#000000', '0.4')}`, tableHeaderBg: '#393f64', star: SystemColor.optional, logo: GrayBlack.gray100, /********************CSS special button *******************/ buttonPot: GrayBlack.gray100, buttonIcon: GrayBlack.gray200, buttonInactive: GrayBlack.gray700, buttonDisabled: GrayBlack.gray600, buttonOutlined: GrayBlack.gray500, /********************CSS shadow *******************/ shadow: ` 0px 4px 4px ${hexToRGB(GrayBlack.gray600, '.25')}`, shadowHeader: `0px 4px 8px ${hexToRGB(GrayBlack.gray600, '.15')}`, shadow2: `0px -4px 8px ${hexToRGB(GrayBlack.gray600, '.15')}`, shadowHover: `0px 10px 20px ${hexToRGB(GrayBlack.gray600, '.45')}`, shadow3: `0px 10px 20px ${hexToRGB(GrayBlack.gray600, '.15')}`, /********************Case for provider*******************/ white: SystemColor.white, black: SystemColor.black, dark: GrayBlack.gray700, opacity: `${hexToRGB(GrayBlack.gray700, '0')}`, providerBtn: `${hexToRGB(GrayBlack.gray100, '0.1')}`, providerBtnHover: `${hexToRGB(GrayBlack.gray100, '0.03')}`, providerApprove: `${hexToRGB(GrayBlack.gray100, '0.03')}`, boxNFTLabel: `${hexToRGB(GrayBlack.gray700, '0.48')}`, boxNFTBtn: `${hexToRGB(GrayBlack.gray700, '0.28')}`, redPacket1: `linear-gradient(96.56deg, #FFD596 1.14%, #FFD390 46.4%, #FDBD6A 98.91%)`, redPacket0: `linear-gradient(95.9deg, #FC7A5A 0.7%, #FF6151 99.3%);`, redPacket1Disabled: `${hexToRGB(GrayBlack.gray400, '0.5')}`, redPacketText1: '#A25402', redPacketText0: '#FFF7B1', redPacketBorder: `1px dashed ${hexToRGB(GrayBlack.gray100, '0.2')}`, }) export const ColorLightDefault = Object.freeze({ ...ColorDarkDefault, primary: SystemColor.blue, primaryHover: `${hexToRGB(GrayLight.gray100, '0.2')}`, primaryPressed: '#293EAA', secondary: SystemColor.blue, secondaryHover: '#46A6FF', secondaryPressed: '#1064B2', disable: '#F4F5F9', success: SystemColor.green, warning: SystemColor.orange, error: SystemColor.red, textPrimary: GrayLight.gray100, textSecondary: GrayLight.gray200, textThird: GrayLight.gray300, textButton: SystemColor.white, textButtonSelect: SystemColor.blue, textButtonDisabled: GrayLight.gray400, textDisable: `${hexToRGB(GrayLight.gray100, '0.25')}`, border: GrayLight.gray500, borderHover: SystemColor.blue, borderDark: GrayLight.gray400, placeholder: GrayLight.gray400, borderSelect: SystemColor.blue, borderDisable: GrayLight.gray300, borderDisable2: GrayLight.gray200, tag: '#6787FF', box: GrayLight.gray800, boxHover: GrayLight.gray700, popBg: GrayLight.gray1100, boxLinear: GrayLight.gray1100, globalBg: GrayLight.gray900, globalBgOpacity: `${hexToRGB(GrayLight.gray500, '0.5')}`, fieldOpacity: GrayLight.gray700, divide: GrayLight.gray500, boxEnhance: GrayLight.gray400, boxSecondary: GrayLight.gray700, boxThird: GrayLight.gray1000, mask: `${hexToRGB('#000000', '0.4')}`, tableHeaderBg: GrayLight.gray600, star: SystemColor.optional, logo: SystemColor.blue, /********************CSS special buttonr*******************/ buttonPot: GrayBlack.gray100, buttonIcon: '#15162B', buttonInactive: GrayLight.gray700, buttonDisabled: GrayLight.gray600, buttonOutlined: GrayLight.gray500, // box-shadow: 0px 4px 20px 0px #1D20231A; /********************CSS shadow *******************/ shadow: `0px 4px 20px 0px #1D20231A`, shadowHeader: `0px 4px 8px ${hexToRGB('#5766EC', '0.1')}`, shadow2: `0px -4px 8px ${hexToRGB('#5766EC', '0.1')}`, shadowHover: `0px 0px 4px ${hexToRGB('#5781EC', '0.25')}`, shadow3: `0px 10px 20px ${hexToRGB('#5781EC', '0.1')}`, /********************Case for provider*******************/ white: SystemColor.white, black: SystemColor.black, dark: GrayBlack.gray700, opacity: `${hexToRGB(GrayBlack.gray100, '0')}`, providerBtn: `${hexToRGB(SystemColor.blue, '0.1')}`, providerBtnHover: `${hexToRGB(SystemColor.blue, '0.15')}`, providerApprove: `#F6F7FB`, boxNFTLabel: `${hexToRGB(GrayBlack.gray100, '0.58')}`, boxNFTBtn: `${hexToRGB(GrayBlack.gray700, '0.28')}`, redPacket1: `linear-gradient(96.56deg, #FFD596 1.14%, #FFD390 46.4%, #FDBD6A 98.91%)`, redPacket0: `linear-gradient(95.9deg, #FC7A5A 0.7%, #FF6151 99.3%);`, redPacket1Disabled: `${hexToRGB(GrayLight.gray500, '0.5')}`, redPacketText1: '#A25402', redPacketText0: '#FFF7B1', redPacketBorder: `1px dashed ${hexToRGB(GrayBlack.gray100, '0.2')}`, }) export type ColorBaseInterface = typeof ColorDarkDefault ================================================ FILE: packages/common-resources/static-resources/src/themes/css/global.tsx ================================================ import { css } from '@emotion/react' import reset from './reset' // @ts-ignore import InterMedium from '../fonts/english/Inter-Medium.ttf' import { ColorDarkDefault, ColorLightDefault, GrayBlack, GrayLight, hexToRGB, SystemColor, } from './color-lib' import { ThemeType } from '../interface' export const fontDefault = { h1: '3.8rem', h2: '3.0rem', h3: '2.4rem', h4: '2.0rem', h5: '1.6rem', h6: '1.4rem', body1: '1.4rem', body2: '1.2rem', } export enum SvgSize { svgSizeSmall = 12, svgSize = 14, svgSizeMedium = 16, svgSizeCover = 32, svgSizeLarge = 24, svgSizeHuge2 = 40, svgSizeHuge = 48, } export const refreshTime = 15 export const colorBase = ({ theme }: any) => css` html { --gray100: ${theme.mode == ThemeType.dark ? GrayBlack.gray100 : GrayLight.gray100}; --gray200: ${theme.mode == ThemeType.dark ? GrayBlack.gray200 : GrayLight.gray200}; --gray300: ${theme.mode == ThemeType.dark ? GrayBlack.gray300 : GrayLight.gray300}; --gray400: ${theme.mode == ThemeType.dark ? GrayBlack.gray400 : GrayLight.gray400}; --gray500: ${theme.mode == ThemeType.dark ? GrayBlack.gray500 : GrayLight.gray500}; --gray600: ${theme.mode == ThemeType.dark ? GrayBlack.gray600 : GrayLight.gray600}; --gray700: ${theme.mode == ThemeType.dark ? GrayBlack.gray700 : GrayLight.gray700}; --gray800: ${theme.mode == ThemeType.dark ? GrayBlack.gray800 : GrayLight.gray800}; --gray900: ${theme.mode == ThemeType.dark ? GrayBlack.gray900 : GrayLight.gray900}; --color-primary: ${theme.colorBase.primary}; --color-primary-hover: ${theme.colorBase.primaryHover}; --color-primary-pressed: ${theme.colorBase.primaryPressed}; --color-secondary: ${theme.colorBase.secondary}; --color-secondary-hover: ${theme.colorBase.secondaryHover}; --color-secondary-pressed: ${theme.colorBase.secondaryPressed}; --color-disable: ${theme.colorBase.disable}; --color-success: ${theme.colorBase.success}; --color-warning: ${theme.colorBase.warning}; --color-error: ${theme.colorBase.error}; --color-text-primary: ${theme.colorBase.textPrimary}; --color-text-secondary: ${theme.colorBase.textSecondary}; --color-text-third: ${theme.colorBase.textThird}; --color-text-button: ${theme.colorBase.textButton}; --color-text-button-select: ${theme.colorBase.textButtonSelect}; --color-text-button-disabled: ${theme.colorBase.textButtonDisabled}; --color-text-disable: ${theme.colorBase.textDisable}; --color-border: ${theme.colorBase.border}; --color-border-hover: ${theme.colorBase.borderHover}; --color-border-dark: ${theme.colorBase.borderDark}; --color-placeholder: ${theme.colorBase.placeholder}; --color-border-select: ${theme.colorBase.borderSelect}; --color-border-disable: ${theme.colorBase.borderDisable}; --color-border-disable2: ${theme.colorBase.borderDisable2}; --color-tag: ${theme.colorBase.tag}; --color-box: ${theme.colorBase.box}; --color-box-nft-label: ${theme.colorBase.boxNFTLabel}; --color-box-nft-btn: ${theme.colorBase.boxNFTBtn}; --color-box: ${theme.colorBase.box}; --color-box-hover: ${theme.colorBase.boxHover}; --color-pop-bg: ${theme.colorBase.popBg}; --color-box-linear: ${theme.colorBase.boxLinear}; --color-global-bg: ${theme.colorBase.globalBg}; --color-global-bg-opacity: ${theme.colorBase.globalBgOpacity}; --field-opacity: ${theme.colorBase.fieldOpacity}; --color-divide: ${theme.colorBase.divide}; --color-box-secondary: ${theme.colorBase.boxSecondary}; --color-box-third: ${theme.colorBase.boxThird}; --color-mask: ${theme.colorBase.mask}; --color-box-enhance: ${theme.colorBase.boxEnhance}; --color-table-header-bg: ${theme.colorBase.tableHeaderBg}; --color-star: ${theme.colorBase.star}; --color-logo: ${theme.colorBase.logo}; /********************Case for shadow*******************/ --color-button-pot: ${theme.colorBase.buttonPot}; --color-button-icon: ${theme.colorBase.buttonIcon}; --color-button-inactive: ${theme.colorBase.buttonInactive}; --color-button-disabled: ${theme.colorBase.buttonDisabled}; --color-button-outlined: ${theme.colorBase.buttonOutlined}; /********************CSS shadow *******************/ --shadow: ${theme.colorBase.shadow}; --shadow-header: ${theme.colorBase.shadowHeader}; --shadow2: ${theme.colorBase.shadow2}; --shadow-hover: ${theme.colorBase.shadowHover}; --shadow3: ${theme.colorBase.shadow3}; /********************Case for special*******************/ --provider-btn: ${theme.colorBase.providerBtn}; --provider-hover: ${theme.colorBase.providerBtnHover}; --provider-agree: ${theme.colorBase.providerApprove}; --vip-bg: ${hexToRGB(theme.colorBase.warning, '0.2')}; --vip-text: ${theme.colorBase.warning}; --network-bg: ${hexToRGB(theme.colorBase.warning, '0.2')}; --network-text: ${theme.colorBase.warning}; --auto-refresh-color: ${theme.colorBase.primary}; --opacity: ${theme.colorBase.opacity}; --color-white: white; --color-black: ${theme.colorBase.black}; --color-settlet: ${theme.colorBase.opacity}; --color-redPacket0: ${theme.colorBase.redPacket0}; --color-redPacket1: ${theme.colorBase.redPacket1}; --color-redPacket1Disabled: ${theme.colorBase.redPacket1Disabled}; --color-redPacket-text0: ${theme.colorBase.redPacketText0}; --color-redPacket-text1: ${theme.colorBase.redPacketText1}; --color-redPacket-Border: ${theme.colorBase.redPacketBorder}; } ` export const scrollbarDefault = ({ theme }: any) => css` html { scrollbar-face-color: ${theme.colorBase.box}; scrollbar-base-color: ${theme.colorBase.box}; scrollbar-3dlight-color: ${theme.colorBase.box}; scrollbar-highlight-color: ${theme.colorBase.box}; scrollbar-track-color: ${theme.colorBase.box}; scrollbar-arrow-color: ${theme.colorBase.box}; scrollbar-shadow-color: ${theme.colorBase.box}; scrollbar-dark-shadow-color: ${theme.colorBase.box}; } .MuiPaper-elevation2 { box-shadow: var(--shadow); } .MuiPaper-elevation4 { box-shadow: var(--shadow-header); } ::-webkit-scrollbar-track { background-color: ${theme.colorBase.box}; border-radius: 3px; } ::-webkit-scrollbar-track-piece { background-color: ${theme.colorBase.box}; border-radius: 3px; } ::-webkit-scrollbar-thumb { height: 50px; background-color: ${theme.colorBase.blur}; border-radius: 3px; } ::-webkit-scrollbar-corner { background-color: ${theme.colorBase.box}; } ::-webkit-resizer { background-color: ${theme.colorBase.box}; } ` export const globalCss = ({ theme }: any) => css` ${colorBase({ theme })} ${scrollbarDefault({ theme })}; ${reset} #root { display: flex; flex-direction: column; justify-content: space-between; } html, body { position: relative; color: var(--color-text-primary); background: var(--color-global-bg); height: 100vh; box-sizing: border-box; -moz-box-sizing: border-box; /* Firefox */ -webkit-box-sizing: border-box; /* Safari */ font-family: Roboto, Helvetica, Arial, '华文细黑', 'Microsoft YaHei', '微软雅黑', SimSun, '宋体', Heiti, '黑体', sans-serif; font-size: 62.5%; /* 62.5% of 16px = 10px */ } body { display: flex; width: 100%; position: relative; z-index: 1; flex-direction: column; &:before { content: ''; position: fixed; z-index: -1; top: 0; left: 0; right: 0; bottom: 0; } } h1 { font-size: ${fontDefault.h1}; } h2 { font-size: ${fontDefault.h2}; } h3 { font-size: ${fontDefault.h3}; } h4 { font-size: ${fontDefault.h4}; } h5 { font-size: ${fontDefault.h5}; } h6 { font-size: ${fontDefault.h6}; } html { overflow-y: auto; --auto-refresh-duration: ${refreshTime - 1}s; --durationInternal: calc(var(--auto-refresh-duration) * 2); --delay: calc(var(--auto-refresh-duration) / 2); --header-row-height: 44px; --header-height: 64px; --header-submenu-item-height: 52px; --header-submenu-item-width: 250px; --desktop-max-width: 1200px; --desktop-min-width: 1024px; --btn-Input-small-height: 32px; --btn-medium-height: 40px; --btn-min-width: 100px; --coin-min-width: 80px; --datePicker-width: 320px; --datePicker-height: 232px; --list-coin-item: 44px; --withdraw-coin-size: 16px; --header-menu-icon-size: 20px; --header-menu-icon-large: 28px; --list-menu-coin-size: 24px; --slippage-pop-width: 308px; --chart-title-coin-size: 28px; --btn-icon-size-small: 20px; --btn-icon-size-medium: 24px; --btn-icon-size-large: 28px; --btn-icon-size: 36px; --svg-size: ${SvgSize.svgSize}px; --svg-size-small: ${SvgSize.svgSizeSmall}px; --svg-size-medium: ${SvgSize.svgSizeMedium}px; --svg-size-cover: ${SvgSize.svgSizeCover}px; --svg-size-large: ${SvgSize.svgSizeLarge}px; --svg-size-huge2: ${SvgSize.svgSizeHuge2}px; --svg-size-huge: ${SvgSize.svgSizeHuge}px; --swap-box-height: 580px; /** js used also **/ --panel-setting-height: 680px; --panel-setting-width: 800px; --modal-width: 480px; --modal-height: 400px; --swap-box-width: 338px; --mobile-full-panel-width: 352px; --toolbar-row-height: 56px; /** js used also 40 + 56 = 96 CoinList.tsx **/ --toolbar-row-height-minus: -56px; --toolbar-row-padding: 40px; /** js used also 40 + 56 = 96 CoinList.tsx **/ --toolbar-row-padding-minus: -40px; --sub-menuItem-width: 200px; --sub-menuItem-height: 52px; --lage-modal-width: 580px; --lage-modal-height: 620px; --gateway-icon-size: 56px; --account-button-fixed-width: 88px; --account-button-fixed-height: 72px; --empty-size: 130px; --account-modal-box-width: 284px; --walletconnect-width: 150px; --row-height: 44px; --row-header-height: 44px; --chart-height: 396px; --nft-height: 396px; --nft-card: 320px; --nft-large-avatar: 160px; --nft-small-avatar: 80px; --redPacket-avatar: 72px; --notification-activited-heigth: 80px; --modal-min-width: 340px; --carousel-dot-size: 14px; --earning-banner-width: 320px; --provider-btn-height: 56px; --input-height-large: 48px; --input-height-huge: 56px; --dual-type-width: 320px; --min-height: 350px; --input-height-swap: 86px; @media only screen and (max-width: 768px) { --modal-width: var(--modal-min-width); --lage-modal-width: 460px; --walletconnect-width: 126px; --dual-type-width: 240px; } --color-EOA-Text: #fba95c; --color-Loopring-Text: #4169ff; --color-OtherSmart-Text: #979797; --color-Binance-Text: #a25402; --color-Huobi-Text: #199e5e; --color-OtherExchange-Text: #a0635a; --color-EOA-Bg: #fffedc; --color-Loopring-Bg: #c9dbef; --color-OtherSmart-Bg: #d9d9d9; --color-Binance-Bg: #fde3c8; --color-Huobi-Bg: #b1f4dd; --color-OtherExchange-Bg: #c1a6a2; } select { appearance: none; -moz-appearance: none; -webkit-appearance: none; &::-ms-expand { display: none; } } .rdg.rdg { --background-color: inherit; } ` export { ColorDarkDefault, ColorLightDefault, SystemColor } ================================================ FILE: packages/common-resources/static-resources/src/themes/css/reset.tsx ================================================ import { css } from '@emotion/react' export default css` html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; box-sizing: border-box; } address, caption, cite, code, dfn, em, strong, th, var, b { font-weight: normal; font-style: normal; } abbr, acronym { border: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } *, *::after, *::before { margin: 0; padding: 0; box-sizing: inherit; } *:focus-visible { outline: rgba(0, 0, 0, 0); } html { text-size-adjust: 100%; box-sizing: border-box; scroll-behavior: smooth; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote { &:before, &:after { content: ''; content: none; } } q { &:before, &:after { content: ''; content: none; } } table { border-collapse: collapse; border-spacing: 0; } caption, th { text-align: left; } textarea { resize: none; } a { text-decoration: none; cursor: pointer; } button { padding: 0; border: none; background: none; } html { overscroll-behavior-x: none; overscroll-behavior-y: none; text-underline-offset: 3px; } iframe { display: none; } #iubenda-pp, #iframeBanxaTarget { iframe { display: initial; } } #iframeBanxaTarget { z-index: 9999; #iframeBanxaClose { transform: scale(2); position: absolute; top: 20px; right: 20px; cursor: pointer; padding: 4px; border-bottom-left-radius: 80%; background: rgba(255, 255, 255, 0.6); } position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: none; align-items: center; justify-content: center; background-color: rgba(166, 174, 185, 0.7); } #walletconnect-qrcode-modal { font-size: 16px; .walletconnect-modal__mobile__toggle { a { color: rgb(76, 130, 251); } } } #iframeBanxaTarget { z-index: 9999; #iframeBanxaClose { transform: scale(2); position: absolute; top: 20px; right: 20px; cursor: pointer; padding: 4px; border-bottom-left-radius: 80%; background: rgba(255, 255, 255, 0.6); } position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: none; align-items: center; justify-content: center; background-color: rgba(166, 174, 185, 0.7); } ` ================================================ FILE: packages/common-resources/static-resources/src/themes/fonts/english/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty change (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: packages/common-resources/static-resources/src/themes/globalSetup.ts ================================================ export const globalSetup = Object.freeze({ wait: 100, throttleWait: 3000, }) export const FORMAT_STRING_LEN = 10 ================================================ FILE: packages/common-resources/static-resources/src/themes/index.ts ================================================ import styled from '@emotion/styled' import { Avatar, Grid } from '@mui/material' import { AvatarCoinProps, ThemeType } from './interface' import { hr } from './overrides/overrides-mui' import { css } from '@emotion/react' //@ts-ignore import cssStyle from 'github-markdown-css/github-markdown.css' import loopringJSON from '../../../assets/coin/loopring.json' export * from './overrides/muTheme' export * from './css/global' // export * from "./css/color-lib" export * from './interface' export * from './globalSetup' export { hexToRGB } from './css/color-lib' export { hr } //https://static.loopring.io/assets/images/coin/loopring.png export const AvatarCoinStyled = styled(Avatar)` &.MuiAvatar-root { height: 72px; width: 72px; background-image: url('./static/coin/${loopringJSON.file}'); ${({ imgx, imgy, imgheight = 72, imgwidth = 73, }: // size = 24, AvatarCoinProps) => { return ` background-position-x: -${imgx}px ; background-position-y: -${imgy}px ; height: ${imgheight}px ; width: ${imgwidth}px ; transform-origin: center; ` }} background-size: auto; } ` as (props: AvatarCoinProps) => JSX.Element const style = css` ${cssStyle} ` export const MarkdownStyle = styled(Grid)` ${({ theme }) => ` .markdown-body{ border-radius: ${theme.unit / 2}px; max-width:1200px; --color-fg-default: ${theme.colorBase.textPrimary}; --color-fg-muted: ${theme.colorBase.textThird}; --color-fg-subtle: ${theme.colorBase.textSecondary}; --color-canvas-default: ${theme.colorBase.box}; --color-border-default: ${theme.colorBase.border}; --color-border-muted: ${theme.colorBase.divide}; --color-canvas-subtle:${theme.colorBase.fieldOpacity}; ${ theme.mode === ThemeType.dark ? ` --color-prettylights-syntax-comment: #8b949e; --color-prettylights-syntax-constant: #79c0ff; --color-prettylights-syntax-entity: #d2a8ff; --color-prettylights-syntax-storage-modifier-import: #c9d1d9; --color-prettylights-syntax-entity-tag: #7ee787; --color-prettylights-syntax-keyword: #ff7b72; --color-prettylights-syntax-string: #a5d6ff; --color-prettylights-syntax-variable: #ffa657; --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; --color-prettylights-syntax-invalid-illegal-bg: #8e1519; --color-prettylights-syntax-carriage-return-text: #f0f6fc; --color-prettylights-syntax-carriage-return-bg: #b62324; --color-prettylights-syntax-string-regexp: #7ee787; --color-prettylights-syntax-markup-list: #f2cc60; --color-prettylights-syntax-markup-heading: #1f6feb; --color-prettylights-syntax-markup-italic: #c9d1d9; --color-prettylights-syntax-markup-bold: #c9d1d9; --color-prettylights-syntax-markup-deleted-text: #ffdcd7; --color-prettylights-syntax-markup-deleted-bg: #67060c; --color-prettylights-syntax-markup-inserted-text: #aff5b4; --color-prettylights-syntax-markup-inserted-bg: #033a16; --color-prettylights-syntax-markup-changed-text: #ffdfb6; --color-prettylights-syntax-markup-changed-bg: #5a1e02; --color-prettylights-syntax-markup-ignored-text: #c9d1d9; --color-prettylights-syntax-markup-ignored-bg: #1158c7; --color-prettylights-syntax-meta-diff-range: #d2a8ff; --color-prettylights-syntax-brackethighlighter-angle: #8b949e; --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; --color-accent-fg: #58a6ff; --color-accent-emphasis: #1f6feb; --color-danger-fg: #f85149; }` : ` --color-prettylights-syntax-comment: #6e7781; --color-prettylights-syntax-constant: #0550ae; --color-prettylights-syntax-entity: #8250df; --color-prettylights-syntax-storage-modifier-import: #24292f; --color-prettylights-syntax-entity-tag: #116329; --color-prettylights-syntax-keyword: #cf222e; --color-prettylights-syntax-string: #0a3069; --color-prettylights-syntax-variable: #953800; --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; --color-prettylights-syntax-invalid-illegal-bg: #82071e; --color-prettylights-syntax-carriage-return-text: #f6f8fa; --color-prettylights-syntax-carriage-return-bg: #cf222e; --color-prettylights-syntax-string-regexp: #116329; --color-prettylights-syntax-markup-list: #3b2300; --color-prettylights-syntax-markup-heading: #0550ae; --color-prettylights-syntax-markup-italic: #24292f; --color-prettylights-syntax-markup-bold: #24292f; --color-prettylights-syntax-markup-deleted-text: #82071e; --color-prettylights-syntax-markup-deleted-bg: #FFEBE9; --color-prettylights-syntax-markup-inserted-text: #116329; --color-prettylights-syntax-markup-inserted-bg: #dafbe1; --color-prettylights-syntax-markup-changed-text: #953800; --color-prettylights-syntax-markup-changed-bg: #ffd8b5; --color-prettylights-syntax-markup-ignored-text: #eaeef2; --color-prettylights-syntax-markup-ignored-bg: #0550ae; --color-prettylights-syntax-meta-diff-range: #8250df; --color-prettylights-syntax-brackethighlighter-angle: #57606a; --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f; --color-prettylights-syntax-constant-other-reference-link: #0a3069; --color-accent-fg: #0969da; --color-accent-emphasis: #0969da; --color-danger-fg: #cf222e; ` } } `}; ul { list-style: inherit; } ol { list-style: decimal; } ${style} .markdown-body.no-bg { background-color: initial; box-shadow: initial; padding: 0; } ` as typeof Grid ================================================ FILE: packages/common-resources/static-resources/src/themes/interface.ts ================================================ import { AvatarProps, Theme } from '@mui/material' import { ColorBaseInterface } from './css/color-lib' export enum ThemeType { dark = 'dark', light = 'light', } declare module '@mui/material' { interface TextFieldPropsSizeOverrides { large: true } interface InputBasePropsSizeOverrides { large: true } // interface InputBasePropsSizeOverrides { // large: true // } } export type ThemeKeys = keyof typeof ThemeType export type LoopringTheme = Theme & { colorBase: ColorBaseInterface fontDefault: { h1: string h2: string h3: string h4: string h5: string h6: string body1: string body2: string } unit: number mode: ThemeKeys border: { defaultBorder: string defaultRadius: string defaultFrame: (props: { d_W?: number d_R?: number c_key?: 'primary' | 'selected' | 'blur' | 'focus' | string }) => string borderConfig: (props: { d_W?: number c_key?: 'primary' | 'selected' | 'blur' | 'focus' | string }) => string } } export type AvatarCoinProps = AvatarProps & { imgx: number imgy: number imgh?: number imgw?: number imgheight: number imgwidth: number size?: number } declare module '@emotion/react' { export interface Theme extends LoopringTheme {} } ================================================ FILE: packages/common-resources/static-resources/src/themes/overrides/muTheme.ts ================================================ import { createTheme } from '@mui/material' import { ColorDarkDefault, ColorLightDefault } from '../css/color-lib' import { borderFunc, unit } from './utils' import { MuiAlert, MuiBreadcrumbs, MuiButton, MuiButtonBase, MuiCard, MuiCardActions, MuiCardContent, MuiCheckbox, MuiDialog, MuiDialogTitle, MuiDivider, MuiFormLabel, MuiIconButton, MuiInputBase, MuiInputLabel, MuiLinearProgress, MuiLink, MuiList, MuiListItem, MuiListItemAvatar, MuiMenu, MuiMenuItem, MuiModal, MuiPaginationItem, MuiPaper, MuiPopover, MuiRadio, MuiSnackbar, MuiSvgIcon, MuiSwitch, MuiTab, MuiTabs, MuiTextField, MuiToggleButton, MuiToolbar, MuiTooltip, MuiBadge, radius, } from './overrides-mui' import { MuPickDate } from './overrides-date-pick' import { fontDefault } from '../css/global' import { LoopringTheme, ThemeKeys } from '../interface' export { unit } export const getTheme = (themeMode: ThemeKeys, _isMobile = false): LoopringTheme => { const colorBase: typeof ColorDarkDefault = ( themeMode === 'dark' ? ColorDarkDefault : ColorLightDefault ) as typeof ColorDarkDefault const theme = createTheme({ spacing: unit, palette: { mode: themeMode, primary: { light: colorBase.primary, main: colorBase.primary, dark: colorBase.primary, contrastText: themeMode === 'dark' ? '#fff' : '#000', }, secondary: { light: colorBase.secondary, main: colorBase.secondary, dark: colorBase.secondary, contrastText: themeMode === 'dark' ? '#fff' : '#000', // light: }, contrastThreshold: 3, tonalOffset: 0.2, background: { paper: colorBase.box, default: colorBase.globalBg, }, text: { primary: colorBase.textPrimary, secondary: colorBase.textSecondary, disabled: colorBase.textDisable, //hint: colorBase.textHint, }, // divider: "rgba(0, 0, 0, 0.12)", common: { black: '#000', white: '#fff' }, action: { hoverOpacity: 0.05, hover: colorBase.secondaryHover, selected: colorBase.secondaryPressed, // disabledBackground: "rgba(0, 0, 0, 0.12)", disabled: colorBase.textDisable, active: colorBase.secondaryPressed, }, warning: { main: colorBase.warning, }, success: { main: colorBase.success, }, error: { main: colorBase.error, dark: colorBase.error, contrastText: themeMode === 'dark' ? '#fff' : '#000', }, }, typography: { // fontFamily: `DINCondensed, Helvetica, Arial, "华文细黑", "Microsoft YaHei", "微软雅黑", SimSun, "宋体", Heiti, "黑体", sans-serif`, fontFamily: 'Roboto', fontSize: 14, h1: { fontSize: fontDefault.h1, lineHeight: '4.6rem', fontWeight: 500, }, h2: { fontSize: fontDefault.h2, lineHeight: '3.8rem', fontWeight: 500, }, h3: { fontSize: fontDefault.h3, lineHeight: '3.2rem', fontWeight: 500, }, h4: { fontSize: fontDefault.h4, lineHeight: '2.8rem', fontWeight: 400, }, h5: { fontSize: fontDefault.h5, lineHeight: '2.4rem', fontWeight: 400, margin: 0, }, h6: { fontSize: fontDefault.h6, margin: 0, }, subtitle1: { fontSize: 16, lineHeight: '2.4rem', fontWeight: 500, }, button: { fontSize: 20, color: colorBase.textButton, fontWeight: 400, }, body1: { fontSize: fontDefault.body1, color: colorBase.textPrimary, fontWeight: 400, }, body2: { fontSize: 12, color: colorBase.textSecondary, }, }, zIndex: { tooltip: 2002, }, components: { MuiCard: MuiCard({ colorBase }), MuiCardContent: MuiCardContent(), MuiCardActions: MuiCardActions(), MuiCheckbox: MuiCheckbox({ colorBase }), MuiLink: MuiLink({ colorBase }), MuiModal: MuiModal({ colorBase }), // MuiBackdrop:MuiBackdrop({colorBase}), MuiPopover: MuiPopover(), MuiToolbar: MuiToolbar(), MuiSvgIcon: MuiSvgIcon(), MuiTabs: MuiTabs(), MuiTab: MuiTab({ colorBase }), MuiButtonBase: MuiButtonBase, MuiRadio: MuiRadio(), MuiBadge: MuiBadge, MuiButton: MuiButton({ colorBase, themeMode }), MuiToggleButton: MuiToggleButton({ colorBase }), // MuiToggleButtonGroup: MuiToggleButtonGroup({colorBase}), MuiSwitch: MuiSwitch(), MuiIconButton: MuiIconButton({ colorBase }), MuiPaginationItem: MuiPaginationItem({ colorBase }), MuiTextField: MuiTextField({ colorBase }), MuiBreadcrumbs: MuiBreadcrumbs(), MuiFormLabel: MuiFormLabel({ colorBase }), MuiInputBase: MuiInputBase({ colorBase, themeMode }), MuiMenu: MuiMenu({ colorBase }), MuiMenuItem: MuiMenuItem({ colorBase, themeMode }), MuiList: MuiList(), MuiListItem: MuiListItem({ colorBase }), MuiListItemAvatar: MuiListItemAvatar(), MuiInputLabel: MuiInputLabel({ colorBase }), // MuiPopover: MuiPopover({colorBase, themeMode}), MuiPaper: MuiPaper({ colorBase, themeMode }), MuiDivider: MuiDivider({ colorBase }), MuiAlert: MuiAlert({ colorBase }), MuiSnackbar: MuiSnackbar(), MuiDialogTitle: MuiDialogTitle({ colorBase }), MuiDialog: MuiDialog({ colorBase }), MuiLinearProgress: MuiLinearProgress({ colorBase }), MuiTooltip: MuiTooltip({ colorBase }), ...MuPickDate({ colorBase, themeMode }), }, shape: { borderRadius: radius }, }) return { ...theme, ...{ unit, mode: themeMode, border: borderFunc(themeMode), fontDefault, colorBase: themeMode === 'dark' ? ColorDarkDefault : ColorLightDefault, }, } as LoopringTheme } ================================================ FILE: packages/common-resources/static-resources/src/themes/overrides/overrides-date-pick.ts ================================================ import { borderFunc } from './utils' import { ColorDarkDefault } from '../css/color-lib' import { radius } from './overrides-mui' import { fontDefault } from '../css/global' import { ThemeKeys } from '../interface' export const MuPickDate = ({ colorBase, themeMode, }: { colorBase: typeof ColorDarkDefault themeMode: ThemeKeys }) => { return { MuiPickersBasePicker: { styleOverrides: { root: { width: `var(--datePicker-width)`, background: colorBase.popBg, boxShadow: colorBase.shadow, borderRadius: radius * 2 + 'px', // border: borderFunc(themeMode).borderConfig({c_key: 'blur'}), border: borderFunc(themeMode).defaultFrame({ c_key: colorBase.divide }), //`1px solid `, '& svg': { fontSize: '2rem', // color: colorBase.textSecondary }, '& .MuiPickersCalendar-weekDayLabel': { fontSize: '1rem', }, '& .MuiPickersArrowSwitcher-root .MuiIconButton-edgeEnd': { position: 'relative', padding: 3, }, }, }, }, MuiPicker: { styleOverrides: { root: { width: `var(--datePicker-width)`, background: colorBase.popBg, boxShadow: colorBase.shadow, borderRadius: radius * 2 + 'px', border: borderFunc(themeMode).borderConfig({ c_key: 'blur' }), '& svg': { fontSize: '2rem', // color: colorBase.textSecondary }, '& .MuiPickersCalendar-weekDayLabel': { fontSize: '1rem', }, '& .MuiPickersArrowSwitcher-root .MuiIconButton-edgeEnd': { position: 'relative', padding: 3, }, }, }, }, MuiDateRangePickerViewDesktop: { styleOverrides: { root: { '& svg': { fontSize: '2rem', color: colorBase.textSecondary, }, background: colorBase.popBg, boxShadow: colorBase.shadow, borderRadius: radius * 2 + 'px', // border: borderFunc(themeMode).borderConfig({c_key: 'blur'}), border: borderFunc(themeMode).defaultFrame({ c_key: colorBase.divide }), //`1px solid ${colorBase.divide}`, fontSize: 1.6, '& .MuiDateRangePickerViewDesktop-rangeCalendarContainer:not(:last-child)': { borderColor: colorBase.divide, }, '& .MuiPickersArrowSwitcher-root': { border: borderFunc(themeMode).borderConfig({ c_key: 'rgba(0,0,0,0)' }), borderBottomColor: colorBase.divide, boxSizing: 'border-box', height: 52, minHeight: 52, maxHeight: 52, margin: 0, marginBottom: 0, '& .MuiIconButton-root': { color: colorBase.textSecondary, }, '& .MuiTypography-subtitle1,& .MuiTypography-subtitle2': { fontSize: '1.4rem', }, }, '& .MuiPickersCalendar-weekDayLabel': { fontSize: '1rem', }, '& .MuiDateRangePickerDay-rangeIntervalPreview,& .MuiDateRangePickerDay-rangeIntervalDayPreview': { borderWidth: 0, }, '& .MuiDateRangePickerDay-day': { transform: 'none', }, '& .MuiDateRangePickerViewDesktop-calendar': { width: `var(--datePicker-width)`, minHeight: `var(--datePicker-height)`, marginBottom: '.8rem', }, '& .MuiDateRangePickerDay-dayOutsideRangeInterval:hover': { borderColor: 'transparent', }, }, }, }, MuiPickersDesktopDateRangeCalendar: { styleOverrides: { root: { '& svg': { fontSize: '2rem', // color: colorBase.textSecondary }, background: colorBase.popBg, boxShadow: colorBase.shadow, borderRadius: radius * 2 + 'px', border: borderFunc(themeMode).borderConfig({ c_key: 'blur' }), fontSize: 1.6, '& .MuiTypography-subtitle1': { fontSize: fontDefault.h4, }, '& .MuiPickersDesktopDateRangeCalendar-rangeCalendarContainer:not(:last-child)': { borderColor: colorBase.divide, }, '& .MuiPickersArrowSwitcher-root': { border: borderFunc(themeMode).borderConfig({ c_key: 'rgba(0,0,0,0)' }), borderBottomColor: colorBase.divide, boxSizing: 'border-box', height: 52, minHeight: 52, maxHeight: 52, margin: 0, marginBottom: 0, '& .MuiIconButton-root': { color: colorBase.textSecondary, }, '& .MuiTypography-subtitle1,& .MuiTypography-subtitle2': { fontSize: '1.4rem', }, }, '& .MuiPickersCalendar-weekDayLabel': { fontSize: '1rem', }, '& .MuiPickersDateRangeDay-rangeIntervalPreview,& .MuiPickersDateRangeDay-rangeIntervalDayPreview': { borderWidth: 0, }, '& .MuiPickersDateRangeDay-day': { transform: 'none', }, '& .MuiPickersDesktopDateRangeCalendar-calendar': { width: `var(--datePicker-width)`, minHeight: `var(--datePicker-height)`, marginBottom: '.8rem', }, '& .MuiPickersDateRangeDay-dayOutsideRangeInterval:hover': { borderColor: 'transparent', }, }, }, }, MuiPickersDateRangeDay: { styleOverrides: { root: { '&.MuiPickersDateRangeDay-rangeIntervalDayHighlight:last-child, &.MuiPickersDateRangeDay-rangeIntervalDayHighlightEnd': { borderTopRightRadius: radius * 2 + 'px', borderBottomRightRadius: radius * 2 + 'px', }, '&.MuiPickersDateRangeDay-rangeIntervalDayHighlight:first-of-type, &.MuiPickersDateRangeDay-rangeIntervalDayHighlightStart': { borderTopLeftRadius: radius * 2 + 'px', borderBottomLeftRadius: radius * 2 + 'px', }, '& .MuiPickersDay-root:focus.Mui-selected': { backgroundColor: colorBase.primaryPressed, }, '& .MuiPickersDay-root:focus.Mui-selected,& .MuiPickersDay-root.Mui-selected,& .MuiPickersDay-root': { '&:hover': { borderColor: 'transparent', }, }, }, }, }, MuiDateRangePickerDay: { styleOverrides: { root: { '&.MuiDateRangePickerDay-rangeIntervalDayHighlight:last-child, &.MuiDateRangePickerDay-rangeIntervalDayHighlightEnd': { borderTopRightRadius: radius * 2 + 'px', borderBottomRightRadius: radius * 2 + 'px', }, '&.MuiDateRangePickerDay-rangeIntervalDayHighlight:first-of-type, &.MuiDateRangePickerDay-rangeIntervalDayHighlightStart': { borderTopLeftRadius: radius * 2 + 'px', borderBottomLeftRadius: radius * 2 + 'px', }, '& .MuiPickersDay-root:focus.Mui-selected': { backgroundColor: colorBase.primaryPressed, }, }, }, }, MuiPickersToolbarButton: { styleOverrides: { root: { fontSize: 1.6, }, }, }, MuiPickersToolbar: { root: {}, }, MuiPickersDay: { styleOverrides: { root: { '&&': { fontSize: '1.4rem', borderRadius: radius * 2 + 'px', border: borderFunc(themeMode).borderConfig({ c_key: 'rgba(0,0,0,0)' }), backgroundColor: colorBase.opacity, }, '&.Mui-selected,&:focus.Mui-selected ': { backgroundColor: colorBase.primary, // color: colorBase.textThird, }, '&.Mui-disabled': { //backgroundColor: colorBase.primary, color: colorBase.textDisable, }, '&.MuiPickersDay-today': { '&:not(.Mui-selected)': { backgroundColor: 'transparent', color: colorBase.primary, borderColor: 'transparent', }, // borderColor: colorBase.primary, '&.Mui-selected': { //backgroundColor: colorBase.primary, color: colorBase.textPrimary, backgroundColor: colorBase.primary, }, }, '&:hover.Mui-selected, &:hover': { backgroundColor: colorBase.tag, }, }, }, }, MuiPickersCalendar: { styleOverrides: { root: { minHeight: `var(--datePicker-height)`, marginBottom: '.8rem', }, }, }, MuiPickersCalendarHeader: { styleOverrides: { root: { '&&': { border: borderFunc(themeMode).borderConfig({ c_key: 'rgba(0,0,0,0)' }), borderBottomColor: colorBase.divide, boxSizing: 'border-box', height: 52, minHeight: 52, maxHeight: 52, margin: 0, marginBottom: 0, // margin:0, }, '& .MuiButtonBase': { color: colorBase.textSecondary, }, '& .MuiTypography-subtitle1': { fontSize: fontDefault.h4, }, }, }, }, MuiPickersYear: { styleOverrides: { root: { '&&': { fontSize: '1.4rem', }, '&& .MuiPickersYear-yearButton': { color: colorBase.textSecondary, fontSize: '1.4rem', '&.Mui-selected': { backgroundColor: colorBase.primary, // color: colorBase.textThird, borderRadius: radius * 2 + 'px', }, }, }, }, }, MuiPickersModalDialog: { dialogAction: {}, }, } } ================================================ FILE: packages/common-resources/static-resources/src/themes/overrides/overrides-mui.ts ================================================ import { borderFunc, pxToRem, unit } from './utils' import { ComponentsOverrides, TooltipProps } from '@mui/material' import { fontDefault } from '../css/global' export const radius = 4 export const checkBoxSize = 18 export const hr = ({ colorBase }: any) => { return { borderRadius: `${radius}px`, content: '""', margin: `0 ${2 * unit}px`, // margin: `0 ${unit}px`, display: 'block', height: `2px`, backgroundColor: colorBase.primary, position: 'absolute', left: 0, right: 0, bottom: 0, } } export const MuiCheckbox = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiCheckbox'] } => { return { styleOverrides: { root: { '&.MuiCheckbox-colorDefault': { color: colorBase.textSecondary, }, '&:hover': { backgroundColor: 'inherit', color: colorBase.textButtonSelect, }, '&.Mui-checked': { color: colorBase.textButtonSelect, '&:hover': { color: colorBase.textButtonSelect, }, }, ' .MuiSvgIcon-fontSizeMedium ': { // fontSize: fontDefault.h5 height: fontDefault.h5, width: fontDefault.h5, }, }, }, } } export const MuiCard = ({ colorBase }: any) => { return { styleOverrides: { root: { backgroundColor: colorBase.box, padding: pxToRem(24), }, }, } } export const MuiCardContent = () => { return { styleOverrides: { root: { padding: pxToRem(8), }, }, } } export const MuiCardActions = () => { return { styleOverrides: { root: { padding: 0, }, }, } } export const MuiLink = ({ colorBase }: any) => { return { styleOverrides: { root: { color: colorBase.secondary, textDecoration: 'none', '&:hover': { color: colorBase.secondaryHover, }, '&.Mui-selected': { color: colorBase.secondaryPressed, }, }, }, } } export const MuiTextField = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiTextField'] } => { return { styleOverrides: { root: { '.MuiFormHelperText-root': { marginLeft: 0, marginRight: 0, textAlign: 'right', whiteSpace: 'pre-line', }, ' .MuiFormLabel-root': { color: colorBase.textThird, '&.Mui-focused': { color: colorBase.textSecondary, }, }, }, }, } } // export const MuiFormLabel = ({colorBase}: any): { styleOverrides: ComponentsOverrides['MuiFormLabel'] } => { // return { // styleOverrides: { // root: { // // // '.MuiFormLabel-root':{ // // // // } // } // } // } // } export const MuiModal = ({ colorBase }: any) => { return { styleOverrides: { root: { color: colorBase.textPrimary, ' .MuiBackdrop-root': { zIndex: -1, backgroundColor: colorBase.mask, }, }, }, defaultProps: { disableAutoFocus: true, disableEnforceFocus: true, disableScrollLock: true, }, } } export const MuiToolbar = () => { return { styleOverrides: { root: { height: 'var(--header-height)' }, }, } } // @ts-ignore export const MuiSwitch = (): { styleOverrides: ComponentsOverrides['MuiSwitch'] } => { return { styleOverrides: {} } } export const MuiBadge = (): { styleOverrides: ComponentsOverrides['MuiBadge'] } => { return { styleOverrides: { fontSize: '1rem', }, } } export const MuiButton = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiButton'] } => { return { styleOverrides: { root: { textTransform: 'capitalize', '&:not($sizeLarge):not($sizeSmall) $label': { fontSize: '1.4rem', // font: 'normal normal 700 0.875rem/1.6875rem Open Sans', // color: 'green' }, fontFamily: 'Roboto', fontSize: pxToRem(14), borderRadius: pxToRem(4), fontWeight: 'normal', paddingLeft: pxToRem(12), paddingRight: pxToRem(12), // '&.MuiButton-contained':{ // // } }, text: { color: colorBase.secondary, fontSize: '1.6rem', '&:hover': { 'svg, &': { color: colorBase.secondaryHover, }, backgroundColor: 'inherit', }, '&:active': { color: colorBase.secondaryPressed, }, '&:disabled': { color: colorBase.disable, }, '& .MuiButton-endIcon,& .MuiButton-startIcon': { color: 'inherit', }, '&.MuiButton-sizeSmall': { fontSize: '1.4rem', }, }, contained: { color: colorBase.textButton, height: pxToRem(40), fontSize: pxToRem(14), boxShadow: 'initial', '&:hover': { boxShadow: 'initial', '&:before': { background: colorBase.primaryHover, content: "''", position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, borderRadius: pxToRem(8), }, }, borderRadius: pxToRem(8), '&.Mui-disabled': { backgroundColor: colorBase.defaultDisable, color: colorBase.textDisable, }, }, sizeLarge: { height: pxToRem(48), fontSize: '2.0rem', fontWeight: 'normal', // '& .MuiButton-label': { // // } }, sizeSmall: { height: pxToRem(32), fontSize: '1.6rem', // '& $label': { // // }, }, outlinedSizeSmall: { height: pxToRem(24), fontSize: '1.2rem', }, outlined: { height: pxToRem(32), // boxShadow: '0px 4px 62px rgba(0, 0, 0, 0.25)', fontSize: '1.4rem', fontWeight: 'normal', color: colorBase.textSecondary, borderColor: colorBase.border, backgroundColor: colorBase.box, '&:hover': { color: colorBase.textPrimary, borderColor: colorBase.textPrimary, backgroundColor: colorBase.box, }, '&.MuiContained-sizeMedium': { height: pxToRem(40), fontSize: pxToRem(14), }, '&.Mui-disabled': { // backgroundColor: colorBase.defaultDisable, color: colorBase.textDisable, border: `1px dashed ${colorBase.textDisable}`, // borderColor: // backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='4' ry='4' stroke='%23FFFFFF33' stroke-width='1' stroke-dasharray='4%25%2c 8%25' stroke-dashoffset='5' stroke-linecap='square'/%3e%3c/svg%3e")` }, }, }, } } // @ts-ignore export const MuiPopover = (): { styleOverrides: ComponentsOverrides['MuiPopover'] } => { return { styleOverrides: {}, } } // export const MuiBackdrop = ({colorBase}: any) => { // return { // styleOverrides: { // root: { // zIndex: -1, // // } // } // } // } export const MuiPaper = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiPaper'] } => { return { styleOverrides: { root: { borderRadius: pxToRem(8), backgroundImage: 'none', backgroundColor: colorBase.popBg, border: `.5px solid ${colorBase.border}`, '&.MuiPopover-paper': { backgroundImage: 'none', boxShadow: colorBase.shadowHover, }, }, }, } } export const MuiSvgIcon = () => { return { styleOverrides: { root: { // margin: '-4px', '&.MuiSvgIcon-fontSizeSmall': { height: 'var(--svg-size)', width: 'var(--svg-size)', }, '&.MuiSvgIcon-fontSizeLarge': { height: 'var(--svg-size-large)', width: 'var(--svg-size-large)', // margin: '-6px', }, '&.MuiSvgIcon-fontSizeMedium': { height: 'var(--svg-size-medium)', width: 'var(--svg-size-medium)', // margin: '-6px', }, '&.tag': { width: 'auto', height: 'auto', }, '&.custom-size': { width: '1em', height: '1em', }, }, }, } } //@ts-ignore export const MuiRadio = () => { return { styleOverrides: { root: { ' svg': { height: 'var(--svg-size)', width: 'var(--svg-size)', }, }, }, } } export const MuiInputLabel = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiInputLabel'] } => { return { styleOverrides: { root: { fontSize: fontDefault.body1, height: 20, color: colorBase.textThird, transform: 'none', top: 0, left: 0, right: 0, '&.Mui-focused': { color: colorBase.textSecondary, }, '&.Mui-disabled ': { color: colorBase.textSecondary, }, }, }, } } export const MuiInputBase = ({ colorBase, themeMode, }: any): { styleOverrides: ComponentsOverrides['MuiInputBase'] } => { return { styleOverrides: { root: { 'label + &': { marginTop: 24, }, position: 'relative', fontSize: '1.4rem', backgroundColor: colorBase.box, border: borderFunc(themeMode).borderConfig({ c_key: colorBase.border }), //`1px solid ${colorBase.border}`, borderRadius: 4, boxSizing: 'border-box', height: pxToRem(32), '&.MuiInputBase-sizeSmall': { height: pxToRem(32), }, '&:not(.MuiFormControl-fullWidth)': { // width: 'var(--btn-min-width)', minWidth: 'var(--btn-min-width)', // maxWidth: 'var(--btn-max-width)', }, '& .MuiListItemText-multiline': { display: 'flex', flexDirection: 'row', alignItems: 'center', flexWrap: 'noWrap', justifyContent: 'space-between', margin: 0, height: 'inherit', }, '& fieldset': { display: 'none', }, ' .MuiInputAdornment-root, .MuiSelect-iconOutlined': { color: colorBase.textThird, }, '.search .MuiInputAdornment-root': { pointerEvents: 'none', }, paddingRight: 0, '&:hover': { border: borderFunc(themeMode).borderConfig({ c_key: colorBase.borderHover, }), //`1px solid ${colorBase.border}`, }, '&:disabled': { backgroundColor: colorBase.defaultDisable, borderColor: colorBase.defaultDisable, color: colorBase.textBtnDisabled, }, '.MuiSelect-iconOpen': { transform: 'rotate(180deg)', }, ' .MuiSelect-select': { padding: 0, border: 'transparent', backgroundColor: 'transparent', color: 'var(--color-text-primary)', '&:focus': { background: 'transparent', }, ' svg': { right: '.4rem', top: '50%', transform: 'translateY(-50%)', position: 'absolute', transition: 'fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', // color: 'var(--color-text-secondary)' }, '&.MuiInputBase-root': { minWidth: 'auto', width: 'auto', }, }, '&.MuiOutlinedInput-root': { padding: '.3rem .3rem .3rem .8rem', minWidth: 'auto', }, '.text-address &.MuiOutlinedInput-root': { paddingRight: '2em', }, ' .MuiOutlinedInput-input': { padding: 0, height: 'calc(3.2rem - .6rem)', }, ' .MuiSelect-outlined': { height: 'calc(3.2rem - .6rem)', lineHeight: 'calc(3.2rem - .6rem)', minWidth: 'auto', }, ' .MuiSelect-icon': { color: colorBase.textThird, }, }, }, } } export const MuiIconButton = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiIconButton'] } => { return { styleOverrides: { root: { color: 'inherit', height: 'var(--btn-icon-size-medium)', width: 'var(--btn-icon-size-medium)', ' svg': { height: 'var(--svg-size-medium)', width: 'var(--svg-size-medium)', }, '&.MuiIconButton-sizeLarge': { height: 'var(--btn-icon-size)', width: 'var(--btn-icon-size)', ' svg': { height: 'var(--svg-size-large)', width: 'var(--svg-size-large)', }, }, '&.MuiIconButton-sizeMedium': { height: 'var(--btn-icon-size-medium)', width: 'var(--btn-icon-size-medium)', ' svg': { height: 'var(--svg-size-medium)', width: 'var(--svg-size-medium)', }, }, '&.MuiIconButton-sizeSmall': { height: 'var(--btn-icon-size-small)', width: 'var(--btn-icon-size-small)', ' svg': { height: 'var(--svg-size)', width: 'var(--svg-size)', }, }, '&.MuiIconButton-colorInherit': { color: colorBase.textSecondary, '&:hover': { color: colorBase.textPrimary, }, }, }, // label: { // width: 'auto' // } }, } } export const MuiToggleButton = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiToggleButton'] } => { return { styleOverrides: { root: { '&.MuiToggleButton-sizeMedium': { fontSize: '1.4rem', minWidth: pxToRem(51), }, height: pxToRem(28), // boxShadow: '0px 4px 62px rgba(0, 0, 0, 0.25)', borderRadius: '4px !important', marginRight: pxToRem(2), fontSize: '1.4rem', color: colorBase.textSecondary, border: 'none', textTransform: 'none', // borderColor: colorBase.border, '&&:not(:first-of-type), &&:not(:last-child)': { // borderColor: colorBase.border, }, backgroundColor: 'inherit', '&:hover': { backgroundColor: 'inherit', // color: colorBase.textButton, color: colorBase.textPrimary, // borderColor: colorBase.textPrimary, border: 'none', '&:not(:last-child), &:not(:first-of-type)': { // borderColor: colorBase.textPrimary, }, '&.Mui-selected,&.Mui-selected': { // borderColor: colorBase.primary, }, }, '&.Mui-disabled': { // backgroundColor: colorBase.defaultDisable, color: colorBase.textDisable, border: 'none', // border: '1px dashed', // borderColor: colorBase.border // backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='4' ry='4' stroke='%23FFFFFF33' stroke-width='1' stroke-dasharray='4%25%2c 8%25' stroke-dashoffset='5' stroke-linecap='square'/%3e%3c/svg%3e")` }, '&&.Mui-selected, &&.Mui-selected + &.Mui-selected': { color: colorBase.textButton, backgroundColor: colorBase.primary, // border: 'none' // border: borderFunc(themeMode).borderConfig({c_key: rgba(colorBase.primary, 0.5)}) }, }, }, } } export const MuiButtonBase = { defaultProps: { disableRipple: true, }, } export const MuiPaginationItem = ({ colorBase }: any) => { return { styleOverrides: { root: { fontSize: '1.4rem', color: colorBase.textSecondary, borderRadius: '4px', }, icon: { fontSize: '2rem', }, textPrimary: { '&:hover': { backgroundColor: colorBase.boxHover, color: colorBase.textButtonSelect, }, '&.Mui-selected': { backgroundColor: 'transparent', color: colorBase.textButtonSelect, '&:hover': { backgroundColor: colorBase.boxHover, color: colorBase.textButtonSelect, }, }, }, selected: {}, }, } } export const MuiFormLabel = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiFormLabel'] } => { return { styleOverrides: { root: { color: colorBase.textSecondary, lineHeight: 1, boxSizing: 'border-box', '&.MuiInputLabel-shrink': { transform: 'none', }, '&.Mui-focused': { color: colorBase.textSecondary, }, }, }, } } export const MuiBreadcrumbs = (): { styleOverrides: ComponentsOverrides['MuiBreadcrumbs'] } => { return { styleOverrides: { root: { ' .MuiLink-root': { textDecoration: 'none', }, }, }, } } export const MuiList = () => { return { styleOverrides: { root: { '&.MuiList-padding': { padding: '0.8rem 0', }, }, }, } } export const MuiListItem = ({ colorBase }: any) => { return { styleOverrides: { root: { height: pxToRem(32), paddingLeft: pxToRem(20), paddingRight: pxToRem(20), color: colorBase.textSecondary, '&:hover': { color: colorBase.textButtonSelect, background: colorBase.boxHover, }, '&.Mui-selected, &.Mui-focusVisible': { backgroundColor: 'transparent', color: colorBase.textButtonSelect, // " .MuiTypography-body1":{ // color: colorBase.textButtonSelect, // }, '&:hover': { color: colorBase.textButtonSelect, // " .MuiTypography-body1":{ // color: colorBase.textButtonSelect, // }, }, }, ' .MuiListItemAvatar-root': { color: colorBase.buttonIcon, // "var(--color-button-icon)" }, }, }, } } export const MuiMenu = ({ colorBase }: any) => { return { styleOverrides: { root: { ' .MuiBackdrop-root': { opacity: '0 !important', }, }, list: { backgroundColor: colorBase.popBg, }, }, } } export const MuiMenuItem = ({ colorBase, themeMode }: any) => { return { styleOverrides: { root: { height: pxToRem(32), borderLeft: borderFunc(themeMode).borderConfig({ c_key: 'var(--opacity)', }), ////`1px solid transparent`, // borderLeftColor: 'transparent', paddingLeft: pxToRem(12), paddingRight: pxToRem(12), color: colorBase.textSecondary, '&.Mui-selected, &.Mui-selected.Mui-focusVisible': { background: colorBase.boxHover, color: colorBase.textButtonSelect, // " .MuiTypography-body1":{ // color: colorBase.textButtonSelect, // }, }, // backgroundColor: `${colorBase.borderHover} !important`, '&:hover, &.Mui-selected:hover': { // background:'inherit', // color: colorBase.textPrimary, // borderColor: colorBase.primaryLight, background: colorBase.boxHover, }, '& .MuiListItemText-multiline': { display: 'flex', flexDirection: 'row', alignItems: 'space-between', flexWrap: 'noWrap', justifyContent: 'space-between', '&.Mui-selected, &.Mui-selected.Mui-focusVisible': { '&:after': { display: 'none', }, }, }, // '&.Mui-selected, &.Mui-selected.Mui-focusVisible': { // // backgroundColor: 'transparent', // background: 'none', // color: colorBase.textPrimary, // // '&:after': { // // fontSize: '1.6rem', // // height: '1em', // // width: '1em', // // right: '1em', // // position: 'absolute', // // display: 'block', // // content: `url("data:image/svg+xml;utf8,\")` // // } // }, }, }, } } export const MuiTab = ({ colorBase }: any): { styleOverrides: ComponentsOverrides['MuiTab'] } => { return { styleOverrides: { root: { fontWeight: 'normal', padding: `${unit * 2}px`, maxWidth: 'initial', minWidth: 'auto !important', fontSize: fontDefault.h5, color: colorBase.textSecondary, '&:hover': { backgroundColor: 'transparent', }, '&.MuiTab-root': { textTransform: 'capitalize', }, '&.Mui-selected': { color: colorBase.textPrimary, '&:after': hr({ colorBase }), }, '&:focus-visible::after, &:active::after, &.Mui-selected:after': hr({ colorBase, }), '&MuiTab-fullWidth.:focus-visible::after, &.MuiTab-fullWidth:active::after, &.MuiTab-fullWidth.Mui-selected:after': { margin: 0, }, '> div, > button': { height: 'calc(100% - 2px)', textTransform: 'capitalize', }, '.MuiTabs-indicator': { display: 'none', }, '.MuiTabs-small &.MuiTab-root': { fontSize: fontDefault.body1, padding: `${unit}px`, minHeight: `36px`, }, }, }, } } export const MuiTabs = () => { return { styleOverrides: { root: { '& .MuiTabs-indicator': { display: 'none', background: 'red', }, '& .MuiTabs-small': { minHeight: '28px', height: '28px', fontSize: '', }, }, }, } } export const MuiListItemAvatar = () => { return { styleOverrides: { root: { minWidth: 'auto', marginRight: `${unit}px`, '.MuiAvatar-root': { height: `var(--list-menu-coin-size)`, width: `var(--list-menu-coin-size)`, svg: { height: `var(--header-menu-icon-size)`, width: `var(--header-menu-icon-size)`, }, }, }, }, } } export const MuiDivider = ({ colorBase }: any) => { return { styleOverrides: { root: { borderColor: `${colorBase.divide}`, // margin: `${unit / 4 * 5}px 0`, }, }, } } export const MuiAlert = ({ colorBase }: any) => { return { styleOverrides: { root: { // backgroundColor: colorBase.borderDark, // backgroundColor: 'var(--color-pop-bg)', backgroundColor: colorBase.popBg, height: `auto`, '.MuiAlertTitle-root': { color: colorBase.textPrimary, fontSize: pxToRem(16), }, }, }, } } export const MuiDialogTitle = ({ colorBase }: any) => { return { styleOverrides: { root: { '&.MuiDialogTitle-root': { color: colorBase.textPrimary, fontSize: pxToRem(16), padding: `${2 * unit}px`, }, }, }, } } export const MuiDialog = ({ colorBase }: any) => { return { styleOverrides: { root: { ' .MuiPaper-root': { background: colorBase.box, borderRadius: pxToRem(4), }, ' .MuiDialogContent-root': { padding: `0 ${2 * unit}px ${2 * unit}px ${2 * unit}px`, }, ' .MuiDialogActions-root': { padding: `0 ${2 * unit}px ${2 * unit}px ${2 * unit}px`, }, }, }, defaultProps: { disableAutoFocus: true, disableEnforceFocus: true, disableScrollLock: true, }, } } export const MuiSnackbar = () => { return { styleOverrides: { root: { '@media (min-width: 600px)': { top: `${unit * 10}px`, right: `${unit * 2}px`, }, top: `${unit * 10}px`, right: `${unit * 2}px`, width: 'auto', minHeight: `${unit * 10}px`, justifyContent: 'flex-end', alignItems: 'flex-start', pointerEvents: 'none' as any, }, }, } } export const MuiLinearProgress = ({ colorBase }: any) => { return { styleOverrides: { root: { height: '0.8rem', }, barColorPrimary: { backgroundColor: colorBase.star, borderRadius: '28px', }, determinate: { backgroundColor: colorBase.borderHover, borderRadius: '28px', }, }, } } export const MuiTooltip = ({ colorBase, }: any): { styleOverrides: ComponentsOverrides['MuiTooltip'] defaultProps?: Partial } => { return { defaultProps: { arrow: true, }, styleOverrides: { tooltip: { fontSize: fontDefault.body1, fontWeight: 400, color: colorBase.textSecondary, background: colorBase.popBg, boxShadow: colorBase.shadowHover, lineHeight: '1.5em', border: `0.5px solid ${colorBase.border}`, padding: `${unit * 2}px`, borderRadius: `${unit}px`, }, arrow: { color: colorBase.popBg, '&:before': { border: `0.5px solid ${colorBase.border}`, }, }, // root: { // fontSize: fontDefault.body1, // color: colorBase.textSecondary, // }, }, } } ================================================ FILE: packages/common-resources/static-resources/src/themes/overrides/utils.ts ================================================ import { ColorDarkDefault, ColorLightDefault } from '../css/color-lib' import { fontDefault } from '../css/global' import { ThemeKeys } from '../interface' import { IsMobile } from '../../utils' const isMobile = IsMobile.any() export const unit = isMobile ? 4 : 8 export const pxToRem = (px: any, oneRemPx = 10) => `${px / oneRemPx}rem` export const borderFunc = (themeMode: ThemeKeys) => { const colorBase = themeMode === 'dark' ? ColorDarkDefault : ColorLightDefault const borderColor = { default: colorBase.border, primary: colorBase.secondary, selected: colorBase.secondaryPressed, focus: colorBase.borderHover, } return { defaultBorder: `1px solid ${borderColor.default}`, defaultRadius: `${unit / 2}px`, FontDefault: fontDefault, defaultFrame: ({ d_W = 1, d_R = 1, c_key = 'primary', }: { d_W?: number d_R?: number c_key?: 'primary' | 'selected' | 'focus' | string }) => { let color switch (c_key) { case 'primary': case 'selected': case 'focus': color = borderColor[c_key] break default: color = c_key } return ` border: ${d_W}px solid ${color}; border-radius: ${d_R * unit}px; ` }, borderConfig: ({ d_W = 1, c_key = 'primary', }: { d_W?: number c_key?: 'primary' | 'selected' | 'focus' | string }) => { let color switch (c_key) { case 'primary': case 'selected': case 'focus': color = borderColor[c_key] break default: color = c_key } return `${d_W}px solid ${color}` }, } } ================================================ FILE: packages/common-resources/static-resources/src/utils/format_tools.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import BigNumber from 'bignumber.js' import { getValuePrecisionThousand } from './util' import { myError } from './log_tools' import { getAddress } from '@ethersproject/address' export function isAddress(value: any): string | false { try { return getAddress(value) } catch { return false } } export function getShowStr( rawVal: string | number | undefined, fixed: number = 2, precision: number = 4, ) { if (rawVal === '0' || rawVal === 0) return '0' let newVal: any = undefined if (rawVal) { newVal = typeof rawVal === 'number' ? rawVal : parseFloat(rawVal) if (newVal > 10) { newVal = newVal.toFixed(fixed) } else { newVal = sdk.toBig(newVal).toPrecision(precision) } } return newVal } export type DepthData = { amtSlice: sdk.ABInfo[] amtTotalSlice: string[] priceSlice: number[] baseDecimal: number quoteDecimal: number count: number maxVal: BigNumber precisionForPrice: number basePrecision: number } export type DepthViewData = { price: number amt: string amtForShow: string amtTotal: string amtTotalForShow: string percentage: number } function genABViewDataAccumulated({ // precisionForPrice, amtSlice, amtTotalSlice, priceSlice, baseDecimal, count, maxVal, basePrecision, }: DepthData): DepthViewData[] { if (amtTotalSlice.length < count) { const lastV = amtTotalSlice[amtTotalSlice.length - 1] amtSlice = amtSlice.concat(new Array(count - amtSlice.length).fill(lastV)) amtTotalSlice = amtTotalSlice.concat(new Array(count - amtTotalSlice.length).fill(lastV)) priceSlice = priceSlice.concat(new Array(count - priceSlice.length).fill(0)) } amtSlice = amtSlice.reverse() amtTotalSlice = amtTotalSlice.reverse() priceSlice = priceSlice.reverse() return amtTotalSlice.reduce((prv, value: string, ind: number) => { if (amtSlice[ind] && amtSlice[ind].amt) { const amt = amtSlice[ind].amt // @ts-ignore const amtForShow = getValuePrecisionThousand( sdk.toBig(amt).div('1e' + baseDecimal), undefined, undefined, basePrecision, true, { isAbbreviate: true }, ) const amtTotalForShow = getValuePrecisionThousand( sdk.toBig(value).div('1e' + baseDecimal), undefined, undefined, basePrecision, true, { isAbbreviate: true }, ) const percentage = maxVal.gt(sdk.toBig(0)) ? sdk.toBig(value).div(maxVal).toNumber() : 0 prv.push({ price: priceSlice[ind], amt, amtForShow, amtTotal: amtTotalSlice[ind], amtTotalForShow, percentage, }) } return prv // myLog('value:', value, ' maxVal:', maxVal.toString(), percentage) }, [] as DepthViewData[]) } export function depth2ViewDataAccumulated({ depth, countAsk, countBid, baseDecimal, quoteDecimal, precisionForPrice, basePrecision, }: { depth: sdk.DepthData baseDecimal: number quoteDecimal: number countAsk?: number countBid?: number maxWidth?: number basePrecision: number precisionForPrice: number }): { asks: DepthViewData[]; bids: DepthViewData[] } { if (countAsk === undefined || countBid === undefined) { countAsk = 8 countBid = 8 } let askSlice = depth.asks.slice(0, countAsk) let askTotalSlice = depth.asks_amtTotals.slice(0, countAsk) let askPriceSlice = depth.asks_prices.slice(0, countAsk) let bidSlice = countBid > 0 ? depth.bids.slice(-countBid) : [] let bidTotalSlice = countBid > 0 ? depth.bids_amtTotals.slice(-countBid) : [] let bidPriceSlice = countBid > 0 ? depth.bids_prices.slice(-countBid) : [] let maxVal = sdk.toBig(0) if (askTotalSlice.length && bidTotalSlice.length) { maxVal = BigNumber.max( sdk.toBig(askTotalSlice[askTotalSlice.length - 1]), sdk.toBig(bidTotalSlice[0]), ) } else if (askTotalSlice.length) { maxVal = sdk.toBig(askTotalSlice[askTotalSlice.length - 1]) } else if (bidTotalSlice.length) { maxVal = sdk.toBig(bidTotalSlice[0]) } else { myError('no ab input!') // throw new Error('no ab input!') } const asks = genABViewDataAccumulated({ basePrecision, precisionForPrice, amtSlice: askSlice, amtTotalSlice: askTotalSlice, priceSlice: askPriceSlice, baseDecimal, quoteDecimal, count: countAsk, maxVal, }) const bids = genABViewDataAccumulated({ basePrecision, precisionForPrice, amtSlice: bidSlice, amtTotalSlice: bidTotalSlice, priceSlice: bidPriceSlice, baseDecimal, quoteDecimal, count: countBid, maxVal, }) return { asks, bids, } } export type DepthDataNew = { amtSlice: sdk.ABInfo[] abInfoSlice: sdk.ABInfo[] amtTotalSlice: string[] priceSlice: number[] baseDecimal: number quoteDecimal: number count: number maxVal: BigNumber precisionForPrice: number basePrecision: number } function genABViewData({ // precisionForPrice, amtSlice, abInfoSlice, amtTotalSlice, priceSlice, baseDecimal, count, maxVal, basePrecision, }: DepthDataNew): DepthViewData[] { if (abInfoSlice.length < count) { const lastV = abInfoSlice[abInfoSlice.length - 1] ?? {} amtSlice = amtSlice.concat(new Array(count - amtSlice.length).fill(lastV.amt)) amtTotalSlice = amtTotalSlice.concat( new Array(count - amtTotalSlice.length).fill(lastV.amtTotal), ) abInfoSlice = abInfoSlice.concat(new Array(count - abInfoSlice.length).fill(lastV)) priceSlice = priceSlice.concat(new Array(count - priceSlice.length).fill(0)) } amtSlice = amtSlice.reverse() amtTotalSlice = amtTotalSlice.reverse() abInfoSlice = abInfoSlice.reverse() priceSlice = priceSlice.reverse() return abInfoSlice.reduce((prv, value: sdk.ABInfo, ind: number) => { if (amtSlice[ind] && amtSlice[ind].amt) { const amt = amtSlice[ind].amt const amtForShow = getValuePrecisionThousand( sdk.toBig(amt).div('1e' + baseDecimal), undefined, undefined, basePrecision, true, { isAbbreviate: true }, ) const amtTotalForShow = getValuePrecisionThousand( sdk.toBig(amtTotalSlice[ind]).div('1e' + baseDecimal), undefined, undefined, basePrecision, true, { isAbbreviate: true }, ) const percentage = maxVal.gt(sdk.toBig(0)) ? sdk.toBig(value.amt).div(maxVal).toNumber() : 0 prv.push({ price: priceSlice[ind], amt, amtForShow, amtTotal: amtTotalSlice[ind], amtTotalForShow, percentage, }) } return prv }, [] as DepthViewData[]) } function getMaxAmt(askInfoSlice: sdk.ABInfo[], bidInfoSlice: sdk.ABInfo[]) { let maxVal = sdk.toBig(0) const totalLst = askInfoSlice.concat(bidInfoSlice) totalLst.forEach((item: sdk.ABInfo) => { const newAmt = sdk.toBig(item.amt) if (newAmt.gte(maxVal)) { maxVal = newAmt } }) return maxVal } export function depth2ViewData({ depth, countAsk, countBid, baseDecimal, quoteDecimal, precisionForPrice, basePrecision, }: { depth: sdk.DepthData baseDecimal: number quoteDecimal: number countAsk?: number countBid?: number maxWidth?: number basePrecision: number precisionForPrice: number }): { asks: DepthViewData[]; bids: DepthViewData[] } { if (countAsk === undefined || countBid === undefined) { countAsk = 8 countBid = 8 } let askSlice = depth.asks.slice(0, countAsk) let askInfoSlice = depth.asks.slice(0, countAsk) let askTotalSlice = depth.asks_amtTotals.slice(0, countAsk) let askPriceSlice = depth.asks_prices.slice(0, countAsk) let bidSlice = countBid > 0 ? depth.bids.slice(-countBid) : [] let bidInfoSlice = countBid > 0 ? depth.bids.slice(-countBid) : [] let bidTotalSlice = countBid > 0 ? depth.bids_amtTotals.slice(-countBid) : [] let bidPriceSlice = countBid > 0 ? depth.bids_prices.slice(-countBid) : [] const maxVal = getMaxAmt(askInfoSlice, bidInfoSlice) const asks = countAsk > 0 ? genABViewData({ basePrecision, precisionForPrice, amtSlice: askSlice, amtTotalSlice: askTotalSlice, abInfoSlice: askInfoSlice, priceSlice: askPriceSlice, baseDecimal, quoteDecimal, count: countAsk, maxVal, }) : [] const bids = countBid > 0 ? genABViewData({ basePrecision, precisionForPrice, amtSlice: bidSlice, amtTotalSlice: bidTotalSlice, abInfoSlice: bidInfoSlice, priceSlice: bidPriceSlice, baseDecimal, quoteDecimal, count: countBid, maxVal, }) : [] return { asks, bids, } } ================================================ FILE: packages/common-resources/static-resources/src/utils/index.ts ================================================ export * from './types' export * from './util' export * from './log_tools' export * from './format_tools' export * from './obj_tools' export * from './makeDom' ================================================ FILE: packages/common-resources/static-resources/src/utils/log_tools.ts ================================================ let _myLog // @ts-ignore if (process.env.NODE_ENV !== 'production' || window?.___OhTrustDebugger___) { _myLog = console.log } else { // @ts-ignore _myLog = function (message?: any, ...optionalParams: any[]) {} } let _myError // @ts-ignore if (process.env.NODE_ENV !== 'production' || window?.___OhTrustDebugger___) { _myError = console.error } else { // @ts-ignore _myError = function (message?: any, ...optionalParams: any[]) {} } export const setMyLog = (___OhTrustDebugger___: boolean) => { if (___OhTrustDebugger___) { _myLog = console.log } } export const myLog = _myLog export const myError = _myError ================================================ FILE: packages/common-resources/static-resources/src/utils/makeDom.ts ================================================ export const htmlDecode = (input: string) => { const doc = new DOMParser().parseFromString(input, 'text/html')?.documentElement?.textContent return doc ?? '' } ================================================ FILE: packages/common-resources/static-resources/src/utils/obj_tools.ts ================================================ import { myLog } from './log_tools' export async function copyToClipBoard(text: string) { if (document.execCommand) { var textarea = document.createElement('textarea') document.body.appendChild(textarea) textarea.value = text textarea.select() document.execCommand('copy') document.body.removeChild(textarea) } if (navigator.clipboard) { await navigator.clipboard.writeText(text) } if ((window as any).clipboardData) { ;(window as any).clipboardData.setData('Text', text) myLog('clipboardData:', text) return true } return false } ================================================ FILE: packages/common-resources/static-resources/src/utils/types.tsx ================================================ type Values = T[keyof T] type Tuplize = Pick | number>> type _OneOf = Values<{ [K in keyof T]: T[K] & { [M in Values<{ [L in keyof Omit]: keyof T[L] }>]?: undefined } }> export type OneOf = _OneOf> export type Complete = { [P in keyof Required]: Pick extends Required> ? T[P] : T[P] | undefined } export type Required = { [P in keyof T]-?: T[P] } export type RequireOne = { [X in Exclude]?: T[X] } & { [P in K]-?: T[P] } // type Partial = { // [P in keyof T]?: T[P] // } ================================================ FILE: packages/common-resources/static-resources/src/utils/util.ts ================================================ import { toBig } from '@loopring-web/loopring-sdk' import BigNumber from 'bignumber.js' export const DOT = '.' export function abbreviateNumber(value: number, precision?: number) { let newValue = value, result: string const suffixes = ['', 'K', 'M', 'B', 'T', 'Qa', 'Qi'] let suffixNum = 0 while (newValue >= 1000) { newValue /= 1000 suffixNum++ } if (precision) { result = newValue.toFixed(precision) } else { result = newValue.toPrecision(3) } result += suffixes[suffixNum] return result } export const getAbbreviateNumber = (value: number | string) => { let newValue: any = value value = parseInt(toBig(value).toString()) const formattedValue = toBig(value).toNumber() if (formattedValue >= 1000) { let suffixes = ['', 'K', 'M', 'B', 'T'] let suffixNum = Math.floor(('' + formattedValue).length / 3) let shortValue: string | number = 0 for (let precision = 3; precision >= 1; precision--) { shortValue = parseFloat( (suffixNum !== 0 ? formattedValue / 1000 ** suffixNum //(Math.pow(1000, suffixNum)) : formattedValue ).toPrecision(precision), ) const dotLessShortValue = (shortValue + '').replace(/[^a-zA-Z 0-9]+/g, '') if (dotLessShortValue.length <= 3) { break } } if (shortValue && toBig(shortValue).toNumber() % 1 !== 0) { shortValue = toBig(shortValue).toNumber() } newValue = shortValue + suffixes[suffixNum] } return newValue } export const getFormattedHash = (hash?: string) => { if (!hash) return hash const firstSix = hash.slice(0, 6) const lastFour = hash.slice(hash.length - 4) return `${firstSix}****${lastFour}` } export function getShortAddr(address: string, isMobile?: boolean): string | '' { if (!address || address.trim() === '') { return '' } return (isMobile ? '0x' : address.substr(0, 6)) + '...' + address.substr(address.length - 4) } const getFloatFloor = (value: number | string | undefined, precision: number) => { if ( (!value || !Number.isFinite(Number(value)) || Number(value) === 0) && !BigNumber.isBigNumber(value) ) { return '0.00' } const result = Math.floor(Number(value) * 10 ** precision) // Math.pow(10, precision)); return result / 10 ** precision //Math.pow(10, precision); } const getFloatCeil = (value: number | string | undefined, precision: number) => { if ( (!value || !Number.isFinite(Number(value)) || Number(value) === 0) && !BigNumber.isBigNumber(value) ) { return '0.00' } let result = Math.ceil(Number(value) * 10 ** precision) // Math.pow(10, precision) return result / 10 ** precision //Math.pow(10, precision); } const addZeroAfterDot = (value: string) => { let [_init, _dot] = value.split(DOT) if (_dot) { _dot = _dot.replace(/0+?$/, '') if (_dot) { value = _init + DOT + _dot } else { value = _init } return value } return value } /** * @param value * @param minDigit default = 6 * @param precision default = 2 * @param fixed default = undefined * @param notRemoveEndZero default will remove after dot end 0 * @param option { floor?: boolean, isFait?: boolean, isTrade?: boolean } */ export const getValuePrecisionThousand = ( value: number | string | BigNumber | undefined | BigNumber, minDigit = 6, precision = 2, fixed?: number, notRemoveEndZero?: boolean, option?: { floor?: boolean isFait?: boolean isTrade?: boolean isExponential?: boolean isPrice?: boolean abbreviate?: 3 | 6 | 9 | 12 | 15 | 18 isAbbreviate?: boolean }, ) => { const floor = option?.floor const isFait = option?.isFait const isTrade = option?.isTrade const isExponential = option?.isExponential const isPrice = option?.isPrice const isAbbreviate = option?.isAbbreviate const abbreviate = option?.abbreviate ?? 6 if ( (!value || !Number.isFinite(Number(value)) || Number(value) === 0) && !BigNumber.isBigNumber(value) ) { return '0.00' } let result: any = value if (!BigNumber.isBigNumber(result)) { result = toBig(value) } // integer part exceed 6 digits abbreaviate, otherwise toLocaleString if (isAbbreviate === true) { let [_init, _dot] = result.toString().split(DOT) const integerPartLength = _init.length if (integerPartLength > abbreviate) { // return getAbbreviateNumber(result) return abbreviateNumber(result.toString()) } } // remove exponential if (isExponential === true) { result = toBig(toBig(value).toFixed(20)) } if (isPrice === true) { return toBig(toBig(result).toFixed(fixed || 6)) .toNumber() .toLocaleString('en-US', { minimumFractionDigits: fixed || 6 }) } // fait price if (isFait === true) { if (toBig(result).isGreaterThanOrEqualTo(1)) { if (floor === true) { result = getFloatFloor(result, 2) } if (floor === false) { result = getFloatCeil(result, 2) } // fixed 2 decimals return toBig(result.toFixed(2)) .toNumber() .toLocaleString('en-US', { minimumFractionDigits: 2 }) } else { if (floor === true) { result = toBig(result).sd(minDigit ?? 6, BigNumber.ROUND_DOWN) //getFloatFloor(result, precision ?? 6) } if (floor === false) { result = toBig(result).sd(minDigit ?? 6, BigNumber.ROUND_CEIL) // result = getFloatCeil(result, precision ?? 6) } return toBig(result) .toNumber() .toLocaleString('en-US', { minimumFractionDigits: notRemoveEndZero ? 6 : undefined }) } } if (isTrade === true) { let [_init, _dot] = result.toString().split('.') if (_dot && _dot.length > 3) { return result.toNumber().toLocaleString('en-US', { minimumFractionDigits: _dot.length }) } else { return result.toNumber().toLocaleString('en-US') } } if (result.isGreaterThan(1)) { let formattedValue: any = null if (floor === true) { formattedValue = getFloatFloor(result, fixed || minDigit) } if (floor === false) { formattedValue = getFloatCeil(result, fixed || minDigit) } if (floor === undefined) { formattedValue = result.toFixed(fixed || minDigit) } // remain string-number zero result = toBig(formattedValue) .toNumber() .toLocaleString('en-US', { minimumFractionDigits: fixed || minDigit }) } else if (result.isLessThanOrEqualTo(1)) { if (floor === false) { result = getFloatCeil(result, fixed || precision).toString() } else { result = fixed ? result.toFixed(fixed) : precision ? toBig(result).toPrecision(precision) : toBig(result).toFixed(0) } } if (result && !notRemoveEndZero) { result = addZeroAfterDot(result) } if (BigNumber.isBigNumber(result) ) { result = result.toString() } return result } export const IsMobile = { Android: function () { return navigator.userAgent.match(/Android/i) }, BlackBerry: function () { return navigator.userAgent.match(/BlackBerry/i) }, iOS: function () { return navigator.userAgent.match(/iPhone|iPad|iPod/i) }, Opera: function () { return navigator.userAgent.match(/Opera Mini/i) }, Windows: function () { return navigator.userAgent.match(/IEMobile/i) || navigator.userAgent.match(/WPDesktop/i) }, Ethereum: function () { // @ts-ignore return window?.ethereum && window?.ethereum.isImToken }, any: function () { return ( IsMobile.Android() || IsMobile.BlackBerry() || IsMobile.iOS() || IsMobile.Opera() || IsMobile.Windows() || IsMobile.Ethereum() ) }, } export const IsWhichWebView = { any: function () { // @ts-ignore if (window?.ethereum.isImToken) { return 'isImToken' } // @ts-ignore if (window?.ethereum.isMetaMask) { return 'isMetaMask' } }, } export function type(value: any) { var matches = Object.prototype.toString.call(value).match(/^\[object (\w+?)\]$/) || [] return (matches[1] || 'undefined').toLowerCase() } ================================================ FILE: packages/common-resources/static-resources/types.d.ts ================================================ /// declare global { interface Window { __ChainIdExtends: any __MapChainId: any } } ================================================ FILE: packages/common-resources/tsconfig.json ================================================ { "extends": "../../tsconfig.build.json", "compilerOptions": { "jsx": "react-jsx", "noUnusedLocals": true, "resolveJsonModule": true, "noUnusedParameters": true, "baseUrl": "./static-resources/src", "rootDir": "./static-resources" }, "include": [ "./static-resources/src", "./static-resources/src/**/*.json", "./static-resources/src/*" ], "exclude": [ "node_modules", "build", "dist", "example", "rollup.config.js" ] } ================================================ FILE: packages/common-resources/tsconfig.tsbuildinfo ================================================ {"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/typescript/lib/lib.esnext.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/@types/react/global.d.ts","../../node_modules/csstype/index.d.ts","../../node_modules/@types/prop-types/index.d.ts","../../node_modules/@types/scheduler/tracing.d.ts","../../node_modules/@types/react/index.d.ts","../../node_modules/@types/react/jsx-runtime.d.ts","../../node_modules/i18next/index.d.ts","../../node_modules/react-i18next/ts4.1/index.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/utils/network_tools.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/utils/symbol_tools.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/querystring/decode.d.ts","../../node_modules/querystring/encode.d.ts","../../node_modules/querystring/index.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/globals.global.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/@types/bn.js/index.d.ts","../../node_modules/bignumber.js/bignumber.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/web3_defs.d.ts","../../node_modules/web3-bzz/types/index.d.ts","../../node_modules/web3-core-helpers/types/index.d.ts","../../node_modules/web3-core-method/types/index.d.ts","../../node_modules/web3-core/types/index.d.ts","../../node_modules/web3-core-subscriptions/types/index.d.ts","../../node_modules/web3-utils/types/index.d.ts","../../node_modules/web3-eth-abi/types/index.d.ts","../../node_modules/web3-eth-accounts/types/index.d.ts","../../node_modules/web3-eth-contract/types/index.d.ts","../../node_modules/web3-eth-ens/types/index.d.ts","../../node_modules/web3-eth-iban/types/index.d.ts","../../node_modules/web3-eth-personal/types/index.d.ts","../../node_modules/web3-net/types/index.d.ts","../../node_modules/web3-eth/types/index.d.ts","../../node_modules/web3-shh/types/index.d.ts","../../node_modules/web3/types/index.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/loopring_enums.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/error_codes.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/loopring_constants.d.ts","../../node_modules/axios/index.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/request.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/base_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/ws_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/exchange_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/ammpool_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/sign/sign_tools.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/user_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/wallet_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/whitelisted_user_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/contract_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/nft_defs.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/nft_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/global_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/delegate_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/defi_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/lucktoken_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/contacts_api.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/api/index.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/loopring_defs.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/account_defs.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/ws_defs.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/url_defs.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/defs/index.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/utils/formatter.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/utils/swap_calc_utils.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/utils/index.d.ts","../../node_modules/@loopring-web/loopring-sdk/dist/index.d.ts","../../node_modules/@mui/types/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/createtheme/createbreakpoints.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/createtheme/shape.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/createtheme/createspacing.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/createtheme/createtheme.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/createtheme/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/stylefunctionsx/standardcssproperties.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/stylefunctionsx/aliasescssproperties.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/stylefunctionsx/overwritecssproperties.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/stylefunctionsx/stylefunctionsx.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/stylefunctionsx/extendsxprop.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/stylefunctionsx/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/box/box.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/box/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/private-theming/defaulttheme/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/private-theming/themeprovider/themeprovider.d.ts","../../node_modules/@mui/material/node_modules/@mui/private-theming/themeprovider/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/private-theming/usetheme/usetheme.d.ts","../../node_modules/@mui/material/node_modules/@mui/private-theming/usetheme/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/private-theming/index.d.ts","../../node_modules/@emotion/utils/types/index.d.ts","../../node_modules/@emotion/cache/types/index.d.ts","../../node_modules/@emotion/serialize/types/index.d.ts","../../node_modules/@emotion/react/types/jsx-namespace.d.ts","../../node_modules/@emotion/react/types/helper.d.ts","../../node_modules/@emotion/react/types/theming.d.ts","../../node_modules/@emotion/react/types/index.d.ts","../../node_modules/@emotion/styled/types/base.d.ts","../../node_modules/@emotion/styled/types/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/styled-engine/styledengineprovider/styledengineprovider.d.ts","../../node_modules/@mui/material/node_modules/@mui/styled-engine/styledengineprovider/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/styled-engine/globalstyles/globalstyles.d.ts","../../node_modules/@mui/material/node_modules/@mui/styled-engine/globalstyles/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/styled-engine/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/style.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/spacing.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/sx/sx.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/sx/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/createbox.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/createstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/styled.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/usethemeprops/usethemeprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/usethemeprops/getthemeprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/usethemeprops/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/usetheme.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/usethemewithoutdefault.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/colormanipulator.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/themeprovider/themeprovider.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/themeprovider/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/cssvars/getinitcolorschemescript.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/cssvars/usecurrentcolorscheme.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/cssvars/createcssvarsprovider.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/cssvars/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/cssvars/creategetcssvar.d.ts","../../node_modules/@mui/material/node_modules/@mui/system/index.d.ts","../../node_modules/@mui/material/styles/createmixins.d.ts","../../node_modules/@mui/material/styles/createpalette.d.ts","../../node_modules/@mui/material/styles/createtypography.d.ts","../../node_modules/@mui/material/styles/shadows.d.ts","../../node_modules/@mui/material/styles/createtransitions.d.ts","../../node_modules/@mui/material/styles/zindex.d.ts","../../node_modules/@mui/material/overridablecomponent.d.ts","../../node_modules/@mui/material/paper/paperclasses.d.ts","../../node_modules/@mui/material/paper/paper.d.ts","../../node_modules/@mui/material/paper/index.d.ts","../../node_modules/@mui/material/alert/alertclasses.d.ts","../../node_modules/@mui/material/alert/alert.d.ts","../../node_modules/@mui/material/alert/index.d.ts","../../node_modules/@mui/material/alerttitle/alerttitleclasses.d.ts","../../node_modules/@mui/material/alerttitle/alerttitle.d.ts","../../node_modules/@mui/material/alerttitle/index.d.ts","../../node_modules/@mui/material/appbar/appbarclasses.d.ts","../../node_modules/@mui/material/appbar/appbar.d.ts","../../node_modules/@mui/material/appbar/index.d.ts","../../node_modules/@mui/material/chip/chipclasses.d.ts","../../node_modules/@mui/material/chip/chip.d.ts","../../node_modules/@mui/material/chip/index.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/enums.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/popperoffsets.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/flip.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/hide.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/offset.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/eventlisteners.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/computestyles.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/arrow.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/preventoverflow.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/applystyles.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/types.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/modifiers/index.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/utils/detectoverflow.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/createpopper.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/popper-lite.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/popper.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/lib/index.d.ts","../../node_modules/@mui/material/node_modules/@popperjs/core/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/portal/portal.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/portal/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/popperunstyled/popperunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/popperunstyled/index.d.ts","../../node_modules/@mui/material/popper/popper.d.ts","../../node_modules/@mui/material/popper/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/utils/appendownerstate.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/utils/arearraysequal.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/utils/types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/utils/extracteventhandlers.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/utils/ishostcomponent.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/utils/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/autocompleteunstyled/useautocomplete.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/autocompleteunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/badgeunstyled/badgeunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/badgeunstyled/badgeunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/badgeunstyled/usebadge.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/badgeunstyled/badgeunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/badgeunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/buttonunstyled/usebutton.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/buttonunstyled/buttonunstyled.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/buttonunstyled/buttonunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/buttonunstyled/buttonunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/buttonunstyled/usebutton.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/buttonunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/clickawaylistener/clickawaylistener.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/clickawaylistener/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/composeclasses/composeclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/composeclasses/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/generateutilityclass/generateutilityclass.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/generateutilityclass/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/generateutilityclasses/generateutilityclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/generateutilityclasses/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/formcontrolunstyled/formcontrolunstyled.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/formcontrolunstyled/formcontrolunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/formcontrolunstyled/formcontrolunstyledcontext.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/formcontrolunstyled/formcontrolunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/formcontrolunstyled/useformcontrolunstyledcontext.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/formcontrolunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/inputunstyled/inputunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/inputunstyled/inputunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/inputunstyled/useinput.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/inputunstyled/inputunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/inputunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/listboxunstyled/uselistbox.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/listboxunstyled/uselistbox.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/listboxunstyled/defaultlistboxreducer.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/listboxunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuunstyled/usemenu.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuunstyled/menuunstyled.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuunstyled/menuunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuunstyled/menuunstyledcontext.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuunstyled/menuunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuunstyled/usemenu.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuitemunstyled/menuitemunstyled.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuitemunstyled/menuitemunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuitemunstyled/menuitemunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuitemunstyled/usemenuitem.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuitemunstyled/usemenuitem.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/menuitemunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/modalunstyled/modalunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/modalunstyled/modalunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/modalunstyled/modalmanager.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/modalunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/selectunstyled/useselect.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/selectunstyled/selectunstyled.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/selectunstyled/selectunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/selectunstyled/selectunstyledcontext.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/selectunstyled/selectunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/selectunstyled/useselect.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/selectunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/multiselectunstyled/multiselectunstyled.types.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/multiselectunstyled/multiselectunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/multiselectunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/nossr/nossr.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/nossr/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optiongroupunstyled/optiongroupunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optiongroupunstyled/optiongroupunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optiongroupunstyled/optiongroupunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optiongroupunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optionunstyled/optionunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optionunstyled/optionunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optionunstyled/optionunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/optionunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/sliderunstyled/sliderunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/sliderunstyled/slidervaluelabelunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/sliderunstyled/sliderunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/sliderunstyled/sliderunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/sliderunstyled/useslider.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/sliderunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/switchunstyled/useswitch.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/switchunstyled/switchunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/switchunstyled/switchunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/switchunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabpanelunstyled/tabpanelunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabpanelunstyled/tabpanelunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabpanelunstyled/tabpanelunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabpanelunstyled/usetabpanel.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabpanelunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabslistunstyled/tabslistunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabslistunstyled/tabslistunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabslistunstyled/tabslistunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabslistunstyled/usetabslist.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabslistunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabsunstyled/tabsunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabsunstyled/tabsunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabsunstyled/tabscontext.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabsunstyled/tabsunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabsunstyled/usetabs.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabsunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabunstyled/tabunstyledprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabunstyled/tabunstyled.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabunstyled/tabunstyledclasses.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabunstyled/usetab.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/tabunstyled/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/textareaautosize/textareaautosize.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/textareaautosize/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/trapfocus/trapfocus.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/trapfocus/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/index.d.ts","../../node_modules/@mui/material/autocomplete/autocompleteclasses.d.ts","../../node_modules/@mui/material/autocomplete/autocomplete.d.ts","../../node_modules/@mui/material/autocomplete/index.d.ts","../../node_modules/@mui/material/avatar/avatarclasses.d.ts","../../node_modules/@mui/material/avatar/avatar.d.ts","../../node_modules/@mui/material/avatar/index.d.ts","../../node_modules/@mui/material/avatargroup/avatargroupclasses.d.ts","../../node_modules/@mui/material/avatargroup/avatargroup.d.ts","../../node_modules/@mui/material/avatargroup/index.d.ts","../../node_modules/@types/react-transition-group/transition.d.ts","../../node_modules/@mui/material/transitions/transition.d.ts","../../node_modules/@mui/material/fade/fade.d.ts","../../node_modules/@mui/material/fade/index.d.ts","../../node_modules/@mui/material/backdrop/backdropclasses.d.ts","../../node_modules/@mui/material/backdrop/backdrop.d.ts","../../node_modules/@mui/material/backdrop/index.d.ts","../../node_modules/@mui/material/badge/badgeclasses.d.ts","../../node_modules/@mui/material/badge/badge.d.ts","../../node_modules/@mui/material/badge/index.d.ts","../../node_modules/@mui/material/buttonbase/touchrippleclasses.d.ts","../../node_modules/@mui/material/buttonbase/touchripple.d.ts","../../node_modules/@mui/material/buttonbase/buttonbaseclasses.d.ts","../../node_modules/@mui/material/buttonbase/buttonbase.d.ts","../../node_modules/@mui/material/buttonbase/index.d.ts","../../node_modules/@mui/material/bottomnavigationaction/bottomnavigationactionclasses.d.ts","../../node_modules/@mui/material/bottomnavigationaction/bottomnavigationaction.d.ts","../../node_modules/@mui/material/bottomnavigationaction/index.d.ts","../../node_modules/@mui/material/bottomnavigation/bottomnavigationclasses.d.ts","../../node_modules/@mui/material/bottomnavigation/bottomnavigation.d.ts","../../node_modules/@mui/material/bottomnavigation/index.d.ts","../../node_modules/@mui/material/breadcrumbs/breadcrumbsclasses.d.ts","../../node_modules/@mui/material/breadcrumbs/breadcrumbs.d.ts","../../node_modules/@mui/material/breadcrumbs/index.d.ts","../../node_modules/@mui/material/buttongroup/buttongroupclasses.d.ts","../../node_modules/@mui/material/buttongroup/buttongroup.d.ts","../../node_modules/@mui/material/buttongroup/index.d.ts","../../node_modules/@mui/material/button/buttonclasses.d.ts","../../node_modules/@mui/material/button/button.d.ts","../../node_modules/@mui/material/button/index.d.ts","../../node_modules/@mui/material/cardactionarea/cardactionareaclasses.d.ts","../../node_modules/@mui/material/cardactionarea/cardactionarea.d.ts","../../node_modules/@mui/material/cardactionarea/index.d.ts","../../node_modules/@mui/material/cardactions/cardactionsclasses.d.ts","../../node_modules/@mui/material/cardactions/cardactions.d.ts","../../node_modules/@mui/material/cardactions/index.d.ts","../../node_modules/@mui/material/cardcontent/cardcontentclasses.d.ts","../../node_modules/@mui/material/cardcontent/cardcontent.d.ts","../../node_modules/@mui/material/cardcontent/index.d.ts","../../node_modules/@mui/material/typography/typographyclasses.d.ts","../../node_modules/@mui/material/typography/typography.d.ts","../../node_modules/@mui/material/typography/index.d.ts","../../node_modules/@mui/material/cardheader/cardheaderclasses.d.ts","../../node_modules/@mui/material/cardheader/cardheader.d.ts","../../node_modules/@mui/material/cardheader/index.d.ts","../../node_modules/@mui/material/cardmedia/cardmediaclasses.d.ts","../../node_modules/@mui/material/cardmedia/cardmedia.d.ts","../../node_modules/@mui/material/cardmedia/index.d.ts","../../node_modules/@mui/material/card/cardclasses.d.ts","../../node_modules/@mui/material/card/card.d.ts","../../node_modules/@mui/material/card/index.d.ts","../../node_modules/@mui/material/internal/switchbaseclasses.d.ts","../../node_modules/@mui/material/internal/switchbase.d.ts","../../node_modules/@mui/material/checkbox/checkboxclasses.d.ts","../../node_modules/@mui/material/checkbox/checkbox.d.ts","../../node_modules/@mui/material/checkbox/index.d.ts","../../node_modules/@mui/material/circularprogress/circularprogressclasses.d.ts","../../node_modules/@mui/material/circularprogress/circularprogress.d.ts","../../node_modules/@mui/material/circularprogress/index.d.ts","../../node_modules/@mui/material/collapse/collapseclasses.d.ts","../../node_modules/@mui/material/collapse/collapse.d.ts","../../node_modules/@mui/material/collapse/index.d.ts","../../node_modules/@mui/material/container/containerclasses.d.ts","../../node_modules/@mui/material/container/container.d.ts","../../node_modules/@mui/material/container/index.d.ts","../../node_modules/@mui/material/cssbaseline/cssbaseline.d.ts","../../node_modules/@mui/material/cssbaseline/index.d.ts","../../node_modules/@mui/material/dialogactions/dialogactionsclasses.d.ts","../../node_modules/@mui/material/dialogactions/dialogactions.d.ts","../../node_modules/@mui/material/dialogactions/index.d.ts","../../node_modules/@mui/material/dialogcontent/dialogcontentclasses.d.ts","../../node_modules/@mui/material/dialogcontent/dialogcontent.d.ts","../../node_modules/@mui/material/dialogcontent/index.d.ts","../../node_modules/@mui/material/dialogcontenttext/dialogcontenttextclasses.d.ts","../../node_modules/@mui/material/dialogcontenttext/dialogcontenttext.d.ts","../../node_modules/@mui/material/dialogcontenttext/index.d.ts","../../node_modules/@mui/material/modal/modal.d.ts","../../node_modules/@mui/material/modal/index.d.ts","../../node_modules/@mui/material/dialog/dialogclasses.d.ts","../../node_modules/@mui/material/dialog/dialog.d.ts","../../node_modules/@mui/material/dialog/index.d.ts","../../node_modules/@mui/material/dialogtitle/dialogtitleclasses.d.ts","../../node_modules/@mui/material/dialogtitle/dialogtitle.d.ts","../../node_modules/@mui/material/dialogtitle/index.d.ts","../../node_modules/@mui/material/divider/dividerclasses.d.ts","../../node_modules/@mui/material/divider/divider.d.ts","../../node_modules/@mui/material/divider/index.d.ts","../../node_modules/@mui/material/slide/slide.d.ts","../../node_modules/@mui/material/slide/index.d.ts","../../node_modules/@mui/material/drawer/drawerclasses.d.ts","../../node_modules/@mui/material/drawer/drawer.d.ts","../../node_modules/@mui/material/drawer/index.d.ts","../../node_modules/@mui/material/accordionactions/accordionactionsclasses.d.ts","../../node_modules/@mui/material/accordionactions/accordionactions.d.ts","../../node_modules/@mui/material/accordionactions/index.d.ts","../../node_modules/@mui/material/accordiondetails/accordiondetailsclasses.d.ts","../../node_modules/@mui/material/accordiondetails/accordiondetails.d.ts","../../node_modules/@mui/material/accordiondetails/index.d.ts","../../node_modules/@mui/material/accordion/accordionclasses.d.ts","../../node_modules/@mui/material/accordion/accordion.d.ts","../../node_modules/@mui/material/accordion/index.d.ts","../../node_modules/@mui/material/accordionsummary/accordionsummaryclasses.d.ts","../../node_modules/@mui/material/accordionsummary/accordionsummary.d.ts","../../node_modules/@mui/material/accordionsummary/index.d.ts","../../node_modules/@mui/material/fab/fabclasses.d.ts","../../node_modules/@mui/material/fab/fab.d.ts","../../node_modules/@mui/material/fab/index.d.ts","../../node_modules/@mui/material/inputbase/inputbaseclasses.d.ts","../../node_modules/@mui/material/inputbase/inputbase.d.ts","../../node_modules/@mui/material/inputbase/index.d.ts","../../node_modules/@mui/material/filledinput/filledinputclasses.d.ts","../../node_modules/@mui/material/filledinput/filledinput.d.ts","../../node_modules/@mui/material/filledinput/index.d.ts","../../node_modules/@mui/material/formcontrollabel/formcontrollabelclasses.d.ts","../../node_modules/@mui/material/formcontrollabel/formcontrollabel.d.ts","../../node_modules/@mui/material/formcontrollabel/index.d.ts","../../node_modules/@mui/material/formcontrol/formcontrolclasses.d.ts","../../node_modules/@mui/material/formcontrol/formcontrol.d.ts","../../node_modules/@mui/material/formcontrol/useformcontrol.d.ts","../../node_modules/@mui/material/formcontrol/index.d.ts","../../node_modules/@mui/material/formgroup/formgroupclasses.d.ts","../../node_modules/@mui/material/formgroup/formgroup.d.ts","../../node_modules/@mui/material/formgroup/index.d.ts","../../node_modules/@mui/material/formhelpertext/formhelpertextclasses.d.ts","../../node_modules/@mui/material/formhelpertext/formhelpertext.d.ts","../../node_modules/@mui/material/formhelpertext/index.d.ts","../../node_modules/@mui/material/formlabel/formlabelclasses.d.ts","../../node_modules/@mui/material/formlabel/formlabel.d.ts","../../node_modules/@mui/material/formlabel/index.d.ts","../../node_modules/@mui/material/grid/gridclasses.d.ts","../../node_modules/@mui/material/grid/grid.d.ts","../../node_modules/@mui/material/grid/index.d.ts","../../node_modules/@mui/material/iconbutton/iconbuttonclasses.d.ts","../../node_modules/@mui/material/iconbutton/iconbutton.d.ts","../../node_modules/@mui/material/iconbutton/index.d.ts","../../node_modules/@mui/material/icon/iconclasses.d.ts","../../node_modules/@mui/material/icon/icon.d.ts","../../node_modules/@mui/material/icon/index.d.ts","../../node_modules/@mui/material/imagelist/imagelistclasses.d.ts","../../node_modules/@mui/material/imagelist/imagelist.d.ts","../../node_modules/@mui/material/imagelist/index.d.ts","../../node_modules/@mui/material/imagelistitembar/imagelistitembarclasses.d.ts","../../node_modules/@mui/material/imagelistitembar/imagelistitembar.d.ts","../../node_modules/@mui/material/imagelistitembar/index.d.ts","../../node_modules/@mui/material/imagelistitem/imagelistitemclasses.d.ts","../../node_modules/@mui/material/imagelistitem/imagelistitem.d.ts","../../node_modules/@mui/material/imagelistitem/index.d.ts","../../node_modules/@mui/material/inputadornment/inputadornmentclasses.d.ts","../../node_modules/@mui/material/inputadornment/inputadornment.d.ts","../../node_modules/@mui/material/inputadornment/index.d.ts","../../node_modules/@mui/material/inputlabel/inputlabelclasses.d.ts","../../node_modules/@mui/material/inputlabel/inputlabel.d.ts","../../node_modules/@mui/material/inputlabel/index.d.ts","../../node_modules/@mui/material/input/inputclasses.d.ts","../../node_modules/@mui/material/input/input.d.ts","../../node_modules/@mui/material/input/index.d.ts","../../node_modules/@mui/material/linearprogress/linearprogressclasses.d.ts","../../node_modules/@mui/material/linearprogress/linearprogress.d.ts","../../node_modules/@mui/material/linearprogress/index.d.ts","../../node_modules/@mui/material/link/linkclasses.d.ts","../../node_modules/@mui/material/link/link.d.ts","../../node_modules/@mui/material/link/index.d.ts","../../node_modules/@mui/material/listitemavatar/listitemavatarclasses.d.ts","../../node_modules/@mui/material/listitemavatar/listitemavatar.d.ts","../../node_modules/@mui/material/listitemavatar/index.d.ts","../../node_modules/@mui/material/listitemicon/listitemiconclasses.d.ts","../../node_modules/@mui/material/listitemicon/listitemicon.d.ts","../../node_modules/@mui/material/listitemicon/index.d.ts","../../node_modules/@mui/material/listitem/listitemclasses.d.ts","../../node_modules/@mui/material/listitem/listitem.d.ts","../../node_modules/@mui/material/listitem/index.d.ts","../../node_modules/@mui/material/listitembutton/listitembuttonclasses.d.ts","../../node_modules/@mui/material/listitembutton/listitembutton.d.ts","../../node_modules/@mui/material/listitembutton/index.d.ts","../../node_modules/@mui/material/listitemsecondaryaction/listitemsecondaryactionclasses.d.ts","../../node_modules/@mui/material/listitemsecondaryaction/listitemsecondaryaction.d.ts","../../node_modules/@mui/material/listitemsecondaryaction/index.d.ts","../../node_modules/@mui/material/listitemtext/listitemtextclasses.d.ts","../../node_modules/@mui/material/listitemtext/listitemtext.d.ts","../../node_modules/@mui/material/listitemtext/index.d.ts","../../node_modules/@mui/material/list/listclasses.d.ts","../../node_modules/@mui/material/list/list.d.ts","../../node_modules/@mui/material/list/index.d.ts","../../node_modules/@mui/material/listsubheader/listsubheaderclasses.d.ts","../../node_modules/@mui/material/listsubheader/listsubheader.d.ts","../../node_modules/@mui/material/listsubheader/index.d.ts","../../node_modules/@mui/material/menuitem/menuitemclasses.d.ts","../../node_modules/@mui/material/menuitem/menuitem.d.ts","../../node_modules/@mui/material/menuitem/index.d.ts","../../node_modules/@mui/material/menulist/menulist.d.ts","../../node_modules/@mui/material/menulist/index.d.ts","../../node_modules/@mui/material/popover/popoverclasses.d.ts","../../node_modules/@mui/material/popover/popover.d.ts","../../node_modules/@mui/material/popover/index.d.ts","../../node_modules/@mui/material/menu/menuclasses.d.ts","../../node_modules/@mui/material/menu/menu.d.ts","../../node_modules/@mui/material/menu/index.d.ts","../../node_modules/@mui/material/mobilestepper/mobilestepperclasses.d.ts","../../node_modules/@mui/material/mobilestepper/mobilestepper.d.ts","../../node_modules/@mui/material/mobilestepper/index.d.ts","../../node_modules/@mui/material/nativeselect/nativeselectinput.d.ts","../../node_modules/@mui/material/nativeselect/nativeselectclasses.d.ts","../../node_modules/@mui/material/nativeselect/nativeselect.d.ts","../../node_modules/@mui/material/nativeselect/index.d.ts","../../node_modules/@mui/material/usemediaquery/usemediaquery.d.ts","../../node_modules/@mui/material/usemediaquery/index.d.ts","../../node_modules/@mui/material/outlinedinput/outlinedinputclasses.d.ts","../../node_modules/@mui/material/outlinedinput/outlinedinput.d.ts","../../node_modules/@mui/material/outlinedinput/index.d.ts","../../node_modules/@mui/material/usepagination/usepagination.d.ts","../../node_modules/@mui/material/pagination/paginationclasses.d.ts","../../node_modules/@mui/material/pagination/pagination.d.ts","../../node_modules/@mui/material/pagination/index.d.ts","../../node_modules/@mui/material/paginationitem/paginationitemclasses.d.ts","../../node_modules/@mui/material/paginationitem/paginationitem.d.ts","../../node_modules/@mui/material/paginationitem/index.d.ts","../../node_modules/@mui/material/radiogroup/radiogroup.d.ts","../../node_modules/@mui/material/radiogroup/radiogroupcontext.d.ts","../../node_modules/@mui/material/radiogroup/useradiogroup.d.ts","../../node_modules/@mui/material/radiogroup/index.d.ts","../../node_modules/@mui/material/radio/radioclasses.d.ts","../../node_modules/@mui/material/radio/radio.d.ts","../../node_modules/@mui/material/radio/index.d.ts","../../node_modules/@mui/material/rating/ratingclasses.d.ts","../../node_modules/@mui/material/rating/rating.d.ts","../../node_modules/@mui/material/rating/index.d.ts","../../node_modules/@mui/material/scopedcssbaseline/scopedcssbaselineclasses.d.ts","../../node_modules/@mui/material/scopedcssbaseline/scopedcssbaseline.d.ts","../../node_modules/@mui/material/scopedcssbaseline/index.d.ts","../../node_modules/@mui/material/select/selectinput.d.ts","../../node_modules/@mui/material/select/selectclasses.d.ts","../../node_modules/@mui/material/select/select.d.ts","../../node_modules/@mui/material/select/index.d.ts","../../node_modules/@mui/material/skeleton/skeletonclasses.d.ts","../../node_modules/@mui/material/skeleton/skeleton.d.ts","../../node_modules/@mui/material/skeleton/index.d.ts","../../node_modules/@mui/material/slider/slider.d.ts","../../node_modules/@mui/material/slider/index.d.ts","../../node_modules/@mui/material/snackbarcontent/snackbarcontentclasses.d.ts","../../node_modules/@mui/material/snackbarcontent/snackbarcontent.d.ts","../../node_modules/@mui/material/snackbarcontent/index.d.ts","../../node_modules/@mui/material/snackbar/snackbarclasses.d.ts","../../node_modules/@mui/material/snackbar/snackbar.d.ts","../../node_modules/@mui/material/snackbar/index.d.ts","../../node_modules/@mui/material/transitions/index.d.ts","../../node_modules/@mui/material/speeddial/speeddialclasses.d.ts","../../node_modules/@mui/material/speeddial/speeddial.d.ts","../../node_modules/@mui/material/speeddial/index.d.ts","../../node_modules/@mui/material/tooltip/tooltipclasses.d.ts","../../node_modules/@mui/material/tooltip/tooltip.d.ts","../../node_modules/@mui/material/tooltip/index.d.ts","../../node_modules/@mui/material/speeddialaction/speeddialactionclasses.d.ts","../../node_modules/@mui/material/speeddialaction/speeddialaction.d.ts","../../node_modules/@mui/material/speeddialaction/index.d.ts","../../node_modules/@mui/material/speeddialicon/speeddialiconclasses.d.ts","../../node_modules/@mui/material/speeddialicon/speeddialicon.d.ts","../../node_modules/@mui/material/speeddialicon/index.d.ts","../../node_modules/@mui/material/stack/stack.d.ts","../../node_modules/@mui/material/stack/index.d.ts","../../node_modules/@mui/material/stepbutton/stepbuttonclasses.d.ts","../../node_modules/@mui/material/stepbutton/stepbutton.d.ts","../../node_modules/@mui/material/stepbutton/index.d.ts","../../node_modules/@mui/material/stepconnector/stepconnectorclasses.d.ts","../../node_modules/@mui/material/stepconnector/stepconnector.d.ts","../../node_modules/@mui/material/stepconnector/index.d.ts","../../node_modules/@mui/material/stepcontent/stepcontentclasses.d.ts","../../node_modules/@mui/material/stepcontent/stepcontent.d.ts","../../node_modules/@mui/material/stepcontent/index.d.ts","../../node_modules/@mui/material/stepicon/stepiconclasses.d.ts","../../node_modules/@mui/material/stepicon/stepicon.d.ts","../../node_modules/@mui/material/stepicon/index.d.ts","../../node_modules/@mui/material/steplabel/steplabelclasses.d.ts","../../node_modules/@mui/material/steplabel/steplabel.d.ts","../../node_modules/@mui/material/steplabel/index.d.ts","../../node_modules/@mui/material/stepper/stepperclasses.d.ts","../../node_modules/@mui/material/stepper/stepper.d.ts","../../node_modules/@mui/material/stepper/index.d.ts","../../node_modules/@mui/material/step/stepclasses.d.ts","../../node_modules/@mui/material/step/step.d.ts","../../node_modules/@mui/material/step/stepcontext.d.ts","../../node_modules/@mui/material/step/index.d.ts","../../node_modules/@mui/material/svgicon/svgiconclasses.d.ts","../../node_modules/@mui/material/svgicon/svgicon.d.ts","../../node_modules/@mui/material/svgicon/index.d.ts","../../node_modules/@mui/material/swipeabledrawer/swipeabledrawer.d.ts","../../node_modules/@mui/material/swipeabledrawer/index.d.ts","../../node_modules/@mui/material/switch/switchclasses.d.ts","../../node_modules/@mui/material/switch/switch.d.ts","../../node_modules/@mui/material/switch/index.d.ts","../../node_modules/@mui/material/tablebody/tablebodyclasses.d.ts","../../node_modules/@mui/material/tablebody/tablebody.d.ts","../../node_modules/@mui/material/tablebody/index.d.ts","../../node_modules/@mui/material/tablecell/tablecellclasses.d.ts","../../node_modules/@mui/material/tablecell/tablecell.d.ts","../../node_modules/@mui/material/tablecell/index.d.ts","../../node_modules/@mui/material/tablecontainer/tablecontainerclasses.d.ts","../../node_modules/@mui/material/tablecontainer/tablecontainer.d.ts","../../node_modules/@mui/material/tablecontainer/index.d.ts","../../node_modules/@mui/material/tablehead/tableheadclasses.d.ts","../../node_modules/@mui/material/tablehead/tablehead.d.ts","../../node_modules/@mui/material/tablehead/index.d.ts","../../node_modules/@mui/material/tablepagination/tablepaginationactions.d.ts","../../node_modules/@mui/material/tablepagination/tablepaginationclasses.d.ts","../../node_modules/@mui/material/tablepagination/tablepagination.d.ts","../../node_modules/@mui/material/tablepagination/index.d.ts","../../node_modules/@mui/material/table/tableclasses.d.ts","../../node_modules/@mui/material/table/table.d.ts","../../node_modules/@mui/material/table/index.d.ts","../../node_modules/@mui/material/tablerow/tablerowclasses.d.ts","../../node_modules/@mui/material/tablerow/tablerow.d.ts","../../node_modules/@mui/material/tablerow/index.d.ts","../../node_modules/@mui/material/tablesortlabel/tablesortlabelclasses.d.ts","../../node_modules/@mui/material/tablesortlabel/tablesortlabel.d.ts","../../node_modules/@mui/material/tablesortlabel/index.d.ts","../../node_modules/@mui/material/tablefooter/tablefooterclasses.d.ts","../../node_modules/@mui/material/tablefooter/tablefooter.d.ts","../../node_modules/@mui/material/tablefooter/index.d.ts","../../node_modules/@mui/material/tab/tabclasses.d.ts","../../node_modules/@mui/material/tab/tab.d.ts","../../node_modules/@mui/material/tab/index.d.ts","../../node_modules/@mui/material/tabscrollbutton/tabscrollbuttonclasses.d.ts","../../node_modules/@mui/material/tabscrollbutton/tabscrollbutton.d.ts","../../node_modules/@mui/material/tabscrollbutton/index.d.ts","../../node_modules/@mui/material/tabs/tabsclasses.d.ts","../../node_modules/@mui/material/tabs/tabs.d.ts","../../node_modules/@mui/material/tabs/index.d.ts","../../node_modules/@mui/material/textfield/textfieldclasses.d.ts","../../node_modules/@mui/material/textfield/textfield.d.ts","../../node_modules/@mui/material/textfield/index.d.ts","../../node_modules/@mui/material/togglebutton/togglebuttonclasses.d.ts","../../node_modules/@mui/material/togglebutton/togglebutton.d.ts","../../node_modules/@mui/material/togglebutton/index.d.ts","../../node_modules/@mui/material/togglebuttongroup/togglebuttongroupclasses.d.ts","../../node_modules/@mui/material/togglebuttongroup/togglebuttongroup.d.ts","../../node_modules/@mui/material/togglebuttongroup/index.d.ts","../../node_modules/@mui/material/toolbar/toolbarclasses.d.ts","../../node_modules/@mui/material/toolbar/toolbar.d.ts","../../node_modules/@mui/material/toolbar/index.d.ts","../../node_modules/@mui/material/styles/props.d.ts","../../node_modules/@mui/material/styles/overrides.d.ts","../../node_modules/@mui/material/styles/variants.d.ts","../../node_modules/@mui/material/styles/components.d.ts","../../node_modules/@mui/material/styles/createtheme.d.ts","../../node_modules/@mui/material/styles/adaptv4theme.d.ts","../../node_modules/@mui/material/styles/createstyles.d.ts","../../node_modules/@mui/material/styles/responsivefontsizes.d.ts","../../node_modules/@mui/material/styles/usetheme.d.ts","../../node_modules/@mui/material/styles/usethemeprops.d.ts","../../node_modules/@mui/material/styles/styled.d.ts","../../node_modules/@mui/material/styles/themeprovider.d.ts","../../node_modules/@mui/material/styles/cssutils.d.ts","../../node_modules/@mui/material/styles/makestyles.d.ts","../../node_modules/@mui/material/styles/withstyles.d.ts","../../node_modules/@mui/material/styles/withtheme.d.ts","../../node_modules/@mui/material/styles/experimental_extendtheme.d.ts","../../node_modules/@mui/material/styles/cssvarsprovider.d.ts","../../node_modules/@mui/material/styles/index.d.ts","../../node_modules/@mui/material/colors/amber.d.ts","../../node_modules/@mui/material/colors/blue.d.ts","../../node_modules/@mui/material/colors/bluegrey.d.ts","../../node_modules/@mui/material/colors/brown.d.ts","../../node_modules/@mui/material/colors/common.d.ts","../../node_modules/@mui/material/colors/cyan.d.ts","../../node_modules/@mui/material/colors/deeporange.d.ts","../../node_modules/@mui/material/colors/deeppurple.d.ts","../../node_modules/@mui/material/colors/green.d.ts","../../node_modules/@mui/material/colors/grey.d.ts","../../node_modules/@mui/material/colors/indigo.d.ts","../../node_modules/@mui/material/colors/lightblue.d.ts","../../node_modules/@mui/material/colors/lightgreen.d.ts","../../node_modules/@mui/material/colors/lime.d.ts","../../node_modules/@mui/material/colors/orange.d.ts","../../node_modules/@mui/material/colors/pink.d.ts","../../node_modules/@mui/material/colors/purple.d.ts","../../node_modules/@mui/material/colors/red.d.ts","../../node_modules/@mui/material/colors/teal.d.ts","../../node_modules/@mui/material/colors/yellow.d.ts","../../node_modules/@mui/material/colors/index.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/chainproptypes.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/deepmerge.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/elementacceptingref.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/elementtypeacceptingref.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/exactprop.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/formatmuierrormessage.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/getdisplayname.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/htmlelementtype.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/ponyfillglobal.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/reftype.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/capitalize.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/createchainedfunction.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/debounce.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/deprecatedproptype.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/ismuielement.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/ownerdocument.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/ownerwindow.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/requirepropfactory.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/setref.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/useenhancedeffect.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/useid.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/unsupportedprop.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/usecontrolled.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/useeventcallback.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/useforkref.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/useisfocusvisible.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/getscrollbarsize.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/scrollleft.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/usepreviousprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/visuallyhidden.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/integerproptype.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/resolveprops.d.ts","../../node_modules/@mui/material/node_modules/@mui/utils/index.d.ts","../../node_modules/@mui/material/utils/capitalize.d.ts","../../node_modules/@mui/material/utils/createchainedfunction.d.ts","../../node_modules/@mui/material/utils/createsvgicon.d.ts","../../node_modules/@mui/material/utils/debounce.d.ts","../../node_modules/@mui/material/utils/deprecatedproptype.d.ts","../../node_modules/@mui/material/utils/ismuielement.d.ts","../../node_modules/@mui/material/utils/ownerdocument.d.ts","../../node_modules/@mui/material/utils/ownerwindow.d.ts","../../node_modules/@mui/material/utils/requirepropfactory.d.ts","../../node_modules/@mui/material/utils/setref.d.ts","../../node_modules/@mui/material/utils/useenhancedeffect.d.ts","../../node_modules/@mui/material/utils/useid.d.ts","../../node_modules/@mui/material/utils/unsupportedprop.d.ts","../../node_modules/@mui/material/utils/usecontrolled.d.ts","../../node_modules/@mui/material/utils/useeventcallback.d.ts","../../node_modules/@mui/material/utils/useforkref.d.ts","../../node_modules/@mui/material/utils/useisfocusvisible.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/classname/classnamegenerator.d.ts","../../node_modules/@mui/material/node_modules/@mui/base/classname/index.d.ts","../../node_modules/@mui/material/utils/index.d.ts","../../node_modules/@mui/material/box/box.d.ts","../../node_modules/@mui/material/box/index.d.ts","../../node_modules/@mui/material/clickawaylistener/index.d.ts","../../node_modules/@mui/material/darkscrollbar/index.d.ts","../../node_modules/@mui/material/grow/grow.d.ts","../../node_modules/@mui/material/grow/index.d.ts","../../node_modules/@mui/material/hidden/hidden.d.ts","../../node_modules/@mui/material/hidden/index.d.ts","../../node_modules/@mui/material/nossr/index.d.ts","../../node_modules/@mui/material/portal/index.d.ts","../../node_modules/@mui/material/textareaautosize/index.d.ts","../../node_modules/@mui/material/usescrolltrigger/usescrolltrigger.d.ts","../../node_modules/@mui/material/usescrolltrigger/index.d.ts","../../node_modules/@mui/material/zoom/zoom.d.ts","../../node_modules/@mui/material/zoom/index.d.ts","../../node_modules/@mui/material/useautocomplete/useautocomplete.d.ts","../../node_modules/@mui/material/useautocomplete/index.d.ts","../../node_modules/@mui/material/globalstyles/globalstyles.d.ts","../../node_modules/@mui/material/globalstyles/index.d.ts","../../node_modules/@mui/material/index.d.ts","./static-resources/src/error/errormap.tsx","./static-resources/src/error/index.ts","./static-resources/src/constant/sagastatus.ts","../../node_modules/@loopring-web/web3-provider/dist/command.d.ts","../../node_modules/eventemitter3/index.d.ts","../../node_modules/@walletconnect/http-connection/dist/cjs/index.d.ts","../../node_modules/@walletconnect/types/index.d.ts","../../node_modules/@walletconnect/web3-provider/dist/cjs/index.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/assets/wallet-logo.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/relaymessage.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/types.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/web3method.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/web3request.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/web3requestmessage.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/web3response.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/web3responsemessage.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/subscription.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/types.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/subscriber.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/operator.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/iif.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/throwerror.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/subject.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/connectableobservable.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/operators/groupby.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/symbol/observable.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/behaviorsubject.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/replaysubject.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/asyncsubject.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/action.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/asyncscheduler.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/asyncaction.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/asapscheduler.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/asap.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/async.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/queuescheduler.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/queue.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/animationframescheduler.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/animationframe.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduler/virtualtimescheduler.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/notification.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/pipe.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/noop.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/identity.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/isobservable.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/argumentoutofrangeerror.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/emptyerror.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/objectunsubscribederror.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/unsubscriptionerror.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/util/timeouterror.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/bindcallback.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/bindnodecallback.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/innersubscriber.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/outersubscriber.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/combinelatest.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/concat.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/defer.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/empty.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/forkjoin.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/from.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/fromevent.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/fromeventpattern.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/generate.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/interval.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/merge.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/never.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/of.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/onerrorresumenext.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/pairs.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/partition.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/race.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/range.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/timer.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/using.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/observable/zip.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/scheduled/scheduled.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/internal/config.d.ts","../../node_modules/@coinbase/wallet-sdk/node_modules/rxjs/index.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/connection/rxwebsocket.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/connection/servermessage.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/connection/diagnosticlogger.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/connection/eventlistener.d.ts","../../node_modules/@metamask/safe-event-emitter/index.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/lib/scopedlocalstorage.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/provider/jsonrpc.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/ethereumtransactionparams.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/session.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/walletsdkrelayabstract.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/relay/walletsdkrelayeventmanager.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/provider/web3provider.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/provider/coinbasewalletprovider.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/provider/walletui.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/coinbasewalletsdk.d.ts","../../node_modules/@coinbase/wallet-sdk/dist/index.d.ts","../../node_modules/@ethersproject/bytes/lib/index.d.ts","../../node_modules/@ethersproject/bignumber/lib/bignumber.d.ts","../../node_modules/@ethersproject/bignumber/lib/fixednumber.d.ts","../../node_modules/@ethersproject/bignumber/lib/index.d.ts","../../node_modules/@ethersproject/networks/lib/types.d.ts","../../node_modules/@ethersproject/networks/lib/index.d.ts","../../node_modules/@ethersproject/properties/lib/index.d.ts","../../node_modules/@ethersproject/transactions/lib/index.d.ts","../../node_modules/@ethersproject/web/lib/index.d.ts","../../node_modules/@ethersproject/abstract-provider/lib/index.d.ts","../../node_modules/@ethersproject/providers/lib/formatter.d.ts","../../node_modules/@ethersproject/providers/lib/base-provider.d.ts","../../node_modules/@ethersproject/abstract-signer/lib/index.d.ts","../../node_modules/@ethersproject/providers/lib/json-rpc-provider.d.ts","../../node_modules/@ethersproject/providers/lib/websocket-provider.d.ts","../../node_modules/@ethersproject/providers/lib/url-json-rpc-provider.d.ts","../../node_modules/@ethersproject/providers/lib/alchemy-provider.d.ts","../../node_modules/@ethersproject/providers/lib/ankr-provider.d.ts","../../node_modules/@ethersproject/providers/lib/cloudflare-provider.d.ts","../../node_modules/@ethersproject/providers/lib/etherscan-provider.d.ts","../../node_modules/@ethersproject/providers/lib/fallback-provider.d.ts","../../node_modules/@ethersproject/providers/lib/ipc-provider.d.ts","../../node_modules/@ethersproject/providers/lib/infura-provider.d.ts","../../node_modules/@ethersproject/providers/lib/json-rpc-batch-provider.d.ts","../../node_modules/@ethersproject/providers/lib/nodesmith-provider.d.ts","../../node_modules/@ethersproject/providers/lib/pocket-provider.d.ts","../../node_modules/@ethersproject/providers/lib/web3-provider.d.ts","../../node_modules/@ethersproject/providers/lib/index.d.ts","../../node_modules/@loopring-web/web3-provider/dist/providers.d.ts","../../node_modules/@loopring-web/web3-provider/dist/walletservices.d.ts","../../node_modules/@loopring-web/web3-provider/dist/index.d.ts","./static-resources/src/svg/icon.tsx","../../node_modules/@types/trusted-types/lib/index.d.ts","../../node_modules/@types/trusted-types/index.d.ts","../../node_modules/@types/dompurify/index.d.ts","./static-resources/src/svg/redpacketsvg.tsx","./static-resources/src/svg/index.ts","./static-resources/src/constant/market.ts","./static-resources/src/constant/vendor.ts","./static-resources/src/constant/trade.ts","./static-resources/src/constant/router.ts","./static-resources/src/constant/walletconnector.ts","./static-resources/src/utils/types.tsx","./static-resources/src/utils/util.ts","./static-resources/src/utils/log_tools.ts","./static-resources/src/utils/format_tools.ts","./static-resources/src/utils/obj_tools.ts","./static-resources/src/utils/makedom.ts","./static-resources/src/utils/index.ts","./static-resources/src/constant/setting.ts","./static-resources/src/constant/chart.ts","./static-resources/src/constant/table.ts","./static-resources/src/constant/loopring.ts","./static-resources/src/constant/prolayout.ts","./static-resources/src/constant/miningouterlinks.ts","./static-resources/src/constant/notification.ts","./static-resources/src/constant/firebase.ts","./static-resources/src/constant/index.ts","./static-resources/src/loopring-interface/coininterface.ts","./static-resources/src/loopring-interface/headerinterface.ts","./static-resources/src/loopring-interface/wallectinterface.ts","./static-resources/src/loopring-interface/vendorinterface.ts","./static-resources/src/loopring-interface/footerinterface.ts","./static-resources/src/loopring-interface/investment.ts","./static-resources/src/loopring-interface/index.ts","./static-resources/src/constant/account.ts","./static-resources/src/i18n/en_us/common.ts","./static-resources/src/i18n/en_us/layout.ts","./static-resources/src/i18n/en_us/tables.ts","./static-resources/src/i18n/en_us/error.ts","./static-resources/src/i18n/en_us/landpage.ts","./static-resources/src/i18n/en_us/index.ts","./static-resources/src/i18n/index.ts","./static-resources/src/i18n/zh_cn/common.ts","./static-resources/src/i18n/zh_cn/error.ts","./static-resources/src/i18n/zh_cn/layout.ts","./static-resources/src/i18n/zh_cn/tables.ts","./static-resources/src/i18n/zh_cn/landpage.ts","./static-resources/src/i18n/zh_cn/index.ts","./static-resources/src/themes/globalsetup.ts","./static-resources/src/themes/css/color-lib.ts","./static-resources/src/themes/interface.ts","./static-resources/src/themes/css/reset.tsx","./static-resources/src/themes/css/global.tsx","./static-resources/src/themes/overrides/utils.ts","./static-resources/src/themes/overrides/overrides-mui.ts","./static-resources/src/themes/overrides/overrides-date-pick.ts","./static-resources/src/themes/overrides/mutheme.ts","./static-resources/src/themes/index.ts","../../node_modules/@types/aria-query/index.d.ts","../../node_modules/@babel/types/lib/index.d.ts","../../node_modules/@types/babel__generator/index.d.ts","../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../node_modules/@types/babel__template/index.d.ts","../../node_modules/@types/babel__traverse/index.d.ts","../../node_modules/@types/babel__core/index.d.ts","../../node_modules/keyv/src/index.d.ts","../../node_modules/@types/http-cache-semantics/index.d.ts","../../node_modules/@types/responselike/index.d.ts","../../node_modules/@types/cacheable-request/index.d.ts","../../node_modules/@types/connect/index.d.ts","../../node_modules/@types/d3-format/index.d.ts","../../node_modules/@types/d3-path/index.d.ts","../../node_modules/@types/d3-time/index.d.ts","../../node_modules/@types/d3-scale/index.d.ts","../../node_modules/@types/d3-shape/index.d.ts","../../node_modules/@types/d3-time-format/index.d.ts","../../node_modules/@types/ms/index.d.ts","../../node_modules/@types/debug/index.d.ts","../../node_modules/@types/eslint/helpers.d.ts","../../node_modules/@types/eslint/lib/rules/index.d.ts","../../node_modules/@types/json-schema/index.d.ts","../../node_modules/@types/estree/index.d.ts","../../node_modules/@types/eslint/index.d.ts","../../node_modules/@types/eslint-scope/node_modules/@types/eslint/helpers.d.ts","../../node_modules/@types/eslint-scope/node_modules/@types/eslint/index.d.ts","../../node_modules/@types/eslint-scope/index.d.ts","../../node_modules/@types/eslint-visitor-keys/index.d.ts","../../node_modules/@types/ethereumjs-abi/index.d.ts","../../node_modules/@types/minimatch/index.d.ts","../../node_modules/@types/glob/index.d.ts","../../node_modules/@types/graceful-fs/index.d.ts","../../node_modules/@types/unist/index.d.ts","../../node_modules/@types/hast/index.d.ts","../../node_modules/@types/history/domutils.d.ts","../../node_modules/@types/history/createbrowserhistory.d.ts","../../node_modules/@types/history/createhashhistory.d.ts","../../node_modules/@types/history/creatememoryhistory.d.ts","../../node_modules/@types/history/locationutils.d.ts","../../node_modules/@types/history/pathutils.d.ts","../../node_modules/@types/history/index.d.ts","../../node_modules/@types/hoist-non-react-statics/index.d.ts","../../node_modules/@types/html-minifier-terser/index.d.ts","../../node_modules/@types/is-function/index.d.ts","../../node_modules/@types/istanbul-lib-coverage/index.d.ts","../../node_modules/@types/istanbul-lib-report/index.d.ts","../../node_modules/@types/istanbul-reports/index.d.ts","../../node_modules/jest-diff/build/cleanupsemantic.d.ts","../../node_modules/jest-diff/build/types.d.ts","../../node_modules/jest-diff/build/difflines.d.ts","../../node_modules/jest-diff/build/printdiffs.d.ts","../../node_modules/jest-diff/build/index.d.ts","../../node_modules/pretty-format/build/types.d.ts","../../node_modules/pretty-format/build/index.d.ts","../../node_modules/@types/jest/index.d.ts","../../node_modules/@types/jest-specific-snapshot/index.d.ts","../../node_modules/@types/js-cookie/index.d.ts","../../node_modules/@types/jsbn/index.d.ts","../../node_modules/@types/json5/index.d.ts","../../node_modules/@types/keyv/index.d.ts","../../node_modules/@types/lodash/common/common.d.ts","../../node_modules/@types/lodash/common/array.d.ts","../../node_modules/@types/lodash/common/collection.d.ts","../../node_modules/@types/lodash/common/date.d.ts","../../node_modules/@types/lodash/common/function.d.ts","../../node_modules/@types/lodash/common/lang.d.ts","../../node_modules/@types/lodash/common/math.d.ts","../../node_modules/@types/lodash/common/number.d.ts","../../node_modules/@types/lodash/common/object.d.ts","../../node_modules/@types/lodash/common/seq.d.ts","../../node_modules/@types/lodash/common/string.d.ts","../../node_modules/@types/lodash/common/util.d.ts","../../node_modules/@types/lodash/index.d.ts","../../node_modules/@types/long/index.d.ts","../../node_modules/@types/mdast/index.d.ts","../../node_modules/@types/mdurl/encode.d.ts","../../node_modules/@types/mdurl/decode.d.ts","../../node_modules/@types/mdurl/parse.d.ts","../../node_modules/@types/mdurl/format.d.ts","../../node_modules/@types/mdurl/index.d.ts","../../node_modules/@types/minimist/index.d.ts","../../node_modules/@types/ms.macro/index.d.ts","../../node_modules/form-data/index.d.ts","../../node_modules/@types/node-fetch/externals.d.ts","../../node_modules/@types/node-fetch/index.d.ts","../../node_modules/@types/normalize-package-data/index.d.ts","../../node_modules/@types/npmlog/index.d.ts","../../node_modules/@types/parse-json/index.d.ts","../../node_modules/@types/parse5/lib/tree-adapters/default.d.ts","../../node_modules/@types/parse5/index.d.ts","../../node_modules/@types/pbkdf2/index.d.ts","../../node_modules/@types/prettier/index.d.ts","../../node_modules/@types/pretty-hrtime/index.d.ts","../../node_modules/@types/q/index.d.ts","../../node_modules/@types/qrcode-svg/index.d.ts","../../node_modules/@types/qrcode.react/index.d.ts","../../node_modules/@types/qs/index.d.ts","../../node_modules/@types/react-beautiful-dnd/index.d.ts","../../node_modules/@types/react-dom/index.d.ts","../../node_modules/@types/react-grid-layout/index.d.ts","../../node_modules/@types/react-is/index.d.ts","../../node_modules/redux/index.d.ts","../../node_modules/@types/react-redux/index.d.ts","../../node_modules/@types/react-router/index.d.ts","../../node_modules/@types/react-router-dom/index.d.ts","../../node_modules/@types/react-swipeable-views/index.d.ts","../../node_modules/@types/react-test-renderer/index.d.ts","../../node_modules/@types/react-transition-group/csstransition.d.ts","../../node_modules/@types/react-transition-group/transitiongroup.d.ts","../../node_modules/@types/react-transition-group/switchtransition.d.ts","../../node_modules/@types/react-transition-group/config.d.ts","../../node_modules/@types/react-transition-group/index.d.ts","../../node_modules/@types/react-virtualized/dist/es/cellmeasurer.d.ts","../../node_modules/@types/react-virtualized/dist/es/list.d.ts","../../node_modules/@types/react-virtualized/dist/es/table.d.ts","../../node_modules/@types/react-virtualized/dist/es/grid.d.ts","../../node_modules/@types/react-virtualized/dist/es/arrowkeystepper.d.ts","../../node_modules/@types/react-virtualized/dist/es/autosizer.d.ts","../../node_modules/@types/react-virtualized/dist/es/collection.d.ts","../../node_modules/@types/react-virtualized/dist/es/columnsizer.d.ts","../../node_modules/@types/react-virtualized/dist/es/infiniteloader.d.ts","../../node_modules/@types/react-virtualized/dist/es/masonry.d.ts","../../node_modules/@types/react-virtualized/dist/es/multigrid.d.ts","../../node_modules/@types/react-virtualized/dist/es/scrollsync.d.ts","../../node_modules/@types/react-virtualized/dist/es/windowscroller.d.ts","../../node_modules/@types/react-virtualized/index.d.ts","../../node_modules/@types/react-virtualized-auto-sizer/index.d.ts","../../node_modules/@types/redux-logger/index.d.ts","../../node_modules/@types/resize-observer-browser/index.d.ts","../../node_modules/@types/resolve/index.d.ts","../../node_modules/@types/scheduler/index.d.ts","../../node_modules/@types/secp256k1/index.d.ts","../../node_modules/@types/source-list-map/index.d.ts","../../node_modules/@types/stack-utils/index.d.ts","../../node_modules/@types/styled-components/index.d.ts","../../node_modules/@types/styled-react-modal/index.d.ts","../../node_modules/@types/tapable/index.d.ts","../../node_modules/@types/testing-library__jest-dom/matchers.d.ts","../../node_modules/@types/testing-library__jest-dom/index.d.ts","../../node_modules/source-map/source-map.d.ts","../../node_modules/@types/uglify-js/index.d.ts","../../node_modules/@types/voca/index.d.ts","../../node_modules/@types/webpack/node_modules/anymatch/index.d.ts","../../node_modules/@types/webpack-sources/node_modules/source-map/source-map.d.ts","../../node_modules/@types/webpack-sources/lib/source.d.ts","../../node_modules/@types/webpack-sources/lib/compatsource.d.ts","../../node_modules/@types/webpack-sources/lib/concatsource.d.ts","../../node_modules/@types/webpack-sources/lib/originalsource.d.ts","../../node_modules/@types/webpack-sources/lib/prefixsource.d.ts","../../node_modules/@types/webpack-sources/lib/rawsource.d.ts","../../node_modules/@types/webpack-sources/lib/replacesource.d.ts","../../node_modules/@types/webpack-sources/lib/sizeonlysource.d.ts","../../node_modules/@types/webpack-sources/lib/sourcemapsource.d.ts","../../node_modules/@types/webpack-sources/lib/index.d.ts","../../node_modules/@types/webpack-sources/lib/cachedsource.d.ts","../../node_modules/@types/webpack-sources/index.d.ts","../../node_modules/@types/webpack/index.d.ts","../../node_modules/@types/webpack-env/index.d.ts","../../node_modules/@types/ws/index.d.ts","../../node_modules/@types/yargs-parser/index.d.ts","../../node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"3ac1b83264055b28c0165688fda6dfcc39001e9e7828f649299101c23ad0a0c3","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","e6b724280c694a9f588847f754198fb96c43d805f065c3a5b28bbc9594541c84","e21c071ca3e1b4a815d5f04a7475adcaeea5d64367e840dd0154096d705c3940","746d62152361558ea6d6115cf0da4dd10ede041d14882ede3568bce5dc4b4f1f","2f93dda35dafec68ec217c9ce67f0f4fbbbb030c055ac312641565ad60dd7e26","aea179452def8a6152f98f63b191b84e7cbd69b0e248c91e61fb2e52328abe8c",{"version":"72704b10d97777e15f1a581b73f88273037ef752d2e50b72287bd0a90af64fe6","affectsGlobalScope":true},{"version":"dbb73d4d99be496175cb432c74c2615f78c76f4272f1d83cba11ee0ed6dbddf0","affectsGlobalScope":true},{"version":"d8996609230d17e90484a2dd58f22668f9a05a3bfe00bfb1d6271171e54a31fb","affectsGlobalScope":true},{"version":"43fb1d932e4966a39a41b464a12a81899d9ae5f2c829063f5571b6b87e6d2f9c","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"0d5f52b3174bee6edb81260ebcd792692c32c81fd55499d69531496f3f2b25e7","affectsGlobalScope":true},{"version":"810627a82ac06fb5166da5ada4159c4ec11978dfbb0805fe804c86406dab8357","affectsGlobalScope":true},{"version":"62d80405c46c3f4c527ee657ae9d43fda65a0bf582292429aea1e69144a522a6","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"75ec0bdd727d887f1b79ed6619412ea72ba3c81d92d0787ccb64bab18d261f14","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"12a310447c5d23c7d0d5ca2af606e3bd08afda69100166730ab92c62999ebb9d","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"1b3fe904465430e030c93239a348f05e1be80640d91f2f004c3512c2c2c89f34","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"5075b36ab861c8c0c45377cb8c96270d7c65f0eeaf105d53fac6850da61f1027","affectsGlobalScope":true},{"version":"6c55633c733c8378db65ac3da7a767c3cf2cf3057f0565a9124a16a3a2019e87","affectsGlobalScope":true},{"version":"fb4416144c1bf0323ccbc9afb0ab289c07312214e8820ad17d709498c865a3fe","affectsGlobalScope":true},{"version":"5b0ca94ec819d68d33da516306c15297acec88efeb0ae9e2b39f71dbd9685ef7","affectsGlobalScope":true},{"version":"e8c9f4e445a489991ca1a4232667de3ac36b07ba75ea335971fbeacf2d26fe67","affectsGlobalScope":true},{"version":"34478567f8a80171f88f2f30808beb7da15eac0538ae91282dd33dce928d98ed","affectsGlobalScope":true},{"version":"6ea9ab679ea030cf46c16a711a316078e9e02619ebaf07a7fcd16964aba88f2d","affectsGlobalScope":true},{"version":"aedb8de1abb2ff1095c153854a6df7deae4a5709c37297f9d6e9948b6806fa66","affectsGlobalScope":true},{"version":"11ffe3c281f375fff9ffdde8bbec7669b4dd671905509079f866f2354a788064","affectsGlobalScope":true},{"version":"10bbdc1981b8d9310ee75bfac28ee0477bb2353e8529da8cff7cb26c409cb5e8","affectsGlobalScope":true},{"version":"bbdf156fea2fabed31a569445835aeedcc33643d404fcbaa54541f06c109df3f","affectsGlobalScope":true},"ea0aa24a32c073b8639aa1f3130ba0add0f0f2f76b314d9ba988a5cb91d7e3c4","f7b46d22a307739c145e5fddf537818038fdfffd580d79ed717f4d4d37249380","f5a8b384f182b3851cec3596ccc96cb7464f8d3469f48c74bf2befb782a19de5",{"version":"ce4e9cdf1ec90f3d435a4df18342b187805924f6046e872e76d3c1659ad545ff","affectsGlobalScope":true},"af7fd2870746deed40e130fc0a3966de74e8f52a97ec114d0fbb35876ab05ca9","d0476ddaacf1b8b9b18aa7cdb54eb8bc7edb69165a455803e2065fc28a321960","2be6f8fda5c8ca751609548426d25da1785178c75e2af1cb675afd207d9da85f","dd051e4d359b018fca5a4f5520be1d6bc9701b3dce8274badfd378aed11fb7ec","60693bef7f2b4d077f08a1a837b7d6ad99a0ad66115bac4b4141466927069f99","0ce65cf5b36034006f2b315f379179f07bd5a6027f6538c7aed4ac5be6742fc7",{"version":"cb8b955fbbb257a05b2d5a2f93212c7edf033b61b9420fa700d10d39d9bd4533","affectsGlobalScope":true},"0721418a90edc5fca466672f266ab6de12cb50a9db29998fc58765481a3c82ef","97b39f33e966bcf9762bccdaca76c94825199f3fef153ebea9bdfd3fcd2413b6","78650a1b5800e82b6914260e9ca0fe9ea744e4333c3bec51b08f91525718d7fa","c41eff6b8e1f91104ae974ccd2bc37c723a462b30ca1df942b2c5b0158ef1df3","2e341737e0711c12040e83047487240b1693a6774253b8142d1a0500a805b7a1","e08e97c2865750e880fea09b150a702ccfa84163382daa0221f5597185a554bf","7ec6b45fc1f5f012ac226b44d0eeaf5a0e90947431ad7c6d1f244ba080b2870d","4a1a19573176829708dc03efea508e7c364f6fa30098a5100bd9d93fc9cd38ee","8296198bc72e7ef2221b0e140738ce56004e8d1323cd08b0ac1a15295fe911b5","baeda1fadac9fd31920480b85340ab9c4266a25ad08403dee8e15fd0751101fb","12c4e8e811f4310b0dcaa3d1f843a35dc985f78941886cad4950453ad6753959","6bdede4dc73ad9816d0beeca7413ab29523f99a98441cc9fa6c614fd97fac49d","8698062058cbdc84171bd576315a5eecab2bf46d7d034144653ae78864889683","b3e4f2772da66bac2144ca8cd63f70d211d2f970c93fcb789d03e8a046d47c93","a3586135924c800f21f739a1da43acace1acfdba124deb0871cbd6d04d7dfd1b","f74c1fae7233cd08c72bb2dc09cb0673c53fb4c3c7903ccf0094404e3f2ba6d4","4ec74fe565d13fd219884cfacf903c89477cc54148887e51c5bead4dae7dc4fd","f50f52f8fc2a5f8dfa594ff59a6273876b1734fe612e9f331ca9078fede3b304","a46d8aa9e561fb135d253e1657a0cd0f6c18377676305eb0ca28e418358b229c","5a168a15e7a423011b10da472ee3b6d92b27227c192cdaf1e09b30f58806856d","ad107fa472d28e615af522b31653e75caad12b834b257c1a83f6c4acff2de9bf",{"version":"07cfc938dfbb5a7b5ba3c363366db93d5728b0fcad1aa08a12052a1b3b72817a","affectsGlobalScope":true},"7f77304372efe3c9967e5f9ea2061f1b4bf41dc3cda3c83cdd676f2e5af6b7e6","67cf04da598e6407427a17d828e9e02d8f5ae5a8466dc73d1585073b8dc29160","fa960168e0650a987d5738376a22a1969b5dff2112b9653f9f1efddf8ba7d5bb","140b05c89cbd5fc75c4e9c1780d85dfb4ea73a2b11dd345f8f944afd002ad74f","ece46d0e5702e9c269aa71b42d02c934c10d4d24545b1d8594a8115f23a9011f","5b0df2143d96172bf207ed187627e8c58b15a1a8f97bdbc2ede942b36b39fc98","75409a3abea76ea586c6fb7f2e9f1a7dc007140e2d4ee26ab127735c4f9154e4","8e721f0a95eb26a30228d571659d2db062213bfe066a97fff7967a0c8e1d56e4","7df562288f949945cf69c21cd912100c2afedeeb7cdb219085f7f4b46cb7dde4","9d16690485ff1eb4f6fc57aebe237728fd8e03130c460919da3a35f4d9bd97f5","ad7e61eca7f2f8bf47e72695f9f6663b75e41d87ef49abdb17c0cb843862f8aa","ecba2e44af95b0599c269a92628cec22e752868bce37396740deb51a5c547a26","46a9fb41a8f3bc7539eeebc15a6e04b9e55d7537a081615ad3614220d34c3e0f",{"version":"fd240b48ab1e78082c96c1faca62df02c0b8befa1fd98d031fab4f75c90feee6","affectsGlobalScope":true},"3d87bdaed72f86b91f99401e6e04729afbb5916064778cf324b3d9b51c3a6d91","8ca837d16a31d6d01b13328ca9e6a39e424b4bf294d3b73349dccacea51be730","a9d40247ec6c68a47effbb1d8acd8df288bcee7b6bf29c17cf4161e5ef609a0c","caf38c850b924a0af08a893d06f68fcae3d5a41780b50cc6df9481beeca8e9a3","7152c46a63e7f9ac7db6cd8d4dbf85d90f051a0db60e650573fae576580cbf9a","496370c58ed054e51a68517846c28a695bf84df2873556cca7fe51e297b32420",{"version":"2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1","affectsGlobalScope":true},"263da3252e029b3b62580b7a0c5e7bab07325a31c8af436ff60143cfda73b629","cdac7e46e615e1fdcca7c3a2aab2fbd19085443048733cf239a090f7a17efa27","1d7d9e42cb2cc36dfc7ce35755ce67d5ff0f4a2ef72d3019f4c461bfd22123da","55c8e16380925dc895e3206510688b1decd3505bc6ff5b76e834f7f9531e22bb","864d3573f14f138134b33f9589fb2bfb68a63cb63801a23d17a247f5f3bfa375","1acfc1c5a9f95332a2c17a1e665661a049c818e1c7b0bb7cc2dacfdbf08f79af","3170c0416dd2064d848fe1f06a2e1e53ed55030e93cb91ca451ad56b06354ff8","3310413f72492d930655e27a99cb740367c5041852c8c84992a75454dde746b5","5666d8fa634bec348d8e96cc49a620bb48f9420af778cd3acd1610e9ab03e236","6aa5fdfff96c112d37fdee50d1bb82bdfc9247420fc76128e81b28fbd29c28a2","f17d0f19ad9242b09b68a6d30402a841a7d4194d063855da5ca0567df0fb310e","9e47cea2039dc59f4d9a33cc1da282a487a71c724efb2e888046417777d85313","98c6b260f35311b89c326595d985c1553eb5d7a5b76d6e4a782cd3b088f362c4","e082f4e967527c06203e5a3d9624f71a5b5102a38f66710325d51777cf4d5377","1e13aa4f2ff194e7b753650d271c814a93419bf3dd6085ab6dcce0990e43bc68","068b7d8771cc226375d8fc8a79fa03d0b2398f915a463b72faac8216c68d0d31","2f208677a8d0b0ec9ae1fd09e8276dd455e6eef200c680078e40dc106cb21f0e","e6df230289fad09ce77339f88a696b64d8b7f224c97113db9b707f4bea66f151","1a86456517579a769ea206f9039a7d11e80c065bbd02b4ac8aafe2cf6ad008ca","089e611f488f1de8aeb1c32c396d376b5c6e04f7b41684d334fe30139ae1dc02","b574cbf23ab912f26339bc1f2a355bb26b8fcfd19e3f4264c7bc08fec99dc6fc","1afca0be2d8f2e7046b636dc4ab1cff021f9394b45f91ce818976f8ae1a2c10f","f3c83c293295b4629a0caccb448f9faf593a6d58396b17c369ed9934feaa9485","3154a026075044aa102298fe9e6a7a14aaa26a06270680c7478a1765af8ffb09","d6e832a6024676e553c569724e059e6438d6666fe1d8495ffc4dfa3696cbcbb3","49de1678763fd9668f70a787c216a8b9fd902ef43dc11823c4581055bba17dda","706f8c94c2f32d9a0f9d8f5948c2a50ca91808061812e9496137227d9fa655d4","5ea44ed1739c4c4bbf8acd92242187cd8de9a6e8607381e66f3e54f26d8c32bc","f079849b1695b3152412a4ae2525da5544158e7c03b47a314f6c4f513e50f0af","8903ca0ff1d3c84b773b2b4b7cf8ab792902c85e0cfe546d578fef5b04f92ae4","e73808fd2b1facc0c486b9225e47f1e65ce3f3fd75bc703dcb09a79c4c422fab","0af5b5acb2a594b5fb23991ba3dc575f4c692cc7473e60fe76ce91c001edad48","4089f519cfea3124c47dd8a2fbc26023fc013b6e860b7b1b29dd647b57af4d40","89c9e52ccc437bf15581a8ee90db80065d3e5cb684a17193f87ad2ddf72ff435","9615c1139380c409d7e014da5bad95f9a7b202bad43cd974dbbe604d367b1682","0fec981c94bf74c599d4ff6b37781eedd4fc9ff0615f461cf9eab5622ea92625","6079f704271f14a5d0a1805c97ca16de28286b8e4eee353607d1e8cddec0669e","eaf998c111f92602e21fc71433f5f48f852e1585f54755b709c7b37b4da6ae17","8d64573a06f0a6baf19add71cd8f0099d26ce7858daaad2c3a834f333dbf495c","d6e0999c4c3fe1674238fc91a51d6cda6344bd8b01638a01db939838f8dec44e","ded66243642cc55406173ba5bccf1162b106eb8671cf5eba95d39705505f7130","0cb822a91a00ab0205385706b5e2d8df56f4b318276a2676cfb30b7d4fa713bf","7f8d78620739775a268c820e9f9606c90552fc1bec08a9b43aec6964908af68d","4bdc041df40c2e45cad929816632497173247e3a73f09e94186c2f1c3a6a6fee","7ed54901a39f26fb71a656d203164bfb77288c416e8d82d33fd7970102b91c28","815260e4afd5b26e8767e95dd2d08720d2021beaa58c357b1f0a769af6225352",{"version":"5169b051efebd75efb356781092c08985d8e6de131e960e45bd852fc6ca25bbd","affectsGlobalScope":true},"cdc14771f2aa98ccefb2d089a7274bfa4b01dc2efd324950e2b50b7bc9b82fe7","417892d55c03ed0d682fbae492a11a0157c837d42db8c535ff6b55ade07edf14","f6d14869dbf8d88d3c91d987178ebc928f5de36db7ee2132c6bd98a451a06437","132c108b095b43892c7e800bc499d2c1dcb46cd2cbe988a08284de0d9ccac9ac","ad919527ce080334f1ee9a7430122a9dfc94ed01da38a3aeb28650a55d54c78a","49b4ba730e0a7cacc42298d011e949c2147f7ebdebcf422e4fd8cdcf5133f62e","e359f4c81ca411741f9986c25ce76481f0731f4d11906496e48e826dcd9ccbe8","9d42a116928e196c8a93807d1c76f9bf24d5ba8a89a7a0cedcdba90446e1efcb","a0b6d7e00a21158c7299d2d17152fc48dec67434a7c9864edece97cd2cd7e185","3bd6ffa4f64fbbc031c1503c3ce63fa6e2134deb673c8571130a3675d91fcb17","8ea05ab5a1250aa9d98070151c3981a85f5fd05185454f6c871ca2a988feb725","0e1f5fa05f1097f2cc3a1581afc7270af08d31be123f3a8e92a5b4080858861e","655638506266d44bc4815f7fda912d712114e200aa11ce4dee055d357dba96c5","a9ecd22f5649301e9be4bacf88e13a9daf20011b63a18f6b7d506e1c8bf41f6a","03fd06fcc894c94effaef2fc57d92c9e2871c6a5adb2db7136859a6ceff3f91a","1121a97eee889dd43800e5486eff4b161397885b880c9e59569b5cc87520ecbf","96251a479e0c56d3130cb7a15a682c86d86423422feb49059882ec574f0c6969","7f6ee041490e9470a22c0cd293205083293c12e654364556bacadb5cbebed30c","47dada41ced5a0e23c415fb8599b1b8c848fdd1df1b2f02b2e756558be9b3153","b0a59b88d6d32ed5734ac9413f8a9e34773d4b7b0eddaeccdecee24ab8a4457d","492dae861616e49ded6e82df7110868489b8f80cebb5f56bbe05bbf829f8a6fc","dd4e64e454be95294aceb5286575faa08af11ebacc2c524310be108c1abd2a84","3711c896e72680d79cfc4df36cae172b7dbb72e11936e5e9545f5351e6ed0962","fdb706b594619f05e73b97213d760f59ed1514b302f58b4b46d86fe77757c031","531cd80e4dba2620d86844a50e7d21b89436e56a14e66d6774e99b3759ac69ad","03d59e612afdc3039a83b12d45b026306b291cfc8e2fc72859f7902b8a857caf","93b24ca76698e62732d72800da132367639a4426363c821338bbbd7cf6b64443","ecfa9ce3a5a37d15b813065e8a7cdf677a0f493018e47ce59815443dfbb9c910","83e56d3337e1a6dbafdbe5a2502a84c330b1a328ed2860d689b2ded82b1f5c95","e630f8a3c49d5db0a8af774799abdb8f19675b940a6cfa25eca35e5280709f28","909bac92983e542dd29efcf9eedf4ab5a330767c70c505a52326f7f5ee4b288d","a10d9cbca387b94d7d31330e44b9af8035331a8c5a9388ff7bea6ac6c547d5ea","137a44f0f62468569f6cb54ec0ee289b9694bf6acf872c749f20a80bf9e5b991","cc051639247f18781cd39ae70ae4606902fb5e7ea104c1b2a4927af5cfd05693","40a5bb1733bb8fb3ffa425b92db062334f9b998ba8ad4390cc8008cc2ce701ed","0cc48e862938eccfe92ba6c7daaf5fa144bbc60ed8d547c33dadeff1ab788cd5","9e7c4846057815d55e1eaf27214286ec0768a1b463a4669e1ce37849b6cc1016","0c60536372b6903323d2c1c95e9b7d1343e667dffb730007226499245481b240","7119474d0a340253e9e1ef219ec97f8478203fa668714e58a5ee818f28631ed1","c6d45af373d0c9312216472066d811aa099cfa1ed1976c153f0d06a4c2623efd","b35a027bb203fdb38fe945b2d8d9fefaf7cb0dd0d5def1c83b900dceec353ada","21d57b691fd32ee6e25d8e3636a0dc536eef3a6d2b17c2b94bad2fc7d4984c77","150abc6422a16506cb374e767f4c26b148fd6b32a4f5314c21cee19089523eaf","e625115cc4849c93391c298e23a6843b61052b984e43ec3579866b33da92fe7d","54b0087a8523d0a289460fb3ac4b9ed55633977f2eb7e7f4bba5ff2c1ba972e0","2d855457c0bafe580ac84cc88a12d41ef2daa53b3620fa7b668b0bc746dbd01f","1f5aa8db4794fcddae21b728f556979e9205b70c2060ae32f30cb3d7ba90fa1e","794d96375f04d39dc8513db4479a0023d3b8074b9738e38f7c0ac62d9696431d","656b3a9ee8a2eb73218ccddedbaf412751787b303bf5b0e293f2c60443aeeb08","e78dd7346725ac2d936a296d601e01f55eefabd010bee84cd03e20f55bd61a8c","c67d068b46b9b7eadd921df08d61aa59bb6238b202ddaa32a22e9217c8ba7a47","86fe8e0f91d25f8b32efa2765956adb5a66d375b2336d563ea6cba79f5914e49","492dae861616e49ded6e82df7110868489b8f80cebb5f56bbe05bbf829f8a6fc","d196a729c0ae94c27ca16d922f39fa633424a7d8fda45755fc567ecf2c7726c9","e672c26404625fb9664bc08a5167bba8fddbcbbf3cc03ec01d6701fabe2696f3","076472e9ee039c77985996c0a2cde3dcc96db4b0665a2a04d029bd916c1906ce","7a887e5116279a22ec2040cfd1928ee54fc045c757f32f780529ea8d8cffe7e2","1e47992f91186f9784bf4995764f00bd15372d7ba9fe6c732bd415baff87ab2c","3c8ae1287018757532451e59daf1b969f6a9a9328ffca61edb180f1691c665fc","164b2bc6d40e0e23fab13be163bf55221ffefc45049eac6bdd42bbfa2ba42b70","b71b7186fe4c29e0f548ba350b4da320d83bc2ad35fa943d4a0cc6f88fa15a03","7926f45e28be799136e12839665fe5a04054936c87202e4bb3215138246015ca","4165eca67f3344524716c2818892d0330f3cfee91eb3f53eb9918c3de6351715","6cc7b9937aaf140567dffcbb8cc7e5be37f159d2d970a6cd6029804bde96498a","92d50ec4ddb64d487c7875f1228e210d3caacc906e1965ec3c4dd32e4030d1ef","d8bb5f19f7a470a9d4d16be98766e87b2841ba18ad7fe3ea154326f5a01b339f","3de03e5772d3171dee3537b5a26a6e687b1f29c57fdeb00bff8ea4cea5e2cf6b","29db4a461345a5e1434379f551fb29e4a49fc8235f31e7dd7a6d703e10bc5db7","486709c57cfd544d043356d4323a8ec01fac74c17339788cc7892c6f5ad96137","1e434bdb7961baca128646c8fef458dead8fb5fb07e045b893b16288e36051e6","60f0ea704b15210f097886213221e35d6dac30b246d5daa90e6772a35fd67a7a","f369dea98bf5569c323f39110018bc30696595504922861cae1522918c9e0701","2716ae07247d93af5e7b9ac2bc96e803f9c294c786763bf4067a975775eef32f","64784db50ecb6a5ef3e593a3b5d50e36057366002731117d7f62a586d089cf3d","91dc72de609fc31f6b5d86741abfa61efb70a56c843e160182a5bc1a786d964d","ba2ef6a6d7a14889c74af720a62b9954acf4cca59108bafbf3dacfee5745a432","18bc2b87197aa9e669883f1f84b8e32b7911a8aa2aeaea32c3a441a36dd84518","4fd346095bed1cfb30362b6209da2dbd5534a27f49ffcea8e9df14de750fe8e0","e3c9d8274d395f82be6723ff3bb054937fe4f7303240f18c29620861f8c3c12a","a7a82c9d00d823f77d4a36825c13f49914cea5120ba301b06ab2026f10c4e9a4","3541ec2884b8ca7517ce60c453fd73c8b44ac57e6e6c511337fd24ba9ede8561","70a29119482d358ab4f28d28ee2dcd05d6cbf8e678068855d016e10a9256ec12","869ac759ae8f304536d609082732cb025a08dcc38237fe619caf3fcdd41dde6f","0ea900fe6565f9133e06bce92e3e9a4b5a69234e83d40b7df2e1752b8d2b5002","e5408f95ca9ac5997c0fea772d68b1bf390e16c2a8cad62858553409f2b12412","3c1332a48695617fc5c8a1aead8f09758c2e73018bd139882283fb5a5b8536a6","9260b03453970e98ce9b1ad851275acd9c7d213c26c7d86bae096e8e9db4e62b","083838d2f5fea0c28f02ce67087101f43bd6e8697c51fd48029261653095080c","969132719f0f5822e669f6da7bd58ea0eb47f7899c1db854f8f06379f753b365","94ca5d43ff6f9dc8b1812b0770b761392e6eac1948d99d2da443dc63c32b2ec1","2cbc88cf54c50e74ee5642c12217e6fd5415e1b35232d5666d53418bae210b3b","ccb226557417c606f8b1bba85d178f4bcea3f8ae67b0e86292709a634a1d389d","5ea98f44cc9de1fe05d037afe4813f3dcd3a8c5de43bdd7db24624a364fad8e6","3a1e3199054ae95161fc6a8418ee28cd774f1a258d1b0ee14aa71c48a1f8448c","0b3fc2d2d41ad187962c43cb38117d0aee0d3d515c8a6750aaea467da76b42aa","ed219f328224100dad91505388453a8c24a97367d1bc13dcec82c72ab13012b7","6847b17c96eb44634daa112849db0c9ade344fe23e6ced190b7eeb862beca9f4","d479a5128f27f63b58d57a61e062bd68fa43b684271449a73a4d3e3666a599a7","6f308b141358ac799edc3e83e887441852205dc1348310d30b62c69438b93ca0","116d0e80d8014e9a290850567e06c1a27e50b87cbf899b4c22d44a1fb9dc5d37","c29b40a4cf970e5265985793c53e2f320691de66e45c68295231558769b9acbc","6dedb11e905cde893a2370e482f52c9b20e7fab5497173c08bb69a083c077056","a12a0a356d8f4e764f33915dac4203e1bab7bbcbcff8897fa0bb5805c0d1da8c","40c3e3db2f1b415c6e8e0e60f4c7079f00eb59a870eec48d71640a89d171a896","2db51435a699cffa904e668db713296300103eb68eb4c7b57d9175673290c28f","c82774c75fd8f0a8453e3441a3a2df817f5db8fc2a7e0d5b4025c447715cbe23","3d196011008cb35d3d6bc62f6e8178f4bfaed6e03bc2a64bedb5fc7147ae958c","3ec2a1975fbdc61c009566ab7142e5dd92945b407443eb73008a6e0f422c0a61","00e49e2d9e77c247cd6856f1708e4badbef3922c420843feef3a55e5d9f13ffe","f12456857ca9139b39c29141c0ae6403cec08649520890b5281c181e40db8244","f0facf58f98febf89a1724213686c54f8881a73dc059acef5fa5a6e770243274","709d1dbfe1c9c5c689dd91dc9709737e876fe83372f2608c2c966a1349b5fb18","df53e721f2c859b8dbf999ad123dd7b426db3a01ca7e18796024e0be8dd7eee1","957a814d65729a44e20dd014e34dd899f5d3f9bf5490c15641ea4fe33ac8bffb","2c947d51aec4c376f6777aba55cf20eb662a71726d6fa5238f149faff8c46b99","736193947ff3690ab548ea0630b861238ad30b118a68c927f5a45afcecf76ec7","56591e2e884fafb673887bd4ea4522f34e247cfba483b0d5732c1c4448e2d97b","12e7fd4b664e48bad66ef64e25f4321b9997ba07ce7dcdde735723a2cb92add3","4319b3a2e847c8ae162c2f43ca16eecc9e2fe012903d03170eb26fb62535f488","8d1b1b3a5e21f45c50b9e4d81648617350f50713da05ec5c53c42be53de67823","cabfa66e2059b7131d24631cedf2c148ef5da81670d7bf043739a4594b0b21da","2ad698fc4a0281c8b8960710163c186e271942b75eeb824b4f52d1f9c18b2b22","45d28611d4d9a75ecebbb01858b6e358da4aa43e643914bca1f87fe1d1cc6c80","3a028db0ff8a34cba2e1c210578069ecd008337ceaee69bbbc86c12473d89414","ccccea50f620b1e56b08156724a1a94e567d45508d31d39254c23dd36a94f7cf","ea495540dbd5923a56dfdecd8640a2ae184a6e46e0b7bac86cecd3427a7cbc61","cbad9dba1273b88b6ea2607fab6b75c720c8848acb245de96eb5d01c60588c97","6da586222c97b893743b885bb6277102a2a6e5b0f4e8577e3ad18bf43e1227e5","91c4645551e8280a1d5b914f4fd98cb4ee1596a9cc6f891fc648be34702c3871","c48e28d82c22f46175446a0a9bfab97d8b4d0448d30d6512356fa726d8613003","4e001356d313995ba5d22c5877adca17bdf286079fae6c0601f3ec46e86bb7b3","e9aa694406c00009f8bb4a8a29235f219b5cb81c34184bb3ee957764918aaacf","7ceded6b3ee49a6569f7a5df3b18a67694e6624d31d83ce8cb4a4f487a7bd1bd","d5344b38ca0ce6978b1ad2e6ab96ca9370573001dc12b52f3faf2f117b15258c","f953f02c90bdc74889f383d14d3da77ad53d7a3356b144b5e9b13aff44d6762a","070aec45444ff31c6c78e433f76bb20a2f0521a0ecb99e37a96e47b7a39e4c59","8e64669869b6e1b5cae2b7e2080c6a0309161e465692357fe19f8ba32bae782d","e44165a63ebf8f093c3f2174caa6a7e5acf1170afa71585afd747092bf0393f5","203ca4bb190e0c2f992efc00bb40a0a7e18ffa1815332b6362c0b2a4eaa9d50e","0ac3acc6c90dcdf9d3a0d930584f6956d39dda76dc150f9867dae347c01fb1f6","9a1951b0694be21f193c8ee78f598eb94a43ad93457d81ad3a91430cfabb7340","c8fcde56a1d77e75bc717fa955bf3599ef56276b3ce1a8d0d59f336960ac8a1b","70f694f85323a0f7aebd36116dcb9aef2d734e61600049a04b63ff703fec507d","7caeaf3da59c1edfccd64ce89321bed55ac4305e08d75d7f9303c239154757b1","597b2da170ee7f119fa3c5fb3ae12bb1e53b9f680f63cffaace97e8a8bfeb96f","d5ff63e4a2e3d6acc7d6745a127bbe3fe83582d5ecd6c38b132c95e0972a146f","a0039d5e07a8547253df379605f359df34d2c15bdb18e05c68bd104840811076","88161201850a32202212dedb6883405313ebf7e88f1d8c15e5014b39ee9a0078","8e11aea6c1a377aa188ba244cdb09ed2ee0484e0c51bd8fecb84157d95b2276d","ebbf6d898eba4ee0c5f56643d9030fb685b06b3d2a9af30c3cab3ccb26bf4455","dbdc1a1aa099ccd56afa36b000df3af56a365a2e098cf42acb3ccb5faa3bf00f","17bcf414ae62927a0208af02a1b7744a4e83cf060d32ad4755ce64738d61b69f","8227d03878d91b818065aec2a4cbba98759a3efba65a2b19138e49d716e5a5a7","7bb82b75b067252c50421e2e6df14409461daa8021b0bf8445de19bbdd3edf96","de22662b7c70dff9ef5a9c84bb392d67b044a160fe4cd7a12678eb2ab2e99cb0","bec4e539bb1da6e4466e65ed565e2a95442ce67ab16eb416f1b12472cc7ac479","eab40a2564d526742a478eed31b5f38719f27be17b68c7e6df96e06df1a1d0ab","651e0baa0dfbe3d32026f14d2e143e68c328d4a54da7e134f1eacafd121b8447","d4de6aa7918b76f00323c238bd2b60c505969546e92edc311f12c1147b223a93","b56fafd88184cde2ba20bd2a1fa26a0a61440df3705dd29ef241aa148f8b6952","8767dab6669e60b6263e95561c8d8f9b685fb132ef39e1439615e39bdc6f22e6","b0d581c82eeeb1ad076442b263de5c78485a8911f6e3dad5b382ab4d81c9251b","2541869881525e60fcda6c1cb5fd2ffd463cafa784b3a065a399a148d49796bc","ee3ec95a69cf06584c2c6e6d9178b2e9099eef7672270d8ae7d67f7f952814cb","bd7cdc04f8b60d831b7292d8abbd95f2ce2b64ad558bffcce61edf1f5a74aa54","3d85076a0c33d6f933ccd3872845bc4add2c4aaa986899a7219ac365a1a47673","c37a891d346dbe6a116084c8fd72d3b9618048f3db641924ec62ed8004b579fc","62fb099143a678b45cbc38f1d9d82fd6a7159064afbc5ecf5421b10c02a3b671","1556e31b98321494a6199e496cd62cf3939644a8069b72946e5355ff9973e68c","6642dd46b3c5f98fb8e712eaa2d8860cdc7008c47adce3de3fb151673f8b5df8","b5e25bcf725731e226b0dffa478559aa3a1d9ed7565e7182019c82df6d089ad8","6eb449d224a5765991ed3f00da4a8e365f7f4744011678eb4468752f49c8ff55","e8b5997569d17831a58e5998b1aaef9ecd16f955036ee939503b02fff0023acb","069067d4b266b4ed33873c3150278d975eb35ccce76713af351b5a58e6239523","efdf0d918e3a873bac075c295d556b1b3c255614a7b8bb18d9371bf2fe58bc20","e750c528779acfa867a986226f44d1a48ad9655f7ad16776164ae0b244a246b4","761fb1b26c4881796eeb34be3c221b4f95ed45cb785dce432fe768a73057771f","7af68f16d603beae2c1facfbdff0630d931484316214dc5a916d7b0cd9a9458b","23a587e27e52bf7690467a04d758f56d352c19cd10390d9283873c4db2066600","e80705540f0784c8b1bd920235c53da00de702f53b285640f9ed0add0d352141","75e45e1eb92b81dd90df2b04f0edf98027d2509850d7ab1afbbb54a9812ad3a2","8f95a1ca16757c8eec472a99efcea19fcf2612825cb8fda75c1b38609bad730c","98f99c3ac0ba2faaa9a315645408eb2080ca1de91c73c0333c8c2b71ab1b9e10","742cc88fbfd1bafe5daf719e770895abd1d3bd7f3069e798831662bee8f34325","bfe1f72d72a2aef5aee49721a7889f964725395837ad80d00ef000945f89c93f","100c2f9b11a935eda58275950bc355cff48626ea207ad6ed77eca3e2106f881e","f577fca4c11b0e8ac24dcb85297d9a93d022809f7ef41ae56e7f3354ac3836de","bc27d1ac93da39bc492aa8a5d8763a40371ce0b684284c2012d68d38b5c2d7a5","333abcbd774b67a3d8cfc74010b6dc4dcc81566234845390c164002ebf654957","196634c2c59c0e622c585e27fef52f38f53b0d580717880a48038719b5bea316","d3457744a704a869417ae2ea3fa370bb7702b5cea469dc66ac6d2f13bda86bef","cbcfc1d0d791ad79e3ce63af9a28a4a97d66aae07c98067f79eb43d4ac1f21d9","09ea63c4bbf3921b83df91a5e44b7525db030186cecea14828e663479c9ec721","3191c985ef62014fcf6bf2d9ecb4d388314be814c02a1f2e61e51258b0492b8e","91543bb08a8b3820b0b24ada224157d9e5296412584792a7e2b4665d36cbf6bb","4b64ab1c145bd2e749cf86f375efad1a176fb87c0a81c6a075a3444484c672ff","c6418057a57e24f0e7fa4a88ae63bd12ed7fbec9d0b3fe9183dfbd18717b7a07","1a19f13c461053526e49ad5f862dc2b7a654952a538993c5758b751e681afb64","61697bf787400fc5a52ca0245be444f5f9bfe4caf9555f5c707af72ee90b997f","febc8d55f3c59aac614b16d7e20d3f4db021533af1639fea45c724ed51d6e1f6","57fe0993a42c115793fad9a53511262f93164c6365790838a9eb1b1c30d90ba9","da8f5f86aa98df54025cadf424f156ea22a7b10fe44dc13877757ade96231fb0","53199d721ed05ce1587ef264709d707f0b9de9164c218d01171231f20153a4ff","a92ea4c9b6338da2101ef4a6d48f1883ba6001882205cfb80e042315c5b526e9","b2422a9563aa6f6801467c55f5afae0649efd34462b96b7d0ce2b66d17a6756a","6de2a81407e9c69353987762afdef3c9ba65c1a8e676f5756413977e63feb57c","84ac3711b2e2dcd512289adaf17cd9cce9e5094c3c2aa26e170a00ba2b868461","9d03902e555324be467f26dd785fc2b5d7608e73c891941f8edb9902564a58f4","20ed7a6271fb28e9aa03650ae159c78f19aea8f5bd40ff7b8f001307078a7a76","7c43c2d9536ac21c2c1ff9157e54c436668acc5d427246149232cce7ba4d4f09","3a8b64fe252b2fdf73c23366022fd1e3de9122ac7ae53359a200b695211476b1","fca44252263c04a11024a21dd7846cdd8015fdca04c8e134670bfdfc2159aae0","54333fc8dea208903a12eb3a2eb90c96a85d1b2b48ab0eadf73f85ddb288bf3a","31b85cdf2eae65dfdad0c9e8a37a97d5e33a9f54aae3d5888b07b19a43055cd3","5f351b90bccca19a363993e23b955389c662e993eb39838f93bbac69211d0154","707b2628af532dab1b2cb276810aa5f7464eaf6874fae5d7a325243a7eb72c20","954d8ae14048dd443054894b3a04fccfd668d00df26ceb898c8157f3dcdc4b9c","f8f26916224f695f8f09da6dc727ea28e9b1dfbbdc0e2430c8c87e9c026f0586","448adb291d55fce06d09c99fadcfeeb7c4e68bd21cd0ca185a920c47e23ff697","0d81ce8f3f8f21533d4954c0b5c47a30b67d3e2f4cceb6bec171f4c9b10480b9","aea7ef3fbc166c93a05b45051da09e5f696a02db9f807fd5fcf2a3eef114320e","5ac619782c83b8ee4ea4b958e2d26e5df67bb6166ea39fd55e7f3586eae4dcbd","e648cc0ba42b6f18788088a10757b89e33ab9d308df3a5cce8b8e7ff15e2b22f","8e00ca1bdf7a90e43b84a473401e6d29855dc34f2186bd2737c4176d877d179e","c1b3ceff69c9f834bce028e07c45152a8f3c92ebddad5011427b0d6e11587874","b96bec9e77061e5853b4fa63d6ea8cf4250773702676e300420b7735c34f9901","5e91b06300a55effee484ade3afef7959c5b7d88c5c099e75ccdbe4fdd152cfb","61c07905bea1e63d21ff444167bcf9f0e390def2594627a1fbbe2a9677e4641b","7705bb666bdd4085a9787d5c2ac6b23020b3246115eafcb4f453bd9c1448edba","30688eab034d1aa3bbe4d8f2c7f462ddaec9f30f1a38a306a4728a9a06a58b11","2217ae539f7c59065b654206f44d4f681d38a5039ba6bcf4be8d8d40afe5f537","f4d43aa1331941b39fa38a255a16eb8855cc2926ce02a73cc06dde067c473900","9921f71db289a60c25a161d036c2885085cd3f06672d9913b37342333993cf3e","a11be745973acc0346b8bfd052e2693172c6b2f63d61fe43d27edc605baf9616","9c951ac943d835e83b7774841625b44c2a5a6f0cc8b804f30b24c5a25a44f8ca","f75ce377d83090f4180590fe78c9431b3d9bdf494373f0418c58e62937e890c9","bd66d12958f4fe9cbd391d9abc812bfe6793cb5229d3c87545a74881dc3ec818","f5e2e8a5acc9b5038db5956d10071f725422f41834524a9752ba7537a19b257e","2ea50238f239ef3217965ea0a5ac6ffa2acb94bd03a912e7edae4cdb90496b16","e82f01d51ac6d7fe5a4aa92d42abe67249c17c3b4bb8aa71abe6c2fdae1e54c7","afa60ee9164efe27fd39fd758994eb8537459ed6bd9c9f0cbba3fa75a14608e6","7d417f30f3db71a32c4f7513e80681b7ac9181aef5b8d33625cc96a533cd38c5","d35dbdada2eef1ea36d48afa970f108f0c346516a9229660a8b48cec9b74995d","0fa6899ee1f2be4f6d8641a444fbf598af4129acf30bce77f27466b3d0a86cf6","c0c8c2902a682290fce1fb42d94f4f9db3ee09bd1797289e5b636b2b2f1317c4","e5c046694911bbb40b9a2e7ef94e9f9ef681df18dc26ad7d5558e9406d2e4cf7","fa7d28cc714e9d5256d2d5d2d7895a85e5db44987b41cc39f047598dbd3e3fe0","64a6b0d7cc063f53f242f630935ba001b5d6906328466c5ebf6c7daebefb5bbf","6264dbd99bf7baf85a6d215eace6fb1307f3d3df6464dcbef84f50744516608d","057dc3da750916d3983709948a7b5a6ef9788378d38a60bb7458b30f79101800","2fd7cced404fed3ec31b730d7cb8dc8a2aeecee576d73a719ab3be8d5f5d41ce","349abba9835b8eef131b9e084130e20ecfc2a4a5480340659f0235a034896908","9ac5c75774da8cdc4d6e0a7ab1a775a00e8f8b13d26c1eecd13230f3882668fd","1070ecda3d1cdfc866df5e21173047a27f0578103362ffa24081b9d26a1de794","650f197ae461ab7dda0b980277b8439951493e0f654dba3f81cd5fb75e65f884","31ba7047f9570c93a5fd3d9264cac8e15ddc83d28f79bdb52fa55bf864c409b9","2ee74e25034063383c1c0d03fc49e6451405241b0596d074750aabd4325e07ce","1a1657d991b048a8ced17278f8fc500374a7094530b6f648e8b96b8ce7d1b66c","183e0a4b07d3e6b6715344771e5a4e73e516246dcea97384e5349c42691742c8","3ff0ab8ac69e2f98f671cc151c890b4d9d4d3a716785416c6c68bf4f3b163d15","435547ade7df6de0a3cfbb2c4b925b9d36f84568f3728bb5c05b6de41f228c08","0fe4061cfe1eab8c542bbc0b2cd2c203630c5de51941d8b8114c4428505d6135","103c4a718e8e0dd8cdc45f7b565a29f84f5c66d8236c040582785b9db2484355","ad91a831d9b7e2434d7f038137999f4675206f8f595f0e68b4443a089b357cca","ddec19525a3a6d2d5128692249af3ff927989304aa6850a420cea5d655b80ebc","0f1f2bd19722285e46efa6eecfac73477bd6e9b7e2ce4c94063fdcf457e5f48f","5bd3e4d7623ab6130114cfe8393fdee2d902bc78ca840443ae8e205b8961e0cf","f65b67af065b6e88888ce795af1e0d201276d21a8d8d38dbbd0eb5432ac0cab0","601d6bd5c983fec28baf7ce27ff7fef77b696d5a7df19ba42f58961ed037186b","1283c26efd1597984ab5c6e9c3be1fe9546c5d61648a73c7251b5d81c0b9e153","b68e17021361507cbb11a8c5b1d7291c28e5f97a3a7c24520026b57b37b88629","e94ff61f3c37cebe7bd248cf511778d06ec6714e44f4f6b2cbd6b27f7ed53b95","66a542affaebb09a0909678a413e63f7a9fb875297853e6be5461e40d7eb9909","2609c35f3d947adebe6e486d6d8b5e7b2864a80bb99898478b6fde940ab71e44","33c104f72b69cd455efed2aaf9e993bbe871823743538560692131b620b9b540","a66349a9c6b3f195c510e6621a6aecd4826a0a98d3d5a8b20a7d748eb8263ccc","892b371df653d6787b8449e611c0206f561c3bea8fb3e41eac0a6570f43bfed2","924c680fe647c2cfe4ad3944c84c9b36726dff2aa38f87340f56cc2a92eefb06","788dacfb72816440b160221ba68f6b048b5c7f4b8a9190e8fe6b35f1c9e99f61","6c31318d3e0c181c9b859eeb8730701e7942d521fc9110873c6a8210ed9e2464","bdba09420c48524e7cdb180ca9a63f7cca6781f09826056ad2c023de8a4540b6","5d9aa25744b6d7ecd29738a79523e2aaf9205ce94481f6fa26fbac63b12c8f1a","2586e841e38c5b310cc5728a2d56c56b47f4fde386410df801c01507594a05c9","4c17c33c33b043e950a9cbd7da1640fd12b9bdcb484eb76e4bf346a11c35e18a","df24accdcf6a15915053cb96127f69f7d29fb7286951d58d4b8ca9361f8bffd2","e30ea0601f5904235d46202c56913cdaa08140875b16d75093f8238ea0895caf","b37f4f501b287c3e8cd36e244c6a25e1c663b4758f82047a4e4b09b349ff3979","679c5345cf9eff4a5b7f14bd5b89e4bf13d75ade530b8ff8fcb25114b6747ec1","6554423eee77326ce78a2c25213005dfa3f8a0703bad5f99182e404623000987","2cb13bcb9a9ee0055324c4f92b365fe3871965780d70f4f5f423833a0c46672b","2a412555ff316ca06ef90dd936584f7e3cfde321d9aab67c7dece93470d3ca4a","531f7aab9b06755e06d996aee169cbd2cd206eb4a1016065200b9a8b544f08f0","cfa161efbf2be5c56a49a25c92ecdf29bbd085e027a4f9c4d6dea112c01b013f","ac0a84a5b1487392bbd89deaaf75e37ff97badb5cebc5b125816cce6c994dc49","2d5f1bb582c3e3aa1c94755a93372f915c396cc68a3a87767b5648d8999a565a","b972bef785abdf30030b19f64b568f7952b8166dc01ca4ddc2ac6919a5649a6a","2ff73f906d4dfc23e04846d7e853863aa68fd14606a369d14d0ac23fde145071","c9bf6475289573665a13651f96f3a4d935e63f755f1c66b567e20142af4739dc","d4083eab88a986f2fcff672be3477a79849f25be3eca5a0fde6d745dac3fdea9","b9d7f8b9ca4ceaaa58bebc717080cd9f504d68096195130271050786c680639b","e65b61493d31153b25aad756d014579d88ea4d82ff4672ba53b523c545918a79","cb41a8d1704595b290fb4bda78ff88dd45dcdb7a039003eedf7c4d50d0196866","f285f3733702fb5ef2c430ca75ceee91c8c221a63f10efe55401b36c03ad8464","d0e47a2e505bc9c18e2e9cfe7c656d8a343972516e35d05a905d4e9037c6bdb7","81807c39ffddf0f980ff2c71b5fce8a5f57b6d85ee8f9860a0c00270f4b4b3ca","931695b9c1db126cc04ebc9e53e30bcf5503cf6e846be941d431e51d677a024f","d2d541552277aaeb140b43c1c158a840ed5fc004743311830a7802e8038daab3","2f67d6611fb61f4aa056dc54938196ace86fdda2d27585f7dcee3205b86ba219","6d86ebe5bfeafa959f1b1eec44c5412a065b9305d42dee581b5282e666a5ffab","3717cf65a204081e3323d5592b6671cc5b1314a2d2cc96df407adff995f716f3","774a0460e08fcfea8f89e6e030974df7f872ef1831c98c4beb129242a577df1d","341ee4fd594942fb3b4cdec6897d0e0758e90c8e66b16edf296513f8115ab588","95578ac9452eb3f422aaf44830dea4704b9f2144f05e88c0000d4c271a9d6589","237e57570cc3793a9966c53c9b5d83f940f87ab197727cfb4b9ef854325aadb8","31a12c4f4852605acf229c9c3dcc5d873fb17c93532fdfd8ad542765a172982f","4128d4e6d5485d7b327fb5381d599014cdf529acb6a693dcb25a74b7c22867e1","51850971a27368bcac5e76b687c81d9dd33c32ad93776efb1fea6010aeb664b3","59bc67c98670c8c2e527f4bc135f3addc61073a9c86fd7db12655a117edd4223","27ea3657821a5a4ec97f8904db29683d528445795af136a8c82c2b00df434d5a","0ae415dfb5d523f2f40994716d8eed4399a63ca0a99c53adf341bfda84ecffcc","3afa1cde2398e3081bd31d85277ac529e66cb78cba646acb29015133711039d5","669cb574eb8b1b78a7bfbf772cd672a4495458fce3e3871c493ed63662e1913a","dc66fb9d538514e75bf3750edb6648961775139c5917217cde4b071666e27e8f","790cfcddd6b7cebbd6d1bc6e70cbdb92acf1b4ab436e5e5dad3437c81a51c2e8","2bda98b9e0c6042bef86ba9098c5e150a7a1a4c56e2c4267fec821d99c06e345","78fca22391d0029ceab9c7e424fea085ba38d0cdefacdd413d04bdb80974cae7","c4285f0b817f5480a4ffe86a977980018dfa65b8918a33af4d8a28150be77869","1de18fad1bc6b35862f27b59df19b8c18bc44679178b96ac0541ac275c1b9ffc","d78fe5788e28252b6a1c9d0c09e8ba556064afe2ebac88b0d3ac494d723d6363","bc5ce122aa88a6a2b5a60c538abdd43d2081f1bd7a05c06ee69ba07deab62133","5e683802155ae9f4987b4cfb34064d14b4f7b5d7d35fc3769bfe2f5fff69daa8","8a305d77d5904e620970e9ffa8f9180c06ca5998eafa24973048b65b0507e025","61296e04fa2cb74b694d71d82fcd25416bbbc7c4decebf3e10d521c7fe27a976","0b60e40f6a5f3b55a250d3269e09584d26ead289f387504de4198bfd8f0bea7d","15aa83eb24e7e5de1979c6c269f48f078019b6909b49d178f5a599da38e5e8c6","f11f9a1d67876a869d99f3145cc63cd1db5ef0034cdbef3930366d4bedbb4d60","01846a047f18ea3c7bc9dc4053839960085d632d48b65bfcdeda3305e29d185e","4ef18ed259b50434626c5c014f51c846649961aab112fe46974d34a8c1555d71","92307dd94cfb0ac601d622976f10278624679021d9b4c6f85a45cabf99ff11d0","c8db51a33c2c8da0f49596a501a82a11fcaf484e1c8564a3245e2dd9383a2426","0b52548eff60d7b381e8cd32b4a13add2b624b58ee09ee92816ff74d66194e72","69da9257d179f2dc2e1bacfe8852eb4301fff47b438930c1d275b949382fd912","cdc3c58b626a97722f44154266a41578707308aefd4bcbbfb07649beadccbf49","f56091a30aa030840992cd83fc810ac341c7911e9976a2d51bc3ac5ed8474c26","16d160f0397cdb35f79a6d6eb3e2b6c059a0557fa0f67ac7c08b48eddaece743","74fc1b18fce4ba9830abca70a4ab6eb42b064541f566f4460d7445ce08048e6a","712a0b2b69621dda100e3beaa0724648c0c617b9bc70e974e2ddf77242b9dc65","5fb9748f22a366aae8f1c94020c5b4687be9f63f5707475c262f535cc2c04714","e56156d303b97504d313d87019b1fc9b794bb9fe65be0ba5d92b78d9d976946b","2d107a101a7cbf697f722758f62d5ed250227d00f902b77c6c3d4efedf39178f","a565ea5ff31a879c0d7d889591ed3ff47af47d5f882d0cd6c464c00911c80a7b","cefbd3c11ff2a8d66c078d323f8f3394a4ecb324d05910e40b2fe15e324c7b9b","d48133c28b8962eee96e27c4fb4732d631d6e49f608691c760ad719f9ccf0a42","e23d352259fb0baf62376748faceefe9fdab1d03e48a9cce74c7b0562aaee25c","dba61a7e471bf5151825b2db98cbbf08a697c8e30e3d3323c7d56066df0e7375","d3495b28f188ca07af6ed2805c0af96cd3f971077ae3ee2322ad7a5bae556abd","270068e563d8f2b2d72b30e4eba99c1acad69eeba293db0d45717201c8d9fe27","b7e4785625d92f0b12ce9302e34f4dae9ad98149e6a37fba6b9789105a56c217","03f71ab7ec76c26bf79e35e8b6cb1461192aee2c53dd934be05bd030a11df208","44de21a271e7c10adad6275bb5936a4d69baa0bdf2e9dd5a46a97b31c3c15319","ad58a5c0408f9297576a7e5e8c63189a0a93bb2b33bdef332edcef900ce04d48","ebee18397576d0f8ab69feb9fb0cc7b43ad2f5b73490b1764507bfb5d92f765f","9ee0c771688eb96dea9ac24adcb8ba5b2d048329e00da4b97c7c7e28bae863b5","8c5e22bb09bb7e396fecbd16438342715a8f2f8d747a0b8264c82753fa610f60","8e8686a68120655842b28c1cd6168ca934a7cb8031db9d2a760184fa6cea28ce","864dec0508df24ea1564c279cf063a65ea9355191b9d753b79fb98b18a864661","f964c8f47956ebd6790b5f85c753c3a02ed97f80428d458da112701efa531e86","b8aff80b84648fe9d0f5e7cf0454e79861c953484dbab32e467012e4912caefc","97c199f54a25929c46d500123ca918b6ca2bd3f7f391b834415a584e14519895","1fdcb5089fe9fcc3a9870d120db60cc99aaa60c861a7751ab04e808cc8b41fd8","edeacf69127fdf1dd68af8d3cae3826e0ce9d191943dd50d0f8090412296df02","a86cbefaa20e0313956af4ad368cc8405c5b403e72b46ce626e232c0e387cfb1","353e434635d5413f8cc0cc02dc014d2e80518dec03beb42eeb48edcefa3d19d9","07e3033c5b7e4d1ff7a7c4a97f7d4a0d763d65e68b2f946086e2dd8b77627fd6","1a5bfd7c176e6619f7c1435a5ee2a9c522666746c10e03d8266811d9f603484d","0d6749f9522cdabea764e7e4ef90f36d15cce8d4d6a130d82de493a500495ca5","adb2d3fa91ce25b3f0805cdf515ba577ee52e9b7ed906f0904c0cb8a94718a20","0ddf692a51d6051318cf95b1b6385fc5865d40da66ab416b76c3e5a7993bc3e6","541dce26752db36391695715fd07e23ab8365fe8f0bfa22fb1988040647f7220","eff553e8f5d9a743ecd52dccd4e5fc9dc1ae0e3dda832721d6bdaff804ea40ea","070372ddfc211ed2eb16aed6ef928a5d0de9da581356d944aafca691cfd3775c","93c3f399a49a8f0ca7f59b77b20f15e2ea646d76dcc1aa67b016620b77dad7df","e8c5583f4bbe3882c1ac602512d712778c8e79743726e649eee3f7fd3935671a","73c4b3a6aa56bc60488233c6ac61971421b1aac371961304e7abe89df157b8ed","8320ac9d1af2097dd0f146f5a61cec3188e1fc87c8b06150d56440a37a21aaff","b2784440af239186fea1e9e8d79fe8c68a393774d5ef7e1064f7aac1b20ffe6f","9831c9be2464a6dc0a47e32f15f4c423e169236ff2d76258cd0b4044bbfefcec","32bf1f74a876afd0ffc272e5b3608fecb1da2da3bf29abdf0b63fb79a79503f8","90acce1b73d50154590d12b572a3e2151c6cabfe08ef3b907fe5921454fefa02","50bc3bec14002f6c484f88abbc40521da9397f5aff8f129aacb0f737ddf67ea5","e06a8867a9a2ec503f9b8614734bb82e58824a4a2eee94cda1f522767993a973","dde10b7ce7fe0f6b95044ecc3525d751f2c7afba86fd61d4d3f9f860a520987b","030f27adaaee1cfacca3bfa4a177214de5ec43637e2e314c273adf7ee6151458","750eb28a121bfda70e7c697d50f2df3363e9d9b2b74c81088bec2d3bc8d3ad68","6472b5d726886eb4450f7ea4d781d722229df8e191d85f7a6f0d703b07c67bff","7246e9b6d9fc705a0990e7a0b6f92e8692d9190d3f5aedcccbd597d5ff0df7c7","d2b04e90889d746abf99b4c59486793f9fea741b705cfd4edab3d509c126477a","3405f252f40153c837e2e7584b6fdf135dc22c7b0a94061309e608b213e9181c","17ec62b7242d227e11336113296b088225da6ebd6cfd2f9f07bfcc9900b9cd42","dff8f02234faac11ec1098f7813a2f08b95b37d472a8eddb9864c2947ee28446","9a6464da21e25bc18de4b285b893c0eaca46a690b2830617a8ef06f96eef4aa3","125ef4d1e5c1a420a80daa8f1b9871d9068ee684d304b38e4351a71ac04b8cc3","502b5d9948de17a1358e68b9ac80dad58590476184f314b2e440d381aa969745","085dbb45b93a4cddcfda33f5ceeaad53d4ae19ba7503e01d0bfd6f98208b832e","bed2ed24c753afb04dcec65f7568f971d84362fb51fcac820e8ee1855ea76bc6","1f222372836b1ed57997de12464e9e11dc91ead0c077c09520b48f81da40b9f4","ba5229b45bf792e65a16d37f3cb86da8bfb1055336f50c5056c3f3da192975b5","7c4dbd82e16b34a81804383b9c28da2cbfad04ed7882ab654056b58a8ec94ec5","8d5e423573fa5dff24971d868f62bdea17b9b4d953b255b0067d312f02895ebb","f939621054f14847addfa2e3017eaf4c62c28614992907200a497167e45333d9","7505f387079b2b2d22beb29a517bb8e1ae68279ec7c576d122d24b0e3f27727f","a8f7305348698c11d9a0fc1839d4cbb094cbf31cef96ee76bd883b0e2de243f4","c29d38dfeaef89564f930868cb75f9f321108b70b0b1c784e168319550ec0285","a646ab41f1cf62899a9edcdf7653108fa437d788767fa8d2b5f0ced7524ea21a","401edf8f46652f4dd13a4358b011c8b887f43f80ea0c5f6f082048a622368262","2dc0752ae058cc4f62c929b0b24866f65bccb221c2b1d8e9857ee9622b7f7b82","3fb078c92a847d104bc0b710521baa0f5112531b436717bb16f37cf7e6882388","d62a65c939304424b6d6b08ab97fb488dad098062c5ae90a64ce6e3f6b9a2af2","cc41b83df5c3a4772e40d094c735b8e795eb73eaba597ddd07efb59a661b7f93","fb607236d72aba12bf6df811ae50b7ac780a1ec06239525c5aeaf5be5ceaf3b0","3ba14ea840bc8d17e5a2de5f0d1fea1b927db415c737bfeb2a4f46e7abe61111","890b4f429929d54134e0e265d13ccd8828e361cf1c37c046bf0764a0bb22af19","09db36cf75bc53cd67d8fc8722ad858df44503d3167b5d49825cd4b8be6f4076","69254494e4c555ed797cd9ddab57a1b4393ad312a1eb6ce4dd164342f6cc785f","5db512ef773765ace42dadcb265ac20a31cd6d8d44fca6795d75b3b4983d483c","cc62668f61863e8c4cfb5aa7edf1c675af6c770167861148223f74d6cf4a52d3","bf2881a9a7f7c609c2451abb06f759df2df99fd68613333d9815a03d06dac3e1","1f826086c3b28aacf9de890d19004b2a308e0e64815d86a7f8c23ff37b3a814b","a11253e1d20bc720789d85374a8f3bb2fb2db3d8dc50475017f1768f9adf9484","c651b2993749b0e1d39dc5e2dbbdd662def0410ebf7ba683a27e1d26ea23809b","921a77e8330dc53decbbb4dee54b81654e7567a1711984d6fe3bd7e8997af8b6","56577c24f2d8711ec54f1f25a9c305aa6e778ce192b30c7c917ad2912af0a850","aa7443532c7c4fa930709fe30e0bf642e4040867b0c180278d60cd04f2832d20","2f463fd8b55fede3ea03465e7f8e6827ed6b735e6bfbfafd9176be6c80b1099c","d3afb6e0fbb2ff982a1aa1f8192754d1fc26f5b80c9e1b79fd29f60a4c8ee4b9","75f45b4569f64e7f8bb60b80aac8a4fb58c1268f6416d28dda85024753d9cb05","1aaaa6952692db7d7ed9ebcb1aa98199632d378d02e28cfb0728d977559119e7","ab63739e2f5354d2829ece988d74f377ffcfd9072580c43878ae56c20a15e12d","39ef22cb2067a2b384a86e391705ea84f7481eeae0fa35d7be2545e896af3bcf","b78d106f6d66731bf40b6cfc8d42c9c88d2f64b8b539cb8f65b97d2dee5d1a1a","c3fb4424ec154c8efffbaed24c29f8295d603567570da0f6d16fbed0ff49079e","f408fb593ad8b84ce2ac6040641475658060fc4c0efb24cc05804a1e45ebea88","8af6a2bc2bc301fd69996eb8118a84476f294da0b1c6d3f811686cf1fc83c23c","7be452a374be117cd1c563a9312a8fc45b46595ecd71411886fab30717b0644e","ebc138e51212ed0f884ac5310237298c50b48d45b7902597f85604ad6851cff6","7fe903d93d7eadfa7b0c41249c02bd805af04faff7c85916999420546dcfa3bb","af357489e64b057dc99b7f42852aa61972d1db4836a8c284c88db68ca8d9abb7","4cdbc6e2f9ea733c647c4f134b3707a66d7579455e2901dafb79f93d9127fac0","d5148561c435367869a0e6ead443cdc3ebcb36cea7f35fedaf6e2bcf9865e436","fb95417798d0671407f8316a4f8ac1d9ae045cf4a65c19c62201fa96b49d0e7c","e5145766e9eecb5337255479b76c62ebe8adb7d46e8e23ecfe956b2795774ada","89b20c074a5abe9208d39e7153ab01726c44a9fce77e9b46bb86f3cf4882ad0f","5cb64c0e2918564a101a6445ef48121d2b7c4d4e565565c043db6266ec2a8d72","21a511987dd4319b5ea9a3723c07c5b8598b5a3e0fc22c720baf74759bcac3e8","3eea6cbdf32fce708775ac2c4d8dd9faf964a0408ceaa4f86f3ad2380b8bdd39","4c814feac4977d0a7f3ab04c474079b022d653a9b58b11ad1e021f2abf6c681c","b0c64de8a3414c763ade5430cb9cacb7de7026f5e0abc69d93b3ba9a7423af4e","311cccecab649ce5438dfc8d891bb192fd9669fd0a58d9b8b09538978247610c","c77a4380d0ad31b3191a8f46dd27ed85605a735590431bd39a98ead41c248eea","8075f3d5502f29821e22b7df9c09c1c1f79651b43fadb00a51de0782adaffd59","3d61ff8c2f62dc2ce2bccbaa8b25d0804b0af705db707162f790d32656ac0b7b","72e4a806db5cfec09a48c5a87a242e6ac4d433a79413eb8cf0bfa9527f9dadc5","64c249438441de30089b1cb2f59b8b6b1e735be5bbc5ef2c1c0aef6affdacec5","abeeac5d26cea85669a4f950d633b48cbe945d151c5766b5570eadf50de95541","e448f86b862b39e330b447215e46a0e16d92e0000144b7c6d7a4960ff7eeaf80","e508b6ec6635ffac325b6347109743dffc0908279d5262907e8918819652d60b","ac9492e075da7f2a119911a4bd90231f1802f05752c39c2af7ab3bc0ed54bd5c","ffc7197b1581632257868a283f2c04159b5686ce518500dd58cde12ad64498db","4f80b6bcb59a68c5b1cd388a588e20063a835471b37e6f66a5f479d557dc21df","171792728ee2bad492204152640940a15304a58749b57459d49840afc9c4abf7","945505221444935396d0f0fe2ec923900ad3e95b26456b32b3b7c60ecee71a07","424b449c04ba8e623bed30672a773ca379146f015d5b669b220bc67d2b1e107f","6a50c27254f43a06c80822a0645c0e8ec85bdf9c111540c6762a784a588c0201","81cbbaf1089bc937bcced90dd4f018dd0c11bc66a234e06b4dbaf8932e86f512","777b0681c7c1fc6c88a646f26b8c0f9cca1a6a12ee596afe86bc7a86db0ab110","572a1f4ffc78bde699d6d6e3192d3ca8588ecc93d8f74a0d5988379383c19c48","5edaecf61850e689c92168580fe06fe310b77280c3577e85fa937f4ba1986671","9f7c544eb1e3e53559971fc5147852c4b7b08ec17ce5933316f0e64ad23bfca1","8d824c8231d69baa4e7cf359412d9c42311ecef21106b461180e07f56008ad47","dffabe54aff3652fe5bb1577c95c05326efc4fd3f768fc4270bec3c8434931b5","ad5b3942fafe6c3b6cecda21965d3a4e7816f839f0e267e094c97cb6273c11e8","0708f9a1cf32e7e142124420c19f3c1c0bc92be4158edcd269553e4e9164174c","f40cf16f9b6d2274dd6ad83e0679d51de268548c2f4b3f64a7b85b025edaa705","a8000efd37b9ce8f0827c6e7e662f264aa578685233961d3eed41e0f34ae59ae","dc74f8b52593fe37c2a1ce212467b1e5c026683011cd6ee619961b3aa30af52b","01a54c0f358c3c2f704c1cfb7a9d17d1c1181e3402cf75b827967a4880b06d72","cad320f4e32a3f55ab3b2dafae2df2669b02c31d922d1ad7a59d1f29d2e9f3e4","32bff26d5c7b3a3a93bdb23e42c2b75de24b988b9bc12daad8bb9b27c64d9911","92745c589913abc8fe8f4bc45ac2abde83b3395ae2880570513ade6ff7e0d6d6","98438cac06c378648053c66b400ca908d1c681309ad29256528ea0245f6993b7","75a50890f1ba583165adcd02e72a62f68e733ed94e6919cb43f090fc9d049b6d","68f33658a8c87c4f4e0bdf7d964173b95181f105d27156df04b45696c4ebb0b7","4d641dde3104d6e9beb0ea8bba72ff4cfb420c2b63b132b42c8586d4ca2bbc22","bf96e3cd8ac82645c19c2ff81770a133c75d54b0ee98086bed5e6acdfbd54f6c","afb81ce8bb3e6de91fe2553d774f6a167cc1e5926a7be46ea8d5088e2c03e553","2e2b8a0d9b0520bbe93416eb49e23f5d71cfec834d59d0138bcc3631db9fba70","97181768db0a446bcea80e6449e884f6d68d85e324e4ea923b2c3c284ab7b80a","c6ef0974736bda6224e3d6e2e8fc0062c41bf2d213433a9b4c625e0730852c20","41a7ee8a48ace43456aac2e36df1b31900d21c0c4c1e1efb8a494f5561860726","7ca5cbc45d37cd33c255d0911a1cf346f94a8c55f95714fa1db723e69367d3dc","e536e6ab99bbc55c57f677ac8f845dbc88fc887dfd48addd1ffdca2a25f55fc8","8eeafa5bd9bb629553d3279e21f56957ca9a1ee50f942dfad966b86346644a25","2bc76065771be133978a14314bf9e0a562a28377b113852fd89e76406135dba9","6236548a81d719390c5d221398aed6e21c2d0f49e76b9eacee2ed3c4a92821e8","85f9a6b1c483960e7883fdf3d5d31d046858a0bbd518fb3b4fda1bae57e8fd57","89c36d5078de736e3bb4b695ca9e2d412ba4242e13eda0a7d64ac5d2387b922d","38efd2cadd1c34164c9b638cd4510d5cc4259bde4add371e5dbd2817ffc74b6a","91d540092ab34ccef7b0e2d582ca2fd5fd21ec157e7fc9c6914438af5125b051","79cd9ee099d926504d2c5281df43e3b013ed1cdb413808ce78c6c8e41a95ef07","e4eceee438d823c529f596806842c342cd8620088d41ceb6b756064c664f3a08","d0cd4d19b9355d54cf276bb76e2f17b18fdee83cbe25cb9df5fa2c38c97fbe88","01e0566609a3d553235099361c083d6ff47767bb3ea8d6ab1cfeba8e26d06354","9df4da519d58916b856971122d79e200f2a3be01fd2a0b4e2a556cc618007824","4db911d5484d25802dab82385ef58f16e4c079db6bf96c844a65b8d3ceafc1f6","f98905b0043d1c0ad988a9cc5ab583acec308482d2c31d31da84c0616f2f0d64","4892d7383c60a6ceff1d527e84cac274c2a8e1a04ce4263b536b74c20f92f3fc","cd77e5aa7e8355cfb68ed697b5cad13c98612bb068c63be0b7f103960a5b1f13","291025a5b950003bb695197781fc77b2a1fd0eed93e9176ec6e1e6a21e195615","19689d91927c6ee23bcf5cdd48f3c72f035518a5acf684098ad5b8b468a8902f","53dfe63d246f0393ba95252485acf8591f2b0a97056e8ac844840e4b2ba027ed","1761017a42df74ef2b3ef3764ca764d1b843ea377b5042c7828d3c81af498a94","cd0dc8d94e8438957ff7774a7d492153eb8a4b410e0720bec3f8c45cb489581d","27f8053a45c7954e6e515d8dc611dac0a2ad3cd541eb1a2230ffcae1236585de","4c7e372a8042e2e70fd52aa2668d6e5b892d45cb8519e1d02e69417bf5494a56","fdf174102948861a8c8af43d57e5dc6b7da2448b3470273d845775a6a0e9243e","c9ff568e82c126c392a79f118b23cfa857188a9f96147d21cf83a28c5f7cd27f","a3b36911d8bf20bd2f3e43e3b2aff8cceda729f7fca3557e469d5ef3f23f37ce","a3ced15b4b32f451c16cf3cc965c9943bec0d0886c163ffdbbd6a9446004ad8e","f65801b9f6ed4e95a87f2d86d5a1cb115d0cac8b63b1045d279275e39ccc8099","f8d698c6794fc3c5116d9af4b75b674942947a58fb689bb9e93b30fcbd12912c","d2b48ae1700a7dd8b1972cae53817ac1c4ad8c41d1d063848704ae5c880757c4","8112ed22d19a5b06f52344e786ac82a34ff4f2cc0b76c6ef3e3e44d6bbc97608","859e4844681f1c837d160cb1159e157a7c8e11a189c1265d0372b83c458b042d","774f43648cb10a2b999b38750e948c662b79deb59996a4bb6b08e026e888895a","bb98f61d5b0abfb7f8979a1334216da7c08a8e559aeb7c6347425a6848bee63a","5418441ec6b92f07033180eed038b750bb80f1b4aef2dd9b5b6d99c6fc017c27","3272ee1bd9d15f9c5b7ee04e78ad993cde0e9fe840cdb6745adae4309f1d6259","6ee0192244f3dbccb01c01cdfc5e85fa034a4b8e39ca4f0e4796cb0651670705","1491401cb59dfb870559cacad62ecf8758a4e48b67d761c746193d850348f052","d43d918a425a086113ee6cc901185771c0052b9a8568fb240a1f6801e7d66cbf","45abc27bd0627bcaf392056fbe2ca4356005fcb6ee811e54b3f85b28f94424e0","c2446e2a19cae904bb71e0d8fa59f6c7525670e56dbfa3ac270663e3238d9530","d6a50ecc2edc5c8d11b26681726b74249399eef9978f853545c099a2edd3b434","935520c5fb3e88ecf473dc43e9f03b6c93b7b068c25929a89eee13db9c370413","7d3f30d8f862a6d6ba8da4e34733e0ea57f8baa075f6f3c03969d4b91256a63b","5380c75f0cbab7c65c3cbac98e1a1800bc09620e9650a27490e91ec2b8030f19","ed455f5e991d93946073ebce07c518bb205b3159f2684b072bb79a7e9ab663c8","0e055675b6b5a94b87dec7e15a632cafb639b3d43995a79ae9360928f5851156","d3f03803d9165bd3cb740c0b304657adebb48bc2b92436b0e9ec4a1e6a14823d","bb117ead947078490ebf5103088c06e89e9562df0c9c7956cad518ad01e95006","67ae8379904c44490fab225a7caa7ae05a8e9a035e2dfd9e9d6a9154a684536c","013600ce63487c1696ea3b4cf60f401cdc24e74d1b0ac836a0193aeec632e2fe","fd9f34e9871cb72fbb2d79b20dfca65bb4d16f71891a8d621d6abad786a25daa","019fa0d21ca411e6b08b439306a4b911e2c61e9f715b0e7e82f86b819ba8c329","9c2faa7239c5785950d9852f56ddf2c66adc00f2279faca943ac6b283ae84fec","6f9a80185e7aa24ce85b79ee9b70c3207c08fab73d23f3cfa09bec38efff9a87","6a9f8f7afea733b1736be30c8b440d25a48b2d82dbdc48404f3f0ce1aa82038e","ca9be90bb0409c07e622a4e03b968974c5736cccad75533c60fb14dcbec7c73b","776ee8795d43b5763d3654c7b5c0758027fd1df812607a7d02bb30e5238dfdfe","9b9e1ccb86c3ad50b74dc49830d275737f4ce166a50b124019fc319b2717cf63","ec94d5d3a4f131ad79abfade176f9fb7472e6a8f202015bb4f7f29b0f0bf0e32","fedf5fe5b4bf8ce15c159340e569317d15c8a198f8710a0d29821c32592e1a71","b66197c2fddf3fdeaa76ed1b6ba7ca29dce2261a9b195dcc3318c9c6524a44a0","80bb561bd66489e524790d47a287833179baacd89ae2b60532c7f92023f48cc2","4a6e633741382a94045e8050c51a92b5aede9aea65d5cbb48005907ecd6701ba","c942521fb4829d3a6d14630db343b8cb46b813709df918c65f2b643d11ed017b","cadde74af3321fe5dfb348dc1d72e19c6a11475d990a2809aa8a8a0c968ff968","bc1a6e48943bb9b281a229f3bd506e698bd82bbc6b110de14fc1cdb82844ca61","f4e5eb0d5e2e30df899d343e7923d656f71265d32b6bf7f469d7207cb8e3f704","af4220aa50c809f7fab954561da809da8a218c2d27dda489a64fb2d4ddf030a0","d62bdde544698cd0023e13982cf5b588d80913fd2ecfb8ea2468b9c557df9ce2","27182fce23c374fdf04959c90ee3153177608c2b1cd7468c60296a93d4b9911e","261c41c9919bebafccdef0c501c7eaf7034258b3c027a22b1166cd096834556f","7ac116a9a8c012220f82014b63dd744115d09a6fa83021f909c87ddac2e39cb2","ae71cfc0629ed8403682a122f7a1bcf6d87a8e9033c2f1033acaa00c857b056f","6231cded9a3b79d8a9c355048efed866c8eaeb4f2cd395951752cdab6318da10","c6d860360ececa1e5e01a4b39fac1e9db8924627c30726932db4f7109f0a551f","947c89d7df64c322d26c487b53867889d340824e2845c0d87d5504bce6532542","dc538f7162e974fbf18b8616a20a9c6d084e5847a35f165d94d69255c20983a9","e3b9222330621eac375f6bc4b52ea78c8469b4c94ae2a8b09fb1d1c3113307d3","4485370e15e4376b92686fd39336d9027b26b371248e25e1cb2d0244e94a1fa1","99e8e188456e5dc71e60d7790267772ad0f22e854fef5d40d8ecb48981fc3296","b88c260399542fb51f72a67584d6390c0e1b68c361b3b927e817a57f93121148","0179850287dfbfd1fc429e9897fdabf1e1094e80592dd6d5630a67e4cd42fd09","4224a4dfccee19347a5743b7d69343b2a16c865c8734a605d28af4023eed2d17","d1028283a35e89fde13ddb206acd55624f9eec7874df694fd185d62bbdd3a72c","05034631dc7665c544420c87919787415718eb904c7e377a2deed50f174726c5","cd2b5bd606dbef4a5a0d0ed510e2d53dac00f0ef92db994e1f72ce4525d918d3","ba2151c63fe743790a29e9e8de924e65d5bcb4f51cff999b825afbaf135d592d","2e51f8457814a3e4aa3b73ca27c14d36928a348f85c003bc3b1b8d8797cf0fc9","c4cdadeb3aecf4031106c52c2fb782ba5cdcd7dbefd0a3aba2fd9850f8cdd478","5f4a10115c06510164e18ac8a5b5db1f615f096a40d5c69726bb32309af93252","58e3f7eeaadad8a3f69dff6a59566e9eaa15b11facd397e2b9e199fdb2a84533","537b5657a19792461eefee12e406dbccb7b3fbbf4117e7a443e81e925f6e1b43","c3be2a86d6ab205269fdb5f1da4ab6e7d825f8e119ecc235fc587cec2af2c8f1","a35dd28e140a6ded634995e25fc6c1d1f85eb59ebb18c3afaf5c66de43972fbf","be4bc9c39dd458aa3a993a48dbbfb09be898bf30f3276660b012401ebcc54cbf","25b92cc86a4aa9b088a1abb16c60547951859d4ae12fc909e23d675e6046b4a6","dbe50a407dca6d6143ac10dc3f4a09816ca2604788851d086e621d990eac1662","8b226b564befbf10933d0e3c0f179c522aa024f1472022d9fe35a0addd6e17d1","43d7bf6b658159db2ec49152852d3b25113b50ead0a0088564a83bbc30b5a61d","4ab69812962d1cf86bccfd350af2e07ce74267e90071888f823e1402212bc100","efe27b07f8f5f9dc29e2c64bae34e21c3f755895adb0aa9813733860d5fa41af","c6ca120560597056bb69d12cf8b3f2e05aa15ad6cd2deda6f7b25ebad41b15ea","0bd118496029e7bde27d232865e91eb1e8bd5f1aafdbd8365c2f8862c2122326","8a9fac3501784d42eed037c9b3702a64e51ab0cf862c19a82851b712703e32ff","e00aed0f8e5f35807d735a1fc5424e3a15fcf4052eab5cc59887006db55d5ee7","3fd188ce8702e15ef258dbe0ce9531a311a8fad945343a1876c7bfd8b75c54e5","c3b262ba6bf1d028b0999b6f2810930fea722787bcfb016997a28c06c82daabd","e862acf7cde7ae3002960593801a2300f2b2f6ffdf70c8814b9ac9c0fa7998ab","7edc3292c8307f372d884c40d32fca914a782ecb704c1ce3461db1f5402c99f4","9c7021a58d44f73b69bf07728c24bc6b7968e9399ea91009e8065ccd72e13c76","1ddf94976ed035a37b6e3445226d3974155a93af053574124e07a2c810e8f355","7d19228684b37a3f0f926ae802498c4dc9a8e175e8f25305c74ab74e48644214","b727fbf25d1eae6af35c84820f0190635114c2aa58bf41632bd75d35b1fb2974","d496b86b371307426bf3ee7832c05c4c8f02c46af10e59bd77d57f711217d653","213ffff1c4eab65acb3905dac82a82ffd8c3159176cb8683a0029510bf74f01f","af560c1ff8c707db02ceaf6b3cef02a112c3d75aacadefdd16fd34d1b2229285","e36bcb121675af7fc068f828968865ef82657106746d191225017bd714cadb7f","4dbf0716499198a5eebbbf51522b2ff4506fab854e247dcccc781aafff70e277","506f2dd82ae2d9db53d80e21068cb73c483627bb0ebcb8755e93921a2c37b9cb","8955c4a967185303e49b5456a90f24f763caba0c40b0d3234efcff6e533cea97","b92e949044dab0dcc4427b28c537a69cf12f6de0afd814bdccbb77c60c6e0c92","302629b070c984735f674addff60fe67000150c2b1eee7f4e0b6bb774b5f2894","1f5973936b80ca510f224b60f2ba970d166be8d8d6fb3ea203d6ad17b10eb920","51972559bff9ee985ee8e675aa09e3a3be8b4ca883642d762b906f4cfebaf2aa","ad9384c0977fcbb01f08a27c5814a2eaf997558e15eb95f5e1445acbe9504c6f","4bd666bc780a7280a544e6897ad4878dead08d648b0734e460fb1f198b241072","28caba7d9bc8ce812dcf2dc0d27e2b13fa12e75b2b83d3598be16ef3d10c5981","6756086988b5faafb5b0f605f761cd13d4878dc0aca5700e62a79bc3ea6673c2","d59622d42ea535480ca6946688a6eca33f69df94378c2b163cc88c3dc846e5e5","3dbb1d73ed0a48d08c3efadef251705a0001668a09b7059f6f46f53d78b9dde4","fb59d7942787b6757b5487815d18eea35197a15790bf237cc9c13e7f95042e5d","a5e50584b9224014d84a17bfad354011db20c4afda7744d51e67ad65426bed6a","b2f527d9297256ef42ec14997a44d4a8a437ffdb510886038562642577ca4c14","d0660043f9daac0c1424b4799f8c7af49bdcfc7755018a512e5d51719c96ff48","48e41db5807769d4b85c78739b8b718649fd78d256eed87e803239d88ec273d3","837c4255eb9d91a850ba844e615145fa84b01ac3e781cc7dbe6867ddab33e694","76106e67bb64a356c5ebda8161f202c6b64522c760a77d2602f7a2679ec6503a","accd08571235cef22f637299d5cbf3b0b97ad6012d0d543df843be956a1d9fd9","27fff93cb53ae9a40c88586558463d9a9565c4951643d80597ce4e2f3e1fbb74","073b157e1b9f89ee0b8067b0b2202a63667d7f3a2d66f2432cc04742796a0c67","9d459e023609e74bbc8da58e71d21fafd293bad7130da8fe9c12b2200750ca36","3544b93ce1c4afc26e27889bc1683efb5fa96ccf470c8bb5432fac6111422aa2","6d7180a99a3385df5c383a82d4ceaabeecc227fa83eb9c7c8e7edbcde284ab4c","52d872949bec72fdbd43c683f2de8e6c99812e520d4242140cbb1def52077b52","cfbd3062c931b1e325f9b3d6fc1ebd25246951a0b90267d35af580dc8048490a","0ebcddd81acec871f2a6bb726f6611d367d5c4f6c2ad6ed46a5edaee52e1ecf3","6d6d1b9c3cc0148d3aa40f3f87f6f616a38ad8c1b68b8151b866186a6c85798d","6d754d32de2cfc04c15c77fbe0eda31a2e2e4886b2e4c5e6fec5e24a07d40bb9","13431dd53835377d79adc5e6fb65642cae90612804d18161c6538a348b817c55","4d6b3f10cab65e78389a1618ca68a5258a6b6d2eafc0f454190e011cc0a90d1d","edbf50304ae52ed829961d62d446dd1e3f4fbda1f0e1db1c8934e05b3b73d3be","7880a5552a9c6fe2fed8a970430085ae9b753d5778033e6a704fa6e0aa164589","9c32dc593fd45d6da83ac6e7265247f13da9a4c0beb58f254fd78f97bd883dd5","27e25e7fb2df5c76ce24a2cb6fdacdd031fabe665b4203ba1e8a82efd7ccad85","233fbf69bc142b400afb9aa69f6289593936bc53dcfb464206dd4e65a4194f91","4dca5a6b9792762913ae2a230b782b351405c243244c35ff0a938347144787d2","fdeb952e3b3c6ac4afe4ed89b8fc547789cbb23c5f362bda24e9118d10271010","c222bb29c98dfb69cdbfa98f24965defdda9c09b3ad754a76dfaee9869279191","5b8125c2f2941ed289aa6b661c86302eb4afa6e22f697e82c8a14e058ed61b67","7f6ee041490e9470a22c0cd293205083293c12e654364556bacadb5cbebed30c","9fab50375eafcd42df96516b3cc42aefc6a4aa3f268e7d5cbbd05875287bd7da","b8a6419ec42bf4d8eed52f187e161b7dee898c96faf691713fe1a4ae0d89234b","1a0fc724d26754b15d02f70589e49e1510b5f031c16c0f36d033491b10e04e7f","20d7df13f5c0f787c1c7c1c66c13e38f65a6ce33f317971868784f6687ea1311","f523e2c118075521f4b6683c5fb2a0b3aa63f588df75ce094db5f4af7cd610da","bd42e75f00e559514fd8c0f8b1efdff737ebfd9dfc4d420b7942ac8921530b6e","4943549f64f3800b8517fbeadc03cd9ea254b9325d3989b6d0b8bdadcf41123e","d3b6559df7ac0bdc2efc652a70ada0524a585390f3892e0533ec1f7a1ac8499d","8f6157e68f24e1b405e1f0376baa6762216888df0fa8292ea7c29cac1705de10","a273bb46ef5465ad1fe1b7bb5b1fddcc119fe788c4e73e226834a186fa052798","a1af0abffba61d11fe81b8338e62f2b7f4e5ef73828a162bb380d9cacc54e111","6c8e9cb096ddabfa76e8e9ef77977a44f39cf77f1c8309ab1398b822fa3f7c05","94ba095ba3e0fc474c0106211ad66c7f6c19aad4d62af9427e38069d9c0ed3ca","ae39fe36423fa67d1a7e1ad8c4b21e837fa15cc8d71b9ce5bbc8647b167bd635","706bfe9d17e578e4d5f546c9b66ae83fc08a86b2e2c640597dbe3b5666a272e0","f8f4cbcdd78975372b40f887fe6dfae1ed68b73e416319bbce7c953edca909c2","9e7c4846057815d55e1eaf27214286ec0768a1b463a4669e1ce37849b6cc1016","d1674a00918199355611c680feb06f169d1454e83ab35c467e5a9263cbc9f263","b38058aec186ffd334c83b6657461d9c9b7c8d84c8abf404d8569520b1771a45","760b1c23d10297af55bbe15ac83c32c5ebcf768d32b530ab66b85249f059a5db","b7f88a8f6233d326148043be8f6124678941cbb3f21152229b4877d2ddb9de6f","764dd6c53fa86e251de57b31a6a41eed7ebb8ce726704e6cfd44b67ca1275e07","b80c780c52524beb13488942543972c8b0e54400e8b59cee0169f38d0fabb968","d1c9f45a2401b2372f6d523471d7775e501401411c1ddec8aceb09e93f242c6b","ec23ea828cdd31b5f3e5adfff57a611c80b2896e01a049ce6e2ff2a762868b5a","a7d13f7c794ed9c8ffd5d3b78e720c3330355eed5b11a104d433146dc8e7b10c","307b73411d5d5aefad6bcf036d154b5d6303ffb598e69952240926758e086254","777b0513cea387c17734fadfb877fb05ea9f9584a7d18e013f76cbbb436d24c5","10cf3bbb1f3fa03e8fabf03ad247ed9631d0c7591a1c2bf12e156c0f6ec850d3","1a731460b5a81522bca63f1c4e3319e9d6518e0c2e1755233d7214974ec00cfa","8e68a60d94268392d286d7869b14ffe93648db4f689026a904cac98bfcd2f55c","a2f4fac4d986243ff1f4fac562c4c93bef390b4d1adad0dbdc21b13e50fb4fcc","2e9b5de48cb2c347378bc6c04df6248e406f6bc363e546ab12ffd5f5377ad766","5ea8401fb134a69d4aa33ea67ff7332e3bb7c846264031d14c87b19c5a0b93b3","6cb35d83d21a7e72bd00398c93302749bcd38349d0cc5e76ff3a90c6d1498a4d",{"version":"369dd7668d0e6c91550bce0c325f37ce6402e5dd40ecfca66fbb5283e23e559d","affectsGlobalScope":true},"2632057d8b983ee33295566088c080384d7d69a492bc60b008d6a6dfd3508d6b","4bf71cf2a94492fc71e97800bdf2bcb0a9a0fa5fce921c8fe42c67060780cbfa","0996ff06f64cb05b6dac158a6ada2e16f8c2ccd20f9ff6f3c3e871f1ba5fb6d9","5c492d01a19fea5ebfff9d27e786bc533e5078909521ca17ae41236f16f9686a","a6ee930b81c65ec79aca49025b797817dde6f2d2e9b0e0106f0844e18e2cc819","84fce15473e993e6b656db9dd3c9196b80f545647458e6621675e840fd700d29","7d5336ee766aa72dffb1cc2a515f61d18a4fb61b7a2757cbccfb7b286b783dfb","63e96248ab63f6e7a86e31aa3e654ed6de1c3f99e3b668e04800df05874e8b77","80da0f61195385d22b666408f6cccbc261c066d401611a286f07dfddf7764017","06a20cc7d937074863861ea1159ac783ff97b13952b4b5d1811c7d8ab5c94776","ab6de4af0e293eae73b67dad251af097d7bcc0b8b62de84e3674e831514cb056","18cbd79079af97af66c9c07c61b481fce14a4e7282eca078c474b40c970ba1d0","e7b45405689d87e745a217b648d3646fb47a6aaba9c8d775204de90c7ea9ff35","669b754ec246dd7471e19b655b73bda6c2ca5bb7ccb1a4dff44a9ae45b6a716a","bcfaca4a8ff50f57fd36df91fba5d34056883f213baff7192cbfc4d3805d2084","76a564b360b267502219a89514953058494713ee0923a63b2024e542c18b40e5","8f62cbd3afbd6a07bb8c934294b6bfbe437021b89e53a4da7de2648ecfc7af25","a20629551ed7923f35f7556c4c15d0c8b2ebe7afaa68ceaab079a1707ba64be2","d6de66600c97cd499526ddecea6e12166ab1c0e8d9bf36fb2339fd39c8b3372a","8e7a5b8f867b99cc8763c0b024068fb58e09f7da2c4810c12833e1ca6eb11c4f","a8932876de2e3138a5a27f9426b225a4d27f0ba0a1e2764ba20930b4c3faf4b9","df877050b04c29b9f8409aa10278d586825f511f0841d1ec41b6554f8362092b","027d600e00c5f5e1816c207854285d736f2f5fa28276e2829db746d5d6811ba1","5443113a16ef378446e08d6500bb48b35de582426459abdb5c9704f5c7d327d9","0fb581ecb53304a3c95bb930160b4fa610537470cce850371cbaad5a458ca0d9","7da4e290c009d7967343a7f8c3f145a3d2c157c62483362183ba9f637a536489","eb21ddc3a8136a12e69176531197def71dc28ffaf357b74d4bf83407bd845991","914560d0c4c6aa947cfe7489fe970c94ba25383c414bbe0168b44fd20dbf0df4","4fb3405055b54566dea2135845c3a776339e7e170d692401d97fd41ad9a20e5d","8d607832a6ef0eac30657173441367dd76c96bf7800d77193428b922e060c3af","20ff7207f0bb5cdde5fee8e83315ade7e5b8100cfa2087d20d39069a3d7d06f4","7ca4c534eab7cff43d81327e369a23464bc37ef38ce5337ceff24a42c6c84eb2","5252dec18a34078398be4e321dee884dc7f47930e5225262543a799b591b36d2","23caed4dff98bd28157d2b798b43f1dfefe727f18641648c01ce4e0e929a1630","f67e013d5374826596d7c23dbae1cdb14375a27cd72e16c5fb46a4b445059329","ea3401b70e2302683bbf4c18b69ef2292b60f4d8f8e6d920413b81fb7bde0f65","71afe26642c0fb86b9f8b1af4af5deb5181b43b6542a3ff2314871b53d04c749","0d7f01634e6234d84cf0106508efdb8ae00e5ed126eff9606d37b031ac1de654","f8d209086bad78af6bd7fef063c1ed449c815e6f8d36058115f222d9f788b848","3ad003278d569d1953779e2f838f7798f02e793f6a1eceac8e0065f1a202669b","fb2c5eceffcd918dbb86332afa0199f5e7b6cf6ee42809e930a827b28ef25afe","f664aaff6a981eeca68f1ff2d9fd21b6664f47bf45f3ae19874df5a6683a8d8a","ce066f85d73e09e9adbd0049bcf6471c7eefbfc2ec4b5692b5bcef1e36babd2a","09d302513cacfbcc54b67088739bd8ac1c3c57917f83f510b2d1adcb99fd7d2a","3faa54e978b92a6f726440c13fe3ab35993dc74d697c7709681dc1764a25219f","2bd0489e968925eb0c4c0fb12ef090be5165c86bd088e1e803102c38d4a717d8","88924207132b9ba339c1adb1ed3ea07e47b3149ff8a2e21a3ea1f91cee68589d","b8800b93d8ab532f8915be73f8195b9d4ef06376d8a82e8cdc17c400553172d6","d7d469703b78beba76d511957f8c8b534c3bbb02bea7ab4705c65ef573532fb8","74c8c3057669c03264263d911d0f82e876cef50b05be21c54fef23c900de0420","b303eda2ff2d582a9c3c5ecb708fb57355cdc25e8c8197a9f66d4d1bf09fda19","4e5dc89fa22ff43da3dee1db97d5add0591ebaff9e4adef6c8b6f0b41f0f60f0","ec4e82cb42a902fe83dc13153c7a260bee95684541f8d7ef26cb0629a2f4ca31","5f36e24cd92b0ff3e2a243685a8a780c9413941c36739f04b428cc4e15de629d","40a26494e6ab10a91851791169582ab77fed4fbd799518968177e7eefe08c7a9","208e125b45bc561765a74f6f1019d88e44e94678769824cf93726e1bac457961","b3985971de086ef3aa698ef19009a53527b72e65851b782dc188ac341a1e1390","c81d421aabb6113cd98b9d4f11e9a03273b363b841f294b457f37c15d513151d","30063e3a184ff31254bbafa782c78a2d6636943dfe59e1a34f451827fd7a68dc","c05d4cae0bceed02c9d013360d3e65658297acb1b7a90252fe366f2bf4f9ccc9","6f14b92848889abba03a474e0750f7350cc91fc190c107408ca48679a03975ae","a588d0765b1d18bf00a498b75a83e095aef75a9300b6c1e91cbf39e408f2fe2f","fe843ca244961850c8ef10e9c132c8905f40b5db8865f8096bf3281b089c181d","011c1e6d5520e98859ac63a30546139f677d6bcdbcce29d2b684a471fab4d511","449ac014c5b8093559db84b53f35728681b2694e31b64b440bc37fdbc7d8439b","ac0ee07a92140d64a014bc3cc97272bd8e3629f8a5a383507ed90bc0e2b705f3","fcc8beef29f39f09b1d9c9f99c42f9fed605ab1c28d2a630185f732b9ba53763","21cfd01ca3bbace027f4e1407926a77668c878ba422daa6259b3667ec74bc081","f7fbd57ffdb07dd247895edfa15c272af7d9ed275638e656d8488e96e86827b6","6eaa2a284a5038d0f955a8df77510ce0d0d4a75c2c36b50ce3f03d3ca4f1abbd","5c6907d6d785564c2f7825acb2e043ab36775fe3c1aa9f3345d757ba6035c560","add28373f3633ed263db511d7974d9de7a3cbb4297601489c650ee0ea591aa7c","d36b86989075dac65b2d6e513109f47a49d55dff5c5d19ffb1ea1696b4fc646a","ff10369fff135948e1fa3d8511f7859ecdbfaad342e770d70121ccbde3cc5340","4dc0f07ce5ed43ab1f9761e0567df52f3b7a5368c5c328ab5e66ba37fe56759c","548d270bdee09e2438e97cdc0b5ba55e7b188d4250e5d080c5009cbb7bbfb8ab","331875925874156fe9138ffa6819608675d6d4e6a0680cb597da030b3f943c7b",{"version":"691f5ee739e59e7214e8ee82d6d460412d15ee4e05fb719150f5cf4df46d7577","affectsGlobalScope":true},"1fcb8b15db812281d69a3090d488903f9e93033004aef9d8889ca3ad0753a96f","bdf5a95eb0a2dd1d39805bdf51b46ba012bb9b92b2ddaae16219595bba7678a5","9f794a0e8550a03baff865a3961cc22afbd85bc4ba9672bdda036971928f85f4","66a697d1e4cdbf25cdce4644a8085a8563041fa8c7731d4d9f5e8f22e66ba72c","4f1ae3f24125216cf07c5211a3f00d2bb4782d7cc76c0681603f8249f9232ff0","d3fb92a5640f83f7844d60b35317a0f95c27e3658a749d76d218c461ad091668","8bc2cad630da1033c1fd8d7df2bffb18af0da6113bd086a8bbec04a2471a1e00","d1f8bfcd91b284657ef8187c55ace7db91a3c43e642c3f14e54364154932f7e4","4cbd412a47e9734c74d1e5d895652019a6c4204435fbb843ea30be1be617b2fd","f97816e5e50a4c54ce155816ebff932709f77a69589249bf5134e44a279e9715","277afd6ab6ec72889e2988e0ddd7d138c1f512e68a1fa4e90eedfd71e2097a51","c0908f85f2b645d375127a3b53a17a65f782e17962d5c1eb68f08b1188acbf15","c993f7ed1b8e1023c1f2ee5b262dbc3b70b27475674e40a53a58591f9972dacc","3fadac5d409cc2f27b1d2f4e7568600f02840205f301c9ae7a3068b46476438b","daef728543afe5b7408e01f0ddeac58e6a290e3cfc6965a8e981f8beae2ab29c","3633f87c97d359cb55fa7bf0668fb2be8a23342951af6ec2d06e6d0cf7409371","cc3a5427d44fc77ff25e80b3edee4650a51f83de761faf5e633994ecf1ab1b44","b350eda75c6e47299b36002b31d5b220c405c21c365e708989829db013fadbb4","f421882756b6714834ae4687ab1aeadf344a1cc45437d2edffbac020ff3801c1","dfb1d4f5b00473a54a4f025ca7606173036bdf111f9806d957d5099e2cd55c62","e5cef5de3e5ad3436d414d20743231e284733b9cf4375dc79eff4fcca4282f99","e624419ba84e33e661e89a28083119ca41f6953dba09a4f82b660684087afe6d","942be430bd0feaced2e3e598273b17e50ea565ec9dac840b580b0b99e1a3cd5c","73350006cec5a0c6b71d53b0b0ddbfb82be96752a9c4e3c904c59e633bc9485e","a7df5c2e9594966c7e0d4a763b13ed5727506d892669df5f7bc9826f539c1d35","2286a5599f5746d6ae48dceef0322b020e6d0b0b0b64683492ac6cda6809c6d0","00a6db28fc4df6ddf10adbe630d9df620ec13af19039c1869653e60dafa739d2","649324d5abb5464aabe35d86cd0eef16562df811f0971481cee664afa5acbc88","7698c256203945e61639d30614dbce3d3d0bf4610b4e576f546d131b8da5f2fd","075e48ac85ba80a50bdee0b926271f1b06715e9baf17716899faf2f7445b95c5","c7ad660832be3a529d67ef9cc459821d04938728fd01fa4951148183c80b06cd","af929fed708b236eded273ad7b344b4411ea85bad120b045d491c065a58aaf11","2fcd2d22b1f30555e785105597cd8f57ed50300e213c4f1bbca6ae149f782c38",{"version":"bb4248c7f953233ac52332088fac897d62b82be07244e551d87c5049600b6cf7","affectsGlobalScope":true},"969afc7787b06c00f480e0c5a82ae92ccb94c3dfd4e1bcc45d740158207cb662","2140fac55503bd2142c95bdeefb459a305833dbc747481e3f031e54393951578","5286545abb6d557eddd26ad4bef22e915ac6495c8173532f259191061674e3aa","833ca9bc196b4ee332010c96e9afcced2c680d15aa15d559fd50e77c73965a78","4fe642bdec9d07f619933c9b4eac66df3ea30b0ee49e9f98586493f98a7ad886","21e198f821e5170e98e65aa26a3bea3641ec340ff43e22102224281d6f30ac98","065c2146947458566ae1eaf2a8dbb94780d71791126ed33e816eb1b6577afe49","fd7599bb27dc7fafaa85cb770619aa794934be09515cfa6a23227a523f1c87af","5b7e5d7274c975b0e3759eacbd4f8620c6d72fcf76dc809d125ba77bec681182","1234b860468564ed0616f53ad4f25d15fd54347b1f6792e515e2ee8829fc9b60","d2ef32b6b38964189331baf5f31aa2db808efb977b16f9f341154a85bf5fd402","c266b536202c877e0ec2a9473d4f82c89586ffc8ed5096bee19a1ff0cddfb639","3484b62cad338eff2313b469bfc6e5853950f549055672450112b086b285613d","9ae112e46f100e07b3b16db152322714d2afd1e16cb6fd848c2164c4b97cf532","9ed3d56eafb7ee9d875da6769b6f73621ce6661cfce283de7bc115e62ae03abf","a362bb8bddc4095ce47b0babf7178ef68bc7aebad0af7626ca5ce1d590494bf4","ba0037eb3d55903ccf7fa3004c2990d6b831da2a0f44e57ea6325bbf3992269b","7bc75399ab3a05535989a336743dd9fbc41b32e1b69905881eab3a1d8fc3f788","3576f553b3104bb09e93a2857077cf37d2634def579e90c76ea3a5a456e177fd","3983674571a3ad496d3d693d5a79d545bbfe8a9598c0da278b151ab1e4e18fbd","19e6b101e0c358787690959f1d4b6f795c0108ee8367eb20e6127a6465c669a1","a58cd8883de63179e8761f4b783e2965e45ff70651d7e20e39b4794724ada314","a82a31423116bad5be7eca7b4738930d9e293eda7509bce6e9265d1b90321f81","f04791198d22830c4898959cc8d32ee2da096df6ff9fcceac868a97e6c375ceb","bb12d0a77526f90f6624ffc85d4749ceabc77d4d2fd6ebd700464527f840f696","e3a6cb11daad5360db3d3747c0bad9b9f2392e179305b927508e5e1a40043e11","5b0526046e67ed5d225ef7c8b5d1fbfd20182906afdc924c7aaac4caf0533a64","6f89168ad01e605745fcd22cb3f158fced83ca048794597f56c094f748be5c4e","e4f093af5ed0eaed770235baaefd7438661f14f57eaf170f4b47e197d58c76db","b8c0e05f44976d6b0f550fa524c0ab327015b5723ec7d192e0cb16fa0de5acd7","21afb72063e9106fb27ae08d982445130f34504a8f2b790f57dfd7f58faa35d5","d23caa4f64e4877ffafd0d58e1e4b807805b91a2b58a67e44e933972b75d86d5","f924d32d12e1b44e4851bc9ca5a7f63e8a2f2cfe932d7b4a77c02fa9d42993e7","2806ec7ceb39ff7791018df324bf8114c8666dea6f17a7fb0a4e5f45a06b6fc2","f58761cc2cbf31f0953b38e13cf6171e1c7130ce84782a8afbe9fbdf412b6cea","7aaa724482e6f15d8f70cb2833dbc55794d7c29d2650fe2afa9de5c390bd9f57","63b5a892c2b2b092e374d33f4c1c51b4bfc9df8104fec0b7b006ce26aa68c989","8e548bc3d16d78659b357da30d0a45b3b697bcd4dbc24595c52dcb897b71a486","1c6c1e15f05a8e3639172a7c649644e00adfcc215a9c9536bf2c5299237b6638","356901e3b86a6e115eafb7a725cd073a0024bc0eb0cf741e85695352517887ca","db9cb7459bc8ecfd91bfd08598d60d62cf5cf78dcf7d3f8a209204e578b830e8","ab7202dc81b82f1e8b4008bd87c0465e1ebbc70bb7ebb33182d528999d9a0470","c60fe1946466c6d68d773a6faaf22bc2f9e107452b8b569254517977d24d5ae9","e6bb26643971a72d3c829f28d4f0d4e5d15d3cd57a0909d9647f107145812615","8e548bc3d16d78659b357da30d0a45b3b697bcd4dbc24595c52dcb897b71a486","064eab1a7b24cd8b1247320848f6ab7cfddede24b80973ad7cffd2f73134b20c","5034adb393d7863f1867c193b5705de1d7219ca97874037d661a30bb405b2049","1f159da169f65d987860bd30d79f502d05613293c4625036974c9d6b35e2a671","42ad4ce262b0400a475c89027fa746f95eeebc13185612ca3c8bcba346f07d6f","8b55ff6e48ec9c8a17c15ca8dc8f5d4dca6e0fa609ca41fa9a11dd796d16b050","b7d829281b4cefb06f2d47b1223821e849a01e8d21c81b08da244d91f0d17fca","fbfb7a3de011ac08cdde24dc1c49d05aa372f1e861bb985aea0ca3dcee952ff8","e2f401fd1afeab210aa64e0a0e996ade90a20395a18d0ab5cd4a6089e651785c","6bda70a402621b19ab7d0e568dae193944e2dd2f866857d0a47d5ebfe1be27eb","936efb9566b2f375e713befa3a69c237be13d3ead2738d8736357df8dd04a851","5024433f8da3a7968f6d12cffd32f2cefae4442a9ad1c965fa2d23342338b700","2ff9995137f3e5d68971388ec58af0c79721626323884513f9f5e2e996ac1fdd","cc957354aa3c94c9961ebf46282cfde1e81d107fc5785a61f62c67f1dd3ac2eb","1a7cc144992d79b062c22ac0309c6624dbb0d49bbddff7ea3b9daa0c17bcac0a","93de1c6dab503f053efe8d304cb522bb3a89feab8c98f307a674a4fae04773e9","3b043cf9a81854a72963fdb57d1884fc4da1cf5be69b5e0a4c5b751e58cb6d88","5426e62886b7be7806312d31a00e8f7dccd6fe63ba9bbefe99ee2eab29cc48a3","a7d9d2a35530516e191ade6dc804d7de42d45ff6620c0319cfb4469dbdbd8044","cab425b5559edac18327eb2c3c0f47e7e9f71b667290b7689faafd28aac69eae","3cfb0cb51cc2c2e1b313d7c4df04dbf7e5bda0a133c6b309bf6af77cf614b971","f992cd6cc0bcbaa4e6c810468c90f2d8595f8c6c3cf050c806397d3de8585562","6d829824ead8999f87b6df21200df3c6150391b894b4e80662caa462bd48d073","becb604e8f5399436f67baa1c4923fea0413533e9000d98d97c9da729eeb4e81","e39b1f1a88ac45ddde24e46eb57fa4a3681bc0bfa898305d6152e4fcb5a82974","a04bbf3e3e34cf8ad52dc42197ea42474f8d4d6f9d3df8af0c271400f0c2dd42","8224fcf3a00e38208a7a8705bd3f9fd486bd0efe4d9e56c520b1b3a6f43acc04",{"version":"924dfa21135e4aeb4498c4facc93545b30bb844618929d342f9ab552947e34db","affectsGlobalScope":true},"68bb1cebc4ec3f0a3083d4ed22837fab5d38ea761810a4b831e492fabd0bce2f","6a9c5127096b35264eb7cd21b2417bfc1d42cceca9ba4ce2bb0c3410b7816042","78828b06c0d3b586954015e9ebde5480b009e166c71244763bda328ec0920f41",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"0133ebdd17a823ae56861948870cde4dac18dd8818ab641039c85bbb720429e0","f3e604694b624fa3f83f6684185452992088f5efb2cf136b62474aa106d6f1b6","a1c79f857f5c7754e14c93949dad8cfefcd7df2ecc0dc9dd79a30fd493e28449","874d84ca5699231d5af2868fef01fc63f948bd83be928881479db48508f92ca0",{"version":"64d4b35c5456adf258d2cf56c341e203a073253f229ef3208fc0d5020253b241","affectsGlobalScope":true},"3adc8ac088388fd10b0e9cd3fa08abbebed9172577807394a241466ccb98f411","e050a0afcdbb269720a900c85076d18e0c1ab73e580202a2bf6964978181222a","725d9be2fd48440256f4deb00649adffdbc5ecd282b09e89d4e200663792c34c","4130f45b114de4cf06a465c0540c4fe31270876afff1cd59111314b6c4077c69","8841e2aa774b89bd23302dede20663306dc1b9902431ac64b24be8b8d0e3f649","fd326577c62145816fe1acc306c734c2396487f76719d3785d4e825b34540b33","3ebae8c00411116a66fca65b08228ea0cf0b72724701f9b854442100aab55aba","cddf5c26907c0b8378bc05543161c11637b830da9fadf59e02a11e675d11e180","3d2cd8f3047fff04a71e7037a6a4cb9f4accb28dbd8c0d83164d414811025af0",{"version":"271cde49dfd9b398ccc91bb3aaa43854cf76f4d14e10fed91cbac649aa6cbc63","affectsGlobalScope":true},"2bcecd31f1b4281710c666843fc55133a0ee25b143e59f35f49c62e168123f4b","a6273756fa05f794b64fe1aff45f4371d444f51ed0257f9364a8b25f3501915d","9c4e644fe9bf08d93c93bd892705842189fe345163f8896849d5964d21b56b78","25d91fb9ed77a828cc6c7a863236fb712dafcd52f816eec481bd0c1f589f4404","4cd14cea22eed1bfb0dc76183e56989f897ac5b14c0e2a819e5162eafdcfe243","8d32432f68ca4ce93ad717823976f2db2add94c70c19602bf87ee67fe51df48b","bfe1b52cf71aea9bf8815810cc5d9490fa9617313e3d3c2ee3809a28b80d0bb4","70b34c8420d6226ed565d55f47fe04912d0ca0ad128825c5a06e018a3498db32","de1d6e224048139baf7494237a9231be6bab9e990fb239c7825bfd38b06d8c90","8b06ac3faeacb8484d84ddb44571d8f410697f98d7bfa86c0fda60373a9f5215","7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee","f5638f7c2f12a9a1a57b5c41b3c1ea7db3876c003bab68e6a57afd6bcc169af0","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","69da61a7b5093dac77fa3bec8be95dcf9a74c95a0e9161edb98bb24e30e439d2","561eca7a381b96d6ccac6e4061e6d2ae53f5bc44203f3fd9f5b26864c32ae6e9","62ea38627e3ebab429f7616812a9394d327c2bc271003dfba985de9b4137369f","b4439890c168d646357928431100daac5cbdee1d345a34e6bf6eca9f3abe22bc","5d72971a459517c44c1379dab9ed248e87a61ba0a1e0f25c9d67e1e640cd9a09","02d734976af36f4273d930bea88b3e62adf6b078cf120c1c63d49aa8d8427c5c",{"version":"516a426e3960379f310107635b8f3a7e8c307c6c665080b128039d9299ec4087","affectsGlobalScope":true},{"version":"597e57d1aed6e56c5711328daa1a5f835d9aa1a23093f133d98acabeedff06fe","affectsGlobalScope":true},"b3338366fe1f2c5f978e2ec200f57d35c5bd2c4c90c2191f1e638cfa5621c1f6","83c5b66b5a31c9c21c57a2e7070e84a0dcaf66bcf01b54f5c36c8acfd369deef","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","fec943fdb3275eb6e006b35e04a8e2e99e9adf3f4b969ddf15315ac7575a93e4","675e702f2032766a91eeadee64f51014c64688525da99dccd8178f0c599f13a8","fe4a2042d087990ebfc7dc0142d5aaf5a152e4baea86b45f283f103ec1e871ea","d70c026dd2eeaa974f430ea229230a1897fdb897dc74659deebe2afd4feeb08f","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","febf0b2de54781102b00f61653b21377390a048fbf5262718c91860d11ff34a6","ca59fe42b81228a317812e95a2e72ccc8c7f1911b5f0c2a032adf41a0161ec5d","9364c7566b0be2f7b70ff5285eb34686f83ccb01bda529b82d23b2a844653bfb","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","ae9930989ed57478eb03b9b80ad3efa7a3eacdfeff0f78ecf7894c4963a64f93","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","3e59f00ab03c33717b3130066d4debb272da90eeded4935ff0604c2bc25a5cae","df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9",{"version":"0714e2046df66c0e93c3330d30dbc0565b3e8cd3ee302cf99e4ede6220e5fec8","affectsGlobalScope":true},"e8465811693dfe4e96ef2b3dffda539d6edfe896961b7af37b44db2c0e48532b","2a2e2c6463bcf3c59f31bc9ab4b6ef963bbf7dffb049cd017e2c1834e3adca63","f313731860257325f13351575f381fef333d4dfe30daf5a2e72f894208feea08","951b37f7d86f6012f09e6b35f1de57c69d75f16908cb0adaa56b93675ea0b853","3816fc03ffd9cbd1a7a3362a264756a4a1d547caabea50ca68303046be40e376","0c417b4ec46b88fb62a43ec00204700b560d01eb5677c7faa8ecd34610f096a8","13d29cdeb64e8496424edf42749bbb47de5e42d201cf958911a4638cbcffbd3f","209e814e8e71aec74f69686a9506dd7610b97ab59dcee9446266446f72a76d05","7366d0aa1eaa9efadb11633ac3a19a099b27d13a8ff0803f45b13ea1ea129cca","736097ddbb2903bef918bb3b5811ef1c9c5656f2a73bd39b22a91b9cc2525e50","208bb742e0f201470da121bc73847c74b62cff4172f38ae5949ae77d6c9c6b71","3663d1b50f356656a314e5df169bb51cb9d5fd75905fa703f75db6bb32030568","6fa0008bf91a4cc9c8963bace4bba0bd6865cbfa29c3e3ccc461155660fb113a","df38da6685578ac3d0e4ce2d20f3d59462ee53959b8263d2532ec9cec48ae098","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","fc37aca06f6b8b296c42412a2e75ab53d30cd1fa8a340a3bb328a723fd678377","5f2c582b9ef260cb9559a64221b38606378c1fabe17694592cdfe5975a6d7efa","a73a445c1e0a6d0f8b48e8eb22dc9d647896783a7f8991cbbc31c0d94bf1f5a2","6209c901f30cc321f4b86800d11fad3d67e73a3308f19946b1bc642af0280298","c0a3ea3aee13c4946a6aefce3a6ab9292a40a29f6622cde0fda0b1067a1a1f5f","62b931417104c7cb35d0725e1869f51d52d7b18462fd58f32f846a314a42ba10","84063cb4682d54b1b83b6cf88430e33e647d57afe29237c4c414196b7ae33ea8","d650d5f6a4c75431fcfeeac768c65f10efe245d5e3f867017da40ed003fca114","ba601641fac98c229ccd4a303f747de376d761babb33229bb7153bed9356c9cc","163be962fa11ca0e3bc9afd1188701e16f5db0d84f904be3a5327c04bf14c1dc","45a63e17814c570ea59407f231ef9c561510bd6edb36f17479b09b44619496c6","39daf0586ebf95e65217d55923ac49ada1939510bed54c6364504d2e0b0ccfc9","06c2fc0bf929858d3ee5fb8c14f0a39b48d91bb8161b6480d833f787df761672",{"version":"8f19251323456195ec9236df857bac3b8f97b73b540ef06ead2ceccc3884954f","affectsGlobalScope":true},"fc5feab7b3f0a96eaddb118cb7c200b9a06e4db387f8eecea6ef463c57496476","6c362c5d50652957065cf52f282a2bf0a456ed3713738d0ee1a9089dbb5b5fe7","8017277c3843df85296d8730f9edf097d68d7d5f9bc9d8124fcacf17ecfd487e","47d7d2886a64d471f1f8a5d895c442068e6d5d868e176ef7d3de347f8127cb9e","60aaac5fb1858fbd4c4eb40e01706eb227eed9eca5c665564bd146971280dbd3","e03334588c63840b7054accd0b90f29c5890db6a6555ac0869a78a23297f1396","c3052485f32a96bfde75a2976c1238995522584ba464f04ff16a8a40af5e50d1","c220410b8e956fa157ce4e5e6ac871f0f433aa120c334d906ff1f5e2c7369e95","960a68ced7820108787135bdae5265d2cc4b511b7dcfd5b8f213432a8483daf1","ed3b711f533ddb3a5451f4c4bb0df3a0b95e9d0433b3b7834644dd1718d06d31","c8178ab5f29f3493dfb6bed728f81a644ecfce0f6206b09408096ee4f10eebc8","8133fbe7d8377a2033c06e889272800bafc776d36d7c10dde05a7f99b90be6d6","064679ecac24a086709d83d1aa9953e04a7f31d456c7a44910e9e218a10fc9c2","8ac3261b8b7017c6b1fa92420ab01356f0f4994237ceb9f813a3cb797f436c8b","6a8d0553dbfd16e9c9ea42373edb4145d06083b60e2e8cd469ab3165baceae5a","0777eaff71bb5a4c44c170b80023cb343a8feb42101259ceef6b449d25d845f4","c87ffbd89d0625350fbe78f727da51a00efa6b777532415fbccaf214309de091","f15541d66c08700872c9ea3f1ee827a0c08ee0c71111df82546b3128d2ca4020","0d6b3824d757ce8bd598edeba960f132eb70db238f7951284c01dedc70c84f3c","319f03b90f7cda79f85ae161866b16e3faa759b5624db0756340399059c6369a","df513d30d09a5fd30306e177d196d3fb1944e23dc8420d2f523da9ffebe58dec","f12702d78d48adcbd27598ec64e9feffa7baf5990b44d40b71d5c381361bc575","f5cba585efc396a54dbacb8ff689799e6b3b3a3ed0e051fb69894b6669524801","ecafe5a693cb23c3f17618522471e7426f9b8c1a2eda59a72e9bc0aa0a0ed45f","23255560340f60336ff79daf50b3b6432ec0f32f7dd19638d3a2406826a15d49","8adb0cee1edd485ae5675aa5100d73c1c17201446ee33a1d2bd364b766d6469c",{"version":"b40add723ee9828e126ff18a0a2fb5124958ff911e1836bfe461916e434bd051","affectsGlobalScope":true},"8a19491eba2108d5c333c249699f40aff05ad312c04a17504573b27d91f0aede","74b0245c42990ed8a849df955db3f4362c81b13f799ebc981b7bec2d5b414a57","3dce33e7eb25594863b8e615f14a45ab98190d85953436750644212d8a18c066","67fc055eb86a0632e2e072838f889ffe1754083cb13c8c80a06a7d895d877aae","b0d10e46cfe3f6c476b69af02eaa38e4ccc7430221ce3109ae84bb9fb8282298",{"version":"20278d7dc78142b77cb5ebaadd6a20eae3eff7bec44fc70930520c2f4dba5e25","affectsGlobalScope":true},"d73c25c0db01c24b2826af004d6147a766e1c61181f1b6a49f5e762cb69e68d1","3833c70307dc3d2b46cb6f2a8b6a90e4d7e7367a21ab18c481d7de0909a43e67","bd0f4458d57115491a1dd9fe522fa1d6ffe45a85b12bbd463967f90b50e43c29",{"version":"06279f0df6f368af41fe267319e90b5af9d89ad489d1662164b94ce30a797c79","affectsGlobalScope":true},"2887592574fcdfd087647c539dcb0fbe5af2521270dad4a37f9d17c16190d579","9d74c7330800b325bb19cc8c1a153a612c080a60094e1ab6cfb6e39cf1b88c36","6d6e06d8269ca289d16f53844f23c254c3944a524bc67382d9b4691ec8c486b9","4fb0b7d532aa6fb850b6cd2f1ee4f00802d877b5c66a51903bc1fb0624126349","b90c59ac4682368a01c83881b814738eb151de8a58f52eb7edadea2bcffb11b9","8560a87b2e9f8e2c3808c8f6172c9b7eb6c9b08cb9f937db71c285ecf292c81d","ffe3931ff864f28d80ae2f33bd11123ad3d7bad9896b910a1e61504cc093e1f5","083c1bd82f8dc3a1ed6fc9e8eaddf141f7c05df418eca386598821e045253af9","274ebe605bd7f71ce161f9f5328febc7d547a2929f803f04b44ec4a7d8729517","6ca0207e70d985a24396583f55836b10dc181063ab6069733561bfde404d1bad","5908142efeaab38ffdf43927ee0af681ae77e0d7672b956dfb8b6c705dbfe106","f772b188b943549b5c5eb803133314b8aa7689eced80eed0b70e2f30ca07ab9c","0026b816ef05cfbf290e8585820eef0f13250438669107dfc44482bac007b14f","05d64cc1118031b29786632a9a0f6d7cf1dcacb303f27023a466cf3cdc860538","e0fff9119e1a5d2fdd46345734126cd6cb99c2d98a9debf0257047fe3937cc3f","d84398556ba4595ee6be554671da142cfe964cbdebb2f0c517a10f76f2b016c0","e275297155ec3251200abbb334c7f5641fecc68b2a9573e40eed50dff7584762","b2f006ee835f315d01c43c0f5d9e9ad78a5870b380899877b32a33078d065dbd",{"version":"3a1e422f919c70fca66e94dc641ad8d9732b3d2533ac047b8a9aaca9eea3aa10","affectsGlobalScope":true},"bc81aff061c53a7140270555f4b22da4ecfe8601e8027cf5aa175fbdc7927c31","70e9a18da08294f75bf23e46c7d69e67634c0765d355887b9b41f0d959e1426e","d9f5e2cb6bce0d05a252e991b33e051f6385299b0dd18d842fc863b59173a18e"],"options":{"allowSyntheticDefaultImports":true,"composite":true,"esModuleInterop":true,"jsx":4,"module":99,"noFallthroughCasesInSwitch":true,"noImplicitAny":true,"noUnusedLocals":true,"noUnusedParameters":true,"rootDir":"./static-resources","skipLibCheck":true,"sourceMap":true,"strict":true,"suppressImplicitAnyIndexErrors":true,"target":2},"fileIdsList":[[1020],[842,916,917,926,927],[847,849,914,915],[913],[844],[926,928],[844,916,918,919,920,923,924,925],[844,846,848,913,922],[844,920],[107,108,844],[913,919],[107,844,846,848,920,921,922],[848],[844,845],[843,846],[843,848],[850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,869,870,872,874,875,876,877,878,879,880,881,882,883,884,885,886,887,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912],[850,852,857],[852,889],[851,856],[850,851,852,853,854,855],[851,852,853,856,889],[850,852,856,857],[856],[856,896],[850,851,852,856],[851,852,853,856],[851,852],[850,851,852,856,857],[852,888],[850,851,852,857],[850,851,865],[850,851,864],[873],[866,867],[868],[866],[850,851,865,866],[850,851,864,865,867],[871],[850,851,866,867],[850,851,852,853,856],[850,851],[851],[850,856],[178],[56],[56,179,180,181,182,183],[56,180,184,1011],[56,182,184,1011],[53,178],[184,185,1011],[930,933,935,936,937,938],[930,933,936,939],[930],[930,931],[931,932],[934],[935,938,940,944,945],[935,938,945],[107,933,935,936,937,939,940],[935,945],[935,939,941],[933,937,939],[935,939,940,941,943,944,945,946,947,948,949,950,951,952,953,954,955,956],[935,943],[107,943],[930,935,936,937,938,939,941,942],[935,938,940,943],[935,941,943],[930,933],[132,153],[126,130,131,153],[110,126,149],[132,149,153],[132],[109,132,153],[127,132,149],[132,133,134,135,136,137,138,139,140,142,143,144,145,146,147],[132,141,149,153],[130,149],[108,110,126,149,153],[130,132,136,149,153],[107,126,132,149,153],[132,149],[149],[110,127,128,129,149,150,151,152],[110,126,127,128,129,148,150],[110,126,148],[127],[148,153,156],[107,108,109,153],[60,61,154,155],[109,153],[126],[837,958,959],[114,126,841,929,957],[126,837],[74,107],[56,212,222,384,481,833],[481,482],[56,212,475,833],[475,476],[56,212,478,833],[478,479],[56,212,219,397,484,833],[484,485],[56,158,212,222,223,833],[223,224],[56,212,226,833],[226,227],[56,158,212,219,222,229,833],[229,230],[56,158,212,222,234,258,373,374,833],[374,375],[56,158,212,219,377,739],[377,378],[56,158,212,379,380,833],[380,381],[56,212,219,384,386,387,739],[387,388],[56,158,212,219,271,390,739],[390,391],[56,212,219,401,833],[401,402],[56,212,219,397,398,833],[398,399],[212,219,739],[814],[56,212,219,404,739],[404,405],[56,158,212,219,397,410,739],[410,411],[56,212,219,394,395,739],[393,395,396],[56,393,833],[56,158,212,219,407,833],[407,408],[56,158,212,219,222,431,833],[431,432],[56,212,219,397,413,833],[413,414],[56,212,416,833],[416,417],[56,212,219,419,833],[419,420],[56,212,219,424,425,833],[425,426],[56,212,219,428,833],[428,429],[56,158,212,435,436,833],[436,437],[56,158,212,219,232,833],[232,233],[56,158,212,439,833],[439,440],[279],[56,212,384,442,833],[442,443],[740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759],[56,212,219,445,739],[445,446],[56,739],[448],[56,212,222,384,460,461,833],[461,462],[56,212,450,833],[450,451],[56,212,453,833],[453,454],[56,212,219,424,456,739],[456,457],[56,212,464,833],[464,465],[56,158,212,219,467,833],[467,468],[56,212,222,384,460,471,472,833],[472,473],[158,212,219,397,487,833],[487,488],[56,384],[385],[212,492,493,833],[493,494],[56,158,212,219,499,739],[499,500,501],[500],[56,212,424,496,833],[496,497],[56,212,503,833],[503,504],[56,212,219,506,739],[506,507],[56,158,212,219,509,739],[509,510],[212,739],[831],[56,212,219,512,739],[512,513],[818],[56,212],[820],[56,158,212,219,518,739],[518,519],[56,158,212,219,397,515,833],[515,516],[56,158,212,219,521,833],[521,522],[56,212,219,527,833],[527,528],[56,212,524,833],[524,525],[56,158,222,225,228,231,234,258,281,283,285,376,379,382,386,389,392,397,400,403,406,409,412,415,418,421,424,427,430,433,438,441,444,447,449,452,455,458,460,463,466,469,471,474,477,480,483,486,489,492,495,498,502,505,508,511,514,517,520,523,526,529,532,535,538,541,544,547,550,553,556,559,562,565,568,571,573,576,579,582,586,588,591,595,598,602,605,608,611,615,618,620,623,626,630,633,636,639,641,644,647,650,653,656,659,663,666,668,671,674,677,680,683,687,690,693,696,699,702,705,708,711,714,717,720,739,760,813,815,816,817,819,821,822,823,824,826,828,830,832,1011],[536,537],[212,492,536,833],[530,531],[56,212,219,530,833],[490,491],[56,158,212,490,739,833],[533,534],[56,212,511,533,739,833],[56,397,434,833],[539,540],[56,158,212,539,833],[542,543],[56,158,212,219,424,542,739],[563,564],[56,212,219,563,833],[551,552],[56,212,219,397,551,739],[545,546],[212,545,833],[554,555],[56,212,219,397,554,739],[548,549],[56,212,548,833],[557,558],[56,212,557,833],[560,561],[56,212,424,560,833],[566,567],[56,212,219,566,833],[577,578],[56,212,384,573,576,577,739,833],[569,570],[212,219,397,569,739],[572],[56,219,565],[580,581],[56,212,222,541,580,833],[317,459],[56,158,212,317,389,739],[584,585],[56,212,538,583,584,833],[56,212,833],[265],[158,267],[56,158],[267,268,269,270],[56,267],[56,273],[56,158,272],[272,273,274,275,276],[56,261,272],[811],[278],[280],[158,286],[56,286],[286,287,288,289,290],[282],[284],[254,256,264,266,271,277,279,281,283,285,291,296,300,307,313,317,324,327,329,333,337,343,347,352,357,363,368,370,372],[292,293,294,295],[158,292],[56,158,291],[56,286,292],[297],[297,298,299],[261,297],[308,309,310,311,312],[56,308],[56,307,311],[301,302,303,304,305,306],[56,302],[56,256,301],[56,301],[56,264,300,301],[56,300],[314,315,316],[56,158,254,314],[325,326],[56,325],[56,158,256,324],[328],[330,331,332],[56,330],[334,335,336],[56,334],[255],[56,252,254],[253],[318,319,320,321,322,323],[56,319],[56,158,256,318],[56,300,318],[318],[56,261,277,300],[338,339,340,341,342],[158,340],[56,158,338,339],[53,56,340],[344,345,346],[56,344],[348,349,350,351],[158,348],[353,354,355,356],[158,353],[358,359,360,361,362],[158,358],[364,365,366,367],[158,364],[56,158,277],[369],[371],[261],[259,260,261,262,263],[172,174,176],[173],[172],[175],[56,184,1011],[189],[53,184,186,188,190,1011],[187],[56,158,163,169],[170],[169,171],[56,163,169,191],[158],[159,160,161],[162],[56,207,208],[209],[159,160,161,163,169,171,177,191,192,193,195,196,197,198,201,202,203,204,206,210,211],[171],[171,191],[197],[164],[167],[164,165,166,167,168],[53],[53,164,165,166],[194],[169,191],[205],[177],[163],[199,200],[54],[761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792],[251],[245,247],[235,245,246,248,249,250],[245],[235,245],[236,237,238,239,240,241,242,243,244],[236,240,241,244,245,248],[236,237,238,239,240,241,242,243,244,245,246,248,249],[235,236,237,238,239,240,241,242,243,244],[329],[589,590],[56,212,492,589,833],[56,158,739],[593,594],[56,158,212,592,593,739,833],[596,597],[56,158,212,219,592,596,739],[220,221],[56,158,212,219,220,739],[574,575],[56,212,222,384,460,574,739,833],[256,257],[56,212,256,739],[254],[603,604],[56,158,212,435,603,833],[599,601],[56,505],[600],[606,607],[56,158,212,606,833],[609,610],[56,219,609],[613,614],[56,212,538,579,591,612,613,833],[56,212,579,833],[616,617],[56,158,212,219,616,833],[470],[619],[56,158,212,219,343,739],[624,625],[56,212,279,384,623,624,739,833],[621,622],[56,212,222,621,739,833],[628,629],[56,212,489,627,628,739,833],[634,635],[56,212,489,633,634,739,833],[637,638],[56,212,637,739,833],[640],[56,212,219,725],[660,661,662],[56,212,660,833],[642,643],[56,212,219,397,642,739],[645,646],[56,212,645,739,833],[648,649],[56,212,384,648,739,833],[651,652],[56,212,651,739,833],[654,655],[56,212,653,654,739,833],[657,658],[56,212,222,657,739,833],[212,213,214,215,216,217,218,721,722,723,725],[721,722,723],[53,212],[833],[212,213,214,215,216,217,218,724],[53,56,214],[215],[212,214,737],[158,212,213,214,215,216,217,218,724],[212,214,215,217,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738],[212,222,225,228,231,234,376,379,382,389,392,394,397,400,403,406,409,412,415,418,421,424,427,430,433,438,441,444,447,452,455,458,460,463,466,469,474,477,480,483,486,489,492,495,498,502,505,508,511,514,517,520,523,526,529,532,535,538,541,544,547,550,553,556,559,562,565,568,571,576,579,582,586,591,595,598,605,608,611,615,618,620,623,626,630,633,636,639,644,647,650,653,656,659,663,666,671,674,677,680,683,687,690,693,696,699,702,708,711,714,717,720,721,1011],[222,225,228,231,234,258,376,379,382,389,392,394,397,400,403,406,409,412,415,418,421,424,427,430,433,438,441,444,447,449,452,455,458,460,463,466,469,474,477,480,483,486,489,492,495,498,502,505,508,511,514,517,520,523,526,529,532,535,538,541,544,547,550,553,556,559,562,565,568,571,573,576,579,582,586,588,591,595,598,602,605,608,611,615,618,620,623,626,630,633,636,639,641,644,647,650,653,656,659,663,666,668,671,674,677,680,683,687,690,693,696,699,702,708,711,714,717,720,1011],[212,215,725],[212,725],[212],[725],[724,725],[212,721,725],[664,665],[56,158,212,219,664,739],[667],[56,474],[669,670],[56,158,212,435,669,833],[700,701],[56,212,219,397,700,833],[688,689],[56,158,212,219,688,833],[672,673],[56,212,219,672,833],[675,676],[56,212,675,833],[678,679],[56,212,219,678,833],[697,698],[56,212,219,697,833],[681,682],[56,212,219,681,833],[685,686],[56,212,219,517,615,677,684,685,739],[56,516],[691,692],[56,212,219,691,833],[694,695],[56,212,219,397,694,833],[706,707],[56,212,219,397,705,706,739],[703,704],[56,212,703,833],[370],[709,710],[56,158,212,492,495,502,508,535,538,591,615,709,739,833],[712,713],[56,158,212,219,397,712,833],[715,716],[56,158,212,715,739,833],[718,719],[56,158,212,219,718,833],[631,632],[56,212,258,384,631,833],[384],[56,383],[422,423],[56,158,212,215,219,422,739],[829],[266],[587],[825],[793],[666],[794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,812],[827],[1020,1021,1022,1023,1024],[1020,1022],[107],[74,76,99,107,1026,1027,1028],[76,107],[1033],[1032],[1037],[963],[1042,1045],[1041,1042,1044],[1039,1040,1041,1042],[1043],[74,75,107,1049],[75,107],[1052],[1054,1060],[1055,1056,1057,1058,1059],[1060],[1064],[1065],[1074],[1071,1073],[1080,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092],[1080,1081,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092],[1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092],[1080,1081,1082,1084,1085,1086,1087,1088,1089,1090,1091,1092],[1080,1081,1082,1083,1085,1086,1087,1088,1089,1090,1091,1092],[1080,1081,1082,1083,1084,1086,1087,1088,1089,1090,1091,1092],[1080,1081,1082,1083,1084,1085,1087,1088,1089,1090,1091,1092],[1080,1081,1082,1083,1084,1085,1086,1088,1089,1090,1091,1092],[1080,1081,1082,1083,1084,1085,1086,1087,1089,1090,1091,1092],[1080,1081,1082,1083,1084,1085,1086,1087,1088,1090,1091,1092],[1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1091,1092],[1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1092],[1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091],[1099],[1095,1096,1097,1098],[76,99,107,1102,1103],[74,81,90],[66,74,81],[90],[72,74,81],[74],[74,90,99],[81,90,99],[74,75,76,81,90,93,99],[76,90,93,99],[62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,99,100,101,102,103,104,105,106],[72,74,90],[64],[95],[88,100,102],[81,90],[81],[87,99],[74,75,90,102],[1108],[1109],[56,1061,1121],[56,1060,1123],[56,1060],[383,1127,1128,1129,1130],[54,56,1135],[54,56],[56,1145],[56,1132,1133,1134,1145],[56,1132,1135,1145],[56,1132,1135],[1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144],[52,53,54,55],[1121],[76,90,107],[53,56,1061],[56,1154],[1074,1157],[962],[1159],[107,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174],[1163,1164,1173],[1164,1173],[1152,1163,1164,1173],[1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1174],[1164],[70,1163,1173],[70,107,1156,1159,1160,1162,1175],[74,76,78,81,90,93,99,105,107],[1179],[838],[839,840],[1067,1068],[1067,1068,1069,1070],[1072],[96,97],[56,58,59],[76,78,81],[112],[81,108,109,112,113],[116],[114],[108,114,116,118],[112,114,119,124],[81,114],[81,108,109,114,115,116,117,118,119,120,121,122,123],[81,114,123],[108],[81,111,114,116,122,123,124,125],[57,836,960,994],[57],[57,836,967,968,969,970,971,979,980,981,982,983,984,985,986,995],[57,157,994,995],[57,966,969,994],[57,835],[57,978],[57,59,157,967,968,994],[57,960,970,994],[57,58,59,157,833],[57,834],[57,996,997,998,999,1000],[57,58,59,1001],[57,1003,1004,1005,1006,1007],[56,57,157,987],[57,988,989,990,991,992,993],[57,987],[57,968,987],[57,960],[57,833],[57,961,965],[56,57,964],[57,184,1010,1011,1012],[57,184,1011],[57,184,186,833,1009,1010,1011,1013,1015,1017],[57,184,711,833,1010],[57,833,1010,1011,1013,1014,1015,1016],[57,1010,1011,1013,1014,1015],[57,833,1013,1014],[57,978,1010,1011,1013],[57,109,157,973,974],[57,972,973,974,975,976,977],[57,974],[57,109,157]],"referencedMap":[[1022,1],[928,2],[916,3],[914,4],[915,5],[929,6],[926,7],[927,8],[925,9],[921,10],[922,11],[923,12],[924,13],[846,14],[847,15],[848,14],[849,16],[913,17],[863,18],[861,18],[888,19],[876,20],[856,21],[886,20],[887,20],[890,22],[891,20],[858,23],[892,20],[893,20],[894,20],[895,20],[896,24],[897,25],[898,20],[854,20],[899,20],[900,20],[901,24],[902,20],[903,20],[904,26],[905,20],[906,22],[907,20],[855,20],[908,20],[909,20],[910,27],[853,28],[859,29],[889,30],[862,31],[911,4],[864,32],[865,33],[874,34],[873,35],[869,36],[868,35],[870,37],[867,38],[866,39],[872,40],[871,37],[875,41],[857,42],[852,43],[850,44],[851,45],[880,24],[877,44],[179,46],[182,47],[184,48],[181,49],[183,50],[180,51],[185,49],[186,52],[939,53],[942,54],[931,55],[932,56],[933,57],[935,58],[946,59],[947,60],[941,61],[948,62],[949,63],[950,63],[940,64],[957,65],[952,59],[951,66],[953,67],[943,68],[954,62],[955,60],[945,69],[956,66],[944,70],[937,71],[135,72],[132,73],[147,72],[140,74],[145,75],[144,76],[134,77],[143,78],[148,79],[146,75],[142,80],[131,81],[136,82],[137,83],[138,84],[139,85],[133,76],[150,86],[153,87],[149,88],[141,89],[151,90],[157,91],[154,92],[156,93],[155,94],[837,95],[960,96],[958,97],[959,98],[918,99],[482,100],[483,101],[476,102],[477,103],[479,104],[480,105],[485,106],[486,107],[224,108],[225,109],[227,110],[228,111],[230,112],[231,113],[375,114],[376,115],[378,116],[379,117],[381,118],[382,119],[388,120],[389,121],[391,122],[392,123],[402,124],[403,125],[399,126],[400,127],[814,128],[815,129],[405,130],[406,131],[411,132],[412,133],[396,134],[397,135],[394,136],[408,137],[409,138],[432,139],[433,140],[414,141],[415,142],[417,143],[418,144],[420,145],[421,146],[426,147],[427,148],[429,149],[430,150],[437,151],[438,152],[233,153],[234,154],[440,155],[441,156],[816,157],[443,158],[444,159],[760,160],[446,161],[447,162],[448,163],[449,164],[462,165],[463,166],[451,167],[452,168],[454,169],[455,170],[457,171],[458,172],[465,173],[466,174],[468,175],[469,176],[473,177],[474,178],[488,179],[489,180],[385,181],[386,182],[494,183],[495,184],[500,185],[502,186],[501,187],[497,188],[498,189],[504,190],[505,191],[507,192],[508,193],[510,194],[511,195],[831,196],[832,197],[513,198],[514,199],[818,181],[819,200],[820,201],[821,202],[519,203],[520,204],[516,205],[517,206],[522,207],[523,208],[528,209],[529,210],[525,211],[526,212],[833,213],[538,214],[537,215],[532,216],[531,217],[492,218],[491,219],[535,220],[534,221],[435,222],[541,223],[540,224],[544,225],[543,226],[565,227],[564,228],[553,229],[552,230],[547,231],[546,232],[556,233],[555,234],[550,235],[549,236],[559,237],[558,238],[562,239],[561,240],[568,241],[567,242],[579,243],[578,244],[571,245],[570,246],[573,247],[572,248],[582,249],[581,250],[460,251],[459,252],[586,253],[585,254],[583,255],[266,256],[265,47],[268,257],[267,258],[271,259],[269,260],[274,261],[273,262],[277,263],[276,264],[272,47],[812,265],[278,47],[279,266],[281,267],[287,268],[286,258],[288,269],[291,270],[290,269],[283,271],[285,272],[373,273],[296,274],[293,275],[292,276],[294,277],[299,278],[300,279],[298,280],[297,47],[313,281],[309,282],[308,47],[312,283],[311,47],[307,284],[303,285],[302,286],[304,287],[306,288],[301,289],[317,290],[315,291],[327,292],[326,293],[325,294],[329,295],[328,47],[333,296],[331,297],[330,47],[337,298],[335,299],[334,289],[256,300],[255,301],[254,302],[253,47],[324,303],[320,304],[319,305],[321,306],[323,307],[318,308],[343,309],[341,310],[340,311],[339,47],[342,312],[347,313],[345,314],[344,47],[352,315],[349,316],[348,258],[357,317],[354,318],[353,258],[356,47],[363,319],[360,47],[359,320],[358,258],[362,47],[368,321],[365,322],[364,323],[367,47],[370,324],[369,47],[372,325],[371,47],[259,47],[262,326],[264,327],[263,47],[261,47],[177,328],[174,329],[173,330],[176,331],[175,330],[189,332],[190,333],[191,334],[188,335],[187,47],[170,336],[171,337],[196,338],[197,339],[159,340],[162,341],[163,342],[209,343],[207,47],[210,344],[212,345],[193,346],[192,347],[198,348],[165,349],[168,350],[169,351],[166,352],[164,352],[167,353],[195,354],[194,355],[206,356],[205,357],[202,358],[201,359],[761,360],[763,360],[764,360],[765,360],[767,47],[793,361],[770,360],[778,47],[779,47],[780,47],[785,47],[786,47],[790,47],[252,362],[248,363],[251,364],[244,365],[242,366],[241,366],[240,365],[237,366],[238,365],[246,367],[239,366],[236,365],[243,366],[249,368],[250,369],[245,370],[247,366],[822,371],[591,372],[590,373],[219,374],[595,375],[594,376],[598,377],[597,378],[222,379],[221,380],[576,381],[575,382],[258,383],[257,384],[823,385],[605,386],[604,387],[602,388],[599,389],[600,47],[601,390],[608,391],[607,392],[611,393],[610,394],[615,395],[614,396],[612,397],[618,398],[617,399],[471,400],[470,181],[620,401],[619,402],[626,403],[625,404],[623,405],[622,406],[630,407],[629,408],[636,409],[635,410],[639,411],[638,412],[641,413],[640,414],[663,415],[661,416],[662,47],[644,417],[643,418],[647,419],[646,420],[650,421],[649,422],[653,423],[652,424],[656,425],[655,426],[659,427],[658,428],[726,429],[724,430],[213,431],[214,432],[725,433],[215,434],[733,435],[738,436],[737,437],[739,438],[722,439],[721,440],[728,441],[731,442],[732,443],[729,444],[730,445],[723,446],[666,447],[665,448],[668,449],[667,450],[671,451],[670,452],[702,453],[701,454],[690,455],[689,456],[674,457],[673,458],[677,459],[676,460],[680,461],[679,462],[699,463],[698,464],[683,465],[682,466],[687,467],[686,468],[684,469],[693,470],[692,471],[696,472],[695,473],[708,474],[707,475],[705,476],[704,477],[824,478],[711,479],[710,480],[714,481],[713,482],[717,483],[716,484],[720,485],[719,486],[633,487],[632,488],[627,489],[384,490],[424,491],[423,492],[830,493],[829,494],[588,495],[592,47],[826,496],[794,497],[795,497],[796,498],[797,497],[798,497],[813,499],[799,497],[800,497],[801,497],[802,497],[803,497],[806,497],[807,497],[804,497],[808,497],[809,497],[805,497],[810,497],[828,500],[827,181],[158,47],[1025,501],[1021,1],[1023,502],[1024,1],[108,503],[1029,504],[1030,505],[1034,506],[1035,507],[1038,508],[964,509],[1046,510],[1045,511],[1043,512],[1040,513],[1048,503],[1050,514],[1051,515],[1053,516],[1055,517],[1056,517],[1057,517],[1060,518],[1058,519],[1059,519],[1061,47],[1065,520],[1066,521],[1075,522],[1074,523],[1079,99],[1081,524],[1082,525],[1080,526],[1083,527],[1084,528],[1085,529],[1086,530],[1087,531],[1088,532],[1089,533],[1090,534],[1091,535],[1092,536],[1094,516],[1098,537],[1099,538],[1097,537],[1104,539],[66,540],[67,541],[70,542],[71,543],[73,544],[74,544],[75,545],[76,546],[77,547],[78,548],[107,549],[79,544],[81,550],[84,551],[85,552],[88,544],[89,553],[90,544],[93,554],[95,555],[99,556],[101,542],[104,557],[105,542],[1106,544],[1109,558],[1108,559],[1110,503],[1115,47],[1117,47],[1118,47],[1119,47],[1120,47],[1122,560],[1124,561],[1123,562],[1125,47],[1126,47],[1127,490],[1131,563],[1129,47],[383,47],[1128,490],[1146,47],[1136,564],[1137,565],[1132,47],[1138,566],[1139,47],[1135,567],[1140,566],[1133,568],[1141,568],[1142,569],[1143,47],[1134,568],[1144,47],[1145,570],[56,571],[57,47],[1147,572],[1149,503],[1028,573],[1151,503],[1154,574],[1155,575],[1158,576],[963,577],[1160,578],[1175,579],[1174,580],[1165,581],[1166,582],[1173,583],[1167,582],[1168,581],[1169,581],[1170,581],[1171,584],[1164,585],[1172,580],[1176,586],[1178,587],[1180,588],[839,589],[841,590],[1102,573],[1069,591],[1071,592],[1070,591],[1026,544],[1073,593],[98,594],[59,595],[112,596],[113,597],[114,598],[117,599],[118,600],[119,601],[120,602],[122,603],[124,604],[123,600],[125,605],[116,606],[126,607],[995,608],[980,609],[986,609],[987,610],[982,609],[967,609],[984,609],[985,611],[983,609],[970,612],[836,613],[979,614],[981,609],[969,615],[968,609],[971,616],[834,617],[835,618],[996,609],[999,609],[1001,619],[1000,609],[997,609],[998,609],[1002,620],[1003,609],[1004,609],[1008,621],[1007,609],[1005,609],[1006,609],[988,622],[992,609],[989,609],[994,623],[993,624],[991,625],[990,626],[961,627],[966,628],[965,629],[1010,609],[1013,630],[1012,631],[1009,609],[1018,632],[1011,633],[1017,634],[1016,635],[1015,636],[1014,637],[975,638],[978,639],[974,609],[977,609],[976,640],[972,609],[973,641]],"exportedModulesMap":[[1022,1],[928,2],[916,3],[914,4],[915,5],[929,6],[926,7],[927,8],[925,9],[921,10],[922,11],[923,12],[924,13],[846,14],[847,15],[848,14],[849,16],[913,17],[863,18],[861,18],[888,19],[876,20],[856,21],[886,20],[887,20],[890,22],[891,20],[858,23],[892,20],[893,20],[894,20],[895,20],[896,24],[897,25],[898,20],[854,20],[899,20],[900,20],[901,24],[902,20],[903,20],[904,26],[905,20],[906,22],[907,20],[855,20],[908,20],[909,20],[910,27],[853,28],[859,29],[889,30],[862,31],[911,4],[864,32],[865,33],[874,34],[873,35],[869,36],[868,35],[870,37],[867,38],[866,39],[872,40],[871,37],[875,41],[857,42],[852,43],[850,44],[851,45],[880,24],[877,44],[179,46],[182,47],[184,48],[181,49],[183,50],[180,51],[185,49],[186,52],[939,53],[942,54],[931,55],[932,56],[933,57],[935,58],[946,59],[947,60],[941,61],[948,62],[949,63],[950,63],[940,64],[957,65],[952,59],[951,66],[953,67],[943,68],[954,62],[955,60],[945,69],[956,66],[944,70],[937,71],[135,72],[132,73],[147,72],[140,74],[145,75],[144,76],[134,77],[143,78],[148,79],[146,75],[142,80],[131,81],[136,82],[137,83],[138,84],[139,85],[133,76],[150,86],[153,87],[149,88],[141,89],[151,90],[157,91],[154,92],[156,93],[155,94],[837,95],[960,96],[958,97],[959,98],[918,99],[482,100],[483,101],[476,102],[477,103],[479,104],[480,105],[485,106],[486,107],[224,108],[225,109],[227,110],[228,111],[230,112],[231,113],[375,114],[376,115],[378,116],[379,117],[381,118],[382,119],[388,120],[389,121],[391,122],[392,123],[402,124],[403,125],[399,126],[400,127],[814,128],[815,129],[405,130],[406,131],[411,132],[412,133],[396,134],[397,135],[394,136],[408,137],[409,138],[432,139],[433,140],[414,141],[415,142],[417,143],[418,144],[420,145],[421,146],[426,147],[427,148],[429,149],[430,150],[437,151],[438,152],[233,153],[234,154],[440,155],[441,156],[816,157],[443,158],[444,159],[760,160],[446,161],[447,162],[448,163],[449,164],[462,165],[463,166],[451,167],[452,168],[454,169],[455,170],[457,171],[458,172],[465,173],[466,174],[468,175],[469,176],[473,177],[474,178],[488,179],[489,180],[385,181],[386,182],[494,183],[495,184],[500,185],[502,186],[501,187],[497,188],[498,189],[504,190],[505,191],[507,192],[508,193],[510,194],[511,195],[831,196],[832,197],[513,198],[514,199],[818,181],[819,200],[820,201],[821,202],[519,203],[520,204],[516,205],[517,206],[522,207],[523,208],[528,209],[529,210],[525,211],[526,212],[833,213],[538,214],[537,215],[532,216],[531,217],[492,218],[491,219],[535,220],[534,221],[435,222],[541,223],[540,224],[544,225],[543,226],[565,227],[564,228],[553,229],[552,230],[547,231],[546,232],[556,233],[555,234],[550,235],[549,236],[559,237],[558,238],[562,239],[561,240],[568,241],[567,242],[579,243],[578,244],[571,245],[570,246],[573,247],[572,248],[582,249],[581,250],[460,251],[459,252],[586,253],[585,254],[583,255],[266,256],[265,47],[268,257],[267,258],[271,259],[269,260],[274,261],[273,262],[277,263],[276,264],[272,47],[812,265],[278,47],[279,266],[281,267],[287,268],[286,258],[288,269],[291,270],[290,269],[283,271],[285,272],[373,273],[296,274],[293,275],[292,276],[294,277],[299,278],[300,279],[298,280],[297,47],[313,281],[309,282],[308,47],[312,283],[311,47],[307,284],[303,285],[302,286],[304,287],[306,288],[301,289],[317,290],[315,291],[327,292],[326,293],[325,294],[329,295],[328,47],[333,296],[331,297],[330,47],[337,298],[335,299],[334,289],[256,300],[255,301],[254,302],[253,47],[324,303],[320,304],[319,305],[321,306],[323,307],[318,308],[343,309],[341,310],[340,311],[339,47],[342,312],[347,313],[345,314],[344,47],[352,315],[349,316],[348,258],[357,317],[354,318],[353,258],[356,47],[363,319],[360,47],[359,320],[358,258],[362,47],[368,321],[365,322],[364,323],[367,47],[370,324],[369,47],[372,325],[371,47],[259,47],[262,326],[264,327],[263,47],[261,47],[177,328],[174,329],[173,330],[176,331],[175,330],[189,332],[190,333],[191,334],[188,335],[187,47],[170,336],[171,337],[196,338],[197,339],[159,340],[162,341],[163,342],[209,343],[207,47],[210,344],[212,345],[193,346],[192,347],[198,348],[165,349],[168,350],[169,351],[166,352],[164,352],[167,353],[195,354],[194,355],[206,356],[205,357],[202,358],[201,359],[761,360],[763,360],[764,360],[765,360],[767,47],[793,361],[770,360],[778,47],[779,47],[780,47],[785,47],[786,47],[790,47],[252,362],[248,363],[251,364],[244,365],[242,366],[241,366],[240,365],[237,366],[238,365],[246,367],[239,366],[236,365],[243,366],[249,368],[250,369],[245,370],[247,366],[822,371],[591,372],[590,373],[219,374],[595,375],[594,376],[598,377],[597,378],[222,379],[221,380],[576,381],[575,382],[258,383],[257,384],[823,385],[605,386],[604,387],[602,388],[599,389],[600,47],[601,390],[608,391],[607,392],[611,393],[610,394],[615,395],[614,396],[612,397],[618,398],[617,399],[471,400],[470,181],[620,401],[619,402],[626,403],[625,404],[623,405],[622,406],[630,407],[629,408],[636,409],[635,410],[639,411],[638,412],[641,413],[640,414],[663,415],[661,416],[662,47],[644,417],[643,418],[647,419],[646,420],[650,421],[649,422],[653,423],[652,424],[656,425],[655,426],[659,427],[658,428],[726,429],[724,430],[213,431],[214,432],[725,433],[215,434],[733,435],[738,436],[737,437],[739,438],[722,439],[721,440],[728,441],[731,442],[732,443],[729,444],[730,445],[723,446],[666,447],[665,448],[668,449],[667,450],[671,451],[670,452],[702,453],[701,454],[690,455],[689,456],[674,457],[673,458],[677,459],[676,460],[680,461],[679,462],[699,463],[698,464],[683,465],[682,466],[687,467],[686,468],[684,469],[693,470],[692,471],[696,472],[695,473],[708,474],[707,475],[705,476],[704,477],[824,478],[711,479],[710,480],[714,481],[713,482],[717,483],[716,484],[720,485],[719,486],[633,487],[632,488],[627,489],[384,490],[424,491],[423,492],[830,493],[829,494],[588,495],[592,47],[826,496],[794,497],[795,497],[796,498],[797,497],[798,497],[813,499],[799,497],[800,497],[801,497],[802,497],[803,497],[806,497],[807,497],[804,497],[808,497],[809,497],[805,497],[810,497],[828,500],[827,181],[158,47],[1025,501],[1021,1],[1023,502],[1024,1],[108,503],[1029,504],[1030,505],[1034,506],[1035,507],[1038,508],[964,509],[1046,510],[1045,511],[1043,512],[1040,513],[1048,503],[1050,514],[1051,515],[1053,516],[1055,517],[1056,517],[1057,517],[1060,518],[1058,519],[1059,519],[1061,47],[1065,520],[1066,521],[1075,522],[1074,523],[1079,99],[1081,524],[1082,525],[1080,526],[1083,527],[1084,528],[1085,529],[1086,530],[1087,531],[1088,532],[1089,533],[1090,534],[1091,535],[1092,536],[1094,516],[1098,537],[1099,538],[1097,537],[1104,539],[66,540],[67,541],[70,542],[71,543],[73,544],[74,544],[75,545],[76,546],[77,547],[78,548],[107,549],[79,544],[81,550],[84,551],[85,552],[88,544],[89,553],[90,544],[93,554],[95,555],[99,556],[101,542],[104,557],[105,542],[1106,544],[1109,558],[1108,559],[1110,503],[1115,47],[1117,47],[1118,47],[1119,47],[1120,47],[1122,560],[1124,561],[1123,562],[1125,47],[1126,47],[1127,490],[1131,563],[1129,47],[383,47],[1128,490],[1146,47],[1136,564],[1137,565],[1132,47],[1138,566],[1139,47],[1135,567],[1140,566],[1133,568],[1141,568],[1142,569],[1143,47],[1134,568],[1144,47],[1145,570],[56,571],[57,47],[1147,572],[1149,503],[1028,573],[1151,503],[1154,574],[1155,575],[1158,576],[963,577],[1160,578],[1175,579],[1174,580],[1165,581],[1166,582],[1173,583],[1167,582],[1168,581],[1169,581],[1170,581],[1171,584],[1164,585],[1172,580],[1176,586],[1178,587],[1180,588],[839,589],[841,590],[1102,573],[1069,591],[1071,592],[1070,591],[1026,544],[1073,593],[98,594],[59,595],[112,596],[113,597],[114,598],[117,599],[118,600],[119,601],[120,602],[122,603],[124,604],[123,600],[125,605],[116,606],[126,607],[995,608],[980,609],[986,609],[987,610],[982,609],[967,609],[984,609],[985,611],[983,609],[970,612],[836,613],[979,614],[981,609],[969,615],[968,609],[971,616],[834,617],[835,618],[996,609],[999,609],[1001,619],[1000,609],[997,609],[998,609],[1002,620],[1003,609],[1004,609],[1008,621],[1007,609],[1005,609],[1006,609],[988,622],[992,609],[989,609],[994,623],[993,624],[991,625],[990,626],[961,627],[966,628],[965,629],[1010,609],[1013,630],[1012,631],[1009,609],[1018,632],[1011,633],[1017,634],[1016,635],[1015,636],[1014,637],[975,638],[978,639],[974,609],[977,609],[976,640],[972,609],[973,641]],"semanticDiagnosticsPerFile":[1022,1020,842,928,916,917,914,915,929,919,926,920,927,925,921,843,922,923,924,845,846,847,848,849,844,913,863,861,912,888,876,856,886,887,890,891,858,892,893,894,895,896,897,898,854,899,900,901,902,903,904,905,906,907,855,908,909,910,853,859,889,862,911,864,865,874,873,869,868,870,867,866,872,871,875,857,852,850,860,851,881,882,879,880,878,883,877,885,884,179,182,184,181,183,180,185,186,178,939,942,931,932,933,930,935,934,936,946,947,941,948,949,950,940,957,952,951,953,943,954,955,945,956,944,937,938,135,132,147,140,145,144,134,143,148,146,142,131,136,137,138,139,133,150,128,153,129,149,127,141,152,110,151,157,154,156,60,155,61,837,960,958,959,918,482,481,483,476,475,477,479,478,480,485,484,486,224,223,225,227,226,228,230,229,231,375,374,376,378,377,379,381,380,382,388,387,389,391,390,392,402,401,403,399,398,400,814,815,405,404,406,411,410,412,396,395,397,394,393,408,407,409,432,431,433,414,413,415,417,416,418,420,419,421,426,425,427,429,428,430,437,436,438,233,232,234,440,439,441,816,443,442,444,740,741,742,743,744,745,746,747,748,749,760,750,751,752,753,754,755,756,757,758,759,446,445,447,448,449,817,462,461,463,451,450,452,454,453,455,457,456,458,465,464,466,468,467,469,473,472,474,488,487,489,385,386,494,493,495,500,499,502,501,497,496,498,504,503,505,507,506,508,510,509,511,831,832,513,512,514,818,819,820,821,519,518,520,516,515,517,522,521,523,528,527,529,525,524,526,833,538,537,536,532,531,530,492,491,490,535,534,533,435,434,541,540,539,544,543,542,565,564,563,553,552,551,547,546,545,556,555,554,550,549,548,559,558,557,562,561,560,568,567,566,579,578,577,571,570,569,573,572,582,581,580,460,459,586,585,584,583,266,265,268,270,267,271,269,274,273,275,277,276,272,811,812,278,279,280,281,287,286,289,288,291,290,282,283,284,285,373,296,293,295,292,294,299,300,298,297,313,309,308,310,312,311,307,303,302,305,304,306,301,317,316,315,314,327,326,325,329,328,333,331,332,330,337,335,336,334,256,255,254,253,324,320,319,322,321,323,318,343,341,338,340,339,342,347,345,346,344,352,349,350,348,351,357,354,355,353,356,363,360,359,361,358,362,368,365,366,364,367,370,369,372,371,259,260,262,264,263,261,172,177,174,173,176,175,189,190,191,188,187,170,171,204,196,197,159,161,162,163,160,209,211,207,210,208,212,193,192,198,165,168,169,166,164,167,195,194,206,205,202,200,201,199,203,771,761,772,773,762,774,763,764,765,766,767,787,768,793,791,775,776,777,769,770,778,792,788,779,782,783,780,784,785,781,786,789,790,252,248,235,251,244,242,241,240,237,238,246,239,236,243,249,250,245,247,822,591,590,589,219,595,594,593,598,597,596,222,221,220,576,575,574,258,257,823,605,604,603,602,599,600,601,608,607,606,611,610,609,615,614,613,612,618,617,616,471,470,620,619,626,625,624,623,622,621,630,629,628,636,635,634,639,638,637,641,640,663,661,660,662,644,643,642,647,646,645,650,649,648,653,652,651,656,655,654,659,658,657,726,724,213,214,727,725,217,215,733,738,737,739,734,722,721,728,216,731,732,729,730,723,735,736,218,666,665,664,668,667,671,670,669,702,701,700,690,689,688,674,673,672,677,676,675,680,679,678,699,698,697,683,682,681,687,686,684,685,693,692,691,696,695,694,708,707,706,705,704,703,824,711,710,709,714,713,712,717,716,715,720,719,718,633,632,631,627,384,424,423,422,830,829,588,587,592,826,825,794,795,796,797,798,813,799,800,801,802,803,806,807,804,808,809,805,810,828,827,158,1019,1025,1021,1023,1024,108,1029,1030,1031,1032,1034,1035,1036,1033,1038,964,1046,1044,1045,1047,1039,1043,1040,1042,1048,1050,1051,1053,1055,1056,1057,1054,1060,1058,1059,1061,1062,1027,1063,1064,1065,1066,1075,1074,1076,1077,1041,1078,1079,1081,1082,1080,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1096,1095,1098,1099,1097,1049,1100,1101,1037,1103,1104,62,64,65,66,67,68,69,70,71,72,73,74,75,63,106,76,77,78,107,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,99,100,101,102,103,104,105,1105,1106,1107,1109,1108,1110,1111,1112,54,1113,1114,1115,1116,1117,1118,1119,1120,1122,1124,1123,1125,1126,1130,1127,1131,1129,383,1128,1146,1136,1137,1132,1138,1139,1135,1140,1133,1141,1142,1143,1134,1144,1145,52,56,57,1147,1148,1149,1028,1150,55,1151,1152,1153,1154,1155,1156,1158,1157,963,962,1160,1052,1161,1177,1175,1174,1165,1166,1173,1167,1168,1169,1170,1171,1164,1172,1163,1176,1162,1178,1179,1180,839,840,841,130,109,53,838,1102,58,1067,1069,1071,1070,1068,1026,1073,1072,96,97,98,59,1121,1159,11,12,14,13,2,15,16,17,18,19,20,21,22,3,4,26,23,24,25,27,28,29,5,30,31,32,33,6,34,35,36,37,7,42,38,39,40,41,8,46,43,44,45,47,9,48,49,50,1,10,51,111,112,113,115,114,117,118,119,120,121,122,124,123,125,116,126,995,980,986,987,982,967,984,985,983,970,836,979,981,969,968,971,834,835,996,999,1001,1000,997,998,1002,1003,1004,1008,1007,1005,1006,988,992,989,994,993,991,990,961,966,965,1010,1013,1012,1009,[1018,[{"file":"./static-resources/src/themes/index.ts","start":878,"length":4,"messageText":"'size' is declared but its value is never read.","category":1,"code":6133,"reportsUnnecessary":true}]],1011,1017,1016,1015,1014,975,978,974,977,976,972,973],"affectedFilesPendingEmit":[[1022,1],[1020,1],[842,1],[928,1],[916,1],[917,1],[914,1],[915,1],[929,1],[919,1],[926,1],[920,1],[927,1],[925,1],[921,1],[843,1],[922,1],[923,1],[924,1],[845,1],[846,1],[847,1],[848,1],[849,1],[844,1],[913,1],[863,1],[861,1],[912,1],[888,1],[876,1],[856,1],[886,1],[887,1],[890,1],[891,1],[858,1],[892,1],[893,1],[894,1],[895,1],[896,1],[897,1],[898,1],[854,1],[899,1],[900,1],[901,1],[902,1],[903,1],[904,1],[905,1],[906,1],[907,1],[855,1],[908,1],[909,1],[910,1],[853,1],[859,1],[889,1],[862,1],[911,1],[864,1],[865,1],[874,1],[873,1],[869,1],[868,1],[870,1],[867,1],[866,1],[872,1],[871,1],[875,1],[857,1],[852,1],[850,1],[860,1],[851,1],[881,1],[882,1],[879,1],[880,1],[878,1],[883,1],[877,1],[885,1],[884,1],[179,1],[182,1],[184,1],[181,1],[183,1],[180,1],[185,1],[186,1],[178,1],[939,1],[942,1],[931,1],[932,1],[933,1],[930,1],[935,1],[934,1],[936,1],[946,1],[947,1],[941,1],[948,1],[949,1],[950,1],[940,1],[957,1],[952,1],[951,1],[953,1],[943,1],[954,1],[955,1],[945,1],[956,1],[944,1],[937,1],[938,1],[135,1],[132,1],[147,1],[140,1],[145,1],[144,1],[134,1],[143,1],[148,1],[146,1],[142,1],[131,1],[136,1],[137,1],[138,1],[139,1],[133,1],[150,1],[128,1],[153,1],[129,1],[149,1],[127,1],[141,1],[152,1],[110,1],[151,1],[157,1],[154,1],[156,1],[60,1],[155,1],[61,1],[837,1],[960,1],[958,1],[959,1],[918,1],[482,1],[481,1],[483,1],[476,1],[475,1],[477,1],[479,1],[478,1],[480,1],[485,1],[484,1],[486,1],[224,1],[223,1],[225,1],[227,1],[226,1],[228,1],[230,1],[229,1],[231,1],[375,1],[374,1],[376,1],[378,1],[377,1],[379,1],[381,1],[380,1],[382,1],[388,1],[387,1],[389,1],[391,1],[390,1],[392,1],[402,1],[401,1],[403,1],[399,1],[398,1],[400,1],[814,1],[815,1],[405,1],[404,1],[406,1],[411,1],[410,1],[412,1],[396,1],[395,1],[397,1],[394,1],[393,1],[408,1],[407,1],[409,1],[432,1],[431,1],[433,1],[414,1],[413,1],[415,1],[417,1],[416,1],[418,1],[420,1],[419,1],[421,1],[426,1],[425,1],[427,1],[429,1],[428,1],[430,1],[437,1],[436,1],[438,1],[233,1],[232,1],[234,1],[440,1],[439,1],[441,1],[816,1],[443,1],[442,1],[444,1],[740,1],[741,1],[742,1],[743,1],[744,1],[745,1],[746,1],[747,1],[748,1],[749,1],[760,1],[750,1],[751,1],[752,1],[753,1],[754,1],[755,1],[756,1],[757,1],[758,1],[759,1],[446,1],[445,1],[447,1],[448,1],[449,1],[817,1],[462,1],[461,1],[463,1],[451,1],[450,1],[452,1],[454,1],[453,1],[455,1],[457,1],[456,1],[458,1],[465,1],[464,1],[466,1],[468,1],[467,1],[469,1],[473,1],[472,1],[474,1],[488,1],[487,1],[489,1],[385,1],[386,1],[494,1],[493,1],[495,1],[500,1],[499,1],[502,1],[501,1],[497,1],[496,1],[498,1],[504,1],[503,1],[505,1],[507,1],[506,1],[508,1],[510,1],[509,1],[511,1],[831,1],[832,1],[513,1],[512,1],[514,1],[818,1],[819,1],[820,1],[821,1],[519,1],[518,1],[520,1],[516,1],[515,1],[517,1],[522,1],[521,1],[523,1],[528,1],[527,1],[529,1],[525,1],[524,1],[526,1],[833,1],[538,1],[537,1],[536,1],[532,1],[531,1],[530,1],[492,1],[491,1],[490,1],[535,1],[534,1],[533,1],[435,1],[434,1],[541,1],[540,1],[539,1],[544,1],[543,1],[542,1],[565,1],[564,1],[563,1],[553,1],[552,1],[551,1],[547,1],[546,1],[545,1],[556,1],[555,1],[554,1],[550,1],[549,1],[548,1],[559,1],[558,1],[557,1],[562,1],[561,1],[560,1],[568,1],[567,1],[566,1],[579,1],[578,1],[577,1],[571,1],[570,1],[569,1],[573,1],[572,1],[582,1],[581,1],[580,1],[460,1],[459,1],[586,1],[585,1],[584,1],[583,1],[266,1],[265,1],[268,1],[270,1],[267,1],[271,1],[269,1],[274,1],[273,1],[275,1],[277,1],[276,1],[272,1],[811,1],[812,1],[278,1],[279,1],[280,1],[281,1],[287,1],[286,1],[289,1],[288,1],[291,1],[290,1],[282,1],[283,1],[284,1],[285,1],[373,1],[296,1],[293,1],[295,1],[292,1],[294,1],[299,1],[300,1],[298,1],[297,1],[313,1],[309,1],[308,1],[310,1],[312,1],[311,1],[307,1],[303,1],[302,1],[305,1],[304,1],[306,1],[301,1],[317,1],[316,1],[315,1],[314,1],[327,1],[326,1],[325,1],[329,1],[328,1],[333,1],[331,1],[332,1],[330,1],[337,1],[335,1],[336,1],[334,1],[256,1],[255,1],[254,1],[253,1],[324,1],[320,1],[319,1],[322,1],[321,1],[323,1],[318,1],[343,1],[341,1],[338,1],[340,1],[339,1],[342,1],[347,1],[345,1],[346,1],[344,1],[352,1],[349,1],[350,1],[348,1],[351,1],[357,1],[354,1],[355,1],[353,1],[356,1],[363,1],[360,1],[359,1],[361,1],[358,1],[362,1],[368,1],[365,1],[366,1],[364,1],[367,1],[370,1],[369,1],[372,1],[371,1],[259,1],[260,1],[262,1],[264,1],[263,1],[261,1],[172,1],[177,1],[174,1],[173,1],[176,1],[175,1],[189,1],[190,1],[191,1],[188,1],[187,1],[170,1],[171,1],[204,1],[196,1],[197,1],[159,1],[161,1],[162,1],[163,1],[160,1],[209,1],[211,1],[207,1],[210,1],[208,1],[212,1],[193,1],[192,1],[198,1],[165,1],[168,1],[169,1],[166,1],[164,1],[167,1],[195,1],[194,1],[206,1],[205,1],[202,1],[200,1],[201,1],[199,1],[203,1],[771,1],[761,1],[772,1],[773,1],[762,1],[774,1],[763,1],[764,1],[765,1],[766,1],[767,1],[787,1],[768,1],[793,1],[791,1],[775,1],[776,1],[777,1],[769,1],[770,1],[778,1],[792,1],[788,1],[779,1],[782,1],[783,1],[780,1],[784,1],[785,1],[781,1],[786,1],[789,1],[790,1],[252,1],[248,1],[235,1],[251,1],[244,1],[242,1],[241,1],[240,1],[237,1],[238,1],[246,1],[239,1],[236,1],[243,1],[249,1],[250,1],[245,1],[247,1],[822,1],[591,1],[590,1],[589,1],[219,1],[595,1],[594,1],[593,1],[598,1],[597,1],[596,1],[222,1],[221,1],[220,1],[576,1],[575,1],[574,1],[258,1],[257,1],[823,1],[605,1],[604,1],[603,1],[602,1],[599,1],[600,1],[601,1],[608,1],[607,1],[606,1],[611,1],[610,1],[609,1],[615,1],[614,1],[613,1],[612,1],[618,1],[617,1],[616,1],[471,1],[470,1],[620,1],[619,1],[626,1],[625,1],[624,1],[623,1],[622,1],[621,1],[630,1],[629,1],[628,1],[636,1],[635,1],[634,1],[639,1],[638,1],[637,1],[641,1],[640,1],[663,1],[661,1],[660,1],[662,1],[644,1],[643,1],[642,1],[647,1],[646,1],[645,1],[650,1],[649,1],[648,1],[653,1],[652,1],[651,1],[656,1],[655,1],[654,1],[659,1],[658,1],[657,1],[726,1],[724,1],[213,1],[214,1],[727,1],[725,1],[217,1],[215,1],[733,1],[738,1],[737,1],[739,1],[734,1],[722,1],[721,1],[728,1],[216,1],[731,1],[732,1],[729,1],[730,1],[723,1],[735,1],[736,1],[218,1],[666,1],[665,1],[664,1],[668,1],[667,1],[671,1],[670,1],[669,1],[702,1],[701,1],[700,1],[690,1],[689,1],[688,1],[674,1],[673,1],[672,1],[677,1],[676,1],[675,1],[680,1],[679,1],[678,1],[699,1],[698,1],[697,1],[683,1],[682,1],[681,1],[687,1],[686,1],[684,1],[685,1],[693,1],[692,1],[691,1],[696,1],[695,1],[694,1],[708,1],[707,1],[706,1],[705,1],[704,1],[703,1],[824,1],[711,1],[710,1],[709,1],[714,1],[713,1],[712,1],[717,1],[716,1],[715,1],[720,1],[719,1],[718,1],[633,1],[632,1],[631,1],[627,1],[384,1],[424,1],[423,1],[422,1],[830,1],[829,1],[588,1],[587,1],[592,1],[826,1],[825,1],[794,1],[795,1],[796,1],[797,1],[798,1],[813,1],[799,1],[800,1],[801,1],[802,1],[803,1],[806,1],[807,1],[804,1],[808,1],[809,1],[805,1],[810,1],[828,1],[827,1],[158,1],[1019,1],[1025,1],[1021,1],[1023,1],[1024,1],[108,1],[1029,1],[1030,1],[1031,1],[1032,1],[1034,1],[1035,1],[1036,1],[1033,1],[1038,1],[964,1],[1046,1],[1044,1],[1045,1],[1047,1],[1039,1],[1043,1],[1040,1],[1042,1],[1048,1],[1050,1],[1051,1],[1053,1],[1055,1],[1056,1],[1057,1],[1054,1],[1060,1],[1058,1],[1059,1],[1061,1],[1062,1],[1027,1],[1063,1],[1064,1],[1065,1],[1066,1],[1075,1],[1074,1],[1076,1],[1077,1],[1041,1],[1078,1],[1079,1],[1081,1],[1082,1],[1080,1],[1083,1],[1084,1],[1085,1],[1086,1],[1087,1],[1088,1],[1089,1],[1090,1],[1091,1],[1092,1],[1093,1],[1094,1],[1096,1],[1095,1],[1098,1],[1099,1],[1097,1],[1049,1],[1100,1],[1101,1],[1037,1],[1103,1],[1104,1],[62,1],[64,1],[65,1],[66,1],[67,1],[68,1],[69,1],[70,1],[71,1],[72,1],[73,1],[74,1],[75,1],[63,1],[106,1],[76,1],[77,1],[78,1],[107,1],[79,1],[80,1],[81,1],[82,1],[83,1],[84,1],[85,1],[86,1],[87,1],[88,1],[89,1],[90,1],[91,1],[92,1],[93,1],[94,1],[95,1],[99,1],[100,1],[101,1],[102,1],[103,1],[104,1],[105,1],[1105,1],[1106,1],[1107,1],[1109,1],[1108,1],[1110,1],[1111,1],[1112,1],[54,1],[1113,1],[1114,1],[1115,1],[1116,1],[1117,1],[1118,1],[1119,1],[1120,1],[1122,1],[1124,1],[1123,1],[1125,1],[1126,1],[1130,1],[1127,1],[1131,1],[1129,1],[383,1],[1128,1],[1146,1],[1136,1],[1137,1],[1132,1],[1138,1],[1139,1],[1135,1],[1140,1],[1133,1],[1141,1],[1142,1],[1143,1],[1134,1],[1144,1],[1145,1],[52,1],[56,1],[57,1],[1147,1],[1148,1],[1149,1],[1028,1],[1150,1],[55,1],[1151,1],[1152,1],[1153,1],[1154,1],[1155,1],[1156,1],[1158,1],[1157,1],[963,1],[962,1],[1160,1],[1052,1],[1161,1],[1177,1],[1175,1],[1174,1],[1165,1],[1166,1],[1173,1],[1167,1],[1168,1],[1169,1],[1170,1],[1171,1],[1164,1],[1172,1],[1163,1],[1176,1],[1162,1],[1178,1],[1179,1],[1180,1],[839,1],[840,1],[841,1],[130,1],[109,1],[53,1],[838,1],[1102,1],[58,1],[1067,1],[1069,1],[1071,1],[1070,1],[1068,1],[1026,1],[1073,1],[1072,1],[96,1],[97,1],[98,1],[59,1],[1121,1],[1159,1],[2,1],[3,1],[4,1],[5,1],[6,1],[7,1],[8,1],[9,1],[10,1],[111,1],[112,1],[113,1],[115,1],[114,1],[117,1],[118,1],[119,1],[120,1],[121,1],[122,1],[124,1],[123,1],[125,1],[116,1],[126,1],[995,1],[980,1],[986,1],[987,1],[982,1],[967,1],[984,1],[985,1],[983,1],[970,1],[836,1],[979,1],[981,1],[969,1],[968,1],[971,1],[834,1],[835,1],[996,1],[999,1],[1001,1],[1000,1],[997,1],[998,1],[1002,1],[1003,1],[1004,1],[1008,1],[1007,1],[1005,1],[1006,1],[988,1],[992,1],[989,1],[994,1],[993,1],[991,1],[990,1],[961,1],[966,1],[965,1],[1010,1],[1013,1],[1012,1],[1009,1],[1018,1],[1011,1],[1017,1],[1016,1],[1015,1],[1014,1],[975,1],[978,1],[974,1],[977,1],[976,1],[972,1],[973,1]]},"version":"4.6.3"} ================================================ FILE: packages/component-lib/.babelrc ================================================ { "presets": [ "@babel/typescript", "@babel/preset-env", "@babel/preset-react", "@emotion/babel-preset-css-prop", [ "@babel/env", { "modules": false } ] ], "plugins": [ "@babel/plugin-proposal-class-properties" ] } ================================================ FILE: packages/component-lib/.eslintignore ================================================ *.css *.scss *.svg ================================================ FILE: packages/component-lib/.eslintrc.json ================================================ { "plugins": [ "react-hooks" ], "extends": [ "react-app" ], "overrides": [ { "files": [ "**/*.tsx", "**/*.ts" ], "rules": { "import/no-anonymous-default-export": "off", "@typescript-eslint/no-unused-vars": "off", "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则 "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖 } } ] } ================================================ FILE: packages/component-lib/.gitignore ================================================ # See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies node_modules # builds build dist .rpt2_cache # misc .DS_Store .env .env.local .env.development.local .env.test.local .env.production.local .idea/** .vscode/** ./component-lib/** ./lib/** npm-debug.log* yarn-debug.log* yarn-error.log* src/assets storybook-static ================================================ FILE: packages/component-lib/.storybook/main.ts ================================================ import type { StorybookConfig } from '@storybook/react-webpack5' import CopyWebpackPlugin from 'copy-webpack-plugin' import path, { join, dirname } from 'path' import MiniCssExtractPlugin from 'mini-css-extract-plugin' const nodePath = '../../' const toPath = (filePath) => path.join(process.cwd(), nodePath + filePath) const disableEsLint = (e) => { return ( e.module.rules .filter((e) => e.use && e.use.some((e) => e.options && void 0 !== e.options.useEslintrc)) .forEach((s) => { e.module.rules = e.module.rules.filter((e) => e !== s) }), e ) } function findBabelRules(config): any { let result_rule = {} config.module.rules.filter((rule) => { // console.log(rule); if (rule.oneOf) { result_rule = rule.oneOf.find((rule) => { return rule.test && rule.test.toString() === /\.(js|mjs|jsx|ts|tsx)$/.toString() }) } }) return result_rule as any } /** * This function is used to resolve the absolute path of a package. * It is needed in projects that use Yarn PnP or are set up within a monorepo. */ function getAbsolutePath(value: string): any { return dirname(require.resolve(join(value, 'package.json'))) } const config: StorybookConfig = { stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ getAbsolutePath('@storybook/addon-links'), getAbsolutePath('@storybook/addon-essentials'), getAbsolutePath('@storybook/preset-create-react-app'), getAbsolutePath('@storybook/addon-onboarding'), getAbsolutePath('@storybook/addon-interactions'), ], framework: { name: '@storybook/react-webpack5', options: {}, }, docs: { autodocs: 'tag', }, webpackFinal: async (config, { configType }) => { config = disableEsLint(config) // @ts-ignore const isProd = configType.toLowerCase() === 'production' const rule = findBabelRules(config) const modules = [ // @ts-ignore ...config?.resolve?.modules, path.resolve(__dirname, '..', 'src'), 'node_modules/@loopring-web/common-resources', ] rule.include = [ ...rule.include, path.resolve(__dirname, '..', '..', 'common-resources', 'static-resources'), ] rule.options.presets = [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: 3, loose: true, bugfixes: true, modules: false, }, ], [ '@babel/preset-react', { useBuiltIns: true, }, ], ...rule.options.presets, ] console.log('rule.plugins:', rule.options.plugins) // @ts-ignore config.module.rules.push({ test: /\.s(a|c)ss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { emit: false, esModule: false, hmr: false, }, }, 'css-loader', ], }) config.plugins = [ // @ts-ignore ...config?.plugins, // new MiniCssExtractPlugin({ // filename: isProd ? '[name].[contenthash].css' : '[name].css', // chunkFilename: isProd ? '[id].[contenthash].css' : '[id].css', // ignoreOrder: true, // }), new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], }), new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '..', '..', 'common-resources', 'assets'), to: './static', toType: 'dir', }, ], }), ] return { ...config, // framework: getAbsolutePath('@storybook/react-webpack5'), plugins: [...config.plugins], resolve: { ...config.resolve, modules, extensions: [...config?.resolve?.extensions, '.ts', '.js'], fallback: { ...config?.resolve?.fallback, crypto: require.resolve('crypto-browserify'), 'crypto-js': require.resolve('crypto-js'), 'crypto-js/sha256': require.resolve('crypto-js/sha256'), stream: require.resolve('stream-browserify'), assert: require.resolve('assert'), http: require.resolve('stream-http'), https: require.resolve('https-browserify'), os: require.resolve('os-browserify'), url: require.resolve('url'), util: require.resolve('util'), buffer: require.resolve('buffer'), timers: require.resolve('timers-browserify'), 'process/browser': require.resolve('process/browser'), }, alias: { // @ts-ignore ...config.resolve.alias, '@emotion/core': toPath('node_modules/@emotion/react'), 'emotion-theming': toPath('node_modules/@emotion/react'), '@emotion/styled': toPath('node_modules/@emotion/styled'), '@material-ui/core/Menu': '@mui/material/Menu', '@material-ui/core': '@mui/material', '@material-ui/core/Popover': '@mui/material/Popover', }, }, } }, } export default config export const framework = '@storybook/react' // import CopyWebpackPlugin from 'copy-webpack-plugin' // import path from 'path' // const direct = '../../' // import { addBeforeLoader, getLoader, loaderByName } from '@craco/craco' // const toPath = (filePath) => path.join(process.cwd(), direct + filePath) // const disableEsLint = (e) => { // return ( // e.module.rules // .filter((e) => e.use && e.use.some((e) => e.options && void 0 !== e.options.useEslintrc)) // .forEach((s) => { // e.module.rules = e.module.rules.filter((e) => e !== s) // }), // e // ) // } // const config = { // stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], // addons: [ // '@storybook/addon-links', // // '@storybook/addon-essentials', // '@storybook/preset-create-react-app', // '@storybook/addon-onboarding', // '@storybook/addon-interactions', // ], // framework: { // name: '@storybook/react-webpack5', //getAbsolutePath('@storybook/react-webpack5'), // options: {}, // }, // docs: { // autodocs: 'tag', // }, // webpackFinal: async (config, { configType }) => { // config = disableEsLint(config) // var { isFound, match } = getLoader(config, loaderByName('babel-loader')) // if (isFound && match?.loader?.include) { // match.loader.include = [ // //@ts-ignore // ...match.loader.include, // // toPath('packages/component-lib'), // toPath('node_modules/@loopring-web/common-resources/static-resources'), // toPath('node_modules/@loopring-web/loopring-sdk'), // ] // console.log('match?.loader', match?.loader) // } // // const rule = findBabelRules(config) // // rule.include = [ // // ...rule.include, // // path.resolve(__dirname, '..', '..', 'common-resources', 'static-resources'), // // ] // // var { isFound, match } = getLoader(config, loaderByName('babel-loader')) // // if (isFound && match?.loader) { // // console.log(match.loader) // // match.loader.include = [ // // path.resolve(__dirname, packagesPath), // // ...[ // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@web3modal`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@walletconnect`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@metamask`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@scure`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@noble`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@ethereumjs`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/micro-ftch`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/react-spring`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@react-spring`, // // ), // // path.resolve( // // __dirname, // // `${process.env.NODE_ENV === 'development' ? direct : direct}`, // // `node_modules/@loopring-web/loopring-sdk`, // // ), // // ], // // ] // // } // addBeforeLoader(config, loaderByName('babel-loader'), { // loader: 'html-loader', // test: /\.html$/i, // exclude: [/node_modules/, /index.html/i], // options: { // attrs: [':data-src'], // minimize: true, // removeComments: false, // collapseWhitespace: false, // }, // }) // // addBeforeLoader(config, loaderByName('babel-loader'), { // // test: /\.md$/, // // use: 'raw-loader', // // }) // config.resolve.extensions.push('.html') // config.resolve.extensions.push('.md') // return { // ...config, // // alias: { // // '@material-ui/core/Menu': '@mui/material/Menu', // // '@material-ui/core': '@mui/material', // // '@material-ui/core/Popover': '@mui/material/Popover', // // process: 'process/browser', // // }, // plugins: [ // ...config.plugins, // new CopyWebpackPlugin({ // patterns: [ // { // from: toPath('packages/common-resources/assets'), // to: './static', // toType: 'dir', // }, // ], // }), // ], // resolve: { // ...config.resolve, // fallback: Object.assign(config.resolve.fallback ?? {}, { // crypto: require.resolve('crypto-browserify'), // 'crypto-js': require.resolve('crypto-js'), // 'crypto-js/sha256': require.resolve('crypto-js/sha256'), // stream: require.resolve('stream-browserify'), // assert: require.resolve('assert/'), // http: require.resolve('stream-http'), // https: require.resolve('https-browserify'), // os: require.resolve('os-browserify'), // url: require.resolve('url/'), // util: require.resolve('util'), // buffer: require.resolve('buffer'), // timers: require.resolve('timers-browserify'), // 'process/browser': require.resolve('process/browser'), // // "fs": require.resolve('browserify-fs'), // }), // alias: { // // @ts-ignore // ...config.resolve.alias, // '@emotion/core': toPath('node_modules/@emotion/react'), // 'emotion-theming': toPath('node_modules/@emotion/react'), // '@emotion/styled': toPath('node_modules/@emotion/styled'), // '@material-ui/core/Menu': '@mui/material/Menu', // '@material-ui/core': '@mui/material', // '@material-ui/core/Popover': '@mui/material/Popover', // }, // }, // } // }, // } // export default config // export const framework = '@storybook/react' ================================================ FILE: packages/component-lib/.storybook/preview.tsx ================================================ import type { Preview } from '@storybook/react' import { getTheme, globalCss, i18n } from '@loopring-web/common-resources' import StoryRouter from 'storybook-react-router/dist/react' import { ThemeProvider } from '@emotion/react' import { Provider } from 'react-redux' import { LocalizationProvider } from '@mui/lab' import { I18nextProvider } from 'react-i18next' import DateAdapter from '@mui/lab/AdapterMoment' import createStorybookListener from 'storybook-addon-redux-listener' import { GlobalStyles, ThemeProvider as MuThemeProvider } from '@mui/material' import { applyMiddleware, combineReducers, compose, createStore } from 'redux' import { modalsSlice, provider, ProviderComposer, setLanguage, setTheme, settingsSlice, } from '../src' export const parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, // controls: { // matchers: { // color: /(background|color)$/i, // date: /Date$/, // }, // }, backgrounds: { default: 'dark', values: [ { name: 'dark', value: '#14172C' }, { name: 'light', value: '#ffffff' }, ], }, } export const globalTypes = { locale: { name: 'Locale', description: 'Internationalization locale', defaultValue: 'zh_CN', toolbar: { icon: 'globe', items: [ { value: 'en_US', right: 'en_US', title: 'English' }, { value: 'zh_CN', right: 'zh_CN', title: '中文' }, ], }, }, } const preview: Preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, }, } const middlewares: any[] = [ // window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), //...getDefaultMiddleware({ thunk: true }) Button.stories.tsx ] const reducers = combineReducers({ settings: settingsSlice.reducer, modals: modalsSlice.reducer, }) if (process.env.NODE_ENV === 'storybook') { const reduxListener = createStorybookListener() middlewares.push(reduxListener) } // @ts-ignore const composeEnhancers = (global ?? window)?.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose const enhancer = composeEnhancers( applyMiddleware(...middlewares), // other stores enhancers if any ) const createStoreWithMiddleware = (reducers) => { return createStore(reducers, enhancer) } const configureStore = () => createStoreWithMiddleware(reducers) const store = configureStore() export const decorators = [ (Story, context) => { const { backgrounds, locale } = context.globals const themeMode = backgrounds && backgrounds.value === '#ffffff' ? 'light' : 'dark' store.dispatch(setLanguage(locale)) store.dispatch(setTheme(themeMode)) // @ts-ignore const theme = getTheme(store.getState().settings.themeMode) StoryRouter() return ( ) }, ] ================================================ FILE: packages/component-lib/.travis.yml ================================================ language: node_js node_js: - 8 env: - SKIP_PREFLIGHT_CHECK=true ================================================ FILE: packages/component-lib/README.md ================================================ # component-lib > Loopring UI component lib [![NPM](https://img.shields.io/npm/v/component-lib.svg)](https://www.npmjs.com/package/component-lib) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) ## Install ```bash npm install --save component-lib ``` ## Usage ```tsx import * as React from 'react' < ProviderComposer providers = { [ provider(LocalizationProvider, {dateAdapter: MomentUtils}), provider(I18nextProvider, {i18n}), provider(ThemeProvider), ] }> < Story {... context } /> < ProviderComposer providers = { [ provider(LocalizationProvider, {dateAdapter: MomentUtils}), provider(I18nextProvider, {i18n}), provider(ThemeProvider), ] }> < Story {... context } /> "@loopring-web/static-resource": "file:../static-resource/src", ``` ## License MIT © [Loopring dev Team](https://github.com/Loopring dev Team) --- This hook is created using [create-react-hook](https://github.com/hermanya/create-react-hook). ================================================ FILE: packages/component-lib/craco.config.cjs ================================================ module.exports = require("../../craco.config.cjs") ================================================ FILE: packages/component-lib/package.json ================================================ { "name": "@loopring-web/component-lib", "version": "1.0.0", "main": "src/index.ts", "private": true, "resolutions": { "**/@emotion/styled": "^11.1.5" }, "dependencies": { "@loopring-web/common-resources": "1.0.0", "@storybook/cli": "^7.6.4", "cross-env": "^7.0.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1" }, "scripts": { "rollup:build": "rollup --config --no-stdin", "release:version": "lerna version --exact --no-changelog --no-push --no-git-tag-version", "release:build": "lerna run --parallel --scope \"@loopring-web/*\" build", "test": "react-scripts-rewired test", "eject": "react-scripts eject", "build-storybook": "storybook build", "build": "NODE_ENV=production storybook build -c .storybook_back --quiet ", "storybook": "storybook dev -p 6006" }, "eslintConfig": { "extends": [ "react-app" ], "overrides": [ { "files": [ "**/*.stories.*" ], "rules": { "import/no-anonymous-default-export": "off" } }, { "files": [ "**/*.stories.*" ], "rules": { "import/no-anonymous-default-export": "off" } } ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "@emotion/react": "^11.1.5", "@emotion/server": "^11.0.0", "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-typescript": "^8.2.1", "@rollup/plugin-url": "^6.0.0", "@storybook/addon-actions": "^7.6.4", "@storybook/addon-essentials": "^7.6.4", "@storybook/addon-interactions": "^7.6.4", "@storybook/addon-links": "^7.6.4", "@storybook/addon-onboarding": "^1.0.10", "@storybook/blocks": "^7.6.4", "@storybook/builder-webpack5": "^7.6.4", "@storybook/cli": "^7.6.4", "@storybook/manager-webpack5": "^6.5.16", "@storybook/node-logger": "^7.6.4", "@storybook/preset-create-react-app": "^7.6.4", "@storybook/react": "^7.6.4", "@storybook/react-webpack5": "^7.6.4", "@storybook/test": "^7.6.4", "@storybook/testing-library": "^0.2.2", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@types/d3-format": "^2.0.0", "@types/d3-time-format": "^3.0.0", "@typescript-eslint/eslint-plugin": "^4.21.0", "arr-flatten": "^1.1.0", "babel-plugin-named-asset-import": "0.3.8", "babel-plugin-react-require": "^3.1.3", "eslint-plugin-storybook": "^0.6.15", "prop-types": "^15.8.1", "repeat-element": "^1.1.3", "rollup": "2.30", "rollup-plugin-font": "^1.1.1", "rollup-plugin-json": "^4.0.0", "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-svg": "^2.0.0", "rollup-plugin-typescript2": "^0.30.0", "snapdragon-node": "^3.0.0", "storybook": "^7.6.4", "storybook-addon-redux-listener": "^0.1.7", "storybook-react-router": "^1.0.8", "mini-css-extract-plugin": "^2.7.6" } } ================================================ FILE: packages/component-lib/rollup.config.js ================================================ import ts from 'rollup-plugin-typescript2' import typescript from 'typescript' import commonjs from '@rollup/plugin-commonjs' import resolve from '@rollup/plugin-node-resolve' import external from 'rollup-plugin-peer-deps-external' import svg from 'rollup-plugin-svg' import url from '@rollup/plugin-url' import json from 'rollup-plugin-json' // importfont from "rollup-plugin-font"; // import pkg from './package.json' export default { input: './src/DualListPanel.tsx', output: [ { file: './dist/bundle.cjs', //pkg.main, format: 'cjs', preferConst: true, interop: false, //exports: 'named', sourcemap: true, }, { file: './dist/bundle.js', //pkg.module, format: 'es', preferConst: true, //exports: 'named', sourcemap: true, }, ], plugins: [ external(), url({ exclude: ['**/*.svg'] }), url({ include: ['**/*.ttf', '**/*.eot', '**/*.woff', '**/*.woff2'], limit: Infinity, }), resolve(), ts({ typescript }), svg(), commonjs({ extensions: ['.js', '.ts'] }), json({ // 默认情况下将解析所有JSON文件, // 但您可以专门包含/排除文件 // exclude: [ 'node_modules/foo/**', 'node_modules/bar/**' ], // exclude: [ 'node_modules/foo/**', 'node_modules/bar/**' ], // 对于 tree-shaking, 属性将声明为 // 变量, 使用 `var` 或者 `const` preferConst: true, // 默认是 false // 为生成的默认导出指定缩进 — // 默认为 '\t' indent: ' ', // 忽略缩进并生成最小的代码 compact: true, // 默认是 false // 为JSON对象的每个属性生成一个命名导出 namedExports: true, // 默认是 true }), //font() ], } ================================================ FILE: packages/component-lib/src/components/basic-lib/Icon.stories.tsx ================================================ import { Meta, Story } from '@storybook/react' import { WithTranslation, withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { Grid, Typography } from '@mui/material' import { ActiveIcon, AddIcon, AlertIcon, AmmRankIcon, ApprovalIcon, AssetsIcon, AudioIcon, BackIcon, BanxaIcon, CalendarIcon, CardIcon, CheckBoxIcon, CheckedIcon, CheckIcon, CircleIcon, ClockIcon, CloseIcon, CloseRedPacketIcon, CompleteIcon, ContactIcon, ConvertToIcon, CopyIcon, DarkIcon, DeleteIcon, DepositIcon, DepthFIcon, DepthHIcon, DiscordIcon, DoneIcon, DownloadIcon, DragIcon, DragListIcon, DropDownIcon, EditIcon, EmptyIcon, ErrorIcon, ExchangeAIcon, ExchangeIcon, ExitIcon, FailedIcon, FavHollowIcon, FavSolidIcon, FirstPlaceIcon, GoodIcon, GoTopIcon, GrowIcon, HelpIcon, HideIcon, ImageIcon, IncomingIcon, Info2Icon, InfoIcon, KLineFeaturesIcon, L1l2Icon, L2HistoryIcon, L2l2Icon, L2MyLiquidityIcon, L2OrderIcon, LegacyIcon, LightIcon, LinkedIcon, LinkIcon, LoadingIcon, LockGuardianIcon, LockIcon, LoopringDarkFooterIcon, LoopringIcon, LoopringLightFooterIcon, LoopringLogoIcon, MediumIcon, MenuIcon, MintIcon, MoreIcon, NFTIcon, NoPhotosIcon, NotificationIcon, OrderListIcon, OutputIcon, PlayIcon, ProfileIcon, ProToLiteIcon, QRIcon, RampIcon, RecordIcon, RedPacketIcon, RefreshIcon, RefreshIPFSIcon, RefuseIcon, ResizeIcon, ReverseIcon, RewardIcon, RoundAddIcon, ScanQRIcon, SearchIcon, SecondPlaceIcon, SecurityIcon, SettingIcon, SpeakerIcon, StarHollowIcon, StarSolidIcon, SubmitIcon, SwapSettingIcon, SyncIcon, ThirdPlaceIcon, TransferIcon, TrophyIcon, TwitterIcon, UnConnectIcon, UpIcon, UploadedIcon, VideoIcon, ViewHistoryIcon, ViewIcon, ViewMoreIcon, VipIcon, WaitApproveIcon, WaitingIcon, WarningIcon, WarningIcon2, WithdrawIcon, YoutubeIcon, ZoomIcon, RiskAlertIcon, RiskIcon, AnotherIcon, LinkSharedIcon, ReadIcon, MessageIcon, GoIcon, ThreeDIcon, RoundCheckIcon, RoundCircleIcon, DiscordSvg, BorderTickIcon, MarginLevelIcon, LoadIcon, MarginIcon, VaultTradeIcon, CloseOutIcon, SwapExchangeIcon, Overview, AmmLogo, SatkingLogo, DualInvestmentLogo, DualUpIcon, DualDownIcon, DualConvertIcon, DualBTCIcon, DualChartDD, DualChartDH, DualChartHD, DualChartHH, VaultIcon, } from '@loopring-web/common-resources' const Styled = styled.div` background: var(--color-global-bg); svg { height: 24px !important; width: 24px !important; } ` // @ts-ignore const listIcon = [ , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] export const IconList: Story = withTranslation()(({}: WithTranslation & any) => { const view = listIcon.map((item, index) => { return ( {item} {item.type.name} ) }) return ( <> {/**/} {view} {/**/} ) }) as Story //export const Button = Template.bind({}); // @ts-ignore export default { title: 'Resource/IconsList', component: IconList, argTypes: {}, } as Meta // LButton.args = {} ================================================ FILE: packages/component-lib/src/components/basic-lib/btns/BtnPercentage.tsx ================================================ /* Rectangle 340 */ import styled from '@emotion/styled' import { BtnPercentageProps } from './Interface' import { Box, Slider } from '@mui/material' import { WithTranslation, withTranslation } from 'react-i18next' import React from 'react' import { Mark } from '@mui/base/SliderUnstyled/SliderUnstyledProps' import { myLog } from '@loopring-web/common-resources' const StyledSlider = styled(Slider)` && { border: 0; & .MuiSlider-mark { width: 24px; height: 24px; line-height: initial; box-sizing: border-box; display: flex; justify-content: center; align-items: center; cursor: pointer; background: var(--opacity); border: 0; transform: translate(-50%, -50%); z-index: 25; :after { content: ''; width: 8px; height: 8px; background: var(--color-box); ${({ theme }) => theme.border.defaultFrame({ d_W: 1, d_R: 2, c_key: 'var(--color-secondary)', })}; } } & .MuiSlider-markLabel { line-height: initial; } & .MuiSlider-rail { width: 100%; height: 4px; border-radius: 4px; background-color: var(--color-divide); z-index: 10; transform: translateY(-50%); } & .MuiSlider-track { width: 100%; height: 4px; border-radius: 4px; background-color: var(--color-secondary); z-index: 15; transform: translateY(-50%); } & .MuiSlider-thumb { z-index: 30; transform: translate(-50%, -50%); width: 18px; height: 18px; color: var(--color-button-pot); margin-top: 0; margin-left: 0; ${({ theme }) => theme.border.defaultFrame({ d_W: 2, d_R: 12, c_key: 'var(--color-secondary)', })}; box-shadow: initial; input { cursor: pointer; } .MuiSlider-valueLabel { background: var(--opacity); padding: 0; top: -4px; } } } ` as typeof Slider export const BtnPercentage = withTranslation('common')( ({ selected = -1, handleChanged, anchors, valueLabelDisplay = 'off', valuetext, step = 1, t, tReady, ...rest }: BtnPercentageProps & WithTranslation) => { const [value, setValue] = React.useState(selected) React.useEffect(() => { myLog('selected', selected) if (selected >= 0 && selected <= 100) { setValue(Math.floor(selected)) } else { setValue(0) } }, [selected]) const _anchors: Mark[] = anchors && anchors.length ? anchors : [ { value: 0, label: '0', }, { value: 25, label: '', }, { value: 50, label: '', }, { value: 75, label: '', }, { value: 100, label: t('labelMax:') + '100%', }, ] const _handleChanged = (_event: Event, value: number | number[], _activeThumb: number) => { setValue(value as number) handleChanged(value) } const _valuetext = (value: number): string | number => { if (valuetext) { return valuetext(value) } else { return value } } // function valuetext(value: number) { // return `${value}°C`; // } return ( { _handleChanged(_event, value, _activeThumb) }} step={step} marks={_anchors} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/basic-lib/btns/Button.stories.tsx ================================================ import React from 'react' import { Meta, Story } from '@storybook/react' import { Box, Breadcrumbs, Grid, Link, Pagination, Switch, Typography } from '@mui/material' import { ButtonProps, TGItemData, TGItemJSXInterface } from './Interface' import { BtnPercentage, Button, LinkActionStyle, ModalCloseButton, ToggleButtonGroup, } from './index' import { WithTranslation, withTranslation } from 'react-i18next' import styled from '@emotion/styled' const Styled = styled.div` background: var(--color-global-bg); ` const toggleData: TGItemData[] = [ { value: '15M', key: '15m' }, { value: '1H', key: '1h' }, { value: '4HHH', key: '4HHH', disabled: true }, { value: '1D', key: '1d' }, ] const ToggleButtonDefault = withTranslation()(({ ...rest }: any) => { const [value, setValue] = React.useState('4H') const [values, setValues] = React.useState(['4H', '1H']) const [cValue, setCValues] = React.useState('4H') const tgItemJSXs: TGItemJSXInterface[] = toggleData.map(({ value, label, key, disabled }) => { return { value, tlabel: rest.t(label), disabled, JSX: <>C {rest.t(key)}, } }) const handleChange = (_e: React.MouseEvent, value: any) => { setCValues(value) } return ( {' '} {' '} {' '} {' '} ) }) const PaginationControlled = () => { const [page, setPage] = React.useState(1) const handleChange = (_event: React.ChangeEvent, value: number) => { setPage(value) } return ( <> Page: {page} ) } export const LButton: Story = withTranslation()( ({ t, ...rest }: WithTranslation & any) => { const [switched, setSwitched] = React.useState(false) /*********Toggle Button**********/ const [selected, setSelected] = React.useState(-1) /*********Toggle **********/ return ( <>

    Button

    {' '} {' '} {' '} {' '} {' '} {' '} xxxxxxxxxxx Test Link

    Tab Button

    setSwitched(e.target.checked)} /> setSwitched(!e.target.checked)} /> setSwitched(e.target.checked)} />

    Pagination

    Percentage selector

    { console.log(value) setSelected(value) }} />
    Material-UI Core Breadcrumb

    Font

    Font size h1 48 Font size h2 36 Font size h3 24 Font size h4 20 Font size h5 16 Font size h6 12 Font size subtitle1 Font size body1 Font size body2 {/**/} {/* /!* *!/*/} {/* /!**!/*/} {/* */} {/* */} {/* */} {/* */} {/**/} {/**/} {/* /!* *!/*/} {/* */} {/* */} {/**/} {/**/} ) }, ) as Story //export const Button = Template.bind({}); export default { title: 'basic-lib/Buttons', component: LButton, argTypes: {}, } as Meta // LButton.args = {} ================================================ FILE: packages/component-lib/src/components/basic-lib/btns/Button.tsx ================================================ import { Box, Button as MuButton, IconButton, Link, ToggleButton, ToggleButtonGroup as MuToggleButtonGroup, useScrollTrigger, Zoom, Tabs as MuTabs, } from '@mui/material' import { ButtonProps, TGItemJSXInterface, ToggleButtonGroupProps } from './Interface' import { TFunction, withTranslation, WithTranslation } from 'react-i18next' import styled from '@emotion/styled' import { BackIcon, CloseIcon, QRIcon, SoursURL } from '@loopring-web/common-resources' import React from 'react' import { SxProps } from '@mui/system' import { Theme } from '@mui/material/styles' const loadingSvg = SoursURL + 'svg/loading.svg' export const Button = styled(MuButton)` && { line-height: 1em; &:hover { cursor: pointer; } &.MuiButton-outlined { background-color: transparent; :hover { border-color: var(--color-primary); } } &.MuiButton-root.Mui-disabled { color: var(--color-text-button-disabled); background-color: var(--color-button-disabled); ${({ loading, theme, loadingbg }) => { return loading === 'true' ? ` color:transparent; background-color:${theme.colorBase.primary}; background-color:${loadingbg}; &::after{ display: block; content: url(${loadingSvg}); height: 40px; width: 40px; position: absolute; transform:scale(.55); display:flex; flex-direction:row; align-items: center; justify-content: center; color:#fff } ` : '' }} } &.disabledViewOnly { pointer-events: inherit; } } //&.disabled{ // //} ` as (props: ButtonProps) => JSX.Element export function ScrollTop({ // anchorTopRef, ...props }: { children: React.ReactElement // anchorTopRef?: React.Ref; }) { const { children } = props const trigger = useScrollTrigger({ target: window ? window : undefined, disableHysteresis: true, threshold: 100, }) const scrollToTop = React.useCallback((event: React.MouseEvent) => { const anchor = (event.currentTarget as HTMLDivElement).parentElement || //.ownerDocument || document document.querySelector('#back-to-top-anchor') if (anchor) { window.scrollTo(0, anchor?.offsetTop) } }, []) return ( {children} ) } export const MuToggleButtonGroupStyle = styled(MuToggleButtonGroup)` ${({ theme, size }) => size !== 'small' ? ` background: var(--color-box); padding: ${theme.unit / 2}px; padding-right: ${theme.unit / 4}px; box-shadow: var(--shadow3); ` : ``}; .MuiToggleButton-sizeSmall { background: var(--color-box); height: 2.4rem; font-size: 1.2rem; margin-right: ${({ theme }) => theme.unit}px; border: ${({ theme }) => theme.border.borderConfig({ c_key: 'var(--color-border)' })}; color: var(--color-text-secondary); &:not(:first-of-type), &:not(:last-child) { border-color: var(--color-border); } &:hover { //backgroundColor: var(--color-box); // color: var(--color-primary); color: var(--color-text-button-select); border: ${({ theme }) => theme.border.borderConfig({ c_key: 'var(--color-border-hover)' })}; background: var(--color-box); // &:not(:last-child), &:not(:first-of-type) { border: ${({ theme }) => theme.border.borderConfig({ c_key: 'var(--color-primary)' })}; // } &.Mui-selected, &.Mui-selected { //background: var(--opacity); background: var(--color-box); // color: var(--color-primary); color: var(--color-text-button-select); border: ${({ theme }) => theme.border.borderConfig({ c_key: 'var(--color-primary)' })}; /* border: ${({ theme }) => theme.border.borderConfig({ c_key: 'var(--color-border-hover)' })}; */ } } &.Mui-disabled { //background ; background: var(--opacity); color: var(--color-disable); border: 1px dashed var(--color-border); } &.Mui-selected, &.Mui-selected + &.Mui-selected { // color: var(--color-primary); color: var(--color-text-button-select) !important; background: var(--color-box) !important; //background: var(--color-disable); border: ${({ theme }) => theme.border.borderConfig({ c_key: 'var(--color-primary)' })}; /* border: ${({ theme }) => theme.border.borderConfig({ c_key: 'var(--color-border-hover)' })}; */ } } ` as typeof MuToggleButtonGroup export const ToggleButtonGroup = withTranslation('common')( ({ t, value, // handleChange, size = 'medium', tgItemJSXs, data, exclusive, onChange, }: { t: TFunction } & ToggleButtonGroupProps) => { const _handleChange = React.useCallback( (_e: React.MouseEvent, value: any) => { // setValue(value) if (onChange) { onChange(_e, value) } }, [onChange], ) if (data) { tgItemJSXs = data.map(({ value, key, disabled }) => { return { value, JSX: t(key), tlabel: t(key), disabled } }) } return ( {tgItemJSXs?.map(({ value, JSX, tlabel, disabled, key, notWrap }: TGItemJSXInterface) => notWrap ? ( {JSX} ) : ( {JSX} ), )} ) }, ) export const ModalCloseButton = ({ onClose, className = '', closeIcon = , t, }: { className?: string closeIcon?: JSX.Element onClose?: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] } & { t: TFunction }) => { return ( { // myLog("IconButton escapeKeyDown"); onClose && onClose(event, 'escapeKeyDown') }} > {closeIcon} ) } export const ModalCloseButtonPosition = ({ onClose, className = '', closeIcon = , t, right, top, }: { className?: string closeIcon?: JSX.Element onClose?: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] right?: number top?: number } & { t: TFunction }) => { return ( { onClose && onClose(event, 'escapeKeyDown') }} > {closeIcon} ) } export const ModalBackButton = ({ onBack, t, marginTop = '-24px', marginLeft = 1.5, sx, }: { onBack?: () => void marginTop?: number | string marginLeft?: number | string sx?: SxProps } & Partial) => { return ( { onBack && onBack() }} > ) } const QRStyle = styled(Box)` .MuiButtonBase-root { position: relative; //z-index: 10; } &:after { pointer-events: none; content: ''; position: absolute; display: block; height: 48px; width: 48px; top: -2px; left: -2px; //z-index: -1; background-image: ${({ theme }) => { if (theme.mode === 'dark') { return `url('${SoursURL}images/qr_code_dark.png')` } else { return `url('${SoursURL}images/qr_code_light.png')` } }}; } ` as typeof Box export const QRButtonStyle = ({ onQRClick, t, }: { onQRClick?: () => void } & WithTranslation) => { return ( { onQRClick && onQRClick() }} > ) } export const LinkActionStyle = styled(Link)` text-decoration: underline dotted; color: inherit; ` as typeof Link export const Tabs = styled(MuTabs)` &.btnTab { .MuiTab-root { color: var(--color-text-primary); margin-right: ${({ theme }) => theme.unit}px; padding: ${({ theme }) => theme.unit}px ${({ theme }) => theme.unit * 1.5}px; min-height: auto; line-height: 24px; &.Mui-selected { color: var(--color-text-button); border-radius: ${({ theme }) => theme.unit * 0.5}px; background: var(--color-primary); &:focus-visible::after, &:after { display: none; } } } } &.btnOutLineTab { min-height: 28px; height: 28px; .MuiTabs-fixed { .MuiTab-root { margin: 0 ${({ theme }) => theme.unit}px; &:first-child { margin-left: 0; } &:last-child { margin-right: 0; } } } .MuiTab-root { min-height: 28px; height: 28px; padding: 0; color: var(--color-text-secondary); border: var(--color-text-secondary) 1px solid; border-radius: ${({ theme }) => theme.unit * 0.5}px; margin: 0; &.Mui-selected { color: var(--color-primary); border: var(--color-primary) 1px solid; &:focus-visible::after, &:after { display: none; } } } } ` ================================================ FILE: packages/component-lib/src/components/basic-lib/btns/Interface.ts ================================================ import { ButtonProps as MuButtonPros, SliderProps, ToggleButtonGroupProps as MuToggleButtonGroupProps, } from '@mui/material' import { XOR } from '../../../types/lib' import { Mark } from '@mui/base/SliderUnstyled/SliderUnstyledProps' export type ButtonProps = MuButtonPros & { // bg:'', // color:'', // hBg:'', // hColor:'', loading?: 'true' | 'false' loadingbg?: string } export type BtnInfo = { label: string params: { [key: string]: string } } export type BtnInfoProps = { btnInfo?: BtnInfo } export interface BtnPercentageProps extends SliderProps { anchors?: Mark[] //0 --100 default 0,20,40,60,80,100 selected: number valueLabelDisplay?: 'on' | 'auto' | 'off' valuetext?: (value: any) => string handleChanged: (item: any) => void step?: number } export interface BtnPercentageDraggableProps extends BtnPercentageProps { maxValue: string | number } export interface TGItemJSXInterface { value: any key?: string notWrap?: boolean JSX: React.ReactElement tlabel?: string // after 18n disabled?: boolean } export interface TGItemData { value: string | number key: string label?: string disabled?: boolean } export type ToggleButtonGroupProps = MuToggleButtonGroupProps & { value: Array | string | number } & XOR<{ tgItemJSXs: TGItemJSXInterface[] }, { data: TGItemData[] }> // & { handleChange: (event: MouseEvent|InputEvent, newValue: string) => void } ================================================ FILE: packages/component-lib/src/components/basic-lib/btns/index.ts ================================================ export * from './Button' export * from './Interface' export * from './BtnPercentage' ================================================ FILE: packages/component-lib/src/components/basic-lib/checkbox/index.tsx ================================================ import { useTheme } from '@emotion/react' import { CheckIcon } from '@loopring-web/common-resources' import CheckCircleRoundedIcon from '@mui/icons-material/RadioButtonChecked' import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked' import { Box, BoxProps, SvgIconProps } from '@mui/material' type CustomCheckBoxProps = { CheckedIcon?: React.ComponentType UncheckIcon?: React.ComponentType checked: boolean onCheck: () => void } export const CustomCheckBox = (props: CustomCheckBoxProps & BoxProps) => { const theme = useTheme() const { CheckedIcon = CheckCircleRoundedIcon, UncheckIcon = RadioButtonUncheckedIcon, checked, onCheck, ...rest } = props return ( {checked ? ( ) : ( )} ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/color.stories.tsx ================================================ import { Meta, Story } from '@storybook/react' import { WithTranslation, withTranslation } from 'react-i18next' import { Box, Grid, Typography } from '@mui/material' import { ColorDarkDefault, ColorLightDefault } from '@loopring-web/common-resources' import styled from '@emotion/styled' import React from 'react' const Styled = styled.div` background: var(--color-global-bg); svg { height: 24px; width: 24px; } ` export const ColorList: Story = withTranslation()(({}: WithTranslation & any) => { const dark = Object.keys(ColorDarkDefault).map((key, indx) => { if (key !== 'background' && key !== 'border') { return ( {key} ) } else { return } }) const light = Object.keys(ColorLightDefault).map((key, indx) => { if (key !== 'background' && key !== 'border') { return ( {key} ) } else { return } }) return ( <> {/**/} {/**/} {dark} {light} {/**/} ) }) as Story //export const Button = Template.bind({}); export default { title: 'Resource/ColorsList', component: ColorList, argTypes: {}, } as Meta // LButton.args = {} ================================================ FILE: packages/component-lib/src/components/basic-lib/display/SpaceBetweenBox.tsx ================================================ import { Box, BoxProps } from '@mui/material' export type SpaceBetweenProps = { leftNode: React.ReactNode, rightNode: React.ReactNode } export default (props: BoxProps & SpaceBetweenProps) => { const { leftNode, rightNode, ...rest } = props return {leftNode} {rightNode} } ================================================ FILE: packages/component-lib/src/components/basic-lib/display/index.ts ================================================ import SpaceBetweenBox from "./SpaceBetweenBox"; export { SpaceBetweenBox } ================================================ FILE: packages/component-lib/src/components/basic-lib/empty/Empty.tsx ================================================ import { withTranslation, WithTranslation } from 'react-i18next' import styled from '@emotion/styled' import { EmptyIcon } from '@loopring-web/common-resources' import { Box, BoxProps, Typography } from '@mui/material' export type EmptyProps = { height?: number | string emptyPic?: string | JSX.Element //PATH or element message: () => JSX.Element } const EmptyIconStyle = styled(EmptyIcon)` && { height: var(--empty-size); width: var(--empty-size); } opacity: 0.3; font-size: ${({ theme }) => theme.fontDefault.h1}; color: var(--color-text-disable); ` as typeof EmptyIcon const WrapStyled = styled(Box)<{ height: number | undefined | string }>` display: flex; flex-direction: column; flex-flow: column wrap; flex-wrap: nowrap; justify-content: center; align-items: center; height: ${(props) => props.height ? typeof props.height == 'number' ? props.height + 'px' : props.height : `${350 - 35}px`}; ` as typeof Box export const EmptyDefault = withTranslation(['layout', 'common'])( ({ t, i18n, tReady, emptyPic = , height, message, ...rest }: EmptyProps & BoxProps & WithTranslation) => { const renderPic = !emptyPic || typeof emptyPic === 'string' ? ( {t('Empty')} ) : ( emptyPic ) return ( {renderPic} {message()} ) }, ) as (props: EmptyProps & BoxProps) => JSX.Element export const ComingSoonPanel = withTranslation(['common', 'layout'])(({ t }: WithTranslation) => { return ( {t('labelComingSoon')} ) }) ================================================ FILE: packages/component-lib/src/components/basic-lib/empty/index.ts ================================================ import { EmptyDefault } from './Empty' export { EmptyDefault } export * from './Empty' // export default EmptyWallet; ================================================ FILE: packages/component-lib/src/components/basic-lib/form/Form.stories.tsx ================================================ import React from 'react' import { Meta, Story } from '@storybook/react' import styled from '@emotion/styled' import { Box, Checkbox, FormControl, FormControlLabel as MuiFormControlLabel, Grid, Link, ListItemText, SelectChangeEvent, Typography, } from '@mui/material' import { InputCoinProps, MenuItem, OutlineSelect, OutlineSelectItem } from '../../basic-lib' import { Trans, withTranslation } from 'react-i18next' import { DatePicker, DateRangePicker, InputButton, InputButtonProps, InputSearch, InputSelect, InputSelectProps, TextField, } from './input' import { CheckBoxIcon, CheckedIcon, CloseIcon, CoinInfo, CoinKey, DropDownIcon, i18n, IBData, LanguageType, } from '@loopring-web/common-resources' import { DateRange } from '@mui/lab' import { EmptyDefault } from '../empty' import { coinMap, CoinType, inputProps, walletMap } from '../../../static' import { CoinMenu } from '../lists' import { InputCoin } from './input' import { IconClearStyled } from '../../tradePanel' const Style = styled.div` background: var(--color-global-bg); ` export default { title: 'basic-lib/Form', component: TextField, argTypes: {}, } as Meta const InputButtonWrap = () => { let information = { value: "hello, it's callback inject" } setTimeout(() => { information.value = 'I have update' }, 100) const ref = React.createRef() const handleError = ({ belong, balance, tradeValue }: IBData) => { if (typeof tradeValue !== 'undefined' && balance < tradeValue) { return { error: true, message: `Not enough ${belong} perform a deposit` } } return { error: false } } const [data, setData] = React.useState>({ belong: 'ETH', balance: 23244, tradeValue: 0, }) const handleCountChange = React.useCallback((ibData: IBData) => { setData(ibData) }, []) const handleOnClick = React.useCallback( (_event) => { console.log(information) }, [information], ) let _inputProps: InputButtonProps, CoinType, CoinInfo> = { isShowCoinIcon: true, isShowCoinInfo: true, handleOnClick, ...inputProps, } return ( <> , CoinType, CoinInfo> {..._inputProps} /> , CoinType, CoinInfo> {...{ ..._inputProps, ...{ maxAllow: true, inputData: data, handleError, handleCountChange, ref, }, }} /> , CoinType, CoinInfo> {...{ ..._inputProps, ...{ inputData: data } }} /> ) } export const BtnLanguage = ({ handleChange }: any) => { const _handleChange = React.useCallback( (event: SelectChangeEvent) => { if (handleChange) { handleChange(event.target.value) } }, [handleChange], ) return ( EN 中文 ) } const InputIconWrap = () => { const ref = React.createRef() const handleError = ({ belong, balance, tradeValue }: IBData) => { if (typeof tradeValue !== 'undefined' && balance < tradeValue) { return { error: true, message: `Not enough ${belong} perform a deposit` } } return { error: false } } const [data, setData] = React.useState>({ belong: 'ETH', balance: 23244, tradeValue: 0, }) const handleCountChange = React.useCallback((ibData: IBData) => { setData(ibData) }, []) let _inputProps: InputCoinProps, CoinType, CoinInfo> = { handleCountChange, ...inputProps, isShowCoinIcon: true, isShowCoinInfo: true, } as any return ( <> , CoinType, CoinInfo> {...{ ..._inputProps, ...{ maxAllow: true, inputData: data, handleError, ref, }, }} /> , CoinType, CoinInfo> {...{ ..._inputProps, ...{ order: 'right', maxAllow: true, inputData: data, handleError, ref, }, }} /> ) } const SimpleSelect = ({ t }: any) => { const datas = [ { label: 'Text1', value: '1' }, { label: 'Text2', value: '2' }, { label: 'Text3', value: '3' }, { label: 'Text4', value: '4' }, { label: 'Text5', value: '5' }, { label: 'Text6', value: '6' }, { label: 'Text7', value: '7' }, { label: 'Text8', value: '8' }, { label: 'Text4TextTextTextTextText', value: '9' }, { label: 'Text3', value: '10' }, { label: 'Text4TextTextTextTextText', value: '11' }, { label: 'Text4TextTextTextTextText', value: '12' }, ] const [value, setValue] = React.useState('1') return ( <> ) => { setValue(event.target.value as string) }} inputProps={{ IconComponent: DropDownIcon }} > {datas.map(({ label, value }) => ( {t(label)} ))} ) } const InputSelectWrap = (rest: any) => { const ref = React.useRef(null) const selected: CoinKey = 'TEST3' const backElement = React.useMemo( () => ( <> {}} > Cancel ), [], ) const inputSelectProps: InputSelectProps = { placeholder: 'Search Coin', focusOnInput: true, allowScroll: true, selected: '', panelRender: () => <>, backElement, handleContentChange: (value) => { console.log('FilterString', value) //setFilterString(value); }, } const handleListItemClick = (value: any) => { console.log('handleListItemClick', value) } const filterBy = (coinInfo: CoinInfo, filterString: string) => { return filterString && filterString.length ? RegExp(filterString, 'i').test(coinInfo.simpleName) : true } const PanelRender = ({ selected, value }: any) => { return ( > {...{ coinMap, height: '410px', filterBy, filterString: value, walletMap, selected, ...rest, }} ref={ref} /> ) { /*> style={{ width: '100px', height:'100px' }}*/ } { /* width={330} height={100} {...{coinMap, walletMap, ...rest}}>*/ } // } const PanelEmptyRender = () => { return ( { return Content is Empty }} /> ) } return ( <> ) } const MyDatePicker = (props: any) => { const [value, setValue] = React.useState(new Date()) const [svalue, setSValue] = React.useState>([new Date(), new Date()]) return ( <> setValue(newValue)} /> setSValue(newValue)} startText='Start' endText='End' /> ) } // const SearchWrap = () => { // const inputProps: OutlinedInputProps = { // placeholder: 'Search Coin', // value: '', // onChange: (value: any) => { // console.log('FilterString', value); // //setFilterString(value); // }, // } // return // // } // /> // } const Template: Story = withTranslation()((props: any) => { const [value, setValue] = React.useState('') const [searchValue, setSearchValue] = React.useState('') const handleSearchChange = React.useCallback((value) => { setSearchValue(value) }, []) const handleClear = React.useCallback(() => { // @ts-ignore // addressInput?.current?.value = ""; setSearchValue('') }, []) // const handleClear = React.useCallback(() => { // // @ts-ignore // // addressInput?.current?.value = ""; // setAddress('') // }, []) return ( ) }) as Story // @ts-ignore export const FormItem = Template.bind({}) FormItem.args = {} ================================================ FILE: packages/component-lib/src/components/basic-lib/form/hooks/index.ts ================================================ export * from './useFocusRef' export * from './usePanelRef' ================================================ FILE: packages/component-lib/src/components/basic-lib/form/hooks/useFocusRef.ts ================================================ import React from 'react' export function useFocusRef({ value, shouldFocusOn, callback, }: { value?: any shouldFocusOn?: boolean | undefined // ref: React.RefObject, callback?: (prorps: { current: any }) => void }) { const ref = React.useRef(null) React.useEffect(() => { if (shouldFocusOn) { ref?.current?.focus() } callback && callback({ current: ref.current }) }, [value, shouldFocusOn]) return ref } ================================================ FILE: packages/component-lib/src/components/basic-lib/form/hooks/usePanelRef.ts ================================================ import React from 'react' export function usePanelRef({ callback, }: { callback?: (prorps: { current: any }) => void }) { const ref = React.useRef(null) React.useEffect(() => { if (ref.current) { callback && callback({ current: ref.current }) } }, []) return ref } // { // const ref = React.useRef(null); // React.useEffect(() => { // // callback && callback({current: ref.current}); // return ref // }, [height, width, callback]); // return ref; // } ================================================ FILE: packages/component-lib/src/components/basic-lib/form/index.ts ================================================ export * from './input' ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/CollectionInput.tsx ================================================ import { Avatar, Box, BoxProps, Divider, Link, Modal, Tooltip, Typography } from '@mui/material' import { CollectionMeta, CopyIcon, copyToClipBoard, getShortAddr, ImageIcon, Info2Icon, LoadingIcon, MakeMeta, myLog, TOAST_TIME, RouterPath, NFTSubRouter, htmlDecode, } from '@loopring-web/common-resources' import { Button, CollectionCardList, CollectionListProps, DropdownIconStyled, MenuItem, SwitchPanelStyled, // TextField, Toast, ToastType, } from '../../../index' import React from 'react' import { Trans, useTranslation } from 'react-i18next' import styled from '@emotion/styled' import { useHistory } from 'react-router-dom' import { TextField } from '../../../basic-lib' const SizeCss = { small: ` height: 2.4rem; lineHeight: 2.4rem; `, large: ` height: 4.8rem; lineHeight: 4.8rem; `, medium: ` height: 3.2rem; lineHeight: 3.2rem; `, } const BoxStyle = styled(Box)` padding: 0.3rem 0.3rem 0.3rem 0.8rem; ${({ theme }) => theme.border.defaultFrame({ c_key: 'var(--color-border)', d_R: 0.5 })}; &:hover, &:active { color: var(--color-text-primary); ${({ theme }) => theme.border.defaultFrame({ c_key: 'var(--color-border-hover)', d_R: 0.5, })}; } .selected { color: var(--color-text-button-select); background: var(--color-box); ${({ theme }) => theme.border.defaultFrame({ c_key: 'var(--color-border-select)', d_R: 0.5, })}; } ${({ size }) => SizeCss[size]} ` as (props: BoxProps & { size: 'small' | 'large' | 'medium' }) => JSX.Element export type CollectionInputProps = { collection?: Co collectionListProps: CollectionListProps onSelected: (item: Co) => void domain: string makeMeta: MakeMeta } export const CollectionInput = ({ collection, collectionListProps, fullWidth = false, width = 'content-fit', showCopy = false, onSelected, domain, size = 'large', // makeMeta, isRequired = false, }: CollectionInputProps & { showCopy?: boolean fullWidth?: boolean width?: any size?: 'small' | 'large' | 'medium' domain: string makeMeta: MakeMeta isRequired?: boolean }) => { const [_modalState, setModalState] = React.useState(false) // const [selectCollectionMeta, setSelectCollectionMeta] = React.useState(collection); const { t } = useTranslation('common') const [dropdownStatus, setDropdownStatus] = React.useState<'up' | 'down'>('down') const { onPageChange, collectionList, isLoading: isLoadingCollectionList } = collectionListProps const noCollectionAndSelected = collectionList.length === 0 && collection === undefined myLog('collectionList', collectionListProps.collectionList) const history = useHistory() const pushRoute = React.useCallback((route: string) => { history.push(route) }, []) return ( This is the collection where your NFT will appear.NFT minted under collection will be bound with different contract address than previous created one. If you have incomplete work to finish and would like them created under previous contract address, you can still use the legacy created method under legacy-nft.loopring.io } placement={'top-start'} > Choose Collection {'\uFE61'} {dropdownStatus === 'up' && noCollectionAndSelected ? ( ( ), }} sx={{ width: '100%', maxWidth: 'none!important' }} onClick={(_e: any) => { _e.stopPropagation() setDropdownStatus((prev) => (prev === 'up' ? 'down' : 'up')) }} > { pushRoute(`${RouterPath.nft}/${NFTSubRouter.addCollection}`) }} > {t('labelNFTCreateCollection')} ) : ( { if (isLoadingCollectionList) return if (noCollectionAndSelected) { _e.stopPropagation() setDropdownStatus((prev) => (prev === 'up' ? 'down' : 'up')) } else { setModalState(true) onPageChange(1, { isMintable: true }) } }} style={{ cursor: 'pointer', whiteSpace: 'nowrap' }} position={'relative'} > {collection ? ( <> {( collection?.cached?.tileUri ?? collectionListProps.getIPFSString( collection?.tileUri ?? '', collectionListProps.baseURL, ) ).startsWith('http') ? ( ) : ( )} {htmlDecode(collection.name)} {size === 'large' ? collection.contractAddress : ' ' + getShortAddr(collection.contractAddress ?? '', true)} ) : ( <> )} {isLoadingCollectionList ? ( ) : ( )} )} {collection && showCopy && ( collection_metadata: { e.stopPropagation() if (collection) { // @ts-ignore copyToClipBoard(`${domain}/${(collection as any).collectionAddress}`) collectionListProps.setCopyToastOpen({ isShow: true, type: 'url', }) } }} > {domain}/{getShortAddr((collection as any).collectionAddress)} )} { // setDropdownStatus((prev) => (prev === "up" ? "down" : "up")); setModalState(false) }} > {t('labelChooseCollection')} { onSelected(item as Co) // setSelectCollectionMeta(item as any); setDropdownStatus((prev) => (prev === 'up' ? 'down' : 'up')) setModalState(false) }} /> { collectionListProps.setCopyToastOpen({ isShow: false, type: '' }) }} severity={ToastType.success} /> ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/DatePicker.tsx ================================================ import { Box, experimentalStyled, IconButton, InputAdornment, TextFieldProps } from '@mui/material' import styled from '@emotion/styled' import { DatePicker as MuDatePicker, DatePickerProps as MuDatePickerProps, DateRangePicker as MuDateRangePicker, DateRangePickerProps as MuDateRangePickerProps, DateTimePicker as MuDateTimePicker, DateTimePickerProps as MuDateTimePickerProps, } from '@mui/lab' import { TFunction } from 'i18next' import { CalendarIcon, YEAR_DAY_FORMAT, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { TextField } from './style' export const DateTextField = styled(TextField)` && .date-range-adornment { .MuiIconButton-edgeEnd { height: var(--btn-icon-size); width: var(--btn-icon-size); } } ` // const DateRangeDelimiterStyled = styled(DateRangeDelimiter)` // margin: 0 ${({theme}) => theme.unit}px !important; // ` export type DateRangePickerProps = {} & Omit, 'renderInput'> export const DateRangePicker = experimentalStyled( ({ ...props }: DateRangePickerProps & { t: TFunction }) => { return ( } OpenPickerButtonProps={props.OpenPickerButtonProps} renderInput={(startProps: any, endProps: any) => { startProps.InputProps = { ...startProps.InputProps, endAdornment: ( ), } endProps.InputProps = { ...endProps.InputProps, endAdornment: ( ), } return ( <> - {/*-*/} ) }} /> ) }, )` ` as React.ComponentType export type DatePickerProps = Omit & { textFiledProps?: TextFieldProps } export const DatePicker = styled( ({ t, inputFormat, value, ...props }: DatePickerProps & { t?: TFunction }) => ( { return ( ) }} /> ), )`` as React.ComponentType export type DateTimePickerProps = Omit export const DateTimePicker = ({ t, inputFormat, value, fullWidth = false, textFiledProps, ...props }: DateTimePickerProps & { t?: TFunction fullWidth?: boolean textFiledProps?: Partial }) => ( { return ( ) }} /> ) // (({ theme }) => ({ // position: 'relative', // '& .MuiIconButton-edgeEnd': { // position: 'absolute', // left: 0, // right: 0, // padding: 0, // height: '100%', // display: 'flex', // justifyContent: 'center', // color: theme.palette.primary.light // } // })) ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/Default.tsx ================================================ import styled from '@emotion/styled' import React from 'react' import { Avatar, Box, FormControlLabel as MuFormControlLabel } from '@mui/material' import { AvatarCoinProps, AvatarCoinStyled, LPTokenType, MarketType, SoursURL, TokenType, } from '@loopring-web/common-resources' import { useSettings } from '../../../../stores' import { VaultTag } from '../../tags' export const FormControlLabel = styled(MuFormControlLabel)` && { padding-right: ${({ theme }) => theme.unit * 2}px; background-color: inherit; border-radius: ${({ theme }) => theme.unit / 2}px; color: var(--color-text-secondary); } ` export const AvatarCoin = (props: AvatarCoinProps) => { const size = props.size ?? 36 return ( ) } export const CoinIcon = ({ symbol, lpSize = 24, size: _size, type, tokenImageKey, }: { symbol: R lpSize?: number size?: number | 'middle' | 'small' | 'large' type?: TokenType tokenImageKey?: R }) => { const { coinJson } = useSettings() const size = React.useMemo(() => { if (!_size) { return 24 } else if (typeof _size === 'string') { switch (_size) { case 'middle': return 24 break case 'small': return 20 break case 'large': return 36 break } } else { return _size } }, [_size]) if (symbol && symbol.match(/LP-(\w+)-(\w+)/i) && coinJson) { // @ts-ignore const [, coinA, coinB] = symbol.match(/LP-(\w+)-(\w+)/i) const coinAIcon: any = coinJson[coinA] const coinBIcon: any = coinJson[coinB] return ( {coinAIcon ? ( ' } /> ) : ( )} {coinBIcon ? ( ' } /> ) : ( )} ) } else { if (type && type == TokenType.vault) { const coinIcon: any = coinJson[tokenImageKey ?? symbol] return ( {coinIcon ? ( ' } /> ) : ( )} ) } else { const coinIcon: any = coinJson[symbol] return ( <> {coinIcon ? ( ' } /> ) : ( )} ) } // {[TokenType.vault].includes(type) && ( // // // // )} } } ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/InputButton.tsx ================================================ import { FormHelperText, Grid, Typography } from '@mui/material' import { CoinInfo, DropDownIcon, EmptyValueTag, FORMAT_STRING_LEN, getValuePrecisionThousand, IBData, mapSpecialTokenName, SoursURL, } from '@loopring-web/common-resources' import { InputButtonProps, InputSize } from './Interface' import React from 'react' import { useFocusRef } from '../hooks' import { IInput, ISBtn, IWrap } from './style' import { CoinIcon } from './Default' import { sanitize } from 'dompurify' import * as sdk from '@loopring-web/loopring-sdk' function _InputButton>, C, I extends CoinInfo>( { label = 'Enter token', handleError, subLabel, // wait = globalSetup.wait, // coinMap, disableInputValue, maxAllow, disabled, decimalsLimit = 8, allowDecimals = true, isShowCoinIcon = false, CoinIconElement, emptyText = 'tokenSelectToken', placeholderText = '0.00', inputData, handleCountChange, handleOnClick, focusOnInput, name, size = InputSize.middle, isHideError = false, fullwidth = false, loading = false, disableBelong = false, className, tokenType, tokenImageKey = undefined, belongAlice = undefined, subEle, }: // isAllowBalanceClick InputButtonProps, ref: React.ForwardedRef, ) { const { balance, belong, tradeValue } = (inputData ? inputData : {}) as IBData const [sValue, setsValue] = React.useState( tradeValue ? tradeValue : undefined, ) const [error, setError] = React.useState<{ error: boolean message?: string | JSX.Element }>({ error: false, message: '', }) React.useEffect(() => { if (tradeValue === undefined && error.error) { setError({ error: false }) } else if (balance && tradeValue) { _handleError(tradeValue) } }, [tradeValue, balance]) const _handleError = React.useCallback( (value: any) => { if (handleError) { let _error = handleError( { balance: Number(balance), belong, ...{ tradeValue: value }, maxAllow, } as T & { maxAllow?: boolean }, ref, ) setError(_error ? _error : { error: false }) } }, [handleError, balance, belong, maxAllow, ref], ) const inputCallback = React.useCallback( ({ current }: any) => { if (inputData && inputData.tradeValue !== Number(current?.value)) { setsValue(inputData.tradeValue) _handleError(inputData.tradeValue) } }, [inputData, _handleError], ) const inputEle = useFocusRef({ callback: inputCallback, shouldFocusOn: focusOnInput, value: tradeValue, }) // const debounceCount = debounce(({...props}: any) => { // if (handleCountChange) { // handleCountChange({...props}, ref) // } // }, wait) const _handleContChange = React.useCallback( (value: any, _name: any) => { _handleError(value) setsValue(value) if (handleCountChange) { handleCountChange({ ...inputData, ...{ tradeValue: value } } as any, _name, ref) } //debounceCount({...inputData, ...{tradeValue: value}}) }, [_handleError, setsValue, inputData, handleCountChange, ref], ) // const _handleContChange = // const _handleOnClick = React.useCallback((event: React.MouseEvent) => { // if (handleOnClick) handleOnClick(event,ref) // }, []) const _handleMaxAllowClick = React.useCallback( (_event: React.MouseEvent) => { if (maxAllow && !disabled) { _handleContChange(balance, name) //setsValue(balance); } }, [_handleContChange, balance, name, maxAllow], ) //@ts-ignore // const {coinJson} = useSettings(); // const coinIcon: any = coinJson[ belong ]; //"x": 248, // "y": 322, // "w": 36, // "h": 35, // "offX": 0, // "offY": 0, // "sourceW": 37, // "sourceH": 36 // const coinInfo: any = coinMap[ belong ] ? coinMap[ belong ] : {}; // const hasLoaded = useImage(coinInfo.icon ? coinInfo.icon : '').hasLoaded; // formatValue(sValue) return ( {label} {subEle ? ( subEle ) : subLabel && belong ? ( 0 ? `max-allow ${disabled ? 'disabled' : ''}` : `no-balance ${disabled ? 'disabled' : ''}` } onClick={_handleMaxAllowClick} > {subLabel} {balance ? getValuePrecisionThousand(balance, 8, 8, 8, false) : EmptyValueTag} ) : null} = FORMAT_STRING_LEN ? 'text-small' : ''} ${error.error ? 'error' : ''} `} wrap={'nowrap'} alignItems={'stretch'} alignContent={'stretch'} > handleOnClick(event, name ?? 'inputBtnDefault', ref)} endIcon={ disabled || disableBelong ? ( <> ) : ( ) } disabled={disabled || disableBelong} sx={{textTransform: 'none'}} > {belong ? ( {isShowCoinIcon && ( )} {!isShowCoinIcon && CoinIconElement && ( {CoinIconElement} )} ) : ( {emptyText} )} {loading && ( {'loading'} )} {isHideError ? ( <> ) : ( {error.message} )} ) } export const InputButton = React.memo(React.forwardRef(_InputButton)) as < T, C, I extends CoinInfo, >( props: InputButtonProps & React.RefAttributes, ) => JSX.Element //as React.ComponentType & RefAttributes>; ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/InputCode.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Box } from '@mui/material' const InputCodeStyle = styled(Box)` .code-input { display: flex; flex-direction: column; align-items: start; } //.code-label { // margin-bottom: 16px; //} //.code-inputs { // display: flex; // justify-content: start; // align-items: center; //} .code-inputs input { border: none; color: var(--color-text-third); background-color: var(--field-opacity); -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; text-align: center; height: 60px; width: 40px; border-radius: 4px; margin: 0 4px; border: 1px solid var(--color-border); font-size: 38px; } .code-inputs input:focus { outline: none; } .code-inputs input:first-of-type { margin-left: 24px; } .code-inputs input:nth-of-type(3n) { margin-right: 24px; } ` as typeof Box const InputCode = ({ length, loading, onComplete, }: { length: number loading: boolean onComplete: (code: string) => void }) => { const [code, setCode] = React.useState([...Array(length)].map(() => '')) const inputs = React.useRef([]) // Typescript // useRef<(HTMLInputElement | null)[]>([]) const processInput = (e: React.ChangeEvent, slot: number) => { const num = e.target.value if (/[^0-9]/.test(num)) return const newCode = [...code] newCode[slot] = num setCode(newCode) if (slot !== length - 1) { // @ts-ignore inputs.current[slot + 1].focus() } if (newCode.every((num) => num !== '')) { onComplete(newCode.join('')) } } const onKeyUp = (e: React.KeyboardEvent, slot: number) => { if (e.keyCode === 8 && !code[slot] && slot !== 0) { const newCode = [...code] newCode[slot - 1] = '' setCode(newCode) // @ts-ignore inputs.current[slot - 1].focus() } } return ( {/**/} {code.map((num, idx) => { return ( processInput(e, idx)} onKeyUp={(e) => onKeyUp(e, idx)} // @ts-ignore ref={(ref) => ref && inputs.current.push(ref)} /> ) })} ) } export { InputCode } ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/InputCoin.tsx ================================================ import { FormHelperText, Grid, Typography } from '@mui/material' import { CoinInfo, FORMAT_STRING_LEN, getValuePrecisionThousand, IBData, myLog, } from '@loopring-web/common-resources' import { InputCoinProps, InputSize } from './Interface' import React from 'react' import { useFocusRef } from '../hooks' import { CoinWrap, IInput, IWrap } from './style' import { CoinIcon } from './Default' import { useSettings } from '../../../../stores' import { sanitize } from 'dompurify' function _InputCoin, C, I extends CoinInfo>( { order = 'left', label = 'Amount', handleError, subLabel, className, // wait = globalSetup.wait, // coinMap, // isBalanceLimit = true, maxAllow, disabled, placeholderText = '0.00', inputData, handleCountChange, focusOnInput, name, allowDecimals = true, noBalance = '0.00', decimalsLimit = 8, size = InputSize.middle, isHideError = false, isShowCoinInfo = true, isShowCoinIcon = true, coinLabelStyle = undefined, coinPrecision = 6, CoinIconElement, tokenType, tokenImageKey, belongAlice = undefined, }: InputCoinProps, ref: React.ForwardedRef, ) { const { balance, belong, tradeValue } = (inputData ? inputData : {}) as IBData // myLog("InputCoin", balance, belong, tradeValue); const { isMobile } = useSettings() const [sValue, setsValue] = React.useState( tradeValue ? tradeValue : undefined, ) React.useEffect(() => { if (tradeValue === undefined && error.error) { setError({ error: false }) } else if (balance !== undefined && tradeValue) { _handleError(tradeValue) } }, [tradeValue, balance]) const [error, setError] = React.useState<{ error: boolean message?: string | JSX.Element }>({ error: false, message: '', }) const _handleError = React.useCallback( (value: any) => { if (handleError) { let _error = handleError( { balance: Number(balance), belong, ...{ tradeValue: value }, maxAllow, } as T & { maxAllow?: boolean }, ref, ) setError(_error ? _error : { error: false }) } }, [handleError, balance, belong, maxAllow, ref], ) const inputCallback = React.useCallback( ({ current }: any) => { if (inputData && inputData.tradeValue !== Number(current?.value)) { setsValue(inputData.tradeValue) _handleError(inputData.tradeValue) // debounceCount({...inputData, ...{tradeValue: inputData.tradeValue}}) // _handleContChange(current?.value, name) } }, [inputData, _handleError], ) const inputEle = useFocusRef({ callback: inputCallback, shouldFocusOn: focusOnInput, value: tradeValue, }) myLog('inputCoin ref inputEle', inputEle) // const debounceCount = debounce(({...props}: any) => { // if (handleCountChange) { // handleCountChange({...props}, ref) // } // }, wait) const _handleContChange = React.useCallback( (value: any, _name: any) => { _handleError(value) setsValue(value) if (handleCountChange) { handleCountChange({ ...inputData, ...{ tradeValue: value } } as any, _name, ref) } //debounceCount({...inputData, ...{tradeValue: value}}) }, [_handleError, setsValue, inputData, handleCountChange, ref], ) // const _handleContChange = // const _handleOnClick = React.useCallback((event: React.MouseEvent) => { // if (handleOnClick) handleOnClick(event,ref) // }, []) const _handleMaxAllowClick = React.useCallback( (_event: React.MouseEvent) => { if (maxAllow && !disabled) { _handleContChange(balance, name) //setsValue(balance); } }, [_handleContChange, balance, name, maxAllow, disabled], ) // const coinInfo: any = coinMap[ belong ] ? coinMap[ belong ] : {}; // const hasLoaded = useImage(coinInfo.icon ? coinInfo.icon : '').hasLoaded; // formatValue(sValue) const formattedBalance = getValuePrecisionThousand( balance, undefined, undefined, coinPrecision, false, { floor: true }, ) return ( <> {label} {subLabel && belong ? ( 0 ? 'max-allow' : 'no-balance'} onClick={_handleMaxAllowClick} > {subLabel} {balance ? formattedBalance : noBalance} ) : null} = FORMAT_STRING_LEN ? 'text-small' : ''} ${error.error ? 'error' : ''}`} wrap={'nowrap'} alignItems={'stretch'} alignContent={'stretch'} > {isShowCoinInfo && ( {isShowCoinIcon && ( )} {!isShowCoinIcon && CoinIconElement && ( {CoinIconElement} )} )} {isHideError ? ( <> ) : ( {error.message} )} ) } export const InputCoin = React.memo(React.forwardRef(_InputCoin)) as < T extends IBData, C, I extends CoinInfo, >( props: InputCoinProps & React.RefAttributes, ) => JSX.Element //as React.ComponentType & RefAttributes>; ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/InputMaxButton.tsx ================================================ import { Divider, FormHelperText, Grid, Link, Typography } from '@mui/material' import { CoinInfo, DropDownIcon, FORMAT_STRING_LEN, getValuePrecisionThousand, IBData, SoursURL, } from '@loopring-web/common-resources' import { InputButtonProps, InputSize } from './Interface' import React from 'react' import { useFocusRef } from '../hooks' import { IInput, ISBtn, IWrap } from './style' import { sanitize } from 'dompurify' import { useTranslation } from 'react-i18next' import { CoinIcon } from './Default' function _InputMaxButton>, C, I extends CoinInfo>( { label = 'Enter token', handleError, subLabel, order = 'left', // wait = globalSetup.wait, // coinMap, disableInputValue, maxAllow, disabled, decimalsLimit = 8, allowDecimals = true, isShowCoinIcon = false, CoinIconElement, emptyText = 'tokenSelectToken', placeholderText = '0.00', inputData, handleCountChange, handleOnClick, focusOnInput, name, noBalance = '0.00', size = InputSize.middle, isHideError = false, fullwidth = false, loading = false, className, coinPrecision = 6, tokenType, tokenImageKey, belongAlice = undefined, }: // tokenType = TokenType.single, // isAllowBalanceClick InputButtonProps, ref: React.ForwardedRef, ) { const { t } = useTranslation('common') const { balance, belong, tradeValue, max } = (inputData ? inputData : {}) as IBData const [sValue, setsValue] = React.useState( tradeValue ? tradeValue : undefined, ) const [error, setError] = React.useState<{ error: boolean message?: string | JSX.Element }>({ error: false, message: '', }) React.useEffect(() => { if (tradeValue === undefined && error.error) { setError({ error: false }) } else if (balance && tradeValue) { _handleError(tradeValue) } }, [tradeValue, balance]) const _handleError = React.useCallback( (value: any) => { if (handleError) { let _error = handleError( { balance: Number(balance), belong, ...{ tradeValue: value }, maxAllow, } as T & { maxAllow?: boolean }, ref, ) setError(_error ? _error : { error: false }) } }, [handleError, balance, belong, maxAllow, ref], ) const inputCallback = React.useCallback( ({ current }: any) => { if (inputData && inputData.tradeValue !== Number(current?.value)) { setsValue(inputData.tradeValue) _handleError(inputData.tradeValue) } }, [inputData, _handleError], ) const inputEle = useFocusRef({ callback: inputCallback, shouldFocusOn: focusOnInput, value: tradeValue, }) const _handleContChange = React.useCallback( (value: any, _name: any) => { _handleError(value) setsValue(value) if (handleCountChange) { handleCountChange({ ...inputData, ...{ tradeValue: value } } as any, _name, ref) } //debounceCount({...inputData, ...{tradeValue: value}}) }, [_handleError, setsValue, inputData, handleCountChange, ref], ) // const _handleContChange = // const _handleOnClick = React.useCallback((event: React.MouseEvent) => { // if (handleOnClick) handleOnClick(event,ref) // }, []) const _handleMaxAllowClick = React.useCallback( (_event: React.MouseEvent) => { if (maxAllow && !disabled) { _handleContChange(max ?? balance, name) //setsValue(balance); } }, [_handleContChange, balance, name, maxAllow], ) const formattedBalance = getValuePrecisionThousand( balance, undefined, undefined, coinPrecision, false, { floor: true }, ) return ( {label} = FORMAT_STRING_LEN ? 'text-small' : ''} ${error.error ? 'error' : ''} `} wrap={'nowrap'} alignItems={'stretch'} alignContent={'stretch'} marginBottom={size == 'small' ? 1 : 2} > {loading && ( {'loading'} )} handleOnClick(event, name ?? 'inputBtnDefault', ref)} endIcon={ } disabled={disabled} sx={{ justifyContent: 'left' }} > {belong ? ( {isShowCoinIcon && ( )} {!isShowCoinIcon && CoinIconElement && ( {CoinIconElement} )} ) : ( {emptyText} )} 0 ? 'max-allow' : 'no-balance'} onClick={_handleMaxAllowClick} marginLeft={1 / 2} > {t('labelInputMax')} {subLabel && ( {subLabel} {/*{t('labelTokenMaxBalance')}*/} {balance ? formattedBalance : noBalance} )} {isHideError ? ( <> ) : ( {error.message} )} ) } export const InputMaxButton = React.memo(React.forwardRef(_InputMaxButton)) as < T, C, I extends CoinInfo, >( props: InputButtonProps & React.RefAttributes, ) => JSX.Element //as React.ComponentType & RefAttributes>; ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/InputMaxCoin.tsx ================================================ import { Divider, FormHelperText, Grid, Link, Typography } from '@mui/material' import { CoinInfo, FORMAT_STRING_LEN, getValuePrecisionThousand, IBData, IBDataMax, } from '@loopring-web/common-resources' import { InputCoinProps, InputSize } from './Interface' import React from 'react' import { useFocusRef } from '../hooks' import { CoinWrap, IInput, IWrap } from './style' import { useSettings } from '../../../../stores' import { useTranslation } from 'react-i18next' import { CoinIcon } from './Default' function _InputMaxCoin, C, I extends CoinInfo>( { order = 'left', label = 'Amount', handleError, subLabel, className, maxAllow, disabled, placeholderText = '0.00', inputData, handleCountChange, focusOnInput, name, allowDecimals = true, noBalance = '0.00', decimalsLimit = 8, size = InputSize.middle, isHideError = false, isShowCoinIcon = true, coinLabelStyle = undefined, coinPrecision = 6, CoinIconElement, tokenType, tokenImageKey, }: // coinIcon, InputCoinProps, ref: React.ForwardedRef, ) { const { t } = useTranslation('common') const { balance, belong, tradeValue, max } = (inputData ? inputData : {}) as IBData // myLog("InputCoin", balance, belong, tradeValue); const { isMobile } = useSettings() const [sValue, setsValue] = React.useState( tradeValue ? tradeValue : undefined, ) React.useEffect(() => { if (tradeValue === undefined && error.error) { setError({ error: false }) } else if (balance !== undefined && tradeValue) { _handleError(tradeValue) } }, [tradeValue, balance]) const [error, setError] = React.useState<{ error: boolean message?: string | JSX.Element }>({ error: false, message: '', }) const _handleError = React.useCallback( (value: any) => { if (handleError) { let _error = handleError( { balance: Number(balance), belong, ...{ tradeValue: value }, maxAllow, } as T & { maxAllow?: boolean }, ref, ) setError(_error ? _error : { error: false }) } }, [handleError, balance, belong, maxAllow, ref], ) const inputCallback = React.useCallback( ({ current }: any) => { if (inputData && inputData.tradeValue !== Number(current?.value)) { setsValue(inputData.tradeValue) _handleError(inputData.tradeValue) // debounceCount({...inputData, ...{tradeValue: inputData.tradeValue}}) // _handleContChange(current?.value, name) } }, [inputData, _handleError], ) const inputEle = useFocusRef({ callback: inputCallback, shouldFocusOn: focusOnInput, value: tradeValue, }) // const debounceCount = debounce(({...props}: any) => { // if (handleCountChange) { // handleCountChange({...props}, ref) // } // }, wait) const _handleContChange = React.useCallback( (value: any, _name: any) => { _handleError(value) setsValue(value) if (handleCountChange) { handleCountChange({ ...inputData, ...{ tradeValue: value } } as any, _name, ref) } //debounceCount({...inputData, ...{tradeValue: value}}) }, [_handleError, setsValue, inputData, handleCountChange, ref], ) // const _handleContChange = // const _handleOnClick = React.useCallback((event: React.MouseEvent) => { // if (handleOnClick) handleOnClick(event,ref) // }, []) const _handleMaxAllowClick = React.useCallback( (_event: React.MouseEvent) => { if (maxAllow && !disabled) { _handleContChange(max ?? balance, name) //setsValue(balance); } }, [_handleContChange, balance, name, maxAllow, disabled], ) // const coinInfo: any = coinMap[ belong ] ? coinMap[ belong ] : {}; // const hasLoaded = useImage(coinInfo.icon ? coinInfo.icon : '').hasLoaded; // formatValue(sValue) const formattedBalance = getValuePrecisionThousand( balance, undefined, undefined, coinPrecision, false, { floor: true }, ) return ( <> {label} = FORMAT_STRING_LEN ? 'text-small' : ''} ${error.error ? 'error' : ''}`} wrap={'nowrap'} alignItems={'stretch'} alignContent={'stretch'} marginBottom={size == 'small' ? 1 : 2} > {isShowCoinIcon && ( )} {!isShowCoinIcon && CoinIconElement && ( {CoinIconElement} )} {belong} 0 ? 'max-allow' : 'no-balance'} onClick={_handleMaxAllowClick} > {t('labelInputMax')} {subLabel && ( {subLabel} {/*{t('labelTokenMaxBalance')}*/} {balance ? formattedBalance : noBalance} )} {/**/} {/* */} {/**/} {isHideError ? ( <> ) : ( {error.message} )} ) } export const InputMaxCoin = React.memo(React.forwardRef(_InputMaxCoin)) as < T extends IBData, C, I extends CoinInfo, >( props: InputCoinProps & React.RefAttributes, ) => JSX.Element //as React.ComponentType & RefAttributes>; ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/InputSearch.tsx ================================================ import { InputAdornment, OutlinedInput, OutlinedInputProps } from '@mui/material' import { CloseIcon, SearchIcon } from '@loopring-web/common-resources' import React from 'react' import styled from '@emotion/styled' const CloseIconStyled = styled(CloseIcon)` position: absolute; top: 55%; transform: translateY(-50%); right: ${({ theme }) => theme.unit}px; cursor: pointer; ` export type InputSearchProps = { value?: string onChange?: (value: string) => void; } & OutlinedInputProps export const InputSearch = React.forwardRef( ({ value, onChange, ...rest }: InputSearchProps, _ref: React.ForwardedRef) => { return ( { if (onChange) { onChange(event.target.value) } }} startAdornment={ } endAdornment={ { if (onChange) { onChange('' as any) } }} /> } sx={{ backgroundColor: 'transparent' }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/InputSelect.tsx ================================================ import { Box } from '@mui/material' import { CoinInfo, CoinKey, globalSetup } from '@loopring-web/common-resources' import React from 'react' import { InputSelectProps } from './Interface' import { useFocusRef, usePanelRef } from '../hooks' import { WithTranslation } from 'react-i18next' import { InputSearch } from './InputSearch' import * as _ from 'lodash' function _InputSelect>( { t, handleContentChange, wait = globalSetup.wait, panelRender, inputProps, placeholder, focusOnInput, backElement, selected, }: InputSelectProps & WithTranslation, _ref: React.ForwardedRef, ) { const [_value, setValue] = React.useState<{ selected: string | undefined focusOnInput: boolean }>({ selected: '', focusOnInput: focusOnInput ? focusOnInput : false, }) const debounceContentChange = React.useCallback( _.debounce(({ value }: any) => { if (handleContentChange) { handleContentChange(value) } }, wait), [], ) const _handleContentChange = (value: any) => { setValue({ ..._value, selected: value }) debounceContentChange({ value }) } const inputEle = useFocusRef({ shouldFocusOn: _value.focusOnInput, value: _value.selected, }) // let height = '100%'; // let width = '100%'; const panelRef = usePanelRef({ // callback:({current})=>{ // height = current.offsetHeight; // width = current.offsetWidth; // } }) React.useEffect(() => { setValue({ selected: '', focusOnInput: false, }) }, [selected]) return ( <> {backElement ? {backElement} : <>} {panelRender({ selected, value: _value.selected })} ) } export const InputSelect = React.memo(React.forwardRef(_InputSelect)) as >( props: InputSelectProps & WithTranslation & React.RefAttributes, ) => JSX.Element //as React.ComponentType & RefAttributes>; // export const InputSelectSearch=withTranslation()(()=><>) ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/Interface.ts ================================================ import { CoinInfo, CoinKey, CoinMap, CoinSource, TokenType } from '@loopring-web/common-resources' import React from 'react' import { InputProps } from '@mui/material' import { XOR } from '../../../../types/lib' export type defaultProps = { label: string | JSX.Element coinMap: CoinMap ? CoinInfo : CoinInfo> placeholderText?: string allowDecimals?: boolean size?: InputSize order?: 'left' | 'right' tokenType?: TokenType | undefined placeholder: string handleError?: ( ibData: T & { maxAllow?: boolean }, ref: React.ForwardedRef, ) => { error: boolean; message?: string | JSX.Element } maxValue?: string | number | undefined focusOnInput?: boolean coinPrecision?: number className?: string name?: string minimum?: number | string maxAllow?: boolean decimalsLimit?: number disabled?: boolean logoColor?: string wait?: number isHideError?: boolean tokenImageKey?: string belongAlice?: string coinIcon?: [CoinSource, CoinSource?] noBalance?: string } & XOR< { isShowCoinInfo?: true } & XOR< { isShowCoinIcon: true }, { isShowCoinIcon: false; CoinIconElement?: JSX.Element } >, { isShowCoinInfo: false } > & XOR<{ subLabel?: string }, { subEle: JSX.Element }> export type InputButtonProps = defaultProps & { inputData?: T | undefined emptyText: string isAllowBalanceClick?: boolean disableInputValue?: boolean disableBelong?: boolean disabled?: boolean logoColor?: string wait?: number maxValue?: string | number | undefined minimum?: string | number | undefined isHideError?: boolean handleCountChange?: (ibData: T, name: string, ref: React.ForwardedRef) => void handleOnClick: (event: React.MouseEvent, name: string, ref: React.ForwardedRef) => void coinPrecision?: number handleError?: ( tradeData: T & { maxAllow?: boolean }, ref: React.ForwardedRef, ) => { error: boolean; message?: string | JSX.Element } focusOnInput?: boolean name?: string className?: string fullwidth?: boolean loading?: boolean belongAlice?: string } export enum InputSize { middle = 'middle', small = 'small', } export type InputCoinProps = defaultProps & { inputData?: T | undefined handleCountChange?: (ibData: T, name: string, ref: React.ForwardedRef) => void coinLabelStyle?: React.CSSProperties } export type InputSelectProps> = { placeholder?: string focusOnInput?: boolean // coinMap: CoinMap, // walletMap: WalletMap | {}, panelRender: (props: any) => JSX.Element height?: number //outSide height, default is defined in global.ts file inputProps?: InputProps backElement?: JSX.Element allowScroll?: boolean selected?: string | undefined // handleClose?: (event: React.MouseEvent) => void, handleContentChange?: (value: string | undefined | I) => void // onRefresh?: (event: React.MouseEvent) => void, useInputRef?: ( props: useFocusRefProps, deps: any[], ) => React.RefObject hasCancel?: boolean } export type useFocusRefProps = { selected?: I | null | undefined isFocus?: boolean callback?: (props?: any) => void } // export type {IBData, CoinMap, CoinInfo, coinType} ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/TextareaWithCount.tsx ================================================ import { TextareaAutosizeStyled } from './style' import React from 'react' import { TextareaAutosizeProps } from '@mui/base/TextareaAutosize/TextareaAutosize' import { Box, Typography } from '@mui/material' import { useTranslation } from 'react-i18next' export const TextareaWithCount = ({ // totalCount, value, handleError, ...rest }: TextareaAutosizeProps & { label?: string // totalCount: number; handleError?: ( value: string, count: number, ) => | { error: boolean message?: string | JSX.Element } | undefined }) => { const [countObj, setCountObj] = React.useState({ number: 0, count: rest.maxLength ?? 25, }) const { t } = useTranslation(['error', 'common']) const [error, setError] = React.useState<{ error: boolean message?: string | JSX.Element }>({ error: false, message: '', }) React.useEffect(() => { if (value === undefined && error.error) { setError({ error: false }) } }, [value]) const _handleError = React.useCallback( (value: any) => { if (handleError) { const error = handleError(value, countObj.count) if (error) { setError(error) } else { setError({ error: false }) } } else { if (value.length > countObj.count) { setError({ error: true, message: t('errorLengthLimit', {}), }) } else { setError({ error: false }) } } }, [countObj], ) return ( { setCountObj((state) => ({ number: event.target.value?.length ?? 0, count: state.count, })) _handleError(event.target.value) rest.onChange && rest.onChange(event, ..._rest) }} draggable={true} /> {`${countObj.number}/${countObj.count}`} {/*{this.state.count}*/} {/*{countLimit > 0 && `/${countLimit}`}*/} ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/index.ts ================================================ export * from './InputSelect' export * from './InputButton' export * from './DatePicker' export * from './Interface' export * from './Default' export * from './InputCoin' export * from './InputSearch' export * from './InputCode' export * from './CollectionInput' export * from './TextareaWithCount' export * from './InputMaxButton' export * from './InputMaxCoin' export { TextField, TextareaAutosizeStyled, InputSearchWrapperStyled, RadioGroupStyle, OutlinedInput, } from './style' ================================================ FILE: packages/component-lib/src/components/basic-lib/form/input/style.ts ================================================ import styled from '@emotion/styled' import { Box, BoxProps, Button, ButtonProps, RadioGroup, TextareaAutosize, TextField as MuiTextField, OutlinedInput as MuiOutlinedInput, TextFieldProps, OutlinedInputProps, } from '@mui/material' import CurrencyInput from 'react-currency-input-field' import { InputSize } from './Interface' import { css } from '@emotion/react' export const inputHeightLarge = () => css` height: var(--input-height-large); font-size: 1.5rem; .MuiInputAdornment-root { svg { height: var(--btn-icon-size-large); width: var(--btn-icon-size-large); } } ` export const OutlinedInput = styled(MuiOutlinedInput)` ${({ size }) => size?.toLowerCase() === 'large' && inputHeightLarge} ` export const TextField = styled(MuiTextField)` && .MuiOutlinedInput-root { ${({ size }) => size?.toLowerCase() === 'large' && inputHeightLarge} background-color: transparent; } background-color: transparent; input::placeholder { color: var(--color-placeholder); opacity: 1; } .MuiInputAdornment-root { padding: 0 ${({ theme }) => theme.unit}px; } label + & { //margin-top: 24px; margin-top: 0; } && { .MuiSelect-nativeInput + svg { position: absolute; right: ${({ size }) => (size === 'large' ? 1 : 0.4)}rem; top: ${({ theme, size }) => (size === 'large' ? 2 * theme.unit : theme.unit)}px; color: var(--color-text-secondary); } &:not(.MuiFormControl-fullWidth) { max-width: 260px; } text-overflow: fade(); } &:focus { ${({ theme }) => theme.border.defaultFrame({ c_key: 'focus', d_R: 0.5 })}; outline: transparent; } ` // export MuiTextField = styled(MuTextField)<>``; export const IWrap = styled(Box)< BoxProps & { size: 'middle' | 'small' isMobile?: boolean fullWidth?: boolean } >` ${({ theme }) => theme.border.defaultFrame({ c_key: 'var(--opacity)' })}; ${({ fullWidth }) => fullWidth && `width:100%`}; .label-wrap { white-space: nowrap; text-overflow: ellipsis; text-transform: capitalize; color: var(--color-text-secondary); } .message-wrap { .MuiFormHelperText-root { color: var(--color-error); text-align: right; font-size: ${({ theme }) => theme.fontDefault.h6}; } } .sub-label { text-align: right; cursor: pointer; .max-allow { text-decoration: underline dotted; color: var(--color-secoundary); &:hover { color: var(--color-primary); } } .no-balance { text-decoration: none; } .disabled { color: var(--color-text-disable); } } .coinInput-wrap, .btnInput-wrap { position: relative; box-sizing: border-box; border-radius: ${({ theme }) => theme.unit}px; margin-top: ${({ theme }) => `${theme.unit / 2}px`}; height: var(--input-height-large); ::before { content: ''; display: block; width: 100%; height: 100%; position: absolute; top: 0; left: 0; box-sizing: border-box; pointer-events: none; z-index: 1; } &.error { border: 1px solid var(--color-error) !important; border-radius: ${({ theme }) => theme.unit / 2}px; } } .input-wrap { //min-width: 128px; // width: 100%; flex: 1; height: 100%; } .icon-wrap, .btn-wrap { max-width: var(--btn-max-width); min-width: var(--coin-min-width); .MuiButton-label { justify-content: flex-start; } &.icon-wrap-left, &.btn-wrap-left { justify-content: flex-end; } } ${({ size, theme, isMobile }) => { if (size === InputSize.small) { return ` .input-wrap,.icon-wrap{ font-size: ${isMobile ? theme.fontDefault.body2 : theme.fontDefault.body1}; } .label-wrap, .main-label{ font-size: ${theme.fontDefault.body2}; } .coinInput-wrap, .btnInput-wrap { font-size: ${isMobile ? theme.fontDefault.body2 : theme.fontDefault.body1}; height: var(--btn-Input-small-height); &.text-small{ font-size: ${theme.fontDefault.body2}; } input[type=text]{ font-size: ${theme.fontDefault.body1}; &:focus { outline: 0; } } } ` } else { return ` .input-wrap,.icon-wrap{ font-size: ${theme.fontDefault.h5}; } .label-wrap{ font-size: ${theme.fontDefault.body1}; } .coinInput-wrap, .btnInput-wrap{ font-size: ${theme.fontDefault.h4}; height: var(--input-height-large); &.text-small{ font-size: ${theme.fontDefault.body1}; } input[type=text]{ font-size: ${theme.fontDefault.h4}; } } ` } }}; &.swapWrap { &.buyInput { padding-top: ${({ theme }) => 2 * theme.unit}px; } :has(.btnInput-wrap.error) { border-color: var(--color-error); } .btnInput-wrap { &.error { border: initial !important; //border-bottom: 1px solid var(--color-error) !important; } input:focus + label::before { box-shadow: initial; ${({ theme }) => `${theme.border.defaultFrame({ c_key: 'var(--opacity)', d_R: 0.5 })};`} } } //.input-wrap { // position: initial; //} flex: 1; &:focus-within { box-shadow: initial; ${({ theme }) => `${theme.border.defaultFrame({ c_key: 'var(--color-border-hover)', d_R: 0.5 })};`} } min-height: var(--input-height-swap); background: var(--field-opacity); display: flex; flex-direction: column; position: relative; .label-wrap { //margin-left: 40%; //width: 60%; .MuiGrid-item { flex-basis: inherit; width: inherit; max-width: inherit; } p { padding-right: 1rem; } padding-top: 0.8rem; height: var(--input-height-swap-label); } .btnInput-wrap { position: initial; } .btn-wrap { position: absolute; top: 0rem; } //.input-wrap-right{ // //} } ` as ( props: BoxProps & { size: 'middle' | 'small' isMobile?: boolean fullWidth?: boolean }, ) => JSX.Element export const CoinWrap = styled(Box)` & { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: 1px solid transparent; white-space: nowrap; text-overflow: ellipsis; font-size: ${({ theme }) => theme.fontDefault.h5}; color: var(--color-text-primary); .placeholder { color: var(--color-text-secondary); } } &.icon-wrap-right > div { justify-content: flex-start; padding-left: ${({ theme }) => (theme.unit / 2) * 3}px; align-items: center; } &.icon-wrap-left > div { justify-content: flex-end; padding-right: ${({ theme }) => theme.unit}px; align-items: center; } ` as (props: BoxProps & { logoColor?: any }) => JSX.Element export const ISBtn = styled(Button)` && { width: 100%; height: 100%; border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: 1px solid transparent; white-space: nowrap; text-overflow: ellipsis; font-size: ${({ theme }) => theme.fontDefault.h5}; color: var(--color-text-primary); .MuiButton-endIcon { color: var(--color-text-third); } .placeholder { color: var(--color-text-secondary); } } &:hover, &:active { color: var(--color-text-primary); background: var(--color-box-hover); } &.swapWrap { //.input-wrap-right { // input[type='text'] { // text-align: left; // } //} & { :active { //color: var(--color-text-primary); background: var(--opacity); } } } ` as (props: ButtonProps & { logoColor?: any }) => JSX.Element export const IInput = styled(CurrencyInput)` text-align: right; color: var(--color-text-primary); ::placeholder { color: var(--color-placeholder); } :disabled { color: var(--color-text-disable); } .loading:disabled { color: var(--color-text-primary); } width: 100%; height: 100%; border: 0; margin: 0; display: block; padding: .8rem 1rem; min-width: 0; background: none; box-sizing: border-box; animation-name: mui-auto-fill-cancel; letter-spacing: inherit; animation-duration: 10ms; -webkit-tap-highlight-color: transparent; + label { height: 0; width: 0; } :focus { outline: 0; & + label::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; ${({ theme }) => `${theme.border.defaultFrame({ c_key: 'var(--color-border-hover)', d_R: 0.5, })};`}; } } .error &:focus { & + label::before { ${({ theme }) => `${theme.border.defaultFrame({ c_key: 'var(--opacity)', d_R: 0.5 })};`} } } .input-wrap-right & { text-align: right; border-top-left-radius: 0; border-bottom-left-radius: 0; :focus { } } .input-wrap-left & { text-align: left; border-top-right-radius: 0; border-bottom-right-radius: 0; :focus { } } }` as typeof CurrencyInput export const TextareaAutosizeStyled = styled(TextareaAutosize)` label + & { margin-top: ${({ theme }) => theme.unit}px; } background: (var(--opacity)); font-family: inherit; color: inherit; padding: ${({ theme }) => theme.unit}px; width: 100%; ${({ theme }) => theme.border.defaultFrame({ c_key: theme.colorBase.border, d_R: 0.5, })}; &:focus { ${({ theme }) => theme.border.defaultFrame({ c_key: 'focus', d_R: 0.5 })}; outline: transparent; } &:disabled { line-height: 1.5em; border: 0; background: (var(--opacity)); color: var(--color-text-third); ${({ theme }) => theme.border.defaultFrame({ c_key: theme.colorBase.opacity, d_R: 0.5, })}; } &.error { ${({ theme }) => theme.border.defaultFrame({ c_key: 'var(--color-error)', d_R: 0.5 })}; } ` as typeof TextareaAutosize export const InputSearchWrapperStyled = styled(Box)` padding: ${({ theme }) => theme.unit * 2}px; padding-bottom: 0; ` as typeof Box export const RadioGroupStyle = styled(RadioGroup)` margin: 0; .MuiFormControlLabel-root { margin-right: 0; display: inline-flex; align-items: center; justify-content: flex-end; flex-direction: row; } .MuiFormControlLabel-label { line-height: var(--svg-size-cover); } ` as typeof RadioGroup ================================================ FILE: packages/component-lib/src/components/basic-lib/index.tsx ================================================ export * from './btns' export * from './form' export * from './lists' export * from './panel' export * from './tables' export * from './popover' export * from './resource' export * from './table-pagination' export * from './tags' export * from './media' export { SpaceBetweenBox } from './display' export { Loading } from './loading' export { CustomCheckBox } from './checkbox' ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/CoinList.tsx ================================================ import { Box, ListItem, ListItemIcon, ListItemText, Typography } from '@mui/material' import { Trans, WithTranslation } from 'react-i18next' import React from 'react' import styled from '@emotion/styled' import { CoinItemProps, CoinMenuProps } from './Interface' import { CoinInfo, CoinKey, mapSpecialTokenName, TokenType, WalletCoin } from '@loopring-web/common-resources' import { Virtuoso } from 'react-virtuoso' import { CoinIcon } from '../form' import { EmptyDefault } from '../empty' function _CoinMenu>( { coinMap = {}, walletMap = {}, nonZero, sorted, filterBy = (ele, filterString) => { return filterString && filterString.length ? RegExp(filterString).test(ele.simpleName as string) : true }, filterString, handleSelect, allowScroll = true, selected = null, listProps = {}, height = '100px', tokenType, className, filterWithBorrowed, ...rest }: CoinMenuProps & WithTranslation, _ref: React.Ref, ) { const [select, setSelect] = React.useState | null>(selected as CoinKey) const [list, setList] = React.useState([]) const virtuoso = React.useRef(null) let rowIndex = 0 React.useEffect(() => { if (select !== selected) { setSelect(selected) } }, [select, selected]) if (nonZero === undefined) { nonZero = false } if (sorted === undefined) { sorted = true } const update = React.useCallback(() => { if (coinMap) { setList( Object.keys(coinMap) .reduce((list: Array<{ walletCoin: WalletCoin; key: string }>, key) => { const filter = filterBy(coinMap[key], filterString) if (filter) { const walletCoin: WalletCoin = walletMap[key] ? walletMap[key] : { belong: key, count: 0 } if ((nonZero && walletMap[key] && (filterWithBorrowed ? walletMap[key].borrowed > 0 : walletMap[key].count > 0) ) || !nonZero) { list.push({ walletCoin, key: key }) if (select === key) { rowIndex = list.length - 1 } } } return list }, []) .sort(function (a, b) { if (sorted) { if (a.walletCoin.count && b.walletCoin.count) { return b.walletCoin.count - a.walletCoin.count } else if (a.walletCoin.count && !b.walletCoin.count) { return -1 } else if (!a.walletCoin.count && b.walletCoin.count) { return 1 } return a.walletCoin.belong.localeCompare(b.walletCoin.belong) } return 1 }), ) } }, [coinMap, filterString, sorted, walletMap, nonZero]) const coinMapJSONString = JSON.stringify(coinMap) React.useEffect(() => { update() }, [coinMapJSONString, filterString, sorted]) const handleListItemClick = React.useCallback( (_event: React.MouseEvent, select: CoinKey) => { setSelect(select) handleSelect && handleSelect(_event, select) }, [handleSelect], ) return ( <> {list.length ? ( ; key: string }> data={list} className={`coin-menu ${className}`} style={{ minHeight: '210px', flex: 1 }} ref={virtuoso} initialTopMostItemIndex={rowIndex} itemContent={(index, item) => { let { walletCoin, key } = item //list[ index ]; return ( tokenType={tokenType} key={index} {...{ coinInfo: coinMap[key] ?? ({} as CoinInfo), walletCoin, select: select, handleListItemClick, itemKey: key as CoinKey, ...rest, }} /> ) }} /> ) : ( { return Content is Empty }} /> )} ) } export const CoinMenu = React.memo(React.forwardRef(_CoinMenu)) as unknown as >( props: CoinMenuProps & WithTranslation & React.RefAttributes, ) => JSX.Element const StyledCoinItem = styled(ListItem)` && { width: 100%; display: flex; justify-content: stretch; justify-items: center; height: var(--list-coin-item); box-sizing: border-box; padding-left: ${({ theme }) => (theme.unit / 2) * 5}px; padding-right: ${({ theme }) => (theme.unit / 2) * 5}px; align-items: center; } &.Mui-selected, &.Mui-focusVisible { background: var(--color-box-hover); &:hover { background: var(--color-box-hover); } } .MuiListItemIcon-root { height: var(--btn-icon-size); width: var(--btn-icon-size); min-width: var(--btn-icon-size); margin-right: ${({ theme }) => theme.unit}px; display: flex; justify-content: center; justify-items: center; align-items: center; .MuiAvatar-root { transform-origin: center; } } .MuiListItemText-multiline { display: flex; flex-direction: row; justify-content: space-between; } ` export const CoinItem = React.memo( React.forwardRef( ( { // t, coinInfo, walletCoin, select, itemKey, handleListItemClick, tokenType, contentEle, }: CoinItemProps & WithTranslation, ref: React.ForwardedRef, ) => { const { simpleName, erc20Symbol, belongAlice } = coinInfo return ( handleListItemClick(event, itemKey)} > {contentEle ? ( contentEle({ ele: { ...walletCoin } }) ) : ( {walletCoin.count} )} } /> ) }, ), ) as unknown as ( props: CoinItemProps & WithTranslation & React.RefAttributes, ) => JSX.Element // (props: CoinItemProps & RefAttributes) => JSX.Element; //as React.ComponentType & RefAttributes>; ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/CollectionItem.tsx ================================================ import { CardStyleItem, CollectionItemProps, CollectionListProps, CollectionMedia, EmptyDefault, IconButtonStyle, } from '../../../index' import { Avatar, Box, Grid, Link, MenuItem, Pagination, Popover, Radio, Typography, } from '@mui/material' import { CollectionLimit, CollectionMeta, CopyIcon, copyToClipBoard, getShortAddr, ImageIcon, NFT_TYPE_STRING, NFTLimit, sizeNFTConfig, SoursURL, ViewMoreIcon, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' import { useTranslation } from 'react-i18next' import styled from '@emotion/styled' import { bindMenu, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks' import { sanitize } from 'dompurify' const BoxStyle = styled(Box)` .MuiRadio-root { position: absolute; right: ${({ theme }) => theme.unit}px; top: ${({ theme }) => theme.unit}px; transform: scale(1.5); } .btn-group { width: auto; } .collection:hover { .btn-group { display: flex; width: initial; } } ` as typeof Box const BoxLabel = styled(Box)` background: var(--color-box-third); color: var(--color-text-button); border-radius: ${({ theme }) => theme.unit}px; ` as typeof Box const BoxBtnGroup = styled(Box)` position: absolute; right: ${({ theme }) => 2 * theme.unit}px; top: ${({ theme }) => 2 * theme.unit}px; width: 100%; //flex-direction: row-reverse; &.mobile { } ` as typeof Box const ActionMemo = React.memo( ({ setShowDeploy, setShowEdit, setShowManageLegacy, item, account, setShowMintNFT, }: CollectionItemProps) => { const { t } = useTranslation('common') const popupState = usePopupState({ variant: 'popover', popupId: 'collection-action', }) const bindContent = bindMenu(popupState) const bindAction = bindTrigger(popupState) return ( {!!( item.extra?.properties?.isCounterFactualNFT && item.extra?.properties?.isEditable && item.owner?.toLowerCase() === account?.accAddress?.toLowerCase() && item?.nftType !== NFT_TYPE_STRING.ERC721 ) && ( { if (setShowEdit) { setShowEdit(item) popupState.close() } }} > {t('labelCollectionEditBtn')} )} {!!( item.extra?.properties?.isCounterFactualNFT && item.extra?.properties?.isEditable && item.extra?.properties?.isLegacy && item.owner?.toLowerCase() === account?.accAddress?.toLowerCase() && item?.nftType !== NFT_TYPE_STRING.ERC721 ) && ( { if (setShowManageLegacy) { setShowManageLegacy(item) popupState.close() } }} > {t('labelCollectionImportNFTBtn')} )} {item.extra?.properties?.isCounterFactualNFT && item.baseUri !== '' && item.deployStatus === sdk.DEPLOYMENT_STATUS.NOT_DEPLOYED && item.owner?.toLowerCase() === account?.accAddress?.toLowerCase() ? ( { setShowDeploy && setShowDeploy(item) popupState.close() }} > {t('labelNFTDeployContract')} ) : ( <> // { // e.stopPropagation(); // window.open( // `${etherscanBaseUrl}address/${item?.contractAddress}` // ); // window.opener = null; // }} // > // {t("labelViewEtherscan")} // // )} {/* {!!( item.extra?.properties?.isCounterFactualNFT && item.extra?.properties?.isMintable && item.owner?.toLowerCase() === account?.accAddress?.toLowerCase() && item?.nftType !== NFT_TYPE_STRING.ERC721 ) && ( { if (setShowMintNFT) { setShowMintNFT(item) popupState.close() } }} > {t('labelNFTMintSimpleBtn')} )} */} ) }, ) export const CollectionItem = React.memo( React.forwardRef( (props: CollectionItemProps, _ref: React.Ref) => { const { item, index, setCopyToastOpen, noEdit, isSelectOnly = false, selectCollection, onItemClick, getIPFSString, baseURL, size, } = props const { t } = useTranslation('common') const sizeConfig = sizeNFTConfig(size ?? 'large') return ( undefined} onClick={(e) => { e.stopPropagation() if (onItemClick) { onItemClick(item) } }} /> {isSelectOnly && ( )} {!isSelectOnly && !noEdit && ( )} {(item?.cached?.avatar ?? getIPFSString(item?.avatar ?? '', baseURL)).startsWith( 'http', ) ? ( ) : ( )} { e.stopPropagation() copyToClipBoard(item?.contractAddress ?? '') setCopyToastOpen({ isShow: true, type: 'address' }) }} > {getShortAddr(item?.contractAddress ?? '')} {item?.extends?.count && ( {t( size == 'small' ? 'labelCollectionItemSimpleValue' : 'labelCollectionItemValue', { value: item?.extends?.count?.toString()?.length > 2 ? '99+' : item?.extends?.count, }, )} )} {item?.nftType} ) }, ), ) export const CollectionCardList = ({ collectionList, page, total, onPageChange, setCopyToastOpen, setShowDeploy, setShowManageLegacy, setShowEdit, setShowMintNFT, setShowTradeIsFrozen, toggle, account, onSelectItem, onItemClick, isSelectOnly = false, selectCollection, etherscanBaseUrl, isLoading, noEdit = false, filter, size = 'large', ...rest }: CollectionListProps & Partial> & { onSelectItem?: (item: Co) => void }) => { const { t } = useTranslation('common') const sizeConfig = sizeNFTConfig(size) return ( {isLoading ? ( {'loading'} ) : !collectionList?.length ? ( ( {t('labelNoContent')} )} /> ) : ( <> {collectionList.map((item, index) => { return ( { e.stopPropagation() if (onSelectItem) { onSelectItem(item) } }} > ) })} {total > CollectionLimit && ( 0 ? 1 : 0)} page={page} onChange={(_event, value) => { onPageChange(Number(value), filter ? { ...filter } : undefined) }} /> )} )} ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/FileListItem.tsx ================================================ import { Box, IconButton, ListItem, ListItemText, Typography } from '@mui/material' import { CloseIcon, CompleteIcon, FailedIcon, LoadingIcon } from '@loopring-web/common-resources' import React, { ForwardedRef } from 'react' import { IpfsFile } from '../panel' export const FileListItem = React.memo( React.forwardRef( ( { file, // index, onDelete, isProcessing, error, }: IpfsFile & { onDelete: () => void index: number }, ref: ForwardedRef, ) => { return ( {file.name} } secondary={ size: {file.size} } /> {isProcessing ? ( ) : error ? ( ) : ( )} ) }, ), ) ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/HeadMenuItem.tsx ================================================ import { Box, BoxProps, Container, ListItemAvatar, MenuItem, MenuProps, Typography, } from '@mui/material' import { WithTranslation } from 'react-i18next' import { bindHover, bindMenu, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks' import { BasicHeaderItem, HeadMenuType, MenuItemLink, MenuItemProps } from './Interface' import styled from '@emotion/styled' import clsx from 'clsx' import { ammDisableList, DropDownIcon, L1L2_NAME_DEFINED, MapChainId, orderDisableList, } from '@loopring-web/common-resources' import Menu from 'material-ui-popup-state/HoverMenu' import React, { ForwardedRef, RefAttributes } from 'react' import { useHistory, useRouteMatch } from 'react-router-dom' import { useSettings } from '../../../stores' export const HeaderMenu = styled(Container)` display: flex; justify-content: space-between; align-items: stretch; position: relative; ` as typeof Container const StyledHeadMenuItem = styled(MenuItem)>` &:not(.layer-0) { display: flex; min-height: var(--header-submenu-item-height); height: fit-content; width: var(--header-submenu-item-weight); align-items: flex-start; } &.layer-0 { display: flex; flex-direction: column; justify-content: center; text-transform: capitalize; // font-size: ${({ theme }) => theme.fontDefault.h5}; box-shadow: inherit; height: var(--header-height); //color: var(--color-text-secondary); background: inherit; position: relative; &.Mui-disabled{ color: var(--color-text-disable) } &.Mui-selected, &.Mui-selected:hover { background: inherit; color: var(--color-text-button-select); //color: var(--color-primary); } &:hover { background: inherit; color: var(--color-primary); } &.Mui-selected.Mui-focusVisible { background: inherit; } .MuiButtonBase-root { opacity: 1; color: inherit; &:hover { // color: var(--primary); } } .MuiTab-root { &:hover { //color: var(--primary); } } //.MuiButtonBase-root { // opacity: 1; // color: inherit; //} } //svg { // width: var(--header-menu-icon-size); // height: var(--header-menu-icon-size); //} &&.layer-next { display: flex; } .mobile & { align-items: flex-start; } } ` as typeof MenuItem const StyledLayer2Item = styled(Box)>` padding: 0; margin: 0; display: flex; flex-direction: row; align-items: center; &:hover { //border-left-color: transparent; background: var(--opacity); h5 { color: var(--color-primary); } svg { fill: var(--color-primary); } } ` as typeof MenuItem const StyledHeaderMenuSub = styled(Menu)` && { color: var(--color-text-third); ul { box-shadow: inset 0.5px var(--color-border); background: var(--color-box-pop); padding: 0; //.layer-sub { // height: var(--header-menu-list-height) //} } } ` as typeof Menu const StyledTabBtn = styled(MenuItem)>` &.Mui-selected, &.Mui-selected.Mui-focusVisible { background: inherit; } &.Mui-disabled{ color: var(--color-text-disable) } && { text-transform: capitalize; display: flex; height: 100%; padding-right: 0; svg { transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; } &:hover { background-color: inherit; color: var(--color-primary); svg { transform: rotate(180deg); } } ` as typeof MenuItem const checkEnable = ({ allowTrade, id }: { id: string; allowTrade?: any }): boolean => { if (allowTrade?.order?.enable === false && orderDisableList.includes(id)) { return true } else if (allowTrade?.joinAmm?.enable === false && ammDisableList.includes(id)) { return true } else { return false } } export const HeadMenuItem = React.memo( React.forwardRef( ( { className, layer, selected, allowTrade, handleListKeyDown, children, status, router, label, }: MenuItemLink, ref: ForwardedRef, ) => { const history = useHistory() const match = useRouteMatch('/trade/:item/:pair') //@ts-ignore const pair = match?.params?.pair ?? 'LRC-ETH' return ( { router?.path?.startsWith('http') ? window.open(router?.path, '_blank') : history.push(router?.path.replace('${pair}', pair) ?? '') } } > {children} ) }, ), ) as (props: MenuItemLink) => JSX.Element export let Layer2Item: ( props: MenuItemProps & WithTranslation, ) => JSX.Element Layer2Item = React.memo( ({ t, label }: MenuItemProps & WithTranslation) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( {label.icon && } {t(label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, ns: ['layout', 'landPage', 'common'], })} {label?.description ? t(label.description) : ''} ) }, ) as (props: MenuItemProps & WithTranslation) => JSX.Element export const HeaderMenuSub = React.memo( React.forwardRef( ( { t, label, className, selected, allowTrade, status, renderList, layer = 0, anchorOrigin = { vertical: 'bottom', horizontal: 'left' }, }: HeadMenuType & WithTranslation, ref: ForwardedRef, ) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const popupState = usePopupState({ variant: 'popover', popupId: `tradeHeaderSubMenu${label.id}`, }) return ( <> {checkEnable({ allowTrade, id: label.id }) || status === 'disabled' ? ( {t(label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, ns: ['layout', 'landPage', 'common'], })} ) : ( <> {t(label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, ns: ['layout', 'landPage', 'common'], })} {renderList && renderList({ handleListKeyDown: popupState.close, })} )} ) }, ), ) as ( props: HeadMenuType & WithTranslation & RefAttributes, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/HeadToolbar.tsx ================================================ import styled from '@emotion/styled' import { MenuItem, MenuItemProps } from '@mui/material' //${({ theme }) => theme.unit * 2} export const TabItemPlus = styled(MenuItem)` && { &.Mui-focusVisible { background-color: transparent; } margin: 0; padding: 0 ${({ theme }) => 0.5 * theme.unit}px; &:hover { background-color: transparent; border-left-color: transparent; } .MuiIconButton-root { &.MuiIconButton-sizeMedium { svg { width: var(--header-menu-icon-size); height: var(--header-menu-icon-size); color: var(--color-text-secondary); } } &.MuiIconButton-sizeLarge { svg { width: var(--header-menu-icon-large); height: var(--header-menu-icon-large); color: var(--color-text-secondary); } } :hover { svg { color: var(--color-text-primary); } color: var(--color-text-primary); } } } ` as (props: MenuItemProps) => JSX.Element ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/Interface.ts ================================================ import React from 'react' import { LinkProps, ListItemProps, MenuItemProps as muMenuItemProps } from '@mui/material' import { Account, CoinInfo, CoinKey, CoinMap, GET_IPFS_STRING, L2CollectionFilter, MakeMeta, TokenType, WalletCoin, WalletMap, } from '@loopring-web/common-resources' import { ListProps } from 'react-virtualized' import { List } from 'immutable' export type MuiMenuItemProps = muMenuItemProps & { withnocheckicon?: 'true' | 'false' | undefined } export type BasicListItem = { label: { id: string [key: string]: any } router?: { path: string; [key: string]: any } } export type NotificationItem = { handleClick?: (event: React.MouseEvent) => void startIcon: { className: string iconItem: JSX.Element } } & BasicListItem export type BasicHeaderItem = { status?: 'disabled' | 'hidden' | 'default' } & BasicListItem export type HeadMenuType = { children?: JSX.Element className?: string allowTrade?: object renderList?: (props: { handleListKeyDown: ({ ...rest }) => any }) => any onOpen?: boolean selected?: boolean setOnOpen?: () => {} toggle?: boolean style?: any layer?: number anchorOrigin?: { vertical: string; horizontal: string } } & I export type MenuItemLink = HeadMenuType & { className?: string allowTrade: any handleListKeyDown?: () => any layer: number } & LinkProps export type MenuItemProps = HeadMenuType & { className?: string handleListKeyDown?: () => any layer: number } & muMenuItemProps export type SubMenuListProps = { selected: string subMenu: { [key: string]: List } } export interface CoinItemProps extends ListItemProps { tokenType?: TokenType itemKey: CoinKey coinInfo: CoinInfo walletCoin: WalletCoin select: CoinKey | null handleListItemClick: (event: React.MouseEvent, selected: CoinKey) => void contentEle?: (props: any) => JSX.Element } export type CoinMenuProps = { tokenType?: TokenType listProps?: ListProps | any selected: CoinKey | null nonZero: boolean sorted: boolean filterString: string height?: string allowScroll?: boolean //boolean filterBy: (coinInfo: CoinInfo, filterString: string) => boolean coinMap: CoinMap ? CoinInfo : CoinInfo> walletMap: WalletMap ? WalletCoin : WalletCoin> | {} handleSelect?: (event: React.MouseEvent, selected: CoinKey) => void className?: string filterWithBorrowed?: boolean } export type CollectionListProps = { onPageChange: (page: number, filter?: L2CollectionFilter | undefined) => void collectionList: Co[] size?: 'large' | 'medium' | 'small' total: number domain: string makeMeta: MakeMeta page: number copyToastOpen: { isShow: boolean; type: string } setCopyToastOpen: (props: { isShow: boolean; type: string }) => void isLoading: boolean etherscanBaseUrl: string baseURL: string getIPFSString: GET_IPFS_STRING filter?: Partial } export type CollectionItemProps = { item: Co index: number size?: 'large' | 'medium' | 'small' setCopyToastOpen: (prosp: { isShow: boolean; type: string }) => void setShowDeploy?: (item: Co) => void setShowEdit?: (item: Co) => void setShowManageLegacy?: (item: Co) => void setShowTradeIsFrozen?: (item: Co, type: string) => void setShowMintNFT?: (item: Co) => void onItemClick?: (item: Co) => void account?: Account toggle: any isSelectOnly?: boolean noEdit?: boolean selectCollection?: Co domain: string makeMeta: MakeMeta baseURL: string getIPFSString: GET_IPFS_STRING etherscanBaseUrl: string } ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/List.stories.tsx ================================================ import { Meta, Story } from '@storybook/react' import styled from '@emotion/styled' import { MemoryRouter } from 'react-router-dom' import { HeaderMenu, HeaderMenuSub, HeadMenuItem, Layer2Item } from './HeadMenuItem' import { withTranslation } from 'react-i18next' import { Box, Divider, GlobalStyles, Grid, IconButton, List as MuiList, ListItemAvatar, ListItemText, MenuItem, Tab, Tabs, Typography, } from '@mui/material' import { coinMap, CoinType, layer2ItemData, walletMap } from '../../../static' import { CoinMenu } from './CoinList' import { ACTIVITY, AssetsIcon, CoinInfo, DownloadIcon, globalCss, NOTIFICATION_ITEM, } from '@loopring-web/common-resources' import React from 'react' import { SubMenuItem } from './SubMenuList' import { TabItemPlus } from './HeadToolbar' import { ListItemActivity, NotificationListItem } from './Notification' import { useTheme } from '@emotion/react' const BtnDownload = ({ t }: any) => ( ) const Style = styled.div` background: var(--color-global-bg); ` const SubMenu = withTranslation('layout')((rest: any) => { return ( <> Assets } /> Assets } /> AMM Records } secondary={ (Joins & Exits ) } /> AMM Records } secondary={ (Joins & Exits ) } /> ) }) const LoopringHeader = (wrap: any) => { const layer2ItemDemo = layer2ItemData.map(({ label, router, child }) => ( )) return ( <> {wrap.t('markets')} {wrap.t('layer 2')} layer2ItemDemo} layer={0} key={'trade-0'} label={{ id: 'trade', i18nKey: 'trade', icon: '' }} {...wrap} />
      {layer2ItemDemo}
    ) } const CoinMapMenu = (rest: any) => { return ( > {...{ coinMap, walletMap, handleListItemClick: (_event: any) => { // console.log('handleListItemClick', key) }, ...rest, }} /> ) } const TabPanelBtn = () => { const [value, setValue] = React.useState('one') const handleChange = (_event: any, newValue: any) => { setValue(newValue) } return ( ) } const Template: Story = withTranslation()(({ t }: any) => { const theme = useTheme() const activity: ACTIVITY = { type: '', link: `2021/12/2021-12-23`, title: '🎄 Loopring Holiday Trading Giveaway', description1: 'Loopring Holiday Trading Giveaway,$600,000 in Prizes!\n', description2: 'Period: 2021-12-23 12AM to 2021-12-31 12AM (UAT)', startDate: 1639526400000, endDate: 1612915200000, pairs: ['LRC-ETH'], // config: [string, number, number, number, number]; // giftIcon?: string; } const notify: NOTIFICATION_ITEM = { name: '', type: '', version: '', title: '路印中继系统升级', description1: 'Loopring Relayer System Upgrade Notice', description2: 'Period: 2021-12-23 12AM to 2021-12-31 12AM (UAT)', link: `2021/12/2021-12-23`, startShow: 1639526400000, endShow: 1612915200000, } return ( ) }) as Story // @ts-ignore export const ListItem = Template.bind({}) ListItem.args = {} export default { title: 'basic-lib/ListItem', component: ListItem, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/NFTList.tsx ================================================ import { Box, Checkbox, Grid, MenuItem, Pagination, Popover, Radio, Typography, } from '@mui/material' import { Account, CollectionMeta, EmptyValueTag, GET_IPFS_STRING, getShortAddr, NFTLimit, NFTWholeINFO, sizeNFTConfig, SoursURL, ViewMoreIcon, } from '@loopring-web/common-resources' import { AccountStep, CardStyleItem, EmptyDefault, NFTMedia } from '../../index' import { useTranslation, WithTranslation, withTranslation } from 'react-i18next' import { sanitize } from 'dompurify' import { IconButtonStyle } from '../../../' import { ToggleState, useSettings } from '../../../stores' import { XOR } from '../../../types/lib' import React from 'react' import { bindMenu, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks' import styled from '@emotion/styled' import { DEPLOYMENT_STATUS } from '@loopring-web/loopring-sdk' const BoxBtnGroup = styled(Box)` position: absolute; right: ${({ theme }) => 2 * theme.unit}px; top: ${({ theme }) => 2 * theme.unit}px; z-index: 99; //flex-direction: row-reverse; &.mobile { } ` as typeof Box export type NFTItemBasicProps = { toggle?: ToggleState setNFTMetaNotReady: (props: any) => void setShowNFTDeploy: (props: any) => void setShowNFTDetail: (props: any) => void setShowNFTTransfer: (props: any) => void setShowNFTWithdraw: (props: any) => void setShowTradeIsFrozen: (props: any) => void setShowRedPacket: (props: any) => void setShowAccount: (props: any) => void } const ActionMemo = React.memo( ({ account, toggle, setShowNFTDeploy, // setShowNFTDetail, setShowNFTTransfer, setShowNFTWithdraw, setShowAccount, setShowTradeIsFrozen, setNFTMetaNotReady, setShowRedPacket, item, }: NFTItemBasicProps & { item: NFT; account?: Account }) => { const { t } = useTranslation('common') const popupState = usePopupState({ variant: 'popover', popupId: 'collection-action', }) const bindContent = bindMenu(popupState) const bindAction = bindTrigger(popupState) const [isKnowNFTNoMeta, setIsKnowNFTNoMeta] = React.useState( !!(item?.name !== '' && item.image && item.image !== ''), ) React.useEffect(() => { setIsKnowNFTNoMeta((_state) => { return !!(item.name !== '' && item.image && item.image !== '') }) }, [item.name, item.image]) return ( {!!( item.isCounterFactualNFT && item.deploymentStatus === DEPLOYMENT_STATUS.NOT_DEPLOYED && item.minter?.toLowerCase() === account?.accAddress.toLowerCase() ) && ( toggle?.deployNFT?.enable ? setShowNFTDeploy({ ...item, }) : setShowTradeIsFrozen({ isShow: true, type: t('nftDeployDescription'), }) } > {t('labelNFTDeployContract')} )} { setShowNFTTransfer({ ...item }) setShowNFTWithdraw({ ...item }) isKnowNFTNoMeta ? setShowAccount({ isShow: true, step: AccountStep.SendNFTGateway, info: { ...item }, }) : setNFTMetaNotReady({ isShow: false, info: { method: 'Send' }, }) }} > {t('labelNFTSendBtn')} { isKnowNFTNoMeta ? setShowRedPacket({ ...item }) : setNFTMetaNotReady({ isShow: false, info: { method: 'Send' }, }) }} > {t('labelNFTRedpacketBtn')} ) }, ) export const NFTList = withTranslation('common')( ({ baseURL, nftList, getIPFSString, size = 'large', total, page, isLoading, onClick, selected = undefined, isSelectOnly = false, isMultipleSelect = false, onPageChange, setNFTMetaNotReady, isEdit, t, ...props }: { getIPFSString: GET_IPFS_STRING baseURL: string isManage?: boolean nftList: Partial[] etherscanBaseUrl?: string size?: 'large' | 'medium' | 'small' onClick?: (item: Partial) => Promise onNFTReload?: (item: Partial, index: number) => Promise total: number page: number isLoading: boolean selected?: Partial[] onPageChange?: (page: number) => void setNFTMetaNotReady: (props: any) => void account?: Account toggle?: any collectionMeta?: Co // onSelected: (item: Partial) => void; } & ((NFTItemBasicProps & { isEdit: true }) | { isEdit?: false }) & XOR< { isSelectOnly: true; isMultipleSelect: true; selected: Partial[] }, | { isSelectOnly: true; isMultipleSelect?: false; selected: NFT } | { isSelectOnly?: false isMultipleSelect?: false } > & WithTranslation) => { const sizeConfig = sizeNFTConfig(size) const { isMobile } = useSettings() return ( {isLoading ? ( {'loading'} ) : nftList && nftList.length ? ( <> {nftList.map((item, index) => ( {isEdit && ( )} { e.isPropagationStopped() onClick && onClick(item) }} > undefined} isOrigin={false} getIPFSString={getIPFSString} baseURL={baseURL} /> {isSelectOnly && (isMultipleSelect ? ( { return item.nftData === _item.nftData }) } // color={"default"} value={item.nftData} name='radio-nft' inputProps={{ 'aria-label': 'selectNFT' }} /> ) : ( ))} {t('labelNFTTokenID')} #{' ' + getShortAddr(item?.nftId ?? '')} {t( size == 'small' ? 'labelNFTAmountSimpleValue' : 'labelNFTAmountValue', { value: item.total }, )} ))} {total > NFTLimit && onPageChange && ( 0 ? 1 : 0)} page={page} onChange={(_event, value) => { onPageChange(Number(value)) }} /> )} ) : ( ( No NFT )} /> )} ) }, ) ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/Notification.tsx ================================================ import { Box, ListItem, ListItemAvatar, ListItemProps, ListItemText } from '@mui/material' import styled from '@emotion/styled' import { Account, ACTIVITY, hexToRGB, languageMap, NOTIFICATION_ITEM, NOTIFY_COLOR, ThemeType, } from '@loopring-web/common-resources' import { css, Theme } from '@emotion/react' import { useHistory } from 'react-router-dom' import { useSettings } from '../../../stores' import * as sdk from '@loopring-web/loopring-sdk' const cssBackground = ({ theme, color, banner, bannerDark, lng, }: { theme: Theme; lng: string } & Partial) => { let svg: string, _color: string const fillColor = theme.colorBase.textDisable const _banner: string | undefined = theme.mode === ThemeType.dark && bannerDark?.length ? bannerDark : banner const opacity = 0.2 if (banner) { return css` text-indent: -99999em; &, &:hover { background: url('${_banner?.replace('{lng}', lng)}'); background-size: cover; } &:hover { filter: blur(0.6px); box-shadow: var(--shadow-hover); } ` } switch (color) { case NOTIFY_COLOR.primary: _color = theme.colorBase.warning svg = encodeURI(` `) break case NOTIFY_COLOR.secondary: _color = theme.colorBase.success svg = encodeURI(` `) break case NOTIFY_COLOR.tertiary: _color = theme.colorBase.error svg = encodeURI(` `) break case NOTIFY_COLOR.default: _color = theme.colorBase.primary svg = encodeURI(` `) break default: _color = theme.colorBase.box svg = '' } return css` &, &:hover { background-color: ${hexToRGB(_color, 0.25)}; background-image: url('data:image/svg+xml, ${svg}'); background-repeat: no-repeat; background-position-x: 100%; } &:hover { background-color: ${hexToRGB(_color, 0.45)}; } ` } const NotificationListItemStyled = styled(ListItem)< ListItemProps & Partial & { lng: string; bannerDark: string } >` cursor: pointer; height: var(--notification-activited-heigth); width: calc(var(--notification-activited-heigth) * 343 / 80); overflow: hidden; padding: ${({ theme }) => theme.unit}px ${({ theme }) => theme.unit}px; background-color: var(--opacity); &:not(:last-child) { border-bottom: 1px solid var(--color-divide); // margin-bottom: ${({ theme }) => theme.unit}px; } .MuiListItemText-root { margin-top: 0; } .description { text-overflow: ellipsis; word-break: break-all; white-space: nowrap; width: 100%; } .MuiListItemAvatar-root { width: 1em; height: 100%; } ${(props) => cssBackground(props)} ` as (props: ListItemProps & Partial & { lng: string }) => JSX.Element export const NotificationListItem = ( props: Partial & { closePop?: () => void account?: Account chainId: sdk.ChainId }, ) => { const history = useHistory() const { language } = useSettings() const lng = languageMap[language] const { title, description1, description2, account, banner, bannerDark, webRouter, link, linkParam, chainId, closePop, } = props return ( { const hasParamTag = /\?/.test(link ?? '') let params = '' if (linkParam) { params = linkParam.split('&').reduce((pre, ele) => { const [key, value] = ele.split('=') const [_, valueKey] = value.match(/\{(.*)\}/) ?? [] let _value: any = '' switch (valueKey) { case 'chainId': _value = chainId break case 'l2address': _value = account?.accAddress break } return `${pre}&${key}=${_value}` }, '') } if (webRouter) { const [_, target, router] = webRouter.match(/\[(.*)\](.*)/i) ?? [] if (router && target === 'self') { history.push(router) } else { window.open( router ? `https://loopring.io/#/${router}` : `${link}${hasParamTag ? '' : '?'}` + `${params}`, '_blank', ) window.opener = null } } else if (link) { window.open(`${link}${hasParamTag ? '' : '?'}` + `${params}`, '_blank') window.opener = null } closePop && closePop() }} className={`notification`} > } primaryTypographyProps={{ component: 'h4', color: 'textPrimary', title, }} /> } primaryTypographyProps={{ component: 'p', variant: 'body1', textOverflow: 'ellipsis', title: description1, color: 'textPrimary', overflow: 'hidden', }} /> } primaryTypographyProps={{ component: 'p', variant: 'body2', textOverflow: 'ellipsis', title: description2, color: 'textSecondary', overflow: 'hidden', }} /> ) } const ListItemActivityStyle = styled(NotificationListItemStyled)< ListItemProps & Partial & { lng: string } >` &:not(:last-child) { border-bottom: 0; margin-bottom: ${({ theme }) => theme.unit}px; } padding: ${({ theme }) => theme.unit}px; ${(props) => cssBackground(props)} border-radius: ${({ theme }) => theme.unit}px; ` as (props: ListItemProps & Partial & { lng: string }) => JSX.Element export const ListItemActivity = (props: ACTIVITY & { account?: Account; chainId: sdk.ChainId }) => { const { type, title, description1, description2, startShow, link, account, banner, color, webRouter, linkParam, chainId, } = props const { language } = useSettings() const lng = languageMap[language] const history = useHistory() if (Date.now() > startShow) { return ( // history.replace(``) // } onClick={() => { const hasParamTag = /\?/.test(link) let params = '' if (linkParam) { params = linkParam.split('&').reduce((pre, ele) => { const [key, value] = ele.split('=') const [_, valueKey] = value.match(/\{(.*)\}/) ?? [] let _value: any = '' switch (valueKey) { case 'chainId': _value = chainId break case 'l2address': _value = account?.accAddress break } return `${pre}&${key}=${_value}` }, '') } if (webRouter) { const [_, target, router] = webRouter.match(/\[(.*)\](.*)/i) ?? [] if (router && target === 'self') { history.push(router) } else { window.open( router ? `https://loopring.io/#/${router}` : `${link}${hasParamTag ? '' : '?'}` + `${params}`, '_blank', ) window.opener = null } } else if (link) { window.open(`${link}${hasParamTag ? '' : '?'}` + `${params}`, '_blank') window.opener = null } }} type={props.type} > ) } else { return <> } } ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/SubMenuList.tsx ================================================ import styled from '@emotion/styled' import { Box, Divider, ListItem, ListItemAvatar, ListItemProps, ListItemText, Typography, } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { SubMenuListProps } from './Interface' import { Link as RouterLink } from 'react-router-dom' import { L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import React from 'react' export const SubMenuItem = styled(ListItem)` border-left: 2px solid transparent; border-right: 0px solid transparent; padding: 0 0 0 ${({ theme }) => theme.unit * 3}px; width: var(--sub-menuItem-width); min-width: var(--sub-menuItem-width); height: var(--sub-menuItem-height); color: var(--color-text-secondary); text-transform: capitalize; .MuiListItemAvatar-root { margin-left: ${({ theme }) => theme.unit * 0.75}px; color: var(--color-text-third); svg { width: var(--header-menu-icon-size); height: var(--header-menu-icon-size); } } //&:hover, //&.Mui-selected, //&.Mui-selected.Mui-focusVisible, //&.Mui-selected:hover { //} &:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible, &.Mui-selected.Mui-focusVisible:hover { background-color: var(--color-box-hover); border-color: var(--color-primary); color: var(--color-button-select); &&, .MuiListItemAvatar-root { color: var(--color-button-select); } } //&.Mui-selected, &.Mui-selected.Mui-focusVisible { // background-color: var(--color-primary); // color: var(--color-text-button); // &:hover{ // background-color: var(--color-primary); // color: var(--color-text-button); // } // //border-color:var(--primary); // //} ` as (props: ListItemProps) => JSX.Element export const SubMenuList = withTranslation(['layout', 'common'], { withRef: true, })(({ t, selected, subMenu }: SubMenuListProps & WithTranslation<'layout'>) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const filteredSubMenus = Object.keys(subMenu).filter(key => subMenu[key]) return ( <> {filteredSubMenus.map((list: any, index) => { const subList = subMenu[list]?.map((item: any) => { return ( {item.label.description ? ( {t(item.label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, })} } secondary={ {t(item.label.description)} } /> ) : ( {t(item.label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, })} } /> )} ) }) return ( <> {subList ? (
    {subList} {index + 1 !== filteredSubMenus.length ? ( ) : ( '' )}
    ) : ( )} ) })} ) }) ================================================ FILE: packages/component-lib/src/components/basic-lib/lists/index.tsx ================================================ import { Card, CardProps, IconButton, MenuItem as MuiMenuItem, Select, SelectProps, } from '@mui/material' import styled from '@emotion/styled' import { MuiMenuItemProps } from './Interface' import React from 'react' import { BorderTickIcon, SystemColor } from '@loopring-web/common-resources' import { useTheme } from '@emotion/react' export const MenuItem = styled(MuiMenuItem)` ${({ withnocheckicon }) => { return withnocheckicon === 'true' ? ` &.Mui-selected, &.Mui-selected.Mui-focusVisible { color: var(--color-text-primary); &:after{ display:none; } } ` : '' }} ` as React.ComponentType export const OutlineSelect = styled(Select)` //padding: 0; min-width: var(--btn-min-width); //background-color: transparent; color: var(--color-text-secondary); .MuiInput-input { padding: 0.3rem 0.3rem 0.3rem 0.8rem; } border: transparent; .MuiSelect-select, &.Mui-selected.Mui-focusVisible { border: transparent; &:focus { background-color: transparent; } &:before { content: ''; display: none; pointer-events: none; } } &:hover { color: var(--color-text-primary); border: transparent; } input { padding-right: 0; } &:hover:not(.Mui-disabled):before, &:after, &:before { margin: 0 auto; width: 60%; border: 0; pointer-events: none; } ` as React.ComponentType export const OutlineSelectItem = styled(MenuItem)` &.MuiSelect-root { padding: ${({ theme }) => `0 ${theme.unit * 1} $0 ${theme.unit * 1} `}; padding-right: ${({ theme }) => `${theme.unit * 2}`}; &:hover { color: var(--color-text-primary); border-left-color: transparent; } } &.Mui-selected, &.Mui-selected.Mui-focusVisible { padding: ${({ theme }) => `${theme.unit * 1} ${theme.unit * 1} 0 ${theme.unit * 1} `}; padding-right: ${({ theme }) => `${theme.unit * 2}`}; &:after { content: ''; } } ` as typeof MenuItem export const IconButtonStyle = styled(IconButton)` background-color: var(--color-box-nft-btn); margin: 0 ${({ theme }) => theme.unit / 2}px; ${({ theme }) => theme.border.defaultFrame({ c_key: 'transparent' })}; }` export const CardStyleItem = styled(Card)< CardProps & { contentheight?: number size?: 'large' | 'medium' | 'small' | undefined } >` background: var(--color-global-bg); width: 100%; cursor: pointer; height: auto; display: flex; cursor: pointer; padding: 0 0 calc(100% + ${({ contentheight }) => `${contentheight ? contentheight : 80}px`}); position: relative; .boxLabel { overflow: hidden; } &.collection { padding: 0 0 calc(140%); .boxLabel { ${({ size, theme }) => size === 'small' ? ` padding: ${1 * theme.unit}px; margin:0; ` : ` .content{ width:60%; } padding: ${2 * theme.unit}px; margin: ${2 * theme.unit}px;`} } } &.nft-item { .MuiRadio-root, .MuiCheckbox-root { &:hover { background-color: rgba(65, 105, 255, 0.05); color: var(--color-text-secondary); } &.Mui-checked { box-shadow: inset 0px 0px 60px var(--color-global-bg-opacity); } position: absolute; right: ${({ theme }) => theme.unit}px; top: ${({ theme }) => theme.unit}px; transform: scale(1.5); } background-color: var(--color-box-secondary); } &.btnCard { background: var(--color-box); .MuiCardContent-root { padding: ${({ theme }) => theme.unit}px; } .MuiCardContent-root { display: flex; flex-direction: row; align-items: center; justify-content: center; } &.column .MuiCardContent-root { flex-direction: column; } height: auto; display: flex; align-items: center; justify-content: center; padding: 0px; box-shadow: none; transition: none; ${({ theme }) => theme.border.defaultFrame({ c_key: 'var(--color-border)', d_R: 0.5, })}; .Mui-selected &, &.selected, &:hover { ${({ theme }) => theme.border.defaultFrame({ c_key: 'var(--color-border-select)', d_R: 0.5, })}; &:after { display: none; } } } img { object-fit: contain; } &.dualPrice { padding: ${({ theme }) => theme.unit}px; height: 54px; } &.MuiPaper-root.dualInvestCard { padding: 0; background: var(--color-box); &.selected, &:hover { padding: inherit; } .MuiCardContent-root { box-sizing: border-box; padding: ${({ theme }) => 2 * theme.unit}px ${({ theme }) => 3 * theme.unit}px; &:last-child { padding: ${({ theme }) => 2 * theme.unit}px ${({ theme }) => 3 * theme.unit}px; } } } ` as ( props: CardProps & { contentheight?: number size?: 'large' | 'medium' | 'small' | undefined }, ) => JSX.Element export const TickCardStyleItem = ( props: CardProps & { contentheight?: number size?: 'large' | 'medium' | 'small' | undefined selected?: boolean width?: string }, ) => { const { children, selected, width, ...rest } = props const theme = useTheme() return ( {selected && ( )} {children} ) } export * from './FileListItem' export * from './HeadMenuItem' export * from './Interface' export * from './CoinList' export * from './HeadToolbar' export * from './SubMenuList' export * from './Notification' export * from './CollectionItem' export * from './NFTList' // export * from './SimpleSelectItem' ================================================ FILE: packages/component-lib/src/components/basic-lib/loading/index.tsx ================================================ import { SoursURL } from '@loopring-web/common-resources' import { Box, BoxProps } from '@mui/material' export type LoadingProps = { size?: number withContainer?: boolean } export const Loading = (props: BoxProps & LoadingProps) => { const { size = 40, withContainer, ...rest } = props const loading = ( ) return withContainer ? ( {loading} ) : ( loading ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/media/Interface.ts ================================================ import { NFTWholeINFO } from '@loopring-web/common-resources' import { ReactEventHandler } from 'react' export type NftImageProps = { onError: ReactEventHandler } & Partial & Partial ================================================ FILE: packages/component-lib/src/components/basic-lib/media/index.ts ================================================ export * from './nftImage' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/basic-lib/media/nftImage.tsx ================================================ import { NftImageProps } from './Interface' import { css, Theme } from '@emotion/react' import styled from '@emotion/styled' import { Box } from '@mui/material' import { SoursURL } from '@loopring-web/loopring-sdk' export const NftImage = (props: NftImageProps & any) => { return ( {props.name ) } export const NftImageStyle = (props: { src: string | undefined; style?: React.CSSProperties }) => { return ( {'NFT'} ) } export const cssBackground = (_props: { theme: Theme }) => { // const fillColor = theme.colorBase.textDisable.replace("#", "%23"); // const _svg = // encodeURI(` // // // // `); // background-image: url("data:image/svg+xml, ${svg}"); return css` flex: 1; background-color: var(--color-box); background-repeat: no-repeat; background-clip: content-box; background-size: contain; background-position: 50% 50%; align-self: stretch; ` } export const BoxNFT = styled(Box)` background: no-repeat 50% 50%; background-color: var(--opacity); background-image: url(${SoursURL + 'svg/loopring.svg'}); &.redPacketNFT { //height: var(--nft-large-avatar); width: var(--nft-large-avatar); padding-top: var(--nft-large-avatar); } img { object-fit: contain; overflow: hidden; border-radius: ${({ theme }) => theme.unit}px; } ` as typeof Box ================================================ FILE: packages/component-lib/src/components/basic-lib/panel/IPFSSourceUpload.tsx ================================================ import { Box, ButtonProps, FormControl, FormHelperText, IconButton, Link, Typography, TypographyProps, } from '@mui/material' import { DropzoneOptions, useDropzone } from 'react-dropzone' import styled from '@emotion/styled' import { AudioIcon, CloseIcon, ErrorIcon, GET_IPFS_STRING, hexToRGB, ImageIcon, Media, myLog, PlayIcon, SoursURL, ThreeDIcon, VideoIcon, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import React from 'react' import { useTheme } from '@emotion/react' import { NftImage } from '../media' export const TYPES = ['image/jpeg', 'image/jpg', 'image/gif', 'image/png'] export const MediaTYPES = [ 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp', 'image/png', 'audio/mp3', 'audio/mpeg', 'video/mp4', 'video/mpeg4', 'model', 'model/glb', '', ] export const getMediaType = (type: string): Media | undefined => { if (/audio/gi.test(type ?? '')) { return Media.Audio } if (/video/gi.test(type ?? '')) { return Media.Video } if (/(model)/gi.test(type ?? '')) { return Media.Media3D } if (/image/gi.test(type ?? '')) { return Media.Image } return } const BoxStyle = styled(Box)` ${({ theme }) => theme.border.defaultFrame({ c_key: theme.colorBase.divide, d_W: 0, d_R: 1, })}; background: ${({ theme }) => theme.colorBase.globalBg}; width: 100%; height: 100%; border-style: dashed; .MuiFormHelperText-sizeMedium { font-size: ${({ theme }) => theme.fontDefault.body2}; color: var(--color-error); } opacity: 0.8; &.focused, &:hover { opacity: 0.95; } ` as typeof Box const LinkStyle = styled(Link)` ${({ theme }) => theme.border.defaultFrame({ c_key: theme.colorBase.divide, d_W: 0, d_R: 1, })}; .media-content { height: 100%; width: 100%; } ` as typeof Link export type IpfsFile = { file: File isProcessing: boolean error: | { [key: string]: any } | undefined uniqueId: string isUpdateIPFS: boolean cid?: string localSrc?: string fullSrc?: string } export const IPFS_INIT: Partial = { file: undefined, isProcessing: false, error: undefined, isUpdateIPFS: false, } export const MediaSVGToggle = ({ url, play, getIPFSString, baseURL, mediaTyp, setPlay, isShow, shouldPlay = false, }: { url: string play: boolean getIPFSString: GET_IPFS_STRING baseURL: string mediaTyp: Media | undefined setPlay: (props: boolean) => void isShow: boolean shouldPlay: boolean }) => { const theme = useTheme() const vidRef = React.useRef(null) const aidRef = React.useRef(null) const d3Ref = React.useRef(null) const typeSvg = React.useMemo(() => { myLog('item.__mediaType__', mediaTyp) switch (mediaTyp) { case Media.Audio: return ( <> {url && shouldPlay && ( )} ) case Media.Video: return ( <> {url && shouldPlay && ( { e.stopPropagation() setPlay(true) }} className={'media-content'} > {play ? ( )} ) case Media.Media3D: return ( <> {url && shouldPlay && ( )} ) case Media.Image: return ( <> {url && shouldPlay && ( { e.stopPropagation() // setPlay(true); }} className={'media-content'} > { e?.target?.style?.setItem('opacity', '0.3') e?.target?.setItem('alt', 'image loaded failed') }} src={getIPFSString(url, baseURL)} /> )} ) default: return <> } }, [mediaTyp, url, play, theme.unit]) React.useEffect(() => { if (isShow === false) { if (vidRef.current) { vidRef.current.pause() } if (aidRef.current) { aidRef.current.pause() } if (d3Ref.current) { d3Ref.current.pause() } } return () => { if (vidRef.current) { vidRef.current.pause() } if (aidRef.current) { aidRef.current.pause() } if (d3Ref.current) { d3Ref.current.pause() } } }, [isShow]) return <>{typeSvg} } export const IPFSSourceUpload = ({ value, onChange, width, height, fullSize = false, title = 'labelLoadDes', buttonText = 'labelUpload', typographyProps, buttonProps, disabled, maxSize = 10485760, onDelete, types = TYPES, getIPFSString, messageTypes, baseURL, ...options }: Omit & { // sx?: SxProps; fullSize?: boolean width?: number | string messageTypes?: string[] height?: number | string typographyProps?: TypographyProps buttonProps?: Omit title?: string buttonText?: string value: IpfsFile | undefined types?: string[] onDelete: () => void onChange: (files: IpfsFile) => void getIPFSString: GET_IPFS_STRING baseURL: string }) => { const { t } = useTranslation() const onDropAccepted = React.useCallback( (file: File[]) => { onChange({ file: file[0], isProcessing: true, error: undefined, localSrc: URL.createObjectURL(file[0]), isUpdateIPFS: false, uniqueId: Date.now().toString() + file[0].lastModified, }) }, [onChange], ) const { fileRejections, getRootProps, getInputProps, open, isFocused } = useDropzone({ ...options, disabled, maxSize, accept: types?.length ? types : undefined, onDropAccepted, noClick: true, noKeyboard: true, }) const isFileTooLarge = maxSize !== undefined && fileRejections.length > 0 && fileRejections[0].file.size > maxSize const close = React.useMemo( () => ( ), [onDelete, t], ) return ( {value ? ( value.isProcessing ? ( {'loading'} {close} ) : value.error ? ( {t(value.error.message)} {close} ) : ( <> {value && value.fullSrc ? ( {}} mediaTyp={getMediaType(value.file?.type)} getIPFSString={getIPFSString} baseURL={baseURL} // isShow={true} /> ) : ( <> )} Successfully Uploaded {close} ) ) : ( {t(title, { types: (messageTypes ?? types) ?.reduce((prev, ele) => { const split = ele?.split('/') if (split.length === 2) { return [...prev, split[1]] } return prev }, [] as string[]) ?.join(', '), size: (maxSize / 1000000).toFixed(0), })} {fileRejections[0]?.errors[0]?.message} )} ) } //${({ theme }) => // // padding: ${({ theme }) => 2 * theme.unit}px; //background: ${({ theme }) => hexToRGB(theme.colorBase.textPrimary, ".6")}; const PlayIconStyle = styled(PlayIcon)` color: ${({ theme }) => hexToRGB(theme.colorBase.box, '.9')}; border-radius: 100%; box-shadow: inset 0px 0px 60px ${({ theme }) => hexToRGB(theme.colorBase.textPrimary, '.7')}; padding: ${({ theme }) => 1 * theme.unit}px; ` ================================================ FILE: packages/component-lib/src/components/basic-lib/panel/Interface.ts ================================================ export type PanelContent = { key: T element: JSX.Element toolBarItem: JSX.Element | undefined } export type SwitchPanelProps = { // swipedBy: T, index: number // defaultIndex: number, panelList: Array> size?: string className?: string // onChangeIndex?: (index: number,data:any) => void, // onTransitionEnd?: () => void, _height?: number | string _width?: number | string scrollDisabled?: boolean } ================================================ FILE: packages/component-lib/src/components/basic-lib/panel/Panel.stories.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { withTranslation } from 'react-i18next' import { SwitchPanel } from './SwitchPanel' import { MemoryRouter } from 'react-router-dom' import { Box, Button, Grid, IconButton, ListItemAvatar, ListItemText, Typography, } from '@mui/material' import { AssetsIcon, DropDownIcon } from '@loopring-web/common-resources' import { SubMenuItem } from '../lists' import { SubMenu, SwitchPanelProps } from '.' const Style = styled.div` background: var(--color-global-bg); height: 100%; flex: 1; ` const FistWrap = ({ onChangeIndex }: any) => { // const _tradeValue = tradeValue as {buy:IBData|null,sell:IBData|null} ; // let information = {value: "hello, it's callback inject"}; // setTimeout(() => { // information.value = 'I have update'; // }, 100); const handleOnClick = React.useCallback( (_event) => { onChangeIndex(1, { data: 'any' }) }, [onChangeIndex], ) return ( ) } const SecondWrap = () => { return } const WrapSwitchPanel = (rest: any) => { const [index, setIndex] = React.useState(0) const onChangeIndex = React.useCallback((index: 0 | 1, value: any) => { setIndex(index) console.log(value) }, []) const onTransitionEnd = () => { console.log('onTransitionEnd') } const props: SwitchPanelProps<'coinMap' | 'trade'> = { index: index, // show default show panelList: [ { key: 'trade', element: , toolBarItem: ( {/**/} toolbar {/**/} {/**/} {/**/} {/* {'loading'}/*/} {/**/} {/**/} ), }, { key: 'coinMap', element: , toolBarItem: ( onChangeIndex(0, { data: 0 })} aria-label='to Professional' > ), }, ], } return } const Template: Story = withTranslation()((props: any) => { return ( ) }) as Story export default { title: 'basic-lib/Panel', component: SwitchPanel, argTypes: {}, } as Meta export const PanelStory = Template.bind({}) ================================================ FILE: packages/component-lib/src/components/basic-lib/panel/QRCodeUpload.tsx ================================================ import { Box } from '@mui/material' import styled from '@emotion/styled' import { QRCODE_REGION_ID } from '@loopring-web/common-resources' import React from 'react' const BoxStyle = styled(Box)` ${({ theme }) => theme.border.defaultFrame({ c_key: theme.colorBase.divide, d_W: 0, d_R: 1, })}; background: ${({ theme }) => theme.colorBase.globalBg}; width: 100%; height: 100%; border-style: dashed; &#qrcodeRegionId { border: 0 !important; } font-size: ${({ theme }) => theme.fontDefault.body1}; .html5-qrcode-element{ margin: ${({ theme }) => theme.unit}px auto; } #qrcodeRegionId__scan_region{ img{ //opacity: 0 !important; width: 0; height: 0; } position: relative; &:after{ background: var(--color-logo); display: block; mask: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzNzEuNjQzIDM3MS42NDMiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDM3MS42NDMgMzcxLjY0MyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTEwNS4wODQgMzguMjcxaDE2My43Njh2MjBIMTA1LjA4NHoiLz48cGF0aCBkPSJNMzExLjU5NiAxOTAuMTg5Yy03LjQ0MS05LjM0Ny0xOC40MDMtMTYuMjA2LTMyLjc0My0yMC41MjJWMzBjMC0xNi41NDItMTMuNDU4LTMwLTMwLTMwSDEyNS4wODRjLTE2LjU0MiAwLTMwIDEzLjQ1OC0zMCAzMHYxMjAuMTQzaC04LjI5NmMtMTYuNTQyIDAtMzAgMTMuNDU4LTMwIDMwdjEuMzMzYTI5LjgwNCAyOS44MDQgMCAwIDAgNC42MDMgMTUuOTM5Yy03LjM0IDUuNDc0LTEyLjEwMyAxNC4yMjEtMTIuMTAzIDI0LjA2MXYxLjMzM2MwIDkuODQgNC43NjMgMTguNTg3IDEyLjEwMyAyNC4wNjJhMjkuODEgMjkuODEgMCAwIDAtNC42MDMgMTUuOTM4djEuMzMzYzAgMTYuNTQyIDEzLjQ1OCAzMCAzMCAzMGg4LjMyNGMuNDI3IDExLjYzMSA3LjUwMyAyMS41ODcgMTcuNTM0IDI2LjE3Ny45MzEgMTAuNTAzIDQuMDg0IDMwLjE4NyAxNC43NjggNDUuNTM3YTkuOTg4IDkuOTg4IDAgMCAwIDguMjE2IDQuMjg4IDkuOTU4IDkuOTU4IDAgMCAwIDUuNzA0LTEuNzkzYzQuNTMzLTMuMTU1IDUuNjUtOS4zODggMi40OTUtMTMuOTIxLTYuNzk4LTkuNzY3LTkuNjAyLTIyLjYwOC0xMC43Ni0zMS40aDgyLjY4NWMuMjcyLjQxNC41NDUuODE4LjgxNSAxLjIxIDMuMTQyIDQuNTQxIDkuMzcyIDUuNjc5IDEzLjkxMyAyLjUzNCA0LjU0Mi0zLjE0MiA1LjY3Ny05LjM3MSAyLjUzNS0xMy45MTMtMTEuOTE5LTE3LjIyOS04Ljc4Ny0zNS44ODQgOS41ODEtNTcuMDEyIDMuMDY3LTIuNjUyIDEyLjMwNy0xMS43MzIgMTEuMjE3LTI0LjAzMy0uODI4LTkuMzQzLTcuMTA5LTE3LjE5NC0xOC42NjktMjMuMzM3YTkuODU3IDkuODU3IDAgMCAwLTEuMDYxLS40ODZjLS40NjYtLjE4Mi0xMS40MDMtNC41NzktOS43NDEtMTUuNzA2IDEuMDA3LTYuNzM3IDE0Ljc2OC04LjI3MyAyMy43NjYtNy42NjYgMjMuMTU2IDEuNTY5IDM5LjY5OCA3LjgwMyA0Ny44MzYgMTguMDI2IDUuNzUyIDcuMjI1IDcuNjA3IDE2LjYyMyA1LjY3MyAyOC43MzMtLjQxMyAyLjU4NS0uODI0IDUuMjQxLTEuMjQ1IDcuOTU5LTUuNzU2IDM3LjE5NC0xMi45MTkgODMuNDgzLTQ5Ljg3IDExNC42NjEtNC4yMjEgMy41NjEtNC43NTYgOS44Ny0xLjE5NCAxNC4wOTJhOS45OCA5Ljk4IDAgMCAwIDcuNjQ4IDMuNTUxIDkuOTU1IDkuOTU1IDAgMCAwIDYuNDQ0LTIuMzU4YzQyLjY3Mi0zNi4wMDUgNTAuODAyLTg4LjUzMyA1Ni43MzctMTI2Ljg4OC40MTUtMi42ODQuODIxLTUuMzA5IDEuMjI5LTcuODYzIDIuODM0LTE3LjcyMS0uNDU1LTMyLjY0MS05Ljc3Mi00NC4zNDV6bS0yMzIuMzA4IDQyLjYyYy01LjUxNCAwLTEwLTQuNDg2LTEwLTEwdi0xLjMzM2MwLTUuNTE0IDQuNDg2LTEwIDEwLTEwaDE1djIxLjMzM2gtMTV6bS0yLjUtNTIuNjY2YzAtNS41MTQgNC40ODYtMTAgMTAtMTBoNy41djIxLjMzM2gtNy41Yy01LjUxNCAwLTEwLTQuNDg2LTEwLTEwdi0xLjMzM3ptMTcuNSA5My45OTloLTcuNWMtNS41MTQgMC0xMC00LjQ4Ni0xMC0xMHYtMS4zMzNjMC01LjUxNCA0LjQ4Ni0xMCAxMC0xMGg3LjV2MjEuMzMzem0zMC43OTYgMjguODg3Yy01LjUxNCAwLTEwLTQuNDg2LTEwLTEwdi04LjI3MWg5MS40NTdjLS44NTEgNi42NjgtLjQzNyAxMi43ODcuNzMxIDE4LjI3MWgtODIuMTg4em03OS40ODItMTEzLjY5OGMtMy4xMjQgMjAuOTA2IDEyLjQyNyAzMy4xODQgMjEuNjI1IDM3LjA0IDUuNDQxIDIuOTY4IDcuNTUxIDUuNjQ3IDcuNzAxIDcuMTg4LjIxIDIuMTUtMi41NTMgNS42ODQtNC40NzcgNy4yNTEtLjQ4Mi4zNzgtLjkyOS44LTEuMzM1IDEuMjYxLTYuOTg3IDcuOTM2LTExLjk4MiAxNS41Mi0xNS40MzIgMjIuNjg4aC05Ny41NjRWMzBjMC01LjUxNCA0LjQ4Ni0xMCAxMC0xMGgxMjMuNzY5YzUuNTE0IDAgMTAgNC40ODYgMTAgMTB2MTM1LjU3OWMtMy4wMzItLjM4MS02LjE1LS42OTQtOS4zODktLjkxNC0yNS4xNTktMS42OTQtNDIuMzcgNy43NDgtNDQuODk4IDI0LjY2NnoiLz48cGF0aCBkPSJNMTc5LjEyOSA4My4xNjdoLTI0LjA2YTUgNSAwIDAgMC01IDV2MjQuMDYxYTUgNSAwIDAgMCA1IDVoMjQuMDZhNSA1IDAgMCAwIDUtNVY4OC4xNjdhNSA1IDAgMCAwLTUtNXpNMTcyLjYyOSAxNDIuODZoLTEyLjU2VjEzMC44YTUgNSAwIDEgMC0xMCAwdjE3LjA2MWE1IDUgMCAwIDAgNSA1aDE3LjU2YTUgNSAwIDEgMCAwLTEwLjAwMXpNMjE2LjU2OCA4My4xNjdoLTI0LjA2YTUgNSAwIDAgMC01IDV2MjQuMDYxYTUgNSAwIDAgMCA1IDVoMjQuMDZhNSA1IDAgMCAwIDUtNVY4OC4xNjdhNSA1IDAgMCAwLTUtNXptLTUgMjQuMDYxaC0xNC4wNlY5My4xNjdoMTQuMDZ2MTQuMDYxek0yMTEuNjY5IDEyNS45MzZIMTk3LjQxYTUgNSAwIDAgMC01IDV2MTQuMjU3YTUgNSAwIDAgMCA1IDVoMTQuMjU5YTUgNSAwIDAgMCA1LTV2LTE0LjI1N2E1IDUgMCAwIDAtNS01eiIvPjwvc3ZnPg==") space; content: '.'; position: absolute; top: 50%; left: 50%; margin-top: -32px; margin-left: -32px; opacity: .1; //right: 0; //bottom: 0; height: 64px; width: 64px; } } #qrcodeRegionId__header_message{ color: ${({ theme }) => theme.colorBase.error} !important; border: 0 !important;; } //button#html5-qrcode-button-camera-permission{ // display: none; //} button.html5-qrcode-element { position: relative; color: var(--color-text-button); height: 36px; padding: 0 ${({ theme }) => theme.unit}px; background-color: ${({ theme }) => theme.colorBase.primary}; ${({ theme }) => theme.border.defaultFrame({ c_key: theme.colorBase.divide, d_W: 0, d_R: 1 / 2, })}; //box-shadow: "initial"; &:hover { //boxShadow: "initial", &:before { background: ${({ theme }) => theme.colorBase.primaryHover}; content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; }, } &[disabled] { backgroundColor: ${({ theme }) => theme.colorBase.disable}; color: ${({ theme }) => theme.colorBase.textDisable} } } .MuiFormHelperText-sizeMedium { font-size: ${({ theme }) => theme.fontDefault.body2}; color: var(--color-error); } opacity: 0.8; &.focused, &:hover { opacity: 0.95; } ` as typeof Box // const LinkStyle = styled(Link)` // ${({ theme }) => // theme.border.defaultFrame({ // c_key: theme.colorBase.divide, // d_W: 0, // d_R: 1, // })}; // ` as typeof Link; export const QRCodeUpload = React.forwardRef(({}: any, _ref: React.ForwardedRef) => { return }) ================================================ FILE: packages/component-lib/src/components/basic-lib/panel/SubMenu.tsx ================================================ import styled from '@emotion/styled' import { List, ListProps } from '@mui/material' export const SubMenu = styled(List)` width: 100%; flex: 1; padding: ${({ theme }) => (theme.unit / 2) * 5}px 0; background-color: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; &.color-light { background-color: initial; } ` as typeof List ================================================ FILE: packages/component-lib/src/components/basic-lib/panel/SwitchPanel.tsx ================================================ import { WithTranslation } from 'react-i18next' import SwipeableViews, { SwipeableViewsProps } from 'react-swipeable-views' import React, { RefAttributes } from 'react' import { useTheme } from '@emotion/react' import styled from '@emotion/styled' import { Box, Toolbar } from '@mui/material' import { boxLiner, toolBarPanel } from '../../styled' import { useSettings } from '../../../stores' import { PanelContent, SwitchPanelProps } from './Interface' export const SwipeableViewsStyled = styled(SwipeableViews)< SwipeableViewsProps & { _height?: number | string _width?: number | string ismobile?: 'true' | 'false' scroolDisabled?: boolean } >` overflow: ${({ scroolDisabled }) => (scroolDisabled ? 'hidden' : 'auto')}; position: relative; flex: 1; ${({ _height, _width, ismobile }) => ` height: ${ typeof _height === 'string' ? _height : typeof _height === 'number' ? _height + 'px' : `var(--swap-box-height)` }; ${ ismobile === 'true' ? `` : ` width: ${ typeof _width === 'string' ? _width : typeof _width === 'number' ? _width + 'px' : `var(--swap-box-width)` };` } `} .createRedPacket & { width: 100%; ${({ ismobile }) => `${ismobile === 'true' ? 'width:380px;' : ''}`} .container { & > div { width: 100%; flex: 1; display: flex; justify-content: center; .redPacket { justify-content: center; } .menu-panel { max-width: 760px; padding: 0 ${({ theme, ismobile }) => (ismobile === 'true' ? 2 : 10) * theme.unit}px; padding-top: ${({ theme }) => 2 * theme.unit}px; } } } } ${({ theme }) => toolBarPanel({ theme })} border-radius: ${({ theme }) => theme.unit}px; .react-swipeable-view-container { height: auto; & > div { .container { flex: 1; .coinInput-wrap, .btnInput-wrap, .MuiOutlinedInput-root { background: var(--field-opacity); border-color: var(--opacity); :hover { border-color: var(--color-border-hover); } } } display: flex; flex-direction: column; flex-wrap: wrap; align-content: stretch; align-items: stretch; } } &.noToolBar { .react-swipeable-view-container > div { & > .MuiGrid-container { padding-top: 0; //margin-top: ; margin-bottom: calc(var(--toolbar-row-padding) / 2); padding: 0 calc(var(--toolbar-row-padding) / 2); } //margin-top: calc(var(--toolbar-row-padding) * -1); } } &.hasLinerBg { .react-swipeable-view-container > div { ${({ theme }) => boxLiner({ theme })} } } .MuiToolbar-root { align-items: flex-end; display: flex; justify-content: space-between; padding: 0 ${({ theme }) => (theme.unit * 5) / 2}px; } &.vaultSwap { height: auto; width: var(--swap-box-width); .react-swipeable-view-container > div { border: none; } .cover { position: absolute; top: 0; left: 0; right: 0; bottom: 0; } } &.vaultBorrow { .menu-panel { .MuiListItemText-root { .MuiListItemText-secondary { visibility: hidden; } } } } ` as ( props: SwipeableViewsProps & { _height?: number | string _width?: number | string ismobile?: boolean | undefined | string scroolDisabled?: boolean }, ) => JSX.Element function _SwitchPanel( { index, className, panelList, // _height, // _width, size, scrollDisabled, ...rest }: SwitchPanelProps & WithTranslation, _ref: React.ForwardedRef, ) { // {/*{...{ _height, _width }}*/} const { isMobile } = useSettings() const theme = useTheme() const hasToolBar = panelList.find((item) => item.toolBarItem !== undefined) const ref = React.useRef(null) React.useEffect(() => { ref.current && ref.current.rootNode.scrollTo(0, 0) }, [index]) return ( {panelList.map((panel: PanelContent, _index) => { return ( {panel.toolBarItem ? ( {panel.toolBarItem} ) : ( <> )} {panel.element} ) })} ) } export const SwitchPanel = React.memo(React.forwardRef(_SwitchPanel)) as ( props: SwitchPanelProps & WithTranslation & RefAttributes, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/basic-lib/panel/index.tsx ================================================ import styled from '@emotion/styled' import { Box, Card, Typography, BoxProps, Container, IconButton } from '@mui/material' import { FirstPlaceIcon, SecondPlaceIcon, ThirdPlaceIcon } from '@loopring-web/common-resources' import React from 'react' export * from './SwitchPanel' export * from './SubMenu' export * from './Interface' export * from './IPFSSourceUpload' export const CardNFTStyled = styled(Card)` display: flex; padding: 0; flex-direction: column; justify-content: space-between; position: relative; width: var(--nft-card); ` export const LoadingStyled = styled(IconButton)` position: absolute; z-index: 21; top: 40%; left: 50%; transform: translate(-50%, -50%); ` export const ImageUploadWrapper = styled(Box)` position: relative; width: 100%; background: var(--color-box-third); border-radius: ${({ theme }) => theme.unit}px; .MuiFormControlLabel-root { align-items: flex-start; .MuiFormControlLabel-label { color: var(--color-text-secondary); } } ` as typeof Box export const PlaceComponent = ({ rank }: { rank: number }) => { return ( <> {rank.toString() === '1' ? ( ) : rank.toString() === '2' ? ( ) : rank.toString() === '3' ? ( ) : ( '' )} {rank} ) } export const MaxWidthContainer = ({ containerProps = {}, ...props }: { children: React.ReactNode background?: string containerProps?: BoxProps } & BoxProps) => { const { children, background, sx, ...otherProps } = props const { sx: containerPropsSX, ..._containerProps } = containerProps return ( {children} ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/popover/Interface.ts ================================================ import { PopoverOrigin } from '@mui/material' import { OneOf } from '@loopring-web/common-resources' import * as React from 'react' import { PopupState as InjectedProps } from 'material-ui-popup-state/core' export enum PopoverType { hover = 'hover', click = 'click', } type HorizonLeft = { left: number } type HorizonRight = { right: number } // export type PopoverProps = MuiPopoverProps & { // arrowHorizon?: OneOf<[HorizonLeft, HorizonRight]>; // } export type PopoverWrapProps = { type: keyof typeof PopoverType // children: React.ReactNode; className: string popupId: string children?: JSX.Element | undefined | React.ReactElement popoverContent: React.ReactNode // popoverStyle?: React.CSSProperties; anchorOrigin?: PopoverOrigin transformOrigin?: PopoverOrigin // popoverTop?: number; handleStateChange?: (state: boolean) => void // variant: Variant parentPopupState?: InjectedProps | null | undefined disableAutoFocus?: boolean | null arrowHorizon?: OneOf<[HorizonLeft, HorizonRight]> } ================================================ FILE: packages/component-lib/src/components/basic-lib/popover/Popover.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Popover as MuiPopover, PopoverOrigin, PopoverProps } from '@mui/material' import { PopoverWrapProps } from './Interface' import { bindHover, bindMenu, bindPopover, bindTrigger, usePopupState, } from 'material-ui-popup-state/hooks' import HoverMenu from 'material-ui-popup-state/HoverMenu' import HoverPopover from 'material-ui-popup-state/HoverPopover' const DEFAULT_ANCHOR_ORIGIN: PopoverOrigin = { vertical: 'bottom', horizontal: 'left', } const DEFAULT_TRANSFORM_ORIGIN: PopoverOrigin = { vertical: 'top', horizontal: 'left', } // const POPOVER_TOP = 8; export const Popover: React.FC = ({ type, popupId, className, children, popoverContent, anchorOrigin = DEFAULT_ANCHOR_ORIGIN, transformOrigin = DEFAULT_TRANSFORM_ORIGIN, handleStateChange, }) => { const popupState = usePopupState({ variant: 'popover', popupId }) const { isOpen } = popupState React.useEffect(() => { if (handleStateChange) { handleStateChange(isOpen) } }, [handleStateChange, isOpen]) const isHover = type === 'hover' const bindAction = isHover ? bindHover(popupState) : bindTrigger(popupState) const bindContent = isHover ? bindMenu(popupState) : bindPopover(popupState) const CustomPopover = isHover ? HoverMenu : MuiPopover const PopoverStyled = styled(CustomPopover)` &.MuiModal-root { z-index: 200; &.arrow-center, &.arrow-right, &.arrow-left, &.arrow-left, &.arrow-top-center { .MuiPopover-paper { background: var(--color-pop-bg); margin-top: ${({ theme }) => theme.unit * 1.5}px; margin-left: ${({ theme }) => theme.unit}px; overflow: visible; box-shadow: var(--shadow); border-radius: ${({ theme }) => theme.unit * 0.5}px; &:before { position: absolute; top: ${({ theme }) => theme.unit * -2}px; content: ''; display: block; width: 0; height: 0; border: ${({ theme }) => theme.unit}px solid transparent; border-bottom: ${({ theme }) => theme.unit}px solid var(--color-pop-bg); } } } &.arrow-center .MuiPopover-paper { &:before { left: 50%; transform: translateX(-50%); } } &.arrow-right .MuiPopover-paper { &:before { right: ${({ theme }) => theme.unit}px; } } &.arrow-left .MuiPopover-paper { &:before { left: ${({ theme }) => theme.unit}px; } } &.arrow-top-center .MuiPopover-paper { &:before { left: 50%; transform: translateX(-50%) rotate(-180deg); bottom: ${({ theme }) => theme.unit * -2}px; top: initial; } } &.arrow-none { &:before { display: none; } &:after { display: none; } } } ` as (props: PopoverProps) => JSX.Element const getRenderChild = React.useCallback( (popoverChildren: React.ReactNode) => { if (React.isValidElement(popoverChildren)) { return React.Children.map(popoverChildren, (child) => React.cloneElement(child, { ...bindAction, }), ) } throw new Error('Invalid popover element!') }, [bindAction], ) return ( <> {getRenderChild(children)} {popoverContent} ) } export const PopoverPure = styled(HoverPopover)` &.MuiModal-root { .MuiBackdrop-root { background-color: inherit; } &.arrow-center, &.arrow-right, &.arrow-left, &.arrow-left, &.arrow-top-right, &.arrow-top-center { .MuiPopover-paper { background: var(--color-pop-bg); overflow: visible; box-shadow: var(--shadow); border-radius: ${({ theme }) => theme.unit * 0.5}px; margin-top: ${({ theme }) => theme.unit}px; &:before { position: absolute; top: ${({ theme }) => theme.unit * -2}px; content: ''; display: block; width: 0; height: 0; border: ${({ theme }) => theme.unit}px solid transparent; border-bottom: ${({ theme }) => theme.unit}px solid var(--color-pop-bg); } &:after { content: ''; position: absolute; top: ${({ theme }) => -theme.unit}px; width: 100%; height: ${({ theme }) => theme.unit}px; background-color: transparent; } } } &.arrow-center .MuiPopover-paper { &:before { left: 50%; transform: translateX(-50%); } } &.no-arrow .MuiPopover-paper { &:before { display: none !important; } &:after { display: none !important; } } &.arrow-right .MuiPopover-paper { &:before { right: ${({ theme }) => theme.unit}px; } } &.arrow-left .MuiPopover-paper { &:before { left: ${({ theme }) => theme.unit}px; } } &.arrow-top-center .MuiPopover-paper { margin-top: ${({ theme }) => theme.unit * -0.5}px; &:before { left: 50%; transform: translateX(-50%) rotate(-180deg); bottom: ${({ theme }) => theme.unit * -2}px; top: initial; } &:after { content: ''; position: absolute; top: 100%; width: 100%; height: ${({ theme }) => theme.unit}px; background-color: transparent; } } &.arrow-top-right .MuiPopover-paper { margin-top: ${({ theme }) => theme.unit * -0.5}px; &:before { right: ${({ theme }) => theme.unit}px; transform: translateX(-50%) rotate(-180deg); bottom: ${({ theme }) => theme.unit * -2}px; top: initial; } &:after { content: ''; position: absolute; top: 100%; width: 100%; height: ${({ theme }) => theme.unit}px; background-color: transparent; } } } ` as (props: PopoverProps) => JSX.Element ================================================ FILE: packages/component-lib/src/components/basic-lib/popover/index.ts ================================================ export * from './Popover' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/basic-lib/popover/popover.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { Box, Button, Grid } from '@mui/material' import { withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import { Popover, PopoverPure, PopoverType } from './index' import { bindPopper, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks' import { bindHover } from 'material-ui-popup-state/es' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` const Template: Story = withTranslation()((args: any) => { const leftState = usePopupState({ variant: 'popover', popupId: `popupId: 'left'`, }) const rightState = usePopupState({ variant: 'popover', popupId: `popupId: 'right'`, }) const centerState = usePopupState({ variant: 'popover', popupId: `popupId: 'center'`, }) const topState = usePopupState({ variant: 'popover', popupId: `popupId: 'top'`, }) return ( <> ) }) as Story const children = ( ) const popoverContent = 'Because the pool price changes dynamically, the price you see when placing an order may be inconsistent with the final transaction price.' // @ts-ignore export const PopoverStory = Template.bind({}) PopoverStory.args = { type: PopoverType.click, popupId: 'testPopup', children, popoverContent, popoverStyle: { width: '180px', lineHeight: '16px', fontSize: '12px', margin: '12px', }, } export default { title: 'basic-lib/Popover', component: Popover, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/basic-lib/resource/hook/useImage.ts ================================================ import React from 'react' export const useImage = (src: string) => { const [hasLoaded, setHasLoaded] = React.useState(false) const [hasError, setHasError] = React.useState(false) const [hasStartedInitialFetch, setHasStartedInitialFetch] = React.useState(false) React.useEffect(() => { if (src.trim() !== '') { setHasStartedInitialFetch(true) setHasLoaded(false) setHasError(false) // Here's where the magic happens. const image = new Image() image.src = src const handleError = () => { setHasError(true) } const handleLoad = () => { setHasLoaded(true) setHasError(false) } image.addEventListener('error', handleError) image.addEventListener('load', handleLoad) return () => { image.removeEventListener('error', handleError) image.removeEventListener('load', handleLoad) } } else { setHasError(false) setHasLoaded(true) setHasStartedInitialFetch(false) } }, [src]) return { hasLoaded, hasError, hasStartedInitialFetch } } ================================================ FILE: packages/component-lib/src/components/basic-lib/resource/index.tsx ================================================ export * from './hook/useImage' ================================================ FILE: packages/component-lib/src/components/basic-lib/table-pagination/TablePagination.tsx ================================================ import React from 'react' import { Box, Pagination } from '@mui/material' import styled from '@emotion/styled' import { RowConfig } from '@loopring-web/common-resources' // const StyledPaginationWrapper = styled(Box)` // height: 44px; // position: relative; // ` const StyledPagination = styled(Pagination)` display: flex; justify-content: center; align-items: center; ` export type PaginationProps = { page: number pageSize: number total: number height?: number alignItems?: string justifyContent?: string onPageChange: (page: number) => void size?: 'small' | 'medium' | 'large' | undefined } export const TablePagination = ({ onPageChange, page, height = RowConfig.rowHeight, justifyContent = 'center', alignItems = 'center', pageSize, total, size, }: PaginationProps) => { const getCount = React.useCallback(() => { if (!total) return 0 return Math.ceil(total / pageSize) // total % pageSize > 0 // ? parseInt(String(total / pageSize)) + 1 // : parseInt(String(total / pageSize)) }, [pageSize, total]) const handleChange = React.useCallback( (_e: any, value: number) => { onPageChange(value) }, [onPageChange], ) return ( ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/table-pagination/index.ts ================================================ export * from './TablePagination' ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/Interface.ts ================================================ import { WithT } from 'i18next' import { CalculatedColumn, Column as RdgColumns, DataGridProps as RdgDataGridProps, SortDirection, } from 'react-data-grid' // import { XOR } from '../../../types/lib'; export type DataGridProps = Omit, 'rows' | 'columns'> & TableProps export type Column = RdgColumns & { [key: string]: any } export type TableProps = { rawData: any columnMode: Column[] generateRows: (rawData: any, ...rest: any[]) => Array generateColumns: ( props: { columnsRaw: Column[] [key: string]: any } & WithT, ) => Array> // rows: any; columns?: readonly Column[] setRows?: () => any rowClassFn?: (row: R, ...rest: TableProps[]) => string frozeSort?: boolean rowHeight?: number // px actionColumns?: Array sortInitDirection?: 'DESC' | 'ASC' | undefined sortDefaultKey?: string sortMethod?: (rows: R[], sortColumn: string, sortDirection: 'DESC' | 'ASC' | undefined) => R[] handleSort?: (columnKey: string, direction: SortDirection) => boolean EmptyRowsRenderer?: React.ReactNode onRowClick?: (rowIdx: number, row: R, column: CalculatedColumn) => void } //& XOR<{ rows: R[] }, {generateRows: (rawData: any, ...rest: any[]) => Array}>; ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/Table.tsx ================================================ import type { Column as RdgColumn } from 'react-data-grid' import DataGrid, { SortColumn } from 'react-data-grid' import styled from '@emotion/styled' import { Trans, WithTranslation } from 'react-i18next' import { WithT } from 'i18next' import React, { ForwardedRef } from 'react' import { Column, DataGridProps, SortableHeaderCell, SortableHeaderCellProps, TableProps } from './' import { EmptyDefault } from '../empty' import { RowConfig, SoursURL } from '@loopring-web/common-resources' import { Box } from '@mui/material' import { css } from '@emotion/react' import { LoadingStyled } from '../panel' interface TableWrapperStyledProps { showloading: 'true' | 'false' } const TableWrapperStyled = styled(Box)` display: flex; position: relative; flex: 1; ` as any const hr = ({ theme }: any) => css` border-radius: ${theme.unit / 2}px; content: ''; display: block; height: 1px; //margin-bottom: -2px; background: var(--color-divide); position: absolute; left: 0; right: 0; bottom: 0; ` const hrShort = ({ theme }: any) => css` border-radius: ${theme.unit / 2}px; content: ''; display: block; height: 1px; width: calc(100% - ${theme.unit * 6}px); background: var(--color-divide); position: absolute; left: ${theme.unit * 3}px; right: 0; bottom: 0; ` export const DataGridStyled = styled(DataGrid)` width: 100%; height: 100%; .table-divide &.rdg .rdg-header-row { &:after { ${hr} } } .table-divide-short &.rdg .rdg-header-row { &:after { ${hrShort} } } &.rdg { &.scrollable .rdg-header-row { background: transparent; /* background: var(--color-box); */ } min-height: var(--min-height); color: var(--color-text-primary); //color: inherit; box-sizing: border-box; border: rgba(0, 0, 0, 0) 0 solid; //background-color: inherit; .rdg-header-row { color: var(--color-text-third); width: 100%; background-color: inherit; font-weight: normal; @media only screen and (max-width: 768px) { .rdg-cell { font-size: 12px; } } } .rdg-header-sort-name { flex-grow: initial; } .rdg-cell-selected { box-shadow: inherit; } .rdg-row { box-sizing: border-box; background: inherit; width: 100%; transition: background 0.4s ease-out; &:hover { background: var(--color-box-hover); .rdg-cell:first-of-type { // border-left: ${({ theme }) => theme.border.borderConfig({ d_W: 2, c_key: 'selected' })} } } } .rdg-cell { color: inherit; border-left: rgba(0, 0, 0, 0) 2px solid; border-right: rgba(0, 0, 0, 0) 2px solid; border-bottom: rgba(0, 0, 0, 0) 2px solid; box-sizing: border-box; height: 100%; // padding: 0 ${({ theme }) => theme.unit}px; & > span, div { user-select: text; } &.textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: right; } } &.textAlignLeft { text-align: left; .rdg-header-sort-cell { justify-content: left; } } &.textAlignCenter { text-align: center; .rdg-header-sort-cell { justify-content: center; } } } .rdg-header-sort-cell { .rdg-header-sort-name + span { display: none; } .rdg-header-sort-name { .sort-icon svg { display: inline-block; transform-origin: center; } .DESC svg { transform: rotate(0deg) translateX(-3px) scale(1.2); } .ASC svg { transform: rotate(180deg) translateX(-3px) scale(1.2); } .NONE svg { transform: rotate(90deg) translateX(-3px) scale(1.2); } } } .rdg-cell[aria-selected='true'] { box-shadow: none; } .rdg-cell.action { text-overflow: initial; } .rdg-cell.success { color: var(--color-success); } .rdg-cell.error { color: var(--color-error); } } ` as typeof DataGrid export const generateColumns = ({ columnsRaw, t, }: { columnsRaw: Column[] [key: string]: any } & WithT): RdgColumn[] => { const columns: Column[] = columnsRaw.reduce( (prev: RdgColumn[], column: Column) => { const { name, isHidden } = column if (typeof name === 'string' && !isHidden) { //@ts-ignore column.name = t(name) prev.push(column) } return prev }, [], ) return columns as Column[] } export const generateRows = (rawData: [][], rest: TableProps): Row[] => { const { columnMode } = rest return rawData.map( (row) => row.reduce( (prev: { [key: string]: any }, cell, index) => { if (columnMode[index]) { prev[columnMode[index].key] = cell } return prev }, { _rawData: row }, ) as Row, ) } export type ExtraTableProps = { showloading?: boolean } export const Table = React.forwardRef( ( props: DataGridProps & WithTranslation & ExtraTableProps, ref: ForwardedRef, ) => { const { EmptyRowsRenderer, generateRows, generateColumns, sortInitDirection, sortDefaultKey, sortMethod, rawData, style, frozeSort, rowRenderer, rowClassFn, rowKeyGetter, columnMode, onScroll, onRowClick, rowHeight, showloading, t, ...rest } = props const columns = generateColumns({ columnsRaw: columnMode, t }) const [rows, setRows] = React.useState(generateRows(rawData, props)) React.useEffect(() => { setRows(generateRows(rawData, props)) }, [rawData]) /*** sort handle start ***/ const [sortColumns, setSortColumns] = React.useState[]>([ { columnKey: sortDefaultKey as any, direction: sortInitDirection ? sortInitDirection : ('ASC' as any), }, ]) const sortedRows: readonly R[] = React.useMemo(() => { if (sortColumns.length === 0) return rows const { columnKey, direction } = sortColumns[0] let sortedRows: R[] = [...rows] sortedRows = sortMethod ? sortMethod(sortedRows, columnKey, direction) : rows return direction === 'DESC' ? sortedRows.reverse() : sortedRows }, [rows, sortColumns, sortMethod]) const onSortColumnsChange = React.useCallback((sortColumns: SortColumn[]) => { setSortColumns(sortColumns.slice(-1)) }, []) const loopringColumns = React.useMemo(() => { return columns.map((c) => { if (c.headerRenderer) { return { ...c } as Column } else { return { ...c, headerRenderer: (props: SortableHeaderCellProps) => ( ), } as Column } }) as Column[] }, [columns]) const RenderEmptyMsg = styled.span` display: flex; .link { margin: 0 5px; } ` /*** sort handle end ***/ return ( (rowClassFn ? rowClassFn(row, props) : '')} rowHeight={rowHeight ? rowHeight : RowConfig.rowHeight} onRowsChange={setRows} onSortColumnsChange={onSortColumnsChange} rowRenderer={rowRenderer as any} sortColumns={sortColumns} onRowClick={onRowClick} emptyRowsRenderer={ !showloading ? () => EmptyRowsRenderer ? ( EmptyRowsRenderer ) : ( { return ( Content is Empty ) }} /> ) : null } /> {showloading && ( {'loading'} )} ) }, ) ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/Formatters/CellActionsFormatter.tsx ================================================ // // import { useState } from 'react'; // // import type { ReactNode } from 'react'; // // import clsx from 'clsx'; // // import { usePopper } from 'react-popper'; // // import { createPortal } from 'react-dom'; // import styled from "@emotion/styled";; // // // // const CellActionClassname = styled.div` // // float: right; // // `; // // // // const CellActionLastClassname = styled.div` // // margin-right: -8px; // // `; // // // const CellActionButtonClassname = styled.div` // // width: 35px; // // height: 100%; // // text-align: center; // // position: relative; // // display: table; // // color: #4a9de2; // // // // > span { // // display: table-cell; // // vertical-align: middle; // // } // // `; // // const CellActionMenuClassname = styled.div` // position: absolute; // top: 100%; // z-index: 1000; // float: left; // min-width: 160px; // padding: 5px 0; // text-align: left; // list-style: none; // background- // background-clip: padding-box; // border: 1px solid #ccc; // box-shadow: 0 0 3px 0 #ccc; // // > span { // display: block; // padding: 3px 10px; // clear: both; // font-weight: 400; // line-height: 1.42857143; // color: #333; // white-space: nowrap; // cursor: pointer; // // &:hover { // color: #262626; // text-decoration: none; // background-color: #f5f5f5; // } // } // `; // // // interface Action { // // text: ReactNode; // // callback: () => void; // // } // // // interface CellActionButton { // // icon: ReactNode; // // actions?: Action[]; // // callback?: () => void; // // } // // // interface CellActionProps extends CellActionButton { // // isFirst: boolean; // // } // // // // function CellAction({ icon, actions, callback, isFirst }: CellActionProps) { // // const [isOpen, setIsOpen] = useState(false); // // const [reference, setReference] = useState(undefined); // // const [popper, setPopper] = useState(undefined); // // const { styles } = usePopper(reference, popper, { // // placement: 'bottom-start', // // modifiers: [{ name: 'offset', options: { offset: [0, -8] } }] // // }); // // // // const cellActionClasses = clsx(CellActionMenuClassname, { // // [CellActionLastClassname]: isFirst // // }); // // // // function onActionIconClick() { // // if (typeof callback === 'function') { // // callback(); // // } // // // // if (actions && actions.length > 0) { // // setIsOpen(isOpen => !isOpen); // // } // // } // // // // return ( // //
    setIsOpen(false)}> // // // // {icon} // // // // {isOpen && actions && actions.length && createPortal( // // // // {actions.map((action, index) => {action.text})} // // , // // document.body // // )} // //
    // // ); // // } // // // // interface CellActionsFormatterProps { // // actions: CellActionButton[]; // // } // // // // export function CellActionsFormatter({ actions }: CellActionsFormatterProps) { // // const actionButtons = actions.map((action, index) => { // // return ; // // }); // // // // return <>{actionButtons}; // // } export default {} ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/Formatters/CellDepthFormatter.tsx ================================================ import clsx from 'clsx' import styled from '@emotion/styled' import { withTranslation, WithTranslation } from 'react-i18next' import { CellRendererProps } from 'react-data-grid' const StyleDepth = styled.div` position: absolute; right: 0; height: 100%; z-index: -1; opacity: 0.06; .rgb-depth-cell { width: 100%; background: var(--color-success); &.rgb-depth-red { background: var(--color-error); } } ` interface IDepthRendererProps extends CellRendererProps { depthKey: string } export const CellDepthFormatter = ({ t, row, column, className, depthKey, ...props }: WithTranslation & IDepthRendererProps) => { className = clsx(className, { 'rgb-depth-cell': true }) const style = { width: `${Number((row as any)[depthKey]) * 100}%` } return (
    ) } export default withTranslation()(CellDepthFormatter) ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/Formatters/CellExpanderFormatter.tsx ================================================ import styled from '@emotion/styled' import { useFocusRef } from '../../hook' const CellExpandClassname = styled.div` float: right; display: table; height: 100%; > span { display: table-cell; vertical-align: middle; cursor: pointer; } ` interface CellExpanderFormatterProps { isCellSelected: boolean expanded: boolean onCellExpand: () => void } export function CellExpanderFormatter({ isCellSelected, expanded, onCellExpand, }: CellExpanderFormatterProps) { const iconRef = useFocusRef(isCellSelected) function handleClick(e: React.MouseEvent) { e.stopPropagation() onCellExpand() } function handleKeyDown(e: React.KeyboardEvent) { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault() onCellExpand() } } return ( {expanded ? '\u25BC' : '\u25B6'} ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/Formatters/ChildRowDeleteButton.tsx ================================================ import styled from '@emotion/styled' import { useFocusRef } from '../../hook' const ChildRowActionCrossClassname = styled.div` &::before, &::after { content: ''; position: absolute; background: grey; } &::before { left: 21px; width: 1px; height: 100%; } &::after { top: 50%; left: 20px; height: 1px; width: 15px; } &:hover { background: red; } ` const ChildRowButtonClassname = styled.div` cursor: pointer; position: absolute; left: 21px; transform: translateX(-50%); filter: grayscale(1); ` interface ChildRowDeleteButtonProps { isCellSelected: boolean isDeleteSubRowEnabled: boolean onDeleteSubRow: () => void } export function ChildRowDeleteButton({ isCellSelected, onDeleteSubRow, isDeleteSubRowEnabled, }: ChildRowDeleteButtonProps) { const iconRef = useFocusRef(isCellSelected) function handleKeyDown(e: React.KeyboardEvent) { if (e.key === 'Enter') { e.preventDefault() onDeleteSubRow() } } return ( <> {isDeleteSubRowEnabled && ( )} ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/Formatters/ImageFormatter.tsx ================================================ import styled from '@emotion/styled' const WrapperClassname = styled.div` display: flex; justify-content: space-around; ` const ImageCellClassname = styled.div` background: #efefef; background-size: 100%; display: inline-block; height: 28px; width: 28px; vertical-align: middle; background-position: center; ` interface Props { /** image url, used as background-image */ value: string } export function ImageFormatter({ value }: Props) { return ( ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/Formatters/index.ts ================================================ // export * from './CellActionsFormatter'; export * from './ImageFormatter' export * from './CellExpanderFormatter' export * from './ChildRowDeleteButton' export * from './CellDepthFormatter' ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/HeaderRenderers/SortableHeaderCell.tsx ================================================ import { HeaderRendererProps } from 'react-data-grid' import styled from '@emotion/styled' import { Box, BoxProps } from '@mui/material' import { css } from '@emotion/react' import React from 'react' export const headerSortCell = css` cursor: pointer; display: flex; ` export const headerSortName = css` flex-grow: 1; overflow: hidden; text-overflow: ellipsis; ` const StyledArrowSort = styled(Box)` margin-left: ${({ theme }) => `${theme.unit / 2}px`}; .up { width: 0; height: 0; border: ${({ theme }) => `${theme.unit / 2}px`} solid transparent; border-bottom-color: ${({ // theme, sortdirection, }) => (sortdirection === 'DESC' ? `var(--color-text-primary)` : `var(--color-text-third)`)}; } .down { width: 0; height: 0; border: ${({ theme }) => `${theme.unit / 2}px`} solid transparent; border-top-color: ${({ // theme, sortdirection, }) => (sortdirection === 'ASC' ? `var(--color-text-primary)` : `var(--color-text-third)`)}; margin-top: ${({ theme }) => `${theme.unit / 4}px`}; } ` as (props: BoxProps & { sortdirection: 'ASC' | 'DESC' | undefined }) => JSX.Element // @ts-ignore export const ArrowSort = ({ sortDirection, // children, ...rest }: BoxProps & { sortDirection: 'ASC' | 'DESC' | undefined }) => { return (
    ) } // type SharedHeaderCellProps = Pick< // HeaderRendererProps, // 'sortDirection' | 'onSort' | 'priority' // >; export interface SortableHeaderCellProps extends HeaderRendererProps { children?: React.ReactNode } export function SortableHeaderCell({ column, sortDirection, priority, onSort, children, }: SortableHeaderCellProps) { // let sortText = ''; // if (sortDirection === 'ASC') { // sortText = '\u25B2'; // } else if (sortDirection === 'DESC') { // sortText = '\u25BC'; // } if (column.sortable) { // const showUp = sortDirection === 'ASC' // const showDown = sortDirection === 'DESC' return ( onSort(e.ctrlKey)} > {children ? children : column.name} {priority} ) } else { return <>{children ? children : column.name} } } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/HeaderRenderers/index.ts ================================================ export * from './SortableHeaderCell' ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/RowRenders/RowDepthFormatter.tsx ================================================ import clsx from 'clsx' import styled from '@emotion/styled' import { CalculatedColumn, Cell, RowRendererProps } from 'react-data-grid' import React from 'react' // import type { Column } from 'react-data-grid'; const RowDepthStyled = styled.div` contain: strict; contain: size layout style paint; display: grid; grid-template-rows: var(--row-height); grid-template-columns: var(--template-columns); position: absolute; left: 0; width: var(--row-width); height: var(--row-height); line-height: var(--row-height); background-color: var(--background-color); .row-background-value { opacity: .06; height: var(--row-height); width: var(--row-width); position: absolute; top: 0; left: 0; z-index: 1; .rgb-depth-row { float: right; height: var(--row-height); background: var(--color-success); &.rgb-depth-red { background: var(--color-error); } } } } ` export interface IDepthRendererProps extends RowRendererProps { depthKey: string rowBeforeRender: (prors: { depthKey: string row: R column: readonly CalculatedColumn[] className: any [key: string]: any }) => JSX.Element } function _DepthRow( { cellRenderer: CellRenderer = Cell, className, rowIdx, isRowSelected, copiedCellIdx, draggedOverCellIdx, row, viewportColumns, selectedCellProps, onRowClick, rowClass, setDraggedOverRowIdx, onMouseEnter, top, onRowChange, selectCell, selectRow, rowBeforeRender, depthKey, 'aria-rowindex': ariaRowIndex, 'aria-selected': ariaSelected, ...props }: IDepthRendererProps & { selectRow: any }, ref: React.Ref, ) { function handleDragEnter(event: React.MouseEvent) { setDraggedOverRowIdx?.(rowIdx) onMouseEnter?.(event) } className = clsx( `rdg-row`, `rdg-row-${rowIdx % 2 === 0 ? 'even' : 'odd'}`, { 'rdg-cell-selected': isRowSelected, 'rdg-group-row-selected': selectedCellProps?.idx === -1, }, rowClass?.(row), className, ) return (
    {rowBeforeRender({ row, depthKey, column: viewportColumns, className, ...props, })}
    {viewportColumns.map((column) => { const isCellSelected = selectedCellProps?.idx === column.idx return ( CellRenderer && ( ) ) })}
    ) } export const DepthRow = React.memo(React.forwardRef(_DepthRow)) as unknown as ( props: IDepthRendererProps & React.RefAttributes, ) => JSX.Element export const RowBefore = ({ row, // isRowSelected, className, column, depthKey, width, // onRowReorder, ...props }: { column: any; width: string } & IDepthRendererProps) => { return
    } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/components/RowRenders/index.ts ================================================ export * from './RowDepthFormatter' ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/hook/index.ts ================================================ export * from './useClickOutside' export * from './useCombinedRefs' export * from './useFocusRef' export * from './useLatestFunc' ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/hook/useClickOutside.ts ================================================ import { useEffect, useRef } from 'react' /** * Detecting outside click on a react component is surprisingly hard. * A general approach is to have a global click handler on the document * which checks if the click target is inside the editor container or * not using editorContainer.contains(e.target). This approach works well * until portals are used for editors. Portals render children into a DOM * node that exists outside the DOM hierarchy of the parent component so * editorContainer.contains(e.target) does not work. Here are some examples * of the DOM structure with different types of editors * * * SimpleEditor for example Texbox (No Portals) *
    ..
    *
    *
    *
    ..
    *
    *
    * * ComplexEditor for example Modals (using Portals) *
    ..
    *
    *
    * // Nothing here *
    *
    *
    *
    ..
    *
    * * * One approach to detect outside click is to use synthetic event bubbling through * portals. An event fired from inside a portal will propagate to ancestors * in the containing React tree, even if those elements are not ancestors * in the DOM tree. This means a click handler can be attached on the window * and on the editor container. The editor container can set a flag to notify * that the click was inside the editor and the window click handler can use * this flag to call onClickOutside. This approach however has a few caveats * - Click handler on the window is set using window.addEventListener * - Click handler on the editor container is set using onClick prop * * This means if a child component inside the editor calls e.stopPropagation * then the click handler on the editor container will not be called whereas * the document click handler will be called. * https://github.com/facebook/react/issues/12518 * * To solve this issue onClickCapture event is used. */ export function useClickOutside(onClick: () => void) { const frameRequestRef = useRef() function cancelAnimationFrameRequest() { if (typeof frameRequestRef.current === 'number') { cancelAnimationFrame(frameRequestRef.current) frameRequestRef.current = undefined } } // We need to prevent the `useEffect` from cleaning up between re-renders, // as `handleDocumentClick` might otherwise miss valid click events. // To that end we instead access the latest `onClick` prop via a ref. const onClickRef = useRef((): void => { throw new Error('Cannot call an event handler while rendering.') }) useEffect(() => { onClickRef.current = onClick }) useEffect(() => { function onOutsideClick() { frameRequestRef.current = undefined onClickRef.current() } function onWindowCaptureClick() { cancelAnimationFrameRequest() frameRequestRef.current = requestAnimationFrame(onOutsideClick) } window.addEventListener('click', onWindowCaptureClick, { capture: true }) return () => { window.removeEventListener('click', onWindowCaptureClick, { capture: true, }) cancelAnimationFrameRequest() } }, []) return cancelAnimationFrameRequest } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/hook/useCombinedRefs.ts ================================================ import { useCallback } from 'react' export function useCombinedRefs(...refs: readonly React.Ref[]) { return useCallback( (handle: T | null) => { for (const ref of refs) { if (typeof ref === 'function') { ref(handle) } else if (ref !== null) { // @ts-ignore ref.current = handle as any } } }, // eslint-disable-next-line react-hooks/exhaustive-deps refs, ) } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/hook/useFocusRef.ts ================================================ import { useLayoutEffect, useRef } from 'react' export function useFocusRef(isCellSelected: boolean | undefined) { const ref = useRef(null) useLayoutEffect(() => { if (!isCellSelected) return ref.current?.focus({ preventScroll: true }) }, [isCellSelected]) return ref } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/hook/useLatestFunc.ts ================================================ import { useCallback, useEffect, useRef } from 'react' // https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often // eslint-disable-next-line @typescript-eslint/no-explicit-any export function useLatestFunc any>(fn: T) { const ref = useRef(fn) useEffect(() => { ref.current = fn }) return useCallback((...args: Parameters) => { ref.current(...args) }, []) } ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/index.ts ================================================ export * from './Table' export * from '../empty' export * from './components/RowRenders' export * from './components/HeaderRenderers' export * from './components/Formatters' export * from './Interface' export * from './hook' ================================================ FILE: packages/component-lib/src/components/basic-lib/tables/table.stories.tsx ================================================ import { Meta, Story } from '@storybook/react' import { CellDepthFormatter } from './components/Formatters' import { Column, DataGridProps, generateColumns, generateRows, Table } from './index' import styled from '@emotion/styled' import { RowRendererProps } from 'react-data-grid' import { DepthRow, RowBefore } from './components/RowRenders' import { useTranslation } from 'react-i18next' const Style = styled.div` background: var(--color-global-bg); ` interface Row { price: string size: string volume: string number: string sortColumn: string filterColumn: string cellExpend: { value: string children: [] isExpanded: boolean } children?: Row[] isExpanded?: boolean format?: any } const rawData: Array> = [ ['1.2', 'big', '0.12', '123', 'abc', 'start_a'], ['1.3', 'big', '0.99', '1293', 'abc', 'start_a'], ['1.5', 'small', '0.42', '23', 'abc', 'start_a'], ['1.2', 'big', '0.32', '123', 'abc', 'end_a'], ['1.6', 'big', '0.52', '123', 'abc', 'start_a'], ['1.3', 'middle', '0.852', '5', 'abc', 'before_a'], ['1.2', 'big', '0.12', '123', 'abc', 'start_a'], ] const columnModeDefault: Column[] = [ { key: 'price', name: 'price' }, { key: 'size', name: 'size' }, { key: 'volume', name: 'volume' }, { key: 'number', name: 'size' }, { key: 'string', name: 'volume' }, { key: 'filter', name: 'number' }, ] /** * Table StoryBook */ export default { component: Table, title: 'basic-lib/Table', argTypes: {}, } as Meta const Template: Story> = (args: DataGridProps & any) => { let rest = useTranslation() return ( ) } export const Default = Template.bind({}) export const Empty = Template.bind({}) export const FormatCell = Template.bind({}) export const FormatRow = Template.bind({}) export const SortColumn = Template.bind({}) export const ExpendRowDemo = Template.bind({}) Default.args = { rawData: rawData, columnMode: columnModeDefault, generateRows: generateRows, generateColumns: generateColumns, } Empty.args = { ...Default.args, rawData: [], columnMode: columnModeDefault, } const columnModeCellDepth: Column[] = [ { key: 'price', name: 'price', cellClass: (row: Row) => (Number(row.volume) > 0.4 ? 'success' : 'error'), }, { key: 'size', name: 'size', formatter: ({ row, column, ...props }: any) => { return ( <> 0.4 ? 'rgb-depth-success' : 'rgb-depth-error'} depthKey={'price'} />
    {row[column.key]}
    ) }, }, { key: 'volume', name: 'volume', sortable: true }, ] FormatCell.args = { ...Default.args, ...{ columnMode: columnModeCellDepth, }, } FormatRow.args = { ...Default.args, ...{ columnMode: columnModeDefault, rowRenderer: (p: RowRendererProps) => { const { row } = p return ( { const width = `${Number(row.volume) * 100}%` return ( 0.4 ? 'rgb-depth-red' : ''} /> ) }} {...p} /> ) }, }, } const columnModeSort: Column[] = [ { key: 'price', name: 'price', cellClass: (row: Row) => (Number(row.price) > 100 ? 'upper' : 'row'), }, { key: 'size', name: 'size', sortable: true }, { key: 'volume', name: 'volume', sortable: true }, { key: 'sortColumn', name: 'value', sortable: true }, ] SortColumn.args = { ...Default.args, columnMode: columnModeSort, sortDefaultKey: 'sortColumn', frozeSort: false, sortMethod: (sortedRows: Row[], sortColumn) => { switch (sortColumn) { case 'size': sortedRows = sortedRows.sort((a, b) => a[sortColumn].localeCompare(b[sortColumn])) break case 'sortColumn': case 'volume': sortedRows = sortedRows.sort( (a: Row, b: Row) => Number(a[sortColumn]) - Number(b[sortColumn]), ) break default: } return sortedRows }, } ================================================ FILE: packages/component-lib/src/components/basic-lib/tags/Tags.stories.tsx ================================================ import { Meta, Story } from '@storybook/react' import { Box, Grid } from '@mui/material' import { WithTranslation, withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { NewTagIcon } from './index' import { ButtonProps } from '../btns' const Styled = styled.div` background: var(--color-global-bg); ` export const Tags: Story = withTranslation()(({}: WithTranslation & any) => { return ( <>

    Tags

    ) }) as Story //export const Button = Template.bind({}); export default { title: 'resource/tags', component: Tags, argTypes: {}, } as Meta // LButton.args = {} ================================================ FILE: packages/component-lib/src/components/basic-lib/tags/index.tsx ================================================ import { SvgIcon, Chip, SvgIconProps } from '@mui/material' import * as sdk from '@loopring-web/loopring-sdk' import { useTranslation } from 'react-i18next' import styled from '@emotion/styled' import { fontDefault, myLog } from '@loopring-web/common-resources' export const NewTagIcon = () => ( ) export const ChipStyle = styled(Chip)` font-size: ${fontDefault.body2}; border-radius: ${({ theme }) => `${theme.unit}`}px; height: ${({ theme }) => `${(theme.unit * 5) / 2}`}px; ` export const AddressTypeTag = ({ addressType }: { addressType }) => { const { t } = useTranslation('common') myLog('addressType', addressType, sdk.AddressType) switch (addressType) { case sdk.AddressType.EOA: return ( ) case sdk.AddressType.LOOPRING_HEBAO_CF: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_1_6: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_2_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_0_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_1_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_2_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_3_0_0: return ( ) case sdk.AddressType.CONTRACT: return ( ) case sdk.AddressType.EXCHANGE_BINANCE: return ( ) case sdk.AddressType.EXCHANGE_HUOBI: return ( ) case sdk.AddressType.EXCHANGE_OTHER: return ( ) case sdk.AddressType.UNKNOWN_ADDRESS: default: return <> } } export const VaultTag = (props: SvgIconProps) => { return ( ) } ================================================ FILE: packages/component-lib/src/components/block/AmmCard.tsx ================================================ import { Box, Card, CardActions, CardContent, Divider, Typography } from '@mui/material' import { Button, CoinIcons } from '../' import React from 'react' import moment from 'moment' import { WithTranslation, withTranslation } from 'react-i18next' import { useHistory } from 'react-router-dom' import { AmmCardProps, CurrencyToTag, DAY_FORMAT, EmptyValueTag, getValuePrecisionThousand, myLog, PriceTag, TokenType, YEAR_DAY_FORMAT, } from '@loopring-web/common-resources' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { PopoverPure } from '../basic-lib' import { bindHover } from 'material-ui-popup-state/es' import { useSettings } from '../../stores' import styled from '@emotion/styled' export interface Reward { startAt: number timeInterval: string accountId: number tokenId: number market: string score: number amount: string } const CardStyled = styled(Card)` min-height: ${({ theme }) => theme.unit * 61.5}px; display: flex; flex-direction: column; justify-content: space-between; position: relative; ` const LabelStyled = styled(Box)` position: absolute; top: 0; left: 0; border-radius: ${({ theme }) => theme.unit}px 0; padding: ${({ theme }) => theme.unit / 2}px ${({ theme }) => theme.unit}px; display: flex; justify-content: center; align-items: center; color: var(--color-box); font-size: 1.4rem; background: ${({ type }: any) => type === 'ORDERBOOK_MINING' ? 'var(--color-warning)' : 'var(--color-tag)'}; ` as any const CardActionBoxStyled = styled(Box)` position: relative; ` const DetailWrapperStyled = styled(Box)` display: flex; justify-content: space-between; align-items: center; margin-bottom: ${({ theme }) => theme.unit}px; ` const ViewDetailStyled = styled(Typography)` &:hover { color: var(--color-text-primary); } ` as any export const AmmCard = withTranslation('common', { withRef: true })( React.memo( React.forwardRef( ( { t, coinAInfo, coinBInfo, amountU, account, APR, coinA, coinB, activity: { duration, myRewards, rewardToken, isPass, ruleType, totalRewards, rewardTokenU, maxSpread, }, handleClick, popoverIdx, totalA, totalB, precisionA, precisionB, getLiquidityMining, //: (market: string, size?: number) => Promise setShowRewardDetail, setChosenCardInfo, ammInfo, forexMap, rewardB, rewardAU, rewardBU, }: // ...rest AmmCardProps & WithTranslation, ref: React.ForwardedRef, ) => { const isOrderbook = ruleType === 'ORDERBOOK_MINING' const isAmm = ruleType === 'AMM_MINING' const { coinJson, currency } = useSettings() const pathname = `${coinAInfo?.simpleName}-${coinBInfo?.simpleName}` const pair = `${coinAInfo?.simpleName} / ${coinBInfo?.simpleName}` const myBalanceA = ammInfo?.balanceA const myBalanceB = ammInfo?.balanceB const myTotalAmmValueDollar = ammInfo?.totalAmmValueDollar const totalAmmReward = PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (rewardAU ?? 0) * (forexMap[currency] ?? 0) + (rewardBU ?? 0) * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true }, ) myLog({ totalAmmReward, }) const orderbookReward = CurrencyToTag[currency] + getValuePrecisionThousand( (totalRewards || 0) * ((rewardTokenU || 0) * (forexMap[currency] ?? 0)), undefined, undefined, 2, true, { isFait: true }, ) const isComing = moment(duration.from).unix() * 1000 > moment.now() const popLiquidityState = usePopupState({ variant: 'popover', popupId: `popup-totalLiquidty-${popoverIdx}`, }) const popTotalRewardState = usePopupState({ variant: 'popover', popupId: `popup-totalReward-${popoverIdx}`, }) const popMyAmmValueState = usePopupState({ variant: 'popover', popupId: `popup-myAmmValue-${popoverIdx}`, }) const history = useHistory() const handleViewDetail = React.useCallback(() => { const date = new Date(duration.from) const year = date.getFullYear() const month = ('0' + (date.getMonth() + 1).toString()).slice(-2) const current_event_date = `${year}-${month}` history.push( `/race-event/${current_event_date}?selected=${pathname}&type=${''}&l2account=${ account?.accAddress }`, ) }, [history, pathname]) const handleMyRewardClick = React.useCallback(() => { getLiquidityMining(pathname, 120) setShowRewardDetail(true) setChosenCardInfo(rewardToken?.simpleName) }, [ getLiquidityMining, pathname, rewardToken?.simpleName, setChosenCardInfo, setShowRewardDetail, ]) return ( {isOrderbook ? 'Orderbook' : 'AMM Pools'} {pair} {coinA} {coinB} {isOrderbook ? ( {totalRewards ? getValuePrecisionThousand(totalRewards) + ' ' + rewardToken?.simpleName : EmptyValueTag} ) : ( {getValuePrecisionThousand(APR, 2, 2, 2, true) + '%' || EmptyValueTag} )} {isOrderbook ? t('labelMiningReward') : t('labelAPR')} {t('labelMiningActiveDate')} {' ' + moment(duration.from).format(YEAR_DAY_FORMAT) + ' - '} {moment(duration.to).format(DAY_FORMAT)} {isAmm && ( {t('labelMiningLiquidity')} {t('labelLiquidity') + ' ' + amountU === undefined ? EmptyValueTag : PriceTag[CurrencyToTag[currency]] + amountU} {coinA} {getValuePrecisionThousand( totalA, undefined, undefined, precisionA, false, { floor: true }, )} {coinB} {getValuePrecisionThousand( totalB, undefined, undefined, precisionB, false, { floor: true }, )} )} {isOrderbook && ( {t('labelMiningMaxSpread')} {getValuePrecisionThousand(maxSpread, undefined, undefined, 2, true)}   {'%'} )} {!isOrderbook && ( {t('labelMiningActivityReward')} {isPass ? ( {getValuePrecisionThousand(totalRewards)}   {rewardToken?.simpleName} ) : ( {getValuePrecisionThousand(totalRewards)}   {rewardToken?.simpleName} )} {isOrderbook ? orderbookReward : isAmm ? totalAmmReward : orderbookReward} {coinB && ( {coinB} {getValuePrecisionThousand( rewardB, undefined, undefined, precisionB, false, { floor: true }, )}   {coinBInfo?.simpleName} )} )} {isAmm && ( {t('labelMiningMyShare')} {myTotalAmmValueDollar ? ( {CurrencyToTag[currency] + getValuePrecisionThousand( myTotalAmmValueDollar * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true }, )} ) : ( {EmptyValueTag} )} {coinA} {getValuePrecisionThousand(myBalanceA, precisionA, 2, undefined, false, { floor: true, })} {coinB} {getValuePrecisionThousand(myBalanceB, precisionB, 2, undefined, false, { floor: true, })} )} {t('labelMiningMyReward')} {myRewards === 0 ? EmptyValueTag : getValuePrecisionThousand(myRewards, undefined, undefined, undefined, true, { isFait: true, floor: true, }) + rewardToken?.simpleName} handleViewDetail()} component={'a'} variant={'body1'} color={'var(--color-text-secondary)'} marginTop={1} > {t('labelMiningViewDetails')}   {'>'} ) }, ), ), ) as (props: AmmCardProps & React.RefAttributes) => JSX.Element ================================================ FILE: packages/component-lib/src/components/block/AmmPairDetail.tsx ================================================ import { Box, Typography } from '@mui/material' import React from 'react' import { EmptyValueTag, getValuePrecisionThousand, myLog, TokenType, } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { CoinIcons } from '../tableList' import { useSettings } from '../../stores' export const AmmPairDetail = ({ coinA, coinB, balanceA, balanceB, }: { coinA: string coinB: string balanceA: string | undefined balanceB: string | undefined }) => { const { coinJson } = useSettings() return ( {coinA} {balanceA ? balanceA : EmptyValueTag} {coinB} {balanceB ? balanceB : EmptyValueTag} ) } export const AmmAPRDetail = withTranslation('tables')( ({ self = 0, event = 0, fee = 0, t, }: { self?: number event?: number fee?: number } & WithTranslation) => { return ( {t('labelAprPool')} {self === 0 || typeof self === 'undefined' ? EmptyValueTag : getValuePrecisionThousand(self, 2, 2, 2, true) + '%'} {t('labelAprFee')} {fee === 0 || typeof fee === 'undefined' ? EmptyValueTag : getValuePrecisionThousand(fee, 2, 2, 2, true) + '%'} {t('labelAprEvent')} {event === 0 || typeof event === 'undefined' ? EmptyValueTag : getValuePrecisionThousand(event, 2, 2, 2, true) + '%'} ) }, ) as (props: { self?: number; event?: number; fee?: number }) => JSX.Element const TypographyStyle = styled(Typography)` .rewardItem:not(:last-child) { &:after { display: inline-flex; content: '+'; padding: 0 ${({ theme }) => theme.unit + 'px'}; } } ` as typeof Typography type AmmPairDetailProps = { feeA: number | undefined feeB: number | undefined rewards: Array<{ tokenSymbol: string; amount: number | undefined }> extraRewards: Array<{ tokenSymbol: string; amount: number | undefined }> tokenMap: { [key: string]: any } coinA: string coinB: string } export const AmmRewardsDetail = withTranslation('tables')( ({ feeA, feeB, rewards, extraRewards, tokenMap, coinA, coinB, t, }: AmmPairDetailProps & WithTranslation) => { // labelRewardFee // labelRewardReward // labelRewardExtra myLog('coinB', coinB) return ( {t('labelRewardFee')} {(feeA ? getValuePrecisionThousand( feeA, tokenMap[coinA].precision, tokenMap[coinA].precision, tokenMap[coinA].precision, true, ) : EmptyValueTag) + ` ${coinA}`} {(feeB ? getValuePrecisionThousand( feeB, tokenMap[coinB].precision, tokenMap[coinB].precision, tokenMap[coinB].precision, true, ) : EmptyValueTag) + ` ${coinB}`} {t('labelRewardReward')} {rewards.length ? ( <> {rewards.map((item: any, index: number) => { return ( {item?.amount ? ( {getValuePrecisionThousand( item.amount, tokenMap[item.symbol].precision, tokenMap[item.symbol].precision, tokenMap[item.symbol].precision, true, )` ${coinA}`}{' '} ) : ( <> )} ) })} ) : ( EmptyValueTag )} {t('labelRewardExtra')} {extraRewards?.length ? ( <> {extraRewards?.map((item: any, index: number) => { return ( {item?.amount ? ( {getValuePrecisionThousand( item.amount, tokenMap[item.symbol].precision, tokenMap[item.symbol].precision, tokenMap[item.symbol].precision, true, )` ${coinA}`}{' '} ) : ( <> )} ) })} ) : ( EmptyValueTag )} ) }, ) as (props: AmmPairDetailProps) => JSX.Element ================================================ FILE: packages/component-lib/src/components/block/AssetTitle.tsx ================================================ import { Box, Grid, IconButton, Typography } from '@mui/material' import { getValuePrecisionThousand, HeaderMenuItemInterface, HiddenTag, HideIcon, L1L2_NAME_DEFINED, MapChainId, RouterPath, subMenuLayer2, TradeBtnStatus, ViewIcon, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { useTranslation, withTranslation, WithTranslation } from 'react-i18next' import { AssetTitleMobileProps, AssetTitleProps } from './Interface' import styled from '@emotion/styled' import { DropdownIconStyled } from '../tradePanel' import { AnimationArrow, Button, ButtonListRightStyled } from './../' import { useHistory, useRouteMatch } from 'react-router-dom' import { useSettings } from '../../stores' const BoxStyled = styled(Box)` color: var(--color-text-secondary); .MuiButtonBase-root { color: var(--color-text-secondary); } ` as typeof Box export const AssetTitle = withTranslation('common')( ({ t, assetInfo, accountId, onShowSend, onShowReceive, hideL2Assets, setHideL2Assets, assetBtnStatus, isWebEarn, forexMap, onClickBridge, showBridgeBtn = false, }: AssetTitleProps & WithTranslation) => { const history = useHistory() const { defaultNetwork, currency } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( {isWebEarn ? ( t('labelEarnVaultTitle') ) : ( <> {t('labelAssetTitle', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} {` (UID: ${accountId})`} )} setHideL2Assets(!hideL2Assets)} aria-label={t('labelShowAccountInfo')} > {!hideL2Assets ? : } {!hideL2Assets && assetInfo.priceTag} {!hideL2Assets ? ( {assetInfo.totalAsset ? getValuePrecisionThousand( sdk.toBig(assetInfo.totalAsset).times(forexMap[currency] ?? 0), 2, 2, 2, true, { floor: true }, ) : '0.00'} ) : ( {HiddenTag} )} {isWebEarn ? ( <> {showBridgeBtn && ( )} ) : ( <> )} ) }, ) export const AssetTitleMobile = ({ assetInfo, accountId, onShowSend, onShowReceive, hideL2Assets, setHideL2Assets, }: AssetTitleMobileProps) => { const { hideL2Action, setHideL2Action, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { t } = useTranslation(['common', 'layout']) let match: any = useRouteMatch('/l2assets/:item') const history = useHistory() const label = Reflect.ownKeys(subMenuLayer2) .reduce((pre, item) => [...pre, ...subMenuLayer2[item]], [] as HeaderMenuItemInterface[]) .find((item) => RegExp(item?.router?.path ?? '').test(match?.url ?? ''))?.label?.i18nKey return ( {t(label ?? 'labelAssets', { ns: 'layout', loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, })} {t('labelAssetMobileTitle', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {` (UID: ${accountId})`} setHideL2Assets(!hideL2Assets)} aria-label={t('labelShowAccountInfo')} > {!hideL2Assets ? : } {assetInfo.priceTag} {!hideL2Assets ? ( {assetInfo.totalAsset ? getValuePrecisionThousand(assetInfo.totalAsset, 2, 2, 2, true, { floor: true }) : '0.00'} ) : ( ✱✱✱✱.✱✱ )} setHideL2Action(!hideL2Action)} marginBottom={1} > {!hideL2Action ? ( ) : ( )} {!hideL2Action && ( {/**/} {/* onShowTransfer()}*/} {/* >*/} {/* {t("labelL2toL2")}*/} {/* */} {/**/} {/**/} {/* onShowWithdraw()}*/} {/* >*/} {/* {t("labelL2toL1")}*/} {/* */} {/**/} )} ) } export const AssetTitleMobileEarn = ({ assetInfo, onShowSend, onShowReceive, hideL2Assets, setHideL2Assets, onClickBridge, showBridgeBtn, }: AssetTitleMobileProps) => { const { t } = useTranslation(['common', 'layout']) const history = useHistory() return ( {t('labelTotalPortfolio')} setHideL2Assets(!hideL2Assets)} aria-label={t('labelShowAccountInfo')} > {!hideL2Assets ? : } {assetInfo.priceTag} {!hideL2Assets ? ( {assetInfo.totalAsset ? getValuePrecisionThousand(assetInfo.totalAsset, 2, 2, 2, true, { floor: true }) : '0.00'} ) : ( ✱✱✱✱.✱✱ )} {showBridgeBtn && ( )} {/**/} {/* onShowTransfer()}*/} {/* >*/} {/* {t("labelL2toL2")}*/} {/* */} {/**/} {/**/} {/* onShowWithdraw()}*/} {/* >*/} {/* {t("labelL2toL1")}*/} {/* */} {/**/} ) } ================================================ FILE: packages/component-lib/src/components/block/Block.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { Grid } from '@mui/material' import { AmmCardProps, CoinInfo, FloatTag, PriceTag, TradeBtnStatus, } from '@loopring-web/common-resources' import { coinMap, CoinType, FOREXMAP } from '../../static' import { withTranslation } from 'react-i18next' import { AssetTitle, AssetTitleProps, RedPacketBgOpened, RedPacketClock, RedPacketDetail, RedPacketOpen, RedPacketQRCode, RedPacketTimeout, TradeTitle, VipPanel, } from './' import { SettingPanel } from './SettingPanel' import { MarketBlock } from './MarketBlock' // import { PoolDetailTitle } from './PoolDetailTitle'; import { AmmCard } from './AmmCard' import React from 'react' const Style = styled.div` background: var(--color-global-bg); height: 100%; flex: 1; ` const vipData = [ { level: 'VIP 0', tradeVolume: '< 10,000 LRC', rule: '1,000.00', balance: '1,000.00', maker: '1,000.00', taker: '1,000.00', }, ] const TradeTitleWrap = withTranslation('common')((rest) => { // let tradeData: any = {sell: {belong: undefined}, buy: {belong: undefined}}; let props: any = { // swapTradeData: tradeData, coinAInfo: coinMap.LRC, coinBInfo: coinMap.ETH, } return ( <> ) }) const AmmCardWrap = () => { const ref = React.createRef() const ammInfo: AmmCardProps = { handleClick(): void {}, // ammCalcData, forexMap: FOREXMAP, coinAInfo: coinMap.ETH as CoinInfo, coinBInfo: coinMap.LRC as CoinInfo, // @ts-ignore activity: { totalRewards: 241232132, myRewards: 1232.123, rewardToken: coinMap.ETH as CoinInfo, duration: { from: new Date('2021-1-1'), to: new Date(), }, }, APR: 56, tradeFloat: { priceU: 123, change: '0%', timeUnit: '24h', volume: Number('112312312'), floatTag: FloatTag.none, }, totalLPToken: 12132131, totalA: 0.002, totalB: 12344, totalAU: 0.002, totalBU: 12344, rewardToken: 'LRC', rewardA: 13, feeA: 121, feeB: 1232, isNew: true, isActivity: false, } return } const MarketWrap = withTranslation('common')((rest) => { let props: any = { ...rest, forexMap: FOREXMAP, coinAInfo: coinMap.ETH, coinBInfo: coinMap.LRC, tradeFloat: { priceU: +123, change: '+15%', timeUnit: '24h', volume: '112312312 USBD', floatTag: FloatTag.increase, }, } const RowStyled = styled(Grid)` & .MuiGrid-root:not(:last-of-type) > div { margin-right: ${({ theme }) => theme.unit * 3}px; } ` as typeof Grid return ( <> ) }) const SettingPanelWrap = (_rest: any) => { return } const AssetTitleWrap = (rest: any) => { // const dispatch = useDispatch(); const assetTitleProps: AssetTitleProps = { onShowReceive: () => {}, onShowSend: () => {}, accountId: 0, setHideL2Assets: () => undefined, hideL2Assets: false, assetBtnStatus: TradeBtnStatus.AVAILABLE, assetInfo: { totalAsset: 123456.789, priceTag: PriceTag.Dollar, }, } return ( <> ) } const Template: Story = withTranslation('common')((...rest) => { const url = `https://loopring.io/wallet?redpacket&id=${'sfgffddd'}&referrer=${'0x234234'}` // @ts-ignore // @ts-ignore return ( ) }) as Story export default { title: 'components/Block', component: TradeTitleWrap, argTypes: {}, } as Meta //@ts-ignore export const BlockStory = Template.bind({}) // SwitchPanel.args = {} ================================================ FILE: packages/component-lib/src/components/block/CollectionDetailView.tsx ================================================ import { Account, CollectionMeta, CopyIcon, copyToClipBoard, GET_IPFS_STRING, getShortAddr, ImageIcon, } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { Avatar, Box, BoxProps, Link, Typography } from '@mui/material' import { useTheme } from '@emotion/react' import { Button, useSettings } from '../../index' import { useTranslation } from 'react-i18next' import { sanitize } from 'dompurify' const StyledPaper = styled(Box)` background: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; ` //--color-box const HeaderBannerStyle = styled(Box)` background-image: url(${({ url }) => url}); background-position: center; background-repeat: no-repeat; background-size: cover; border-radius: ${({ theme }) => theme.unit}px; height: 100%; width: 100%; ` as (props: BoxProps & { url: string }) => JSX.Element export const CollectionDetailView = ({ collectionDate, getIPFSString, baseURL, account, setCopyToastOpen, setShowEdit, setShowManageLegacy, count, }: { collectionDate: Co getIPFSString: GET_IPFS_STRING baseURL: string account: Account setShowManageLegacy?: (item: Co) => void setShowEdit?: (item: Co) => void count: number setCopyToastOpen: (props: { isShow: boolean; type: string }) => void }) => { const theme = useTheme() const { isMobile } = useSettings() const { t } = useTranslation() const lageSize = isMobile ? { icon: 36, move: 20, width: 'var(--nft-small-avatar)', } : { icon: 48, move: 40, width: 'var(--nft-large-avatar)', } return ( {( collectionDate?.cached?.banner ?? getIPFSString(collectionDate?.banner ?? '', baseURL) ).startsWith('http') ? ( ) : ( )} {( collectionDate?.cached?.avatar ?? getIPFSString(collectionDate?.avatar ?? '', baseURL) ).startsWith('http') ? ( ) : ( )} { e.stopPropagation() copyToClipBoard(collectionDate?.contractAddress ?? '') setCopyToastOpen({ isShow: true, type: 'address' }) }} > {getShortAddr(collectionDate?.contractAddress ?? '')} {t('labelCollectionItemValue', { value: count, //collectionDate?.extends.count, })} {collectionDate?.nftType} {account.accAddress === collectionDate.owner ? ( <> {setShowEdit && collectionDate.extra?.properties?.isEditable && ( )} {setShowManageLegacy && collectionDate.extra?.properties?.isLegacy && collectionDate.extra?.properties?.isEditable && ( )} ) : ( <> )} ) } ================================================ FILE: packages/component-lib/src/components/block/CollectionMedia.tsx ================================================ import { CollectionMeta, GET_IPFS_STRING, ImageIcon, LegacyIcon, SoursURL, } from '@loopring-web/common-resources' import { Theme, useTheme } from '@emotion/react' import React from 'react' import { Box, BoxProps, Typography } from '@mui/material' import { cssBackground, EmptyDefault, MediaLabelStyled, NftImage, useImage } from '../../index' import styled from '@emotion/styled' import { useTranslation } from 'react-i18next' const BoxStyle = styled(Box)` ${(props) => cssBackground(props)}; width: 100%; //height: 100vw; position: relative; overflow: hidden; ` as (prosp: BoxProps & { theme: Theme }) => JSX.Element export const CollectionMedia = React.memo( React.forwardRef( ( { item, onRenderError, index, baseURL, getIPFSString, onClick, }: { item: Partial index?: number onRenderError: (popItem: Partial, index?: number) => void baseURL: string getIPFSString: GET_IPFS_STRING onClick?: (e: any) => void // isOrigin?: boolean; // shouldPlay?: boolean; }, ref: React.ForwardedRef, ) => { const theme = useTheme() const { t } = useTranslation() const { hasLoaded, hasError } = useImage( item?.cached?.tileUri ?? getIPFSString(item.tileUri, baseURL) ?? '', ) React.useEffect(() => { if (hasError) { onRenderError(item, index) } }, [hasError, item, index]) return ( {!hasLoaded ? ( ) : ( {!!item.tileUri && !hasError ? ( onRenderError(item, index)} src={item?.cached?.tileUri ?? getIPFSString(item.tileUri, baseURL)} /> ) : ( } message={() => ( <> // // {t("labelNoCollectionCover")} // )} /> )} )} {item.extra?.properties?.isLegacy ? ( {t('labelLegacy')} ) : ( '' )} ) }, ), ) ================================================ FILE: packages/component-lib/src/components/block/DepthRaw.tsx ================================================ import { Box, Grid, Typography } from '@mui/material' import { MarketInfo } from '@loopring-web/loopring-sdk' import { WithTranslation, withTranslation } from 'react-i18next' import { DepthViewData, MarketRowHeight } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { useSettings } from '../../stores' export type Row = DepthViewData & { type: DepthType onClick: (event: MouseEvent, chooseDepth: DepthViewData, type: DepthType) => void } export const GridStyle = styled(Grid)` margin: 0; &:hover { background: var(--color-box-hover); transition: background 0.4s ease-out; } & > .MuiGrid-item { padding-top: 0; padding-left: 0; } ` as typeof Grid export enum DepthType { ask = 'ask', bid = 'bid', } export const Depth = ({ onClick, depthLevel, ...rest }: Row & { depthLevel?: number }) => { // const theme = useTheme(); const { isMobile } = useSettings() const { price, amtForShow, amtTotalForShow, percentage, type } = rest const digitNum = depthLevel || 0 let formattedPrice = price as any let [_init, _dot] = String(price || '').split('.') if (_dot) { const dotLen = _dot.length if (dotLen < digitNum) { for (let i = dotLen; i < digitNum; i++) { _dot += '0' } formattedPrice = _init + '.' + _dot } } else { let fakeDot = '.' for (let i = 0; i < digitNum; i++) { fakeDot += '0' } formattedPrice += fakeDot } const color = type === DepthType.ask ? 'var(--color-error)' : 'var(--color-success)' return ( onClick(e as any, { ...rest }, type)} > {formattedPrice} {amtForShow} {!isMobile && ( {amtTotalForShow} )} ) } export const DepthTitle = withTranslation('common')( ({ marketInfo, t, }: { marketInfo: MarketInfo } & WithTranslation) => { // @ts-ignore const [, baseSymbol, quoteSymbol] = marketInfo?.market?.match(/(\w+)-(\w+)/i) const { isMobile } = useSettings() return marketInfo?.market ? ( {t('labelDepthPrice', { symbol: quoteSymbol })} {t('labelDepthAmount', { symbol: baseSymbol })} {!isMobile && ( {t('labelDepthTotal')} )} ) : ( <> ) }, ) export const DepthBlock = withTranslation('common')( ({ depths, onClick, // tokenBaseInfo, type, depthLevel, }: { onClick: (event: MouseEvent, chooseDepth: DepthViewData, type: DepthType) => void type: DepthType // quotePrecision:number, depths: DepthViewData[] marketInfo: MarketInfo depthLevel?: number } & WithTranslation) => { return ( <> {depths.map((depth, index) => { // const amt_p = ; // // const amtTotal_p = getValuePrecisionThousand(toBig(depth.amtTotal).div('1e' + tokenBaseInfo.decimals), // undefined, undefined, marketInfo.precisionForPrice, true); return ( ) })} ) }, ) ================================================ FILE: packages/component-lib/src/components/block/DownloadPanel.tsx ================================================ import { useTheme } from '@emotion/react' import { SoursURL } from '@loopring-web/common-resources' import { Box, Link } from '@mui/material' import { WithTranslation, withTranslation } from 'react-i18next' export const DownloadPanel = withTranslation(['common', 'layout'])( ({ t, viewMoreUrl }: WithTranslation & { viewMoreUrl: string }) => { const theme = useTheme() return ( {'GooglePlay'} {'Android'} {'AppStore'} {t('labelDownloadViewMore')} ) }, ) ================================================ FILE: packages/component-lib/src/components/block/ETHStakingDetail.tsx ================================================ import { Box, Divider, Typography } from '@mui/material' import { ChartType, DEFI_ADVICE_MAP, myLog, SoursURL, YEAR_DAY_MINUTE_FORMAT, UpColor, } from '@loopring-web/common-resources' import moment from 'moment' import TrendAprChart from '../charts/scaleAreaChart/APRChart' import { useSettings } from '../../stores' import React, { useEffect } from 'react' export const ETHStakingDetail = ({ symbol, trends, defiInfo: _defiInfo }: any) => { myLog('trends', trends) const [, baseSymbol] = symbol.match(/(\w+)-(\w+)/i) const { upColor } = useSettings() const colorRight = upColor === UpColor.green ? ['var(--color-success)', 'var(--color-error)'] : ['var(--color-error)', 'var(--color-success)'] const [defiInfo, setDefiInfo] = React.useState(undefined as undefined | { apy: string, timestamp: number }) useEffect(() => { setDefiInfo({ apy: _defiInfo?.apy, timestamp: Date.now() }) }, [_defiInfo]) return symbol ? ( <> {DEFI_ADVICE_MAP[baseSymbol]?.project} {/*{t('labelEstAPR')}*/} {defiInfo && ( {(defiInfo?.apy?.toString().charAt(0) == '-' ? '' : '+') + defiInfo?.apy + '%' + ' APR'} {moment(defiInfo.timestamp).format(YEAR_DAY_MINUTE_FORMAT)} )} {!trends?.length ? ( {'loading'} ) : ( { setDefiInfo({ apy: props.apy, timestamp: props.createdAt }) }}/> )} ) : ( <> ) } ================================================ FILE: packages/component-lib/src/components/block/ErrorBlock.tsx ================================================ import { RESULT_INFO } from '@loopring-web/loopring-sdk' import { TOptions } from 'i18next' import { useTranslation } from 'react-i18next' import { useSettings } from '../../stores' import { L1L2_NAME_DEFINED, MapChainId, SDK_ERROR_MAP_TO_UI } from '@loopring-web/common-resources' export const TransErrorHelp = ({ error, options = {}, }: { error: RESULT_INFO options?: TOptions | string }) => { const { t } = useTranslation(['error']) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const errorItem = SDK_ERROR_MAP_TO_UI[error?.code ?? 700001] const _options = { ...errorItem?.options, ...options } if (errorItem) { return ( <> {t(errorItem.messageKey, { ..._options, layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) } else { return <>{error.message} } } ================================================ FILE: packages/component-lib/src/components/block/Interface.ts ================================================ import { CoinInfo, ForexMap, GET_IPFS_STRING, NFTWholeINFO, PriceTag, RedPacketQRPropsExtends, TradeBtnStatus, TradeFloat, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { RawDataRedPacketDetailItem } from '../tableList' export type MarketBlockProps = { coinAInfo: CoinInfo coinBInfo: CoinInfo tradeFloat: TradeFloat forexMap: ForexMap chartData?: { close: number timeStamp: number }[] } export type AssetTitleProps = { assetInfo: { isShow?: boolean totalAsset: number priceTag: (typeof PriceTag)[keyof typeof PriceTag] [key: string]: any } accountId: number onShowReceive: (token?: string) => void onShowSend: (token?: string) => void onClickBridge?: () => void showBridgeBtn?: boolean hideL2Assets: boolean setHideL2Assets: (value: boolean) => void assetBtnStatus: TradeBtnStatus forexMap: ForexMap isWebEarn?: boolean } export type AssetTitleMobileProps = AssetTitleProps & { // onShowNFTDeposit: () => void; // onShowNFTMINT: () => void; btnShowNFTDepositStatus?: keyof typeof TradeBtnStatus btnShowNFTMINTStatus?: keyof typeof TradeBtnStatus } export type NFTMedaProps = { item: Partial index?: number onNFTError: (popItem: Partial, index?: number) => void isOrigin?: boolean shouldPlay?: boolean getIPFSString: GET_IPFS_STRING baseURL: string } export type RedPacketDefaultBg = RedPacketDefault & { content: JSX.Element className?: string } export type RedPacketDefault = { type?: 'default' | 'official' | 'blindbox' size?: 'middle' | 'large' ImageEle?: JSX.Element | undefined } export type RedPacketTimeoutProps = RedPacketDefault & { sender: string memo?: string viewDetail?: () => void } export type RedPacketQRCodeProps = { url: string imageEleUrl?: string } & RedPacketQRPropsExtends export type RedPacketOpenProps = { sender: string amountStr: string memo: string viewDetail?: () => void onOpen: () => void } export type RedPacketUnreadyProps = { sender: string amountStr: string memo: string validSince: number // viewDetail: () => void; onClickOpen: () => void } export type RedPacketOpenedProps = { sender: string amountStr: string myAmountStr: string | undefined memo: string viewDetail: () => void } export const RedPacketDetailLimit = 5 export const RedPacketNFTDetailLimit = 5 export const RedPacketBlindBoxLimit = 8 export type RedPacketDetailProps = { redPacketType: 'normal' | 'lucky' | 'relay' sender: string amountStr: string amountClaimStr: string memo: string myAmountStr: string claimList: RawDataRedPacketDetailItem[] detail: sdk.LuckTokenClaimDetail isShouldSharedRely: boolean totalCount: number remainCount: number onShared: () => void page: number relyAmount?: string relyNumber?: string handlePageChange: (page: number, limit?: number) => void ImageEle?: JSX.Element | undefined showRelayText: boolean showShareBtn: boolean tokenSymbol?: string ended: boolean bottomButton: 'ended' | 'share' | 'hidden' claimButton: 'claim' | 'claimed' | 'expired' | 'hidden' | 'claiming' onClickClaim?: () => void totalNumber: number showReceiptListBtn: boolean } export type RedPacketBlindBoxDetailTypes = | 'Not Started' | 'Blind Box Started' | 'Lottery Started' | 'Lottery Started and Win Lottery' | 'Lottery Started and Not Win Lottery' | 'BlindBox Claime Detail' export type RedPacketBlindBoxDetailProps = { sender: string memo: string NFTURL?: string // Not Started: Phase 1, can't get blind boxs, only red packet sender can view this detail // Blind Box Started: Phase 2, can get blind boxs, everyone can view this detail // Lottery Started: Phase 3, users can participate in lottery if they have blind boxs, everyone can view this detail // Lottery Started And Open: Phase 3, Same as 'Lottery Started' but one more Popup to show if win NFTs // BlindBox Claime Detail: Phase 2 or Phase 3, shows detail of blindboxs distribution. type: RedPacketBlindBoxDetailTypes blindBoxStartTime?: number lotteryStartTime?: number lotteryEndTime?: number opendBlindBoxAmount: number totalBlindBoxAmount: number deliverdGiftsAmount: number totalGiftsAmount: number imageEle?: JSX.Element | undefined onShared?: () => void onClickViewDetail?: () => void NFTClaimList?: { who: string isMe: boolean when: number amount: number showLuckiest?: boolean showMultiplier: boolean }[] BlindBoxClaimList?: { who: string isMe: boolean when: number amount: number }[] showOpenLottery?: boolean wonPrizeInfo?: | { name: string url: string isNFT: true } | { amountStr: string tokenName: string isNFT: false } onClickClaim?: () => void onCloseOpenModal?: () => void onClickClaimDetailBack?: () => void description: string shareButton: 'hidden' | 'share' claimButton: 'claimed' | 'claim' | 'claiming' | 'expired' | 'hidden' | 'ended' didClaimABlindBox: boolean wonInfo: | { participated: boolean won: boolean amount: number isNFT: true } | { participated: boolean won: boolean amount: string total: string symbol: string isNFT: false } page: number totalCount: number remainCount: number handlePageChange: (page: number, limit?: number) => void totalClaimedNFTsCount: number totalBlindboxCount: number pageForBlindbox: number handlePageChange_BlindBox: (page: number, limit?: number) => void // didClaimABlindBox: boolean; onClickClaimPopViewDetail: () => void expired: boolean isTokenBlindbox: boolean remainGiftsAmount: string showReceiptListBtn: boolean targets?: string[] } export type RedPacketClockProps = RedPacketDefault & { validSince: number sender: string amountStr: string memo: string showRedPacket: () => void // viewDetail: () => void; // onOpen: () => void; } ================================================ FILE: packages/component-lib/src/components/block/LoadingBlock.tsx ================================================ import { SoursURL } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { Box } from '@mui/material' const StyleBlock = styled(Box)` background: var(--color-global-bg); svg path, svg rect { fill: var(--color-primary); } ` as typeof Box export const LoadingBlock = () => { return (
    {'loading'}
    ) } ================================================ FILE: packages/component-lib/src/components/block/MarketBlock.tsx ================================================ import { WithTranslation } from 'react-i18next' import { CoinKey, CurrencyToTag, EmptyValueTag, getValuePrecisionThousand, PriceTag, } from '@loopring-web/common-resources' import { Box, BoxProps, Grid, Typography } from '@mui/material' import styled from '@emotion/styled' import { floatTag, MarketBlockProps, useSettings } from './../../index' import { ChartType, ScaleAreaChart } from '../charts' type StyledProps = { custom: any } const MarketBlockStyled = styled(Box)` min-height: ${({ theme }) => theme.unit * 14.625}px; & { background: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; border: 1px solid var(--color-box); cursor: pointer; } ${({ theme, custom }) => floatTag({ theme, custom })}; .left-block { min-width: 76px; } .float-group span { display: flex; align-items: flex-end; } &:hover { box-shadow: var(--shadow-hover); } ` as (props: StyledProps & BoxProps) => JSX.Element export const MarketBlock = , I>({ coinAInfo, t, coinBInfo, tradeFloat, chartData = [], handleBlockClick, forexMap, }: WithTranslation & MarketBlockProps & { handleBlockClick: () => void }) => { const { upColor, currency } = useSettings() const { volume, coinApriceU, marketPrecision, coinBPrecision } = tradeFloat as any const baseFaitPrice = getValuePrecisionThousand( coinApriceU * (forexMap[currency] ?? 0), undefined, undefined, undefined, true, { isFait: true, }, ) return ( {coinAInfo && coinBInfo ? ( {coinAInfo?.simpleName} {` / `} {coinBInfo.simpleName} {tradeFloat.close ? ( {getValuePrecisionThousand( tradeFloat?.close, undefined, undefined, marketPrecision, true, { isPrice: true }, )} {PriceTag[CurrencyToTag[currency]]} {baseFaitPrice} ) : ( '' )} {tradeFloat.change ? `${tradeFloat.change > 0 ? '+' : ''}${getValuePrecisionThousand( tradeFloat.change, 2, 2, 2, true, )}%` : EmptyValueTag + '%'} {t('labelAmount') + ' '} : {getValuePrecisionThousand( volume, coinBPrecision, coinBPrecision, undefined, true, )}  {coinBInfo.simpleName} ) : ( <> )} ) } ================================================ FILE: packages/component-lib/src/components/block/NotificationPanel.tsx ================================================ import { Trans, useTranslation } from 'react-i18next' import { ACTIVITY, Layer2RouterID, NOTIFICATION_ITEM, NOTIFICATIONHEADER, RouterPath, SoursURL, } from '@loopring-web/common-resources' import { Box, Divider, Grid, Link, Typography } from '@mui/material' import { EmptyDefault, ListItemActivity, NotificationListItem } from '../basic-lib' import styled from '@emotion/styled' import { NotificationItem } from '@loopring-web/core' import { useHistory } from 'react-router-dom' const BoxStyle = styled(Box)` background: var(--color-pop-bg); box-shadow: var(--shadow); .MuiInputBase-root { background: var(--opacity); text-align: right; } ` as typeof Box export const NotificationPanel = ({ notification: { myNotifyMap, notifyMap, chainId, account }, onClickExclusiveredPacket, showExclusiveRedpacket, exclusiveRedpacketCount, closePop, }: { notification: NOTIFICATIONHEADER & { chainId: number; account } onClickExclusiveredPacket: () => void showExclusiveRedpacket: boolean exclusiveRedpacketCount: number closePop?: () => void }) => { const { t } = useTranslation() const history = useHistory() const notifications = notifyMap?.notifications?.reduce((prev, item) => { if (item.endShow >= Date.now() && item.startShow <= Date.now() && item.webFlag) { prev.push(item) } return prev }, [] as NOTIFICATION_ITEM[]) let activities = notifyMap?.activities?.reduce((prev, item) => { if (item.endShow >= Date.now() && item.startShow <= Date.now() && item.webFlag) { prev.push(item) } return prev }, [] as ACTIVITY[]) activities = notifyMap?.activitiesInvest?.reduce((prev, item) => { if (item.endShow >= Date.now() && item.startShow <= Date.now() && item.webFlag) { prev.push(item) } return prev }, activities) const hasActivities = activities && activities?.length const hasNotifications = notifications && notifications?.length return ( {myNotifyMap?.total !== undefined && ( <> {t('labelTotalUnRead', { total: myNotifyMap?.total ?? 0 })} { history.push(`${RouterPath.layer2}/${Layer2RouterID.notification}`) closePop && closePop() }} color={'primary'} > {t('labelReadAll')} {myNotifyMap?.total ? ( {myNotifyMap?.items?.reduce((prev, ele, index) => { if (index < 3) { prev.push( { history.push(`${RouterPath.layer2}/${Layer2RouterID.notification}`) closePop && closePop() }} /> , ) } return prev }, [])} ) : ( )} )} {!!(showExclusiveRedpacket || hasActivities || hasNotifications) && ( <> {showExclusiveRedpacket && ( { onClickExclusiveredPacket() closePop && closePop() }} sx={{ backgroundImage: `url(${SoursURL + 'images/target_pop_bg.png'})`, backgroundSize: 'contain', width: '343px', height: '77px', borderRadius: 2, paddingLeft: 3.5, paddingTop: 2.5, cursor: 'pointer', marginTop: 1, marginBottom: !!hasActivities ? 1 : 0, }} > Congratulations! You've received {exclusiveRedpacketCount} exclusive Red Packet! )} {!!hasActivities && activities.map((activity, index) => ( ))} {!!hasNotifications && ( <> {!!hasActivities && } {notifications?.map((notify, index) => ( ))} )} )} {!( myNotifyMap?.items?.length || showExclusiveRedpacket || hasActivities || hasNotifications ) && ( Content is Empty} /> )} ) } ================================================ FILE: packages/component-lib/src/components/block/RedPacket.tsx ================================================ import styled from '@emotion/styled' import { Box, BoxProps, Button, Divider, IconButton, Link, Modal, Typography } from '@mui/material' import React from 'react' import { Trans, useTranslation } from 'react-i18next' import { Account, BackIcon, EmptyValueTag, FirstPlaceIcon, GET_IPFS_STRING, getShortAddr, NFTWholeINFO, RedPacketColorConfig, RedPacketCssColorConfig, RedPacketOpenWrapSVG, RedPacketQRCodeSvg, RedPacketWrapSVG, SoursURL, YEAR_DAY_MINUTE_FORMAT, DAY_MINUTE_FORMAT, hexToRGB, } from '@loopring-web/common-resources' import QRCodeStyling from 'qr-code-styling' import * as sdk from '@loopring-web/loopring-sdk' import { RedPacketViewStep } from '../modal' import { ModalStatePlayLoad } from '../../stores' import moment from 'moment' import { RedPacketBlindBoxDetailProps, RedPacketBlindBoxLimit, RedPacketClockProps, RedPacketDefault, RedPacketDefaultBg, RedPacketDetailLimit, RedPacketDetailProps, RedPacketNFTDetailLimit, RedPacketOpenedProps, RedPacketOpenProps, RedPacketQRCodeProps, RedPacketTimeoutProps, RedPacketUnreadyProps, } from './Interface' import { BoxNFT, CoinIcon, ModalCloseButtonPosition, TablePagination } from '../basic-lib' import { NFTMedia } from './nftMedia' import { sanitize } from 'dompurify' import { useTheme } from '@emotion/react' import { ReceiptListModal } from '../tradePanel/components/CreateRedPacketWrap' export const RedPacketBg = styled(Box)` display: flex; align-items: center; position: relative; justify-content: center; .content { color: ${({ type }) => RedPacketCssColorConfig[type]?.primaryColor}; .betweenEle { left: 50%; top: 128px; position: absolute; .open { background: #fff7b1; color: ${({ type }) => RedPacketCssColorConfig[type]?.highLightColor}; //#7c3400; &.disable { color: ${({ type }) => RedPacketCssColorConfig[type]?.highLightDisableColor}; //#7c3400; } cursor: pointer; display: inline-flex; z-index: 100; align-items: center; justify-content: center; width: 64px; height: 64px; content: 'Open'; font-size: 20px; font-weight: 500; border-radius: 100%; transform: translate(-50%, -50%); } .open.openUnready { background: url('${SoursURL}/images/redpacketLock.webp') center no-repeat; color: ${({ type }) => RedPacketCssColorConfig[type]?.colorTop}; //#7c3400; width: 76px; height: 76px; background-size: contain; border-radius: initial; } .clock { display: flex; z-index: 100; align-items: center; justify-content: center; position: absolute; font-size: 28px; font-weight: 900; transform: translate(-50%, -50%); left: 50%; top: -50%; .hours, .minutes, .seconds { justify-content: center; height: 52px; width: 52px; background: #fff7b1; color: #7c3400; display: inline-flex; align-items: center; border-radius: ${({ theme }) => theme.unit + 'px'}; h4 { text-indent: -9999em; height: 0; width: 0; } } .hours, .minutes { position: relative; &:after { display: block; content: ':'; position: absolute; font-size: 20px; right: -12px; line-height: 52px; top: 0; } } } } .secondary { color: ${({ type }) => RedPacketCssColorConfig[type]?.secondaryColor}; } .viewDetail { color: ${({ type }) => RedPacketCssColorConfig[type]?.primaryColor}; &:hover { text-decoration: underline; //color: ${({ type }) => RedPacketCssColorConfig[type]?.secondaryColor}; } } .top { height: 140px; display: flex; align-items: center; justify-content: center; color: ${({ type }) => RedPacketCssColorConfig[type]?.primaryColor}; } .middle { height: 218px; display: flex; align-items: center; justify-content: center; } .footer { display: flex; align-items: center; justify-content: center; } } &.RedPacketClock { .top { height: 40px; margin-top: 50px; } .middle { margin-top: 40px; height: 128px; display: flex; align-items: center; justify-content: center; } .betweenEle { top: 328px; } } &.redPacketOpened { .top { color: ${({ type }) => RedPacketCssColorConfig[type]?.highLightColor}; } } &.redPacketOpened, &.redPacketOpen { .redPacketNFT { margin-top: 48px; padding-top: 0; width: var(--nft-large-avatar); height: var(--nft-large-avatar); } .RedPacketReceived .redPacketNFT { margin-top: 20px; } } //&.redPacketOpened { // .content { // // } //} ` as (props: BoxProps & { imageSrc?: string; type: string }) => JSX.Element export const BoxClaim = styled(Box)` &.self { //background-color: var(--field-opacity); } ` as typeof Box export const RedPacketSize = { middle: { height: 414, width: 260, }, large: { height: 600, width: 320, }, } const qrCode = new QRCodeStyling({ type: 'svg', width: 200, height: 200, image: `${SoursURL + 'svg/loopring.svg'}`, dotsOptions: { gradient: { type: 'linear', rotation: 45, colorStops: [ { offset: 0, color: '#4169FF', // hardcode for export png }, { offset: 1, color: '#000', }, ], }, type: 'dots', }, backgroundOptions: { color: '#ffffff', //colorConfig.bgColor }, imageOptions: { crossOrigin: 'anonymous', margin: 4, }, cornersSquareOptions: { type: 'extra-rounded', }, cornersDotOptions: { type: 'square', }, }) export const RedPacketQRCode = ({ type = 'default', imageEleUrl, url, ...rest }: RedPacketDefault & RedPacketQRCodeProps) => { const qrcodeRef = React.createRef() const ref = React.useRef() const [qrCodeG, setQrCodeG] = React.useState(undefined) const updateSvg = React.useCallback(async () => { qrCode.update({ data: url, }) const svgEle = await qrCode._getElement('svg') setQrCodeG(svgEle?.innerHTML) }, [url]) React.useEffect(() => { updateSvg() }, [url]) const onClick = (e: React.MouseEvent) => { try { // @ts-ignore-start const svg: SVGElement = ref.current as SVGElement const w = parseInt(svg.getAttribute('width') ?? '334') const h = parseInt(svg.getAttribute('height') ?? '603') if (svg && svg.outerHTML) { const canvas = document.createElement('canvas') const base64doc = btoa(unescape(encodeURIComponent(svg.outerHTML))) const img_to_download = document.createElement('img') img_to_download.src = 'data:image/svg+xml;base64,' + base64doc img_to_download.onload = function () { canvas.setAttribute('width', w.toString()) canvas.setAttribute('height', h.toString()) // @ts-ignore const context: CanvasRenderingContext2D = canvas.getContext('2d') context.drawImage(img_to_download, 0, 0, w, h) const dataURL = canvas.toDataURL('image/png') // @ts-ignore if (window.navigator.msSaveBlob) { // @ts-ignore window.navigator.msSaveBlob( // @ts-ignore canvas.msToBlob(), 'Loopring_Red_Packet.png', ) e.preventDefault() } else { const a = document.createElement('a') const my_evt = new MouseEvent('click') a.download = 'Loopring_Red_Packet.png' a.href = dataURL a.dispatchEvent(my_evt) } //canvas.parentNode.removeChild(canvas); } } // @ts-ignore-end } catch (error) {} } return ( <> {qrCodeG && ( )} ) } export const RedPacketBgDefault = ({ type = 'default', size = 'middle', className, content, }: RedPacketDefaultBg & any) => { const scale = RedPacketSize[size].width / 260 return ( {content} ) } export const RedPacketBgOpened = ({ type = 'default', size = 'middle', content, }: RedPacketDefaultBg & any) => { const scale = RedPacketSize[size].width / 260 return ( {content} ) } export const RedPacketOpen = ({ type = 'default', size, sender, amountStr, memo, viewDetail, onOpen, ImageEle, }: RedPacketDefault & RedPacketOpenProps) => { const { t } = useTranslation() const content = React.useMemo(() => { return ( {t('labelRedPacketOpen')} {sender} {ImageEle} {amountStr} {viewDetail && ( { e.stopPropagation() viewDetail() }} > {t('labelLuckyRedPacketDetail')} )} ) }, [size, sender, amountStr, memo, viewDetail, onOpen]) return } export const RedPacketClock = ({ type = 'default', size, validSince, sender, memo, showRedPacket, ImageEle, }: RedPacketDefault & RedPacketClockProps) => { const { t } = useTranslation('common') const anchorRef = React.useRef() const nodeTimer = React.useRef(-1) const [countDown, setCountDown] = React.useState<{ days: undefined | string hours: undefined | string seconds: undefined | string minutes: undefined | string }>() const calculateTimeLeft = React.useCallback((validSince: number) => { if (nodeTimer.current !== -1) { clearTimeout(nodeTimer.current as NodeJS.Timeout) } let difference = +new Date(validSince).getTime() - Date.now() if (difference > 0) { setCountDown({ days: Math.floor(difference / (1000 * 60 * 60 * 24)).toString(), hours: ('0' + Math.floor((difference / (1000 * 60 * 60)) % 24).toString()).slice(-2), minutes: ('0' + Math.floor((difference / 1000 / 60) % 60).toString()).slice(-2), seconds: ('0' + Math.floor((difference / 1000) % 60).toString()).slice(-2), }) nodeTimer.current = setTimeout(() => calculateTimeLeft(validSince), 1000) } else { showRedPacket() } }, []) React.useEffect(() => { calculateTimeLeft(validSince) return () => { if (nodeTimer.current !== -1) { clearTimeout(nodeTimer.current as NodeJS.Timeout) } } }, [validSince]) const content = React.useMemo(() => { return ( <> {t('labelCountDown')} {Number(countDown?.hours) >= 0 ? countDown?.hours : EmptyValueTag} {t('labelHours')} {Number(countDown?.minutes) >= 0 ? countDown?.minutes : EmptyValueTag} {t('labelMinutes')} {Number(countDown?.seconds) >= 0 ? countDown?.seconds : EmptyValueTag} {t('labelSeconds')} {sender} {ImageEle} ) }, [countDown]) return ( ) } export const RedPacketUnready = ({ type = 'default', size, sender, validSince, amountStr, memo, ImageEle, onClickOpen, }: // ImageEle, RedPacketDefault & RedPacketUnreadyProps) => { const { t } = useTranslation() const content = React.useMemo(() => { return ( {t('labelRedPacketOpen')} {sender} {`${moment(validSince).format(DAY_MINUTE_FORMAT)} ${t('labelOpenStart')}`} {ImageEle} {amountStr} ) }, [size, sender, amountStr, memo]) return } export const RedPacketOpened = ({ type = 'default', size, sender, memo, myAmountStr, amountStr, viewDetail, ImageEle, }: RedPacketDefault & RedPacketOpenedProps) => { const { t } = useTranslation('common') const content = React.useMemo(() => { return ( {type === 'blindbox' ? t('labelRedPacketBlindboxReceived1') : t('labelRedPacketReceived')} {type === 'blindbox' && ( {t('labelRedPacketBlindboxReceived2')} )} {type !== 'blindbox' && ( <> {myAmountStr ? myAmountStr : EmptyValueTag} {t('labelTotalRedPacket', { value: amountStr })} )} {ImageEle} {sender} { e.stopPropagation() viewDetail() }} > {t('labelLuckyRedPacketDetail')} ) }, [type]) return } export const RedPacketDetailStyled = styled(Box)` border-radius: ${({ theme }) => theme.unit}px; background-color: var(--color-box); ` as typeof Box export const RedPacketTimeout = ({ type = 'default', size, sender, memo, viewDetail, }: RedPacketTimeoutProps) => { const { t } = useTranslation('common') const content = React.useMemo(() => { return ( {t('labelLuckyRedPacketTimeout')} {sender} {viewDetail && ( { e.stopPropagation() viewDetail() }} > {t('labelLuckyRedPacketDetail')} )} ) }, []) return } const BoxStyle = styled(Box)` background: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; .redPacketNFT { margin-top: ${({ theme }) => 2 * theme.unit}px; padding-top: var(--nft-large-avatar); } .top { border-radius: ${({ theme }) => theme.unit}px; border-bottom-right-radius: 100%; border-bottom-left-radius: 100%; } ` export const RedPacketDetail = ({ redPacketType, sender, amountStr, // _amountClaimStr, memo, // page = 1, claimList, // detail, // detail, handlePageChange, myAmountStr, totalCount, remainCount, onShared, relyNumber, relyAmount, ImageEle, showRelayText, tokenSymbol, detail, bottomButton, page, onClickClaim, claimButton, totalNumber, showReceiptListBtn, }: RedPacketDetailProps) => { const { t } = useTranslation('common') const showLucky = detail.luckyToken?.tokenAmount?.remainCount == 0 const limit = detail.luckyToken.isNft ? RedPacketNFTDetailLimit : RedPacketDetailLimit const pageNation = totalNumber - limit > 0 && ( { handlePageChange(_page) }} /> ) const theme = useTheme() const isTarget = detail.luckyToken.type.scope === sdk.LuckyTokenViewType.TARGET const [showExclusiveReceipt, setShowExclusiveReceipt] = React.useState(false) return ( {t(`label${redPacketType}RedPacket`)} {sender} {isTarget && ( {t('labelRedPacketExclusiveTag')} )} {memo ?? ''}{' '} {ImageEle} {myAmountStr ? myAmountStr : EmptyValueTag} {t('labelTotalRedPacket', { value: amountStr })} {/**/} {t('labelRedPacketReceivedRecord', { value: totalCount - remainCount, count: totalCount, })} {/**/} {claimList && claimList.map((item, index) => { return ( {item.accountStr} {item.isSelf ? ` (${t('labelRedPacketMe')})` : ''} {item.amountStr} {moment(new Date(item.createdAt), 'YYYYMMDDHHMM').fromNow()} {item.helper && ( {item.helper} Help )} {showLucky && (redPacketType === 'lucky' || redPacketType === 'relay') && item.isMax && ( {t('labelLuckDraw')} )} {index + 1 !== claimList.length && ( )} ) })} {pageNation} {showReceiptListBtn && ( )} setShowExclusiveReceipt(false)} targets={(detail as any).targets} /> {/* {showShareBtn && ( */} {claimButton === 'claim' ? ( ) : claimButton === 'expired' && bottomButton === 'hidden' ? ( ) : claimButton === 'claimed' && bottomButton === 'hidden' ? ( ) : claimButton === 'claiming' && bottomButton === 'hidden' ? ( ) : ( <> )} {bottomButton === 'share' ? ( claimButton === 'claim' ? ( ) : ( ) ) : ( bottomButton === 'ended' && ( ) )} {showRelayText && ( have {relyNumber ?? EmptyValueTag} friends help you pick up Redpacket, you extends reward: {relyAmount ?? EmptyValueTag} )} {/* )} */} ) } export const RedPacketPrepare = ({ chainId, account, tokenInfo, setShowRedPacket, claim, _type = 'default', amountStr, myAmountStr, onOpen, getIPFSString, baseURL, claimed, ...props }: { chainId: sdk.ChainId account: Account amountStr: string tokenInfo: sdk.TokenInfo claim: string | undefined myAmountStr: string | undefined setShowRedPacket: ( state: ModalStatePlayLoad & { step?: number info?: { [key: string]: any } }, ) => void baseURL: string getIPFSString: GET_IPFS_STRING onOpen: () => void _type?: 'official' | 'default' | 'blindbox' claimed: boolean } & sdk.LuckyTokenItemForReceive) => { // const { t } = useTranslation("common"); const ref = React.createRef() const _info = props as sdk.LuckyTokenItemForReceive // const props.isNft; const ImageEle = React.useMemo(() => { return props.isNft ? ( } shouldPlay={true} onNFTError={() => undefined} isOrigin={true} getIPFSString={getIPFSString} baseURL={baseURL} /> ) : ( <> ) }, [props]) // following code is for triggering rerender const getDifferenceStatus = () => { const difference = new Date(_info.validSince).getTime() - Date.now() return difference > 180000 ? 0 : difference > 0 ? 1 : 2 } const [differenceStatus, setDifferenceStatus] = React.useState(getDifferenceStatus()) React.useEffect(() => { setInterval(() => { setDifferenceStatus(getDifferenceStatus) }, 1000) }, []) const viewItem = React.useMemo(() => { let difference = new Date(_info.validSince).getTime() - Date.now() if (claimed) { return ( { if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, step: RedPacketViewStep.BlindBoxDetail, info: { ..._info, }, }) } else { setShowRedPacket({ isShow: true, step: RedPacketViewStep.DetailPanel, info: { ..._info, }, }) } }} sender={props?.sender?.ens ? props?.sender?.ens : getShortAddr(props?.sender?.address)} memo={props?.info?.memo} type={_type} /> ) } else if (difference > 180000) { return ( { // if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, step: RedPacketViewStep.RedPacketClock, info: { ..._info, }, }) }} /> ) } else if (difference > 0) { return ( { // do nothing // setShowRedPacket({ // isShow: true, // step: RedPacketViewStep.OpenPanel, // info: _info, // }); }} amountStr={amountStr} validSince={_info.validSince} type={_type} /> ) } else if ( // difference + 86400000 < 0 || _info.status == sdk.LuckyTokenItemStatus.COMPLETED || _info.status == sdk.LuckyTokenItemStatus.OVER_DUE || _info.tokenAmount.remainCount === 0 ) { return ( { if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, step: RedPacketViewStep.BlindBoxDetail, info: { ..._info, }, }) } else { setShowRedPacket({ isShow: true, step: RedPacketViewStep.DetailPanel, info: { ..._info, }, }) } }} /> ) } else { return ( { setShowRedPacket({ isShow: false, step: RedPacketViewStep.Loading, info: _info, }) onOpen() }} viewDetail={ _info.sender.accountId === account.accountId ? () => { if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, step: RedPacketViewStep.BlindBoxDetail, info: { ..._info, }, }) } else { setShowRedPacket({ isShow: true, step: RedPacketViewStep.DetailPanel, info: { ..._info, }, }) } } : undefined } /> ) } }, [amountStr, claim, myAmountStr, onOpen, _info, differenceStatus]) return {viewItem} } const BlindBoxDetailBoxStyle = styled(Box)` background: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; .redPacketNFT { margin-top: ${({ theme }) => 2 * theme.unit}px; padding-top: var(--nft-large-avatar); } .top { border-radius: ${({ theme }) => theme.unit}px; border-bottom-right-radius: 100%; border-bottom-left-radius: 100%; } .viewDetail { color: ${RedPacketCssColorConfig.default?.primaryColor}; &:hover { text-decoration: underline; } } ` export const RedPacketBlindBoxDetail = ({ sender, memo, type, NFTURL, blindBoxStartTime, lotteryStartTime, lotteryEndTime, opendBlindBoxAmount, totalBlindBoxAmount, deliverdGiftsAmount, totalGiftsAmount, onShared, onClickViewDetail, NFTClaimList, BlindBoxClaimList, showOpenLottery, wonPrizeInfo, onClickClaim, onCloseOpenModal, onClickClaimDetailBack, description, shareButton, claimButton, didClaimABlindBox, wonInfo, page, handlePageChange, totalClaimedNFTsCount, handlePageChange_BlindBox, pageForBlindbox, totalBlindboxCount, onClickClaimPopViewDetail, expired, isTokenBlindbox, remainGiftsAmount, showReceiptListBtn, targets, }: RedPacketBlindBoxDetailProps) => { const { t } = useTranslation('common') const theme = useTheme() const emptyImg = theme.mode === 'dark' ? SoursURL + 'images/redpackBlind1.webp' : SoursURL + 'images/redpackBlind2.webp' const [showExclusiveReceipt, setShowExclusiveReceipt] = React.useState(false) const pageNation = totalClaimedNFTsCount - RedPacketNFTDetailLimit > 0 && ( { handlePageChange(_page) }} size={'small'} /> ) const pageNationBlindBox = totalBlindboxCount - RedPacketBlindBoxLimit > 0 && ( { handlePageChange_BlindBox(_page) }} size={'small'} /> ) const LooteryModal = ( <> {/* */} {wonPrizeInfo ? t('labelBlindBoxCongratulations') : t('labelBlindBoxSorry')} {wonPrizeInfo ? ( wonPrizeInfo.isNFT ? ( <> {wonPrizeInfo.name} {''} ) : ( <> {/* {""} */} {wonPrizeInfo.amountStr} {t('labelBlindBoxClaimHint')} ) ) : ( <> {t('labelBlindBoxNoRewards') + ' '} {''} )} {t('labelLuckyRedPacketDetail')} {/* */} {wonPrizeInfo && wonPrizeInfo.isNFT && ( )} ) if ( type === 'Lottery Started and Not Win Lottery' || type === 'Lottery Started and Win Lottery' ) { return LooteryModal } return ( {LooteryModal} {type === 'BlindBox Claime Detail' && ( )} {isTokenBlindbox ? t('labelToken') : t('labelRedPacketMarketNFT')}/ {t('labelLuckyBlindBox')} {type === 'BlindBox Claime Detail' ? ( {t('labelLuckyRecievedBlindBox', { opendBlindBoxAmount, totalBlindBoxAmount, })} {/* Received Blind Box {opendBlindBoxAmount}/{totalBlindBoxAmount} */} {BlindBoxClaimList && BlindBoxClaimList.map((info) => { return ( {info.who} {info.isMe ? ` (${t('labelRedPacketMe')})` : ''} x 1 {moment(info.when).fromNow()} ) })} {pageNationBlindBox} ) : ( {sender} {!isTokenBlindbox && ( {NFTURL ? ( {''} ) : ( {''} )} )} {isTokenBlindbox && (type === 'Blind Box Started' || type === 'Not Started') && ( {''} )} {type === 'Blind Box Started' && didClaimABlindBox && ( {t('labelBlindBoxCongratulationsBlindBox')} )} {type === 'Lottery Started' && (wonInfo.isNFT ? ( wonInfo.participated ? ( wonInfo.won ? ( {`${wonInfo.amount} NFTs`} ) : ( {t('labelBlindBoxSorryBlindBox')} ) ) : ( {EmptyValueTag} ) ) : ( <> {wonInfo.participated ? ( wonInfo.won ? ( `${wonInfo.amount} ${wonInfo.symbol}` ) : ( {t('labelBlindBoxSorryBlindBox')} ) ) : ( {EmptyValueTag} )} {t('labelRedpacketTotalReward', { amount: `${wonInfo.total} ${wonInfo.symbol}`, })}{' '} ))} {description} {type !== 'Lottery Started' && ( <> {t('labelBlindBoxExplaination2', { opendBlindBoxAmount, totalBlindBoxAmount, // deliverdGiftsAmount, // totalGiftsAmount, remainingGiftsAmount: totalGiftsAmount - deliverdGiftsAmount, })} {/* {opendBlindBoxAmount} out of {totalBlindBoxAmount} blind boxes have been opened; {deliverdGiftsAmount} out of {totalGiftsAmount} gifts delivered. */} {t('labelBlindBoxExplaination3', { remainingGiftsAmount: remainGiftsAmount, })} )} {type === 'Not Started' && ( {t('labelBlindBoxNotStarted', { time: moment(blindBoxStartTime).format(YEAR_DAY_MINUTE_FORMAT), interpolation: { escapeValue: false, }, })} )} {(type === 'Not Started' || type === 'Blind Box Started') && ( {t('labelBlindBoxStarted', { time: moment(lotteryStartTime).format(YEAR_DAY_MINUTE_FORMAT), interpolation: { escapeValue: false, }, })} )} {(type === 'Not Started' || type === 'Blind Box Started') && isTokenBlindbox && ( {t('labelBlindBoxTokenHint', { time: moment(lotteryEndTime).format(YEAR_DAY_MINUTE_FORMAT), interpolation: { escapeValue: false, }, })} )} {!isTokenBlindbox && ( {t('labelBlindBoxClaimStarted', { time: moment(lotteryEndTime).format(YEAR_DAY_MINUTE_FORMAT), interpolation: { escapeValue: false, }, })} )} {/* {type === "Not Started" ? t("labelBlindBoxNotStarted", { time: moment(blindBoxStartTime).format( YEAR_DAY_MINUTE_FORMAT ), interpolation: { escapeValue: false, } }) : type === "Blind Box Started" ? t("labelBlindBoxStarted", { time: moment(lotteryStartTime).format( YEAR_DAY_MINUTE_FORMAT ), interpolation: { escapeValue: false, } }) : t("labelBlindBoxClaimStarted", { time: moment(lotteryEndTime).format(YEAR_DAY_MINUTE_FORMAT), interpolation: { escapeValue: false, } })} */} {(type === 'Blind Box Started' || type === 'Lottery Started') && ( { e.stopPropagation() onClickViewDetail!() }} > {t('labelLuckyRedPacketDetail')} )} {type === 'Lottery Started' && ( <> {isTokenBlindbox ? t('labelBlindBoxRecieved', { deliverdGiftsAmount, totalGiftsAmount, }) : t('labelBlindBoxRecievedNFT', { deliverdGiftsAmount, totalGiftsAmount, })} {NFTClaimList && NFTClaimList.map((info) => { return ( {info.who} {info.isMe ? ` (${t('labelRedPacketMe')})` : ''} {info.showMultiplier && 'x'} {info.amount} {moment(info.when).fromNow()} {info.showLuckiest && ( {t('labelLuckDraw')} )} ) })} {pageNation} )} setShowExclusiveReceipt(false)} targets={targets ?? []} /> {/* {(type === "Not Started" || type === "Blind Box Started") && ( */} {showReceiptListBtn && ( )} {shareButton === 'share' && ( )} {claimButton === 'claim' ? ( ) : claimButton === 'claimed' ? ( ) : claimButton === 'expired' ? ( ) : claimButton === 'ended' ? ( ) : undefined} {/* )} */} )} ) } ================================================ FILE: packages/component-lib/src/components/block/Referral.tsx ================================================ import React from 'react' import { ShareReferralSvg, ShareReferralSvgProps } from '@loopring-web/common-resources' export const ReferralImage = React.forwardRef( ( { src, width, height, ...props }: ShareReferralSvgProps, ref: React.ForwardedRef, ) => { // let name: any = ; // name = name[name.length - 1]; const [base64, setBase64] = React.useState('') const convertImageToBase64 = (imgUrl: string, callback: (dataUrl: string) => void) => { const image = new Image() image.crossOrigin = 'anonymous' image.onload = () => { const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') canvas.height = image.naturalHeight canvas.width = image.naturalWidth // @ts-ignore ctx.drawImage(image, 0, 0) const dataUrl = canvas.toDataURL() callback && callback(dataUrl) } image.src = imgUrl } convertImageToBase64(src, (dataUrl) => { setBase64(dataUrl) }) return ( <> {base64 && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/block/SettingPanel.tsx ================================================ import styled from '@emotion/styled' import { Box, Divider, FormControlLabel, Grid, Radio, SelectChangeEvent, Switch, Typography, } from '@mui/material' import React from 'react' import { CurrencyToTag, DropDownIcon, GrowIcon, i18n, LanguageType, PriceTag, ThemeType, UpColor, } from '@loopring-web/common-resources' import { OutlineSelect, OutlineSelectItem, RadioGroupStyle } from '../basic-lib' import { Trans, WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../stores' const StyledSwitch = styled(Switch)` margin: 0; ` const BoxStyle = styled(Box)` .MuiInputBase-root { background: var(--opacity); text-align: right; } ` as typeof Box export const BtnCurrency = ({ t, currency, label, handleChange }: any) => { const _handleChange = React.useCallback( (event: SelectChangeEvent) => { // setState(event.target.value) if (handleChange) { handleChange(event.target.value) } }, [handleChange], ) return ( {Object.keys(CurrencyToTag).map((item) => { return ( {item} - {PriceTag[CurrencyToTag[item]]} ) })} {/*¥ {t('labelCNYYuan')}*/} ) } const StyledDivider = styled(Divider)` margin: 0; ` export const BtnLanguage = ({ t, label, handleChange }: any) => { const _handleChange = React.useCallback( (event: SelectChangeEvent) => { if (handleChange) { handleChange(event.target.value) } }, [handleChange], ) return ( English {/*简体中文*/} ) } export const SettingPanel = withTranslation(['common', 'layout'], { withRef: true, })(({ t, ...rest }: WithTranslation) => { // const theme = useTheme(); const { setUpColor, setCurrency, setLanguage, currency, upColor, setTheme, themeMode, isMobile } = useSettings() const handleOnLanguageChange = React.useCallback( (value: any) => { setLanguage(value) }, [setLanguage], ) const handleOnCurrencyChange = React.useCallback( (value: any) => { setCurrency(value) }, [setCurrency], ) const handleColorChange = React.useCallback( (_e: any, value: any) => { setUpColor(value) }, [setUpColor], ) //const [mode, setMode] = React.useState(themeMode) const handleThemeClick = React.useCallback( (e: any) => { if (e.target.checked) { setTheme(ThemeType.dark) } else { setTheme(ThemeType.light) } }, [themeMode], ) const updown = React.useCallback( ({ key }: any) => { return ( <> color up and color down ) }, [UpColor], ) const styles = isMobile ? { flex: 1 } : { width: 'var(--swap-box-width)' } return ( {/*{t('labelTitleLayout')}*/} {t('labelLanguage')} {t('labelCurrency')} {t('labelColors')} {Object.keys(UpColor).map((key) => { return ( } label={updown({ key })} /> ) })} {t('labelTheme')} ) }) ================================================ FILE: packages/component-lib/src/components/block/ShareSocialPanel.tsx ================================================ export const ShareSocialPanel = () => { return <> } ================================================ FILE: packages/component-lib/src/components/block/TagIconList.tsx ================================================ import { Avatar, Tooltip, Typography } from '@mui/material' import React from 'react' import { CAMPAIGNTAGCONFIG, SCENARIO } from '@loopring-web/common-resources' import { useTheme } from '@emotion/react' export const TagIconList = React.memo( ({ campaignTagConfig, symbol, scenario, size, }: { campaignTagConfig: CAMPAIGNTAGCONFIG symbol: string size?: string scenario: SCENARIO }) => { const theme = useTheme() return ( {campaignTagConfig[scenario]?.map((item, index) => { return ( {item.symbols?.includes(symbol) && item.webFlag && item.endShow >= Date.now() && item.startShow <= Date.now() ? ( item?.behavior === 'tooltips' ? ( ) : ( { if (item?.behavior === 'link') { window.open(item.content, '_blank') window.opener = null } }} alt={item.name} variant={'square'} style={{ width: 'auto', // width: size ? size : "var(--svg-size-medium)", height: size ? size : 'var(--svg-size-medium)', marginRight: theme.unit / 2, }} src={item.iconSource} /> ) ) : ( <> )} ) })} ) }, ) ================================================ FILE: packages/component-lib/src/components/block/TradeRace.tsx ================================================ import React from 'react' import { Column, Table } from '../basic-lib' import { useTranslation } from 'react-i18next' import styled from '@emotion/styled' import { Box, Typography } from '@mui/material' import { EmptyValueTag, getValuePrecisionThousand, RowConfig } from '@loopring-web/common-resources' export interface TradeRaceRow { project: string pair: string reward: { count: number token: string } } const rowHeight = RowConfig.rowHeight const TableStyle = styled(Table)` &.rdg { box-sizing: border-box; border-top: 1px solid var(--color-divide); text-align: center; min-height: initial; height: ${(props: any) => { if (props.currentheight) { return props.currentheight + 2 + 'px' } else { return '100%' } }}; .rdg-header-row { // background:var(--color-table-header-bg); } .rdg-row, .rdg-header-row { border-right: 1px solid var(--color-divide); } .rdg-cell { box-sizing: border-box; border-left-width: 1px; border-bottom-width: 1px; border-left-color: var(--color-divide); border-bottom-color: var(--color-divide); } .rdg-align-left { text-align: left; } .rdg-align-right { text-align: right; } } ` export const TradeRacePanel = ({ rawData, ...rest }: { rawData: TradeRaceRow[] }) => { const { t } = useTranslation(['tables']) const getColumnMode = React.useCallback( (): Column[] => [ { key: 'project', name: t('labelTradeRaceProject'), formatter: ({ row }) => { const value = row['project'] return {value} }, }, { key: 'pair', name: /\-/gi.test(rawData[0].pair) ? t('labelTradeRacePair') : t('labelTradeRaceToken'), }, { key: 'reward', // cellClass: "", // headerCellClass: "", name: t('labelTradeRaceReward'), formatter: ({ row }) => { return ( <> {row.reward.token ? ( <> {row.reward.count !== '' && getValuePrecisionThousand(row.reward.count)} {row.reward.token} ) : ( {EmptyValueTag} )} ) }, }, ], [], ) const defaultArgs = { columnMode: getColumnMode(), currentheight: (rawData.length + 1) * rowHeight, generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return } ================================================ FILE: packages/component-lib/src/components/block/TradeTitle.tsx ================================================ import { WithTranslation } from 'react-i18next' import { Account, CAMPAIGNTAGCONFIG, CoinInfo, CurrencyToTag, EmptyValueTag, FloatTag, ForexMap, getValuePrecisionThousand, PriceTag, SCENARIO, SoursURL, Ticker, UpColor, } from '@loopring-web/common-resources' import { Avatar, Box, Grid, Typography } from '@mui/material' import styled from '@emotion/styled' import { AvatarCoin, baseTitleCss, TagIconList, useSettings } from '../../index' import { NewTagIcon } from '../basic-lib' import { Currency } from '@loopring-web/loopring-sdk' export type StyledProps = { custom: { chg: UpColor } } const TradeTitleStyled = styled(Box)` ${({ theme, custom }) => baseTitleCss({ theme, custom })} ` as (props: StyledProps) => JSX.Element export const TradeTitle = ({ baseShow, quoteShow, coinAInfo, coinBInfo, // t, ticker, isNew, forexMap, campaignTagConfig, }: WithTranslation & { account: Account baseShow: string quoteShow: string coinAInfo: CoinInfo coinBInfo: CoinInfo ticker: Ticker isNew: boolean forexMap: ForexMap campaignTagConfig: CAMPAIGNTAGCONFIG }) => { const { coinJson } = useSettings() const sellCoinIcon: any = coinJson[coinAInfo?.simpleName] const buyCoinIcon: any = coinJson[coinBInfo?.simpleName] const tradeFloatType = ticker?.changeU === 0 || !ticker?.changeU ? FloatTag.none : ticker.changeU < 0 ? FloatTag.decrease : FloatTag.increase const { currency, upColor } = useSettings() const close: any = ticker.close const value = '\u2248 ' + PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( ticker?.closeU ? ticker?.closeU * (forexMap[currency] ?? 0) : 0, undefined, undefined, undefined, true, { isFait: true }, ) const pair = `${coinAInfo?.simpleName}-${coinBInfo?.simpleName}` const change = ticker?.change ? ticker?.change.toFixed(2) + '%' : '0.00%' return ( // @ts-ignore {coinBInfo && coinAInfo ? ( {sellCoinIcon ? ( ' } /> ) : ( )} {buyCoinIcon ? ( ' } /> ) : ( )} {baseShow} / {quoteShow} {campaignTagConfig && ( )} {isNew ? : undefined} {close === 'NaN' ? EmptyValueTag : close} {quoteShow} {value} ({tradeFloatType === FloatTag.increase ? '+' : ''} {change}) ) : ( <> )} ) } ================================================ FILE: packages/component-lib/src/components/block/Vip.tsx ================================================ import React from 'react' import { Column, Table } from '../basic-lib' import { WithTranslation, withTranslation } from 'react-i18next' import { Box, Typography } from '@mui/material' import { CheckIcon } from '@loopring-web/common-resources' import styled from '@emotion/styled' interface Row { level: string tradeVolume: string rule: string balance: string maker: string taker: string } const TableStyle = styled(Table)` &.rdg { border-top: 1px solid var(--color-divide); text-align: center; min-height: 300px; overflow: auto; --template-columns: auto !important; .rdg-header-row { // background:var(--color-table-header-bg); } .rdg-row, .rdg-header-row { border-right: 1px solid var(--color-divide); } .rdg-cell { border-left-width: 1px; border-bottom-width: 1px; border-left-color: var(--color-divide); border-bottom-color: var(--color-divide); } } ` as typeof Table export const VipPanel = withTranslation(['tables'])( ({ t, rawData, currentLevel, ...rest }: WithTranslation & { rawData: Row[]; currentLevel: number }) => { const getColumnModeTransaction = React.useCallback( ({ t }: WithTranslation): Column[] => [ { key: 'level', name: t('labelVipTableLevel'), frozen: true, formatter: ({ row }) => { const [_, level] = row.level.split(' ') return ( {String(currentLevel) === String(level) && ( )} {row.level} ) }, // headerRenderer: (props: SortableHeaderCellProps) => {t('labelLevel')}}/>, // formatter: ({row}) => {row.level} }, { key: 'tradeVolume', name: t('labelVipTable30dTradeVolume') }, { key: 'rule', name: t('labelVipTableRule') }, { key: 'balance', name: t('labelVipTableBalance') }, { key: 'maker', name: t('labelVipTableMaker') }, { key: 'taker', name: t('labelVipTableTaker') }, ], [currentLevel], ) const defaultArgs: any = { columnMode: getColumnModeTransaction({ t, ...rest }), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return {...{ ...defaultArgs, t, ...rest, rawData }} /> }, ) ================================================ FILE: packages/component-lib/src/components/block/index.ts ================================================ export * from './TradeTitle' export * from './Interface' // export * from '../modal/AccountInfo/AccountInfo' export * from './AssetTitle' export * from './MarketBlock' export * from './AmmCard' export * from './Vip' // export * from './styled' // export * as from './Styled' export * from './Interface' export * from './DepthRaw' export * from './TradeRace' export * from './nftMedia' export * from './AmmPairDetail' export * from './CollectionMedia' export * from './LoadingBlock' export * from './TagIconList' export * from './RedPacket' export * from './CollectionDetailView' export * from './Referral' export * from './ErrorBlock' export * from './ShareSocialPanel' export * from './ETHStakingDetail' ================================================ FILE: packages/component-lib/src/components/block/nftMedia.tsx ================================================ import { GET_IPFS_STRING, ImageIcon, NFTWholeINFO, RefreshIcon, SoursURL, SyncIcon, RouterPath, NFTSubRouter, } from '@loopring-web/common-resources' import { useTheme } from '@emotion/react' import React from 'react' import { Box, BoxProps, Modal, Tooltip, Typography } from '@mui/material' import { cssBackground, EmptyDefault, MediaLabelStyled, MediaSVGToggle, ModalCloseButton, NftImage, NFTMedaProps, useImage, useOpenModals, useSettings, SwitchPanelStyled, Button, } from '../../index' import { NFT_IMAGE_SIZES } from '@loopring-web/loopring-sdk' import styled from '@emotion/styled' import { useTranslation, WithTranslation, withTranslation } from 'react-i18next' import { useHistory } from 'react-router-dom' // import "@google/model-viewer"; //background-color: var(--color-box-secondary); const BoxStyle = styled(Box)` ${(props) => cssBackground(props)}; width: 100%; position: relative; overflow: hidden; background-color: var(--opacity); .redPacketNFT & { background-image: none; background-color: initial; } ` export const NFTMedia = React.memo( React.forwardRef( ( { item, shouldPlay = true, onNFTError, index, isOrigin = false, getIPFSString, baseURL, }: NFTMedaProps, ref: React.ForwardedRef, ) => { const theme = useTheme() const { t } = useTranslation() const { modals: { isShowNFTDetail: { isShow }, }, } = useOpenModals() const [play, setPlay] = React.useState(false) const [previewSrc, setPreviewSrc] = React.useState( (isOrigin ? item?.metadata?.imageSize[NFT_IMAGE_SIZES.original] : item?.metadata?.imageSize[NFT_IMAGE_SIZES.small]) ?? getIPFSString(item?.image, baseURL), // item?.image?.startsWith(IPFS_HEAD_URL) ? // baseURL + `/api/v3/delegator/ipfs?path=${item?.image?.replace(IPFS_HEAD_URL_REG, '')}`: item?.image // item?.image?.replace(IPFS_HEAD_URL, IPFS_LOOPRING_SITE) ) const { hasLoaded: previewSrcHasLoaded, hasError: previewSrcHasError } = useImage( previewSrc ?? '', ) const fullSrc = isOrigin ? getIPFSString(item?.image, baseURL) : item?.metadata?.imageSize[NFT_IMAGE_SIZES.original] ?? getIPFSString(item?.image, baseURL) const { hasLoaded: fullSrcHasLoaded } = useImage(fullSrc ?? '') return ( {!previewSrcHasLoaded ? ( ) : ( <> {item && previewSrcHasError ? ( { event.stopPropagation() setPreviewSrc( item?.metadata?.imageSize['160-160'] ?? getIPFSString(item?.image, baseURL), ) }} > ) : ( <> {item.__mediaType__ && item.animationUrl && ( )} {play && shouldPlay ? ( <> ) : !!fullSrc && fullSrcHasLoaded ? ( undefined} src={fullSrc} /> ) : !!previewSrc ? ( onNFTError(item, index)} src={previewSrc} /> ) : ( } message={() => ( <> // // {t("labelNoCollectionCover")} // )} /> )} )} )} {item?.pendingOnSync ? ( {t('labelSync')} ) : ( '' )} ) }, ), ) const ModalFullStyled = styled(Box)` & > div { background: var(--color-global-bg-opacity); position: absolute; top: 56px; left: 0; right: 0; bottom: 0; } .close-button { margin-top: 0; } ` as typeof Box export const ZoomMedia = withTranslation('common')( ({ t, open, onClose, getIPFSString, baseURL, nftItem, className = '', ...rest }: WithTranslation & { open: boolean nftItem: Partial onClose: (event: any) => void className?: string getIPFSString: GET_IPFS_STRING baseURL: string }) => { const ref = React.useRef() // const { language } = useSettings(); return ( } shouldPlay={true} onNFTError={() => 'undefined'} isOrigin={true} getIPFSString={getIPFSString} baseURL={baseURL} /> ) }, ) export const CollectionHadUnknown = withTranslation('common')( ({ t, open, onClose, ...rest }: WithTranslation & { open: boolean onClose: () => void }) => { const { mode } = useTheme() const { isMobile } = useSettings() const history = useHistory() return ( {t('labelHadUnknownCollectionTitle')} {t('labelHadUnknownCollectionDes')} {'collection ) }, ) ================================================ FILE: packages/component-lib/src/components/bottomRule/index.tsx ================================================ import { Box, Typography } from '@mui/material' import { Button, ModalCloseButton } from '@loopring-web/component-lib' import styled from '@emotion/styled' import React from 'react' import { useTranslation } from 'react-i18next' const StyledBox = styled(Box)` bottom: 0; left: 0; right: 0; z-index: 100; background: var(--color-pop-bg); border-top: 1px solid var(--color-border); //text-align: center; .close-button { right: ${({ theme }) => (theme.unit * 5) / 2}px; top: 50%; transform: translateY(-50%); margin-top: 0; } .full-btn-close { } ` as typeof Box export interface PopperProps { isShow: boolean title?: string content: string btnTxt: string clickToConfirm?: () => void } export const BottomRule = ({ isShow, title, content, btnTxt, clickToConfirm }: PopperProps) => { const [_isShow, setIsShow] = React.useState(isShow) const trans = useTranslation() return _isShow ? ( {title ? {title} : <>} {content} setIsShow(false)} {...{ ...trans, tReady: true }} /> ) : ( <> ) } ================================================ FILE: packages/component-lib/src/components/carousel/Carousel.tsx ================================================ import styled from '@emotion/styled' import { Box, BoxProps } from '@mui/material' import { CarouselItem } from './Interface' import { myLog, SoursURL } from '@loopring-web/common-resources' import React from 'react' const StyleBox = styled(Box)` &.carousel { //margin-left: 15%; //margin-right: 15%; ul.slides { display: block; position: relative; margin: 0; padding: 0; overflow: hidden; list-style: none; input { display: none; } ${({ imageList }) => ` height: calc(${imageList[0]?.height ?? 880 / 2}px + 60px); width: calc(${imageList[0]?.width ?? 630 / 2}px); `} } } .slides * { user-select: none; -webkit-touch-callout: none; } .slide-container { display: none; position: absolute; } .slide-image { display: block; position: absolute; top: 0; opacity: 0; transition: all 0.7s ease-in-out; } .carousel-controls { position: absolute; top: 0; //50%; left: 0; right: 0; z-index: 999; font-size: ${({ theme }) => theme.fontDefault.body1}; color: var(--color-text-primary); height: 100%; //transform: translateY(-50%); label { display: none; position: absolute; padding: 0 ${({ theme }) => 2 * theme.unit}px; opacity: 0; transition: opacity 0.2s; cursor: pointer; font-size: var(--svg-size-huge); height: 100%; align-items: center; justify-content: center; &:nth-child(1) { justify-content: left; } &:nth-child(2) { justify-content: right; } &:hover { opacity: 1; } } .prev-slide { width: 30%; text-align: left; left: 0; } .next-slide { width: 30%; text-align: right; right: 0; } } .slide-image:hover + .carousel-controls label { opacity: 0.5; } .carousel-dots { position: absolute; left: 0; right: 0; bottom: ${({ theme }) => 2 * theme.unit}px; z-index: 999; text-align: center; } .carousel-dots .carousel-dot { display: inline-block; width: var(--carousel-dot-size); height: var(--carousel-dot-size); border-radius: 50%; background-color: #fff; opacity: 0.5; margin: 10px; } input:checked + .slide-container { display: block; } input:checked + .slide-container .slide-image { opacity: 1; transform: scale(1); transition: opacity 1s ease-in-out; } input:checked + .slide-container .carousel-controls label { //display: block; display: inline-flex; } ${({imageList, preStr}) => { let label: string = imageList.reduce((prev, _, index) => { prev += `input#${preStr}${index + 1}:checked ~ .carousel-dots label#${preStr}dot-${index + 1},` return prev }, '' as string) label += `{ opacity: 1;}` return label }} input:checked + .slide-container .nav label { display: block; } } ` as (props: BoxProps & { imageList: CarouselItem[], preStr: string }) => JSX.Element export const Carousel = ({ loading, imageList, preStr = 'img-', selected, handleSelected }: { loading: boolean imageList: CarouselItem[] selected: number preStr?: string, handleSelected: (id: number) => void }) => { myLog('imageList', imageList) return ( {loading ? ( ) : ( {imageList.map((item, index) => { return ( {'image'} ) })} {imageList.map((_, index) => { return ( )} ) } ================================================ FILE: packages/component-lib/src/components/carousel/Interface.ts ================================================ export type SocialButtonProps = { url: string /** Social Network Enum */ socialEnum: string /** Message to be shared */ message?: string /** Button size in pixels */ size?: number imageUrl?: string /** Function that sends share event to analytics */ sendShareEvent: () => void } export type CarouselItem = { imageUrl: string; size: [number, number], height?: number, width?: number } export type ShareProps = { /** Social Networks configuration */ social: SocialButtonProps size: number /** share URL title */ title: string /** Indcates if the component should render the Content Loader */ loading: boolean imageUrl: string /** Function that sends share event to analytics */ sendShareEvent: () => {} } ================================================ FILE: packages/component-lib/src/components/carousel/index.tsx ================================================ export * from './Interface' export * from './Carousel' ================================================ FILE: packages/component-lib/src/components/charts/constant.ts ================================================ export enum ChartType { Kline = 'Kline', Depth = 'Depth', Trend = 'Trend', } export enum timeUnit { W1 = '1W', H1 = '1H', D1 = '1D', } ================================================ FILE: packages/component-lib/src/components/charts/doughnutChart/DoughnutChart.stories.tsx ================================================ // also exported from '@storybook/react' if you can deal with breaking changes in 6.1 import { Meta, Story } from '@storybook/react' import { DoughnutChart } from './DoughnutChart' import { withTranslation } from 'react-i18next' import styled from '@emotion/styled' const Styled = styled.div` flex: 1; width: 500px; height: 300px; ` // @ts-ignore const data = [ { name: 'LP Token', value: 12898.0, }, { name: 'LRC', value: 12898.5, }, { name: 'ETH', value: 36547.0, }, { name: 'DOT', value: 23898.09, }, { name: 'BTC', value: 3489, }, { name: 'CRV', value: 180.09, }, ] export const Doughnut = withTranslation()(() => { return ( <> ) }) as Story export default { title: 'Charts/Doughnut', component: Doughnut, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/charts/doughnutChart/DoughnutChart.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Cell, Legend, Pie, PieChart, ResponsiveContainer, Sector } from '@loopring-web/recharts' const colors = ['#00BBA8', '#ED9526', '#1C60FF', '#FFCA28', '#63992D', '#FF5677'] const StyledLegendItem = styled.div` display: flex; justify-content: space-between; align-items: center; width: 170px; // margin-bottom: ${({ theme }) => theme.unit}px; color: ${({ theme }) => `${theme.colorBase.textPrimary}`}; font-size: 1.4rem; padding: ${({ theme }) => theme.unit / 1.5}px; border-radius: ${({ theme }) => theme.unit / 2}px; & > span:first-of-type { display: flex; align-items: center; & > div:first-of-type { margin-right: ${({ theme }) => theme.unit / 2}px; } } ` const UlStyled = styled.ul` margin-top: ${({ theme }) => theme.unit}px; margin-right: ${({ theme }) => theme.unit * 2}px; ` as any const LiWrapperStyled = styled.li` background-color: ${({ theme, active }: any) => { return active ? theme.colorBase.fieldOpacity : 'inherit' }}; ` as any const renderActiveShape = ({ cx, cy, innerRadius, outerRadius, startAngle, endAngle, fill, }: any) => ( ) export interface DoughnutChartProps { data?: { name: string value: number }[] } export const DoughnutChart = ({ data }: DoughnutChartProps) => { const [activeIndex, setActiveIndex] = React.useState(undefined) const onPieEnter = React.useCallback((_: any, index: any) => { setActiveIndex(index) }, []) const onPieLeave = React.useCallback(() => { setActiveIndex(undefined) }, []) const getRenderLegend = React.useCallback( ({ payload }: any) => ( {payload.map((entry: any, index: number) => { const { color, value, payload: { value: amount }, } = entry const StyledColoredRect = styled.div` width: 1.2rem; height: 1.2rem; background-color: ${color}; ` return ( {value} {(amount * 100).toFixed(2)}% ) })} ), [activeIndex], ) const getFormattedData = React.useCallback(() => { if (!data || !data.length) return [] if (data.length < 7) return data data.sort((a, b) => b.value - a.value) const others = data.slice(5, data.length).reduce((a, b) => ({ name: 'Others', value: a.value + b.value, })) const result = data.slice(0, 5).concat([others]) return result }, [data]) const formattedData = getFormattedData() const paddingAngle = formattedData.filter((item) => { return item.value }).length > 1 ? 5 : 0 return ( {formattedData.map((entry, index) => ( ))} ) } ================================================ FILE: packages/component-lib/src/components/charts/doughnutChart/index.ts ================================================ export * from './DoughnutChart' ================================================ FILE: packages/component-lib/src/components/charts/index.tsx ================================================ export * from './scaleAreaChart' export * from './doughnutChart' export * from './constant' ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/APRChart/index.tsx ================================================ import React from 'react' import { Area, ComposedChart, Line, ResponsiveContainer, Tooltip, XAxis, YAxis, } from '@loopring-web/recharts' import moment from 'moment' import { getAprRenderData } from '../data' import { Box, Typography } from '@mui/material' import styled from '@emotion/styled' import { DAT_STRING_FORMAT, DAT_STRING_FORMAT_S, UpColor } from '@loopring-web/common-resources' import { ChartType } from '../../constant' import { IndicatorProps } from '../KlineChart' import * as sdk from '@loopring-web/loopring-sdk' import { useTheme } from '@emotion/react' import { useSettings } from '../../../../stores' // import { getValuePrecisionThousand, myLog } from '@loopring-web/common-resources'; const DEFAULT_YAXIS_DOMAIN = 0.01 const TooltipStyled = styled(Box)` background: var(--color-pop-bg); border: 1px solid var(--color-border); border-radius: ${({ theme }) => theme.unit * 0.25}px; padding: ${({ theme }) => theme.unit * 2}px ${({ theme }) => theme.unit * 2}px; > div:last-of-type { color: var(--color-text-secondary); } ` const TrendAprChart = ({ type, data, yAxisDomainPercent = DEFAULT_YAXIS_DOMAIN, handleMove, showTooltip = true, showArea = true, showXAxis = false, }: // isHeadTailCompare = false, // isDailyTrend = false, // handleMoveOut = undefined, { type: ChartType data: { apy: string; createdAt: number }[] indicator?: IndicatorProps interval?: sdk.TradingInterval handleMove?: (props: any) => void yAxisDomainPercent?: number riseColor?: 'green' | 'red' showTooltip?: boolean showArea?: boolean showXAxis?: boolean }) => { const theme = useTheme() const { upColor } = useSettings() const colorRight = upColor === UpColor.green ? ['var(--color-success)', 'var(--color-error)'] : ['var(--color-error)', 'var(--color-success)'] const renderData = getAprRenderData(type, data) const trendColor = theme.colorBase.primary // current chart xAxis index const [currentIndex, setCurrentIndex] = React.useState(-1) const hasData = data && Array.isArray(data) && !!data.length const handleMousemove = React.useCallback( (props: any) => { if (!hasData) return const { activeTooltipIndex } = props // avoid duplicated event const isUpdate = activeTooltipIndex !== currentIndex if (Number.isFinite(activeTooltipIndex) && isUpdate) { setCurrentIndex(activeTooltipIndex) if (handleMove) { handleMove(renderData[activeTooltipIndex]) } } }, [renderData, handleMove, currentIndex, hasData], ) const renderTooltipContent = React.useCallback( (props: any) => { if (!hasData) return if (!props.payload || !props.payload.length || !props.payload[0].payload.createdAt) return const { createdAt, apy } = props.payload[0].payload // const index = data.findIndex((o: any) => o.createdAt === createdAt) return ( APR: {(apy?.toString().charAt(0) == '-' ? '' : '+') + apy + '%'} {moment(createdAt).format(DAT_STRING_FORMAT)} ) }, [hasData, data], ) const customTick = ({ x, y, payload }: any) => { if (!renderData || !renderData.length) { return } return ( {moment(payload.value).format(DAT_STRING_FORMAT_S)} ) } return ( (index == 0 ? '' : value)} domain={[ Math.min.apply( null, Object.values(renderData ?? {}).map((item) => Number(item.apy)), ) - 1, Math.max.apply( null, Object.values(renderData ?? {}).map((item) => Number(item.apy)), ), ]} /* tickFormatter={convertValue} */ stroke={theme.colorBase.textThird} /> {hasData && showTooltip && ( } position={{ y: 50 }} content={(props) => renderTooltipContent(props)} /> )} {showArea && ( )} ) } export default TrendAprChart ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/DepthChart/ScaleAreaChart.stories.tsx ================================================ // also exported from '@storybook/react' if you can deal with breaking changes in 6.1 import { Meta, Story } from '@storybook/react' import { ScaleAreaChart } from '../ScaleAreaChart' import { ChartType } from '../../index' import { withTranslation } from 'react-i18next' import styled from '@emotion/styled' const Styled = styled.div` flex: 1; width: 1000px; height: 500px; ` // @ts-ignore const testDepthData = { bidsPrices: [1.5, 1.55, 1.56, 1.57, 1.58, 1.59, 1.6, 1.61, 1.62, 1.63], bidsAmtTotals: [310, 290, 280, 280, 180, 170, 160, 160, 150, 70], asksPrices: [1.5, 1.52, 1.53, 1.54, 1.55, 1.56, 1.57, 1.58, 1.59, 1.6], asksAmtTotals: [60, 80, 80, 100, 120, 130, 140, 140, 150, 200], } export const Depth = withTranslation()(() => { return ( <> { console.log(props) }} // riseColor="red" /> ) }) as Story export default { title: 'Charts/Depth', component: Depth, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/DepthChart/index.tsx ================================================ import React, { useCallback, useState } from 'react' import { getDepthData } from '../data' import { Box, Typography } from '@mui/material' import { Area, ComposedChart, Line, ResponsiveContainer, Tooltip, XAxis, YAxis, } from '@loopring-web/recharts' import { getValuePrecisionThousand } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { withTranslation, WithTranslation } from 'react-i18next' const ASKS_COLOR = '#FF5677' const BIDS_COLOR = '#00BBA8' const YAXIS_DOMAIN = 0.1 const TooltipStyled = styled(Box)` background: var(--color-pop-bg); border: 1px solid var(--color-border); border-radius: ${({ theme }) => theme.unit * 0.25}px; padding: ${({ theme }) => theme.unit * 2}px ${({ theme }) => theme.unit * 2}px; > div:last-of-type { color: var(--color-text-secondary); } ` export interface DepthData { // timeStamp: number bidsPrices: any[] bidsAmtTotals: any[] asksPrices: any[] asksAmtTotals: any[] } export interface DepthProps { data?: DepthData handleMove?: (props: { type: 'asks' | 'bids'; price: number; amount: number }) => void yAxisDomainPercent?: number colorBase: any marketPrecision?: number } const DepthChart = withTranslation('common')( ({ data, handleMove, yAxisDomainPercent = YAXIS_DOMAIN, marketPrecision = 2, t, }: DepthProps & WithTranslation) => { const [currentIndex, setCurrentIndex] = useState(-1) const asksColor = ASKS_COLOR const bidsColor = BIDS_COLOR const hasData = data const renderTooltipContent = useCallback( (props: any) => { // no data if (!hasData) return '' if (props.payload && !!props.payload.length && props.payload[0].payload) { const { payload } = props.payload[0] const { price } = payload if (payload.bids || payload.asks) { return ( {t('labelProOrderPrice')}: {formatAxisTicker(price, true)} {t('labelProOrderTotalAmount')}:{' '} {getValuePrecisionThousand( payload.bids || payload.asks, undefined, undefined, marketPrecision, false, )} ) } // if (payload.asks) { // return ( // // price: {price} // amount: {payload.asks} // // ) // } } return }, [hasData, t], ) const handleMousemove = useCallback( (props: any) => { // no data if (!hasData) return const { activeTooltipIndex } = props // avoid duplicated event const isUpdate = activeTooltipIndex !== currentIndex if (Number.isFinite(activeTooltipIndex) && isUpdate) { setCurrentIndex(activeTooltipIndex) // const {payload, name} = props.activePayload[ 0 ] const payload = props.activePayload ? props.activePayload[0].payload : undefined const name = props.activePayload ? props.activePayload[0].name : undefined if (handleMove && props.activePayload) { handleMove({ price: payload.price, type: name, amount: payload.bids || payload.asks, }) } } }, [handleMove, currentIndex, hasData], ) const formattedData = getDepthData(data) const formatAxisTicker = React.useCallback( (value: any, addZeros: boolean) => { // myLog(getValuePrecisionThousand(value, undefined, undefined, marketPrecision, false)) return getValuePrecisionThousand(value, undefined, undefined, marketPrecision, addZeros) }, [marketPrecision], ) return ( formatAxisTicker(value, true)} allowDuplicatedCategory /> formatAxisTicker(value, false)} domain={[ (dataMin: number) => dataMin * (1 - yAxisDomainPercent), (dataMax: number) => dataMax * (1 + yAxisDomainPercent), ]} /> {hasData && ( } position={{ y: 50 }} content={(props) => renderTooltipContent(props)} /> )} ) }, ) export default DepthChart ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/KlineChart/KlineChart.stories.tsx ================================================ // also exported from '@storybook/react' if you can deal with breaking changes in 6.1 import { Meta, Story } from '@storybook/react' import { ScaleAreaChart } from '../ScaleAreaChart' import { ChartType } from '../../index' import { withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { testKlineData } from './data' import { MainIndicator, SubIndicator } from '.' import { TradingInterval } from '@loopring-web/loopring-sdk' const Styled = styled.div` flex: 1; width: 100%; height: 100%; ` const formatDateData = testKlineData.map((d) => ({ ...d, date: new Date(d.date), })) export const Kline = withTranslation()(() => { return ( <> ) }) as Story export default { title: 'Charts/KineDay', component: Kline, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/KlineChart/KlineChart.tsx ================================================ import { format } from 'd3-format' import { timeFormat } from 'd3-time-format' import React from 'react' import { BarSeries, bollingerBand, BollingerBandTooltip, BollingerSeries, CandlestickSeries, Chart, ChartCanvas, CrossHairCursor, CurrentCoordinate, discontinuousTimeScaleProviderBuilder, EdgeIndicator, ema, Label, lastVisibleItemBasedZoomAnchor, LineSeries, MACDSeries, MACDTooltip, MouseCoordinateX, MouseCoordinateY, MovingAverageTooltip, OHLCTooltip, rsi, RSISeries, RSITooltip, sar, SARSeries, SingleValueTooltip, sma, withDeviceRatio, withSize, XAxis, YAxis, } from 'react-financial-charts' import { macd } from '@react-financial-charts/indicators' export interface IOHLCData { date: Date open: number high: number low: number close: number volume: number txs: number } export enum MainIndicator { MA = 'MA', EMA = 'EMA', BOLL = 'BOLL', } export enum SubIndicator { VOLUME = 'VOLUME', MACD = 'MACD', RSI = 'RSI', SAR = 'SAR', } export interface IndicatorProps { mainIndicators?: { indicator: MainIndicator; params?: any }[] subIndicator?: { indicator: SubIndicator; params?: any }[] } export interface StockChartProps { readonly data: IOHLCData[] readonly height: number readonly dateTimeFormat: string readonly width: number readonly ratio: number } export function fibonacci(n: number) { let n1 = 1, n2 = 1 for (let i = 2; i < n; i++) { ;[n1, n2] = [n2, n1 + n2] } return [n1, n2] } export type StockChartExtraProps = { upColor: 'green' | 'red' colorBase: any marketPrecision?: number } class StockChart extends React.Component { // private readonly marginRight = (this.props.marketPrecision || 2) * 10; // private readonly margin = { left: 0, right: this.marginRight, top: 0, bottom: 24 }; // private readonly pricesDisplayFormat = format(`.${this.props.marketPrecision || 2}f`); private readonly xScaleProvider = discontinuousTimeScaleProviderBuilder().inputDateAccessor( (d: IOHLCData) => d.date, ) private readonly macdAppearance = { fillStyle: { divergence: '#4682B4', }, strokeStyle: { macd: '#0093FF', signal: '#D84315', zero: 'rgba(0, 0, 0, 0.3)', }, } public render() { const { data: initialData, dateTimeFormat, height, ratio, width, mainIndicators, subIndicator, upColor, colorBase, marketPrecision, } = this.props const pricesDisplayFormat = format(`.${this.props.marketPrecision || 2}f`) const isUpGreen = upColor === 'green' const getPricePrecisioned = (price: number, precision = 2) => { let formattedPrice = price as any let [_init, _dot] = String(price || '').split('.') if (_dot) { const dotLen = _dot.length if (dotLen < precision) { for (let i = dotLen; i < precision; i++) { _dot = _dot + '0' } formattedPrice = _init + '.' + _dot } } return String(formattedPrice) } const getPriceWithPrecisionLenth = (price: number, precision = 2) => { const formattedPrice = getPricePrecisioned(price, precision) return formattedPrice.length } const marginRight = getPriceWithPrecisionLenth(initialData[initialData.length - 1]?.close || 6, marketPrecision) * 8.5 const margin = { left: 0, right: marginRight, top: 0, bottom: 24 } let mainIndicatorLst: any[] = [] let subIndicatorLst: any[] = [] let id = 1 let maToolTipOptions: any[] = [] let bollToolTipOption: any = undefined if (mainIndicators && mainIndicators?.length > 0) { mainIndicators.forEach((item: { indicator: MainIndicator; params?: any }) => { switch (item.indicator) { case MainIndicator.MA: const periodMA = item.params.period const indMA = sma() .id(id++) .options({ windowSize: periodMA }) .merge((d: any, c: any) => { d = { ...d, [`sma${periodMA}`]: c, } return d }) .accessor((d: any = {}) => d && d[`sma${periodMA}`]) mainIndicatorLst.push({ func: indMA, type: item.indicator }) maToolTipOptions.push({ yAccessor: indMA.accessor(), type: 'MA', stroke: indMA.stroke(), windowSize: indMA.options().windowSize, }) break case MainIndicator.EMA: const periodEMA = item.params.period const indEMA = ema() .id(id++) .options({ windowSize: periodEMA }) .merge((d: any, c: any) => { d && (d[`ema${periodEMA}`] = c) }) .accessor((d: any = {}) => d && d[`ema${periodEMA}`]) mainIndicatorLst.push({ func: indEMA, type: item.indicator }) maToolTipOptions.push({ yAccessor: indEMA.accessor(), type: 'EMA', stroke: indEMA.stroke(), windowSize: indEMA.options().windowSize, }) break case MainIndicator.BOLL: const indBOLL = bollingerBand() .id(id++) .merge((d: any, c: any) => { d && (d.bb = c) }) .accessor((d: any = {}) => d && d.bb) mainIndicatorLst.push({ func: indBOLL, type: item.indicator }) bollToolTipOption = indBOLL.options() break default: break } }) } if (subIndicator && subIndicator.length > 0) { subIndicator.forEach((item: { indicator: SubIndicator; params?: any }) => { switch (item.indicator) { case SubIndicator.MACD: const macdCalculator = macd() .id(id++) .options({ fast: 12, signal: 9, slow: 26, }) .merge((d: any, c: any) => { d.macd = c }) .accessor((d: any = {}) => d && d.macd) subIndicatorLst.push({ func: macdCalculator, type: item.indicator, }) break case SubIndicator.RSI: const rsiCalculator = rsi() .options({ windowSize: 14 }) .merge((d: any, c: any) => { d.rsi = c }) .accessor((d: any = {}) => d && d.rsi) subIndicatorLst.push({ func: rsiCalculator, type: item.indicator, }) break case SubIndicator.SAR: const sarCalculator = sar() .id(id++) .options({ accelerationFactor: item?.params.accelerationFactor, maxAccelerationFactor: item?.params.maxAccelerationFactor, }) .merge((d: any, c: any) => { d.sar = c }) .accessor((d: any = {}) => d && d.sar) subIndicatorLst.push({ func: sarCalculator, type: item.indicator, params: item?.params, }) break case SubIndicator.VOLUME: subIndicatorLst.push({ func: undefined, type: item.indicator }) break default: break } }) } let calculatedData = initialData mainIndicatorLst.forEach((item: any) => { if (item.func) { calculatedData = item.func(calculatedData) } }) subIndicatorLst.forEach((item: any) => { if (item.func) { calculatedData = item.func(calculatedData) } }) const { xScaleProvider } = this const { data, xScale, xAccessor, displayXAccessor } = xScaleProvider(calculatedData) const max = xAccessor(data[data.length - 1]) const min = xAccessor(data[Math.max(0, data.length - 100)]) const xExtents = [min, max + 5] const gridHeight = height - margin.top - margin.bottom const [h1, hall] = fibonacci(3 + subIndicatorLst.length) const chartHeight = Math.floor((gridHeight * h1) / hall) const subHeight = subIndicatorLst.length ? Math.floor((gridHeight - chartHeight) / subIndicatorLst.length) : 0 const timeDisplayFormat = timeFormat(dateTimeFormat) const bbStroke = { top: '#FF01FF', middle: '#CC0165', bottom: '#01CCCB', } let chartId = 1 const getTrendValueColor = (data?: any) => { if (data && data.close && data.open) { return data.close > data.open ? isUpGreen ? colorBase.success : colorBase.error : isUpGreen ? colorBase.error : colorBase.success } return colorBase.textPrimary } return ( getTrendValueColor(data)} wickStroke={(data) => getTrendValueColor(data)} /> {mainIndicatorLst && mainIndicatorLst.map((item: any, index: number) => { return ( ) })} getTrendValueColor(data)} lineStroke={(data) => getTrendValueColor(data)} displayFormat={pricesDisplayFormat} yAccessor={this.yEdgeIndicator} /> getPricePrecisioned(n, marketPrecision)} changeFormat={(_: any) => ''} textFill={(data: any) => getTrendValueColor(data)} /> {maToolTipOptions && ( getPricePrecisioned(n | n.valueOf(), marketPrecision)} /> )} {bollToolTipOption && ( <> d && d.bb} options={bollToolTipOption} textFill={colorBase.textPrimary} /> )} {subIndicatorLst && subIndicatorLst.length > 0 && subIndicatorLst.map((item: any, ind: number) => { switch (item.type) { case SubIndicator.MACD: return ( /* @ts-ignore */ [0, _h - (subIndicatorLst.length - ind) * subHeight]} yExtents={item.func.accessor()} > ) case SubIndicator.VOLUME: return ( /* @ts-ignore */ [ 0, h - (subIndicatorLst.length - ind) * subHeight, ]} yExtents={this.barChartExtents} > ) case SubIndicator.RSI: return ( /* @ts-ignore */ [ 0, h - (subIndicatorLst.length - ind) * subHeight, ]} yExtents={[0, 100]} > ) case SubIndicator.SAR: return ( /* @ts-ignore */ [ 0, h - (subIndicatorLst.length - ind) * subHeight, ]} yExtents={item.func.accessor()} > ) default: break } return <> })} ) } // @ts-ignore private readonly barChartExtents = (data: IOHLCData) => { return data.volume } private readonly candleChartExtents = (data: any) => { // data max > bolling band ? data max : bolling band // data min < booling band ? data min : bolling band return [ data.bb ? (data.high >= data.bb.top ? data.high : data.bb.top * 1.05) : data.high, data.bb ? (data.low <= data.bb.bottom ? data.low : data.bb.bottom * 0.95) : data.low, ] } private readonly yEdgeIndicator = (data: IOHLCData) => { return data.close } private readonly volumeColor = (data: IOHLCData) => { return data.close > data.open ? this.props.upColor === 'green' ? 'rgba(38, 166, 154, 0.3)' : 'rgba(239, 83, 80, 0.3)' : this.props.upColor === 'green' ? 'rgba(239, 83, 80, 0.3)' : 'rgba(38, 166, 154, 0.3)' } private readonly volumeSeries = (data: IOHLCData) => { return data.volume } } export const WrapperedKlineChart = withSize({ style: { minHeight: 200 } })( withDeviceRatio()(StockChart), ) ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/KlineChart/KlineChart_min.stories.tsx ================================================ // also exported from '@storybook/react' if you can deal with breaking changes in 6.1 import { Meta, Story } from '@storybook/react' import { ScaleAreaChart } from '../ScaleAreaChart' import { ChartType } from '../../index' import { withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { testKlineData } from './data_min' import { MainIndicator, SubIndicator } from '.' import { TradingInterval } from '@loopring-web/loopring-sdk' const Styled = styled.div` flex: 1; width: 100%; height: 100%; ` const formatDateData = testKlineData.map((d) => ({ ...d, date: new Date(d.date), })) export const Kline = withTranslation()(() => { return ( <> ) }) as Story export default { title: 'Charts/KineMin', component: Kline, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/KlineChart/data.ts ================================================ export const testKlineData = [ { date: '2010-01-04', open: 25.436282332605284, high: 25.835021381744056, low: 25.411360259406774, close: 25.710416, volume: 38409100, split: '', dividend: '', }, { date: '2010-01-05', open: 25.627344939513726, high: 25.83502196495549, low: 25.452895407434543, close: 25.718722, volume: 49749600, split: '', dividend: '', }, { date: '2010-01-06', open: 25.65226505944465, high: 25.81840750861228, low: 25.353210976925574, close: 25.560888, volume: 58182400, split: '', dividend: '', }, { date: '2010-01-07', open: 25.444587793771767, high: 25.502739021094353, low: 25.079077898061875, close: 25.295062, volume: 50559700, split: '', dividend: '', }, { date: '2010-01-08', open: 25.153841756996414, high: 25.6522649488092, low: 25.120612602739726, close: 25.46951, volume: 51197400, split: '', dividend: '', }, { date: '2010-01-11', open: 25.511044730573705, high: 25.55258096597291, low: 25.02092861663475, close: 25.145534, volume: 68754700, split: '', dividend: '', }, { date: '2010-01-12', open: 25.045848646491518, high: 25.253525666777517, low: 24.84647870701696, close: 24.979392, volume: 65912100, split: '', dividend: '', }, { date: '2010-01-13', open: 25.13722727051071, high: 25.353211377924218, low: 24.929550244151567, close: 25.211991, volume: 51863500, split: '', dividend: '', }, { date: '2010-01-14', open: 25.178761733851413, high: 25.83502196495549, low: 25.137227159471163, close: 25.718722, volume: 63228100, split: '', dividend: '', }, { date: '2010-01-15', open: 25.818406945612217, high: 25.95132023748152, low: 25.51104412745638, close: 25.635652, volume: 79913200, split: '', dividend: '', }, { date: '2010-01-19', open: 25.544274163987136, high: 25.95132113440514, low: 25.486124596784563, close: 25.835022, volume: 46575700, split: '', dividend: '', }, { date: '2010-01-20', open: 25.59411494568944, high: 25.702108656795026, low: 25.17876090842236, close: 25.41136, volume: 54849500, split: '', dividend: '', }, { date: '2010-01-21', open: 25.427975689088637, high: 25.51935191837554, low: 24.92124291902699, close: 24.92955, volume: 73086700, split: '', dividend: '', }, { date: '2010-01-22', open: 24.921242227943445, high: 25.087384673504477, low: 23.9576208617963, close: 24.057305, volume: 102004600, split: '', dividend: '', }, { date: '2010-01-25', open: 24.289904353342425, high: 24.63880174829468, low: 24.17360522169168, close: 24.356361, volume: 63373000, split: '', dividend: '', }, { date: '2010-01-26', open: 24.256677400199628, high: 24.796636835593223, low: 24.165298678305085, close: 24.505889, volume: 66639900, split: '', dividend: '', }, { date: '2010-01-27', open: 24.381282411526794, high: 24.771715213346813, low: 24.107148742163798, close: 24.647109, volume: 63949500, split: '', dividend: '', }, { date: '2010-01-28', open: 24.788329503429356, high: 24.813251576935805, low: 23.999155984106725, close: 24.223448, volume: 117513700, split: '', dividend: '', }, { date: '2010-01-29', open: 24.838171916252662, high: 24.854786078069555, low: 22.977385792760824, close: 23.409354, volume: 193888500, split: '', dividend: '', }, { date: '2010-02-01', open: 23.583802007377084, high: 23.658566566701865, low: 23.193370033086943, close: 23.600417, volume: 85931100, split: '', dividend: '', }, { date: '2010-02-02', open: 23.567188934614894, high: 23.675180153730853, low: 23.376124415817756, close: 23.641951, volume: 54413700, split: '', dividend: '', }, { date: '2010-02-03', open: 23.47581083464236, high: 23.916086957012187, low: 23.359512531703963, close: 23.783172, volume: 61397900, split: '', dividend: '', }, { date: '2010-02-04', open: 23.575494533516057, high: 23.67518033405172, low: 23.101990926835022, close: 23.126913, volume: 77850000, split: '', dividend: '', }, { date: '2010-02-05', open: 23.259826837972874, high: 23.492425937060705, low: 22.90262235438972, close: 23.276441, volume: 80960100, split: '', dividend: '', }, { date: '2010-02-08', open: 23.268134182833126, high: 23.326283750587436, low: 22.902622614091726, close: 23.027228, volume: 52820600, split: '', dividend: '', }, { date: '2010-02-09', open: 23.234904845121957, high: 23.542267674401998, low: 23.052149892895393, close: 23.268134, volume: 59195800, split: '', dividend: '', }, { date: '2010-02-10', open: 23.284748153680564, high: 23.459196018578062, low: 23.12691278885316, close: 23.251519, volume: 48591300, split: '', dividend: '', }, { date: '2010-02-11', open: 23.201676634364272, high: 23.59210943129056, low: 23.01061460700204, close: 23.359512, volume: 65993700, split: '', dividend: '', }, { date: '2010-02-12', open: 23.101991198292986, high: 23.309668221207414, low: 22.9109291679198, close: 23.201677, volume: 81117200, split: '', dividend: '', }, { date: '2010-02-16', open: 23.47709177175651, high: 23.677395688560953, low: 23.38528740952381, close: 23.660703, volume: 51935600, split: '', dividend: '$0.130', }, { date: '2010-02-17', open: 23.810930273207585, high: 23.911080561385095, low: 23.669049515949805, close: 23.861005, volume: 45882900, split: '', dividend: '', }, { date: '2010-02-18', open: 23.861005604453073, high: 24.228227231839046, low: 23.794238187581573, close: 24.17815, volume: 42856500, split: '', dividend: '', }, { date: '2010-02-19', open: 24.0279246886073, high: 24.13642090510949, low: 23.944465418534303, close: 24.011232, volume: 44451800, split: '', dividend: '', }, { date: '2010-02-22', open: 24.06965319596241, high: 24.15311329961183, low: 23.91108058475461, close: 23.977848, volume: 36707100, split: '', dividend: '', }, { date: '2010-02-23', open: 23.93611844264031, high: 24.061307346629015, low: 23.443708753618075, close: 23.644011, volume: 52266200, split: '', dividend: '', }, { date: '2010-02-24', open: 23.80258363823205, high: 24.027924500255413, low: 23.685739826453087, close: 23.894388, volume: 43165900, split: '', dividend: '', }, { date: '2010-02-25', open: 23.593936399999997, high: 23.911081636363633, low: 23.385288218181813, close: 23.869352, volume: 48735300, split: '', dividend: '', }, { date: '2010-02-26', open: 23.911081145797, high: 24.077999687826996, low: 23.794238166376, close: 23.927773, volume: 40370600, split: '', dividend: '', }, { date: '2010-03-01', open: 24.01123281771192, high: 24.24491794728184, low: 23.810931397308096, close: 24.219881, volume: 43805400, split: '', dividend: '', }, { date: '2010-03-02', open: 24.26995529971733, high: 24.45356485597533, low: 23.568897443742003, close: 23.752507, volume: 93123900, split: '', dividend: '', }, { date: '2010-03-03', open: 23.794237468876933, high: 23.87769757203811, low: 23.660702639167347, close: 23.752507, volume: 48442100, split: '', dividend: '', }, { date: '2010-03-04', open: 23.7525072419881, high: 23.911080688476446, low: 23.593935464685135, close: 23.894388, volume: 42890600, split: '', dividend: '', }, { date: '2010-03-05', open: 23.919426488282614, high: 23.93611834207765, low: 23.719124242742218, close: 23.861005, volume: 56001800, split: '', dividend: '', }, { date: '2010-03-08', open: 23.80258363823205, high: 24.144766642848992, low: 23.785891784348298, close: 23.894388, volume: 39414500, split: '', dividend: '', }, { date: '2010-03-09', open: 23.835966751378393, high: 24.294994406988312, low: 23.827620824352493, close: 24.036269, volume: 50271600, split: '', dividend: '', }, { date: '2010-03-10', open: 24.086346470987106, high: 24.29499464871055, low: 24.036269239148055, close: 24.17815, volume: 44891400, split: '', dividend: '', }, { date: '2010-03-11', open: 24.11138228226816, high: 24.361761761597496, low: 24.077999408841674, close: 24.353415, volume: 35349700, split: '', dividend: '', }, { date: '2010-03-12', open: 24.470258636146227, high: 24.520333364928977, low: 24.23657350832009, close: 24.428529, volume: 31700200, split: '', dividend: '', }, { date: '2010-03-15', open: 24.35341496847337, high: 24.511988415951947, low: 24.21153420957548, close: 24.445221, volume: 37512000, split: '', dividend: '', }, { date: '2010-03-16', open: 24.55371679966916, high: 24.61213828763574, low: 24.370107243509732, close: 24.511988, volume: 36723500, split: '', dividend: '', }, { date: '2010-03-17', open: 24.620484783006575, high: 24.929284918267495, low: 24.5370255125557, close: 24.728981, volume: 50385700, split: '', dividend: '', }, { date: '2010-03-18', open: 24.728980184354267, high: 24.804093525282557, low: 24.620483970939414, close: 24.71229, volume: 43845200, split: '', dividend: '', }, { date: '2010-03-19', open: 24.837478759040216, high: 24.954321737073336, low: 24.495295751943225, close: 24.695598, volume: 81332100, split: '', dividend: '', }, { date: '2010-03-22', open: 24.62048472972973, high: 24.787404104862972, low: 24.52867869783973, close: 24.703944, volume: 37718200, split: '', dividend: '', }, { date: '2010-03-23', open: 24.695597951994575, high: 24.95432168856498, low: 24.545371266244018, close: 24.937629, volume: 42026600, split: '', dividend: '', }, { date: '2010-03-24', open: 24.804093653097034, high: 24.912591536256325, low: 24.70394336593592, close: 24.745673, volume: 33987700, split: '', dividend: '', }, { date: '2010-03-25', open: 24.89590031356214, high: 25.51349891336221, low: 24.870861697896466, close: 25.046127, volume: 73168700, split: '', dividend: '', }, { date: '2010-03-26', open: 25.112893854012135, high: 25.204699883817227, low: 24.695597512137557, close: 24.754019, volume: 55595500, split: '', dividend: '', }, { date: '2010-03-29', open: 24.795748289435686, high: 24.88755432105441, low: 24.662213457397836, close: 24.695598, volume: 33336000, split: '', dividend: '', }, { date: '2010-03-30', open: 24.72898118589772, high: 24.920939178563152, low: 24.620484968088682, close: 24.845825, volume: 34954800, split: '', dividend: '', }, { date: '2010-03-31', open: 24.737326775604377, high: 24.804094191556327, low: 24.345069041479377, close: 24.445221, volume: 63760000, split: '', dividend: '', }, { date: '2010-04-01', open: 24.495295612139916, high: 24.65386905887253, low: 23.886043779037138, close: 24.336723, volume: 74768100, split: '', dividend: '', }, { date: '2010-04-05', open: 24.311685184197852, high: 24.56206383566792, low: 24.228227581090845, close: 24.428529, volume: 34331200, split: '', dividend: '', }, { date: '2010-04-06', open: 24.32837724079127, high: 24.68725210231924, low: 24.18649648158254, close: 24.470258, volume: 47366800, split: '', dividend: '', }, { date: '2010-04-07', open: 24.336723385349234, high: 24.670559634231825, low: 24.32003069658276, close: 24.495296, volume: 58318800, split: '', dividend: '', }, { date: '2010-04-08', open: 24.470258371657753, high: 25.021089562834224, low: 24.453565682786962, close: 24.971014, volume: 63713800, split: '', dividend: '', }, { date: '2010-04-09', open: 24.996052675396935, high: 25.379964490112062, low: 24.954322205009888, close: 25.321543, volume: 54752500, split: '', dividend: '', }, { date: '2010-04-12', open: 25.24642851253298, high: 25.44673075527704, low: 25.21304397081629, close: 25.30485, volume: 37068800, split: '', dividend: '', }, { date: '2010-04-13', open: 25.16296936082202, high: 25.455076799504866, low: 25.146276672590325, close: 25.413348, volume: 41374600, split: '', dividend: '', }, { date: '2010-04-14', open: 25.697110053606323, high: 25.872373685918234, low: 25.58861216807268, close: 25.722147, volume: 68941200, split: '', dividend: '', }, { date: '2010-04-15', open: 25.722146531190592, high: 25.830644414746768, low: 25.630340501321108, close: 25.763877, volume: 52745400, split: '', dividend: '', }, { date: '2010-04-16', open: 25.697109958166223, high: 25.855681735898273, low: 25.53853651124878, close: 25.596958, volume: 88703100, split: '', dividend: '', }, { date: '2010-04-19', open: 25.680417138517488, high: 26.081021630766053, low: 25.672071211595643, close: 25.905758, volume: 64970300, split: '', dividend: '', }, { date: '2010-04-20', open: 26.055983352397597, high: 26.23959541623828, low: 25.980870009129525, close: 26.172828, volume: 52199500, split: '', dividend: '', }, { date: '2010-04-21', open: 26.147789, high: 26.289669757421002, low: 26.064329730928822, close: 26.147789, volume: 55343100, split: '', dividend: '', }, { date: '2010-04-22', open: 25.90575822439064, high: 26.314708647103302, low: 25.788914411880036, close: 26.197864, volume: 84847600, split: '', dividend: '', }, { date: '2010-04-23', open: 25.972525500371916, high: 26.356437305440483, low: 25.580266099168803, close: 25.838989, volume: 126766600, split: '', dividend: '', }, { date: '2010-04-26', open: 25.87237297099412, high: 26.106059755002228, low: 25.75553083087908, close: 25.964179, volume: 63649300, split: '', dividend: '', }, { date: '2010-04-27', open: 25.83064510525721, high: 26.08102208265802, low: 25.663725729335493, close: 25.747185, volume: 68730900, split: '', dividend: '', }, { date: '2010-04-28', open: 25.805605926884507, high: 25.872373341960532, low: 25.55522895494209, close: 25.79726, volume: 64557900, split: '', dividend: '', }, { date: '2010-04-29', open: 25.813952510322583, high: 26.23124886516129, low: 25.596958405806454, close: 25.872374, volume: 52665200, split: '', dividend: '', }, { date: '2010-04-30', open: 25.93079529827127, high: 25.93914122530644, low: 25.471769311336956, close: 25.488462, volume: 63214800, split: '', dividend: '', }, { date: '2010-05-03', open: 25.59695755583417, high: 25.922447867207428, low: 25.521844214457413, close: 25.755531, volume: 43989500, split: '', dividend: '', }, { date: '2010-05-04', open: 25.471768984791538, high: 25.49680593098337, low: 24.829132611322027, close: 25.146277, volume: 82085600, split: '', dividend: '', }, { date: '2010-05-05', open: 24.845824584254604, high: 25.11289424723618, low: 24.77905800310191, close: 24.912592, volume: 66833800, split: '', dividend: '', }, { date: '2010-05-06', open: 24.695597537612148, high: 24.937628581556385, low: 23.29348182746722, close: 24.186496, volume: 128613000, split: '', dividend: '', }, { date: '2010-05-07', open: 24.144766572660995, high: 24.16145926108892, low: 22.801072338924936, close: 23.543859, volume: 173718100, split: '', dividend: '', }, { date: '2010-05-10', open: 24.21153365302234, high: 24.60379221272315, low: 23.96115501436531, close: 24.153113, volume: 86653300, split: '', dividend: '', }, { date: '2010-05-11', open: 23.936118296957, high: 24.745673204490068, low: 23.844313101257377, close: 24.103036, volume: 63789400, split: '', dividend: '', }, { date: '2010-05-12', open: 24.186496522197807, high: 24.6872521437754, low: 24.13642096004005, close: 24.57041, volume: 47146800, split: '', dividend: '', }, { date: '2010-05-13', open: 24.420181853625174, high: 24.81244041381669, low: 24.35341443912449, close: 24.40349, volume: 45188800, split: '', dividend: '', }, { date: '2010-05-14', open: 24.370107865356623, high: 24.37845212323654, low: 23.90273428051272, close: 24.144767, volume: 63334000, split: '', dividend: '', }, { date: '2010-05-17', open: 24.30333968243861, high: 24.395144042669525, low: 23.744162586694905, close: 24.153113, volume: 46053300, split: '', dividend: '', }, { date: '2010-05-18', open: 24.203414670176816, high: 24.31240045454545, low: 23.80938527272727, close: 23.977057, volume: 52690600, split: '', dividend: '$0.130', }, { date: '2010-05-19', open: 23.90998841926346, high: 24.052510226460626, low: 23.29798745025666, close: 23.675248, volume: 61746700, split: '', dividend: '', }, { date: '2010-05-20', open: 23.180615815912365, high: 23.33990395352623, low: 22.669217896668577, close: 22.727903, volume: 87991100, split: '', dividend: '', }, { date: '2010-05-21', open: 22.325489846440167, high: 22.72790367218875, low: 22.166203380832563, close: 22.501546, volume: 117596300, split: '', dividend: '', }, { date: '2010-05-24', open: 22.509930022839743, high: 22.518314447799085, low: 22.015298413399314, close: 22.023682, volume: 73711700, split: '', dividend: '', }, { date: '2010-05-25', open: 21.503899367088607, high: 22.073983248945147, low: 21.277541693286924, close: 21.85601, volume: 98373600, split: '', dividend: '', }, { date: '2010-05-26', open: 21.990147560975608, high: 22.30872468881847, low: 20.59008776619952, close: 20.96735, volume: 176684100, split: '', dividend: '', }, { date: '2010-05-27', open: 21.570968163461536, high: 22.09913495374327, low: 21.570968163461536, close: 21.797325, volume: 136433600, split: '', dividend: '', }, { date: '2010-05-28', open: 21.663187183844464, high: 21.89792844060389, low: 21.512282629158243, close: 21.629652, volume: 67496900, split: '', dividend: '', }, { date: '2010-06-01', open: 21.40329755729635, high: 22.057215639324475, low: 21.394913132287105, close: 21.705105, volume: 76152400, split: '', dividend: '', }, { date: '2010-06-02', open: 21.84762554061438, high: 22.19973701132793, low: 21.570968024979894, close: 22.182969, volume: 65718800, split: '', dividend: '', }, { date: '2010-06-03', open: 22.258421143829665, high: 22.57699826667914, low: 22.14105177211274, close: 22.518314, volume: 67837000, split: '', dividend: '', }, { date: '2010-06-04', open: 21.881160338070558, high: 22.275188895882554, low: 21.478749032280763, close: 21.62127, volume: 89832200, split: '', dividend: '', }, { date: '2010-06-07', open: 21.646420244111496, high: 21.654803830573194, low: 21.160172229332847, close: 21.202091, volume: 80456200, split: '', dividend: '', }, { date: '2010-06-08', open: 21.16855536963141, high: 21.176938955916413, low: 20.665540192531253, close: 21.051186, volume: 87355000, split: '', dividend: '', }, { date: '2010-06-09', open: 21.14340352939433, high: 21.394911953412183, low: 20.749375816886815, close: 20.782911, volume: 87794000, split: '', dividend: '', }, { date: '2010-06-10', open: 21.067951784841362, high: 21.084719796, low: 20.77452793755864, close: 20.958966, volume: 78930900, split: '', dividend: '', }, { date: '2010-06-11', open: 20.992501474368005, high: 21.56258368073722, low: 20.76614380007794, close: 21.512283, volume: 68057700, split: '', dividend: '', }, { date: '2010-06-14', open: 21.679954944240983, high: 21.763789130268826, low: 21.352993402817845, close: 21.378145, volume: 50972400, split: '', dividend: '', }, { date: '2010-06-15', open: 21.587735317908205, high: 22.342258105718585, low: 21.579351731376974, close: 22.283573, volume: 81641500, split: '', dividend: '', }, { date: '2010-06-16', open: 22.191351955334383, high: 22.283572242401213, low: 21.990146723784193, close: 22.065599, volume: 48698000, split: '', dividend: '', }, { date: '2010-06-17', open: 22.266804463620687, high: 22.359024751648665, low: 21.830859650991975, close: 22.107518, volume: 47995500, split: '', dividend: '', }, { date: '2010-06-18', open: 22.10751789594119, high: 22.241655276647037, low: 21.939845331700255, close: 22.166203, volume: 52075600, split: '', dividend: '', }, { date: '2010-06-21', open: 22.45124465372495, high: 22.54346242509174, low: 21.70510380614602, close: 21.755407, volume: 54625300, split: '', dividend: '', }, { date: '2010-06-22', open: 21.93146288242142, high: 22.17458773591397, low: 21.59611941327125, close: 21.604503, volume: 55985400, split: '', dividend: '', }, { date: '2010-06-23', open: 21.612887249772587, high: 21.612887249772587, low: 21.143404720053248, close: 21.218857, volume: 61466200, split: '', dividend: '', }, { date: '2010-06-24', open: 21.34461013604136, high: 21.562583382441364, low: 20.9002808952, close: 20.958966, volume: 85243400, split: '', dividend: '', }, { date: '2010-06-25', open: 21.00088281019891, high: 21.051186004637266, low: 20.380497424972063, close: 20.564938, volume: 156256700, split: '', dividend: '', }, { date: '2010-06-28', open: 20.548170568826432, high: 20.63200727241897, low: 20.22121153277291, close: 20.380498, volume: 73784800, split: '', dividend: '', }, { date: '2010-06-29', open: 20.229593082687863, high: 20.288279864024833, low: 19.37446894923243, close: 19.542139, volume: 119882100, split: '', dividend: '', }, { date: '2010-06-30', open: 19.53375617598292, high: 19.852333308996087, low: 19.240332318150063, close: 19.290633, volume: 81050500, split: '', dividend: '', }, { date: '2010-07-01', open: 19.3577008955095, high: 19.55052338169257, low: 19.055891786701206, close: 19.416386, volume: 92239400, split: '', dividend: '', }, { date: '2010-07-02', open: 19.58405911768827, high: 19.684661318435754, low: 19.32416625661341, close: 19.508606, volume: 62485100, split: '', dividend: '', }, { date: '2010-07-06', open: 19.86910080057527, high: 20.196059835012594, low: 19.768496924433247, close: 19.969703, volume: 73592000, split: '', dividend: '', }, { date: '2010-07-07', open: 19.969702693403402, high: 20.388882011065103, low: 19.793648218344124, close: 20.372114, volume: 79965300, split: '', dividend: '', }, { date: '2010-07-08', open: 20.623622138467844, high: 20.640390149296763, low: 20.0954553672948, close: 20.464334, volume: 50758100, split: '', dividend: '', }, { date: '2010-07-09', open: 20.39726652039555, high: 20.464335214256288, low: 20.2463619592089, close: 20.346965, volume: 53806100, split: '', dividend: '', }, { date: '2010-07-12', open: 20.48110154450262, high: 20.86674567996597, low: 20.472717958115187, close: 20.816445, volume: 49854200, split: '', dividend: '', }, { date: '2010-07-13', open: 21.076335586485612, high: 21.210472970255505, low: 20.875130349189423, close: 21.067952, volume: 61928700, split: '', dividend: '', }, { date: '2010-07-14', open: 21.37814467853205, high: 21.470364964523547, low: 21.059569243249793, close: 21.327844, volume: 72808100, split: '', dividend: '', }, { date: '2010-07-15', open: 21.378145413563306, high: 21.453597691493528, low: 20.94219891885535, close: 21.386529, volume: 56934700, split: '', dividend: '', }, { date: '2010-07-16', open: 21.386529202351518, high: 21.495514988701046, low: 20.858362413483988, close: 20.866746, volume: 65064800, split: '', dividend: '', }, { date: '2010-07-19', open: 20.92543033405517, high: 21.210472265089656, low: 20.883513241379312, close: 21.151788, volume: 38181800, split: '', dividend: '', }, { date: '2010-07-20', open: 20.841596485140425, high: 21.361378, low: 20.707459103664757, close: 21.361378, volume: 45530700, split: '', dividend: '', }, { date: '2010-07-21', open: 21.461981311226864, high: 21.50389924347535, low: 20.942198951345585, close: 21.05957, volume: 73297300, split: '', dividend: '', }, { date: '2010-07-22', open: 21.386528652089787, high: 21.788940794504644, low: 21.352993468529917, close: 21.663187, volume: 73016400, split: '', dividend: '', }, { date: '2010-07-23', open: 21.663187597953797, high: 21.814092155524683, low: 21.16855599258256, close: 21.638036, volume: 108520100, split: '', dividend: '', }, { date: '2010-07-26', open: 21.67995576019774, high: 21.96499770425904, low: 21.62965256394019, close: 21.881161, volume: 67249900, split: '', dividend: '', }, { date: '2010-07-27', open: 21.914693988858485, high: 21.998530691131496, low: 21.763789433812615, close: 21.931462, volume: 60672100, split: '', dividend: '', }, { date: '2010-07-28', open: 21.85600919591487, high: 21.956613068546975, low: 21.654803127367895, close: 21.755407, volume: 69704800, split: '', dividend: '', }, { date: '2010-07-29', open: 21.90631018637011, high: 22.141051441373364, low: 21.4619809503657, close: 21.822476, volume: 69446200, split: '', dividend: '', }, { date: '2010-07-30', open: 21.587735319168356, high: 21.663187597953797, low: 21.252391857899724, close: 21.638036, volume: 83534800, split: '', dividend: '', }, { date: '2010-08-02', open: 21.788941062286362, high: 22.11590009365807, low: 21.58773498860615, close: 22.073983, volume: 55044600, split: '', dividend: '', }, { date: '2010-08-03', open: 21.96499718392439, high: 22.09075014143731, low: 21.772173020204054, close: 21.931462, volume: 56877700, split: '', dividend: '', }, { date: '2010-08-04', open: 21.923077615623782, high: 21.998529890400306, low: 21.327843841856467, close: 21.570967, volume: 78531900, split: '', dividend: '', }, { date: '2010-08-05', open: 21.36976119591008, high: 21.445213471611606, low: 21.135019944257824, close: 21.269159, volume: 64922100, split: '', dividend: '', }, { date: '2010-08-06', open: 21.10987113306736, high: 21.428446586629494, low: 20.97573374699545, close: 21.420063, volume: 55982100, split: '', dividend: '', }, { date: '2010-08-09', open: 21.42006180630899, high: 21.570967195588945, low: 21.269158932104883, close: 21.470365, volume: 57096500, split: '', dividend: '', }, { date: '2010-08-10', open: 21.235624245313122, high: 21.24400783167132, low: 20.85836202083562, close: 21.017651, volume: 87257700, split: '', dividend: '', }, { date: '2010-08-11', open: 20.69069061099394, high: 20.875129506229705, low: 20.590086738870365, close: 20.841596, volume: 76746900, split: '', dividend: '', }, { date: '2010-08-12', open: 20.472717895467543, high: 20.690691140873827, low: 20.422417215655496, close: 20.531403, volume: 70240500, split: '', dividend: '', }, { date: '2010-08-13', open: 20.414033067622952, high: 20.682307834836067, low: 20.32181361639344, close: 20.455951, volume: 45263500, split: '', dividend: '', }, { date: '2010-08-16', open: 20.42241762693008, high: 20.63200729019539, low: 20.3721144310291, close: 20.539787, volume: 40909700, split: '', dividend: '', }, { date: '2010-08-17', open: 20.826348, high: 21.03705569772188, low: 20.73363745583316, close: 20.826348, volume: 52912600, split: '', dividend: '$0.130', }, { date: '2010-08-18', open: 20.80106369057212, high: 21.028628844442387, low: 20.573499379532635, close: 20.91906, volume: 46818900, split: '', dividend: '', }, { date: '2010-08-19', open: 20.750494539619087, high: 20.8516333898677, low: 20.404932235936283, close: 20.598785, volume: 54064600, split: '', dividend: '', }, { date: '2010-08-20', open: 20.489214616929882, high: 20.56507022699133, low: 20.39650492042051, close: 20.421789, volume: 49560100, split: '', dividend: '', }, { date: '2010-08-23', open: 20.598784923605727, high: 20.767349392451344, low: 20.430217926267794, close: 20.463932, volume: 51643000, split: '', dividend: '', }, { date: '2010-08-24', open: 20.303793696597598, high: 20.52292970162522, low: 20.2279389256265, close: 20.261653, volume: 66522500, split: '', dividend: '', }, { date: '2010-08-25', open: 20.227938921161822, high: 20.41336085177502, low: 20.118371761502985, close: 20.312222, volume: 47404800, split: '', dividend: '', }, { date: '2010-08-26', open: 20.303793308564234, high: 20.388077228641016, low: 20.05094491965697, close: 20.076229, volume: 49105300, split: '', dividend: '', }, { date: '2010-08-27', open: 20.126798617261137, high: 20.244795771834518, low: 19.814952064772253, close: 20.168941, volume: 60939400, split: '', dividend: '', }, { date: '2010-08-30', open: 20.00880292169217, high: 20.07622938478128, low: 19.890806611286237, close: 19.924519, volume: 45453100, split: '', dividend: '', }, { date: '2010-08-31', open: 19.890806846647074, high: 20.00037485046335, low: 19.654814223042784, close: 19.781238, volume: 66074600, split: '', dividend: '', }, { date: '2010-09-01', open: 19.949803926778245, high: 20.185797380487656, low: 19.840236771701047, close: 20.143655, volume: 65235900, split: '', dividend: '', }, { date: '2010-09-02', open: 20.126797469333063, high: 20.185797307500906, low: 19.983516241817657, close: 20.177369, volume: 48837100, split: '', dividend: '', }, { date: '2010-09-03', open: 20.430217619175888, high: 20.607212921578718, low: 20.396505231611968, close: 20.47236, volume: 64189100, split: '', dividend: '', }, { date: '2010-09-07', open: 20.312222154099423, high: 20.48078747022381, low: 20.16051261103976, close: 20.194225, volume: 51928700, split: '', dividend: '', }, { date: '2010-09-08', open: 20.286937311742584, high: 20.39650615833435, low: 20.00880314834935, close: 20.168941, volume: 65512400, split: '', dividend: '', }, { date: '2010-09-09', open: 20.388077383022363, high: 20.40493231293765, low: 20.219510384423156, close: 20.236367, volume: 46028900, split: '', dividend: '', }, { date: '2010-09-10', open: 20.211082000838577, high: 20.253224382453418, low: 20.050944996289896, close: 20.101514, volume: 58284300, split: '', dividend: '', }, { date: '2010-09-13', open: 20.396505016605975, high: 21.31519053517684, low: 20.303792791167155, close: 21.163481, volume: 114680400, split: '', dividend: '', }, { date: '2010-09-14', open: 21.104483307693638, high: 21.365760003365565, low: 20.97805700662757, close: 21.096055, volume: 87160400, split: '', dividend: '', }, { date: '2010-09-15', open: 21.15505254159823, high: 21.25619139219342, low: 21.003343001459278, close: 21.17191, volume: 56201900, split: '', dividend: '', }, { date: '2010-09-16', open: 21.121338842917336, high: 21.382618074571813, low: 21.112910534982078, close: 21.348904, volume: 44548300, split: '', dividend: '', }, { date: '2010-09-17', open: 21.407902387307782, high: 21.51747123448307, low: 21.13819653045981, close: 21.256192, volume: 70341600, split: '', dividend: '', }, { date: '2010-09-20', open: 21.306763224270032, high: 21.509041771136452, low: 21.16348198990118, close: 21.433187, volume: 49838700, split: '', dividend: '', }, { date: '2010-09-21', open: 21.424758309343936, high: 21.424758309343936, low: 21.138195845725644, close: 21.197194, volume: 52675700, split: '', dividend: '', }, { date: '2010-09-22', open: 20.9780569288857, high: 21.045483390184913, low: 20.531358308439973, close: 20.742066, volume: 94299400, split: '', dividend: '', }, { date: '2010-09-23', open: 20.657782462546052, high: 20.725208925092097, low: 20.531358688102987, close: 20.590356, volume: 46201800, split: '', dividend: '', }, { date: '2010-09-24', open: 20.767349008365777, high: 20.902201929477442, low: 20.716780005779658, close: 20.885347, volume: 51948800, split: '', dividend: '', }, { date: '2010-09-27', open: 20.944344692680957, high: 21.062341000808733, low: 20.72520869187222, close: 20.843205, volume: 43603300, split: '', dividend: '', }, { date: '2010-09-28', open: 20.902202852469042, high: 20.98648677471637, low: 20.522929837925446, close: 20.801064, volume: 56041200, split: '', dividend: '', }, { date: '2010-09-29', open: 20.75892115798555, high: 20.78420692408163, low: 20.565070922448978, close: 20.649354, volume: 44318900, split: '', dividend: '', }, { date: '2010-09-30', open: 20.742066537400003, high: 20.9274884679461, low: 20.53135884038081, close: 20.640926, volume: 61262700, split: '', dividend: '', }, { date: '2010-10-01', open: 20.876918853852292, high: 20.91906039372684, low: 20.48078753620072, close: 20.548214, volume: 62672300, split: '', dividend: '', }, { date: '2010-10-04', open: 20.194224696274194, high: 20.21951046256796, low: 20.042516841157838, close: 20.152084, volume: 98143400, split: '', dividend: '', }, { date: '2010-10-05', open: 20.278508224931002, high: 20.607213922912933, low: 20.1520844476386, close: 20.52293, volume: 78152900, split: '', dividend: '', }, { date: '2010-10-06', open: 20.49764461399918, high: 20.6830682288316, low: 20.33750592262153, close: 20.590356, volume: 50489700, split: '', dividend: '', }, { date: '2010-10-07', open: 20.750494770654107, high: 20.860061929879254, low: 20.463932303738595, close: 20.67464, volume: 50096100, split: '', dividend: '', }, { date: '2010-10-08', open: 20.750494381292306, high: 20.77577846153846, low: 20.539786688984616, close: 20.708352, volume: 41327800, split: '', dividend: '', }, { date: '2010-10-11', open: 20.851633617730783, high: 20.851633617730783, low: 20.649354229361528, close: 20.725209, volume: 27587800, split: '', dividend: '', }, { date: '2010-10-12', open: 20.77577846153846, high: 21.01177107692308, low: 20.590355692307693, close: 20.927488, volume: 50141500, split: '', dividend: '', }, { date: '2010-10-13', open: 21.087626149960535, high: 21.525898999105447, low: 20.97805730555122, close: 21.357332, volume: 75336500, split: '', dividend: '', }, { date: '2010-10-14', open: 21.315190688252873, high: 21.35733138327388, low: 21.070768925881886, close: 21.26462, volume: 51949100, split: '', dividend: '', }, { date: '2010-10-15', open: 21.374189459346496, high: 21.53432562215252, low: 21.26462061493263, close: 21.525899, volume: 68954800, split: '', dividend: '', }, { date: '2010-10-18', open: 21.56803892718823, high: 21.871458840506975, low: 21.450043464829204, close: 21.76189, volume: 48330500, split: '', dividend: '', }, { date: '2010-10-19', open: 21.298334235458164, high: 21.382618157970235, low: 21.02862922330888, close: 21.155053, volume: 66150900, split: '', dividend: '', }, { date: '2010-10-20', open: 21.289906302248372, high: 21.407902615879202, low: 21.155053372384568, close: 21.332047, volume: 56283600, split: '', dividend: '', }, { date: '2010-10-21', open: 21.407901384736427, high: 21.525898534412196, low: 21.112909774793152, close: 21.424758, volume: 50032400, split: '', dividend: '', }, { date: '2010-10-22', open: 21.509041150080424, high: 21.52589860823257, low: 21.298333458563178, close: 21.391044, volume: 25837900, split: '', dividend: '', }, { date: '2010-10-25', open: 21.27304869579005, high: 21.365760080755855, low: 21.2140505417209, close: 21.230908, volume: 50912400, split: '', dividend: '', }, { date: '2010-10-26', open: 21.171909840514168, high: 21.888314311223283, low: 21.12133830852058, close: 21.829317, volume: 69304200, split: '', dividend: '', }, { date: '2010-10-27', open: 21.736606682623712, high: 22.00631253251645, low: 21.593325449868193, close: 21.955741, volume: 64805500, split: '', dividend: '', }, { date: '2010-10-28', open: 22.090593161712853, high: 22.233874389517986, low: 21.846173086523095, close: 22.149593, volume: 80730300, split: '', dividend: '', }, { date: '2010-10-29', open: 22.88285577615298, high: 22.924998158166364, low: 22.318159151106112, close: 22.478297, volume: 114193200, split: '', dividend: '', }, { date: '2010-11-01', open: 22.65529016068348, high: 22.941852621293407, low: 22.50358230837505, close: 22.71429, volume: 61912100, split: '', dividend: '', }, { date: '2010-11-02', open: 22.8069998452671, high: 23.11041976598831, low: 22.77328745722116, close: 23.085134, volume: 54402100, split: '', dividend: '', }, { date: '2010-11-03', open: 23.144131536594614, high: 23.16941730190835, low: 22.722716161878203, close: 22.781716, volume: 110255300, split: '', dividend: '', }, { date: '2010-11-04', open: 23.101992158142675, high: 23.118848774091703, low: 22.76485983916212, close: 22.874427, volume: 93599300, split: '', dividend: '', }, { date: '2010-11-05', open: 22.899712856238363, high: 22.91657031508406, low: 22.360301986592436, close: 22.630007, volume: 110953700, split: '', dividend: '', }, { date: '2010-11-08', open: 22.486725838370972, high: 24.33252614094812, low: 22.40244275801726, close: 22.596293, volume: 71670800, split: '', dividend: '', }, { date: '2010-11-09', open: 22.596292007028495, high: 22.849142922639963, low: 22.512008930378517, close: 22.71429, volume: 58538600, split: '', dividend: '', }, { date: '2010-11-10', open: 22.764859311623635, high: 22.823857466078042, low: 22.596292313208824, close: 22.705862, volume: 52277300, split: '', dividend: '', }, { date: '2010-11-11', open: 22.486725, high: 22.52043738805379, low: 22.149593533985197, close: 22.486725, volume: 62073100, split: '', dividend: '', }, { date: '2010-11-12', open: 22.309730316286068, high: 22.35187269889608, low: 21.997883764750668, close: 22.141165, volume: 64962200, split: '', dividend: '', }, { date: '2010-11-15', open: 22.191734157185717, high: 22.33501538797651, low: 22.0568812340885, close: 22.082167, volume: 51794600, split: '', dividend: '', }, { date: '2010-11-16', open: 22.082167877926842, high: 22.082167877926842, low: 21.75144333016053, close: 21.887124, volume: 65339200, split: '', dividend: '$0.160', }, { date: '2010-11-17', open: 21.96344512319124, high: 21.971925217833398, low: 21.666640962706218, close: 21.683602, volume: 58299700, split: '', dividend: '', }, { date: '2010-11-18', open: 21.80232291940538, high: 22.116087275541794, low: 21.71752366728193, close: 21.912565, volume: 59514000, split: '', dividend: '', }, { date: '2010-11-19', open: 21.878643345114547, high: 21.904084477069503, low: 21.70904230015406, close: 21.785364, volume: 52423200, split: '', dividend: '', }, { date: '2010-11-22', open: 21.751442244461717, high: 21.827763094442282, low: 21.57336110918317, close: 21.819283, volume: 53350500, split: '', dividend: '', }, { date: '2010-11-23', open: 21.68360241824831, high: 21.7090427026655, low: 21.27655786757333, close: 21.301999, volume: 69742500, split: '', dividend: '', }, { date: '2010-11-24', open: 21.36983939078288, high: 21.590320155919546, low: 21.33591816413409, close: 21.514001, volume: 56825900, split: '', dividend: '', }, { date: '2010-11-26', open: 21.378317773376672, high: 21.547920514455445, low: 21.34439824277228, close: 21.412239, volume: 21356500, split: '', dividend: '', }, { date: '2010-11-29', open: 21.361359558849408, high: 21.55640089041489, low: 21.14087624697259, close: 21.463119, volume: 56603600, split: '', dividend: '', }, { date: '2010-11-30', open: 21.24263616505467, high: 21.598800138926403, low: 21.200236539984164, close: 21.420719, volume: 75282100, split: '', dividend: '', }, { date: '2010-12-01', open: 21.68360170915508, high: 22.260248137087245, low: 21.675120766617212, close: 22.082167, volume: 74123500, split: '', dividend: '', }, { date: '2010-12-02', open: 22.251768687681988, high: 22.8792957009779, low: 22.21784915659439, close: 22.802974, volume: 91759200, split: '', dividend: '', }, { date: '2010-12-03', open: 22.73513316235322, high: 22.94713553096906, low: 22.709694574138265, close: 22.913216, volume: 52622000, split: '', dividend: '', }, { date: '2010-12-06', open: 22.836894851713858, high: 22.879295324888226, low: 22.692733242921015, close: 22.760574, volume: 36264200, split: '', dividend: '', }, { date: '2010-12-07', open: 22.964096138291918, high: 23.006495763211362, low: 22.769053962818983, close: 22.786015, volume: 57860500, split: '', dividend: '', }, { date: '2010-12-08', open: 22.752093224752112, high: 23.0997770943812, low: 22.726652093599082, close: 23.091297, volume: 41666800, split: '', dividend: '', }, { date: '2010-12-09', open: 23.133698738703696, high: 23.184578457902514, low: 22.904735338257023, close: 22.964096, volume: 47148300, split: '', dividend: '', }, { date: '2010-12-10', open: 23.05737842701459, high: 23.23545956839795, low: 22.989537669150657, close: 23.184579, volume: 37625800, split: '', dividend: '', }, { date: '2010-12-13', open: 23.1252181893578, high: 23.27786074158745, low: 23.04041724256881, close: 23.108258, volume: 47943900, split: '', dividend: '', }, { date: '2010-12-14', open: 23.159137372876202, high: 23.532262381163566, low: 23.116737748126805, close: 23.422022, volume: 64070500, split: '', dividend: '', }, { date: '2010-12-15', open: 23.345701814616298, high: 23.73578532710951, low: 23.345701814616298, close: 23.617064, volume: 69634200, split: '', dividend: '', }, { date: '2010-12-16', open: 23.540742822436588, high: 23.735785, low: 23.455941875669883, close: 23.735785, volume: 57680200, split: '', dividend: '', }, { date: '2010-12-17', open: 23.676424189247314, high: 23.82058579784946, low: 23.53226258064516, close: 23.659464, volume: 87456500, split: '', dividend: '', }, { date: '2010-12-20', open: 23.701866024272167, high: 23.73578555576359, low: 23.47290261463152, close: 23.583143, volume: 52811000, split: '', dividend: '', }, { date: '2010-12-21', open: 23.61706292304952, high: 23.86298481283844, low: 23.54074207338796, close: 23.803625, volume: 38153000, split: '', dividend: '', }, { date: '2010-12-22', open: 23.75274445254543, high: 24.083468134676547, low: 23.72730416930457, close: 23.905387, volume: 42252300, split: '', dividend: '', }, { date: '2010-12-23', open: 23.71882387668399, high: 24.015628037301347, low: 23.710343782038052, close: 23.998667, volume: 24902500, split: '', dividend: '', }, { date: '2010-12-27', open: 23.84602632004364, high: 23.913867075298363, low: 23.6425023582606, close: 23.803625, volume: 21652800, split: '', dividend: '', }, { date: '2010-12-28', open: 23.718823773554266, high: 23.88842651374509, low: 23.710343678945197, close: 23.752745, volume: 23042200, split: '', dividend: '', }, { date: '2010-12-29', open: 23.693385411948853, high: 23.846027116369363, low: 23.642503147789746, close: 23.718824, volume: 19502500, split: '', dividend: '', }, { date: '2010-12-30', open: 23.676424663554755, high: 23.744265421903048, low: 23.557704184454717, close: 23.617064, volume: 20786100, split: '', dividend: '', }, { date: '2010-12-31', open: 23.574662111503258, high: 23.676424094589752, low: 23.43050050347746, close: 23.667944, volume: 24752000, split: '', dividend: '', }, { date: '2011-01-03', open: 23.78666481496408, high: 23.89690689421015, low: 23.676424431736955, close: 23.727305, volume: 53443800, split: '', dividend: '', }, { date: '2011-01-04', open: 23.69338542757515, high: 23.88842675756497, low: 23.61706372730509, close: 23.820586, volume: 54405600, split: '', dividend: '', }, { date: '2011-01-05', open: 23.659464053571426, high: 23.752745094642858, low: 23.549222823214283, close: 23.744265, volume: 58998700, split: '', dividend: '', }, { date: '2011-01-06', open: 23.778186459390458, high: 24.465073284177656, low: 23.625544754324533, close: 24.439633, volume: 88026300, split: '', dividend: '', }, { date: '2011-01-07', open: 24.2869905310115, high: 24.371792326573424, low: 23.95626768356643, close: 24.253071, volume: 73762000, split: '', dividend: '', }, { date: '2011-01-10', open: 23.964748227666487, high: 24.083469556465964, low: 23.778186987562506, close: 23.930827, volume: 57573600, split: '', dividend: '', }, { date: '2011-01-11', open: 23.913867852140847, high: 23.95626747754296, low: 23.786664735887168, close: 23.837547, volume: 50298900, split: '', dividend: '', }, { date: '2011-01-12', open: 23.84602761669694, high: 24.244591227481305, low: 23.803626294347684, close: 24.21067, volume: 52631100, split: '', dividend: '', }, { date: '2011-01-13', open: 24.024107473781218, high: 24.074987192253488, low: 23.75274445254543, close: 23.905387, volume: 67077600, split: '', dividend: '', }, { date: '2011-01-14', open: 23.81210576579879, high: 24.06650775716752, low: 23.667944156817818, close: 23.998667, volume: 62688400, split: '', dividend: '', }, { date: '2011-01-18', open: 23.879946272156317, high: 24.37179175645499, low: 23.86298523503311, close: 24.303951, volume: 53322700, split: '', dividend: '', }, { date: '2011-01-19', open: 24.13434890521671, high: 24.32091183845844, low: 23.973227952343795, close: 24.142829, volume: 50005900, split: '', dividend: '', }, { date: '2011-01-20', open: 24.168270423280422, high: 24.210670049697743, low: 23.85450606451256, close: 24.041069, volume: 58613600, split: '', dividend: '', }, { date: '2011-01-21', open: 24.083469607423268, high: 24.10890989221984, low: 23.761226, close: 23.761226, volume: 58080300, split: '', dividend: '', }, { date: '2011-01-24', open: 23.76122543767531, high: 24.219149705167077, low: 23.735785153480798, close: 24.066508, volume: 52047800, split: '', dividend: '', }, { date: '2011-01-25', open: 23.862985371217736, high: 24.12587, low: 23.846026878026116, close: 24.12587, volume: 42436600, split: '', dividend: '', }, { date: '2011-01-26', open: 24.17674959879258, high: 24.583794137811182, low: 24.16826950422969, close: 24.405713, volume: 74628800, split: '', dividend: '', }, { date: '2011-01-27', open: 24.380272016616836, high: 24.98235788623513, low: 24.159789556640472, close: 24.482034, volume: 146938600, split: '', dividend: '', }, { date: '2011-01-28', open: 24.507473899099097, high: 24.532914183423422, low: 23.277861004766233, close: 23.532263, volume: 141249400, split: '', dividend: '', }, { date: '2011-01-31', open: 23.54922237793004, high: 23.659463606202667, low: 23.252419071042194, close: 23.515302, volume: 65029000, split: '', dividend: '', }, { date: '2011-02-01', open: 23.5746623531338, high: 23.795144814727227, low: 23.413542250296, close: 23.735785, volume: 62810700, split: '', dividend: '', }, { date: '2011-02-02', open: 23.68490405744796, high: 23.837546607223995, low: 23.642502736725568, close: 23.693385, volume: 45824000, split: '', dividend: '', }, { date: '2011-02-03', open: 23.71882317260539, high: 23.71882317260539, low: 23.354180809673093, close: 23.447461, volume: 60340100, split: '', dividend: '', }, { date: '2011-02-04', open: 23.489863185063847, high: 23.608583662945627, low: 23.32874053763054, close: 23.549223, volume: 40412200, split: '', dividend: '', }, { date: '2011-02-07', open: 23.574662514591115, high: 24.032588478277003, low: 23.566184115875316, close: 23.913868, volume: 68980900, split: '', dividend: '', }, { date: '2011-02-08', open: 23.829065451588917, high: 24.0325877187911, low: 23.786664131245683, close: 23.981708, volume: 34904200, split: '', dividend: '', }, { date: '2011-02-09', open: 23.90538777919956, high: 23.964747594020295, low: 23.6679442798693, close: 23.718824, volume: 52905100, split: '', dividend: '', }, { date: '2011-02-10', open: 23.684904065454546, high: 23.693385008009457, low: 23.14217886255491, close: 23.32026, volume: 76672400, split: '', dividend: '', }, { date: '2011-02-11', open: 23.540742828623856, high: 23.583142454008883, low: 22.955616295779816, close: 23.108258, volume: 83939700, split: '', dividend: '', }, { date: '2011-02-14', open: 23.07433596322817, high: 23.12521737752479, low: 22.85385520533592, close: 23.091297, volume: 56766200, split: '', dividend: '', }, { date: '2011-02-15', open: 23.065708444479725, high: 23.31308389328946, low: 22.988936488738926, close: 22.997465, volume: 44116500, split: '', dividend: '$0.160', }, { date: '2011-02-16', open: 23.07423779982798, high: 23.091299088082902, low: 22.69037886010363, close: 23.048648, volume: 70817900, split: '', dividend: '', }, { date: '2011-02-17', open: 23.00599577968669, high: 23.347206186252375, low: 22.95481532763011, close: 23.210721, volume: 57207300, split: '', dividend: '', }, { date: '2011-02-18', open: 23.14247952327094, high: 23.210721264152006, low: 23.023057329750827, close: 23.082768, volume: 68667800, split: '', dividend: '', }, { date: '2011-02-22', open: 22.84392298314584, high: 23.116889086122605, low: 22.622136478375328, close: 22.681848, volume: 60889000, split: '', dividend: '', }, { date: '2011-02-23', open: 22.630667548772017, high: 22.91216472214547, low: 22.545364522000753, close: 22.681848, volume: 60234100, split: '', dividend: '', }, { date: '2011-02-24', open: 22.724498320680166, high: 23.08276745179709, low: 22.605076129996267, close: 22.835392, volume: 64494200, split: '', dividend: '', }, { date: '2011-02-25', open: 22.954815688316973, high: 22.988937411927097, low: 22.605076764786318, close: 22.647727, volume: 53006300, split: '', dividend: '', }, { date: '2011-02-28', open: 22.767151245045824, high: 22.91216494181031, low: 22.61360647780286, close: 22.673318, volume: 51379900, split: '', dividend: '', }, { date: '2011-03-01', open: 22.69037857033639, high: 22.843923338495753, low: 22.30651878249235, close: 22.315049, volume: 60055000, split: '', dividend: '', }, { date: '2011-03-02', open: 22.272398505245672, high: 22.49418415785303, low: 22.21268698338984, close: 22.246807, volume: 48658200, split: '', dividend: '', }, { date: '2011-03-03', open: 22.400350450368304, high: 22.519773491611698, low: 22.332108712514934, close: 22.34917, volume: 68271500, split: '', dividend: '', }, { date: '2011-03-04', open: 22.366229163693866, high: 22.383290451510963, low: 22.007960032991328, close: 22.135915, volume: 70437200, split: '', dividend: '', }, { date: '2011-03-07', open: 22.289457924562168, high: 22.408881824995404, low: 21.794705299571785, close: 21.939719, volume: 64980400, split: '', dividend: '', }, { date: '2011-03-08', open: 21.9823699579313, high: 22.195625390196838, low: 21.880007350443844, close: 22.101793, volume: 50549800, split: '', dividend: '', }, { date: '2011-03-09', open: 22.016490260786338, high: 22.161504809637112, low: 21.88853785278246, close: 22.084732, volume: 39789100, split: '', dividend: '', }, { date: '2011-03-10', open: 21.854417415005194, high: 21.93118766409752, low: 21.624100696576154, close: 21.675282, volume: 66549500, split: '', dividend: '', }, { date: '2011-03-11', open: 21.67528312266355, high: 22.05061270054517, low: 21.63263288728968, close: 21.905599, volume: 49905800, split: '', dividend: '', }, { date: '2011-03-14', open: 21.743523801731264, high: 21.97383966781473, low: 21.62410076005836, close: 21.914129, volume: 54473400, split: '', dividend: '', }, { date: '2011-03-15', open: 21.393785115154987, high: 21.726462738804326, low: 21.325543376350666, close: 21.658221, volume: 76067300, split: '', dividend: '', }, { date: '2011-03-16', open: 21.513207645840353, high: 21.564390656797872, low: 21.05257675463587, close: 21.14641, volume: 100725400, split: '', dividend: '', }, { date: '2011-03-17', open: 21.376723374713386, high: 21.51320684943157, low: 21.112287495468628, close: 21.137879, volume: 62497000, split: '', dividend: '', }, { date: '2011-03-18', open: 21.37672465974942, high: 21.47908812496323, low: 21.154939, close: 21.154939, volume: 85486700, split: '', dividend: '', }, { date: '2011-03-21', open: 21.479087737070667, high: 21.820296438215554, low: 21.4534970844848, close: 21.607041, volume: 46878100, split: '', dividend: '', }, { date: '2011-03-22', open: 21.581449, high: 21.717932477331363, low: 21.521738331689264, close: 21.581449, volume: 30895600, split: '', dividend: '', }, { date: '2011-03-23', open: 21.521738408702493, high: 21.845887521546146, low: 21.479087321883817, close: 21.786176, volume: 43969000, split: '', dividend: '', }, { date: '2011-03-24', open: 21.83735728157138, high: 22.06767401217222, low: 21.75205510469024, close: 22.016491, volume: 38696700, split: '', dividend: '', }, { date: '2011-03-25', open: 22.118853888413195, high: 22.13591517636623, low: 21.82882649458132, close: 21.854418, volume: 57029800, split: '', dividend: '', }, { date: '2011-03-28', open: 21.888537430932704, high: 21.93971788133483, low: 21.649690495266352, close: 21.675282, volume: 48973200, split: '', dividend: '', }, { date: '2011-03-29', open: 21.615570739898004, high: 21.769114652020402, low: 21.581449017515734, close: 21.743524, volume: 40763500, split: '', dividend: '', }, { date: '2011-03-30', open: 21.8373559298182, high: 21.939717682717507, low: 21.752053758217347, close: 21.845887, volume: 41999300, split: '', dividend: '', }, { date: '2011-03-31', open: 21.837356417383084, high: 21.905598156187406, low: 21.615570766269038, close: 21.658221, volume: 63233700, split: '', dividend: '', }, { date: '2011-04-01', open: 21.7776459401489, high: 21.7776459401489, low: 21.58997945074592, close: 21.734994, volume: 63114200, split: '', dividend: '', }, { date: '2011-04-04', open: 21.709404530493526, high: 21.888538246126743, low: 21.675282807251772, close: 21.794705, volume: 35433700, split: '', dividend: '', }, { date: '2011-04-05', open: 22.025021016096936, high: 22.332108838164903, low: 21.95677927785961, close: 21.990901, volume: 73651100, split: '', dividend: '', }, { date: '2011-04-06', open: 22.16150430745698, high: 22.44300062231289, low: 22.059142553977747, close: 22.306518, volume: 65581400, split: '', dividend: '', }, { date: '2011-04-07', open: 22.340639782768328, high: 22.400350450368304, low: 22.15297329762812, close: 22.34917, volume: 46134700, split: '', dividend: '', }, { date: '2011-04-08', open: 22.323579174913693, high: 22.417412420340504, low: 22.144443754573185, close: 22.238277, volume: 39887600, split: '', dividend: '', }, { date: '2011-04-11', open: 22.340639411913163, high: 22.39181986143187, low: 22.007959239357042, close: 22.161504, volume: 34286300, split: '', dividend: '', }, { date: '2011-04-12', open: 22.033551986878, high: 22.050612422020766, low: 21.794705041857565, close: 21.871477, volume: 36920400, split: '', dividend: '', }, { date: '2011-04-13', open: 21.880008288334302, high: 22.067673929794022, low: 21.8032354764061, close: 21.862947, volume: 38144700, split: '', dividend: '', }, { date: '2011-04-14', open: 21.683813, high: 21.700874288112235, low: 21.402315821007083, close: 21.683813, volume: 55239900, split: '', dividend: '', }, { date: '2011-04-15', open: 21.71793224914883, high: 21.803234421584694, low: 21.479087019350143, close: 21.641162, volume: 65080400, split: '', dividend: '', }, { date: '2011-04-18', open: 21.410845434609254, high: 21.564390199114236, low: 21.086696324011765, close: 21.393785, volume: 58045100, split: '', dividend: '', }, { date: '2011-04-19', open: 21.32554274353877, high: 21.470556434194833, low: 21.214650774294075, close: 21.453496, volume: 38892400, split: '', dividend: '', }, { date: '2011-04-20', open: 21.786176070413042, high: 22.178565217391302, low: 21.76058456521739, close: 21.97384, volume: 61608600, split: '', dividend: '', }, { date: '2011-04-21', open: 21.99943172488695, high: 22.084732193608346, low: 21.632632373397925, close: 21.769115, volume: 46892300, split: '', dividend: '', }, { date: '2011-04-25', open: 21.803234208156145, high: 21.854417217160083, low: 21.615570283655984, close: 21.845887, volume: 33525100, split: '', dividend: '', }, { date: '2011-04-26', open: 21.956779367820566, high: 22.553895432865392, low: 21.89706784661826, close: 22.34064, volume: 69200000, split: '', dividend: '', }, { date: '2011-04-27', open: 22.434471259733062, high: 22.511243217533366, low: 22.289457561665827, close: 22.502713, volume: 52689000, split: '', dividend: '', }, { date: '2011-04-28', open: 22.570954563337498, high: 22.920695185507494, low: 22.519774111560245, close: 22.78421, volume: 80200000, split: '', dividend: '', }, { date: '2011-04-29', open: 22.64772685531157, high: 22.724498813644903, low: 21.63263266783657, close: 22.110324, volume: 319317900, split: '', dividend: '', }, { date: '2011-05-02', open: 22.12738494187599, high: 22.17856539360873, low: 21.734994085736556, close: 21.888538, volume: 89825600, split: '', dividend: '', }, { date: '2011-05-03', open: 21.83735728157138, high: 22.050612723774226, low: 21.743524887002124, close: 22.016491, volume: 71892900, split: '', dividend: '', }, { date: '2011-05-04', open: 22.050612285134775, high: 22.39182098587187, low: 21.99943183304596, close: 22.229746, volume: 73292300, split: '', dividend: '', }, { date: '2011-05-05', open: 22.22121494103738, high: 22.246806445645348, low: 21.905597757828705, close: 21.999431, volume: 55600000, split: '', dividend: '', }, { date: '2011-05-06', open: 22.23827649291548, high: 22.366228899346662, low: 21.965309539415944, close: 22.067673, volume: 55993000, split: '', dividend: '', }, { date: '2011-05-09', open: 22.00796049424886, high: 22.144443975472242, low: 21.89706851877662, close: 22.033552, volume: 38696400, split: '', dividend: '', }, { date: '2011-05-10', open: 21.649690843121622, high: 22.050611912738606, low: 21.538798870276587, close: 21.897068, volume: 120798700, split: '', dividend: '', }, { date: '2011-05-11', open: 21.88000744952652, high: 21.888537666855772, low: 21.504677034017785, close: 21.632632, volume: 78600000, split: '', dividend: '', }, { date: '2011-05-12', open: 21.624100651658768, high: 21.658220667515405, low: 21.410845221169037, close: 21.59851, volume: 77400000, split: '', dividend: '', }, { date: '2011-05-13', open: 21.564390434947843, high: 21.59851045151776, low: 21.28289326081669, close: 21.351135, volume: 66812300, split: '', dividend: '', }, { date: '2011-05-16', open: 21.29142162316874, high: 21.385254866910866, low: 20.899032478632478, close: 20.958744, volume: 91350900, split: '', dividend: '', }, { date: '2011-05-17', open: 20.950158433931485, high: 21.207743207715826, low: 20.838538737357258, close: 21.053192, volume: 82882100, split: '', dividend: '$0.160', }, { date: '2011-05-18', open: 21.061777918889348, high: 21.24208579173407, low: 20.821365418332707, close: 21.199156, volume: 53931100, split: '', dividend: '', }, { date: '2011-05-19', open: 21.336533551235178, high: 21.36229108322727, low: 21.036018994175524, close: 21.224913, volume: 37783600, split: '', dividend: '', }, { date: '2011-05-20', open: 21.224913137303677, high: 21.35370680838844, low: 20.984503207326785, close: 21.027433, volume: 45451500, split: '', dividend: '', }, { date: '2011-05-23', open: 20.78702066269437, high: 20.82136604261481, low: 20.632472034037107, close: 20.752677, volume: 52692500, split: '', dividend: '', }, { date: '2011-05-24', open: 20.778435508716523, high: 20.855710678902856, low: 20.64105742838526, close: 20.735504, volume: 47691800, split: '', dividend: '', }, { date: '2011-05-25', open: 20.75267588165871, high: 20.87288084114387, low: 20.744089751794554, close: 20.769849, volume: 34904200, split: '', dividend: '', }, { date: '2011-05-26', open: 20.907226836238344, high: 21.49108454284487, low: 20.88146844588569, close: 21.181983, volume: 78016600, split: '', dividend: '', }, { date: '2011-05-27', open: 21.190568959612275, high: 21.37946382067851, low: 21.16481056946688, close: 21.259258, volume: 50251000, split: '', dividend: '', }, { date: '2011-05-31', open: 21.430980489647656, high: 21.516841793126268, low: 21.207742817829345, close: 21.473912, volume: 60196300, split: '', dividend: '', }, { date: '2011-06-01', open: 21.456739289398282, high: 21.551186721244374, low: 20.92440007760606, close: 20.975916, volume: 74033500, split: '', dividend: '', }, { date: '2011-06-02', open: 21.02743337974539, high: 21.16481146634234, low: 20.761263336963804, close: 20.795607, volume: 51487800, split: '', dividend: '', }, { date: '2011-06-03', open: 20.649641962382393, high: 20.726917133022294, low: 20.4693340895023, close: 20.529437, volume: 60697700, split: '', dividend: '', }, { date: '2011-06-06', open: 20.512264577038774, high: 20.821366128696376, low: 20.40923187130362, close: 20.615299, volume: 54778700, split: '', dividend: '', }, { date: '2011-06-07', open: 20.683988249957945, high: 20.75267729354436, low: 20.52085177144022, close: 20.658229, volume: 41112600, split: '', dividend: '', }, { date: '2011-06-08', open: 20.520850621518353, high: 20.623884181124303, low: 20.4865069602627, close: 20.555196, volume: 42205000, split: '', dividend: '', }, { date: '2011-06-09', open: 20.61529850940311, high: 20.64105775849018, low: 20.452162036400754, close: 20.572367, volume: 42878700, split: '', dividend: '', }, { date: '2011-06-10', open: 20.623884896831925, high: 20.623884896831925, low: 20.3405434566958, close: 20.357714, volume: 49327200, split: '', dividend: '', }, { date: '2011-06-13', open: 20.42640474353799, high: 20.769849953877205, low: 20.349129571211666, close: 20.641058, volume: 47572500, split: '', dividend: '', }, { date: '2011-06-14', open: 20.864296043298474, high: 20.993089716709196, low: 20.769850325989154, close: 20.795607, volume: 42894500, split: '', dividend: '', }, { date: '2011-06-15', open: 20.606712384161753, high: 20.61529851432182, low: 20.32337008887953, close: 20.383473, volume: 49410200, split: '', dividend: '', }, { date: '2011-06-16', open: 20.39205875, high: 20.692573300000003, low: 20.30619745, close: 20.606712, volume: 57184100, split: '', dividend: '', }, { date: '2011-06-17', open: 20.795606620364715, high: 20.86429566240923, low: 20.589540352844185, close: 20.829952, volume: 83320400, split: '', dividend: '', }, { date: '2011-06-20', open: 20.7526769494351, high: 21.173397334425715, low: 20.744090819129166, close: 21.01026, volume: 54338400, split: '', dividend: '', }, { date: '2011-06-21', open: 21.05319087883683, high: 21.345120159097657, low: 20.950157318255247, close: 21.259258, volume: 49708700, split: '', dividend: '', }, { date: '2011-06-22', open: 21.12187935091278, high: 21.302187218466127, low: 21.113293221095336, close: 21.16481, volume: 44287300, split: '', dividend: '', }, { date: '2011-06-23', open: 20.984503242068257, high: 21.16481111915595, low: 20.77843611555315, close: 21.147638, volume: 59470400, split: '', dividend: '', }, { date: '2011-06-24', open: 21.04460559689735, high: 21.070364846693863, low: 20.769850282886676, close: 20.864296, volume: 101387200, split: '', dividend: '', }, { date: '2011-06-27', open: 20.80419351054788, high: 21.860286668359695, low: 20.80419351054788, close: 21.637049, volume: 92044200, split: '', dividend: '', }, { date: '2011-06-28', open: 21.722908491112154, high: 22.255249420746104, low: 21.60270352723657, close: 22.152215, volume: 81032100, split: '', dividend: '', }, { date: '2011-06-29', open: 22.074939453059894, high: 22.074939453059894, low: 21.774426619174058, close: 21.997666, volume: 66051000, split: '', dividend: '', }, { date: '2011-06-30', open: 22.10069862, high: 22.323938, low: 22.03200958, close: 22.323938, volume: 52535400, split: '', dividend: '', }, { date: '2011-07-01', open: 22.263835827440435, high: 22.469902954265955, low: 22.186560654880864, close: 22.341111, volume: 52906200, split: '', dividend: '', }, { date: '2011-07-05', open: 22.40979905071844, high: 22.452729700240887, low: 22.23807645262864, close: 22.349697, volume: 37805300, split: '', dividend: '', }, { date: '2011-07-06', open: 22.298179451679417, high: 22.641626379691644, low: 22.28959332140976, close: 22.607281, volume: 48744200, split: '', dividend: '', }, { date: '2011-07-07', open: 22.744659349645126, high: 23.079517575454954, low: 22.633040513450545, close: 22.985071, volume: 51946500, split: '', dividend: '', }, { date: '2011-07-08', open: 22.787589918048365, high: 23.165378780089153, low: 22.761830669390786, close: 23.113862, volume: 58320700, split: '', dividend: '', }, { date: '2011-07-11', open: 22.856279586974974, high: 23.010828214268276, low: 22.74465903509797, close: 22.864864, volume: 43999800, split: '', dividend: '', }, { date: '2011-07-12', open: 22.79617441281973, high: 23.002243251143437, low: 22.61586654047225, close: 22.78759, volume: 47319300, split: '', dividend: '', }, { date: '2011-07-13', open: 22.8391064678598, high: 23.148206298285476, low: 22.761831295600125, close: 22.864864, volume: 40861800, split: '', dividend: '', }, { date: '2011-07-14', open: 22.85627967146829, high: 23.191137893885074, low: 22.633040284115083, close: 22.727486, volume: 46382300, split: '', dividend: '', }, { date: '2011-07-15', open: 22.727485252757948, high: 23.12244809139477, low: 22.727485252757948, close: 22.993657, volume: 49132400, split: '', dividend: '', }, { date: '2011-07-18', open: 22.864863661883415, high: 23.09669003384731, low: 22.547177705904474, close: 22.83052, volume: 44501900, split: '', dividend: '', }, { date: '2011-07-19', open: 23.0194137895564, high: 23.732062583214756, low: 22.993657116650176, close: 23.646203, volume: 86730600, split: '', dividend: '', }, { date: '2011-07-20', open: 23.42296458599529, high: 23.483066640172456, low: 23.165379815424235, close: 23.234068, volume: 49795400, split: '', dividend: '', }, { date: '2011-07-21', open: 23.21689707706321, high: 23.44872087681133, low: 22.88203713837638, close: 23.268413, volume: 81737400, split: '', dividend: '', }, { date: '2011-07-22', open: 23.062346283881972, high: 23.65478754295661, low: 22.907795083625313, close: 23.637617, volume: 76380600, split: '', dividend: '', }, { date: '2011-07-25', open: 23.405790546040848, high: 24.118439341096384, low: 23.34568849422748, close: 23.963889, volume: 108482400, split: '', dividend: '', }, { date: '2011-07-26', open: 23.886613620370373, high: 24.169955909900285, low: 23.85226995904035, close: 24.109853, volume: 74636500, split: '', dividend: '', }, { date: '2011-07-27', open: 23.9381303056753, high: 24.032578597145992, low: 23.354275165235784, close: 23.465894, volume: 71488700, split: '', dividend: '', }, { date: '2011-07-28', open: 23.43155011949142, high: 24.101267414908637, low: 23.362859360826384, close: 23.800752, volume: 83761400, split: '', dividend: '', }, { date: '2011-07-29', open: 23.62902955912409, high: 23.792165169124235, low: 23.4057901810219, close: 23.525996, volume: 104394800, split: '', dividend: '', }, { date: '2011-08-01', open: 23.620444124312435, high: 23.77499532615977, low: 22.9678982306564, close: 23.414377, volume: 61838400, split: '', dividend: '', }, { date: '2011-08-02', open: 23.165379201693252, high: 23.56892817834911, low: 22.97648433792852, close: 23.010828, volume: 63883100, split: '', dividend: '', }, { date: '2011-08-03', open: 23.036586829866266, high: 23.18255104011887, low: 22.73607227934621, close: 23.113862, volume: 64581200, split: '', dividend: '', }, { date: '2011-08-04', open: 22.779003668211963, high: 23.070932087181568, low: 22.26383501141731, close: 22.272422, volume: 92949500, split: '', dividend: '', }, { date: '2011-08-05', open: 22.298178913193848, high: 22.40979946261682, low: 21.66280614719626, close: 22.049182, volume: 112071700, split: '', dividend: '', }, { date: '2011-08-08', open: 21.48249701470588, high: 21.98049254901961, low: 20.941569972269363, close: 21.018846, volume: 134257200, split: '', dividend: '', }, { date: '2011-08-09', open: 21.216326815741947, high: 21.99766637933233, low: 20.63247168073968, close: 21.963321, volume: 126268900, split: '', dividend: '', }, { date: '2011-08-10', open: 21.42239576677852, high: 21.54260073129749, low: 20.692573839149844, close: 20.778436, volume: 127819900, split: '', dividend: '', }, { date: '2011-08-11', open: 21.03601818038832, high: 21.791596750295408, low: 20.95015688169286, close: 21.628462, volume: 90690100, split: '', dividend: '', }, { date: '2011-08-12', open: 21.576944532223624, high: 21.757254126693223, low: 21.164811137450194, close: 21.551187, volume: 64787100, split: '', dividend: '', }, { date: '2011-08-15', open: 21.671392486083885, high: 21.963320911015284, low: 21.594117314778515, close: 21.903218, volume: 56529400, split: '', dividend: '', }, { date: '2011-08-16', open: 21.790892941095933, high: 22.110585744378696, low: 21.644007455494354, close: 21.903218, volume: 54251500, split: '', dividend: '$0.160', }, { date: '2011-08-17', open: 21.816815, high: 22.20563038878475, low: 21.540324671287127, close: 21.816815, volume: 50923700, split: '', dividend: '', }, { date: '2011-08-18', open: 21.229272773408997, high: 21.678569551682205, low: 20.762696213849857, close: 21.315676, volume: 105714200, split: '', dividend: '', }, { date: '2011-08-19', open: 21.0910274777974, high: 21.272475116525992, low: 20.65901134756804, close: 20.779975, volume: 77397900, split: '', dividend: '', }, { date: '2011-08-22', open: 21.09966718348624, high: 21.160149439949958, low: 20.555327739344996, close: 20.719493, volume: 54721000, split: '', dividend: '', }, { date: '2011-08-23', open: 20.76269645758792, high: 21.384798832313866, low: 20.76269645758792, close: 21.358877, volume: 59670600, split: '', dividend: '', }, { date: '2011-08-24', open: 21.298394937751006, high: 21.540323967469885, low: 21.099667520481933, close: 21.514403, volume: 45329700, split: '', dividend: '', }, { date: '2011-08-25', open: 21.66992946031746, high: 21.73905204232804, low: 21.16879074074074, close: 21.229273, volume: 48192000, split: '', dividend: '', }, { date: '2011-08-26', open: 21.177431114851483, high: 21.89457790495049, low: 21.09966820990099, close: 21.816815, volume: 71957000, split: '', dividend: '', }, { date: '2011-08-29', open: 22.058744858614318, high: 22.34387550954311, low: 21.920499694527628, close: 22.326594, volume: 38863200, split: '', dividend: '', }, { date: '2011-08-30', open: 22.231549873427372, high: 22.83637245062905, low: 22.20562976986527, close: 22.663566, volume: 57341400, split: '', dividend: '', }, { date: '2011-08-31', open: 22.715409853130037, high: 23.078301688223345, low: 22.689488020300754, close: 22.983259, volume: 59300800, split: '', dividend: '', }, { date: '2011-09-01', open: 22.86229306814224, high: 23.207907705234366, low: 22.646285, close: 22.646285, volume: 60510800, split: '', dividend: '', }, { date: '2011-09-02', open: 22.274753082433527, high: 22.46483932034261, low: 22.17106834461505, close: 22.292032, volume: 43894400, split: '', dividend: '', }, { date: '2011-09-06', open: 21.77361386285625, high: 22.110585580948644, low: 21.69585095928902, close: 22.041463, volume: 54929300, split: '', dividend: '', }, { date: '2011-09-07', open: 22.19698986057073, high: 22.464839, low: 22.09330512423077, close: 22.464839, volume: 41961000, split: '', dividend: '', }, { date: '2011-09-08', open: 22.464838766774932, high: 23.035100058546913, low: 22.421638017794166, close: 22.654925, volume: 65811900, split: '', dividend: '', }, { date: '2011-09-09', open: 22.464838383838384, high: 22.620364188034188, low: 22.032822261072262, close: 22.24019, volume: 64529200, split: '', dividend: '', }, { date: '2011-09-12', open: 21.98098219972102, high: 22.4043571554406, low: 21.834095847203393, close: 22.369795, volume: 55046100, split: '', dividend: '', }, { date: '2011-09-13', open: 22.395716264373416, high: 22.629005839492898, low: 22.30067185137969, close: 22.499401, volume: 48792300, split: '', dividend: '', }, { date: '2011-09-14', open: 22.61172435283019, high: 23.156063815213017, low: 22.36979445483566, close: 22.896855, volume: 66739200, split: '', dividend: '', }, { date: '2011-09-15', open: 23.095582609484996, high: 23.354793154880735, low: 22.732688191543872, close: 23.320231, volume: 67808300, split: '', dividend: '', }, { date: '2011-09-16', open: 23.37207201310369, high: 23.56215997632154, low: 23.181985777950377, close: 23.432556, volume: 89681500, split: '', dividend: '', }, { date: '2011-09-19', open: 23.156063772353797, high: 23.596720226255172, low: 22.983258183875716, close: 23.510317, volume: 52324900, split: '', dividend: '', }, { date: '2011-09-20', open: 23.59671977718347, high: 23.760886767976277, low: 23.268388387694586, close: 23.31159, volume: 49211900, split: '', dividend: '', }, { date: '2011-09-21', open: 23.372071313728437, high: 23.380711636160143, low: 22.43891649110435, close: 22.456198, volume: 72750700, split: '', dividend: '', }, { date: '2011-09-22', open: 21.860015746503098, high: 22.162427907519067, low: 21.255194016567998, close: 21.652648, volume: 96278300, split: '', dividend: '', }, { date: '2011-09-23', open: 21.51440369969687, high: 21.730411768970935, low: 21.332957785538934, close: 21.652648, volume: 64768100, split: '', dividend: '', }, { date: '2011-09-26', open: 21.764972940487738, high: 22.050102715011686, low: 21.36751724695294, close: 21.980981, volume: 51057600, split: '', dividend: '', }, { date: '2011-09-27', open: 22.171067677444487, high: 22.395716063887807, low: 21.989621767810984, close: 22.179708, volume: 55620700, split: '', dividend: '', }, { date: '2011-09-28', open: 22.404356288115718, high: 22.784531342922012, low: 22.04146274237686, close: 22.101945, volume: 60736200, split: '', dividend: '', }, { date: '2011-09-29', open: 22.447558236245257, high: 22.611724366533423, low: 21.67856952068489, close: 21.989622, volume: 63407300, split: '', dividend: '', }, { date: '2011-09-30', open: 21.77361474003125, high: 22.03282356499894, low: 21.497122677033335, close: 21.505763, volume: 54060500, split: '', dividend: '', }, { date: '2011-10-03', open: 21.358876399772182, high: 21.894577259903087, low: 21.18607081345003, close: 21.194712, volume: 64592500, split: '', dividend: '', }, { date: '2011-10-04', open: 20.99598356374988, high: 21.93777875001665, low: 20.961423136543015, close: 21.894578, volume: 83485400, split: '', dividend: '', }, { date: '2011-10-05', open: 21.96370068998458, high: 22.603084581038416, low: 21.739052295830525, close: 22.369795, volume: 94061300, split: '', dividend: '', }, { date: '2011-10-06', open: 22.37843580106302, high: 22.810451936218676, low: 22.20563021103303, close: 22.75861, volume: 55111400, split: '', dividend: '', }, { date: '2011-10-07', open: 22.758609904, high: 22.905495389333336, low: 22.637646250698932, close: 22.680847, volume: 52741600, split: '', dividend: '', }, { date: '2011-10-10', open: 22.965977521678635, high: 23.302949239792902, low: 22.870933108835818, close: 23.27703, volume: 41815300, split: '', dividend: '', }, { date: '2011-10-11', open: 23.20790734773596, high: 23.389353258148148, low: 23.086941103375146, close: 23.328871, volume: 38826200, split: '', dividend: '', }, { date: '2011-10-12', open: 23.48439696232926, high: 23.596720292745225, low: 23.24246792813308, close: 23.294309, volume: 52489800, split: '', dividend: '', }, { date: '2011-10-13', open: 23.121503448123622, high: 23.501678509359714, low: 23.00053979486376, close: 23.484397, volume: 43823500, split: '', dividend: '', }, { date: '2011-10-14', open: 23.596720426763476, high: 23.76088742207554, low: 23.346151932526585, close: 23.56216, volume: 50947700, split: '', dividend: '', }, { date: '2011-10-17', open: 23.423915056026313, high: 23.69176418828762, low: 23.19926580800593, close: 23.31159, volume: 39453300, split: '', dividend: '', }, { date: '2011-10-18', open: 23.27703078043763, high: 23.674484770211816, low: 23.156064531649342, close: 23.596721, volume: 52487900, split: '', dividend: '', }, { date: '2011-10-19', open: 23.648564476216713, high: 23.734965976548875, low: 23.337511989956212, close: 23.441195, volume: 42880000, split: '', dividend: '', }, { date: '2011-10-20', open: 23.553519231748552, high: 23.622641812032477, low: 22.81045149369632, close: 23.363433, volume: 76300200, split: '', dividend: '', }, { date: '2011-10-21', open: 23.45847567746686, high: 23.493037831631664, low: 23.156063524774815, close: 23.467116, volume: 76620600, split: '', dividend: '', }, { date: '2011-10-24', open: 23.380712078199704, high: 23.674483910463994, low: 23.363433161074102, close: 23.493038, volume: 56897800, split: '', dividend: '', }, { date: '2011-10-25', open: 23.397994584035605, high: 23.52759942848189, low: 23.08694209333223, close: 23.164705, volume: 53554600, split: '', dividend: '', }, { date: '2011-10-26', open: 23.354793061850998, high: 23.380712301819557, low: 22.55124218879278, close: 22.974618, volume: 63029900, split: '', dividend: '', }, { date: '2011-10-27', open: 23.44119426514206, high: 23.67448383853211, low: 23.02645964587156, close: 23.544879, volume: 74512400, split: '', dividend: '', }, { date: '2011-10-28', open: 23.44983429534507, high: 23.493037635714973, low: 23.14742473727168, close: 23.31159, volume: 57712100, split: '', dividend: '', }, { date: '2011-10-31', open: 23.121504061641158, high: 23.328871811072915, low: 23.000540405171584, close: 23.009179, volume: 46799000, split: '', dividend: '', }, { date: '2011-11-01', open: 22.629005312666333, high: 22.741328640246252, low: 22.343874672420085, close: 22.456198, volume: 61182600, split: '', dividend: '', }, { date: '2011-11-02', open: 22.551242906574394, high: 22.637647000133793, low: 22.205630852498267, close: 22.47348, volume: 53533100, split: '', dividend: '', }, { date: '2011-11-03', open: 22.67220677752707, high: 22.97461807219683, low: 22.447558387200967, close: 22.922777, volume: 65836100, split: '', dividend: '', }, { date: '2011-11-04', open: 22.7931703306344, high: 22.81045184, low: 22.464838933333333, close: 22.680847, volume: 36549200, split: '', dividend: '', }, { date: '2011-11-07', open: 22.646284960082873, high: 23.173345509453192, low: 22.577162378399198, close: 23.156064, volume: 42589700, split: '', dividend: '', }, { date: '2011-11-08', open: 23.337511162002947, high: 23.5016781541648, low: 23.061021704974813, close: 23.467116, volume: 47822500, split: '', dividend: '', }, { date: '2011-11-09', open: 22.974617716235965, high: 23.112862877371647, low: 22.516679755941766, close: 22.637646, volume: 62950900, split: '', dividend: '', }, { date: '2011-11-10', open: 22.870933403816505, high: 22.89685523604052, low: 22.5685238363107, close: 22.706769, volume: 32514400, split: '', dividend: '', }, { date: '2011-11-11', open: 22.965977355629875, high: 23.397993483463395, low: 22.957337033073205, close: 23.251108, volume: 37903000, split: '', dividend: '', }, { date: '2011-11-14', open: 23.225187010407176, high: 23.328871748878925, low: 23.02646044843049, close: 23.121504, volume: 34199200, split: '', dividend: '', }, { date: '2011-11-15', open: 23.12150219602842, high: 23.452308574353026, low: 22.98221690351533, close: 23.2782, volume: 43874200, split: '', dividend: '$0.200', }, { date: '2011-11-16', open: 23.043153534141197, high: 23.07797594514768, low: 22.668823715187536, close: 22.694939, volume: 53262800, split: '', dividend: '', }, { date: '2011-11-17', open: 22.64270723990966, high: 22.668824266434246, low: 22.146501146713145, close: 22.233555, volume: 70977500, split: '', dividend: '', }, { date: '2011-11-18', open: 22.181321806376356, high: 22.198732577025, low: 21.894044090673678, close: 22.024624, volume: 47626200, split: '', dividend: '', }, { date: '2011-11-21', open: 21.9723922448, high: 21.98109763, low: 21.676409148, close: 21.763463, volume: 61882500, split: '', dividend: '', }, { date: '2011-11-22', open: 21.667703111804997, high: 21.728640808822437, low: 21.458774735426594, close: 21.580651, volume: 49204500, split: '', dividend: '', }, { date: '2011-11-23', open: 21.423954135514144, high: 21.580651071218963, low: 21.302077, close: 21.302077, volume: 49099700, split: '', dividend: '', }, { date: '2011-11-25', open: 21.223729084347454, high: 21.476186135645523, low: 21.154086, close: 21.154086, volume: 26164600, split: '', dividend: '', }, { date: '2011-11-28', open: 21.711231696785777, high: 21.73734611147406, low: 21.493597065407997, close: 21.650294, volume: 46766700, split: '', dividend: '', }, { date: '2011-11-29', open: 21.606766229468597, high: 21.798285575852535, low: 21.545828532608695, close: 21.624177, volume: 40917100, split: '', dividend: '', }, { date: '2011-11-30', open: 22.085563775933387, high: 22.27708138545739, low: 21.885338169336357, close: 22.268376, volume: 81350900, split: '', dividend: '', }, { date: '2011-12-01', open: 22.250964048331525, high: 22.311901745683674, low: 21.937571917311832, close: 22.007215, volume: 48545400, split: '', dividend: '', }, { date: '2011-12-02', open: 22.277081128750243, high: 22.303198155359997, low: 21.902749558396096, close: 21.954981, volume: 52293800, split: '', dividend: '', }, { date: '2011-12-05', open: 22.442484082115055, high: 22.459893111566767, low: 22.198732424173837, close: 22.372841, volume: 56818400, split: '', dividend: '', }, { date: '2011-12-06', open: 22.468598910833244, high: 22.52083296445904, low: 22.294492943414614, close: 22.338019, volume: 46175300, split: '', dividend: '', }, { date: '2011-12-07', open: 22.346724698828126, high: 22.42507316875, low: 22.05944697578125, close: 22.285787, volume: 62667000, split: '', dividend: '', }, { date: '2011-12-08', open: 22.181321080314962, high: 22.390249450721342, low: 22.085562715420394, close: 22.111678, volume: 60522200, split: '', dividend: '', }, { date: '2011-12-09', open: 22.2161431947026, high: 22.52083254949449, low: 22.198732424173837, close: 22.372841, volume: 53788500, split: '', dividend: '', }, { date: '2011-12-12', open: 22.120384146609172, high: 22.259670312034494, low: 22.015920393078712, close: 22.207438, volume: 38945900, split: '', dividend: '', }, { date: '2011-12-13', open: 22.416367614518634, high: 22.72105610636646, low: 22.32931375970497, close: 22.425073, volume: 54581100, split: '', dividend: '', }, { date: '2011-12-14', open: 22.390250138449353, high: 22.51212727382106, low: 22.259670229386476, close: 22.277081, volume: 47926400, split: '', dividend: '', }, { date: '2011-12-15', open: 22.390250165073795, high: 22.529536330147586, low: 22.23355497044284, close: 22.250964, volume: 46213900, split: '', dividend: '', }, { date: '2011-12-16', open: 22.346724282307694, high: 22.781993551538463, low: 22.31190187023069, close: 22.634002, volume: 101408100, split: '', dividend: '', }, { date: '2011-12-19', open: 22.651411998769603, high: 22.73846672018732, low: 22.163909563307538, close: 22.224849, volume: 52258300, split: '', dividend: '', }, { date: '2011-12-20', open: 22.51212744863587, high: 22.721055827081994, low: 22.46859878068699, close: 22.660119, volume: 60767600, split: '', dividend: '', }, { date: '2011-12-21', open: 22.642707637034164, high: 22.799405446237305, low: 22.14650153513482, close: 22.425073, volume: 64132500, split: '', dividend: '', }, { date: '2011-12-22', open: 22.47730525599788, high: 22.512127668373758, low: 22.181322150380556, close: 22.468599, volume: 35794100, split: '', dividend: '', }, { date: '2011-12-23', open: 22.555653504969136, high: 22.668824385374364, low: 22.398956568230638, close: 22.660119, volume: 23205800, split: '', dividend: '', }, { date: '2011-12-27', open: 22.599179177112013, high: 22.75587611118663, low: 22.573063891971433, close: 22.668824, volume: 21287200, split: '', dividend: '', }, { date: '2011-12-28', open: 22.72976204598393, high: 22.764582716886135, low: 22.425072687838888, close: 22.477305, volume: 29822500, split: '', dividend: '', }, { date: '2011-12-29', open: 22.590476172229558, high: 22.677528285879596, low: 22.51212770297514, close: 22.651413, volume: 22616900, split: '', dividend: '', }, { date: '2011-12-30', open: 22.634002412711958, high: 22.73846790977072, low: 22.555653942821802, close: 22.59918, volume: 27395700, split: '', dividend: '', }, { date: '2012-01-03', open: 23.112797648325852, high: 23.46971845407856, low: 22.973511480227234, close: 23.304317, volume: 64731500, split: '', dividend: '', }, { date: '2012-01-04', open: 23.34784266788321, high: 23.91369182471697, low: 23.31302199827573, close: 23.852755, volume: 80516100, split: '', dividend: '', }, { date: '2012-01-05', open: 23.835344565155093, high: 24.14003392738439, low: 23.75699783694028, close: 24.096507, volume: 56081400, split: '', dividend: '', }, { date: '2012-01-06', open: 23.965926651544372, high: 24.540482082545605, low: 23.965926651544372, close: 24.470839, volume: 99455500, split: '', dividend: '', }, { date: '2012-01-09', open: 24.418605075748413, high: 24.46213287310743, low: 24.131327358733273, close: 24.148739, volume: 59706800, split: '', dividend: '', }, { date: '2012-01-10', open: 24.314141468749998, high: 24.505659947916666, low: 24.15744453125, close: 24.235793, volume: 60014400, split: '', dividend: '', }, { date: '2012-01-11', open: 23.878872688271017, high: 24.3576688960198, low: 23.82664124523698, close: 24.131328, volume: 65582400, split: '', dividend: '', }, { date: '2012-01-12', open: 24.261909860895678, high: 24.392489770714285, low: 24.0703905125, close: 24.375079, volume: 49370800, split: '', dividend: '', }, { date: '2012-01-13', open: 24.31414166442478, high: 24.592714, low: 24.19226713814917, close: 24.592714, volume: 60196100, split: '', dividend: '', }, { date: '2012-01-17', open: 24.723294394904457, high: 24.940929028662417, low: 24.523070531847132, close: 24.601419, volume: 72395300, split: '', dividend: '', }, { date: '2012-01-18', open: 24.644944209872406, high: 24.723293545873183, low: 24.348961118126038, close: 24.575302, volume: 64860600, split: '', dividend: '', }, { date: '2012-01-19', open: 24.514364670186183, high: 24.758116325797573, low: 24.401195533369435, close: 24.479544, volume: 74053500, split: '', dividend: '', }, { date: '2012-01-20', open: 25.088920574517694, high: 25.88981602658418, low: 25.02798287707785, close: 25.863699, volume: 165902900, split: '', dividend: '', }, { date: '2012-01-23', open: 25.724412197069963, high: 26.072629343461486, low: 25.550305364951228, close: 25.88111, volume: 76078100, split: '', dividend: '', }, { date: '2012-01-24', open: 25.654770140708898, high: 25.74182486605317, low: 25.40231483231084, close: 25.541601, volume: 51703300, split: '', dividend: '', }, { date: '2012-01-25', open: 25.306555975526248, high: 25.81146834105102, low: 25.306555975526248, close: 25.733119, volume: 59231700, split: '', dividend: '', }, { date: '2012-01-26', open: 25.77664711019956, high: 25.854995579013117, low: 25.59383314576271, close: 25.680887, volume: 49102800, split: '', dividend: '', }, { date: '2012-01-27', open: 25.637360345393123, high: 25.707003427158433, low: 25.393608688676018, close: 25.445841, volume: 44187700, split: '', dividend: '', }, { date: '2012-01-30', open: 25.219499615800554, high: 25.785351385048788, low: 25.097625095656024, close: 25.776646, volume: 51114800, split: '', dividend: '', }, { date: '2012-01-31', open: 25.820172135449642, high: 25.854994546292197, low: 25.445840577181155, close: 25.707003, volume: 50572400, split: '', dividend: '', }, { date: '2012-02-01', open: 25.93334388737839, high: 26.159682165917907, low: 25.907226860730244, close: 26.020396, volume: 67409900, split: '', dividend: '', }, { date: '2012-02-02', open: 26.029102202701093, high: 26.264147607207093, low: 25.86369901047315, close: 26.07263, volume: 52223300, split: '', dividend: '', }, { date: '2012-02-03', open: 26.23803027694825, high: 26.464371164021166, low: 26.19450422123016, close: 26.325085, volume: 41838500, split: '', dividend: '', }, { date: '2012-02-06', open: 26.15097783772471, high: 26.307673029207386, low: 26.09003840065224, close: 26.290264, volume: 28039700, split: '', dividend: '', }, { date: '2012-02-07', open: 26.246737289950573, high: 26.542720397034593, low: 26.159682564387314, close: 26.420845, volume: 39242400, split: '', dividend: '', }, { date: '2012-02-08', open: 26.34249559230268, high: 26.699416385192436, low: 26.307673180994424, close: 26.690711, volume: 49659100, split: '', dividend: '', }, { date: '2012-02-09', open: 26.70812253103672, high: 26.812586285782547, low: 26.534014822229445, close: 26.786471, volume: 50481600, split: '', dividend: '', }, { date: '2012-02-10', open: 26.673299522904095, high: 26.812585686838524, low: 26.4295504770959, close: 26.551425, volume: 44605300, split: '', dividend: '', }, { date: '2012-02-13', open: 26.664594055556968, high: 26.786470319162852, low: 26.49048722171354, close: 26.621068, volume: 33319800, split: '', dividend: '', }, { date: '2012-02-14', open: 26.577254561652893, high: 26.69116872306932, low: 26.15664519173554, close: 26.507153, volume: 59644000, split: '', dividend: '$0.200', }, { date: '2012-02-15', open: 26.577254340008462, high: 26.629829634540155, low: 26.314374362271955, close: 26.331898, volume: 43311300, split: '', dividend: '', }, { date: '2012-02-16', open: 26.559728122780374, high: 27.646302321355822, low: 26.550965427630572, close: 27.418474, volume: 94705100, split: '', dividend: '', }, { date: '2012-02-17', open: 27.339609401069506, high: 27.444760865280003, low: 27.120542025069504, close: 27.383422, volume: 70036500, split: '', dividend: '', }, { date: '2012-02-21', open: 27.32208305336886, high: 27.698879815236456, low: 27.295794968327133, close: 27.549914, volume: 50829900, split: '', dividend: '', }, { date: '2012-02-22', open: 27.558677390500414, high: 27.76021850463703, low: 27.32208374288455, close: 27.400948, volume: 49253200, split: '', dividend: '', }, { date: '2012-02-23', open: 27.339610180075415, high: 27.68135442010346, low: 27.16435539801226, close: 27.488576, volume: 35034700, split: '', dividend: '', }, { date: '2012-02-24', open: 27.584964, high: 27.602489390088945, low: 27.37465931893265, close: 27.584964, volume: 35575400, split: '', dividend: '', }, { date: '2012-02-27', open: 27.374660350877193, high: 27.602490430622012, low: 27.251982615629988, close: 27.47105, volume: 34568400, split: '', dividend: '', }, { date: '2012-02-28', open: 27.52362515143944, high: 27.97928529402933, low: 27.49733619002052, close: 27.92671, volume: 45230600, split: '', dividend: '', }, { date: '2012-02-29', open: 27.944233549061313, high: 28.04062407057341, low: 27.6988798409828, close: 27.812794, volume: 59323600, split: '', dividend: '', }, { date: '2012-03-01', open: 27.979286092930135, high: 28.382369200461042, low: 27.90918453053006, close: 28.294744, volume: 77344100, split: '', dividend: '', }, { date: '2012-03-02', open: 28.312269113659283, high: 28.426182398906086, low: 28.040624685746593, close: 28.110728, volume: 47314200, split: '', dividend: '', }, { date: '2012-03-05', open: 28.049385723856783, high: 28.084437381102745, low: 27.707643238145067, close: 27.86537, volume: 45240000, split: '', dividend: '', }, { date: '2012-03-06', open: 27.637542361616234, high: 28.02310008564956, low: 27.59372800803954, close: 27.655066, volume: 51932900, split: '', dividend: '', }, { date: '2012-03-07', open: 27.7514561790201, high: 27.97052356281407, low: 27.62877932036501, close: 27.900422, volume: 34340400, split: '', dividend: '', }, { date: '2012-03-08', open: 28.075676714799723, high: 28.224640782877085, low: 27.952998103904903, close: 28.049386, volume: 36747400, split: '', dividend: '', }, { date: '2012-03-09', open: 28.128249894850768, high: 28.18082781869334, low: 27.970523133479215, close: 28.031862, volume: 34628400, split: '', dividend: '', }, { date: '2012-03-12', open: 28.014335381710012, high: 28.21587912171651, low: 27.8828958313703, close: 28.075676, volume: 34073600, split: '', dividend: '', }, { date: '2012-03-13', open: 28.25093160444785, high: 28.64525026699653, low: 28.172067346176394, close: 28.627724, volume: 48951700, split: '', dividend: '', }, { date: '2012-03-14', open: 28.50504743285465, high: 28.811743526254283, low: 28.46999927985065, close: 28.715353, volume: 41986900, split: '', dividend: '', }, { date: '2012-03-15', open: 28.732878457571047, high: 28.864317133125795, low: 28.54886273450927, close: 28.785452, volume: 49068300, split: '', dividend: '', }, { date: '2012-03-16', open: 28.83803030754787, high: 28.873081965108863, low: 28.478759799310417, close: 28.566385, volume: 65626400, split: '', dividend: '', }, { date: '2012-03-19', open: 28.513810632362368, high: 28.57514949784874, low: 28.172066400922102, close: 28.215879, volume: 44789200, split: '', dividend: '', }, { date: '2012-03-20', open: 28.128249894850768, high: 28.172066876015133, low: 27.812794619568617, close: 28.031862, volume: 41566800, split: '', dividend: '', }, { date: '2012-03-21', open: 28.005572599130055, high: 28.172066434456912, low: 27.88289574428079, close: 27.96176, volume: 37928600, split: '', dividend: '', }, { date: '2012-03-22', open: 27.87413291279297, high: 28.1194892578125, low: 27.85660927470703, close: 28.040625, volume: 31749500, split: '', dividend: '', }, { date: '2012-03-23', open: 28.12825025797339, high: 28.13701558211237, low: 27.795268711688575, close: 28.049386, volume: 35912200, split: '', dividend: '', }, { date: '2012-03-26', open: 28.20711531151813, high: 28.57515026688014, low: 28.172067159105488, close: 28.557624, volume: 36758300, split: '', dividend: '', }, { date: '2012-03-27', open: 28.610201790976934, high: 28.65401439102967, low: 28.39113440936562, close: 28.496285, volume: 36274900, split: '', dividend: '', }, { date: '2012-03-28', open: 28.496284818151132, high: 28.654014208174253, low: 28.075676324411035, close: 28.207115, volume: 41344800, split: '', dividend: '', }, { date: '2012-03-29', open: 28.093201581537283, high: 28.207114866168705, low: 27.874132449824298, close: 28.145776, volume: 37038500, split: '', dividend: '', }, { date: '2012-03-30', open: 28.391134238040127, high: 28.399895180712655, low: 28.075676334153922, close: 28.268453, volume: 31749400, split: '', dividend: '', }, { date: '2012-04-02', open: 28.23340513289994, high: 28.443708067561108, low: 27.996812359799684, close: 28.294744, volume: 35853600, split: '', dividend: '', }, { date: '2012-04-03', open: 28.18082741575368, high: 28.207114624760063, low: 27.742692661155523, close: 27.988049, volume: 42752100, split: '', dividend: '', }, { date: '2012-04-04', open: 27.742693162534227, high: 27.76898212455473, low: 27.208167875994775, close: 27.348371, volume: 49455900, split: '', dividend: '', }, { date: '2012-04-05', open: 27.295796269035534, high: 27.716404773476654, low: 27.20816843845127, close: 27.620016, volume: 50368600, split: '', dividend: '', }, { date: '2012-04-09', open: 27.35713346585267, high: 27.514862855305466, low: 27.138066086431447, close: 27.251982, volume: 31056400, split: '', dividend: '', }, { date: '2012-04-10', open: 27.216931028454187, high: 27.330847821161136, low: 26.550966178242014, close: 26.699932, volume: 54131300, split: '', dividend: '', }, { date: '2012-04-11', open: 26.66488156177924, high: 26.752509390272813, low: 26.489627657331134, close: 26.59478, volume: 43014000, split: '', dividend: '', }, { date: '2012-04-12', open: 26.70869425177534, high: 27.199406046056456, low: 26.65611808198838, close: 27.146829, volume: 38304000, split: '', dividend: '', }, { date: '2012-04-13', open: 27.0679645615093, high: 27.30455820787271, low: 26.918998743302033, close: 26.997863, volume: 39749200, split: '', dividend: '', }, { date: '2012-04-16', open: 27.155592742277992, high: 27.33084752459643, low: 26.962813445624196, close: 27.234457, volume: 38124800, split: '', dividend: '', }, { date: '2012-04-17', open: 27.400947308494043, high: 27.698879815236456, low: 27.33960931966618, close: 27.549914, volume: 34361500, split: '', dividend: '', }, { date: '2012-04-18', open: 27.409711485444554, high: 27.435997818528126, low: 27.199406800463674, close: 27.287032, volume: 40552900, split: '', dividend: '', }, { date: '2012-04-19', open: 27.278269466845597, high: 27.760218582392774, low: 27.111780009452367, close: 27.173118, volume: 54781200, split: '', dividend: '', }, { date: '2012-04-20', open: 28.172066735393138, high: 28.820503549424775, low: 28.084437154849425, close: 28.408656, volume: 106045000, split: '', dividend: '', }, { date: '2012-04-23', open: 28.312268960711236, high: 28.478759292613926, low: 28.066911743497375, close: 28.145776, volume: 61398200, split: '', dividend: '', }, { date: '2012-04-24', open: 28.224640283818204, high: 28.496284710526318, low: 27.89165874342105, close: 27.970523, volume: 40871100, split: '', dividend: '', }, { date: '2012-04-25', open: 27.97052266178501, high: 28.321030464564274, low: 27.92671006270711, close: 28.215879, volume: 62495500, split: '', dividend: '', }, { date: '2012-04-26', open: 28.14577594261007, high: 28.242166465519578, low: 27.970522915897767, close: 28.137015, volume: 40308100, split: '', dividend: '', }, { date: '2012-04-27', open: 28.1457758554378, high: 28.23340455919634, low: 27.935471172510972, close: 28.023099, volume: 41419100, split: '', dividend: '', }, { date: '2012-04-30', open: 28.023099219237977, high: 28.137015132984068, low: 27.970523048094943, close: 28.05815, volume: 35697200, split: '', dividend: '', }, { date: '2012-05-01', open: 28.08443765759104, high: 28.33855669844153, low: 27.996812456826326, close: 28.049386, volume: 43832300, split: '', dividend: '', }, { date: '2012-05-02', open: 27.909184352490072, high: 27.97928591444295, low: 27.72516687609424, close: 27.86537, volume: 37385300, split: '', dividend: '', }, { date: '2012-05-03', open: 27.93547146629975, high: 27.95299773299748, low: 27.698880448057935, close: 27.83032, volume: 31501300, split: '', dividend: '', }, { date: '2012-05-04', open: 27.55867653960068, high: 27.663828002905102, low: 27.094252830213044, close: 27.146829, volume: 57927200, split: '', dividend: '', }, { date: '2012-05-07', open: 26.901474350984014, high: 27.041677470070475, low: 26.78755844045677, close: 26.85766, volume: 48641400, split: '', dividend: '', }, { date: '2012-05-08', open: 26.708694609836066, high: 26.97157633856459, low: 26.437051062295083, close: 26.72622, volume: 46328300, split: '', dividend: '', }, { date: '2012-05-09', open: 26.454578239403478, high: 27.015389867685304, low: 26.375713104681406, close: 26.954051, volume: 50309300, split: '', dividend: '', }, { date: '2012-05-10', open: 27.04167821849463, high: 27.18188046519193, low: 26.68240771589216, close: 26.936525, volume: 43839200, split: '', dividend: '', }, { date: '2012-05-11', open: 26.892712205537805, high: 27.63754129090366, low: 26.892712205537805, close: 27.304558, volume: 43459300, split: '', dividend: '', }, { date: '2012-05-14', open: 27.006626733376795, high: 27.199406904952706, low: 26.848897342765678, close: 26.883949, volume: 40528900, split: '', dividend: '', }, { date: '2012-05-15', open: 27.02507131080001, high: 27.166194403190712, low: 26.592882722372813, close: 26.645803, volume: 61822800, split: '', dividend: '$0.200', }, { date: '2012-05-16', open: 26.73400504373318, high: 26.742826119063547, low: 26.231254907023413, close: 26.372378, volume: 60083700, split: '', dividend: '', }, { date: '2012-05-17', open: 26.451760104702558, high: 26.645803478202875, low: 26.204793806567288, close: 26.213614, volume: 48484000, split: '', dividend: '', }, { date: '2012-05-18', open: 26.275357955131774, high: 26.29299657852043, low: 25.728505062863, close: 25.816707, volume: 56205300, split: '', dividend: '', }, { date: '2012-05-21', open: 25.666762436974793, high: 26.275356655128572, low: 25.63148078184622, close: 26.240075, volume: 38787900, split: '', dividend: '', }, { date: '2012-05-22', open: 26.18715552718064, high: 26.354737440561287, low: 26.01957096774193, close: 26.248896, volume: 39504900, split: '', dividend: '', }, { date: '2012-05-23', open: 25.88726776065724, high: 25.931368727881527, low: 25.261033144053002, close: 25.675584, volume: 65171000, split: '', dividend: '', }, { date: '2012-05-24', open: 25.71968373993808, high: 25.843165564489095, low: 25.366876006879945, close: 25.640302, volume: 52575000, split: '', dividend: '', }, { date: '2012-05-25', open: 25.75496647578969, high: 25.896089574933637, low: 25.587381913536884, close: 25.631482, volume: 29507200, split: '', dividend: '', }, { date: '2012-05-29', open: 25.91372751763317, high: 26.21361409543718, low: 25.772604422195986, close: 26.072491, volume: 37758800, split: '', dividend: '', }, { date: '2012-05-30', open: 25.887268193592366, high: 26.001930710293113, low: 25.684404622987316, close: 25.878448, volume: 41585500, split: '', dividend: '', }, { date: '2012-05-31', open: 25.843165361791353, high: 25.949008562897962, low: 25.525640168568167, close: 25.746145, volume: 39134000, split: '', dividend: '', }, { date: '2012-06-01', open: 25.36687611223634, high: 25.54327909747873, low: 25.084630806636913, close: 25.093451, volume: 56634300, split: '', dividend: '', }, { date: '2012-06-04', open: 25.24339511961636, high: 25.384518218079517, low: 24.97878842797858, close: 25.181652, volume: 47926300, split: '', dividend: '', }, { date: '2012-06-05', open: 25.146372, high: 25.358056646790597, low: 25.040528794585335, close: 25.146372, volume: 45715400, split: '', dividend: '', }, { date: '2012-06-06', open: 25.47271802223959, high: 25.90490926907216, low: 25.410976667554753, close: 25.887268, volume: 46860500, split: '', dividend: '', }, { date: '2012-06-07', open: 26.143052043057644, high: 26.195974966863666, low: 25.728503840232637, close: 25.781425, volume: 37792800, split: '', dividend: '', }, { date: '2012-06-08', open: 25.763783614776624, high: 26.178333579763912, low: 25.622660522702425, close: 26.151873, volume: 42551100, split: '', dividend: '', }, { date: '2012-06-11', open: 26.222435054325263, high: 26.292995719710763, low: 25.41979745259516, close: 25.490359, volume: 46361900, split: '', dividend: '', }, { date: '2012-06-12', open: 25.666762445655092, high: 25.84316542924164, low: 25.43743742036745, close: 25.834347, volume: 35337900, split: '', dividend: '', }, { date: '2012-06-13', open: 25.77260474216896, high: 25.9666507648429, low: 25.62266145140537, close: 25.693223, volume: 32984600, split: '', dividend: '', }, { date: '2012-06-14', open: 25.86962780640763, high: 25.98428944108902, low: 25.472718212731834, close: 25.878448, volume: 39458900, split: '', dividend: '', }, { date: '2012-06-15', open: 26.098952677881414, high: 26.53114216122585, low: 26.010750742504996, close: 26.478221, volume: 62314400, split: '', dividend: '', }, { date: '2012-06-18', open: 26.451759900469167, high: 26.48704155594695, low: 26.204793604240717, close: 26.319457, volume: 58679900, split: '', dividend: '', }, { date: '2012-06-19', open: 26.628164146899994, high: 27.439621921119613, low: 26.504679678088806, close: 27.077994, volume: 75725800, split: '', dividend: '', }, { date: '2012-06-20', open: 27.280858, high: 27.38669943805826, low: 27.025071511126477, close: 27.280858, volume: 36257100, split: '', dividend: '', }, { date: '2012-06-21', open: 27.307317858104042, high: 27.466081339151273, low: 26.513500452867902, close: 26.584062, volume: 48456600, split: '', dividend: '', }, { date: '2012-06-22', open: 26.72518450803979, high: 27.1044536975748, low: 26.487041055731368, close: 27.077994, volume: 45098100, split: '', dividend: '', }, { date: '2012-06-25', open: 26.725184543987197, high: 26.742825812426325, low: 26.125413169752424, close: 26.345918, volume: 42217200, split: '', dividend: '', }, { date: '2012-06-26', open: 26.46058061292472, high: 26.698725838441042, low: 26.407660333718223, close: 26.478221, volume: 38421300, split: '', dividend: '', }, { date: '2012-06-27', open: 26.6281652691589, high: 26.901590387802454, low: 26.487042172042557, close: 26.610524, volume: 33781700, split: '', dividend: '', }, { date: '2012-06-28', open: 26.442939352724846, high: 26.50467982343036, low: 25.949008530926115, close: 26.381198, volume: 45328400, split: '', dividend: '', }, { date: '2012-06-29', open: 26.857490172637203, high: 27.069174817292318, low: 26.584062409252304, close: 26.980972, volume: 55227200, split: '', dividend: '', }, { date: '2012-07-02', open: 27.00743392611076, high: 27.00743392611076, low: 26.645804221246504, close: 26.954511, volume: 30589100, split: '', dividend: '', }, { date: '2012-07-03', open: 26.663444748049415, high: 27.130915, low: 26.610523587451237, close: 27.130915, volume: 20938100, split: '', dividend: '', }, { date: '2012-07-05', open: 26.98097099280225, high: 27.148555545584315, low: 26.795746053624104, close: 27.077994, volume: 28801900, split: '', dividend: '', }, { date: '2012-07-06', open: 26.998612112273996, high: 27.077993850618423, low: 26.41647936441486, close: 26.628164, volume: 38294800, split: '', dividend: '', }, { date: '2012-07-09', open: 26.566423202019333, high: 26.66344444666667, low: 26.266536628686, close: 26.46058, volume: 30680800, split: '', dividend: '', }, { date: '2012-07-10', open: 26.531141573638198, high: 26.654623398411065, low: 26.028390553127107, close: 26.231255, volume: 37534100, split: '', dividend: '', }, { date: '2012-07-11', open: 26.20479393247877, high: 26.23125539492339, low: 25.67558408801195, close: 25.843166, volume: 39184900, split: '', dividend: '', }, { date: '2012-07-12', open: 25.710863942049038, high: 25.737324522435365, low: 25.172833022879708, close: 25.252213, volume: 63523600, split: '', dividend: '', }, { date: '2012-07-13', open: 25.36687668754259, high: 26.001930624087464, low: 25.33159503127074, close: 25.922548, volume: 39085000, split: '', dividend: '', }, { date: '2012-07-16', open: 26.0019298912388, high: 26.046031739830784, low: 25.613842267418743, close: 25.96665, volume: 27900600, split: '', dividend: '', }, { date: '2012-07-17', open: 26.143052730927376, high: 26.337098752552055, low: 25.754965979794132, close: 26.160694, volume: 33771300, split: '', dividend: '', }, { date: '2012-07-18', open: 26.10777267297955, high: 26.85749, low: 25.984289082371784, close: 26.85749, volume: 41090400, split: '', dividend: '', }, { date: '2012-07-19', open: 26.910409906423215, high: 27.1661946315118, low: 26.795746510872743, close: 27.051533, volume: 46663200, split: '', dividend: '', }, { date: '2012-07-20', open: 27.342599125411716, high: 27.386699209723698, low: 26.504679883097516, close: 26.566423, volume: 64021700, split: '', dividend: '', }, { date: '2012-07-23', open: 26.08131172502351, high: 26.090131918369813, low: 25.58738089763044, close: 25.825527, volume: 55151900, split: '', dividend: '', }, { date: '2012-07-24', open: 25.790244738250426, high: 25.896087937936983, low: 25.490358171526584, close: 25.710863, volume: 47723300, split: '', dividend: '', }, { date: '2012-07-25', open: 25.79024593548387, high: 25.869627677419356, low: 25.38451791427742, close: 25.428618, volume: 45579500, split: '', dividend: '', }, { date: '2012-07-26', open: 25.781425353909466, high: 26.019570576131688, low: 25.657942646090536, close: 25.719684, volume: 45301400, split: '', dividend: '', }, { date: '2012-07-27', open: 26.00193058064516, high: 26.32827774193548, low: 25.737324774193542, close: 26.248896, volume: 44242600, split: '', dividend: '', }, { date: '2012-07-30', open: 26.240076011810928, high: 26.301817367132838, low: 25.98428951488652, close: 26.143053, volume: 28905000, split: '', dividend: '', }, { date: '2012-07-31', open: 26.001930075396338, high: 26.204793641048, low: 25.869627174741332, close: 25.993109, volume: 37620900, split: '', dividend: '', }, { date: '2012-08-01', open: 26.098952482488947, high: 26.1518736433186, low: 25.763784248548486, close: 25.940189, volume: 31721800, split: '', dividend: '', }, { date: '2012-08-02', open: 25.763783622475895, high: 26.046031570747292, low: 25.552098984301338, close: 25.746145, volume: 39520500, split: '', dividend: '', }, { date: '2012-08-03', open: 26.046031629918488, high: 26.407659554288237, low: 26.001929781512608, close: 26.240075, volume: 35859400, split: '', dividend: '', }, { date: '2012-08-06', open: 26.460580084788646, high: 26.557603093785538, low: 26.29299552889898, close: 26.41648, volume: 27471800, split: '', dividend: '', }, { date: '2012-08-07', open: 26.522321326834103, high: 26.769286739920684, low: 26.345918342363017, close: 26.689905, volume: 28002900, split: '', dividend: '', }, { date: '2012-08-08', open: 26.645803795527627, high: 26.875128827509165, low: 26.557603624188822, close: 26.751647, volume: 26257600, split: '', dividend: '', }, { date: '2012-08-09', open: 26.8045669901118, high: 27.03389290163934, low: 26.725185249128195, close: 26.90159, volume: 24920800, split: '', dividend: '', }, { date: '2012-08-10', open: 26.901589546351083, high: 27.007432747897038, low: 26.681084714003944, close: 26.831028, volume: 27810300, split: '', dividend: '', }, { date: '2012-08-13', open: 26.769287108235837, high: 26.866308354121138, low: 26.60170343276418, close: 26.804567, volume: 23049100, split: '', dividend: '', }, { date: '2012-08-14', open: 26.902231618019798, high: 26.982139239324397, low: 26.644752392789655, close: 26.751295, volume: 34551400, split: '', dividend: '$0.200', }, { date: '2012-08-15', open: 26.733538382116144, high: 26.884474993674534, low: 26.65362987636987, close: 26.813446, volume: 24351000, split: '', dividend: '', }, { date: '2012-08-16', open: 26.955504772349002, high: 27.470464991486097, low: 26.866717639807746, close: 27.328407, volume: 35787200, split: '', dividend: '', }, { date: '2012-08-17', open: 27.45270624854369, high: 27.45270624854369, low: 27.159711647572816, close: 27.434949, volume: 32589900, split: '', dividend: '', }, { date: '2012-08-20', open: 27.36391999414444, high: 27.390555866948603, low: 27.150833011711125, close: 27.292891, volume: 23737700, split: '', dividend: '', }, { date: '2012-08-21', open: 27.3106493893068, high: 27.48822099422266, low: 27.17747090758552, close: 27.346163, volume: 28822700, split: '', dividend: '', }, { date: '2012-08-22', open: 27.15971223445605, high: 27.31064885033894, low: 27.053167853029215, close: 27.11532, volume: 33437400, split: '', dividend: '', }, { date: '2012-08-23', open: 26.982139231767412, high: 26.991018744216785, low: 26.706902757435554, close: 26.866718, volume: 28355600, split: '', dividend: '', }, { date: '2012-08-24', open: 26.857839524143962, high: 27.195226372452563, low: 26.7956891516914, close: 27.133076, volume: 22943300, split: '', dividend: '', }, { date: '2012-08-27', open: 27.461585096396703, high: 27.488220081566663, low: 27.159711868696256, close: 27.248499, volume: 34691100, split: '', dividend: '', }, { date: '2012-08-28', open: 27.25737814732629, high: 27.346162616746216, low: 27.097562018203135, close: 27.195226, volume: 23947900, split: '', dividend: '', }, { date: '2012-08-29', open: 27.212984, high: 27.301770244698208, low: 27.026533773996217, close: 27.212984, volume: 23346800, split: '', dividend: '', }, { date: '2012-08-30', open: 27.10644099901019, high: 27.177469993733148, low: 26.831201868733874, close: 26.919989, volume: 23982100, split: '', dividend: '', }, { date: '2012-08-31', open: 27.168590266061, high: 27.488219851916938, low: 26.973259644259574, close: 27.36392, volume: 36590100, split: '', dividend: '', }, { date: '2012-09-04', open: 27.035412522788796, high: 27.221862749649976, low: 26.769052899606873, close: 26.982139, volume: 48556700, split: '', dividend: '', }, { date: '2012-09-05', open: 26.831202383318967, high: 27.106441518873986, low: 26.822323758808317, close: 26.982139, volume: 33650000, split: '', dividend: '', }, { date: '2012-09-06', open: 27.0798049122807, high: 27.84336751242386, low: 27.04428952617263, close: 27.834488, volume: 48371700, split: '', dividend: '', }, { date: '2012-09-07', open: 27.559250618419785, high: 27.585885603363955, low: 27.284012378222542, close: 27.479343, volume: 42649100, split: '', dividend: '', }, { date: '2012-09-10', open: 27.3727997588802, high: 27.43495013134603, low: 27.088683770464968, close: 27.275134, volume: 40524000, split: '', dividend: '', }, { date: '2012-09-11', open: 27.248498757349342, high: 27.443827603318365, low: 27.177469763228817, close: 27.337285, volume: 25191800, split: '', dividend: '', }, { date: '2012-09-12', open: 27.470464991486097, high: 27.68355109085279, low: 27.28401298979815, close: 27.328407, volume: 32775800, split: '', dividend: '', }, { date: '2012-09-13', open: 27.426069103538037, high: 27.683550091675823, low: 26.991017408176553, close: 27.470464, volume: 45047300, split: '', dividend: '', }, { date: '2012-09-14', open: 27.532614398994376, high: 27.74570138563606, low: 27.35504102226386, close: 27.710186, volume: 51422800, split: '', dividend: '', }, { date: '2012-09-17', open: 27.69243052683808, high: 27.754580010079465, low: 27.559251160187028, close: 27.710186, volume: 36488500, split: '', dividend: '', }, { date: '2012-09-18', open: 27.612522004490064, high: 27.710185985453787, low: 27.550372521281304, close: 27.683551, volume: 34542700, split: '', dividend: '', }, { date: '2012-09-19', open: 27.60364338562459, high: 27.692430517892387, low: 27.559251151284357, close: 27.568128, volume: 48871900, split: '', dividend: '', }, { date: '2012-09-20', open: 27.479343774051866, high: 27.949909985694436, low: 27.443828388113566, close: 27.923275, volume: 45543000, split: '', dividend: '', }, { date: '2012-09-21', open: 27.90551609472536, high: 28.065332219528628, low: 27.603642869392665, close: 27.69243, volume: 102348900, split: '', dividend: '', }, { date: '2012-09-24', open: 27.523735850430935, high: 27.5858862217061, low: 27.204104481724773, close: 27.328407, volume: 46825900, split: '', dividend: '', }, { date: '2012-09-25', open: 27.47934374832125, high: 27.523735983012042, low: 26.955504902192956, close: 26.982139, volume: 54266400, split: '', dividend: '', }, { date: '2012-09-26', open: 26.884475756937682, high: 27.168590851839575, low: 26.671388769864432, close: 26.78681, volume: 54672000, split: '', dividend: '', }, { date: '2012-09-27', open: 26.78680962433687, high: 26.991017984084877, low: 26.538207255042074, close: 26.777931, volume: 47129900, split: '', dividend: '', }, { date: '2012-09-28', open: 26.795689235887092, high: 26.866718233198924, low: 26.40502975067204, close: 26.422787, volume: 54229300, split: '', dividend: '', }, { date: '2012-10-01', open: 26.467178089078914, high: 26.618115589691424, low: 26.12091263004409, close: 26.183063, volume: 54042700, split: '', dividend: '', }, { date: '2012-10-02', open: 26.351757248819958, high: 26.538207473567095, low: 26.191942009440325, close: 26.334, volume: 43338900, split: '', dividend: '', }, { date: '2012-10-03', open: 26.41390724501315, high: 26.62699422783006, low: 26.342878250740846, close: 26.511573, volume: 46655900, split: '', dividend: '', }, { date: '2012-10-04', open: 26.609236477797324, high: 26.66251, low: 26.254092389141114, close: 26.66251, volume: 43634900, split: '', dividend: '', }, { date: '2012-10-05', open: 26.840081729313233, high: 26.857838978224457, low: 26.40502913098827, close: 26.502694, volume: 41133900, split: '', dividend: '', }, { date: '2012-10-08', open: 26.316241484325538, high: 26.56484385208718, low: 26.236333865786506, close: 26.440544, volume: 29752000, split: '', dividend: '', }, { date: '2012-10-09', open: 26.351757086347096, high: 26.40502883247852, low: 25.90782586858518, close: 25.996613, volume: 45121100, split: '', dividend: '', }, { date: '2012-10-10', open: 25.881189611801243, high: 26.02324671151646, low: 25.703618015191616, close: 25.730253, volume: 47227100, split: '', dividend: '', }, { date: '2012-10-11', open: 25.94333907817074, high: 25.969975838688224, low: 25.63258900625316, close: 25.703618, volume: 41488500, split: '', dividend: '', }, { date: '2012-10-12', open: 25.72137386414528, high: 26.032126604379226, low: 25.57043725013626, close: 25.925584, volume: 46464700, split: '', dividend: '', }, { date: '2012-10-15', open: 26.076521144385666, high: 26.387271227352727, low: 25.969976762114538, close: 26.200821, volume: 42440200, split: '', dividend: '', }, { date: '2012-10-16', open: 26.147549390744764, high: 26.40502860698542, low: 26.032126387249917, close: 26.183063, volume: 47739400, split: '', dividend: '', }, { date: '2012-10-17', open: 26.01436900061338, high: 26.316242234814126, low: 25.827918773234202, close: 26.27185, volume: 44206100, split: '', dividend: '', }, { date: '2012-10-18', open: 26.325121366101694, high: 26.396150361355936, low: 25.97885501423729, close: 26.191942, volume: 59238500, split: '', dividend: '', }, { date: '2012-10-19', open: 25.792403609079038, high: 25.81904037077655, low: 25.30408014329889, close: 25.42838, volume: 90470800, split: '', dividend: '', }, { date: '2012-10-22', open: 25.508287572857142, high: 25.597073815714282, low: 24.709211387142854, close: 24.860148, volume: 83374000, split: '', dividend: '', }, { date: '2012-10-23', open: 24.655940400211776, high: 25.037722144109203, low: 24.647061775652826, close: 24.904541, volume: 64414800, split: '', dividend: '', }, { date: '2012-10-24', open: 25.00220623369176, high: 25.037721619045232, low: 24.744727014744157, close: 24.771362, volume: 53320400, split: '', dividend: '', }, { date: '2012-10-25', open: 25.02884313279939, high: 25.037721757221153, low: 24.735848526881366, close: 24.753604, volume: 54084300, split: '', dividend: '', }, { date: '2012-10-26', open: 24.735848916074012, high: 25.16202200716136, low: 24.718090779088648, close: 25.046599, volume: 57790000, split: '', dividend: '', }, { date: '2012-10-31', open: 25.34847184870123, high: 25.64146645476309, low: 25.30407961443309, close: 25.339595, volume: 69464100, split: '', dividend: '', }, { date: '2012-11-01', open: 25.605953523035232, high: 26.245213610782525, low: 25.58819627371274, close: 26.2097, volume: 72047900, split: '', dividend: '', }, { date: '2012-11-02', open: 26.27184961966102, high: 26.43166485898305, low: 26.041005385084745, close: 26.191942, volume: 57131600, split: '', dividend: '', }, { date: '2012-11-05', open: 26.298486151395515, high: 26.405028755485276, low: 26.041005157981946, close: 26.307363, volume: 38070800, split: '', dividend: '', }, { date: '2012-11-06', open: 26.476057615001416, high: 26.81344622565729, low: 26.289607392899047, close: 26.511573, volume: 43401500, split: '', dividend: '', }, { date: '2012-11-07', open: 26.21857898965062, high: 26.48493683631362, low: 25.792403238685008, close: 25.81904, volume: 57871800, split: '', dividend: '', }, { date: '2012-11-08', open: 25.854556142793232, high: 26.07652176139669, low: 25.570438375255858, close: 25.579317, volume: 49841800, split: '', dividend: '', }, { date: '2012-11-09', open: 25.641466233885748, high: 25.916705364449324, low: 25.579315863438296, close: 25.597074, volume: 43291200, split: '', dividend: '', }, { date: '2012-11-12', open: 25.694740753728514, high: 25.756890238727507, low: 25.046599375305505, close: 25.055478, volume: 61112300, split: '', dividend: '', }, { date: '2012-11-13', open: 24.18717592248062, high: 24.43781933739989, low: 23.94548319490587, close: 24.249837, volume: 131689200, split: '', dividend: '$0.230', }, { date: '2012-11-14', open: 24.38411029359165, high: 24.428869100448843, low: 23.990239775482596, close: 24.026047, volume: 76086100, split: '', dividend: '', }, { date: '2012-11-15', open: 24.061851912043586, high: 24.142416151353416, low: 23.83806235840518, close: 23.864918, volume: 50955600, split: '', dividend: '', }, { date: '2012-11-16', open: 23.873870738687785, high: 23.9007263815836, low: 23.578468513574663, close: 23.739597, volume: 64083300, split: '', dividend: '', }, { date: '2012-11-19', open: 23.990239179664083, high: 23.990239179664083, low: 23.694836969787545, close: 23.927579, volume: 57179300, split: '', dividend: '', }, { date: '2012-11-20', open: 23.95443480772875, high: 23.99024024262689, low: 23.685886437147527, close: 23.909676, volume: 47070400, split: '', dividend: '', }, { date: '2012-11-21', open: 23.909675236208155, high: 24.321448913860895, low: 23.87386980245381, close: 24.124515, volume: 66360300, split: '', dividend: '', }, { date: '2012-11-23', open: 24.375158734470805, high: 24.858544181279992, low: 24.348304882584085, close: 24.795884, volume: 57845700, split: '', dividend: '', }, { date: '2012-11-26', open: 24.652658524682057, high: 24.688463958687986, low: 24.321449084755354, close: 24.518383, volume: 85198700, split: '', dividend: '', }, { date: '2012-11-27', open: 24.491530200918945, high: 24.50943157529967, low: 24.20507956576385, close: 24.240885, volume: 45018600, split: '', dividend: '', }, { date: '2012-11-28', open: 24.178223725211122, high: 24.51838295650903, low: 23.963385750607248, close: 24.49153, volume: 53018400, split: '', dividend: '', }, { date: '2012-11-29', open: 24.26774031565027, high: 24.49152987135381, low: 24.043950759946725, close: 24.124515, volume: 69551400, split: '', dividend: '', }, { date: '2012-11-30', open: 24.21402924894282, high: 24.285641907409662, low: 23.71274153483315, close: 23.829113, volume: 83690200, split: '', dividend: '', }, { date: '2012-12-03', open: 23.972338275407946, high: 24.008143709421113, low: 23.63217725312145, close: 23.659032, volume: 53173800, split: '', dividend: '', }, { date: '2012-12-04', open: 23.72169267266998, high: 23.83806234533996, low: 23.578467358419896, close: 23.605323, volume: 49777500, split: '', dividend: '', }, { date: '2012-12-05', open: 23.614273218077617, high: 24.106611139857517, low: 23.5068551256093, close: 23.87387, volume: 68283800, split: '', dividend: '', }, { date: '2012-12-06', open: 23.99919076178156, high: 24.151368552936777, low: 23.82016090974856, close: 23.927579, volume: 39182300, split: '', dividend: '', }, { date: '2012-12-07', open: 24.00814385971821, high: 24.00814385971821, low: 23.605323549176475, close: 23.685886, volume: 46162100, split: '', dividend: '', }, { date: '2012-12-10', open: 23.775401090907053, high: 24.14241595590279, low: 23.73959565777299, close: 24.115563, volume: 47031100, split: '', dividend: '', }, { date: '2012-12-11', open: 24.21402938119608, high: 24.607899900073203, low: 24.21402938119608, close: 24.455723, volume: 52282800, split: '', dividend: '', }, { date: '2012-12-12', open: 24.643706779886564, high: 24.72427101997467, low: 24.24088468428781, close: 24.38411, volume: 43966300, split: '', dividend: '', }, { date: '2012-12-13', open: 24.45572232918767, high: 24.634753971421837, low: 24.124514686212663, close: 24.26774, volume: 45080100, split: '', dividend: '', }, { date: '2012-12-14', open: 24.26774126769613, high: 24.285642642538257, low: 23.90072638194399, close: 23.999192, volume: 42077500, split: '', dividend: '', }, { date: '2012-12-17', open: 23.981289844235718, high: 24.366206092295645, low: 23.882821543911437, close: 24.258788, volume: 42046100, split: '', dividend: '', }, { date: '2012-12-18', open: 24.393061842999337, high: 24.73322107629394, low: 24.29459354223634, close: 24.67056, volume: 50486900, split: '', dividend: '', }, { date: '2012-12-19', open: 24.786932926536206, high: 24.822738361506346, low: 24.393062399965665, close: 24.446771, volume: 53519900, split: '', dividend: '', }, { date: '2012-12-20', open: 24.491530259320086, high: 24.77798, low: 24.30354613439306, close: 24.77798, volume: 52607300, split: '', dividend: '', }, { date: '2012-12-21', open: 24.572094, high: 24.607899433592003, low: 24.16927190640175, close: 24.572094, volume: 98776500, split: '', dividend: '', }, { date: '2012-12-24', open: 24.348304943506502, high: 24.393061960201845, low: 24.169272400933938, close: 24.222981, volume: 20842400, split: '', dividend: '', }, { date: '2012-12-26', open: 24.19612789939773, high: 24.348304798795464, low: 23.90072568291978, close: 24.043951, volume: 31631100, split: '', dividend: '', }, { date: '2012-12-27', open: 24.07080392386272, high: 24.249836465127466, low: 23.78435418525053, close: 24.133465, volume: 39394000, split: '', dividend: '', }, { date: '2012-12-28', open: 23.909675316129015, high: 24.07975627419044, low: 23.76645, close: 23.76645, volume: 28239900, split: '', dividend: '', }, { date: '2012-12-31', open: 23.802257904989066, high: 23.963386390242846, low: 23.60532398483714, close: 23.909676, volume: 42749500, split: '', dividend: '', }, { date: '2013-01-02', open: 24.393061562524927, high: 24.822737509314358, low: 24.30354574027713, close: 24.724271, volume: 52899300, split: '', dividend: '', }, { date: '2013-01-03', open: 24.733221235483963, high: 24.75112529541284, low: 24.31249775853211, close: 24.393062, volume: 48294400, split: '', dividend: '', }, { date: '2013-01-04', open: 24.4109648605086, high: 24.47362593642483, low: 23.927579417726253, close: 23.936531, volume: 52521100, split: '', dividend: '', }, { date: '2013-01-07', open: 23.96338576308034, high: 24.061852273000145, low: 23.847014298284442, close: 23.891774, volume: 37110400, split: '', dividend: '', }, { date: '2013-01-08', open: 23.945482540319492, high: 23.981289764509974, low: 23.68588575967743, close: 23.76645, volume: 44703100, split: '', dividend: '', }, { date: '2013-01-09', open: 23.91862637380707, high: 23.94548201515049, low: 23.775401060819252, close: 23.900725, volume: 49047900, split: '', dividend: '', }, { date: '2013-01-10', open: 23.85596695978711, high: 24.151369177300424, low: 23.533710890385372, close: 23.685886, volume: 71431300, split: '', dividend: '', }, { date: '2013-01-11', open: 23.712741205739846, high: 24.106610821841226, low: 23.524758875031495, close: 24.017095, volume: 55512100, split: '', dividend: '', }, { date: '2013-01-14', open: 24.07975647749187, high: 24.240884959497393, low: 23.954434324820912, close: 24.070804, volume: 48324400, split: '', dividend: '', }, { date: '2013-01-15', open: 24.01709576137801, high: 24.428869450059697, low: 24.01709576137801, close: 24.357255, volume: 48244500, split: '', dividend: '', }, { date: '2013-01-16', open: 24.303545508374796, high: 24.375158165489715, low: 24.178223358423697, close: 24.205079, volume: 41077400, split: '', dividend: '', }, { date: '2013-01-17', open: 24.33935340084631, high: 24.589995917318827, low: 24.22298103952066, close: 24.393062, volume: 51685900, split: '', dividend: '', }, { date: '2013-01-18', open: 24.258788264220183, high: 24.428869224699522, low: 24.205079665066496, close: 24.393062, volume: 52167700, split: '', dividend: '', }, { date: '2013-01-22', open: 24.4378188396484, high: 24.572094364771495, low: 24.16927226519337, close: 24.303546, volume: 58650600, split: '', dividend: '', }, { date: '2013-01-23', open: 24.348304134987863, high: 24.742171955903984, low: 24.348304134987863, close: 24.715319, volume: 50387700, split: '', dividend: '', }, { date: '2013-01-24', open: 24.79588386641711, high: 25.12709151636234, low: 24.589995683198502, close: 24.733221, volume: 101739300, split: '', dividend: '', }, { date: '2013-01-25', open: 24.688464421393988, high: 25.270317281216546, low: 24.518383459518382, close: 24.957011, volume: 81847700, split: '', dividend: '', }, { date: '2013-01-28', open: 25.07338182228592, high: 25.27031663131494, low: 24.849592266571122, close: 24.983866, volume: 56056500, split: '', dividend: '', }, { date: '2013-01-29', open: 24.903301936451264, high: 25.18080009234623, low: 24.706367126026418, close: 25.073382, volume: 49242600, split: '', dividend: '', }, { date: '2013-01-30', open: 25.073382318491923, high: 25.23451169695357, low: 24.849592758348297, close: 24.930157, volume: 43580500, split: '', dividend: '', }, { date: '2013-01-31', open: 24.87644779437691, high: 25.037574483436487, low: 24.527335193903998, close: 24.572094, volume: 50530000, split: '', dividend: '', }, { date: '2013-02-01', open: 24.769028854278556, high: 25.109188095174723, low: 24.661608968787323, close: 25.00177, volume: 55565900, split: '', dividend: '', }, { date: '2013-02-04', open: 24.948060027517563, high: 25.082332862888745, low: 24.545237940771216, close: 24.563142, volume: 50540000, split: '', dividend: '', }, { date: '2013-02-05', open: 24.724270881340036, high: 24.760076314909092, low: 24.491529744612762, close: 24.616851, volume: 35410400, split: '', dividend: '', }, { date: '2013-02-06', open: 24.50943143402977, high: 24.652658541098244, low: 24.39306175932699, close: 24.473626, volume: 41889600, split: '', dividend: '', }, { date: '2013-02-07', open: 24.48257717989087, high: 24.51838261333213, low: 24.258787626144155, close: 24.419917, volume: 38028300, split: '', dividend: '', }, { date: '2013-02-08', open: 24.482578244376707, high: 24.80483432062524, low: 24.446771019062144, close: 24.661609, volume: 33318500, split: '', dividend: '', }, { date: '2013-02-11', open: 24.751124877920855, high: 24.992817598247754, low: 24.616851144405917, close: 24.939109, volume: 32247700, split: '', dividend: '', }, { date: '2013-02-12', open: 24.957011, high: 25.06443088466395, low: 24.840641323193736, close: 24.957011, volume: 35990900, split: '', dividend: '', }, { date: '2013-02-13', open: 25.001769282134525, high: 25.1628986581658, low: 24.957010475622678, close: 25.091286, volume: 41715600, split: '', dividend: '', }, { date: '2013-02-14', open: 24.99281811580535, high: 25.11813937452292, low: 24.948061098865082, close: 25.100238, volume: 32663200, split: '', dividend: '', }, { date: '2013-02-15', open: 25.100237642034344, high: 25.207655734380577, low: 24.957010535045267, close: 25.073382, volume: 49650900, split: '', dividend: '', }, { date: '2013-02-19', open: 25.163638176348066, high: 25.3531786846053, low: 25.091432612276563, close: 25.317075, volume: 38781400, split: '', dividend: '$0.230', }, { date: '2013-02-20', open: 25.389280275425396, high: 25.452461948408757, low: 25.118510315805153, close: 25.154614, volume: 44110200, split: '', dividend: '', }, { date: '2013-02-21', open: 25.037278379046924, high: 25.037278379046924, low: 24.576967925791198, close: 24.811636, volume: 49078500, split: '', dividend: '', }, { date: '2013-02-22', open: 24.983124438040345, high: 25.05533, low: 24.802610533141213, close: 25.05533, volume: 31425900, split: '', dividend: '', }, { date: '2013-02-25', open: 25.244868914205412, high: 25.317074476784676, low: 24.703329, close: 24.703329, volume: 48011800, split: '', dividend: '', }, { date: '2013-02-26', open: 24.712352890183347, high: 24.91091908984585, low: 24.676251011463247, close: 24.703329, volume: 49923300, split: '', dividend: '', }, { date: '2013-02-27', open: 24.74845678203728, high: 25.271947115136534, low: 24.66722552345291, close: 25.100458, volume: 36394700, split: '', dividend: '', }, { date: '2013-02-28', open: 25.163637562309557, high: 25.2448688199078, low: 25.037278730837365, close: 25.091432, volume: 35840200, split: '', dividend: '', }, { date: '2013-03-01', open: 25.01922620515044, high: 25.253895183044893, low: 24.838713203623854, close: 25.226819, volume: 34849700, split: '', dividend: '', }, { date: '2013-03-04', open: 25.136561143872118, high: 25.407332, low: 25.00117661837769, close: 25.407332, volume: 38157500, split: '', dividend: '', }, { date: '2013-03-05', open: 25.533693729024584, high: 25.759336118795307, low: 25.41635878377425, close: 25.587847, volume: 41432200, split: '', dividend: '', }, { date: '2013-03-06', open: 25.4614854406131, high: 25.479537733713066, low: 25.07338234934774, close: 25.353178, volume: 51448500, split: '', dividend: '', }, { date: '2013-03-07', open: 25.371230718889013, high: 25.524667540972764, low: 25.28097286215256, close: 25.398306, volume: 29196700, split: '', dividend: '', }, { date: '2013-03-08', open: 25.497589383928574, high: 25.569794946785713, low: 25.235843316001894, close: 25.271947, volume: 37667800, split: '', dividend: '', }, { date: '2013-03-11', open: 25.217793867844282, high: 25.24486914892418, low: 24.974099189303942, close: 25.154614, volume: 36627500, split: '', dividend: '', }, { date: '2013-03-12', open: 25.12753613185238, high: 25.226819684368184, low: 24.947021320289643, close: 25.190716, volume: 39255200, split: '', dividend: '', }, { date: '2013-03-13', open: 25.15461342656665, high: 25.289997952005727, low: 25.046304181590255, close: 25.199741, volume: 29093400, split: '', dividend: '', }, { date: '2013-03-14', open: 25.271947166735867, high: 25.41635829340293, low: 25.208767298819026, close: 25.398306, volume: 55914800, split: '', dividend: '', }, { date: '2013-03-15', open: 25.299025304530158, high: 25.41635844306853, low: 25.253895924611413, close: 25.308051, volume: 92710300, split: '', dividend: '', }, { date: '2013-03-18', open: 25.163637799209823, high: 25.524667419295515, low: 25.100457931594164, close: 25.362204, volume: 44809400, split: '', dividend: '', }, { date: '2013-03-19', open: 25.38025672939709, high: 25.470511879545423, low: 25.299025469638394, close: 25.43441, volume: 51901600, split: '', dividend: '', }, { date: '2013-03-20', open: 25.578820390536723, high: 25.714205819562146, low: 25.434409266242938, close: 25.560769, volume: 35447800, split: '', dividend: '', }, { date: '2013-03-21', open: 25.37123, high: 25.59687237902375, low: 25.31707402389527, close: 25.37123, volume: 34233200, split: '', dividend: '', }, { date: '2013-03-22', open: 25.44343573124209, high: 25.57882025699115, low: 25.36220357168142, close: 25.497589, volume: 28720900, split: '', dividend: '', }, { date: '2013-03-25', open: 25.542716831805468, high: 25.5878462109375, low: 25.235843191180468, close: 25.416358, volume: 44154000, split: '', dividend: '', }, { date: '2013-03-26', open: 25.488563562499998, high: 25.578820515624997, low: 25.37123042600703, close: 25.416358, volume: 27824300, split: '', dividend: '', }, { date: '2013-03-27', open: 25.39830520676055, high: 25.678103561078412, low: 25.344151938521257, close: 25.605898, volume: 36047400, split: '', dividend: '', }, { date: '2013-03-28', open: 25.560768935310417, high: 25.86764257365807, low: 25.506614763837305, close: 25.822515, volume: 55453800, split: '', dividend: '', }, { date: '2013-04-01', open: 25.849590280597507, high: 25.86764257365807, low: 25.59687261886202, close: 25.822515, volume: 29201100, split: '', dividend: '', }, { date: '2013-04-02', open: 25.804463298071642, high: 26.039131379830952, low: 25.741283429905675, close: 25.994002, volume: 28456500, split: '', dividend: '', }, { date: '2013-04-03', open: 25.948874114106236, high: 26.129388923556508, low: 25.759335414450995, close: 25.777385, volume: 35062800, split: '', dividend: '', }, { date: '2013-04-04', open: 25.623948492885, high: 25.82251559802409, low: 25.515641049999996, close: 25.813489, volume: 45263200, split: '', dividend: '', }, { date: '2013-04-05', open: 25.47051082737781, high: 25.975951561247193, low: 25.371229985801953, close: 25.903746, volume: 50927300, split: '', dividend: '', }, { date: '2013-04-08', open: 25.930822734872333, high: 25.930822734872333, low: 25.69615375325418, close: 25.804463, volume: 34759500, split: '', dividend: '', }, { date: '2013-04-09', open: 25.93082193141432, high: 26.91462269386617, low: 25.88569345607249, close: 26.725084, volume: 77733800, split: '', dividend: '', }, { date: '2013-04-10', open: 26.688980737484126, high: 27.36590787827253, low: 26.643852261431565, close: 27.329806, volume: 71116700, split: '', dividend: '', }, { date: '2013-04-11', open: 26.264773221673355, high: 26.355031076894676, low: 25.930822496861698, close: 26.120363, volume: 130923200, split: '', dividend: '', }, { date: '2013-04-12', open: 26.039131269915554, high: 26.19256809195665, low: 25.86764305704609, close: 25.984978, volume: 62886300, split: '', dividend: '', }, { date: '2013-04-15', open: 25.858616317231917, high: 26.15646425387019, low: 25.732256586536895, close: 25.89472, volume: 56332900, split: '', dividend: '', }, { date: '2013-04-16', open: 26.084260033975145, high: 26.300875823729264, low: 25.90374702627497, close: 26.147439, volume: 52797300, split: '', dividend: '', }, { date: '2013-04-17', open: 26.03913139091225, high: 26.21062050714811, low: 25.813489004509197, close: 26.02108, volume: 52840700, split: '', dividend: '', }, { date: '2013-04-18', open: 26.12938912662692, high: 26.15646531029992, low: 25.72323193041918, close: 25.984978, volume: 56906600, split: '', dividend: '', }, { date: '2013-04-19', open: 26.734110472606485, high: 27.29370268055089, low: 26.725084777275615, close: 26.869495, volume: 99790700, split: '', dividend: '', }, { date: '2013-04-22', open: 27.34785623982423, high: 28.14211833992864, low: 27.320780056114174, close: 27.826219, volume: 137904000, split: '', dividend: '', }, { date: '2013-04-23', open: 27.70888585681791, high: 27.889398862745097, low: 27.420061798084056, close: 27.618628, volume: 59126900, split: '', dividend: '', }, { date: '2013-04-24', open: 27.63668061226728, high: 28.810020128463474, low: 27.618628318639797, close: 28.665609, volume: 90946600, split: '', dividend: '', }, { date: '2013-04-25', open: 28.620478207935218, high: 29.64038265496611, low: 28.46704319665084, close: 28.828071, volume: 110700200, split: '', dividend: '', }, { date: '2013-04-26', open: 28.79196774482643, high: 28.864173306568944, low: 28.385812362594326, close: 28.692686, volume: 47799300, split: '', dividend: '', }, { date: '2013-04-29', open: 28.701709882413287, high: 29.495971963938306, low: 28.674633699336596, close: 29.432793, volume: 59116400, split: '', dividend: '', }, { date: '2013-04-30', open: 29.387665155600615, high: 29.88407840311803, low: 29.13494387899812, close: 29.87505, volume: 75165200, split: '', dividend: '', }, { date: '2013-05-01', open: 29.721614699217152, high: 29.85700193420385, low: 29.423764948413293, close: 29.532076, volume: 54330900, split: '', dividend: '', }, { date: '2013-05-02', open: 29.450845045512853, high: 29.938229890277086, low: 29.234226550385834, close: 29.929206, volume: 46059500, split: '', dividend: '', }, { date: '2013-05-03', open: 29.99238511989339, high: 30.254130280434147, low: 29.85700149716653, close: 30.227055, volume: 46784600, split: '', dividend: '', }, { date: '2013-05-06', open: 30.163872246416474, high: 30.606133126518518, low: 30.01043722962963, close: 30.461722, volume: 40978300, split: '', dividend: '', }, { date: '2013-05-07', open: 30.371466543311843, high: 30.497825375165615, low: 30.001413035357878, close: 30.064592, volume: 43078300, split: '', dividend: '', }, { date: '2013-05-08', open: 29.847973755806386, high: 30.001412377954388, low: 29.468896365982037, close: 29.77577, volume: 51595700, split: '', dividend: '', }, { date: '2013-05-09', open: 29.649407406434722, high: 29.784794641763625, low: 29.414741132578083, close: 29.477921, volume: 46417800, split: '', dividend: '', }, { date: '2013-05-10', open: 29.48694470685074, high: 29.532075891008656, low: 29.171047176844514, close: 29.504997, volume: 36394900, split: '', dividend: '', }, { date: '2013-05-13', open: 29.432793598385242, high: 29.847974684165145, low: 29.37863762085276, close: 29.811871, volume: 36027600, split: '', dividend: '', }, { date: '2013-05-14', open: 29.86640679874064, high: 30.475367, low: 29.811871068789266, close: 30.475367, volume: 56870100, split: '', dividend: '$0.230', }, { date: '2013-05-15', open: 30.402657307873817, high: 30.811662562946918, low: 30.38447842803418, close: 30.766214, volume: 46303900, split: '', dividend: '', }, { date: '2013-05-16', open: 30.57534591649191, high: 31.03888689767471, low: 30.493545048052987, close: 30.975264, volume: 59382900, split: '', dividend: '', }, { date: '2013-05-17', open: 31.0207079020361, high: 31.693291, low: 30.99343821929613, close: 31.693291, volume: 60666700, split: '', dividend: '', }, { date: '2013-05-20', open: 31.56604569919922, high: 31.902336334863264, low: 31.52060077305583, close: 31.884162, volume: 54020800, split: '', dividend: '', }, { date: '2013-05-21', open: 31.90233663283929, high: 32.056851200967074, low: 31.556957917619165, close: 31.675112, volume: 48702400, split: '', dividend: '', }, { date: '2013-05-22', open: 31.62058073523254, high: 31.6660247527875, low: 31.229754367732582, close: 31.456979, volume: 66047500, split: '', dividend: '', }, { date: '2013-05-23', open: 31.111596063156888, high: 31.402442678074046, low: 30.81166137201901, close: 31.038886, volume: 51102700, split: '', dividend: '', }, { date: '2013-05-24', open: 30.82983669285363, high: 31.157041076511433, low: 30.811662357627835, close: 31.147953, volume: 33174400, split: '', dividend: '', }, { date: '2013-05-28', open: 31.284286055989313, high: 31.975050766990286, low: 31.275198888349504, close: 31.829627, volume: 48212100, split: '', dividend: '', }, { date: '2013-05-29', open: 31.575137113234714, high: 31.829626886765286, low: 31.420622543560135, close: 31.702382, volume: 38412200, split: '', dividend: '', }, { date: '2013-05-30', open: 31.67511135876915, high: 32.03867258174915, low: 31.638758144946394, close: 31.838714, volume: 51131000, split: '', dividend: '', }, { date: '2013-05-31', open: 31.64784630098302, high: 32.06593870909921, low: 31.62058025442405, close: 31.72056, volume: 56165700, split: '', dividend: '', }, { date: '2013-06-03', open: 31.738735161129583, high: 32.384055850455155, low: 31.656937928221353, close: 32.347699, volume: 51252600, split: '', dividend: '', }, { date: '2013-06-04', open: 32.37496434031753, high: 32.48403488930129, low: 31.602401508007926, close: 31.802361, volume: 65529500, split: '', dividend: '', }, { date: '2013-06-05', open: 31.447887355517754, high: 31.71146883841228, low: 31.293376423041305, close: 31.61149, volume: 46025100, split: '', dividend: '', }, { date: '2013-06-06', open: 31.66602508426845, high: 31.91142859858468, low: 31.347912413561104, close: 31.775092, volume: 37618500, split: '', dividend: '', }, { date: '2013-06-07', open: 32.038673432221664, high: 32.520388747977805, low: 31.865983619074186, close: 32.420409, volume: 40757300, split: '', dividend: '', }, { date: '2013-06-10', open: 32.274985214766026, high: 32.40223464547588, low: 31.938693665144473, close: 32.238632, volume: 35994500, split: '', dividend: '', }, { date: '2013-06-11', open: 31.85689278369618, high: 31.97505050229621, low: 31.520601234213544, close: 31.666025, volume: 39435900, split: '', dividend: '', }, { date: '2013-06-12', open: 31.93869388710146, high: 32.056851606571435, low: 31.675112400774342, close: 31.811449, volume: 37372700, split: '', dividend: '', }, { date: '2013-06-13', open: 31.8023615130056, high: 31.829626651220433, low: 31.438800281716585, close: 31.556958, volume: 45654900, split: '', dividend: '', }, { date: '2013-06-14', open: 31.4024430517152, high: 31.529688844898583, low: 31.129774403792183, close: 31.266111, volume: 53192600, split: '', dividend: '', }, { date: '2013-06-17', open: 31.529689542815746, high: 31.956872766857142, low: 31.4751574480414, close: 31.811449, volume: 49670100, split: '', dividend: '', }, { date: '2013-06-18', open: 31.78418192376416, high: 31.965958899755865, low: 31.720559936722132, close: 31.79327, volume: 28616500, split: '', dividend: '', }, { date: '2013-06-19', open: 31.775091545568085, high: 31.893249262792715, low: 31.4388, close: 31.4388, volume: 30816200, split: '', dividend: '', }, { date: '2013-06-20', open: 31.138861218998258, high: 31.202487750464332, low: 30.329942451809558, close: 30.439013, volume: 54493700, split: '', dividend: '', }, { date: '2013-06-21', open: 30.593525437330925, high: 30.657148336339038, low: 30.039096408504506, close: 30.239055, volume: 85338500, split: '', dividend: '', }, { date: '2013-06-24', open: 29.939117313547527, high: 31.08433130378792, low: 29.602825759109557, close: 30.64806, volume: 56109000, split: '', dividend: '', }, { date: '2013-06-25', open: 30.975264036701873, high: 31.24793268958947, low: 30.41174421564825, close: 30.602612, volume: 44073400, split: '', dividend: '', }, { date: '2013-06-26', open: 31.011617244907466, high: 31.338821627878986, low: 30.8025705809164, close: 31.220663, volume: 48665900, split: '', dividend: '', }, { date: '2013-06-27', open: 31.37517705647537, high: 31.611489763877056, low: 31.356999085990733, close: 31.466066, volume: 28993100, split: '', dividend: '', }, { date: '2013-06-28', open: 31.24793223582582, high: 31.62058063152216, low: 31.211575385883755, close: 31.393356, volume: 65545500, split: '', dividend: '', }, { date: '2013-07-01', open: 31.584223513264742, high: 31.802360975469934, low: 31.2024879533475, close: 31.229754, volume: 31055400, split: '', dividend: '', }, { date: '2013-07-02', open: 31.2751982152386, high: 31.302464262063914, low: 30.56625827531742, close: 30.848015, volume: 37630000, split: '', dividend: '', }, { date: '2013-07-03', open: 30.59352532393562, high: 31.23884238829893, low: 30.538989592905118, close: 30.911638, volume: 15994400, split: '', dividend: '', }, { date: '2013-07-05', open: 30.98435108460541, high: 31.12068768276888, low: 30.520814649156694, close: 31.093418, volume: 26085900, split: '', dividend: '', }, { date: '2013-07-08', open: 31.220662334800444, high: 31.438799797331793, low: 30.88437170029876, close: 31.202488, volume: 32396900, split: '', dividend: '', }, { date: '2013-07-09', open: 31.429713299585227, high: 31.447887634772904, low: 31.0297952156893, close: 31.220663, volume: 25318500, split: '', dividend: '', }, { date: '2013-07-10', open: 31.211575619262955, high: 31.638758838617324, low: 31.193397648605256, close: 31.53878, volume: 29658800, split: '', dividend: '', }, { date: '2013-07-11', open: 31.811447935316554, high: 32.51129978989352, low: 31.72055990186982, close: 32.438587, volume: 53638300, split: '', dividend: '', }, { date: '2013-07-12', open: 32.33861176725656, high: 32.47494473002213, low: 32.065939479435656, close: 32.420409, volume: 35501200, split: '', dividend: '', }, { date: '2013-07-15', open: 32.411321567670534, high: 32.92030565317859, low: 32.3386115030948, close: 32.874858, volume: 34142600, split: '', dividend: '', }, { date: '2013-07-16', open: 32.72943456213124, high: 33.11117376619796, low: 32.683990544092914, close: 32.96575, volume: 36378500, split: '', dividend: '', }, { date: '2013-07-17', open: 33.0293722955024, high: 33.07481631271216, low: 32.25681036945857, close: 32.484035, volume: 37285100, split: '', dividend: '', }, { date: '2013-07-18', open: 32.46585640624208, high: 32.62036733857236, low: 32.01140714116169, close: 32.211363, volume: 49547100, split: '', dividend: '', }, { date: '2013-07-19', open: 29.44831435282892, high: 29.693713321693377, low: 28.194032556687898, close: 28.539414, volume: 248428500, split: '', dividend: '', }, { date: '2013-07-22', open: 28.812084183630375, high: 29.09384, low: 28.721193422130174, close: 29.09384, volume: 79040700, split: '', dividend: '', }, { date: '2013-07-23', open: 29.002951867064738, high: 29.12110958394566, low: 28.821171253577905, close: 28.921151, volume: 65810400, split: '', dividend: '', }, { date: '2013-07-24', open: 29.121109699921952, high: 29.257442661109096, low: 28.98477310314071, close: 29.048396, volume: 52803100, split: '', dividend: '', }, { date: '2013-07-25', open: 28.73937248007953, high: 28.766638527130887, low: 28.403079114465726, close: 28.530324, volume: 63213000, split: '', dividend: '', }, { date: '2013-07-26', open: 28.412167625168642, high: 28.739372, low: 28.366721790446118, close: 28.739372, volume: 38633600, split: '', dividend: '', }, { date: '2013-07-29', open: 28.603036284093303, high: 28.721194003766836, low: 28.539414294882235, close: 28.666661, volume: 28870700, split: '', dividend: '', }, { date: '2013-07-30', open: 28.884797009997456, high: 29.193820701148542, low: 28.67574852438245, close: 28.948419, volume: 45799500, split: '', dividend: '', }, { date: '2013-07-31', open: 29.057484899204493, high: 29.13019678111404, low: 28.821171282998463, close: 28.939329, volume: 43898400, split: '', dividend: '', }, { date: '2013-08-01', open: 29.139288342431858, high: 29.166554389958954, low: 28.721194101673508, close: 28.784817, volume: 42557900, split: '', dividend: '', }, { date: '2013-08-02', open: 28.80299610686015, high: 28.993863894445404, low: 28.69392737139942, close: 28.984774, volume: 29199900, split: '', dividend: '', }, { date: '2013-08-05', open: 28.99386279514628, high: 29.084752647168685, low: 28.757548270989517, close: 28.812084, volume: 30984000, split: '', dividend: '', }, { date: '2013-08-06', open: 28.675748134800003, high: 28.784816868904375, low: 28.521235382425083, close: 28.703016, volume: 36331500, split: '', dividend: '', }, { date: '2013-08-07', open: 28.66666076084302, high: 29.175641214778, low: 28.403079276260783, close: 29.139288, volume: 38078600, split: '', dividend: '', }, { date: '2013-08-08', open: 29.302890677112632, high: 30.05727464570613, low: 29.13019722823123, close: 29.893672, volume: 59034400, split: '', dividend: '', }, { date: '2013-08-09', open: 29.784604988544196, high: 29.90276361587775, low: 29.511936336973907, close: 29.720983, volume: 26800700, split: '', dividend: '', }, { date: '2013-08-12', open: 29.502845599858585, high: 29.966385671490105, low: 29.502845599858585, close: 29.875494, volume: 25493700, split: '', dividend: '', }, { date: '2013-08-13', open: 29.756504055524612, high: 29.793117097417905, low: 29.481914021401774, close: 29.500221, volume: 39464100, split: '', dividend: '$0.230', }, { date: '2013-08-14', open: 29.41784324777838, high: 30.534514956385962, low: 29.01511167976134, close: 29.610056, volume: 48519600, split: '', dividend: '', }, { date: '2013-08-15', open: 29.289701752447254, high: 29.45445632480477, low: 28.22795006392104, close: 29.097489, volume: 33338000, split: '', dividend: '', }, { date: '2013-08-16', open: 29.097488798871975, high: 29.28054851825624, low: 28.978498471021965, close: 29.10664, volume: 32866300, split: '', dividend: '', }, { date: '2013-08-19', open: 29.070029093024186, high: 29.262241846157245, low: 28.722212968169703, close: 28.731366, volume: 27902500, split: '', dividend: '', }, { date: '2013-08-20', open: 28.777132430257893, high: 29.198170970962337, low: 28.713061208691517, close: 28.941887, volume: 22979600, split: '', dividend: '', }, { date: '2013-08-21', open: 28.932734, high: 29.298852520584614, low: 28.868662778363532, close: 28.932734, volume: 37409100, split: '', dividend: '', }, { date: '2013-08-22', open: 29.463608364524212, high: 29.738202063647417, low: 29.381230163256934, close: 29.646669, volume: 31169900, split: '', dividend: '', }, { date: '2013-08-23', open: 32.19121049889007, high: 32.2186723397636, low: 31.12030762589928, close: 31.806785, volume: 225493800, split: '', dividend: '', }, { date: '2013-08-26', open: 31.486430791869648, high: 31.733558985876194, low: 31.147765873993066, close: 31.257605, volume: 72786800, split: '', dividend: '', }, { date: '2013-08-27', open: 30.68096265790515, high: 31.21183667281146, low: 30.3423023112017, close: 30.442982, volume: 58522300, split: '', dividend: '', }, { date: '2013-08-28', open: 30.561972261256482, high: 30.754185013730403, low: 30.20500493640218, close: 30.223311, volume: 44257400, split: '', dividend: '', }, { date: '2013-08-29', open: 30.14093393952113, high: 30.754185244034076, low: 30.02194360994106, close: 30.708421, volume: 45284700, split: '', dividend: '', }, { date: '2013-08-30', open: 30.543665159627025, high: 30.64434942129644, low: 30.28738119327059, close: 30.571127, volume: 42790200, split: '', dividend: '', }, { date: '2013-09-03', open: 29.060875503791575, high: 29.353772516743174, low: 28.639836962976815, close: 29.179864, volume: 154507000, split: '', dividend: '', }, { date: '2013-09-04', open: 28.731365772794042, high: 28.804590026857372, low: 28.475082714178757, close: 28.55746, volume: 142320600, split: '', dividend: '', }, { date: '2013-09-05', open: 28.46592858789625, high: 28.777132581009216, low: 28.3286340276951, close: 28.584918, volume: 71644900, split: '', dividend: '', }, { date: '2013-09-06', open: 28.658140588067646, high: 28.731364839753034, low: 28.49338602177551, close: 28.511693, volume: 75434900, split: '', dividend: '', }, { date: '2013-09-09', open: 28.575763694930572, high: 29.097488325915915, low: 28.557459462365696, close: 28.978498, volume: 49628500, split: '', dividend: '', }, { date: '2013-09-10', open: 29.198171358387505, high: 29.655824777683318, low: 29.097488924179, close: 29.646669, volume: 56881200, split: '', dividend: '', }, { date: '2013-09-11', open: 29.811423633694346, high: 30.140932768116514, low: 29.774810592344284, close: 29.967027, volume: 39087500, split: '', dividend: '', }, { date: '2013-09-12', open: 29.94871992505289, high: 30.003636283339773, low: 29.82972960048118, close: 29.921259, volume: 32860200, split: '', dividend: '', }, { date: '2013-09-13', open: 29.99448508944854, high: 30.26907604235774, low: 29.756504432987544, close: 30.232463, volume: 40899000, split: '', dividend: '', }, { date: '2013-09-16', open: 30.55282066813304, high: 30.66265613300781, low: 29.957872693532703, close: 30.021943, volume: 52839700, split: '', dividend: '', }, { date: '2013-09-17', open: 30.58943073179873, high: 30.63519863713739, low: 30.113476734948925, close: 30.140934, volume: 84716500, split: '', dividend: '', }, { date: '2013-09-18', open: 30.195852790690335, high: 30.571127082707143, low: 30.049404286488656, close: 30.497901, volume: 64099900, split: '', dividend: '', }, { date: '2013-09-19', open: 30.644350406788057, high: 30.827411042431955, low: 30.497901898272943, close: 30.790798, volume: 42026600, split: '', dividend: '', }, { date: '2013-09-20', open: 30.580279052751475, high: 30.644350274951197, low: 29.921259851965484, close: 30.012792, volume: 102904900, split: '', dividend: '', }, { date: '2013-09-23', open: 29.783965454462315, high: 30.177545809466576, low: 29.74735241311226, close: 29.967027, volume: 39826100, split: '', dividend: '', }, { date: '2013-09-24', open: 30.086014503617765, high: 30.086014503617765, low: 29.426998962285946, close: 29.701589, volume: 40685000, split: '', dividend: '', }, { date: '2013-09-25', open: 29.738202597336667, high: 30.021943841506694, low: 29.655825309894205, close: 29.756505, volume: 28907500, split: '', dividend: '', }, { date: '2013-09-26', open: 29.875494672124354, high: 30.205004729935915, low: 29.82973042874581, close: 29.994485, volume: 28504000, split: '', dividend: '', }, { date: '2013-09-27', open: 30.09516967274232, high: 30.89148252930568, low: 30.08601481027541, close: 30.452137, volume: 55348000, split: '', dividend: '', }, { date: '2013-09-30', open: 30.205004032602286, high: 30.488748925181397, low: 29.93041400215451, close: 30.461288, volume: 39839500, split: '', dividend: '', }, { date: '2013-10-01', open: 30.525358611599724, high: 30.763340179547395, low: 30.479594368818617, close: 30.735882, volume: 36718700, split: '', dividend: '', }, { date: '2013-10-02', open: 30.534514965687258, high: 31.14776626498969, low: 30.470443743159475, close: 31.047082, volume: 46946800, split: '', dividend: '', }, { date: '2013-10-03', open: 31.010472063251452, high: 31.12030752745696, low: 30.58942986255872, close: 30.992166, volume: 38703800, split: '', dividend: '', }, { date: '2013-10-04', open: 30.836562568859666, high: 31.111156262980746, low: 30.77249134761029, close: 31.010472, volume: 33008100, split: '', dividend: '', }, { date: '2013-10-07', open: 30.75418503888874, high: 30.854869304062294, low: 30.388066512542387, close: 30.479595, volume: 35069300, split: '', dividend: '', }, { date: '2013-10-08', open: 30.488749698626332, high: 30.507056677443963, low: 30.021943248401403, close: 30.214156, volume: 41017600, split: '', dividend: '', }, { date: '2013-10-09', open: 30.269076, high: 30.525359058416928, low: 30.168391735437677, close: 30.269076, volume: 35878600, split: '', dividend: '', }, { date: '2013-10-10', open: 30.48875030859403, high: 31.019624330527684, low: 30.442982402982725, close: 30.900634, volume: 42875100, split: '', dividend: '', }, { date: '2013-10-11', open: 30.82741065961293, high: 31.2484492010622, low: 30.82741065961293, close: 31.239298, volume: 30033300, split: '', dividend: '', }, { date: '2013-10-14', open: 31.02877917374778, high: 31.577959243020054, low: 30.918940047862552, close: 31.532195, volume: 27757900, split: '', dividend: '', }, { date: '2013-10-15', open: 31.73355890853193, high: 32.02645958262386, low: 31.550501021391888, close: 31.568808, volume: 47097800, split: '', dividend: '', }, { date: '2013-10-16', open: 31.66948795777384, high: 31.944082570908904, low: 31.632878576760376, close: 31.706101, volume: 35111600, split: '', dividend: '', }, { date: '2013-10-17', open: 31.532195254203195, high: 32.02645988338172, low: 31.458969169689386, close: 31.962385, volume: 31359200, split: '', dividend: '', }, { date: '2013-10-18', open: 31.870856471134335, high: 32.026459841088545, low: 31.42235974714976, close: 31.998998, volume: 41811700, split: '', dividend: '', }, { date: '2013-10-21', open: 32.01730413790774, high: 32.218671745902135, low: 31.953232917505982, close: 32.026459, volume: 27433500, split: '', dividend: '', }, { date: '2013-10-22', open: 32.05391655847794, high: 32.12713898043239, low: 31.59626497997311, close: 31.651185, volume: 40438500, split: '', dividend: '', }, { date: '2013-10-23', open: 31.44066288448038, high: 31.568808992857992, low: 30.818256712536886, close: 30.900634, volume: 58600500, split: '', dividend: '', }, { date: '2013-10-24', open: 30.9555534022671, high: 31.21183646085752, low: 30.72672760834141, close: 30.864024, volume: 53209700, split: '', dividend: '', }, { date: '2013-10-25', open: 32.84107838969443, high: 33.21635268636389, low: 32.465804093024964, close: 32.703782, volume: 113494000, split: '', dividend: '', }, { date: '2013-10-28', open: 32.59394704237655, high: 32.7037825082935, low: 32.282743046949676, close: 32.557334, volume: 38383600, split: '', dividend: '', }, { date: '2013-10-29', open: 32.61225326524687, high: 32.694630551564444, low: 32.273588342254, close: 32.511569, volume: 31702200, split: '', dividend: '', }, { date: '2013-10-30', open: 32.52072113757465, high: 32.7587017954748, low: 32.42919173468791, close: 32.529876, volume: 36997700, split: '', dividend: '', }, { date: '2013-10-31', open: 32.639710790737084, high: 32.66716897032237, low: 32.34681377859363, close: 32.410885, volume: 41682300, split: '', dividend: '', }, { date: '2013-11-01', open: 32.64886252962062, high: 32.66716950848434, low: 32.39257855507621, close: 32.520721, volume: 40264600, split: '', dividend: '', }, { date: '2013-11-04', open: 32.57563980594435, high: 32.93260804208703, low: 32.53902676385731, close: 32.895995, volume: 28060700, split: '', dividend: '', }, { date: '2013-11-05', open: 32.75870211832451, high: 33.60077922336439, low: 32.740395139202924, close: 33.536708, volume: 51681900, split: '', dividend: '', }, { date: '2013-11-06', open: 34.085891851559715, high: 34.98288804207112, low: 33.92113636580081, close: 34.946275, volume: 88948800, split: '', dividend: '', }, { date: '2013-11-07', open: 34.74490754443016, high: 34.790671787793656, low: 34.259797777866666, close: 34.323869, volume: 60437400, split: '', dividend: '', }, { date: '2013-11-08', open: 34.47946873555222, high: 34.580153, low: 34.177420518724745, close: 34.580153, volume: 36737800, split: '', dividend: '', }, { date: '2013-11-11', open: 34.49777540127039, high: 34.58015268618659, low: 34.18657140961713, close: 34.406246, volume: 26872500, split: '', dividend: '', }, { date: '2013-11-12', open: 34.21403306321451, high: 34.41539701266459, low: 34.04927849428395, close: 34.195727, volume: 31651600, split: '', dividend: '', }, { date: '2013-11-13', open: 33.84791125838574, high: 34.927969, low: 33.774688835323325, close: 34.927969, volume: 44957600, split: '', dividend: '', }, { date: '2013-11-14', open: 34.66253060731649, high: 34.900511265382086, low: 34.52523696054253, close: 34.799827, volume: 46183700, split: '', dividend: '', }, { date: '2013-11-15', open: 34.73575626414038, high: 34.79982657082453, low: 34.52523653475349, close: 34.635072, volume: 50601300, split: '', dividend: '', }, { date: '2013-11-18', open: 34.18657173023845, high: 34.3970951215447, low: 33.93028867203525, close: 34.049279, volume: 53277500, split: '', dividend: '', }, { date: '2013-11-19', open: 33.98471823663619, high: 34.335173096887694, low: 33.81871417653843, close: 33.883275, volume: 44275000, split: '', dividend: '$0.280', }, { date: '2013-11-20', open: 34.049275147458836, high: 34.501176930357225, low: 33.99394322785737, close: 34.196838, volume: 32229900, split: '', dividend: '', }, { date: '2013-11-21', open: 34.37206233625335, high: 34.611845052522824, low: 34.362838044128665, close: 34.491956, volume: 23064700, split: '', dividend: '', }, { date: '2013-11-22', open: 34.6118452869647, high: 34.75018292467394, low: 34.427399099746395, close: 34.648736, volume: 27982000, split: '', dividend: '', }, { date: '2013-11-25', open: 34.980744911550076, high: 34.999190729609026, low: 34.648736786895235, close: 34.713293, volume: 30646800, split: '', dividend: '', }, { date: '2013-11-26', open: 34.64873669792432, high: 34.72251812548108, low: 34.445841, close: 34.445841, volume: 34465300, split: '', dividend: '', }, { date: '2013-11-27', open: 34.648737500198806, high: 34.823962169551024, low: 34.574959759912915, close: 34.676403, volume: 26002100, split: '', dividend: '', }, { date: '2013-11-29', open: 34.87929819094418, high: 35.31275416738633, low: 34.87929819094418, close: 35.165195, volume: 22090400, split: '', dividend: '', }, { date: '2013-12-02', open: 35.12830395427038, high: 35.76465193537152, low: 35.100637532891426, close: 35.460313, volume: 42950400, split: '', dividend: '', }, { date: '2013-12-03', open: 35.17441554044337, high: 35.49720398525695, low: 35.11908361950703, close: 35.331199, volume: 52109800, split: '', dividend: '', }, { date: '2013-12-04', open: 35.23897230705602, high: 35.949101713638974, low: 35.15597027641908, close: 35.912211, volume: 51983600, split: '', dividend: '', }, { date: '2013-12-05', open: 35.82920822656305, high: 35.85687833698163, low: 34.28906127263158, close: 35.045302, volume: 116305000, split: '', dividend: '', }, { date: '2013-12-06', open: 35.4326429205614, high: 35.55253566528033, low: 35.03608135058761, close: 35.377311, volume: 36457300, split: '', dividend: '', }, { date: '2013-12-09', open: 35.56176012559687, high: 35.847654166818884, low: 35.386531770509855, close: 35.700095, volume: 30286000, split: '', dividend: '', }, { date: '2013-12-10', open: 35.607871383596894, high: 35.87532328832786, low: 35.0637460487078, close: 35.146749, volume: 37828600, split: '', dividend: '', }, { date: '2013-12-11', open: 35.1006371498269, high: 35.321974051911695, low: 34.48273130448396, close: 34.685627, volume: 39853400, split: '', dividend: '', }, { date: '2013-12-12', open: 34.71329296724221, high: 34.71329296724221, low: 34.289061286161704, close: 34.325952, volume: 36012800, split: '', dividend: '', }, { date: '2013-12-13', open: 34.51039776604191, high: 34.53806787613049, low: 33.772602865506755, close: 33.83716, volume: 40066100, split: '', dividend: '', }, { date: '2013-12-16', open: 33.87405175315944, high: 34.123057850990996, low: 33.698826162115374, close: 34.02161, volume: 31734200, split: '', dividend: '', }, { date: '2013-12-17', open: 34.06772189265114, high: 34.22450535269386, low: 33.50515533298904, close: 33.68038, volume: 45687700, split: '', dividend: '', }, { date: '2013-12-18', open: 33.53282122553508, high: 33.75415720667724, low: 32.767356211305945, close: 33.735716, volume: 63192100, split: '', dividend: '', }, { date: '2013-12-19', open: 33.6711558035104, high: 33.7080465177552, low: 33.2745942284896, close: 33.431374, volume: 34160100, split: '', dividend: '', }, { date: '2013-12-20', open: 33.38526197619209, high: 34.058499743709234, low: 33.37603768389757, close: 33.938607, volume: 62649100, split: '', dividend: '', }, { date: '2013-12-23', open: 33.947831353097605, high: 34.02160909117985, low: 33.708045865249666, close: 33.772603, volume: 25128700, split: '', dividend: '', }, { date: '2013-12-24', open: 33.864828959740564, high: 34.279836340524575, low: 33.791047533469985, close: 34.196838, volume: 14243000, split: '', dividend: '', }, { date: '2013-12-26', open: 34.30750709498801, high: 34.57495900621386, low: 34.27983698456595, close: 34.528844, volume: 17612800, split: '', dividend: '', }, { date: '2013-12-27', open: 34.65796091024557, high: 34.6948479349596, low: 34.2798368589205, close: 34.390509, volume: 14563000, split: '', dividend: '', }, { date: '2013-12-30', open: 34.32595186496534, high: 34.47351103075887, low: 34.03083445562306, close: 34.390509, volume: 16290500, split: '', dividend: '', }, { date: '2013-12-31', open: 34.4919563967483, high: 34.65796045609073, low: 34.32595141516111, close: 34.501177, volume: 17503500, split: '', dividend: '', }, { date: '2014-01-02', open: 34.44584066358364, high: 34.49195659152939, low: 34.21527946875048, close: 34.270616, volume: 30632200, split: '', dividend: '', }, { date: '2014-01-03', open: 34.30750690978204, high: 34.32595180547427, low: 33.754157272280956, close: 34.040055, volume: 31134800, split: '', dividend: '', }, { date: '2014-01-06', open: 33.98471847838005, high: 34.02160919202009, low: 33.30226010430237, close: 33.320705, volume: 43603700, split: '', dividend: '', }, { date: '2014-01-07', open: 33.50515526085872, high: 33.65271442812046, low: 33.39448311867803, close: 33.578933, volume: 35802800, split: '', dividend: '', }, { date: '2014-01-08', open: 33.20081259512375, high: 33.32992594408221, low: 32.81347162600356, close: 32.979472, volume: 59971700, split: '', dividend: '', }, { date: '2014-01-09', open: 33.09014351639458, high: 33.11780993745595, low: 32.64746694574104, close: 32.767356, volume: 36516300, split: '', dividend: '', }, { date: '2014-01-10', open: 33.108589652242415, high: 33.33915084867523, low: 32.97025108989315, close: 33.237703, volume: 40548800, split: '', dividend: '', }, { date: '2014-01-13', open: 33.191591060613035, high: 33.21925655917668, low: 32.12178712922368, close: 32.260122, volume: 45901900, split: '', dividend: '', }, { date: '2014-01-14', open: 32.02956091222921, high: 33.09014332163389, low: 31.93733735732964, close: 32.997917, volume: 41623300, split: '', dividend: '', }, { date: '2014-01-15', open: 33.10859016106132, high: 33.929387110731525, low: 33.0624742320869, close: 33.901717, volume: 44812600, split: '', dividend: '', }, { date: '2014-01-16', open: 33.837161038643295, high: 34.123057850990996, low: 33.48670985655516, close: 34.02161, volume: 38018700, split: '', dividend: '', }, { date: '2014-01-17', open: 33.966277073014155, high: 33.966277073014155, low: 33.339150622962656, close: 33.551266, volume: 46267500, split: '', dividend: '', }, { date: '2014-01-21', open: 33.95705295421912, high: 33.95705295421912, low: 33.25614784047242, close: 33.357592, volume: 31567300, split: '', dividend: '', }, { date: '2014-01-22', open: 33.44059393341191, high: 33.49593046479265, low: 32.97025093932647, close: 33.136255, volume: 21904300, split: '', dividend: '', }, { date: '2014-01-23', open: 33.283814421413915, high: 33.32070513520363, low: 32.75813489189865, close: 33.256148, volume: 43954000, split: '', dividend: '', }, { date: '2014-01-24', open: 34.538068671821875, high: 34.63029130730445, low: 33.68960161158832, close: 33.947832, volume: 76395500, split: '', dividend: '', }, { date: '2014-01-27', open: 34.00316462683003, high: 34.02160952270693, low: 33.18236768255253, close: 33.228479, volume: 44420800, split: '', dividend: '', }, { date: '2014-01-28', open: 33.311481357325086, high: 33.56048745409928, low: 32.97025170250896, close: 33.449819, volume: 36205500, split: '', dividend: '', }, { date: '2014-01-29', open: 33.18236754282597, high: 34.012388776036396, low: 33.10858980411861, close: 33.809494, volume: 52745900, split: '', dividend: '', }, { date: '2014-01-30', open: 33.92938586637431, high: 34.01238789532163, low: 33.41292787512404, close: 33.993943, volume: 35036300, split: '', dividend: '', }, { date: '2014-01-31', open: 34.07694605570145, high: 34.943854317448654, low: 33.71727058609257, close: 34.897743, volume: 93162300, split: '', dividend: '', }, { date: '2014-02-03', open: 34.80551924745006, high: 35.03608043851365, low: 33.59737676178728, close: 33.643489, volume: 64063100, split: '', dividend: '', }, { date: '2014-02-04', open: 34.09539154895131, high: 34.29828356266768, low: 33.431374363486896, close: 33.523597, volume: 54697900, split: '', dividend: '', }, { date: '2014-02-05', open: 33.4682639685876, high: 33.63426802888911, low: 33.01636218216616, close: 33.034808, volume: 55814400, split: '', dividend: '', }, { date: '2014-02-06', open: 33.01636305232678, high: 33.43137413626312, low: 32.914916123913294, close: 33.366817, volume: 35351800, split: '', dividend: '', }, { date: '2014-02-07', open: 33.495931324509534, high: 33.7449374219109, low: 33.21003359041095, close: 33.717271, volume: 33260500, split: '', dividend: '', }, { date: '2014-02-10', open: 33.78182723180528, high: 33.938607, low: 33.46826400643671, close: 33.938607, volume: 26767000, split: '', dividend: '', }, { date: '2014-02-11', open: 34.01238877763289, high: 34.362839031100464, low: 33.99394388183279, close: 34.279837, volume: 32141400, split: '', dividend: '', }, { date: '2014-02-12', open: 34.44584085911751, high: 34.67640205525946, low: 34.399729542133905, close: 34.556513, volume: 27051800, split: '', dividend: '', }, { date: '2014-02-13', open: 34.4273993845747, high: 34.91618819434827, low: 34.4273993845747, close: 34.685627, volume: 37635500, split: '', dividend: '', }, { date: '2014-02-14', open: 34.48273169877416, high: 34.84240716607015, low: 34.427399778232214, close: 34.694848, volume: 31407500, split: '', dividend: '', }, { date: '2014-02-18', open: 34.964305464131215, high: 35.103677660560564, low: 34.759889254670725, close: 34.769179, volume: 32834000, split: '', dividend: '$0.280', }, { date: '2014-02-19', open: 34.58334928497739, high: 35.075803343151335, low: 34.574055823122066, close: 34.852803, volume: 29750400, split: '', dividend: '', }, { date: '2014-02-20', open: 34.908554138013244, high: 35.18730131216416, low: 34.75059873779089, close: 35.075803, volume: 27526100, split: '', dividend: '', }, { date: '2014-02-21', open: 35.2523426569376, high: 35.633297470273305, low: 35.17801168745419, close: 35.28951, volume: 38021300, split: '', dividend: '', }, { date: '2014-02-24', open: 35.020053, high: 35.28951043325843, low: 34.88068080447688, close: 35.020053, volume: 32085100, split: '', dividend: '', }, { date: '2014-02-25', open: 34.945722225491714, high: 35.16871792541077, low: 34.70413774332713, close: 34.880681, volume: 30736500, split: '', dividend: '', }, { date: '2014-02-26', open: 34.91784856988074, high: 35.06651422911037, low: 34.5554732380274, close: 34.81564, volume: 41041800, split: '', dividend: '', }, { date: '2014-02-27', open: 34.797056254119276, high: 35.20588495235349, low: 34.59264004668146, close: 35.178012, volume: 33903400, split: '', dividend: '', }, { date: '2014-02-28', open: 35.28951114618869, high: 35.73550719833876, low: 35.140845485751875, close: 35.596135, volume: 41215000, split: '', dividend: '', }, { date: '2014-03-03', open: 35.233758519491914, high: 35.42888497995135, low: 34.83422328670135, close: 35.103677, volume: 29717500, split: '', dividend: '', }, { date: '2014-03-04', open: 35.49392625589818, high: 35.75409022442072, low: 35.37313448138506, close: 35.689049, volume: 26802400, split: '', dividend: '', }, { date: '2014-03-05', open: 35.54038352032581, high: 35.558966727395266, low: 35.24305220721459, close: 35.410302, volume: 20520100, split: '', dividend: '', }, { date: '2014-03-06', open: 35.43817460907423, high: 35.531093431002645, low: 35.20588452295575, close: 35.447469, volume: 23582200, split: '', dividend: '', }, { date: '2014-03-07', open: 35.56825714428249, high: 35.64259183034288, low: 35.02005253970226, close: 35.215179, volume: 26591600, split: '', dividend: '', }, { date: '2014-03-10', open: 35.29880311585637, high: 35.31738260598393, low: 35.04792889531582, close: 35.140844, volume: 19006600, split: '', dividend: '', }, { date: '2014-03-11', open: 35.1873020163946, high: 35.52180067622303, low: 35.0479298202703, close: 35.326677, volume: 25216400, split: '', dividend: '', }, { date: '2014-03-12', open: 35.12226070135963, high: 35.70763265769532, low: 35.11297095607439, close: 35.558967, volume: 30494100, split: '', dividend: '', }, { date: '2014-03-13', open: 35.69833906008364, high: 35.726216658276634, low: 34.973594910733965, close: 35.205885, volume: 32169700, split: '', dividend: '', }, { date: '2014-03-14', open: 34.982888911825015, high: 35.43817469423022, low: 34.852802746644706, close: 35.029346, volume: 27195600, split: '', dividend: '', }, { date: '2014-03-17', open: 35.21517973256982, high: 35.689049660947425, low: 35.11297116314119, close: 35.354551, volume: 20479600, split: '', dividend: '', }, { date: '2014-03-18', open: 35.54967321651306, high: 37.07349991074797, low: 35.51250958990647, close: 36.748291, volume: 64063900, split: '', dividend: '', }, { date: '2014-03-19', open: 36.67396100453599, high: 36.74829197636547, low: 36.15363026432391, close: 36.488128, volume: 35597200, split: '', dividend: '', }, { date: '2014-03-20', open: 36.46954395762241, high: 37.77037031379463, low: 36.46025421238704, close: 37.473039, volume: 59269800, split: '', dividend: '', }, { date: '2014-03-21', open: 37.83541072995718, high: 38.03982415052092, low: 37.17570408789443, close: 37.31508, volume: 80721800, split: '', dividend: '', }, { date: '2014-03-24', open: 37.48232834469136, high: 37.76107551923472, low: 37.036332307925775, close: 37.630994, volume: 46098400, split: '', dividend: '', }, { date: '2014-03-25', open: 37.77966031581556, high: 38.08628509357109, low: 37.12924713330865, close: 37.482329, volume: 43193100, split: '', dividend: '', }, { date: '2014-03-26', open: 37.61241070790624, high: 37.8261166577681, low: 36.79474774723977, close: 36.971291, volume: 41977500, split: '', dividend: '', }, { date: '2014-03-27', open: 36.92483385921418, high: 37.13853980877064, low: 36.55316786399472, close: 36.571752, volume: 35369200, split: '', dividend: '', }, { date: '2014-03-28', open: 36.97129206939092, high: 37.76107652595322, low: 36.86908349948098, close: 37.445162, volume: 43472700, split: '', dividend: '', }, { date: '2014-03-31', open: 37.565953340280394, high: 38.56015492509613, low: 37.53808038781189, close: 38.086285, volume: 46886300, split: '', dividend: '', }, { date: '2014-04-01', open: 38.23495042108983, high: 38.64377911848281, low: 38.16061573445754, close: 38.48582, volume: 32605000, split: '', dividend: '', }, { date: '2014-04-02', open: 38.504404361500114, high: 38.708820569713204, low: 38.25353013532049, close: 38.420779, volume: 28666700, split: '', dividend: '', }, { date: '2014-04-03', open: 38.36503269165886, high: 38.36503269165886, low: 37.826117817541345, close: 38.104865, volume: 30139600, split: '', dividend: '', }, { date: '2014-04-04', open: 38.32786420436078, high: 38.457945723409175, low: 36.831915120799934, close: 37.045622, volume: 51409600, split: '', dividend: '', }, { date: '2014-04-07', open: 37.12924665599663, high: 37.41728729365044, low: 36.92483416648231, close: 36.980581, volume: 37559600, split: '', dividend: '', }, { date: '2014-04-08', open: 36.93412377574083, high: 37.101372638121546, low: 36.423086514293445, close: 36.999165, volume: 35918600, split: '', dividend: '', }, { date: '2014-04-09', open: 37.101373472414785, high: 37.677451971075534, low: 37.05491638320248, close: 37.603121, volume: 27398700, split: '', dividend: '', }, { date: '2014-04-10', open: 37.57524331130602, high: 37.80753339686775, low: 36.32087777843299, close: 36.571752, volume: 45960800, split: '', dividend: '', }, { date: '2014-04-11', open: 36.237254252416584, high: 36.97129187027974, low: 36.237254252416584, close: 36.432377, volume: 34330200, split: '', dividend: '', }, { date: '2014-04-14', open: 36.33946270379027, high: 36.61820988335885, low: 36.144339956840376, close: 36.404503, volume: 32006600, split: '', dividend: '', }, { date: '2014-04-15', open: 36.553168255597484, high: 37.12924674480191, low: 36.283710824298765, close: 36.934124, volume: 33968700, split: '', dividend: '', }, { date: '2014-04-16', open: 37.222164551825514, high: 37.55665949035943, low: 37.08278957015893, close: 37.53808, volume: 30615800, split: '', dividend: '', }, { date: '2014-04-17', open: 37.175704, high: 37.352247255191166, low: 36.71112382181554, close: 37.175704, volume: 36688400, split: '', dividend: '', }, { date: '2014-04-21', open: 37.287205322680734, high: 37.30578945861581, low: 36.97129080751011, close: 37.110663, volume: 22221200, split: '', dividend: '', }, { date: '2014-04-22', open: 37.12924640221014, high: 37.29649526406316, low: 37.00845834501954, close: 37.157124, volume: 27056700, split: '', dividend: '', }, { date: '2014-04-23', open: 37.15712388974225, high: 37.15712388974225, low: 36.67395958332912, close: 36.878373, volume: 24602800, split: '', dividend: '', }, { date: '2014-04-24', open: 36.92483368860588, high: 37.13853963717493, low: 36.51600035242518, close: 37.036332, volume: 42381600, split: '', dividend: '', }, { date: '2014-04-25', open: 37.435871866268855, high: 37.79824347782511, low: 36.93412434227011, close: 37.08279, volume: 56876800, split: '', dividend: '', }, { date: '2014-04-28', open: 37.29649593691493, high: 38.365032209684735, low: 37.25003884805576, close: 37.974783, volume: 50610200, split: '', dividend: '', }, { date: '2014-04-29', open: 38.188488607662535, high: 38.27211396850022, low: 37.528785686923904, close: 37.640284, volume: 29636200, split: '', dividend: '', }, { date: '2014-04-30', open: 37.53808, high: 37.63099417668346, low: 37.32436940284904, close: 37.53808, volume: 35458700, split: '', dividend: '', }, { date: '2014-05-01', open: 37.3894143423207, high: 37.50091265516035, low: 37.11995691166035, close: 37.166414, volume: 28787400, split: '', dividend: '', }, { date: '2014-05-02', open: 37.45445426966055, high: 37.48232815072634, low: 36.850499118934216, close: 36.878373, volume: 43416600, split: '', dividend: '', }, { date: '2014-05-05', open: 36.72041743241187, high: 36.83191574646733, low: 36.51600122402249, close: 36.636793, volume: 22460900, split: '', dividend: '', }, { date: '2014-05-06', open: 36.506711885209754, high: 36.56245871995728, low: 36.19079735924751, close: 36.293005, volume: 27112400, split: '', dividend: '', }, { date: '2014-05-07', open: 36.44167071311115, high: 36.71112443346649, low: 35.78196406161665, close: 36.6275, volume: 41744500, split: '', dividend: '', }, { date: '2014-05-08', open: 36.553168819202035, high: 37.073500482778314, low: 36.20938041274714, close: 36.831916, volume: 32120400, split: '', dividend: '', }, { date: '2014-05-09', open: 36.739001, high: 37.02703791970056, low: 36.58104188290231, close: 36.739001, volume: 29647600, split: '', dividend: '', }, { date: '2014-05-12', open: 36.92483504358886, high: 37.184998089442125, low: 36.84121061010436, close: 37.138541, volume: 22782600, split: '', dividend: '', }, { date: '2014-05-13', open: 37.35375334448997, high: 37.89647009631222, low: 37.28825327271856, close: 37.821611, volume: 27004800, split: '', dividend: '$0.280', }, { date: '2014-05-14', open: 37.709326111534835, high: 37.84968527966738, low: 37.47539728369829, close: 37.653186, volume: 18818700, split: '', dividend: '', }, { date: '2014-05-15', open: 37.512827380698354, high: 37.8029010028902, low: 36.97011062074675, close: 37.054325, volume: 37793200, split: '', dividend: '', }, { date: '2014-05-16', open: 37.119824806207994, high: 37.27889828175254, low: 36.7455405503118, close: 37.269543, volume: 29867100, split: '', dividend: '', }, { date: '2014-05-19', open: 37.06368479080966, high: 37.26018407245284, low: 36.92332562126582, close: 37.194684, volume: 24537400, split: '', dividend: '', }, { date: '2014-05-20', open: 37.129184, high: 37.37246904815565, low: 36.92332569331694, close: 37.129184, volume: 21320900, split: '', dividend: '', }, { date: '2014-05-21', open: 37.24146950004528, high: 37.756112, low: 37.185329386936374, close: 37.756112, volume: 22398700, split: '', dividend: '', }, { date: '2014-05-22', open: 37.6999717205019, high: 37.756111833363036, low: 37.28825416663697, close: 37.522183, volume: 20201800, split: '', dividend: '', }, { date: '2014-05-23', open: 37.774826831852664, high: 37.774826831852664, low: 37.42861309642605, close: 37.540898, volume: 18020000, split: '', dividend: '', }, { date: '2014-05-27', open: 37.67189613533472, high: 37.67189613533472, low: 37.2508270571591, close: 37.606397, volume: 26160600, split: '', dividend: '', }, { date: '2014-05-28', open: 37.55961192704963, high: 37.606397692947446, low: 37.26018396101894, close: 37.437968, volume: 25711500, split: '', dividend: '', }, { date: '2014-05-29', open: 37.56897196067208, high: 37.75611128176717, low: 37.344398412493796, close: 37.746756, volume: 19888200, split: '', dividend: '', }, { date: '2014-05-30', open: 37.84968536780335, high: 38.33625733083638, low: 37.66254136938303, close: 38.308184, volume: 34567600, split: '', dividend: '', }, { date: '2014-06-02', open: 38.31754345183343, high: 38.44854266147236, low: 38.064899378649194, close: 38.167829, volume: 18504300, split: '', dividend: '', }, { date: '2014-06-03', open: 37.99003994068052, high: 38.06489903735669, low: 37.662541451661916, close: 37.699971, volume: 18068900, split: '', dividend: '', }, { date: '2014-06-04', open: 37.62511138100096, high: 37.774825829413665, low: 37.29761289652879, close: 37.728041, volume: 23209000, split: '', dividend: '', }, { date: '2014-06-05', open: 37.98068444335561, high: 38.59825654812562, low: 37.802900405837285, close: 38.560827, volume: 31865200, split: '', dividend: '', }, { date: '2014-06-06', open: 38.813471, high: 38.98189975554484, low: 38.58890119737083, close: 38.813471, volume: 24060500, split: '', dividend: '', }, { date: '2014-06-09', open: 38.729256904846814, high: 38.81347222098376, low: 38.383043165495515, close: 38.616972, volume: 15019200, split: '', dividend: '', }, { date: '2014-06-10', open: 38.39239790441121, high: 38.51404182938356, low: 38.23332817450569, close: 38.467257, volume: 15117700, split: '', dividend: '', }, { date: '2014-06-11', open: 38.29882713512416, high: 38.42982727680305, low: 38.149112687491126, close: 38.233328, volume: 18040000, split: '', dividend: '', }, { date: '2014-06-12', open: 38.18654258472754, high: 38.25204265616667, low: 37.69997062546544, close: 37.971329, volume: 29818900, split: '', dividend: '', }, { date: '2014-06-13', open: 38.45789713899869, high: 38.8976852034926, low: 38.233328273091, close: 38.579542, volume: 26310000, split: '', dividend: '', }, { date: '2014-06-16', open: 38.401757886076766, high: 38.935115621498454, low: 38.401757886076766, close: 38.832186, volume: 24205300, split: '', dividend: '', }, { date: '2014-06-17', open: 38.63568695658866, high: 39.21582952615163, low: 37.74675645633398, close: 39.000615, volume: 22518600, split: '', dividend: '', }, { date: '2014-06-18', open: 38.93511445239652, high: 39.056758376748405, low: 38.53275593888326, close: 38.972544, volume: 27097000, split: '', dividend: '', }, { date: '2014-06-19', open: 38.89768579054135, high: 39.08482885424374, low: 38.67311598552912, close: 38.841541, volume: 19828200, split: '', dividend: '', }, { date: '2014-06-20', open: 38.785401409563704, high: 39.140974171094776, low: 38.719901336387125, close: 39.000615, volume: 47764900, split: '', dividend: '', }, { date: '2014-06-23', open: 39.047400146349126, high: 39.30004328173169, low: 39.00997059798454, close: 39.290688, volume: 18743900, split: '', dividend: '', }, { date: '2014-06-24', open: 39.14097409777796, high: 39.243899976859524, low: 38.88833002314048, close: 39.066115, volume: 26509100, split: '', dividend: '', }, { date: '2014-06-25', open: 39.01932981554708, high: 39.34682830641719, low: 38.794756267110216, close: 39.328114, volume: 20049100, split: '', dividend: '', }, { date: '2014-06-26', open: 39.23454328105121, high: 39.243899498515255, low: 38.76668562208327, close: 39.038044, volume: 23604400, split: '', dividend: '', }, { date: '2014-06-27', open: 38.935115135005255, high: 39.571401548259686, low: 38.84154079649837, close: 39.533972, volume: 74640000, split: '', dividend: '', }, { date: '2014-06-30', open: 39.45911238446593, high: 39.49654193223331, low: 39.019329, close: 39.019329, volume: 30793100, split: '', dividend: '', }, { date: '2014-07-01', open: 39.16904371835306, high: 39.44040209331742, low: 39.00997024460404, close: 39.178399, volume: 26917000, split: '', dividend: '', }, { date: '2014-07-02', open: 39.04739952733176, high: 39.206473, low: 38.86025553133689, close: 39.206473, volume: 20208100, split: '', dividend: '', }, { date: '2014-07-03', open: 39.21582962238827, high: 39.29068872049016, low: 38.888330191417, close: 39.1129, volume: 15969300, split: '', dividend: '', }, { date: '2014-07-07', open: 39.066114452673766, high: 39.41232818396418, low: 39.02868490430918, close: 39.290688, volume: 21952400, split: '', dividend: '', }, { date: '2014-07-08', open: 39.1783993785116, high: 39.30004330541033, low: 38.935115267575405, close: 39.094185, volume: 31218200, split: '', dividend: '', }, { date: '2014-07-09', open: 39.28132962425388, high: 39.29068864900142, low: 38.860256789279035, close: 38.991256, volume: 18445900, split: '', dividend: '', }, { date: '2014-07-10', open: 38.710541103395805, high: 39.30004267930062, low: 38.41111220679161, close: 39.00997, volume: 21854700, split: '', dividend: '', }, { date: '2014-07-11', open: 39.01932995923635, high: 39.384258, low: 38.81347165217391, close: 39.384258, volume: 24083000, split: '', dividend: '', }, { date: '2014-07-14', open: 39.50590209769685, high: 39.72111662321214, low: 39.33747333859792, close: 39.431043, volume: 21881100, split: '', dividend: '', }, { date: '2014-07-15', open: 39.60883109807776, high: 39.73983030627293, low: 39.32811369683793, close: 39.721116, volume: 28748700, split: '', dividend: '', }, { date: '2014-07-16', open: 39.77725621104405, high: 41.46154658696098, low: 39.74918662299516, close: 41.246333, volume: 63318000, split: '', dividend: '', }, { date: '2014-07-17', open: 42.528261960378714, high: 42.77154607060733, low: 41.4054026477746, close: 41.667402, volume: 82180300, split: '', dividend: '', }, { date: '2014-07-18', open: 41.77969019498595, high: 41.95747423131515, low: 41.405402201955745, close: 41.817116, volume: 43407500, split: '', dividend: '', }, { date: '2014-07-21', open: 41.695475645795604, high: 42.25690390276539, low: 41.37733243660738, close: 41.957475, volume: 37604400, split: '', dividend: '', }, { date: '2014-07-22', open: 42.10718873043994, high: 42.24754789763868, low: 41.72354545534038, close: 41.948119, volume: 43095800, split: '', dividend: '', }, { date: '2014-07-23', open: 42.528261750920585, high: 42.528261750920585, low: 41.75161617263363, close: 41.985545, volume: 52362900, split: '', dividend: '', }, { date: '2014-07-24', open: 42.04168924722121, high: 42.10718931949598, low: 41.47090290311248, close: 41.545762, volume: 30725300, split: '', dividend: '', }, { date: '2014-07-25', open: 41.452187999116134, high: 41.78904645213483, low: 41.452187999116134, close: 41.639332, volume: 26737700, split: '', dividend: '', }, { date: '2014-07-28', open: 41.50833297873711, high: 41.6486874711054, low: 41.105974451080854, close: 41.143404, volume: 29684200, split: '', dividend: '', }, { date: '2014-07-29', open: 41.087260242361815, high: 41.255689002180205, low: 40.83461616691892, close: 41.068545, volume: 27763100, split: '', dividend: '', }, { date: '2014-07-30', open: 41.23697362955605, high: 41.26504321736952, low: 40.50711662492523, close: 40.778475, volume: 31921400, split: '', dividend: '', }, { date: '2014-07-31', open: 40.591331305038764, high: 40.881401181291174, low: 40.310617646222106, close: 40.385473, volume: 31537500, split: '', dividend: '', }, { date: '2014-08-01', open: 40.432257486070554, high: 40.469687034071704, low: 39.86147021299608, close: 40.104759, volume: 31170300, split: '', dividend: '', }, { date: '2014-08-04', open: 40.20768774797464, high: 40.67554540229462, low: 40.05797329859224, close: 40.581972, volume: 34277400, split: '', dividend: '', }, { date: '2014-08-05', open: 40.525831588647975, high: 40.66618701571514, low: 40.07668916917032, close: 40.310618, volume: 26266400, split: '', dividend: '', }, { date: '2014-08-06', open: 39.992474, high: 40.39482783821705, low: 39.49654208129252, close: 39.992474, volume: 24634000, split: '', dividend: '', }, { date: '2014-08-07', open: 40.086044027758504, high: 40.65683130467205, low: 39.908259989635575, close: 40.450973, volume: 30314900, split: '', dividend: '', }, { date: '2014-08-08', open: 40.450972523357116, high: 40.535186900574374, low: 40.15154362658463, close: 40.422902, volume: 28942700, split: '', dividend: '', }, { date: '2014-08-11', open: 40.47904211099893, high: 40.6568308256035, low: 40.25447230985018, close: 40.422902, volume: 20351600, split: '', dividend: '', }, { date: '2014-08-12', open: 40.27318857909767, high: 40.78783107284007, low: 40.235759030330875, close: 40.722331, volume: 21431100, split: '', dividend: '', }, { date: '2014-08-13', open: 40.87204500217582, high: 41.33990266016775, low: 40.7223305516184, close: 41.246333, volume: 22889500, split: '', dividend: '', }, { date: '2014-08-14', open: 41.24633395726758, high: 41.56447342922439, low: 41.1808301407672, close: 41.424118, volume: 19313200, split: '', dividend: '', }, { date: '2014-08-15', open: 41.714190719070984, high: 42.01361962062426, low: 41.54576196194727, close: 41.91069, volume: 41611300, split: '', dividend: '', }, { date: '2014-08-18', open: 42.05104552424819, high: 42.210119, low: 41.807760476884056, close: 42.210119, volume: 26891100, split: '', dividend: '', }, { date: '2014-08-19', open: 42.341936598875485, high: 42.690312712979804, low: 42.21011919504874, close: 42.680899, volume: 28139500, split: '', dividend: '$0.280', }, { date: '2014-08-20', open: 42.69031230277392, high: 42.74680776194443, low: 42.27602796151684, close: 42.323105, volume: 24770500, split: '', dividend: '', }, { date: '2014-08-21', open: 42.219533402487095, high: 42.60557284706826, low: 42.21011968939704, close: 42.577327, volume: 22285500, split: '', dividend: '', }, { date: '2014-08-22', open: 42.69972615454511, high: 42.81271613169404, low: 42.43609134856738, close: 42.511418, volume: 18294500, split: '', dividend: '', }, { date: '2014-08-25', open: 42.74680847806307, high: 42.78446803804937, low: 42.40784607451711, close: 42.530246, volume: 16910000, split: '', dividend: '', }, { date: '2014-08-26', open: 42.66206771325777, high: 42.746809021391066, low: 42.31368876755788, close: 42.379597, volume: 14873100, split: '', dividend: '', }, { date: '2014-08-27', open: 42.276028613139886, high: 42.370182691557446, low: 42.14420650074992, close: 42.247779, volume: 21287900, split: '', dividend: '', }, { date: '2014-08-28', open: 42.134792309830836, high: 42.35135101890929, low: 42.00297490673398, close: 42.257196, volume: 17657600, split: '', dividend: '', }, { date: '2014-08-29', open: 42.45492273321594, high: 42.784467654522274, low: 42.238364964892206, close: 42.775053, volume: 21607600, split: '', dividend: '', }, { date: '2014-09-02', open: 42.77505326879574, high: 42.80329911565928, low: 42.22894680949554, close: 42.454923, volume: 22976800, split: '', dividend: '', }, { date: '2014-09-03', open: 41.92764836888632, high: 42.47375482420537, low: 41.92764836888632, close: 42.332519, volume: 33684500, split: '', dividend: '', }, { date: '2014-09-04', open: 42.12537876979075, high: 42.62440347920475, low: 42.10654663606009, close: 42.614986, volume: 26475500, split: '', dividend: '', }, { date: '2014-09-05', open: 42.47375524824661, high: 43.245833192332825, low: 42.47375524824661, close: 43.227002, volume: 36939400, split: '', dividend: '', }, { date: '2014-09-08', open: 43.33057323411721, high: 44.06498878805111, low: 43.302328328948214, close: 43.754276, volume: 45736700, split: '', dividend: '', }, { date: '2014-09-09', open: 43.75427653455687, high: 44.22505634511204, low: 43.707195728822484, close: 44.027326, volume: 40302400, split: '', dividend: '', }, { date: '2014-09-10', open: 44.08382080785653, high: 44.196807019157724, low: 43.57537767842331, close: 44.102652, volume: 27302400, split: '', dividend: '', }, { date: '2014-09-11', open: 44.008498382693695, high: 44.253302, low: 43.75427634453834, close: 44.253302, volume: 29216400, split: '', dividend: '', }, { date: '2014-09-12', open: 44.16856157776099, high: 44.27213313550037, low: 43.87667621373991, close: 43.970835, volume: 38244700, split: '', dividend: '', }, { date: '2014-09-15', open: 43.820184939821544, high: 43.98024818948499, low: 43.40589588911705, close: 43.537718, volume: 37667600, split: '', dividend: '', }, { date: '2014-09-16', open: 43.6789498817488, high: 44.11206636589993, low: 43.584795802757, close: 44.027326, volume: 27910600, split: '', dividend: '', }, { date: '2014-09-17', open: 43.556546613011434, high: 43.96141819558568, low: 43.528301707222695, close: 43.801354, volume: 38311900, split: '', dividend: '', }, { date: '2014-09-18', open: 43.86726263431876, high: 44.09323882592129, low: 43.7448589423307, close: 43.952003, volume: 35556600, split: '', dividend: '', }, { date: '2014-09-19', open: 44.07440661348723, high: 44.78999098085017, low: 43.87667626923767, close: 44.742913, volume: 202522400, split: '', dividend: '', }, { date: '2014-09-22', open: 44.53576942742103, high: 44.61109608072038, low: 44.23447128826028, close: 44.309797, volume: 38686100, split: '', dividend: '', }, { date: '2014-09-23', open: 44.11206646606313, high: 44.23447110020466, low: 43.75427663390766, close: 43.839017, volume: 33430300, split: '', dividend: '', }, { date: '2014-09-24', open: 43.904925237017366, high: 44.356873846535265, low: 43.6318720105407, close: 44.328628, volume: 26582700, split: '', dividend: '', }, { date: '2014-09-25', open: 44.140315065358166, high: 44.33804164013811, low: 43.339987520864625, close: 43.349405, volume: 33077400, split: '', dividend: '', }, { date: '2014-09-26', open: 43.245832392372336, high: 43.89550757427751, low: 43.08576537738501, close: 43.697781, volume: 27078800, split: '', dividend: '', }, { date: '2014-09-29', open: 43.29291052439515, high: 43.83901603542298, low: 43.08576552871903, close: 43.726027, volume: 26091000, split: '', dividend: '', }, { date: '2014-09-30', open: 43.66011771288132, high: 43.76369021044671, low: 43.321155315302775, close: 43.650704, volume: 33033100, split: '', dividend: '', }, { date: '2014-10-01', open: 43.56596317272491, high: 43.81076772986658, low: 43.170506253242074, close: 43.217588, volume: 38088400, split: '', dividend: '', }, { date: '2014-10-02', open: 43.15167893913657, high: 43.40589626836233, low: 42.97277978801996, close: 43.085766, volume: 25119400, split: '', dividend: '', }, { date: '2014-10-03', open: 43.292911441527444, high: 43.59420957916071, low: 42.94453532277029, close: 43.396483, volume: 32453200, split: '', dividend: '', }, { date: '2014-10-06', open: 43.424728847114714, high: 43.59420957916071, low: 43.23641598105954, close: 43.396483, volume: 20604000, split: '', dividend: '', }, { date: '2014-10-07', open: 43.1799245536818, high: 43.24583278466578, low: 42.765635501586196, close: 42.869208, volume: 25723700, split: '', dividend: '', }, { date: '2014-10-08', open: 42.822131437839495, high: 44.149729558007095, low: 42.69031309128503, close: 44.046158, volume: 33031000, split: '', dividend: '', }, { date: '2014-10-09', open: 43.782522640459, high: 44.064989586902335, low: 43.06693920730409, close: 43.170507, volume: 34422800, split: '', dividend: '', }, { date: '2014-10-10', open: 42.93511665731044, high: 43.42472859976969, low: 41.38154611375006, close: 41.456869, volume: 51978100, split: '', dividend: '', }, { date: '2014-10-13', open: 41.25914225112751, high: 41.955897306466994, low: 40.94842946188543, close: 41.099079, volume: 37100200, split: '', dividend: '', }, { date: '2014-10-14', open: 41.30621840145436, high: 41.786415676981505, low: 41.014336810756944, close: 41.174401, volume: 38115700, split: '', dividend: '', }, { date: '2014-10-15', open: 40.48706394060472, high: 40.85427125339012, low: 39.63965839361235, close: 40.694208, volume: 60218700, split: '', dividend: '', }, { date: '2014-10-16', open: 40.04452866024061, high: 40.56238926251145, low: 39.75264706871701, close: 40.242259, volume: 49040400, split: '', dividend: '', }, { date: '2014-10-17', open: 40.67537636499818, high: 41.37212859792859, low: 40.28933692232202, close: 41.080247, volume: 40683300, split: '', dividend: '', }, { date: '2014-10-20', open: 40.543557267623285, high: 41.56043975125163, low: 40.30816736791321, close: 41.503949, volume: 34527900, split: '', dividend: '', }, { date: '2014-10-21', open: 41.76758500556174, high: 42.35135101890929, low: 41.607517989645416, close: 42.257196, volume: 36433800, split: '', dividend: '', }, { date: '2014-10-22', open: 42.37018302455649, high: 42.43609219815024, low: 41.645182115025186, close: 41.786417, volume: 33570900, split: '', dividend: '', }, { date: '2014-10-23', open: 42.01238921126135, high: 42.793885577277074, low: 41.92764884564606, close: 42.389014, volume: 45451900, split: '', dividend: '', }, { date: '2014-10-24', open: 44.0932386723402, high: 44.15914784541826, low: 42.53966342380959, close: 43.434146, volume: 61076700, split: '', dividend: '', }, { date: '2014-10-27', open: 43.038689135112136, high: 43.405896444042604, low: 43.038689135112136, close: 43.227002, volume: 30371300, split: '', dividend: '', }, { date: '2014-10-28', open: 43.17992450620045, high: 43.78252171294809, low: 43.095183200035144, close: 43.773108, volume: 29049800, split: '', dividend: '', }, { date: '2014-10-29', open: 43.726027270495905, high: 43.9708346517877, low: 43.63187225121991, close: 43.895508, volume: 30276100, split: '', dividend: '', }, { date: '2014-10-30', open: 43.6130410356795, high: 43.6130410356795, low: 43.0951832513612, close: 43.358819, volume: 30073900, split: '', dividend: '', }, { date: '2014-10-31', open: 44.19680752070219, high: 44.225056192357165, low: 43.76369103804705, close: 44.206225, volume: 35849700, split: '', dividend: '', }, { date: '2014-11-03', open: 44.14972921399962, high: 44.68641819221819, low: 43.99908061781367, close: 44.667587, volume: 23130400, split: '', dividend: '', }, { date: '2014-11-04', open: 44.53576796741665, high: 44.94063953542148, low: 44.48869092915704, close: 44.78999, volume: 21530800, split: '', dividend: '', }, { date: '2014-11-05', open: 45.00654854012552, high: 45.100707326063116, low: 44.498105407768634, close: 45.063044, volume: 22449600, split: '', dividend: '', }, { date: '2014-11-06', open: 45.06304392671273, high: 46.004603537769, low: 44.99713475393879, close: 45.853954, volume: 33037800, split: '', dividend: '', }, { date: '2014-11-07', open: 46.06109442419383, high: 46.06109442419383, low: 45.467914692175874, close: 45.835122, volume: 28000600, split: '', dividend: '', }, { date: '2014-11-10', open: 45.80687751529097, high: 46.27765732651576, low: 45.71271872836714, close: 46.032849, volume: 36370100, split: '', dividend: '', }, { date: '2014-11-11', open: 45.9951848663219, high: 46.089343651593225, low: 45.8068767113753, close: 46.014017, volume: 23445200, split: '', dividend: '', }, { date: '2014-11-12', open: 45.72213576817165, high: 46.06109440431612, low: 45.684472442076114, close: 45.929277, volume: 22722100, split: '', dividend: '', }, { date: '2014-11-13', open: 45.957525315122915, high: 46.74843632580347, low: 45.86336747139407, close: 46.710773, volume: 26210400, split: '', dividend: '', }, { date: '2014-11-14', open: 46.83317653648045, high: 47.12505718873253, low: 46.503627850750654, close: 46.682527, volume: 29081700, split: '', dividend: '', }, { date: '2014-11-17', open: 46.52246096042177, high: 46.80492790568925, low: 46.26823892071777, close: 46.569538, volume: 30318600, split: '', dividend: '', }, { date: '2014-11-18', open: 46.55058734919756, high: 46.740087936026924, low: 46.14316312463557, close: 46.181064, volume: 23995500, split: '', dividend: '$0.310', }, { date: '2014-11-19', open: 46.1052622591194, high: 46.19053709683664, low: 45.41358857541292, close: 45.688364, volume: 26177500, split: '', dividend: '', }, { date: '2014-11-20', open: 45.479913316634224, high: 46.143163, low: 45.35673760390348, close: 46.143163, volume: 21510600, split: '', dividend: '', }, { date: '2014-11-21', open: 46.44636111421426, high: 46.47478511231841, low: 45.07248874343477, close: 45.460963, volume: 42884800, split: '', dividend: '', }, { date: '2014-11-24', open: 45.47044118056061, high: 45.47991426770329, low: 44.90193940971969, close: 45.09144, volume: 35434200, split: '', dividend: '', }, { date: '2014-11-25', open: 45.15776471249705, high: 45.451490105250706, low: 44.95879103578997, close: 44.977741, volume: 28008000, split: '', dividend: '', }, { date: '2014-11-26', open: 44.99669136326865, high: 45.47044046274509, low: 44.79771389899395, close: 45.243039, volume: 27163600, split: '', dividend: '', }, { date: '2014-11-28', open: 45.43253974832357, high: 45.669414298901394, low: 45.11039035953774, close: 45.29989, volume: 21534400, split: '', dividend: '', }, { date: '2014-12-01', open: 45.36621521994194, high: 46.21896171352529, low: 45.20513862932491, close: 46.067362, volume: 31191600, split: '', dividend: '', }, { date: '2014-12-02', open: 46.275812264874375, high: 46.474785940136684, low: 45.66941436205481, close: 45.915762, volume: 25773500, split: '', dividend: '', }, { date: '2014-12-03', open: 45.89681150687733, high: 45.9536623459375, low: 45.299889540472876, close: 45.555715, volume: 23534800, split: '', dividend: '', }, { date: '2014-12-04', open: 45.84943686331261, high: 46.4842625511018, low: 45.66941410065135, close: 46.275812, volume: 30320400, split: '', dividend: '', }, { date: '2014-12-05', open: 46.25686217541769, high: 46.39898785307387, low: 45.839963914452476, close: 45.877861, volume: 27313400, split: '', dividend: '', }, { date: '2014-12-08', open: 45.72626114847817, high: 45.81153598631308, low: 44.95879045045859, close: 45.195665, volume: 26663100, split: '', dividend: '', }, { date: '2014-12-09', open: 44.636641804821174, high: 45.40411251559403, low: 44.57979001699012, close: 45.09144, volume: 24330500, split: '', dividend: '', }, { date: '2014-12-10', open: 45.08196576911306, high: 45.157763729306446, low: 44.24816641452738, close: 44.437667, volume: 30431800, split: '', dividend: '', }, { date: '2014-12-11', open: 44.60821695237249, high: 45.23356576158803, low: 44.22921577906364, close: 44.693488, volume: 29060400, split: '', dividend: '', }, { date: '2014-12-12', open: 44.32396441258775, high: 45.22408864123347, low: 44.21973866411458, close: 44.485041, volume: 34248400, split: '', dividend: '', }, { date: '2014-12-15', open: 44.72191588737027, high: 45.16723719787864, low: 44.10604016375276, close: 44.219739, volume: 29247800, split: '', dividend: '', }, { date: '2014-12-16', open: 43.49016956771564, high: 43.90706688352525, low: 42.76059500130689, close: 42.789019, volume: 47801400, split: '', dividend: '', }, { date: '2014-12-17', open: 42.68479240799839, high: 43.537542671917, low: 42.54267052233924, close: 43.338569, volume: 34970900, split: '', dividend: '', }, { date: '2014-12-18', open: 44.13446857639373, high: 45.025115, low: 43.907067110690235, close: 45.025115, volume: 40105600, split: '', dividend: '', }, { date: '2014-12-19', open: 45.12934000163164, high: 45.57466131104641, low: 44.69348798918322, close: 45.157764, volume: 64551200, split: '', dividend: '', }, { date: '2014-12-22', open: 45.27146241515292, high: 45.593611798646045, low: 45.20513754208081, close: 45.460963, volume: 26566000, split: '', dividend: '', }, { date: '2014-12-23', open: 45.83048724857015, high: 46.23791147689989, low: 45.60308957426624, close: 45.906289, volume: 23648100, split: '', dividend: '', }, { date: '2014-12-24', open: 46.086312106226615, high: 46.086312106226615, low: 45.555715949747444, close: 45.612563, volume: 11437800, split: '', dividend: '', }, { date: '2014-12-26', open: 45.868388101119706, high: 45.868388101119706, low: 45.309364160205426, close: 45.366215, volume: 13197800, split: '', dividend: '', }, { date: '2014-12-29', open: 45.195664547168086, high: 45.27146250726549, low: 44.778762501657695, close: 44.95879, volume: 14439500, split: '', dividend: '', }, { date: '2014-12-30', open: 44.94931430218277, high: 45.11986398061748, low: 44.38081632156529, close: 44.551366, volume: 16384700, split: '', dividend: '', }, { date: '2014-12-31', open: 44.27659054646737, high: 44.949313315810436, low: 44.011292, close: 44.011292, volume: 21552500, split: '', dividend: '', }, { date: '2015-01-02', open: 44.21026607486167, high: 44.930362812889165, low: 44.096567238198205, close: 44.305014, volume: 27913900, split: '', dividend: '', }, { date: '2015-01-05', open: 43.935490085072885, high: 44.27659038067816, low: 43.82179124986871, close: 43.897593, volume: 39673900, split: '', dividend: '', }, { date: '2015-01-06', open: 43.94496672690823, high: 44.29554010753384, low: 43.14906825224879, close: 43.253294, volume: 36447900, split: '', dividend: '', }, { date: '2015-01-07', open: 43.565966454250486, high: 44.02076463459138, low: 43.10169423957781, close: 43.802841, volume: 29114100, split: '', dividend: '', }, { date: '2015-01-08', open: 44.29554150031519, high: 45.243039714225674, low: 44.267117501396086, close: 45.09144, volume: 29645200, split: '', dividend: '', }, { date: '2015-01-09', open: 45.11039013801291, high: 45.30936381202297, low: 44.437667365173674, close: 44.712439, volume: 23944200, split: '', dividend: '', }, { date: '2015-01-12', open: 44.93036353763728, high: 45.04406616612762, low: 43.92601827050325, close: 44.153415, volume: 23651900, split: '', dividend: '', }, { date: '2015-01-13', open: 44.50399089542766, high: 45.39463824580159, low: 43.641767543232305, close: 43.926017, volume: 35270600, split: '', dividend: '', }, { date: '2015-01-14', open: 43.547017, high: 43.812319342609946, low: 43.224867607002835, close: 43.547017, volume: 29675300, split: '', dividend: '', }, { date: '2015-01-15', open: 43.79336761328537, high: 43.94496732480691, low: 43.025893126209326, close: 43.092218, volume: 32750800, split: '', dividend: '', }, { date: '2015-01-16', open: 42.9311437398363, high: 43.85021508493191, low: 42.79849115134908, close: 43.812318, volume: 35695300, split: '', dividend: '', }, { date: '2015-01-20', open: 43.86916616112794, high: 44.20079337701391, low: 43.17749341555278, close: 43.954441, volume: 36161900, split: '', dividend: '', }, { date: '2015-01-21', open: 43.52806691173819, high: 43.717566554138, low: 43.09221868171684, close: 43.509116, volume: 39081100, split: '', dividend: '', }, { date: '2015-01-22', open: 43.94496735180615, high: 44.66506408697953, low: 43.6607188400268, close: 44.655591, volume: 35898000, split: '', dividend: '', }, { date: '2015-01-23', open: 44.87351562320824, high: 44.901938674163524, low: 44.34291473711392, close: 44.702965, volume: 26211600, split: '', dividend: '', }, { date: '2015-01-26', open: 44.53241591288729, high: 44.65559162780413, low: 43.81231916759021, close: 44.541889, volume: 42525500, split: '', dividend: '', }, { date: '2015-01-27', open: 40.695048424057035, high: 40.931922972814654, low: 39.89914994023144, close: 40.420273, volume: 169164000, split: '', dividend: '', }, { date: '2015-01-28', open: 40.49607505586247, high: 40.543449018472906, low: 38.999026001433016, close: 39.02745, volume: 84507100, split: '', dividend: '', }, { date: '2015-01-29', open: 38.78110182580822, high: 39.90862375084145, low: 38.64845302359685, close: 39.804398, volume: 63585300, split: '', dividend: '', }, { date: '2015-01-30', open: 39.368549082524076, high: 39.396976870888714, low: 38.231550300223795, close: 38.278929, volume: 78004900, split: '', dividend: '', }, { date: '2015-02-02', open: 38.45895218529439, high: 39.19799983854833, low: 38.11785283110108, close: 39.112725, volume: 50352500, split: '', dividend: '', }, { date: '2015-02-03', open: 39.44435178905354, high: 39.72860030714424, low: 38.894800927251865, close: 39.415924, volume: 52082400, split: '', dividend: '', }, { date: '2015-02-04', open: 39.73807387324748, high: 39.99389838926087, low: 39.18852680791885, close: 39.643325, volume: 41614800, split: '', dividend: '', }, { date: '2015-02-05', open: 40.003374416912244, high: 40.40132176059787, low: 39.66227506947053, close: 40.221299, volume: 36548200, split: '', dividend: '', }, { date: '2015-02-06', open: 40.439223516151856, high: 40.54344926652674, low: 39.937051360924265, close: 40.183399, volume: 34616600, split: '', dividend: '', }, { date: '2015-02-09', open: 40.022325165479764, high: 40.49607425722318, low: 39.993897377480614, close: 40.136024, volume: 31381100, split: '', dividend: '', }, { date: '2015-02-10', open: 40.49607553988251, high: 40.524498591291014, low: 39.96547464532745, close: 40.363422, volume: 29670700, split: '', dividend: '', }, { date: '2015-02-11', open: 40.41080046364203, high: 40.41080046364203, low: 39.99389840965377, close: 40.154975, volume: 38262500, split: '', dividend: '', }, { date: '2015-02-12', open: 40.42027376839173, high: 40.827698, low: 40.27814714143894, close: 40.827698, volume: 33268800, split: '', dividend: '', }, { date: '2015-02-13', open: 41.10247277796257, high: 41.566745, low: 40.88454913991427, close: 41.566745, volume: 40264900, split: '', dividend: '', }, { date: '2015-02-17', open: 41.95798711908811, high: 41.98661340125684, low: 41.21367706394704, close: 41.585834, volume: 33695700, split: '', dividend: '$0.310', }, { date: '2015-02-18', open: 41.633545029672966, high: 41.700341914506346, low: 41.404525230333256, close: 41.538119, volume: 27111700, split: '', dividend: '', }, { date: '2015-02-19', open: 41.20413581011494, high: 41.538119282310504, low: 41.08008349748292, close: 41.509493, volume: 27603400, split: '', dividend: '', }, { date: '2015-02-20', open: 41.51903271716661, high: 41.87210482400628, low: 41.309102515821195, close: 41.85302, volume: 29721100, split: '', dividend: '', }, { date: '2015-02-23', open: 41.70034150462215, high: 42.167917785377426, low: 41.65263039873706, close: 42.129751, volume: 32518800, split: '', dividend: '', }, { date: '2015-02-24', open: 42.12975138115196, high: 42.272884700102175, low: 41.91027208562055, close: 42.072495, volume: 25271700, split: '', dividend: '', }, { date: '2015-02-25', open: 41.93890239711908, high: 42.07249521311684, low: 41.79576430623774, close: 41.977073, volume: 29759800, split: '', dividend: '', }, { date: '2015-02-26', open: 41.97707306901191, high: 42.20608905274423, low: 41.881646084532115, close: 42.043869, volume: 28957300, split: '', dividend: '', }, { date: '2015-02-27', open: 42.110666406951175, high: 42.177463293008046, low: 41.66217207489952, close: 41.843476, volume: 33807700, split: '', dividend: '', }, { date: '2015-03-02', open: 41.671712479582396, high: 42.167918871922865, low: 41.55720448656996, close: 41.872106, volume: 31924000, split: '', dividend: '', }, { date: '2015-03-03', open: 41.56674844977606, high: 41.82439453312183, low: 41.118254122649134, close: 41.299559, volume: 31748600, split: '', dividend: '', }, { date: '2015-03-04', open: 41.04191307614563, high: 41.23276227490965, low: 40.91786457992948, close: 41.089628, volume: 25748700, split: '', dividend: '', }, { date: '2015-03-05', open: 41.09916839783882, high: 41.26139130998113, low: 40.86060809833894, close: 41.137339, volume: 23193500, split: '', dividend: '', }, { date: '2015-03-06', open: 41.03237141094497, high: 41.13733889670253, low: 40.22126830316449, close: 40.421658, volume: 36248800, split: '', dividend: '', }, { date: '2015-03-09', open: 40.259436739314786, high: 41.156425408450076, low: 40.259436739314786, close: 40.889235, volume: 32108000, split: '', dividend: '', }, { date: '2015-03-10', open: 40.41211323218176, high: 40.75564102114881, low: 40.106757, close: 40.106757, volume: 39159700, split: '', dividend: '', }, { date: '2015-03-11', open: 40.373946553574235, high: 40.431199117697815, low: 39.925452230586004, close: 40.059046, volume: 32215300, split: '', dividend: '', }, { date: '2015-03-12', open: 39.438791687858355, high: 39.744148879472206, low: 38.99029735843429, close: 39.142975, volume: 59992500, split: '', dividend: '', }, { date: '2015-03-13', open: 38.83761896444862, high: 39.572384710587684, low: 38.75173725386094, close: 39.486503, volume: 58007700, split: '', dividend: '', }, { date: '2015-03-16', open: 39.57238429032439, high: 39.73460338900699, low: 39.391076550304554, close: 39.658266, volume: 35273500, split: '', dividend: '', }, { date: '2015-03-17', open: 39.47695749547203, high: 39.915911310259155, low: 39.267027294165246, close: 39.791859, volume: 31673400, split: '', dividend: '', }, { date: '2015-03-18', open: 39.53421389082354, high: 40.87015351224716, low: 39.43879167695304, close: 40.555252, volume: 44194800, split: '', dividend: '', }, { date: '2015-03-19', open: 40.32623190078137, high: 40.641133410708605, low: 40.28806511484505, close: 40.354862, volume: 33879100, split: '', dividend: '', }, { date: '2015-03-20', open: 40.61250681309135, high: 41.01328716666774, low: 40.54571088269629, close: 40.917864, volume: 71904500, split: '', dividend: '', }, { date: '2015-03-23', open: 40.91786382409975, high: 41.15642412534659, low: 40.822437795118596, close: 40.898779, volume: 26246100, split: '', dividend: '', }, { date: '2015-03-24', open: 40.822438191565816, high: 41.19459131088386, low: 40.7938119093794, close: 40.93695, volume: 25513300, split: '', dividend: '', }, { date: '2015-03-25', open: 40.956031226009436, high: 40.96557554668537, low: 39.543755175613, close: 39.56284, volume: 43469900, split: '', dividend: '', }, { date: '2015-03-26', open: 39.333824320749926, high: 39.70597839918122, low: 39.04754909000216, close: 39.32428, volume: 37495600, split: '', dividend: '', }, { date: '2015-03-27', open: 39.2383982754781, high: 39.53421400990447, low: 38.961671182544706, close: 39.095264, volume: 34401400, split: '', dividend: '', }, { date: '2015-03-30', open: 39.21931181513363, high: 39.63918081017821, low: 39.03800789375018, close: 39.085719, volume: 35049700, split: '', dividend: '', }, { date: '2015-03-31', open: 38.91395599214343, high: 39.15251629711146, low: 38.68494000785656, close: 38.799448, volume: 34887200, split: '', dividend: '', }, { date: '2015-04-01', open: 38.742191192151346, high: 38.89486978565138, low: 38.46546410415616, close: 38.856703, volume: 36865300, split: '', dividend: '', }, { date: '2015-04-02', open: 38.79944729065656, high: 38.875788495333076, low: 38.28415608710512, close: 38.446379, volume: 37487500, split: '', dividend: '', }, { date: '2015-04-06', open: 38.49409107037524, high: 39.86819748205717, low: 38.34141247416155, close: 39.648722, volume: 39223700, split: '', dividend: '', }, { date: '2015-04-07', open: 39.705978206251274, high: 39.99224961864314, low: 39.41970583961818, close: 39.629637, volume: 28809400, split: '', dividend: '', }, { date: '2015-04-08', open: 39.56283960263183, high: 39.78231508087787, low: 39.162060203012786, close: 39.524669, volume: 24753400, split: '', dividend: '', }, { date: '2015-04-09', open: 39.36244952386692, high: 39.7155178138398, low: 39.36244952386692, close: 39.581925, volume: 25723900, split: '', dividend: '', }, { date: '2015-04-10', open: 39.72506328945066, high: 40.03042048251496, low: 39.515129264977716, close: 39.810945, volume: 28022000, split: '', dividend: '', }, { date: '2015-04-13', open: 39.50558897292629, high: 40.135387232755896, low: 39.49604369786818, close: 39.849112, volume: 30276700, split: '', dividend: '', }, { date: '2015-04-14', open: 39.88728131767802, high: 40.10675679429384, low: 39.49604242458025, close: 39.744148, volume: 24244400, split: '', dividend: '', }, { date: '2015-04-15', open: 39.849111390576404, high: 40.51708119801066, low: 39.77277400155106, close: 40.326232, volume: 27343600, split: '', dividend: '', }, { date: '2015-04-16', open: 40.03042029722035, high: 40.40257342030361, low: 39.90636798387097, close: 40.23081, volume: 22509700, split: '', dividend: '', }, { date: '2015-04-17', open: 39.76322910601137, high: 39.83002980732979, low: 39.27656799991754, close: 39.715518, volume: 42387600, split: '', dividend: '', }, { date: '2015-04-20', open: 39.82048538103006, high: 41.19459080417199, low: 39.772773320904214, close: 40.94649, volume: 46057700, split: '', dividend: '', }, { date: '2015-04-21', open: 41.032372796256396, high: 41.17551088891184, low: 40.5838784649398, close: 40.688845, volume: 26013800, split: '', dividend: '', }, { date: '2015-04-22', open: 40.71746999975338, high: 41.1564238134446, low: 40.602962010263894, close: 41.022831, volume: 25064300, split: '', dividend: '', }, { date: '2015-04-23', open: 40.92740450168865, high: 41.61446008068329, low: 40.84152279287461, close: 41.356814, volume: 46309500, split: '', dividend: '', }, { date: '2015-04-24', open: 43.57065386945172, high: 45.93717112800596, low: 43.56111336578578, close: 45.679526, volume: 130933700, split: '', dividend: '', }, { date: '2015-04-27', open: 45.06881199227175, high: 45.927630028728586, low: 45.059270534488334, close: 45.832204, volume: 59248200, split: '', dividend: '', }, { date: '2015-04-28', open: 45.59364417269125, high: 46.95820910637718, low: 45.51730678418426, close: 46.910498, volume: 60730800, split: '', dividend: '', }, { date: '2015-04-29', open: 46.49063298663763, high: 47.05363530394292, low: 46.28069896492664, close: 46.815075, volume: 47804600, split: '', dividend: '', }, { date: '2015-04-30', open: 46.47154838169902, high: 47.27311100673114, low: 46.376121396947724, close: 46.414292, volume: 64725500, split: '', dividend: '', }, { date: '2015-05-01', open: 46.35703961214045, high: 46.64331101918161, low: 46.18527619537103, close: 46.433377, volume: 38937300, split: '', dividend: '', }, { date: '2015-05-04', open: 46.15664649490276, high: 46.63376710115813, low: 45.97534161876693, close: 46.032598, volume: 34039500, split: '', dividend: '', }, { date: '2015-05-05', open: 45.631814976126684, high: 45.95625698975869, low: 45.14515290991987, close: 45.42188, volume: 50369200, split: '', dividend: '', }, { date: '2015-05-06', open: 45.39325410832442, high: 45.58410234926756, low: 43.91418024101514, close: 44.162282, volume: 52433000, split: '', dividend: '', }, { date: '2015-05-07', open: 44.152741320498045, high: 44.93521912215805, low: 44.04777478612902, close: 44.563066, volume: 32971700, split: '', dividend: '', }, { date: '2015-05-08', open: 45.37416880282685, high: 45.784493479371726, low: 45.34554252062828, close: 45.565018, volume: 35364900, split: '', dividend: '', }, { date: '2015-05-11', open: 45.37416841717887, high: 45.717696205777834, low: 45.202405, close: 45.202405, volume: 24609400, split: '', dividend: '', }, { date: '2015-05-12', open: 44.70619938850599, high: 45.4982215120685, low: 44.29587566262115, close: 45.18332, volume: 29928300, split: '', dividend: '', }, { date: '2015-05-13', open: 45.98488317162727, high: 46.10893548375109, low: 45.39325457288989, close: 45.45051, volume: 34184600, split: '', dividend: '', }, { date: '2015-05-14', open: 45.83220465244585, high: 46.58605616736338, low: 45.83220465244585, close: 46.490633, volume: 32980900, split: '', dividend: '', }, { date: '2015-05-15', open: 46.63376749573328, high: 46.67193809879788, low: 45.85128969485382, close: 46.08985, volume: 28642700, split: '', dividend: '', }, { date: '2015-05-18', open: 45.78449367192225, high: 46.01351251864495, low: 45.43142537525452, close: 45.813119, volume: 24136500, split: '', dividend: '', }, { date: '2015-05-19', open: 45.67865918412254, high: 45.91876987705609, low: 45.31368997042076, close: 45.697869, volume: 28574800, split: '', dividend: '$0.310', }, { date: '2015-05-20', open: 45.51538199204218, high: 46.03402204922143, low: 45.400129819876845, close: 45.697869, volume: 25047900, split: '', dividend: '', }, { date: '2015-05-21', open: 45.40973397037691, high: 45.717074701513276, low: 45.15041345766422, close: 45.544195, volume: 22410700, split: '', dividend: '', }, { date: '2015-05-22', open: 45.42894222808843, high: 45.47696340632275, low: 44.9679306572311, close: 45.044768, volume: 25720600, split: '', dividend: '', }, { date: '2015-05-26', open: 44.97753718746636, high: 45.02555836589459, low: 44.3628509285892, close: 44.747029, volume: 29581900, split: '', dividend: '', }, { date: '2015-05-27', open: 44.96793025524196, high: 45.88035088195021, low: 44.77584074180799, close: 45.726681, volume: 27335600, split: '', dividend: '', }, { date: '2015-05-28', open: 45.62103217869269, high: 46.12046242570153, low: 45.51538251215188, close: 45.573011, volume: 19283700, split: '', dividend: '', }, { date: '2015-05-29', open: 45.553800416478865, high: 45.688262403792955, low: 44.74702849259436, close: 45.006349, volume: 36519600, split: '', dividend: '', }, { date: '2015-06-01', open: 45.198437689640315, high: 45.88035109549016, low: 44.77584095020725, close: 45.361712, volume: 28837300, split: '', dividend: '', }, { date: '2015-06-02', open: 45.07358034883122, high: 45.476964401661995, low: 44.77584212207396, close: 45.063974, volume: 21498300, split: '', dividend: '', }, { date: '2015-06-03', open: 45.496174213566825, high: 45.851540928848834, low: 44.967931637051514, close: 44.996743, volume: 27955200, split: '', dividend: '', }, { date: '2015-06-04', open: 44.93911839316241, high: 45.294481259394274, low: 44.37245715603259, close: 44.526128, volume: 27745500, split: '', dividend: '', }, { date: '2015-06-05', open: 44.478106193821745, high: 44.67979821759424, low: 44.02669712584953, close: 44.314829, volume: 25438100, split: '', dividend: '', }, { date: '2015-06-08', open: 44.46849942005143, high: 44.593357940957794, low: 43.863419512746646, close: 43.921048, volume: 22121600, split: '', dividend: '', }, { date: '2015-06-09', open: 43.949858862034056, high: 44.12273951961242, low: 43.66172699391746, close: 43.844214, volume: 24406100, split: '', dividend: '', }, { date: '2015-06-10', open: 43.97867591927833, high: 44.97753737234372, low: 43.882629720255984, close: 44.766239, volume: 28417400, split: '', dividend: '', }, { date: '2015-06-11', open: 44.814259368524105, high: 45.063972566280164, low: 44.30522566404364, close: 44.602961, volume: 27347800, split: '', dividend: '', }, { date: '2015-06-12', open: 44.391666697408944, high: 44.631777394817895, low: 44.08432596516828, close: 44.151556, volume: 23931000, split: '', dividend: '', }, { date: '2015-06-15', open: 43.65212467746124, high: 43.84421419111421, low: 43.23913332761654, close: 43.680937, volume: 33254500, split: '', dividend: '', }, { date: '2015-06-16', open: 43.556077629361916, high: 44.4108755350739, low: 43.50805645138104, close: 44.017094, volume: 27070300, split: '', dividend: '', }, { date: '2015-06-17', open: 43.921048770044614, high: 44.247599318520784, low: 43.56568589832216, close: 44.151556, volume: 28704100, split: '', dividend: '', }, { date: '2015-06-18', open: 44.39166660616912, high: 44.94872150212736, low: 44.34364158545767, close: 44.871888, volume: 32658300, split: '', dividend: '', }, { date: '2015-06-19', open: 44.93911941094685, high: 44.977538083251595, low: 44.17076613415085, close: 44.276411, volume: 63837000, split: '', dividend: '', }, { date: '2015-06-22', open: 44.4973151973294, high: 44.871886915017726, low: 44.334038006489294, close: 44.401269, volume: 20318100, split: '', dividend: '', }, { date: '2015-06-23', open: 44.30522637189998, high: 44.44929086791705, low: 43.815398633545456, close: 44.093928, volume: 25896500, split: '', dividend: '', }, { date: '2015-06-24', open: 43.86342032327354, high: 44.42047906267483, low: 43.748168148851015, close: 43.834608, volume: 34890900, split: '', dividend: '', }, { date: '2015-06-25', open: 44.20917936818022, high: 44.23799457192168, low: 43.70014566483481, close: 43.844214, volume: 20616000, split: '', dividend: '', }, { date: '2015-06-26', open: 43.8442155319865, high: 44.449291611775166, low: 43.24873811749529, close: 43.469639, volume: 49835300, split: '', dividend: '', }, { date: '2015-06-29', open: 43.25834358064432, high: 43.44082674759582, low: 42.60524249312796, close: 42.614845, volume: 34081700, split: '', dividend: '', }, { date: '2015-06-30', open: 42.94139506712707, high: 42.95100141566358, low: 42.20185413800095, close: 42.40355, volume: 35945400, split: '', dividend: '', }, { date: '2015-07-01', open: 42.701284506794906, high: 43.44082639863157, low: 42.35552414985628, close: 42.691682, volume: 28343900, split: '', dividend: '', }, { date: '2015-07-02', open: 42.72049450087862, high: 42.9798140493327, low: 42.317109497059505, close: 42.643661, volume: 21752000, split: '', dividend: '', }, { date: '2015-07-06', open: 42.22106360502387, high: 42.72049481055406, low: 42.21146109811928, close: 42.634054, volume: 23034000, split: '', dividend: '', }, { date: '2015-07-07', open: 42.58603267146801, high: 42.73010100869817, low: 41.606381040324635, close: 42.547614, volume: 36435800, split: '', dividend: '', }, { date: '2015-07-08', open: 42.682075672374744, high: 43.123882227220065, low: 42.28829413728349, close: 42.48999, volume: 39785900, split: '', dividend: '', }, { date: '2015-07-09', open: 42.979814841644206, high: 43.43122391327298, low: 42.73970414420485, close: 42.758913, volume: 32099800, split: '', dividend: '', }, { date: '2015-07-10', open: 43.22952722729808, high: 43.35438574804442, low: 42.806934328694595, close: 42.845353, volume: 25465800, split: '', dividend: '', }, { date: '2015-07-13', open: 43.200716084744926, high: 43.81539850123049, low: 43.17190376189419, close: 43.738565, volume: 28178300, split: '', dividend: '', }, { date: '2015-07-14', open: 43.65212564703035, high: 44.141949547710446, low: 43.517663656796636, close: 43.815399, volume: 22880300, split: '', dividend: '', }, { date: '2015-07-15', open: 43.87302649794696, high: 44.07471852271803, low: 43.6329158012638, close: 43.94986, volume: 26629600, split: '', dividend: '', }, { date: '2015-07-16', open: 44.189970273713676, high: 44.843072322883415, low: 44.151555443940424, close: 44.81426, volume: 26271700, split: '', dividend: '', }, { date: '2015-07-17', open: 44.708610006065406, high: 44.92951184327908, low: 44.4300806421793, close: 44.775841, volume: 29467100, split: '', dividend: '', }, { date: '2015-07-20', open: 44.804658287239235, high: 45.26566986818657, low: 44.602962419052666, close: 45.063974, volume: 30631900, split: '', dividend: '', }, { date: '2015-07-21', open: 44.9295126065943, high: 45.45775902066893, low: 44.64138073099367, close: 45.409734, volume: 42781900, split: '', dividend: '', }, { date: '2015-07-22', open: 43.6425188014694, high: 45.07357949882346, low: 43.41201445600682, close: 43.738565, volume: 59152400, split: '', dividend: '', }, { date: '2015-07-23', open: 43.479244114308315, high: 44.40126917173565, low: 43.31596692283668, close: 44.286017, volume: 33934000, split: '', dividend: '', }, { date: '2015-07-24', open: 44.09392767727313, high: 44.48770921392489, low: 43.98827801187501, close: 44.12274, volume: 32333200, split: '', dividend: '', }, { date: '2015-07-27', open: 44.12274121167372, high: 44.18997124713968, low: 43.46003664101595, close: 43.556079, volume: 39701400, split: '', dividend: '', }, { date: '2015-07-28', open: 43.77698418996365, high: 43.8346078759048, low: 43.01823342713885, close: 43.546476, volume: 34328900, split: '', dividend: '', }, { date: '2015-07-29', open: 43.60410389098489, high: 44.92951203870363, low: 43.469638060759735, close: 44.458897, volume: 40945900, split: '', dividend: '', }, { date: '2015-07-30', open: 44.430081588285, high: 45.52499021173481, low: 44.113137388158336, close: 45.025559, volume: 39777900, split: '', dividend: '', }, { date: '2015-07-31', open: 45.419340242898045, high: 45.49617374477832, low: 44.66058948264263, close: 44.852679, volume: 31201500, split: '', dividend: '', }, { date: '2015-08-03', open: 45.12160231400123, high: 45.14081116981818, low: 44.612568595295016, close: 44.958328, volume: 24125900, split: '', dividend: '', }, { date: '2015-08-04', open: 44.90070023873159, high: 45.82272435270982, low: 44.83346924372172, close: 45.659451, volume: 33403900, split: '', dividend: '', }, { date: '2015-08-05', open: 46.08204418780814, high: 46.495034579653854, low: 45.65945032868785, close: 45.697869, volume: 26959700, split: '', dividend: '', }, { date: '2015-08-06', open: 45.822723619838754, high: 45.88035114651118, low: 44.497315477884975, close: 44.775841, volume: 27368000, split: '', dividend: '', }, { date: '2015-08-07', open: 44.55493914910195, high: 44.92951182947966, low: 44.43008062853327, close: 44.891097, volume: 19163000, split: '', dividend: '', }, { date: '2015-08-10', open: 45.09278978073483, high: 45.611429845819956, low: 44.987140113790836, close: 45.457759, volume: 23079900, split: '', dividend: '', }, { date: '2015-08-11', open: 44.96793053609136, high: 45.083182708163136, low: 44.08432510769873, close: 44.574149, volume: 29237400, split: '', dividend: '', }, { date: '2015-08-12', open: 44.362850595062085, high: 45.04476784323189, low: 43.901838065366434, close: 44.891097, volume: 30181400, split: '', dividend: '', }, { date: '2015-08-13', open: 45.198438077070215, high: 45.236852906848235, low: 44.65098665424742, close: 44.881491, volume: 22627200, split: '', dividend: '', }, { date: '2015-08-14', open: 44.68940192955721, high: 45.23685335783783, low: 44.679798462127664, close: 45.140811, volume: 21473400, split: '', dividend: '', }, { date: '2015-08-17', open: 44.95832714641065, high: 45.573010520882335, low: 44.727819920540995, close: 45.448152, volume: 21099700, split: '', dividend: '', }, { date: '2015-08-18', open: 45.28380020647345, high: 45.854198202242436, low: 45.14845249628758, close: 45.699514, volume: 23574100, split: '', dividend: '$0.310', }, { date: '2015-08-19', open: 45.22579202902308, high: 45.51582780448523, low: 44.76173942880967, close: 45.061442, volume: 31444000, split: '', dividend: '', }, { date: '2015-08-20', open: 44.53938226631625, high: 44.92609373681569, low: 44.143004, close: 44.143004, volume: 36238200, split: '', dividend: '', }, { date: '2015-08-21', open: 43.79496306619333, high: 43.968983757603894, low: 41.639053, close: 41.639053, volume: 70053100, split: '', dividend: '', }, { date: '2015-08-24', open: 39.1061001822273, high: 41.27167704330053, low: 38.40035352147875, close: 40.295234, volume: 88753700, split: '', dividend: '', }, { date: '2015-08-25', open: 41.155665168379905, high: 41.80340719267271, low: 39.04809196606059, close: 39.125436, volume: 70616600, split: '', dividend: '', }, { date: '2015-08-26', open: 40.614268652827036, high: 41.416694880278506, low: 39.6958341083317, close: 41.291013, volume: 63408000, split: '', dividend: '', }, { date: '2015-08-27', open: 41.79373698365663, high: 42.48981684537233, low: 41.50370411076519, close: 42.441479, volume: 50943200, split: '', dividend: '', }, { date: '2015-08-28', open: 41.958091516980694, high: 42.683173710242706, low: 41.948420854075096, close: 42.470481, volume: 28246700, split: '', dividend: '', }, { date: '2015-08-31', open: 42.11277501732773, high: 42.47048126815257, low: 41.66805503565703, close: 42.074103, volume: 34441700, split: '', dividend: '', }, { date: '2015-09-01', open: 40.76895275583056, high: 41.17500071664276, low: 40.27589879913916, close: 40.430583, volume: 49688900, split: '', dividend: '', }, { date: '2015-09-02', open: 40.95264275384631, high: 41.93875452492308, low: 40.48859015569255, close: 41.919419, volume: 37671500, split: '', dividend: '', }, { date: '2015-09-03', open: 41.96775713724137, high: 42.518819601379306, low: 41.84207525759156, close: 42.054767, volume: 28285200, split: '', dividend: '', }, { date: '2015-09-04', open: 41.387692250097274, high: 41.610050787709135, low: 40.7979587373006, close: 41.194337, volume: 37138800, split: '', dividend: '', }, { date: '2015-09-08', open: 41.86141100773757, high: 42.53815535516417, low: 41.764735315482895, close: 42.431809, volume: 32469800, split: '', dividend: '', }, { date: '2015-09-09', open: 42.741176955907754, high: 42.9248673433505, low: 41.48436880032504, close: 41.639053, volume: 33469500, split: '', dividend: '', }, { date: '2015-09-10', open: 41.687391103277065, high: 42.33513312697186, low: 41.32968485609414, close: 41.851745, volume: 31366600, split: '', dividend: '', }, { date: '2015-09-11', open: 41.70672611096066, high: 42.141776386614545, low: 41.51337086257059, close: 42.035431, volume: 27132500, split: '', dividend: '', }, { date: '2015-09-14', open: 41.987092772837066, high: 41.996759568615, low: 41.43603127402462, close: 41.610051, volume: 23656000, split: '', dividend: '', }, { date: '2015-09-15', open: 41.75506480425605, high: 42.818520600928125, low: 41.64872231827281, close: 42.518819, volume: 28882200, split: '', dividend: '', }, { date: '2015-09-16', open: 42.50915277036885, high: 42.90553103371824, low: 42.383470890823276, close: 42.828187, volume: 23372200, split: '', dividend: '', }, { date: '2015-09-17', open: 42.81852101671975, high: 43.50493118644068, low: 42.61549897129261, close: 42.779849, volume: 32768200, split: '', dividend: '', }, { date: '2015-09-18', open: 42.05476652483901, high: 42.528488816947146, low: 41.89041649725994, close: 42.035431, volume: 63143700, split: '', dividend: '', }, { date: '2015-09-21', open: 42.17077870788303, high: 42.99254044710861, low: 42.15144221626742, close: 42.644501, volume: 26177200, split: '', dividend: '', }, { date: '2015-09-22', open: 41.93875438687859, high: 42.58649253611699, low: 41.87108004987059, close: 42.441479, volume: 28085900, split: '', dividend: '', }, { date: '2015-09-23', open: 42.47048054161113, high: 42.70250490739819, low: 42.06443258421442, close: 42.412473, volume: 17145200, split: '', dividend: '', }, { date: '2015-09-24', open: 42.00642889321669, high: 42.66383674108734, low: 41.8324082020041, close: 42.451145, volume: 27905600, split: '', dividend: '', }, { date: '2015-09-25', open: 43.002207136144904, high: 43.243901196037804, low: 42.30612631010087, close: 42.480147, volume: 29384600, split: '', dividend: '', }, { date: '2015-09-28', open: 42.37380514390587, high: 42.62516503637873, low: 41.774400966132, close: 41.851745, volume: 27613800, split: '', dividend: '', }, { date: '2015-09-29', open: 41.92908466303696, high: 42.12244087827902, low: 41.61971626549165, close: 41.996759, volume: 32763600, split: '', dividend: '', }, { date: '2015-09-30', open: 42.422142924396766, high: 42.82818701687435, low: 42.20945118208093, close: 42.789515, volume: 34958900, split: '', dividend: '', }, { date: '2015-10-01', open: 43.26323670671965, high: 43.26323670671965, low: 42.29646046746335, close: 43.127889, volume: 28657900, split: '', dividend: '', }, { date: '2015-10-02', open: 42.799183895325875, high: 44.055993, low: 42.46081027974575, close: 44.055993, volume: 41839000, split: '', dividend: '', }, { date: '2015-10-05', open: 44.230013923868455, high: 45.33213789421154, low: 44.18167607761317, close: 45.080778, volume: 34369300, split: '', dividend: '', }, { date: '2015-10-06', open: 44.79074590574503, high: 45.61250379037433, low: 44.68439955073348, close: 45.19679, volume: 27017200, split: '', dividend: '', }, { date: '2015-10-07', open: 45.5351599112159, high: 45.77685397620936, low: 44.42337011257475, close: 45.245128, volume: 27711500, split: '', dividend: '', }, { date: '2015-10-08', open: 45.013103138049125, high: 45.941207370680566, low: 44.955095596309896, close: 45.873534, volume: 33772700, split: '', dividend: '', }, { date: '2015-10-09', open: 45.873533924247425, high: 45.96054378654821, low: 45.36113961259181, close: 45.54483, volume: 28600600, split: '', dividend: '', }, { date: '2015-10-12', open: 45.41914847489362, high: 45.50615833787234, low: 44.95509587234043, close: 45.438484, volume: 19769100, split: '', dividend: '', }, { date: '2015-10-13', open: 45.01310376679978, high: 45.56416623664543, low: 45.01310376679978, close: 45.332138, volume: 19987800, split: '', dividend: '', }, { date: '2015-10-14', open: 45.1001146456348, high: 45.5351600972958, low: 44.984098593635046, close: 45.129116, volume: 24697800, split: '', dividend: '', }, { date: '2015-10-15', open: 45.44815, high: 45.467486491955384, low: 44.984098362477056, close: 45.44815, volume: 27189400, split: '', dividend: '', }, { date: '2015-10-16', open: 45.457819567999145, high: 45.96054418801571, low: 45.34180835080389, close: 45.931538, volume: 26450300, split: '', dividend: '', }, { date: '2015-10-19', open: 45.84452778346828, high: 46.2892477582346, low: 45.45781921750985, close: 46.037884, volume: 29387600, split: '', dividend: '', }, { date: '2015-10-20', open: 45.863864867010626, high: 46.22157401722633, low: 45.457819804061124, close: 46.182902, volume: 30574000, split: '', dividend: '', }, { date: '2015-10-21', open: 46.32791600017805, high: 46.39559420483232, low: 45.54483013743665, close: 45.63184, volume: 25144300, split: '', dividend: '', }, { date: '2015-10-22', open: 45.95087387833878, high: 47.32369807740912, low: 45.52549329805316, close: 46.434262, volume: 56637100, split: '', dividend: '', }, { date: '2015-10-23', open: 50.56239654149683, high: 52.27359145836186, low: 50.514058696123676, close: 51.113459, volume: 135227100, split: '', dividend: '', }, { date: '2015-10-26', open: 50.78475586935277, high: 52.51528633806452, low: 50.7557535483871, close: 52.447612, volume: 64633300, split: '', dividend: '', }, { date: '2015-10-27', open: 52.196251775911406, high: 52.56362385132069, low: 51.799873512615115, close: 51.906216, volume: 50999900, split: '', dividend: '', }, { date: '2015-10-28', open: 51.76120141657247, high: 52.186582, low: 51.103793566257536, close: 52.186582, volume: 47000800, split: '', dividend: '', }, { date: '2015-10-29', open: 51.76120072275076, high: 52.04156679840321, low: 51.45183232674942, close: 51.587181, volume: 30202100, split: '', dividend: '', }, { date: '2015-10-30', open: 51.548509818930654, high: 52.19625184210588, low: 50.87176547493663, close: 50.891101, volume: 46619800, split: '', dividend: '', }, { date: '2015-11-02', open: 51.094122398937216, high: 51.58718118213386, low: 50.87176483030243, close: 51.471169, volume: 30285000, split: '', dividend: '', }, { date: '2015-11-03', open: 51.1714670385423, high: 52.58295940024276, low: 51.14246568452337, close: 52.350936, volume: 36596900, split: '', dividend: '', }, { date: '2015-11-04', open: 52.37993729117878, high: 53.05668163381005, low: 52.263925107808454, close: 52.59263, volume: 37087800, split: '', dividend: '', }, { date: '2015-11-05', open: 52.67964035540543, high: 52.882662403284876, low: 52.20591805432295, close: 52.573294, volume: 31468500, split: '', dividend: '', }, { date: '2015-11-06', open: 52.29292764176722, high: 53.1533585088623, low: 52.16724576181978, close: 53.09535, volume: 32851200, split: '', dividend: '', }, { date: '2015-11-09', open: 52.73764377288401, high: 53.047012174656544, low: 51.780537213452774, close: 52.360602, volume: 32513100, split: '', dividend: '', }, { date: '2015-11-10', open: 52.27359264188349, high: 52.33160018455235, low: 51.50017162998213, close: 51.732196, volume: 55283700, split: '', dividend: '', }, { date: '2015-11-11', open: 51.91588484502847, high: 52.39927296307551, low: 51.68385661481342, close: 51.867547, volume: 36516300, split: '', dividend: '', }, { date: '2015-11-12', open: 51.70319319804951, high: 52.18658131695424, low: 51.42282712230854, close: 51.548509, volume: 35361100, split: '', dividend: '', }, { date: '2015-11-13', open: 51.30681553728236, high: 51.51950727885043, low: 50.78475539601708, close: 51.084457, volume: 36848200, split: '', dividend: '', }, { date: '2015-11-16', open: 51.316486307738884, high: 52.099572185539145, low: 51.094123898695926, close: 51.98356, volume: 32165200, split: '', dividend: '', }, { date: '2015-11-17', open: 51.74996861146696, high: 52.100354941180505, low: 51.43851496131506, close: 51.555313, volume: 31551300, split: '', dividend: '$0.360', }, { date: '2015-11-18', open: 51.58451118234025, high: 52.53833799288164, low: 51.56504532906389, close: 52.411808, volume: 29710000, split: '', dividend: '', }, { date: '2015-11-19', open: 52.54807155185168, high: 53.20017567371478, low: 52.343677177683965, close: 52.499404, volume: 28149200, split: '', dividend: '', }, { date: '2015-11-20', open: 52.80112553148414, high: 52.84978919001775, low: 51.84729874768959, close: 52.742727, volume: 37147600, split: '', dividend: '', }, { date: '2015-11-23', open: 52.80112553148414, high: 53.005516011861765, low: 52.31447921322161, close: 52.742727, volume: 28235900, split: '', dividend: '', }, { date: '2015-11-24', open: 52.47993748051148, high: 52.986050629287995, low: 52.14902187432722, close: 52.801126, volume: 24600000, split: '', dividend: '', }, { date: '2015-11-25', open: 52.64539903027378, high: 52.78166000021717, low: 52.256081, close: 52.256081, volume: 21005100, split: '', dividend: '', }, { date: '2015-11-27', open: 52.363143981278085, high: 52.63566884515754, low: 52.35341300129191, close: 52.489673, volume: 9009100, split: '', dividend: '', }, { date: '2015-11-30', open: 53.08338252484303, high: 53.49216349449629, low: 52.557803516386514, close: 52.898454, volume: 56241400, split: '', dividend: '', }, { date: '2015-12-01', open: 52.95685298176651, high: 53.7549529531881, low: 52.8497898171856, close: 53.745221, volume: 39952800, split: '', dividend: '', }, { date: '2015-12-02', open: 53.842549164328005, high: 54.465455484332, low: 53.589494049718894, close: 53.735486, volume: 47274900, split: '', dividend: '', }, { date: '2015-12-03', open: 54.00801047743383, high: 54.280530469731914, low: 52.489672014212694, close: 52.752462, volume: 38627800, split: '', dividend: '', }, { date: '2015-12-04', open: 52.674597185176324, high: 54.72824564764801, low: 52.65513035890567, close: 54.416792, volume: 43963700, split: '', dividend: '', }, { date: '2015-12-07', open: 54.2999981469175, high: 54.47519082465998, low: 53.813351819855086, close: 54.319464, volume: 30709800, split: '', dividend: '', }, { date: '2015-12-08', open: 53.98854435152275, high: 54.6017158020844, low: 53.5213648520995, close: 54.299998, volume: 32878000, split: '', dividend: '', }, { date: '2015-12-09', open: 53.89121215384451, high: 54.377858470687, low: 53.0541795155828, close: 53.511629, volume: 36373200, split: '', dividend: '', }, { date: '2015-12-10', open: 53.91067914521649, high: 54.1734691351547, low: 53.54082696331157, close: 53.793885, volume: 31620700, split: '', dividend: '', }, { date: '2015-12-11', open: 53.248840280335884, high: 53.62842344319594, low: 52.56753444728194, close: 52.616202, volume: 39549500, split: '', dividend: '', }, { date: '2015-12-14', open: 52.878991869671815, high: 53.73548648582028, low: 52.24634969761243, close: 53.667356, volume: 46768900, split: '', dividend: '', }, { date: '2015-12-15', open: 54.17346864359658, high: 54.4070608250806, low: 53.618691835712106, close: 53.725755, volume: 39843000, split: '', dividend: '', }, { date: '2015-12-16', open: 54.05667434089154, high: 54.74771114381416, low: 53.2975031598194, close: 54.630917, volume: 37503300, split: '', dividend: '', }, { date: '2015-12-17', open: 54.8547741421477, high: 55.27328997718332, low: 54.046939304679704, close: 54.212401, volume: 41280900, split: '', dividend: '', }, { date: '2015-12-18', open: 54.28053096913853, high: 54.50438827813803, low: 52.58700078863231, close: 52.684332, volume: 84684200, split: '', dividend: '', }, { date: '2015-12-21', open: 53.414301659256516, high: 53.87174628534071, low: 52.78166046282471, close: 53.365638, volume: 37246300, split: '', dividend: '', }, { date: '2015-12-22', open: 53.52136553416847, high: 53.99827699289167, low: 53.04445018227462, close: 53.871747, volume: 28322200, split: '', dividend: '', }, { date: '2015-12-23', open: 54.21240085684692, high: 54.38759353151549, low: 53.95934282462925, close: 54.329195, volume: 27279800, split: '', dividend: '', }, { date: '2015-12-24', open: 54.36812852379121, high: 54.465455842423424, low: 53.949611710063294, close: 54.1832, volume: 9558500, split: '', dividend: '', }, { date: '2015-12-28', open: 53.87174549806624, high: 54.455724, low: 53.51162916905041, close: 54.455724, volume: 22458300, split: '', dividend: '', }, { date: '2015-12-29', open: 54.78664485475409, high: 55.33168582497414, low: 54.56278754274954, close: 55.039699, volume: 27731400, split: '', dividend: '', }, { date: '2015-12-30', open: 54.96183682373065, high: 55.263555598123496, low: 54.78664414703366, close: 54.80611, volume: 21704500, split: '', dividend: '', }, { date: '2015-12-31', open: 54.54332085505183, high: 54.68931280536633, low: 53.93987649465479, close: 53.998276, volume: 26529600, split: '', dividend: '', }, { date: '2016-01-04', open: 52.869256503453585, high: 53.336436, low: 51.96409336984777, close: 53.336436, volume: 53778000, split: '', dividend: '', }, { date: '2016-01-05', open: 53.46296585400483, high: 53.91067950464886, low: 53.08338268961204, close: 53.57976, volume: 34079700, split: '', dividend: '', }, { date: '2016-01-06', open: 52.869256989995506, high: 52.947122348937214, low: 52.207417011673456, close: 52.606467, volume: 39518900, split: '', dividend: '', }, { date: '2016-01-07', open: 51.29252402840184, high: 52.06142620119234, low: 50.679348680826095, close: 50.776676, volume: 56564900, split: '', dividend: '', }, { date: '2016-01-08', open: 50.971334785876635, high: 51.857031091793075, low: 50.75721332410444, close: 50.932406, volume: 48754000, split: '', dividend: '', }, { date: '2016-01-11', open: 51.1075954847263, high: 51.43851498818556, low: 50.08563916792417, close: 50.903205, volume: 36943800, split: '', dividend: '', }, { date: '2016-01-12', open: 51.35091817374286, high: 51.68183767413922, low: 50.66961623986361, close: 51.370385, volume: 36095500, split: '', dividend: '', }, { date: '2016-01-13', open: 52.36314414605562, high: 52.62593413760523, low: 49.92991249552828, close: 50.260832, volume: 66883600, split: '', dividend: '', }, { date: '2016-01-14', open: 50.61121719805654, high: 51.99329079803358, low: 50.19270136353415, close: 51.691573, volume: 52381900, split: '', dividend: '', }, { date: '2016-01-15', open: 49.93964667363994, high: 50.58201982043841, low: 48.99555183308289, close: 49.628194, volume: 70739100, split: '', dividend: '', }, { date: '2016-01-19', open: 50.10510627640217, high: 50.29976480894452, low: 48.72303166864411, close: 49.209678, volume: 43564500, split: '', dividend: '', }, { date: '2016-01-20', open: 48.64516697489334, high: 50.00777766737069, low: 47.7886674905348, close: 49.433535, volume: 63273000, split: '', dividend: '', }, { date: '2016-01-21', open: 49.63792416798732, high: 50.2024358404046, low: 48.95661835317329, close: 49.131812, volume: 40191200, split: '', dividend: '', }, { date: '2016-01-22', open: 50.03697450550823, high: 50.93240567880169, low: 49.89097866326402, close: 50.893473, volume: 37555800, split: '', dividend: '', }, { date: '2016-01-25', open: 50.55281894999718, high: 51.24385964703214, low: 50.27056700314901, close: 50.406827, volume: 34707700, split: '', dividend: '', }, { date: '2016-01-26', open: 50.40682770999293, high: 51.03946599084255, low: 50.17323552558549, close: 50.776676, volume: 28900800, split: '', dividend: '', }, { date: '2016-01-27', open: 50.6209492812368, high: 50.805877806446176, low: 49.65739149477955, close: 49.852051, volume: 36775200, split: '', dividend: '', }, { date: '2016-01-28', open: 50.47495846743485, high: 50.81560895283854, low: 49.8812489698185, close: 50.669617, volume: 62513800, split: '', dividend: '', }, { date: '2016-01-29', open: 53.26830664657832, high: 53.618692, low: 52.55780301325104, close: 53.618692, volume: 83611700, split: '', dividend: '', }, { date: '2016-02-01', open: 53.414301697370526, high: 53.61869218093022, low: 53.04444951644031, close: 53.24884, volume: 44208500, split: '', dividend: '', }, { date: '2016-02-02', open: 52.723260443980756, high: 52.81085678171661, low: 51.24385952205698, close: 51.58451, volume: 56313800, split: '', dividend: '', }, { date: '2016-02-03', open: 51.827832975460126, high: 51.96409297149265, low: 49.8909786791816, close: 50.766944, volume: 57559800, split: '', dividend: '', }, { date: '2016-02-04', open: 50.7085453187993, high: 51.39958602290804, low: 49.99804265478427, close: 50.611218, volume: 46987100, split: '', dividend: '', }, { date: '2016-02-05', open: 50.55281893220976, high: 50.611217464114844, low: 48.23638438716825, close: 48.820359, volume: 62009000, split: '', dividend: '', }, { date: '2016-02-08', open: 48.226649998170615, high: 48.24611682452945, low: 46.90297198967031, close: 48.09039, volume: 59290500, split: '', dividend: '', }, { date: '2016-02-09', open: 47.71080588333617, high: 48.89822486740964, low: 47.370151507963264, close: 47.963861, volume: 46740500, split: '', dividend: '', }, { date: '2016-02-10', open: 48.557569678237634, high: 49.0442160066755, low: 48.19745236848627, close: 48.382377, volume: 38237000, split: '', dividend: '', }, { date: '2016-02-11', open: 47.37988541315929, high: 48.771694852779326, low: 47.21442371882881, close: 48.36291, volume: 48878600, split: '', dividend: '', }, { date: '2016-02-12', open: 48.90795583663366, high: 49.326471677623765, low: 48.42130950990099, close: 49.151279, volume: 34243300, split: '', dividend: '', }, { date: '2016-02-16', open: 49.89629261626713, high: 50.082544, low: 49.1414754512144, close: 50.082544, volume: 37291200, split: '', dividend: '$0.360', }, { date: '2016-02-17', open: 50.474657822814684, high: 51.72941522336572, low: 50.43544561249917, close: 51.386315, volume: 40789000, split: '', dividend: '', }, { date: '2016-02-18', open: 51.29809324487058, high: 51.90586632528679, low: 51.07262475284389, close: 51.160851, volume: 27176000, split: '', dividend: '', }, { date: '2016-02-19', open: 50.94519209432939, high: 51.24907716946837, low: 50.513866599225224, close: 50.798149, volume: 33559100, split: '', dividend: '', }, { date: '2016-02-22', open: 51.24907618480655, high: 51.95487929895995, low: 51.24907618480655, close: 51.611783, volume: 25008300, split: '', dividend: '', }, { date: '2016-02-23', open: 51.307894674873, high: 51.337302117218265, low: 49.97471284915982, close: 50.170769, volume: 28895300, split: '', dividend: '', }, { date: '2016-02-24', open: 49.69042994085573, high: 50.48445832390074, low: 49.21009433678205, close: 50.34722, volume: 33014500, split: '', dividend: '', }, { date: '2016-02-25', open: 50.709924076772516, high: 51.072626, low: 49.61201059801626, close: 51.072626, volume: 26939500, split: '', dividend: '', }, { date: '2016-02-26', open: 51.56276600752362, high: 51.6411904288731, low: 50.09234486774933, close: 50.288402, volume: 35975900, split: '', dividend: '', }, { date: '2016-02-29', open: 50.33741501590434, high: 50.631503164737985, low: 49.661023252731454, close: 49.876686, volume: 31654000, split: '', dividend: '', }, { date: '2016-03-01', open: 49.96491100595934, high: 51.552964846977375, low: 49.91589402742267, close: 51.543164, volume: 33024500, split: '', dividend: '', }, { date: '2016-03-02', open: 51.37651441158613, high: 51.91566784699651, low: 51.131444222635615, close: 51.905867, volume: 29289900, split: '', dividend: '', }, { date: '2016-03-03', open: 51.92547301410969, high: 51.92547301410969, low: 50.75893694518009, close: 51.317696, volume: 24427800, split: '', dividend: '', }, { date: '2016-03-04', open: 51.366713822308824, high: 51.415726880064085, low: 50.69031715656949, close: 51.004007, volume: 33034200, split: '', dividend: '', }, { date: '2016-03-07', open: 50.54327676125815, high: 50.778542182144164, low: 49.582602600628164, close: 50.023726, volume: 38407800, split: '', dividend: '', }, { date: '2016-03-08', open: 49.79826141668875, high: 51.10203678252525, low: 49.60220428523883, close: 50.631503, volume: 33835100, split: '', dividend: '', }, { date: '2016-03-09', open: 50.86676730416285, high: 51.80783584697066, low: 50.837360842127836, close: 51.798035, volume: 28251600, split: '', dividend: '', }, { date: '2016-03-10', open: 51.88626105622019, high: 51.89606288362055, low: 50.15116409666021, close: 51.023613, volume: 38387800, split: '', dividend: '', }, { date: '2016-03-11', open: 51.9548793480309, high: 52.023499, low: 51.347106268013924, close: 52.023499, volume: 32275700, split: '', dividend: '', }, { date: '2016-03-14', open: 51.67059783110156, high: 52.53324587937732, low: 51.59217733093626, close: 52.121526, volume: 24083600, split: '', dividend: '', }, { date: '2016-03-15', open: 51.7098101604777, high: 52.533246, low: 51.70000931342586, close: 52.533246, volume: 21104800, split: '', dividend: '', }, { date: '2016-03-16', open: 52.39600824600321, high: 53.52332819301086, low: 52.34699518768181, close: 53.278258, volume: 31691700, split: '', dividend: '', }, { date: '2016-03-17', open: 53.141018680531545, high: 53.91544145627516, low: 52.9351607025247, close: 53.582146, volume: 28223900, split: '', dividend: '', }, { date: '2016-03-18', open: 53.837016543943335, high: 53.88603352202565, low: 52.39600678992719, close: 52.435219, volume: 67625500, split: '', dividend: '', }, { date: '2016-03-21', open: 52.199949764204426, high: 52.86654067199145, low: 51.88625992524583, close: 52.797922, volume: 23925700, split: '', dividend: '', }, { date: '2016-03-22', open: 52.552851836578135, high: 53.180230534492324, low: 52.40580776393971, close: 53.00378, volume: 23124100, split: '', dividend: '', }, { date: '2016-03-23', open: 53.04299330744414, high: 53.170430787494475, low: 52.680290403765376, close: 52.905754, volume: 20129000, split: '', dividend: '', }, { date: '2016-03-24', open: 52.77831609921262, high: 53.25865563199951, low: 52.670485215651816, close: 53.141019, volume: 19950000, split: '', dividend: '', }, { date: '2016-03-28', open: 53.14101915025679, high: 53.21944357181153, low: 52.27837502017354, close: 52.484233, volume: 17025100, split: '', dividend: '', }, { date: '2016-03-29', open: 52.60186518994452, high: 53.77820307346667, low: 52.39600721215803, close: 53.631159, volume: 23924300, split: '', dividend: '', }, { date: '2016-03-30', open: 53.84682228877062, high: 54.542820649924906, low: 53.81741582645461, close: 53.964455, volume: 23008300, split: '', dividend: '', }, { date: '2016-03-31', open: 53.866428369380884, high: 54.493807071156986, low: 53.778203101591636, close: 54.140906, volume: 26360500, split: '', dividend: '', }, { date: '2016-04-01', open: 53.96445403231598, high: 54.51341321008099, low: 53.493920254993704, close: 54.474201, volume: 24399200, split: '', dividend: '', }, { date: '2016-04-04', open: 54.336963, high: 54.56242757676348, low: 53.91544226952914, close: 54.336963, volume: 18928800, split: '', dividend: '', }, { date: '2016-04-05', open: 54.10169391539932, high: 54.209524798503594, low: 53.38608896388915, close: 53.484119, volume: 19272300, split: '', dividend: '', }, { date: '2016-04-06', open: 53.2880625900061, high: 54.11149842061996, low: 53.14101851683499, close: 54.033074, volume: 21188700, split: '', dividend: '', }, { date: '2016-04-07', open: 53.78800411002415, high: 53.82721632055116, low: 53.160625406364765, close: 53.386089, volume: 19225100, split: '', dividend: '', }, { date: '2016-04-08', open: 53.59194719000625, high: 54.189919433902276, low: 53.24885088455902, close: 53.346877, volume: 22167200, split: '', dividend: '', }, { date: '2016-04-11', open: 53.41550051689555, high: 54.062485817816466, low: 53.229244231848774, close: 53.239049, volume: 21414200, split: '', dividend: '', }, { date: '2016-04-12', open: 53.29786344889163, high: 53.69977855678129, low: 52.69989121053115, close: 53.572345, volume: 24944300, split: '', dividend: '', }, { date: '2016-04-13', open: 54.0330744059189, high: 54.34676424850932, low: 53.80760983155704, close: 54.258538, volume: 20818000, split: '', dividend: '', }, { date: '2016-04-14', open: 54.131103695759386, high: 54.48400574408744, low: 53.984060603792265, close: 54.268343, volume: 20877100, split: '', dividend: '', }, { date: '2016-04-15', open: 54.20952479475874, high: 54.8172978828419, low: 54.023273411789376, close: 54.552626, volume: 28793800, split: '', dividend: '', }, { date: '2016-04-18', open: 54.39578159899191, high: 55.474088479704015, low: 54.12130004400725, close: 55.346651, volume: 23150300, split: '', dividend: '', }, { date: '2016-04-19', open: 55.51330034263754, high: 55.650538668567805, low: 54.58203264163916, close: 55.278031, volume: 29596800, split: '', dividend: '', }, { date: '2016-04-20', open: 55.180004506634404, high: 55.385862484259754, low: 54.39578088536811, close: 54.493807, volume: 36195700, split: '', dividend: '', }, { date: '2016-04-21', open: 54.69966461490867, high: 55.12118631572582, low: 54.32715695136319, close: 54.680059, volume: 38909100, split: '', dividend: '', }, { date: '2016-04-22', open: 50.88637347714124, high: 51.39611946458323, low: 49.7688534277492, close: 50.758936, volume: 126834100, split: '', dividend: '', }, { date: '2016-04-25', open: 50.75893639106952, high: 51.10203661505267, low: 50.61189623873604, close: 51.082431, volume: 33226900, split: '', dividend: '', }, { date: '2016-04-26', open: 51.229470237911904, high: 51.317695505762316, low: 50.08254371641804, close: 50.425641, volume: 33532600, split: '', dividend: '', }, { date: '2016-04-27', open: 50.46485258077841, high: 50.48445819561166, low: 49.553190510751676, close: 49.9355, volume: 43369300, split: '', dividend: '', }, { date: '2016-04-28', open: 49.621810193594555, high: 49.7688532852163, low: 48.58271456734632, close: 48.916011, volume: 43134800, split: '', dividend: '', }, { date: '2016-04-29', open: 48.37685302995094, high: 49.25910766511144, low: 48.37685302995094, close: 48.8866, volume: 48411700, split: '', dividend: '', }, { date: '2016-05-02', open: 49.01403775905873, high: 49.74924832544461, low: 48.79837501263812, close: 49.61201, volume: 33114500, split: '', dividend: '', }, { date: '2016-05-03', open: 49.347333203040044, high: 49.41595285588495, low: 48.62192348383233, close: 48.798375, volume: 26460200, split: '', dividend: '', }, { date: '2016-05-04', open: 48.85719255779412, high: 49.07285530297685, low: 48.48468489268268, close: 48.8866, volume: 24257600, split: '', dividend: '', }, { date: '2016-05-05', open: 48.88660034704406, high: 49.30812107234483, low: 48.74936202141293, close: 48.95522, volume: 25390700, split: '', dividend: '', }, { date: '2016-05-06', open: 48.93561405800595, high: 49.396347, low: 48.68074301847078, close: 49.396347, volume: 24715600, split: '', dividend: '', }, { date: '2016-05-09', open: 49.494376874282274, high: 49.59240298841622, low: 49.0140373477132, close: 49.082657, volume: 17951600, split: '', dividend: '', }, { date: '2016-05-10', open: 49.33753322771168, high: 50.09234550121815, low: 49.2002909787549, close: 50.013925, volume: 22891000, split: '', dividend: '', }, { date: '2016-05-11', open: 50.12175642164718, high: 50.758936957406554, low: 49.99431894210223, close: 50.043332, volume: 24039100, split: '', dividend: '', }, { date: '2016-05-12', open: 50.19037590516427, high: 50.78834716891777, low: 49.915894351451534, close: 50.49426, volume: 24102800, split: '', dividend: '', }, { date: '2016-05-13', open: 50.42564113147954, high: 50.87657322028856, low: 50.03353078946127, close: 50.072743, volume: 22592300, split: '', dividend: '', }, { date: '2016-05-16', open: 49.79826089128932, high: 50.935386556073205, low: 49.74924783429489, close: 50.807953, volume: 20032000, split: '', dividend: '', }, { date: '2016-05-17', open: 51.054736973466085, high: 51.06460735832933, low: 49.712230381404844, close: 49.860298, volume: 27803500, split: '', dividend: '$0.360', }, { date: '2016-05-18', open: 49.83068573763657, high: 50.48219530095184, low: 49.65300005492143, close: 50.156442, volume: 24907500, split: '', dividend: '', }, { date: '2016-05-19', open: 49.820815567423374, high: 49.968884173435136, low: 49.17917539904611, close: 49.672744, volume: 23842400, split: '', dividend: '', }, { date: '2016-05-20', open: 49.83068577934978, high: 50.561168293363345, low: 49.751716777745656, close: 49.968884, volume: 23905800, split: '', dividend: '', }, { date: '2016-05-23', open: 49.94914021539466, high: 50.02811316546299, low: 49.337117127265984, close: 49.386473, volume: 26118700, split: '', dividend: '', }, { date: '2016-05-24', open: 50.0478568816904, high: 51.04486347651855, low: 49.75171670968823, close: 50.926408, volume: 34757900, split: '', dividend: '', }, { date: '2016-05-25', open: 51.252161572390285, high: 51.814833726285755, low: 51.12383669749475, close: 51.44959, volume: 24040200, split: '', dividend: '', }, { date: '2016-05-26', open: 51.26203547566073, high: 51.31139233631513, low: 50.69936825133777, close: 51.222549, volume: 24335200, split: '', dividend: '', }, { date: '2016-05-27', open: 51.25216114804977, high: 51.647018, low: 51.10409254319572, close: 51.647018, volume: 17653700, split: '', dividend: '', }, { date: '2016-05-31', open: 51.58778849214068, high: 52.318272, low: 51.41010774333102, close: 52.318272, volume: 37653100, split: '', dividend: '', }, { date: '2016-06-01', open: 51.76547373549193, high: 52.268915681325076, low: 51.76547373549193, close: 52.170199, volume: 25324800, split: '', dividend: '', }, { date: '2016-06-02', open: 51.96290096408232, high: 52.0616176450061, low: 51.173192195121956, close: 51.80496, volume: 22840800, split: '', dividend: '', }, { date: '2016-06-03', open: 51.706246941447944, high: 51.74572946759217, low: 50.936276972698415, close: 51.123836, volume: 23081300, split: '', dividend: '', }, { date: '2016-06-06', open: 51.32126477973645, high: 51.676630221646725, low: 51.22254809875291, close: 51.459463, volume: 18243300, split: '', dividend: '', }, { date: '2016-06-07', open: 51.56805015884442, high: 52.05174542060443, low: 51.429847, close: 51.429847, volume: 20866800, split: '', dividend: '', }, { date: '2016-06-08', open: 51.35087726881482, high: 51.765473906685344, low: 51.20280570131771, close: 51.370621, volume: 21149400, split: '', dividend: '', }, { date: '2016-06-09', open: 51.33113412109907, high: 51.33113412109907, low: 50.82769612610883, close: 50.956021, volume: 20305700, split: '', dividend: '', }, { date: '2016-06-10', open: 50.393353017330554, high: 51.380490216242755, low: 50.383483619615824, close: 50.817823, volume: 25833200, split: '', dividend: '', }, { date: '2016-06-13', open: 48.94226512070967, high: 50.067600559287186, low: 48.42895278149206, close: 49.495059, volume: 83217800, split: '', dividend: '', }, { date: '2016-06-14', open: 49.258147603086506, high: 49.45557109192779, low: 48.932390357118585, close: 49.189048, volume: 42577100, split: '', dividend: '', }, { date: '2016-06-15', open: 49.139688347129045, high: 49.47531499183878, low: 49.050846, close: 49.050846, volume: 33757600, split: '', dividend: '', }, { date: '2016-06-16', open: 48.88303461486475, high: 49.82081595103511, low: 48.873161268495245, close: 49.741843, volume: 31188600, split: '', dividend: '', }, { date: '2016-06-17', open: 49.76158642985065, high: 49.78132917392122, low: 49.179175479769086, close: 49.485189, volume: 45710500, split: '', dividend: '', }, { date: '2016-06-20', open: 49.98862620998684, high: 50.17618523710641, low: 49.38647252534533, close: 49.425959, volume: 35607900, split: '', dividend: '', }, { date: '2016-06-21', open: 49.554288151706196, high: 50.76846591382039, low: 49.514801676788466, close: 50.531552, volume: 34097800, split: '', dividend: '', }, { date: '2016-06-22', open: 50.42296934658395, high: 50.798078515193495, low: 50.29464052549218, close: 50.334127, volume: 28816800, split: '', dividend: '', }, { date: '2016-06-23', open: 50.62039457749389, high: 51.39036356698694, low: 50.49206970303572, close: 51.242292, volume: 29028800, split: '', dividend: '', }, { date: '2016-06-24', open: 49.169304269123806, high: 50.28476731610309, low: 48.88303349777108, close: 49.189048, volume: 133503000, split: '', dividend: '', }, { date: '2016-06-27', open: 48.46843495531468, high: 48.517795764280606, low: 47.422072475883844, close: 47.807055, volume: 50216300, split: '', dividend: '', }, { date: '2016-06-28', open: 48.29074967076508, high: 48.833678090164646, low: 48.04396537167964, close: 48.804062, volume: 38140700, split: '', dividend: '', }, { date: '2016-06-29', open: 49.26801757779942, high: 50.067599695732405, low: 49.15943149882576, close: 49.889915, volume: 31304000, split: '', dividend: '', }, { date: '2016-06-30', open: 50.06760021745182, high: 50.640137824281155, low: 49.8504290443787, close: 50.511809, volume: 28527800, split: '', dividend: '', }, { date: '2016-07-01', open: 50.47232587122633, high: 51.054736817473405, low: 50.4130966522674, close: 50.501939, volume: 21400400, split: '', dividend: '', }, { date: '2016-07-05', open: 50.17618629755698, high: 50.62039508010516, low: 50.08734394876501, close: 50.511809, volume: 24806400, split: '', dividend: '', }, { date: '2016-07-06', open: 50.12682570949911, high: 50.87705195099373, low: 49.74184220395189, close: 50.71911, volume: 28167500, split: '', dividend: '', }, { date: '2016-07-07', open: 50.758592526336855, high: 50.946151554553495, low: 50.41309648281245, close: 50.71911, volume: 19585200, split: '', dividend: '', }, { date: '2016-07-08', open: 51.06460777848198, high: 51.68650520676444, low: 50.8869220938747, close: 51.627275, volume: 28391000, split: '', dividend: '', }, { date: '2016-07-11', open: 51.82470265259555, high: 52.15045990068624, low: 51.79508952393126, close: 51.913545, volume: 22269200, split: '', dividend: '', }, { date: '2016-07-12', open: 52.25904195815585, high: 52.71312802789449, low: 52.11097335362793, close: 52.525569, volume: 27317600, split: '', dividend: '', }, { date: '2016-07-13', open: 52.871069821787515, high: 53.16721098404283, low: 52.49595669579356, close: 52.82171, volume: 25356800, split: '', dividend: '', }, { date: '2016-07-14', open: 53.1474667455353, high: 53.295539299524215, low: 52.8908130483045, close: 53.048755, volume: 24545500, split: '', dividend: '', }, { date: '2016-07-15', open: 53.256052297080366, high: 53.30540816935925, low: 52.5255688034481, close: 53.009268, volume: 32024400, split: '', dividend: '', }, { date: '2016-07-18', open: 53.009268303839704, high: 53.64103512084943, low: 52.86119575046838, close: 53.265922, volume: 31433900, split: '', dividend: '', }, { date: '2016-07-19', open: 53.01913807747007, high: 53.20669710706777, low: 52.24917204784328, close: 52.407114, volume: 53336500, split: '', dividend: '', }, { date: '2016-07-20', open: 55.427755902909716, high: 56.10887859846182, low: 54.81572787585689, close: 55.190841, volume: 89893300, split: '', dividend: '', }, { date: '2016-07-21', open: 55.25994068386991, high: 55.50672498488755, low: 55.04276852469998, close: 55.082255, volume: 32776700, split: '', dividend: '', }, { date: '2016-07-22', open: 55.35865574977377, high: 55.901580218708695, low: 55.062511630504666, close: 55.842351, volume: 32157200, split: '', dividend: '', }, { date: '2016-07-25', open: 55.74363831676879, high: 56.01016634621163, low: 55.536336544675024, close: 56.000293, volume: 25610600, split: '', dividend: '', }, { date: '2016-07-26', open: 55.79299504415063, high: 56.55309168210163, low: 55.78312169778772, close: 56.029906, volume: 28079000, split: '', dividend: '', }, { date: '2016-07-27', open: 55.88183759617504, high: 56.06939168895095, low: 55.38826899867426, close: 55.467238, volume: 32327500, split: '', dividend: '', }, { date: '2016-07-28', open: 55.27968317523008, high: 55.644922951929225, low: 55.00328574649114, close: 55.486981, volume: 37550400, split: '', dividend: '', }, { date: '2016-07-29', open: 55.53633739799094, high: 56.02990600243695, low: 55.32903957126082, close: 55.950937, volume: 30558700, split: '', dividend: '', }, { date: '2016-08-01', open: 55.87196279518604, high: 56.02003534747136, low: 55.41788067642303, close: 55.852224, volume: 26003400, split: '', dividend: '', }, { date: '2016-08-02', open: 56.118747091870944, high: 56.16810789975667, low: 55.585695972443126, close: 55.852224, volume: 35122000, split: '', dividend: '', }, { date: '2016-08-03', open: 55.950936226242995, high: 56.37540620733369, low: 55.76338213342165, close: 56.237207, volume: 22075600, split: '', dividend: '', }, { date: '2016-08-04', open: 56.069392051186426, high: 56.78013182331647, low: 55.94106322786996, close: 56.651803, volume: 26587700, split: '', dividend: '', }, { date: '2016-08-05', open: 56.90846143007942, high: 57.46125529946143, low: 56.71103300337308, close: 57.214471, volume: 29335200, split: '', dividend: '', }, { date: '2016-08-08', open: 57.313187, high: 57.342800128956256, low: 57.03678660885336, close: 57.313187, volume: 19473500, split: '', dividend: '', }, { date: '2016-08-09', open: 57.42176892260238, high: 57.747526172722914, low: 57.27370031694673, close: 57.451386, volume: 16920700, split: '', dividend: '', }, { date: '2016-08-10', open: 57.411900209582896, high: 57.56984216339193, low: 57.07627355773871, close: 57.273701, volume: 15756900, split: '', dividend: '', }, { date: '2016-08-11', open: 57.283570954948075, high: 57.698170554858805, low: 57.283570954948075, close: 57.550098, volume: 18162300, split: '', dividend: '', }, { date: '2016-08-12', open: 57.283570347408386, high: 57.44151229835665, low: 56.878844098103485, close: 57.194728, volume: 21655200, split: '', dividend: '', }, { date: '2016-08-15', open: 57.26382692100828, high: 57.74752612263465, low: 57.2144710482116, close: 57.372413, volume: 19283900, split: '', dividend: '', }, { date: '2016-08-16', open: 57.223424246308966, high: 57.23335515736059, low: 56.885704733560324, close: 57.054563, volume: 20523500, split: '', dividend: '$0.360', }, { date: '2016-08-17', open: 57.153894204653675, high: 57.29295377878816, low: 56.845973383495945, close: 57.17376, volume: 18856400, split: '', dividend: '', }, { date: '2016-08-18', open: 57.034696840666946, high: 57.312820957276585, low: 56.885705361135614, close: 57.213489, volume: 14214300, split: '', dividend: '', }, { date: '2016-08-19', open: 57.04463093881692, high: 57.34261786693193, low: 56.816175287218506, close: 57.233355, volume: 17271000, split: '', dividend: '', }, { date: '2016-08-22', open: 57.21348871615987, high: 57.36248416811112, low: 56.87577019465065, close: 57.283019, volume: 15221900, split: '', dividend: '', }, { date: '2016-08-23', open: 57.51147987760525, high: 57.789599027631695, low: 57.46181141576651, close: 57.501544, volume: 18732400, split: '', dividend: '', }, { date: '2016-08-24', open: 57.412147548347015, high: 57.65053907904407, low: 57.332686353554045, close: 57.561143, volume: 18151500, split: '', dividend: '', }, { date: '2016-08-25', open: 57.49161294624187, high: 57.8988617524048, low: 57.39228198254942, close: 57.779664, volume: 18552600, split: '', dividend: '', }, { date: '2016-08-26', open: 57.888926440949895, high: 58.30611012832525, low: 57.30288548030815, close: 57.640604, volume: 20971200, split: '', dividend: '', }, { date: '2016-08-29', open: 57.7895991686609, high: 58.20677888800843, low: 57.710134, close: 57.710134, volume: 16417200, split: '', dividend: '', }, { date: '2016-08-30', open: 57.590941072913125, high: 57.79953093207785, low: 57.22342484997355, close: 57.501544, volume: 16930200, split: '', dividend: '', }, { date: '2016-08-31', open: 57.26315703553802, high: 57.41214752067035, low: 56.91550263733159, close: 57.074429, volume: 20860300, split: '', dividend: '', }, { date: '2016-09-01', open: 56.62744794535308, high: 57.43201464768189, low: 56.62744794535308, close: 57.203558, volume: 26075400, split: '', dividend: '', }, { date: '2016-09-02', open: 57.283019, high: 57.79953067324506, low: 57.03469655771381, close: 57.283019, volume: 18900500, split: '', dividend: '', }, { date: '2016-09-06', open: 57.3922812724231, high: 57.41214706759988, low: 56.826106109884925, close: 57.223424, volume: 16278400, split: '', dividend: '', }, { date: '2016-09-07', open: 57.08436393744517, high: 57.451880158168585, low: 57.024765558099205, close: 57.273088, volume: 17493400, split: '', dividend: '', }, { date: '2016-09-08', open: 57.24329094906202, high: 57.40221731367982, low: 56.796309555284694, close: 57.044632, volume: 20146100, split: '', dividend: '', }, { date: '2016-09-09', open: 56.4089270534023, high: 57.13402759249293, low: 55.832817, close: 55.832817, volume: 35113900, split: '', dividend: '', }, { date: '2016-09-12', open: 55.62422772347463, high: 56.82610736492421, low: 55.23684570226164, close: 56.667181, volume: 29303000, split: '', dividend: '', }, { date: '2016-09-13', open: 56.1208713005638, high: 56.269866750773126, low: 55.67388991638459, close: 56.150669, volume: 30130200, split: '', dividend: '', }, { date: '2016-09-14', open: 56.011609664606084, high: 56.250001198231125, low: 55.65402534403785, close: 55.882481, volume: 24062500, split: '', dividend: '', }, { date: '2016-09-15', open: 55.7732226182148, high: 56.96516637004169, low: 55.60436137059558, close: 56.806241, volume: 26847000, split: '', dividend: '', }, { date: '2016-09-16', open: 57.24329010368278, high: 57.24329010368278, low: 56.36919411790393, close: 56.865839, volume: 44607000, split: '', dividend: '', }, { date: '2016-09-19', open: 56.88570552415247, high: 57.3624846170736, low: 56.46852183126692, close: 56.547987, volume: 20937100, split: '', dividend: '', }, { date: '2016-09-20', open: 56.965166497575204, high: 56.965166497575204, low: 56.369194620327505, close: 56.428793, volume: 17384000, split: '', dividend: '', }, { date: '2016-09-21', open: 57.1240925580567, high: 57.46181107909958, low: 56.696981931073296, close: 57.372415, volume: 33707300, split: '', dividend: '', }, { date: '2016-09-22', open: 57.53134098955331, high: 57.61080615703909, low: 57.243289938637396, close: 57.432014, volume: 19822200, split: '', dividend: '', }, { date: '2016-09-23', open: 57.48167850940916, high: 57.52141109385339, low: 56.99496850434672, close: 57.044632, volume: 19955300, split: '', dividend: '', }, { date: '2016-09-26', open: 56.69698115575422, high: 56.75657556113638, low: 56.44865871720668, close: 56.518189, volume: 21688700, split: '', dividend: '', }, { date: '2016-09-27', open: 56.547986444210764, high: 57.6704048743872, low: 56.29966400242168, close: 57.561143, volume: 28065100, split: '', dividend: '', }, { date: '2016-09-28', open: 57.491612522009596, high: 57.67040467949352, low: 57.28301869174239, close: 57.640604, volume: 20536400, split: '', dividend: '', }, { date: '2016-09-29', open: 57.422081806806105, high: 57.779663139216126, low: 56.826105966427775, close: 57.014834, volume: 25463500, split: '', dividend: '', }, { date: '2016-09-30', open: 57.183692293357375, high: 57.38235024817189, low: 56.95523564532069, close: 57.213489, volume: 29910800, split: '', dividend: '', }, { date: '2016-10-03', open: 57.02476608881108, high: 57.16382566427994, low: 56.67711566020425, close: 57.034697, volume: 19189500, split: '', dividend: '', }, { date: '2016-10-04', open: 56.88570470629963, high: 57.213488341390764, low: 56.587718770797885, close: 56.855908, volume: 20085900, split: '', dividend: '', }, { date: '2016-10-05', open: 56.905572563129674, high: 57.571074730011325, low: 56.87577088982177, close: 57.253222, volume: 16726400, split: '', dividend: '', }, { date: '2016-10-06', open: 57.352553, high: 57.471746778473495, low: 56.89563672837156, close: 57.352553, volume: 16212600, split: '', dividend: '', }, { date: '2016-10-07', open: 57.46181149545875, high: 57.590941152784445, low: 57.03469689222147, close: 57.412148, volume: 20089000, split: '', dividend: '', }, { date: '2016-10-10', open: 57.52141033715696, high: 57.99818843144854, low: 57.48167775323542, close: 57.650539, volume: 18196500, split: '', dividend: '', }, { date: '2016-10-11', open: 57.501543839575156, high: 57.630672503071736, low: 56.50825406875351, close: 56.806241, volume: 26497400, split: '', dividend: '', }, { date: '2016-10-12', open: 56.726779, high: 56.88570436778666, low: 56.02154426601321, close: 56.726779, volume: 22177500, split: '', dividend: '', }, { date: '2016-10-13', open: 56.319531229394144, high: 56.915503107746915, low: 55.94208012164723, close: 56.538052, volume: 25313700, split: '', dividend: '', }, { date: '2016-10-14', open: 56.73671106023555, high: 57.35255370175028, low: 56.73671106023555, close: 57.034697, volume: 27402500, split: '', dividend: '', }, { date: '2016-10-17', open: 56.97510256869869, high: 57.07442955976107, low: 56.488388591673704, close: 56.836042, volume: 23830000, split: '', dividend: '', }, { date: '2016-10-18', open: 57.14395933692182, high: 57.5611430258947, low: 57.024765558099205, close: 57.273088, volume: 19149500, split: '', dividend: '', }, { date: '2016-10-19', open: 57.08436360087473, high: 57.451879819431255, low: 57.01483431084222, close: 57.143959, volume: 22878400, split: '', dividend: '', }, { date: '2016-10-20', open: 57.11416144104804, high: 57.13402723633188, low: 56.27979803912664, close: 56.865839, volume: 49455600, split: '', dividend: '', }, { date: '2016-10-21', open: 59.875505657062234, high: 60.04436690260924, low: 59.09080972761203, close: 59.259667, volume: 80032200, split: '', dividend: '', }, { date: '2016-10-24', open: 59.53778784998892, high: 60.590676, low: 59.527855945573776, close: 60.590676, volume: 54067000, split: '', dividend: '', }, { date: '2016-10-25', open: 60.44168045917608, high: 60.95819213236384, low: 60.39201696401412, close: 60.580745, volume: 35137200, split: '', dividend: '', }, { date: '2016-10-26', open: 60.40195215934698, high: 60.789335171265456, low: 60.06423363613601, close: 60.22316, volume: 29911600, split: '', dividend: '', }, { date: '2016-10-27', open: 60.203294769439324, high: 60.42181951509263, low: 59.68678308874487, close: 59.696714, volume: 28479900, split: '', dividend: '', }, { date: '2016-10-28', open: 59.60731657325543, high: 60.113896337963865, low: 59.18020594916853, close: 59.468257, volume: 33574700, split: '', dividend: '', }, { date: '2016-10-31', open: 59.756312531252085, high: 60.01456588473448, low: 59.517921, close: 59.517921, volume: 26434700, split: '', dividend: '', }, { date: '2016-11-01', open: 59.567588246761126, high: 59.617251741760064, low: 58.85241862211402, close: 59.398727, volume: 24533000, split: '', dividend: '', }, { date: '2016-11-02', open: 59.41859401009591, high: 59.527855884738344, low: 58.902082336678255, close: 59.031211, volume: 22147000, split: '', dividend: '', }, { date: '2016-11-03', open: 59.13053872484128, high: 59.239800599005484, low: 58.71335901006662, close: 58.812686, volume: 21600400, split: '', dividend: '', }, { date: '2016-11-04', open: 58.25644659408156, high: 58.88221616498338, low: 58.127316938295294, close: 58.316041, volume: 28697000, split: '', dividend: '', }, { date: '2016-11-07', open: 59.37886153960869, high: 60.11389696371722, low: 59.37886153960869, close: 60.014566, volume: 31664800, split: '', dividend: '', }, { date: '2016-11-08', open: 60.143695195833814, high: 60.37215184527226, low: 59.74638226363628, close: 60.064234, volume: 22935400, split: '', dividend: '', }, { date: '2016-11-09', open: 59.59738672419434, high: 60.18342769364892, low: 58.80275589449486, close: 59.766244, volume: 49632500, split: '', dividend: '', }, { date: '2016-11-10', open: 60.0741647823822, high: 60.084099666577856, low: 57.24328995507359, close: 58.30611, volume: 57822400, split: '', dividend: '', }, { date: '2016-11-11', open: 57.839264071331755, high: 58.72329098502266, low: 57.62073833246483, close: 58.623963, volume: 38767800, split: '', dividend: '', }, { date: '2016-11-14', open: 58.62396177949005, high: 58.68356115181626, low: 56.89563659954639, close: 57.73, volume: 41328400, split: '', dividend: '', }, { date: '2016-11-15', open: 58.330002, high: 59.490002, low: 58.32, close: 58.869999, volume: 34764300, split: '', dividend: '$0.390', }, { date: '2016-11-16', open: 58.939999, high: 59.66, low: 58.810001, close: 59.650002, volume: 26851400, split: '', dividend: '', }, { date: '2016-11-17', open: 60.41, high: 60.950001, low: 59.970001, close: 60.639999, volume: 31463700, split: '', dividend: '', }, { date: '2016-11-18', open: 60.779999, high: 61.139999, low: 60.299999, close: 60.349998, volume: 26580600, split: '', dividend: '', }, { date: '2016-11-21', open: 60.5, high: 60.970001, low: 60.419998, close: 60.860001, volume: 19563200, split: '', dividend: '', }, { date: '2016-11-22', open: 60.98, high: 61.259998, low: 60.810001, close: 61.119999, volume: 22645300, split: '', dividend: '', }, { date: '2016-11-23', open: 61.009998, high: 61.099998, low: 60.25, close: 60.400002, volume: 21847200, split: '', dividend: '', }, { date: '2016-11-25', open: 60.299999, high: 60.529999, low: 60.130001, close: 60.529999, volume: 8370500, split: '', dividend: '', }, ] ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/KlineChart/data_min.ts ================================================ export const testKlineData = [ { date: 1460727000000, open: 55.3, high: 55.3, low: 55.25, close: 55.25, volume: 3399547, }, { date: 1460727060000, open: 55.24, high: 55.39, low: 55.11, close: 55.37, volume: 253961, }, { date: 1460727120000, open: 55.37, high: 55.5299, low: 55.37, close: 55.5299, volume: 127451, }, { date: 1460727180000, open: 55.52, high: 55.59, low: 55.52, close: 55.58, volume: 101263, }, { date: 1460727240000, open: 55.58, high: 55.59, low: 55.5, close: 55.51, volume: 123661, }, { date: 1460727300000, open: 55.505, high: 55.52, low: 55.495, close: 55.5065, volume: 149931, }, { date: 1460727360000, open: 55.51, high: 55.63, low: 55.5, close: 55.61, volume: 126985, }, { date: 1460727420000, open: 55.605, high: 55.64, low: 55.52, close: 55.55, volume: 86640, }, { date: 1460727480000, open: 55.56, high: 55.62, low: 55.55, close: 55.61, volume: 96088, }, { date: 1460727540000, open: 55.61, high: 55.62, low: 55.56, close: 55.585, volume: 62305, }, { date: 1460727600000, open: 55.58, high: 55.58, low: 55.5, close: 55.52, volume: 35996, }, { date: 1460727660000, open: 55.52, high: 55.55, low: 55.5, close: 55.5, volume: 39946, }, { date: 1460727720000, open: 55.5, high: 55.54, low: 55.5, close: 55.525, volume: 44578, }, { date: 1460727780000, open: 55.52, high: 55.53, low: 55.5, close: 55.5238, volume: 40415, }, { date: 1460727840000, open: 55.52, high: 55.53, low: 55.51, close: 55.5233, volume: 17257, }, { date: 1460727900000, open: 55.525, high: 55.535, low: 55.5, close: 55.52, volume: 22595, }, { date: 1460727960000, open: 55.52, high: 55.52, low: 55.5, close: 55.505, volume: 16175, }, { date: 1460728020000, open: 55.51, high: 55.5836, low: 55.5, close: 55.58, volume: 120230, }, { date: 1460728080000, open: 55.58, high: 55.63, low: 55.575, close: 55.62, volume: 74342, }, { date: 1460728140000, open: 55.6199, high: 55.66, low: 55.61, close: 55.6437, volume: 100564, }, { date: 1460728200000, open: 55.65, high: 55.72, low: 55.64, close: 55.71, volume: 139687, }, { date: 1460728260000, open: 55.71, high: 55.72, low: 55.64, close: 55.7, volume: 239960, }, { date: 1460728320000, open: 55.71, high: 55.79, low: 55.705, close: 55.79, volume: 83387, }, { date: 1460728380000, open: 55.78, high: 55.785, low: 55.69, close: 55.76, volume: 115994, }, { date: 1460728440000, open: 55.76, high: 55.77, low: 55.71, close: 55.73, volume: 84542, }, { date: 1460728500000, open: 55.74, high: 55.7464, low: 55.675, close: 55.6801, volume: 61252, }, { date: 1460728560000, open: 55.69, high: 55.72, low: 55.68, close: 55.699, volume: 39175, }, { date: 1460728620000, open: 55.7, high: 55.7, low: 55.685, close: 55.69, volume: 60239, }, { date: 1460728680000, open: 55.7, high: 55.75, low: 55.69, close: 55.745, volume: 86128, }, { date: 1460728740000, open: 55.7499, high: 55.7501, low: 55.735, close: 55.7501, volume: 80104, }, { date: 1460728800000, open: 55.75, high: 55.77, low: 55.74, close: 55.7699, volume: 98355, }, { date: 1460728860000, open: 55.769, high: 55.8, low: 55.73, close: 55.74, volume: 127501, }, { date: 1460728920000, open: 55.74, high: 55.8, low: 55.74, close: 55.78, volume: 55714, }, { date: 1460728980000, open: 55.79, high: 55.805, low: 55.74, close: 55.75, volume: 176021, }, { date: 1460729040000, open: 55.75, high: 55.77, low: 55.73, close: 55.76, volume: 52884, }, { date: 1460729100000, open: 55.76, high: 55.78, low: 55.725, close: 55.755, volume: 120460, }, { date: 1460729160000, open: 55.7527, high: 55.76, low: 55.7335, close: 55.75, volume: 94916, }, { date: 1460729220000, open: 55.745, high: 55.76, low: 55.74, close: 55.75, volume: 51462, }, { date: 1460729280000, open: 55.75, high: 55.8, low: 55.74, close: 55.785, volume: 83604, }, { date: 1460729340000, open: 55.78, high: 55.81, low: 55.74, close: 55.805, volume: 75855, }, { date: 1460729400000, open: 55.805, high: 55.81, low: 55.775, close: 55.775, volume: 53635, }, { date: 1460729460000, open: 55.77, high: 55.795, low: 55.75, close: 55.77, volume: 59901, }, { date: 1460729520000, open: 55.775, high: 55.81, low: 55.77, close: 55.7867, volume: 74736, }, { date: 1460729580000, open: 55.79, high: 55.82, low: 55.77, close: 55.82, volume: 77197, }, { date: 1460729640000, open: 55.82, high: 55.825, low: 55.775, close: 55.815, volume: 46040, }, { date: 1460729700000, open: 55.8145, high: 55.8554, low: 55.81, close: 55.8554, volume: 84043, }, { date: 1460729760000, open: 55.8501, high: 55.89, low: 55.82, close: 55.825, volume: 86819, }, { date: 1460729820000, open: 55.82, high: 55.88, low: 55.82, close: 55.85, volume: 64912, }, { date: 1460729880000, open: 55.84, high: 55.86, low: 55.83, close: 55.835, volume: 68115, }, { date: 1460729940000, open: 55.835, high: 55.8599, low: 55.83, close: 55.84, volume: 49842, }, { date: 1460730000000, open: 55.845, high: 55.85, low: 55.795, close: 55.84, volume: 96899, }, { date: 1460730060000, open: 55.835, high: 55.86, low: 55.8342, close: 55.855, volume: 33253, }, { date: 1460730120000, open: 55.86, high: 55.88, low: 55.85, close: 55.86, volume: 41537, }, { date: 1460730180000, open: 55.865, high: 55.865, low: 55.82, close: 55.855, volume: 44682, }, { date: 1460730240000, open: 55.86, high: 55.875, low: 55.84, close: 55.875, volume: 87616, }, { date: 1460730300000, open: 55.87, high: 55.9, low: 55.8601, close: 55.8801, volume: 60941, }, { date: 1460730360000, open: 55.8809, high: 55.895, low: 55.845, close: 55.85, volume: 37573, }, { date: 1460730420000, open: 55.85, high: 55.895, low: 55.831, close: 55.89, volume: 39727, }, { date: 1460730480000, open: 55.8899, high: 55.92, low: 55.88, close: 55.91, volume: 68520, }, { date: 1460730540000, open: 55.91, high: 55.915, low: 55.8648, close: 55.88, volume: 59788, }, { date: 1460730600000, open: 55.88, high: 55.88, low: 55.82, close: 55.845, volume: 64368, }, { date: 1460730660000, open: 55.845, high: 55.865, low: 55.81, close: 55.84, volume: 39541, }, { date: 1460730720000, open: 55.835, high: 55.85, low: 55.82, close: 55.8335, volume: 17768, }, { date: 1460730780000, open: 55.84, high: 55.845, low: 55.81, close: 55.82, volume: 34595, }, { date: 1460730840000, open: 55.82, high: 55.83, low: 55.77, close: 55.78, volume: 131385, }, { date: 1460730900000, open: 55.78, high: 55.82, low: 55.7716, close: 55.81, volume: 45594, }, { date: 1460730960000, open: 55.82, high: 55.8365, low: 55.78, close: 55.7858, volume: 32488, }, { date: 1460731020000, open: 55.79, high: 55.83, low: 55.785, close: 55.81, volume: 33095, }, { date: 1460731080000, open: 55.812, high: 55.815, low: 55.79, close: 55.7964, volume: 24527, }, { date: 1460731140000, open: 55.8, high: 55.8, low: 55.74, close: 55.76, volume: 65555, }, { date: 1460731200000, open: 55.76, high: 55.76, low: 55.73, close: 55.7461, volume: 47269, }, { date: 1460731260000, open: 55.7499, high: 55.76, low: 55.74, close: 55.7599, volume: 24187, }, { date: 1460731320000, open: 55.76, high: 55.78, low: 55.7401, close: 55.76, volume: 109218, }, { date: 1460731380000, open: 55.76, high: 55.8, low: 55.76, close: 55.8, volume: 52788, }, { date: 1460731440000, open: 55.795, high: 55.8, low: 55.79, close: 55.8, volume: 51201, }, { date: 1460731500000, open: 55.8, high: 55.83, low: 55.78, close: 55.83, volume: 47082, }, { date: 1460731560000, open: 55.83, high: 55.86, low: 55.82, close: 55.82, volume: 59396, }, { date: 1460731620000, open: 55.81, high: 55.81, low: 55.77, close: 55.78, volume: 25316, }, { date: 1460731680000, open: 55.79, high: 55.79, low: 55.7601, close: 55.77, volume: 16402, }, { date: 1460731740000, open: 55.77, high: 55.78, low: 55.76, close: 55.7736, volume: 20268, }, { date: 1460731800000, open: 55.775, high: 55.83, low: 55.77, close: 55.83, volume: 22863, }, { date: 1460731860000, open: 55.8238, high: 55.84, low: 55.815, close: 55.84, volume: 33095, }, { date: 1460731920000, open: 55.8361, high: 55.84, low: 55.82, close: 55.825, volume: 11610, }, { date: 1460731980000, open: 55.83, high: 55.85, low: 55.8199, close: 55.83, volume: 76533, }, { date: 1460732040000, open: 55.83, high: 55.83, low: 55.8, close: 55.805, volume: 19637, }, { date: 1460732100000, open: 55.804, high: 55.81, low: 55.79, close: 55.8, volume: 16785, }, { date: 1460732160000, open: 55.79, high: 55.81, low: 55.77, close: 55.78, volume: 32786, }, { date: 1460732220000, open: 55.79, high: 55.81, low: 55.79, close: 55.81, volume: 17408, }, { date: 1460732280000, open: 55.8038, high: 55.81, low: 55.785, close: 55.805, volume: 33830, }, { date: 1460732340000, open: 55.81, high: 55.82, low: 55.8, close: 55.81, volume: 22762, }, { date: 1460732400000, open: 55.8, high: 55.84, low: 55.78, close: 55.78, volume: 32796, }, { date: 1460732460000, open: 55.7801, high: 55.79, low: 55.77, close: 55.79, volume: 43262, }, { date: 1460732520000, open: 55.79, high: 55.795, low: 55.78, close: 55.79, volume: 14810, }, { date: 1460732580000, open: 55.79, high: 55.805, low: 55.77, close: 55.77, volume: 43974, }, { date: 1460732640000, open: 55.77, high: 55.7999, low: 55.765, close: 55.765, volume: 44148, }, { date: 1460732700000, open: 55.77, high: 55.78, low: 55.76, close: 55.77, volume: 19445, }, { date: 1460732760000, open: 55.77, high: 55.775, low: 55.725, close: 55.73, volume: 81213, }, { date: 1460732820000, open: 55.73, high: 55.7499, low: 55.72, close: 55.72, volume: 34281, }, { date: 1460732880000, open: 55.72, high: 55.72, low: 55.695, close: 55.71, volume: 70537, }, { date: 1460732940000, open: 55.705, high: 55.73, low: 55.7, close: 55.72, volume: 47674, }, { date: 1460733000000, open: 55.72, high: 55.74, low: 55.69, close: 55.71, volume: 49875, }, { date: 1460733060000, open: 55.71, high: 55.735, low: 55.695, close: 55.71, volume: 85974, }, { date: 1460733120000, open: 55.715, high: 55.75, low: 55.715, close: 55.72, volume: 59906, }, { date: 1460733180000, open: 55.725, high: 55.75, low: 55.69, close: 55.715, volume: 145854, }, { date: 1460733240000, open: 55.719, high: 55.725, low: 55.7, close: 55.72, volume: 45495, }, { date: 1460733300000, open: 55.72, high: 55.73, low: 55.7, close: 55.73, volume: 35534, }, { date: 1460733360000, open: 55.725, high: 55.75, low: 55.725, close: 55.75, volume: 36370, }, { date: 1460733420000, open: 55.745, high: 55.75, low: 55.71, close: 55.7267, volume: 35040, }, { date: 1460733480000, open: 55.72, high: 55.73, low: 55.71, close: 55.715, volume: 24869, }, { date: 1460733540000, open: 55.7192, high: 55.74, low: 55.71, close: 55.74, volume: 28415, }, { date: 1460733600000, open: 55.74, high: 55.75, low: 55.7301, close: 55.75, volume: 35403, }, { date: 1460733660000, open: 55.75, high: 55.76, low: 55.725, close: 55.75, volume: 55479, }, { date: 1460733720000, open: 55.75, high: 55.76, low: 55.7401, close: 55.755, volume: 48690, }, { date: 1460733780000, open: 55.75, high: 55.76, low: 55.74, close: 55.75, volume: 34400, }, { date: 1460733840000, open: 55.7575, high: 55.76, low: 55.74, close: 55.76, volume: 20475, }, { date: 1460733900000, open: 55.75, high: 55.755, low: 55.71, close: 55.735, volume: 56586, }, { date: 1460733960000, open: 55.7363, high: 55.76, low: 55.73, close: 55.745, volume: 45510, }, { date: 1460734020000, open: 55.74, high: 55.77, low: 55.73, close: 55.7462, volume: 56101, }, { date: 1460734080000, open: 55.745, high: 55.765, low: 55.73, close: 55.7601, volume: 25501, }, { date: 1460734140000, open: 55.7699, high: 55.77, low: 55.74, close: 55.76, volume: 19758, }, { date: 1460734200000, open: 55.76, high: 55.77, low: 55.75, close: 55.76, volume: 14410, }, { date: 1460734260000, open: 55.77, high: 55.7799, low: 55.735, close: 55.74, volume: 24848, }, { date: 1460734320000, open: 55.74, high: 55.77, low: 55.735, close: 55.74, volume: 29375, }, { date: 1460734380000, open: 55.74, high: 55.75, low: 55.73, close: 55.73, volume: 17949, }, { date: 1460734440000, open: 55.7325, high: 55.74, low: 55.73, close: 55.74, volume: 8153, }, { date: 1460734500000, open: 55.74, high: 55.74, low: 55.73, close: 55.74, volume: 36254, }, { date: 1460734560000, open: 55.735, high: 55.77, low: 55.73, close: 55.76, volume: 76928, }, { date: 1460734620000, open: 55.7528, high: 55.77, low: 55.75, close: 55.77, volume: 29448, }, { date: 1460734680000, open: 55.765, high: 55.78, low: 55.76, close: 55.78, volume: 16670, }, { date: 1460734740000, open: 55.78, high: 55.79, low: 55.77, close: 55.7701, volume: 26069, }, { date: 1460734800000, open: 55.78, high: 55.78, low: 55.71, close: 55.72, volume: 49897, }, { date: 1460734860000, open: 55.72, high: 55.75, low: 55.72, close: 55.75, volume: 30420, }, { date: 1460734920000, open: 55.74, high: 55.75, low: 55.7, close: 55.72, volume: 93811, }, { date: 1460734980000, open: 55.72, high: 55.725, low: 55.7, close: 55.715, volume: 15592, }, { date: 1460735040000, open: 55.72, high: 55.74, low: 55.71, close: 55.73, volume: 18233, }, { date: 1460735100000, open: 55.73, high: 55.74, low: 55.715, close: 55.729, volume: 30073, }, { date: 1460735160000, open: 55.725, high: 55.725, low: 55.7, close: 55.71, volume: 19944, }, { date: 1460735220000, open: 55.71, high: 55.73, low: 55.7, close: 55.71, volume: 16861, }, { date: 1460735280000, open: 55.7016, high: 55.71, low: 55.7, close: 55.7001, volume: 6762, }, { date: 1460735340000, open: 55.7, high: 55.71, low: 55.68, close: 55.71, volume: 48130, }, { date: 1460735400000, open: 55.71, high: 55.73, low: 55.7, close: 55.7, volume: 22150, }, { date: 1460735460000, open: 55.7, high: 55.73, low: 55.7, close: 55.72, volume: 30198, }, { date: 1460735520000, open: 55.73, high: 55.73, low: 55.69, close: 55.705, volume: 53952, }, { date: 1460735580000, open: 55.71, high: 55.71, low: 55.69, close: 55.7, volume: 17930, }, { date: 1460735640000, open: 55.7, high: 55.72, low: 55.7, close: 55.705, volume: 25918, }, { date: 1460735700000, open: 55.7, high: 55.73, low: 55.7, close: 55.72, volume: 11680, }, { date: 1460735760000, open: 55.72, high: 55.74, low: 55.72, close: 55.735, volume: 28507, }, { date: 1460735820000, open: 55.735, high: 55.74, low: 55.73, close: 55.7338, volume: 36401, }, { date: 1460735880000, open: 55.7375, high: 55.75, low: 55.73, close: 55.7437, volume: 26757, }, { date: 1460735940000, open: 55.745, high: 55.765, low: 55.74, close: 55.74, volume: 72533, }, { date: 1460736000000, open: 55.74, high: 55.75, low: 55.73, close: 55.73, volume: 28112, }, { date: 1460736060000, open: 55.74, high: 55.74, low: 55.7, close: 55.72, volume: 38786, }, { date: 1460736120000, open: 55.7102, high: 55.725, low: 55.71, close: 55.71, volume: 18429, }, { date: 1460736180000, open: 55.72, high: 55.72, low: 55.71, close: 55.72, volume: 7367, }, { date: 1460736240000, open: 55.7133, high: 55.75, low: 55.7133, close: 55.748, volume: 26052, }, { date: 1460736300000, open: 55.75, high: 55.75, low: 55.7237, close: 55.74, volume: 24857, }, { date: 1460736360000, open: 55.73, high: 55.74, low: 55.71, close: 55.74, volume: 25377, }, { date: 1460736420000, open: 55.74, high: 55.75, low: 55.74, close: 55.75, volume: 11160, }, { date: 1460736480000, open: 55.75, high: 55.766, low: 55.75, close: 55.755, volume: 12313, }, { date: 1460736540000, open: 55.755, high: 55.767, low: 55.75, close: 55.76, volume: 15584, }, { date: 1460736600000, open: 55.76, high: 55.765, low: 55.75, close: 55.76, volume: 5764, }, { date: 1460736660000, open: 55.76, high: 55.78, low: 55.7536, close: 55.78, volume: 22609, }, { date: 1460736720000, open: 55.78, high: 55.78, low: 55.75, close: 55.775, volume: 15464, }, { date: 1460736780000, open: 55.78, high: 55.78, low: 55.77, close: 55.78, volume: 13828, }, { date: 1460736840000, open: 55.7725, high: 55.79, low: 55.77, close: 55.7842, volume: 12254, }, { date: 1460736900000, open: 55.78, high: 55.79, low: 55.74, close: 55.75, volume: 76668, }, { date: 1460736960000, open: 55.7463, high: 55.7463, low: 55.72, close: 55.72, volume: 21869, }, { date: 1460737020000, open: 55.7201, high: 55.73, low: 55.71, close: 55.715, volume: 14693, }, { date: 1460737080000, open: 55.72, high: 55.74, low: 55.71, close: 55.74, volume: 14922, }, { date: 1460737140000, open: 55.7463, high: 55.77, low: 55.7463, close: 55.755, volume: 30150, }, { date: 1460737200000, open: 55.76, high: 55.76, low: 55.75, close: 55.755, volume: 7917, }, { date: 1460737260000, open: 55.75, high: 55.78, low: 55.74, close: 55.77, volume: 64271, }, { date: 1460737320000, open: 55.775, high: 55.795, low: 55.77, close: 55.79, volume: 34924, }, { date: 1460737380000, open: 55.79, high: 55.81, low: 55.79, close: 55.805, volume: 73760, }, { date: 1460737440000, open: 55.8, high: 55.8037, low: 55.775, close: 55.7801, volume: 35494, }, { date: 1460737500000, open: 55.785, high: 55.79, low: 55.773, close: 55.775, volume: 7364, }, { date: 1460737560000, open: 55.77, high: 55.77, low: 55.74, close: 55.765, volume: 35966, }, { date: 1460737620000, open: 55.77, high: 55.77, low: 55.75, close: 55.755, volume: 11259, }, { date: 1460737680000, open: 55.755, high: 55.76, low: 55.75, close: 55.755, volume: 31265, }, { date: 1460737740000, open: 55.755, high: 55.77, low: 55.75, close: 55.765, volume: 43110, }, { date: 1460737800000, open: 55.77, high: 55.77, low: 55.75, close: 55.76, volume: 16859, }, { date: 1460737860000, open: 55.76, high: 55.76, low: 55.75, close: 55.7538, volume: 3623, }, { date: 1460737920000, open: 55.7563, high: 55.76, low: 55.745, close: 55.755, volume: 44887, }, { date: 1460737980000, open: 55.755, high: 55.77, low: 55.745, close: 55.765, volume: 30736, }, { date: 1460738040000, open: 55.76, high: 55.77, low: 55.75, close: 55.755, volume: 16859, }, { date: 1460738100000, open: 55.75, high: 55.77, low: 55.74, close: 55.77, volume: 16519, }, { date: 1460738160000, open: 55.77, high: 55.77, low: 55.695, close: 55.7199, volume: 87759, }, { date: 1460738220000, open: 55.72, high: 55.74, low: 55.71, close: 55.71, volume: 21524, }, { date: 1460738280000, open: 55.72, high: 55.74, low: 55.715, close: 55.73, volume: 19310, }, { date: 1460738340000, open: 55.7201, high: 55.755, low: 55.72, close: 55.755, volume: 40372, }, { date: 1460738400000, open: 55.76, high: 55.76, low: 55.75, close: 55.75, volume: 32131, }, { date: 1460738460000, open: 55.75, high: 55.76, low: 55.7, close: 55.755, volume: 44963, }, { date: 1460738520000, open: 55.7563, high: 55.77, low: 55.7534, close: 55.76, volume: 16116, }, { date: 1460738580000, open: 55.76, high: 55.77, low: 55.7525, close: 55.77, volume: 6044, }, { date: 1460738640000, open: 55.765, high: 55.77, low: 55.745, close: 55.76, volume: 61363, }, { date: 1460738700000, open: 55.754, high: 55.76, low: 55.75, close: 55.75, volume: 19916, }, { date: 1460738760000, open: 55.7522, high: 55.7599, low: 55.75, close: 55.75, volume: 12675, }, { date: 1460738820000, open: 55.75, high: 55.76, low: 55.75, close: 55.75, volume: 2402, }, { date: 1460738880000, open: 55.755, high: 55.76, low: 55.75, close: 55.755, volume: 3900, }, { date: 1460738940000, open: 55.76, high: 55.76, low: 55.75, close: 55.75, volume: 5305, }, { date: 1460739000000, open: 55.75, high: 55.76, low: 55.75, close: 55.76, volume: 6120, }, { date: 1460739060000, open: 55.7595, high: 55.76, low: 55.745, close: 55.75, volume: 52908, }, { date: 1460739120000, open: 55.7501, high: 55.76, low: 55.75, close: 55.755, volume: 7806, }, { date: 1460739180000, open: 55.75, high: 55.785, low: 55.75, close: 55.76, volume: 36102, }, { date: 1460739240000, open: 55.765, high: 55.77, low: 55.75, close: 55.755, volume: 11535, }, { date: 1460739300000, open: 55.75, high: 55.77, low: 55.74, close: 55.77, volume: 17805, }, { date: 1460739360000, open: 55.76, high: 55.78, low: 55.76, close: 55.7636, volume: 13668, }, { date: 1460739420000, open: 55.765, high: 55.78, low: 55.76, close: 55.77, volume: 12392, }, { date: 1460739480000, open: 55.78, high: 55.78, low: 55.77, close: 55.77, volume: 8073, }, { date: 1460739540000, open: 55.7775, high: 55.78, low: 55.7742, close: 55.775, volume: 4131, }, { date: 1460739600000, open: 55.78, high: 55.78, low: 55.75, close: 55.75, volume: 16266, }, { date: 1460739660000, open: 55.75, high: 55.76, low: 55.75, close: 55.75, volume: 6629, }, { date: 1460739720000, open: 55.7532, high: 55.76, low: 55.74, close: 55.74, volume: 67770, }, { date: 1460739780000, open: 55.7399, high: 55.7558, low: 55.72, close: 55.74, volume: 20604, }, { date: 1460739840000, open: 55.74, high: 55.76, low: 55.731, close: 55.731, volume: 32800, }, { date: 1460739900000, open: 55.73, high: 55.75, low: 55.7101, close: 55.7425, volume: 61179, }, { date: 1460739960000, open: 55.7499, high: 55.75, low: 55.73, close: 55.735, volume: 22530, }, { date: 1460740020000, open: 55.74, high: 55.76, low: 55.735, close: 55.75, volume: 20928, }, { date: 1460740080000, open: 55.75, high: 55.76, low: 55.745, close: 55.75, volume: 11024, }, { date: 1460740140000, open: 55.75, high: 55.76, low: 55.75, close: 55.7538, volume: 17719, }, { date: 1460740200000, open: 55.755, high: 55.76, low: 55.75, close: 55.75, volume: 36697, }, { date: 1460740260000, open: 55.7542, high: 55.759, low: 55.71, close: 55.71, volume: 27964, }, { date: 1460740320000, open: 55.714, high: 55.73, low: 55.71, close: 55.72, volume: 24343, }, { date: 1460740380000, open: 55.72, high: 55.73, low: 55.72, close: 55.72, volume: 4458, }, { date: 1460740440000, open: 55.72, high: 55.73, low: 55.72, close: 55.73, volume: 71217, }, { date: 1460740500000, open: 55.729, high: 55.73, low: 55.7101, close: 55.724, volume: 33675, }, { date: 1460740560000, open: 55.727, high: 55.75, low: 55.72, close: 55.75, volume: 23540, }, { date: 1460740620000, open: 55.755, high: 55.755, low: 55.72, close: 55.74, volume: 32343, }, { date: 1460740680000, open: 55.75, high: 55.75, low: 55.74, close: 55.745, volume: 7175, }, { date: 1460740740000, open: 55.74, high: 55.75, low: 55.7, close: 55.711, volume: 36916, }, { date: 1460740800000, open: 55.71, high: 55.72, low: 55.695, close: 55.715, volume: 33613, }, { date: 1460740860000, open: 55.715, high: 55.75, low: 55.71, close: 55.7499, volume: 21823, }, { date: 1460740920000, open: 55.74, high: 55.745, low: 55.72, close: 55.72, volume: 29141, }, { date: 1460740980000, open: 55.725, high: 55.7475, low: 55.725, close: 55.735, volume: 29176, }, { date: 1460741040000, open: 55.733, high: 55.75, low: 55.7327, close: 55.75, volume: 12172, }, { date: 1460741100000, open: 55.75, high: 55.75, low: 55.715, close: 55.715, volume: 25841, }, { date: 1460741160000, open: 55.714, high: 55.72, low: 55.71, close: 55.71, volume: 16999, }, { date: 1460741220000, open: 55.719, high: 55.72, low: 55.69, close: 55.71, volume: 88584, }, { date: 1460741280000, open: 55.71, high: 55.725, low: 55.7, close: 55.72, volume: 29875, }, { date: 1460741340000, open: 55.719, high: 55.72, low: 55.6901, close: 55.705, volume: 23123, }, { date: 1460741400000, open: 55.7, high: 55.72, low: 55.69, close: 55.715, volume: 41599, }, { date: 1460741460000, open: 55.71, high: 55.72, low: 55.66, close: 55.69, volume: 55270, }, { date: 1460741520000, open: 55.6911, high: 55.701, low: 55.68, close: 55.695, volume: 16582, }, { date: 1460741580000, open: 55.69, high: 55.71, low: 55.67, close: 55.67, volume: 30681, }, { date: 1460741640000, open: 55.67, high: 55.705, low: 55.665, close: 55.68, volume: 37554, }, { date: 1460741700000, open: 55.671, high: 55.71, low: 55.671, close: 55.69, volume: 31356, }, { date: 1460741760000, open: 55.69, high: 55.71, low: 55.68, close: 55.68, volume: 28225, }, { date: 1460741820000, open: 55.685, high: 55.7, low: 55.65, close: 55.65, volume: 21736, }, { date: 1460741880000, open: 55.6598, high: 55.66, low: 55.61, close: 55.6199, volume: 56264, }, { date: 1460741940000, open: 55.62, high: 55.62, low: 55.57, close: 55.579, volume: 36346, }, { date: 1460742000000, open: 55.58, high: 55.6, low: 55.5767, close: 55.6, volume: 27253, }, { date: 1460742060000, open: 55.5934, high: 55.64, low: 55.59, close: 55.64, volume: 31052, }, { date: 1460742120000, open: 55.63, high: 55.63, low: 55.605, close: 55.6236, volume: 23105, }, { date: 1460742180000, open: 55.625, high: 55.65, low: 55.61, close: 55.615, volume: 24097, }, { date: 1460742240000, open: 55.6161, high: 55.62, low: 55.6, close: 55.6, volume: 6892, }, { date: 1460742300000, open: 55.6, high: 55.605, low: 55.58, close: 55.6, volume: 16098, }, { date: 1460742360000, open: 55.5936, high: 55.61, low: 55.5801, close: 55.61, volume: 17518, }, { date: 1460742420000, open: 55.605, high: 55.61, low: 55.58, close: 55.58, volume: 25518, }, { date: 1460742480000, open: 55.58, high: 55.59, low: 55.565, close: 55.585, volume: 19398, }, { date: 1460742540000, open: 55.58, high: 55.59, low: 55.545, close: 55.545, volume: 27720, }, { date: 1460742600000, open: 55.54, high: 55.57, low: 55.54, close: 55.57, volume: 43048, }, { date: 1460742660000, open: 55.57, high: 55.58, low: 55.5409, close: 55.5438, volume: 26847, }, { date: 1460742720000, open: 55.5495, high: 55.55, low: 55.5301, close: 55.5301, volume: 29218, }, { date: 1460742780000, open: 55.535, high: 55.54, low: 55.51, close: 55.52, volume: 11017, }, { date: 1460742840000, open: 55.525, high: 55.535, low: 55.511, close: 55.52, volume: 20739, }, { date: 1460742900000, open: 55.51, high: 55.52, low: 55.5, close: 55.5099, volume: 32457, }, { date: 1460742960000, open: 55.51, high: 55.51, low: 55.5, close: 55.51, volume: 12391, }, { date: 1460743020000, open: 55.51, high: 55.5401, low: 55.51, close: 55.5101, volume: 37242, }, { date: 1460743080000, open: 55.52, high: 55.52, low: 55.49, close: 55.4901, volume: 27753, }, { date: 1460743140000, open: 55.49, high: 55.52, low: 55.49, close: 55.52, volume: 20905, }, { date: 1460743200000, open: 55.52, high: 55.53, low: 55.52, close: 55.52, volume: 9589, }, { date: 1460743260000, open: 55.5236, high: 55.56, low: 55.52, close: 55.54, volume: 20668, }, { date: 1460743320000, open: 55.54, high: 55.57, low: 55.54, close: 55.5536, volume: 17312, }, { date: 1460743380000, open: 55.56, high: 55.57, low: 55.55, close: 55.55, volume: 24005, }, { date: 1460743440000, open: 55.555, high: 55.56, low: 55.5, close: 55.52, volume: 57296, }, { date: 1460743500000, open: 55.54, high: 55.548, low: 55.51, close: 55.535, volume: 25071, }, { date: 1460743560000, open: 55.535, high: 55.535, low: 55.5, close: 55.505, volume: 16997, }, { date: 1460743620000, open: 55.5, high: 55.5262, low: 55.5, close: 55.515, volume: 20828, }, { date: 1460743680000, open: 55.51, high: 55.52, low: 55.51, close: 55.5191, volume: 5792, }, { date: 1460743740000, open: 55.51, high: 55.52, low: 55.51, close: 55.515, volume: 9544, }, { date: 1460743800000, open: 55.5101, high: 55.54, low: 55.51, close: 55.52, volume: 17847, }, { date: 1460743860000, open: 55.525, high: 55.53, low: 55.5, close: 55.51, volume: 16255, }, { date: 1460743920000, open: 55.5136, high: 55.515, low: 55.48, close: 55.48, volume: 33330, }, { date: 1460743980000, open: 55.48, high: 55.48, low: 55.46, close: 55.475, volume: 31257, }, { date: 1460744040000, open: 55.48, high: 55.49, low: 55.47, close: 55.47, volume: 16644, }, { date: 1460744100000, open: 55.47, high: 55.477, low: 55.44, close: 55.4401, volume: 20320, }, { date: 1460744160000, open: 55.44, high: 55.469, low: 55.44, close: 55.46, volume: 20978, }, { date: 1460744220000, open: 55.461, high: 55.469, low: 55.45, close: 55.4536, volume: 265104, }, { date: 1460744280000, open: 55.46, high: 55.46, low: 55.44, close: 55.445, volume: 22198, }, { date: 1460744340000, open: 55.44, high: 55.45, low: 55.43, close: 55.44, volume: 58311, }, { date: 1460744400000, open: 55.43, high: 55.43, low: 55.4, close: 55.42, volume: 52215, }, { date: 1460744460000, open: 55.429, high: 55.445, low: 55.42, close: 55.44, volume: 42145, }, { date: 1460744520000, open: 55.435, high: 55.44, low: 55.42, close: 55.44, volume: 16951, }, { date: 1460744580000, open: 55.43, high: 55.45, low: 55.43, close: 55.445, volume: 48127, }, { date: 1460744640000, open: 55.45, high: 55.46, low: 55.44, close: 55.4464, volume: 64815, }, { date: 1460744700000, open: 55.45, high: 55.46, low: 55.44, close: 55.445, volume: 30209, }, { date: 1460744760000, open: 55.45, high: 55.46, low: 55.44, close: 55.46, volume: 23950, }, { date: 1460744820000, open: 55.455, high: 55.48, low: 55.45, close: 55.46, volume: 42372, }, { date: 1460744880000, open: 55.46, high: 55.49, low: 55.45, close: 55.48, volume: 73712, }, { date: 1460744940000, open: 55.48, high: 55.49, low: 55.47, close: 55.485, volume: 50119, }, { date: 1460745000000, open: 55.49, high: 55.53, low: 55.49, close: 55.5229, volume: 28484, }, { date: 1460745060000, open: 55.525, high: 55.525, low: 55.49, close: 55.5, volume: 54757, }, { date: 1460745120000, open: 55.495, high: 55.5, low: 55.49, close: 55.495, volume: 40185, }, { date: 1460745180000, open: 55.49, high: 55.5, low: 55.47, close: 55.47, volume: 16042, }, { date: 1460745240000, open: 55.47, high: 55.49, low: 55.47, close: 55.48, volume: 34317, }, { date: 1460745300000, open: 55.48, high: 55.5, low: 55.48, close: 55.485, volume: 83771, }, { date: 1460745360000, open: 55.485, high: 55.49, low: 55.48, close: 55.485, volume: 39556, }, { date: 1460745420000, open: 55.485, high: 55.49, low: 55.45, close: 55.45, volume: 45505, }, { date: 1460745480000, open: 55.45, high: 55.46, low: 55.44, close: 55.44, volume: 12731, }, { date: 1460745540000, open: 55.45, high: 55.495, low: 55.445, close: 55.495, volume: 51526, }, { date: 1460745600000, open: 55.49, high: 55.4925, low: 55.47, close: 55.48, volume: 18165, }, { date: 1460745660000, open: 55.49, high: 55.5, low: 55.48, close: 55.48, volume: 12064, }, { date: 1460745720000, open: 55.49, high: 55.5, low: 55.48, close: 55.49, volume: 49205, }, { date: 1460745780000, open: 55.483, high: 55.5, low: 55.48, close: 55.485, volume: 21141, }, { date: 1460745840000, open: 55.49, high: 55.505, low: 55.48, close: 55.48, volume: 41447, }, { date: 1460745900000, open: 55.48, high: 55.49, low: 55.47, close: 55.47, volume: 20533, }, { date: 1460745960000, open: 55.4799, high: 55.48, low: 55.46, close: 55.46, volume: 14343, }, { date: 1460746020000, open: 55.465, high: 55.47, low: 55.46, close: 55.46, volume: 35474, }, { date: 1460746080000, open: 55.47, high: 55.47, low: 55.43, close: 55.44, volume: 23002, }, { date: 1460746140000, open: 55.44, high: 55.44, low: 55.41, close: 55.4299, volume: 64568, }, { date: 1460746200000, open: 55.4276, high: 55.4276, low: 55.41, close: 55.417, volume: 21730, }, { date: 1460746260000, open: 55.41, high: 55.415, low: 55.39, close: 55.4, volume: 63270, }, { date: 1460746320000, open: 55.405, high: 55.41, low: 55.4, close: 55.404, volume: 17176, }, { date: 1460746380000, open: 55.405, high: 55.4499, low: 55.4, close: 55.4475, volume: 53000, }, { date: 1460746440000, open: 55.445, high: 55.45, low: 55.425, close: 55.4301, volume: 25449, }, { date: 1460746500000, open: 55.44, high: 55.475, low: 55.433, close: 55.4573, volume: 40570, }, { date: 1460746560000, open: 55.46, high: 55.46, low: 55.41, close: 55.42, volume: 54507, }, { date: 1460746620000, open: 55.42, high: 55.43, low: 55.41, close: 55.41, volume: 19502, }, { date: 1460746680000, open: 55.41, high: 55.42, low: 55.405, close: 55.41, volume: 34054, }, { date: 1460746740000, open: 55.4165, high: 55.43, low: 55.41, close: 55.4236, volume: 33940, }, { date: 1460746800000, open: 55.425, high: 55.47, low: 55.42, close: 55.47, volume: 64408, }, { date: 1460746860000, open: 55.467, high: 55.48, low: 55.44, close: 55.45, volume: 62311, }, { date: 1460746920000, open: 55.45, high: 55.47, low: 55.45, close: 55.46, volume: 45133, }, { date: 1460746980000, open: 55.46, high: 55.48, low: 55.43, close: 55.48, volume: 59113, }, { date: 1460747040000, open: 55.47, high: 55.48, low: 55.45, close: 55.4564, volume: 53308, }, { date: 1460747100000, open: 55.45, high: 55.49, low: 55.45, close: 55.485, volume: 36947, }, { date: 1460747160000, open: 55.49, high: 55.5, low: 55.48, close: 55.49, volume: 53319, }, { date: 1460747220000, open: 55.5, high: 55.5, low: 55.48, close: 55.4901, volume: 20541, }, { date: 1460747280000, open: 55.495, high: 55.5, low: 55.42, close: 55.4411, volume: 63147, }, { date: 1460747340000, open: 55.44, high: 55.46, low: 55.43, close: 55.44, volume: 26645, }, { date: 1460747400000, open: 55.44, high: 55.4599, low: 55.44, close: 55.44, volume: 16121, }, { date: 1460747460000, open: 55.44, high: 55.45, low: 55.43, close: 55.43, volume: 33631, }, { date: 1460747520000, open: 55.43, high: 55.46, low: 55.42, close: 55.4599, volume: 27232, }, { date: 1460747580000, open: 55.455, high: 55.48, low: 55.455, close: 55.465, volume: 39002, }, { date: 1460747640000, open: 55.465, high: 55.47, low: 55.46, close: 55.465, volume: 12293, }, { date: 1460747700000, open: 55.46, high: 55.46, low: 55.42, close: 55.4598, volume: 39589, }, { date: 1460747760000, open: 55.4532, high: 55.4899, low: 55.4532, close: 55.4801, volume: 28729, }, { date: 1460747820000, open: 55.4801, high: 55.49, low: 55.48, close: 55.49, volume: 6677, }, { date: 1460747880000, open: 55.49, high: 55.52, low: 55.49, close: 55.515, volume: 23552, }, { date: 1460747940000, open: 55.52, high: 55.525, low: 55.5, close: 55.51, volume: 27412, }, { date: 1460748000000, open: 55.51, high: 55.52, low: 55.51, close: 55.515, volume: 11199, }, { date: 1460748060000, open: 55.51, high: 55.535, low: 55.5, close: 55.5064, volume: 37314, }, { date: 1460748120000, open: 55.51, high: 55.53, low: 55.51, close: 55.5101, volume: 22990, }, { date: 1460748180000, open: 55.5173, high: 55.555, low: 55.51, close: 55.54, volume: 60492, }, { date: 1460748240000, open: 55.545, high: 55.56, low: 55.52, close: 55.55, volume: 81049, }, { date: 1460748300000, open: 55.54, high: 55.56, low: 55.53, close: 55.545, volume: 23395, }, { date: 1460748360000, open: 55.545, high: 55.55, low: 55.54, close: 55.545, volume: 7124, }, { date: 1460748420000, open: 55.54, high: 55.55, low: 55.54, close: 55.55, volume: 35507, }, { date: 1460748480000, open: 55.55, high: 55.56, low: 55.524, close: 55.55, volume: 82111, }, { date: 1460748540000, open: 55.54, high: 55.55, low: 55.54, close: 55.549, volume: 7804, }, { date: 1460748600000, open: 55.545, high: 55.555, low: 55.52, close: 55.535, volume: 36287, }, { date: 1460748660000, open: 55.54, high: 55.555, low: 55.53, close: 55.535, volume: 35133, }, { date: 1460748720000, open: 55.53, high: 55.56, low: 55.53, close: 55.55, volume: 49918, }, { date: 1460748780000, open: 55.55, high: 55.565, low: 55.54, close: 55.56, volume: 144382, }, { date: 1460748840000, open: 55.555, high: 55.56, low: 55.54, close: 55.5499, volume: 15528, }, { date: 1460748900000, open: 55.5436, high: 55.56, low: 55.54, close: 55.5599, volume: 33980, }, { date: 1460748960000, open: 55.56, high: 55.57, low: 55.55, close: 55.56, volume: 80931, }, { date: 1460749020000, open: 55.555, high: 55.6, low: 55.555, close: 55.5863, volume: 56142, }, { date: 1460749080000, open: 55.5875, high: 55.59, low: 55.56, close: 55.575, volume: 23633, }, { date: 1460749140000, open: 55.57, high: 55.6375, low: 55.57, close: 55.625, volume: 185693, }, { date: 1460749200000, open: 55.63, high: 55.63, low: 55.6, close: 55.615, volume: 30476, }, { date: 1460749260000, open: 55.61, high: 55.61, low: 55.59, close: 55.595, volume: 35781, }, { date: 1460749320000, open: 55.59, high: 55.6269, low: 55.58, close: 55.605, volume: 71387, }, { date: 1460749380000, open: 55.61, high: 55.63, low: 55.61, close: 55.61, volume: 92427, }, { date: 1460749440000, open: 55.615, high: 55.625, low: 55.61, close: 55.615, volume: 131056, }, { date: 1460749500000, open: 55.62, high: 55.63, low: 55.61, close: 55.61, volume: 104309, }, { date: 1460749560000, open: 55.615, high: 55.625, low: 55.61, close: 55.615, volume: 85079, }, { date: 1460749620000, open: 55.615, high: 55.62, low: 55.61, close: 55.6105, volume: 42629, }, { date: 1460749680000, open: 55.615, high: 55.62, low: 55.58, close: 55.5999, volume: 110449, }, { date: 1460749740000, open: 55.6, high: 55.62, low: 55.595, close: 55.595, volume: 111647, }, { date: 1460749800000, open: 55.595, high: 55.65, low: 55.59, close: 55.62, volume: 182012, }, { date: 1460749860000, open: 55.62, high: 55.63, low: 55.61, close: 55.62, volume: 145177, }, { date: 1460749920000, open: 55.62, high: 55.64, low: 55.61, close: 55.63, volume: 116990, }, { date: 1460749980000, open: 55.64, high: 55.64, low: 55.61, close: 55.63, volume: 82011, }, { date: 1460750040000, open: 55.63, high: 55.63, low: 55.62, close: 55.63, volume: 45767, }, { date: 1460750100000, open: 55.62, high: 55.64, low: 55.62, close: 55.625, volume: 141808, }, { date: 1460750160000, open: 55.63, high: 55.655, low: 55.625, close: 55.65, volume: 133620, }, { date: 1460750220000, open: 55.65, high: 55.68, low: 55.64, close: 55.67, volume: 206923, }, { date: 1460750280000, open: 55.66, high: 55.67, low: 55.65, close: 55.6601, volume: 313716, }, { date: 1460750340000, open: 55.66, high: 55.665, low: 55.63, close: 55.65, volume: 240576, }, { date: 1460750400000, open: 55.645, high: 55.69, low: 55.64, close: 55.65, volume: 3778057, }, { date: 1460986200000, open: 55.49, high: 55.49, low: 55.21, close: 55.3, volume: 270788, }, { date: 1460986260000, open: 55.45, high: 55.48, low: 55.34, close: 55.39, volume: 58905, }, { date: 1460986320000, open: 55.39, high: 55.475, low: 55.37, close: 55.4538, volume: 65194, }, { date: 1460986380000, open: 55.46, high: 55.46, low: 55.39, close: 55.43, volume: 67970, }, { date: 1460986440000, open: 55.42, high: 55.45, low: 55.41, close: 55.445, volume: 43356, }, { date: 1460986500000, open: 55.445, high: 55.46, low: 55.42, close: 55.445, volume: 82079, }, { date: 1460986560000, open: 55.4462, high: 55.49, low: 55.41, close: 55.4701, volume: 123780, }, { date: 1460986620000, open: 55.4799, high: 55.51, low: 55.4746, close: 55.485, volume: 57080, }, { date: 1460986680000, open: 55.48, high: 55.53, low: 55.48, close: 55.52, volume: 22014, }, { date: 1460986740000, open: 55.53, high: 55.55, low: 55.52, close: 55.54, volume: 38135, }, { date: 1460986800000, open: 55.55, high: 55.63, low: 55.54, close: 55.63, volume: 51032, }, { date: 1460986860000, open: 55.63, high: 55.675, low: 55.59, close: 55.675, volume: 80029, }, { date: 1460986920000, open: 55.67, high: 55.7, low: 55.62, close: 55.63, volume: 76629, }, { date: 1460986980000, open: 55.63, high: 55.63, low: 55.5601, close: 55.5601, volume: 30191, }, { date: 1460987040000, open: 55.56, high: 55.58, low: 55.56, close: 55.57, volume: 21733, }, { date: 1460987100000, open: 55.57, high: 55.58, low: 55.56, close: 55.565, volume: 17624, }, { date: 1460987160000, open: 55.565, high: 55.57, low: 55.56, close: 55.5642, volume: 27449, }, { date: 1460987220000, open: 55.56, high: 55.57, low: 55.55, close: 55.56, volume: 31745, }, { date: 1460987280000, open: 55.55, high: 55.55, low: 55.51, close: 55.5411, volume: 27868, }, { date: 1460987340000, open: 55.54, high: 55.59, low: 55.53, close: 55.57, volume: 40708, }, { date: 1460987400000, open: 55.579, high: 55.61, low: 55.57, close: 55.6, volume: 32551, }, { date: 1460987460000, open: 55.6, high: 55.6299, low: 55.6, close: 55.62, volume: 36765, }, { date: 1460987520000, open: 55.62, high: 55.65, low: 55.61, close: 55.65, volume: 23649, }, { date: 1460987580000, open: 55.65, high: 55.66, low: 55.64, close: 55.65, volume: 22917, }, { date: 1460987640000, open: 55.655, high: 55.7, low: 55.65, close: 55.6858, volume: 38728, }, { date: 1460987700000, open: 55.685, high: 55.7199, low: 55.68, close: 55.7058, volume: 30410, }, { date: 1460987760000, open: 55.7, high: 55.74, low: 55.69, close: 55.695, volume: 54503, }, { date: 1460987820000, open: 55.6911, high: 55.7068, low: 55.685, close: 55.69, volume: 27636, }, { date: 1460987880000, open: 55.69, high: 55.72, low: 55.68, close: 55.72, volume: 26115, }, { date: 1460987940000, open: 55.72, high: 55.7264, low: 55.7, close: 55.71, volume: 19644, }, { date: 1460988000000, open: 55.72, high: 55.745, low: 55.71, close: 55.71, volume: 26334, }, { date: 1460988060000, open: 55.7, high: 55.74, low: 55.7, close: 55.71, volume: 25794, }, { date: 1460988120000, open: 55.7, high: 55.83, low: 55.7, close: 55.8199, volume: 78858, }, { date: 1460988180000, open: 55.8168, high: 55.85, low: 55.81, close: 55.835, volume: 31849, }, { date: 1460988240000, open: 55.83, high: 55.85, low: 55.83, close: 55.85, volume: 32397, }, { date: 1460988300000, open: 55.85, high: 55.92, low: 55.8436, close: 55.92, volume: 51100, }, { date: 1460988360000, open: 55.918, high: 55.95, low: 55.91, close: 55.95, volume: 56398, }, { date: 1460988420000, open: 55.945, high: 55.95, low: 55.92, close: 55.93, volume: 38569, }, { date: 1460988480000, open: 55.92, high: 55.93, low: 55.9, close: 55.93, volume: 34432, }, { date: 1460988540000, open: 55.93, high: 55.94, low: 55.8699, close: 55.8868, volume: 43117, }, { date: 1460988600000, open: 55.88, high: 55.92, low: 55.88, close: 55.9, volume: 58729, }, { date: 1460988660000, open: 55.9, high: 55.92, low: 55.8926, close: 55.92, volume: 19081, }, { date: 1460988720000, open: 55.92, high: 55.93, low: 55.9, close: 55.92, volume: 21646, }, { date: 1460988780000, open: 55.916, high: 55.95, low: 55.9, close: 55.94, volume: 29451, }, { date: 1460988840000, open: 55.94, high: 55.96, low: 55.93, close: 55.96, volume: 62529, }, { date: 1460988900000, open: 55.96, high: 56, low: 55.95, close: 55.985, volume: 59612, }, { date: 1460988960000, open: 55.989, high: 56, low: 55.97, close: 55.9868, volume: 138666, }, { date: 1460989020000, open: 55.985, high: 55.99, low: 55.965, close: 55.97, volume: 30481, }, { date: 1460989080000, open: 55.97, high: 55.97, low: 55.94, close: 55.95, volume: 41621, }, { date: 1460989140000, open: 55.95, high: 55.97, low: 55.94, close: 55.96, volume: 64080, }, { date: 1460989200000, open: 55.95, high: 55.9655, low: 55.95, close: 55.96, volume: 32541, }, { date: 1460989260000, open: 55.955, high: 55.99, low: 55.95, close: 55.9835, volume: 27999, }, { date: 1460989320000, open: 55.985, high: 55.995, low: 55.9736, close: 55.98, volume: 84496, }, { date: 1460989380000, open: 55.97, high: 55.9799, low: 55.97, close: 55.975, volume: 7630, }, { date: 1460989440000, open: 55.97, high: 55.99, low: 55.97, close: 55.99, volume: 10872, }, { date: 1460989500000, open: 55.99, high: 56, low: 55.98, close: 55.99, volume: 39509, }, { date: 1460989560000, open: 55.9975, high: 56, low: 55.98, close: 55.98, volume: 63393, }, { date: 1460989620000, open: 55.9899, high: 56.04, low: 55.98, close: 55.995, volume: 329191, }, { date: 1460989680000, open: 55.99, high: 56.01, low: 55.99, close: 56, volume: 36167, }, { date: 1460989740000, open: 56, high: 56.01, low: 55.98, close: 55.99, volume: 41782, }, { date: 1460989800000, open: 55.98, high: 56.02, low: 55.98, close: 56.01, volume: 53693, }, { date: 1460989860000, open: 56.02, high: 56.045, low: 56, close: 56.03, volume: 67778, }, { date: 1460989920000, open: 56.03, high: 56.04, low: 56.0001, close: 56.04, volume: 58847, }, { date: 1460989980000, open: 56.03, high: 56.08, low: 56.03, close: 56.07, volume: 40345, }, { date: 1460990040000, open: 56.08, high: 56.13, low: 56.07, close: 56.1211, volume: 38577, }, { date: 1460990100000, open: 56.12, high: 56.16, low: 56.12, close: 56.15, volume: 72715, }, { date: 1460990160000, open: 56.16, high: 56.18, low: 56.12, close: 56.16, volume: 87282, }, { date: 1460990220000, open: 56.16, high: 56.165, low: 56.14, close: 56.14, volume: 80672, }, { date: 1460990280000, open: 56.14, high: 56.145, low: 56.11, close: 56.125, volume: 39832, }, { date: 1460990340000, open: 56.125, high: 56.14, low: 56.12, close: 56.12, volume: 57424, }, { date: 1460990400000, open: 56.127, high: 56.16, low: 56.125, close: 56.138, volume: 21267, }, { date: 1460990460000, open: 56.13, high: 56.16, low: 56.125, close: 56.155, volume: 29140, }, { date: 1460990520000, open: 56.15, high: 56.18, low: 56.15, close: 56.18, volume: 41178, }, { date: 1460990580000, open: 56.18, high: 56.2, low: 56.179, close: 56.185, volume: 31736, }, { date: 1460990640000, open: 56.18, high: 56.19, low: 56.16, close: 56.1601, volume: 19799, }, { date: 1460990700000, open: 56.16, high: 56.18, low: 56.15, close: 56.17, volume: 53005, }, { date: 1460990760000, open: 56.17, high: 56.18, low: 56.165, close: 56.175, volume: 18626, }, { date: 1460990820000, open: 56.1799, high: 56.19, low: 56.17, close: 56.1801, volume: 44761, }, { date: 1460990880000, open: 56.1866, high: 56.1866, low: 56.17, close: 56.17, volume: 34570, }, { date: 1460990940000, open: 56.18, high: 56.19, low: 56.17, close: 56.18, volume: 18372, }, { date: 1460991000000, open: 56.19, high: 56.2, low: 56.17, close: 56.1701, volume: 42659, }, { date: 1460991060000, open: 56.17, high: 56.18, low: 56.145, close: 56.17, volume: 71086, }, { date: 1460991120000, open: 56.16, high: 56.2, low: 56.16, close: 56.18, volume: 44787, }, { date: 1460991180000, open: 56.18, high: 56.22, low: 56.18, close: 56.2165, volume: 34863, }, { date: 1460991240000, open: 56.22, high: 56.25, low: 56.22, close: 56.235, volume: 33919, }, { date: 1460991300000, open: 56.235, high: 56.25, low: 56.19, close: 56.2172, volume: 100072, }, { date: 1460991360000, open: 56.215, high: 56.24, low: 56.21, close: 56.23, volume: 37995, }, { date: 1460991420000, open: 56.2248, high: 56.24, low: 56.205, close: 56.205, volume: 43771, }, { date: 1460991480000, open: 56.2099, high: 56.21, low: 56.18, close: 56.18, volume: 40667, }, { date: 1460991540000, open: 56.1848, high: 56.185, low: 56.1516, close: 56.175, volume: 29099, }, { date: 1460991600000, open: 56.18, high: 56.19, low: 56.17, close: 56.1825, volume: 28612, }, { date: 1460991660000, open: 56.19, high: 56.19, low: 56.15, close: 56.15, volume: 49266, }, { date: 1460991720000, open: 56.15, high: 56.16, low: 56.15, close: 56.15, volume: 30963, }, { date: 1460991780000, open: 56.16, high: 56.16, low: 56.14, close: 56.145, volume: 48859, }, { date: 1460991840000, open: 56.14, high: 56.15, low: 56.13, close: 56.14, volume: 29776, }, { date: 1460991900000, open: 56.13, high: 56.15, low: 56.13, close: 56.13, volume: 49491, }, { date: 1460991960000, open: 56.13, high: 56.14, low: 56.13, close: 56.135, volume: 42261, }, { date: 1460992020000, open: 56.1334, high: 56.15, low: 56.13, close: 56.14, volume: 32474, }, { date: 1460992080000, open: 56.14, high: 56.15, low: 56.13, close: 56.13, volume: 18957, }, { date: 1460992140000, open: 56.13, high: 56.137, low: 56.1, close: 56.1, volume: 52321, }, { date: 1460992200000, open: 56.1032, high: 56.115, low: 56.09, close: 56.104, volume: 79871, }, { date: 1460992260000, open: 56.1, high: 56.13, low: 56.1, close: 56.115, volume: 48988, }, { date: 1460992320000, open: 56.12, high: 56.13, low: 56.115, close: 56.1265, volume: 10396, }, { date: 1460992380000, open: 56.1265, high: 56.16, low: 56.125, close: 56.15, volume: 44327, }, { date: 1460992440000, open: 56.16, high: 56.16, low: 56.12, close: 56.1235, volume: 18012, }, { date: 1460992500000, open: 56.12, high: 56.13, low: 56.11, close: 56.11, volume: 22657, }, { date: 1460992560000, open: 56.11, high: 56.13, low: 56.11, close: 56.13, volume: 10510, }, { date: 1460992620000, open: 56.12, high: 56.15, low: 56.12, close: 56.13, volume: 44488, }, { date: 1460992680000, open: 56.126, high: 56.126, low: 56.11, close: 56.11, volume: 8256, }, { date: 1460992740000, open: 56.11, high: 56.13, low: 56.0801, close: 56.085, volume: 43387, }, { date: 1460992800000, open: 56.09, high: 56.1049, low: 56.07, close: 56.07, volume: 50264, }, { date: 1460992860000, open: 56.08, high: 56.09, low: 56.06, close: 56.0701, volume: 62275, }, { date: 1460992920000, open: 56.0799, high: 56.11, low: 56.07, close: 56.1, volume: 54334, }, { date: 1460992980000, open: 56.09, high: 56.09, low: 56.06, close: 56.06, volume: 16296, }, { date: 1460993040000, open: 56.06, high: 56.07, low: 56.05, close: 56.06, volume: 39992, }, { date: 1460993100000, open: 56.055, high: 56.065, low: 56.01, close: 56.01, volume: 64545, }, { date: 1460993160000, open: 56.01, high: 56.02, low: 56, close: 56.005, volume: 77415, }, { date: 1460993220000, open: 56.01, high: 56.01, low: 56, close: 56, volume: 21017, }, { date: 1460993280000, open: 56.01, high: 56.03, low: 55.98, close: 56.03, volume: 60651, }, { date: 1460993340000, open: 56.021, high: 56.04, low: 56.01, close: 56.01, volume: 22350, }, { date: 1460993400000, open: 56, high: 56.06, low: 56, close: 56.05, volume: 47573, }, { date: 1460993460000, open: 56.05, high: 56.11, low: 56.05, close: 56.0935, volume: 42417, }, { date: 1460993520000, open: 56.09, high: 56.0999, low: 56.06, close: 56.0799, volume: 34189, }, { date: 1460993580000, open: 56.08, high: 56.09, low: 56.0435, close: 56.05, volume: 46758, }, { date: 1460993640000, open: 56.05, high: 56.075, low: 56.05, close: 56.07, volume: 69316, }, { date: 1460993700000, open: 56.065, high: 56.0796, low: 56.05, close: 56.0796, volume: 50199, }, { date: 1460993760000, open: 56.075, high: 56.08, low: 56.06, close: 56.064, volume: 26574, }, { date: 1460993820000, open: 56.07, high: 56.07, low: 56.03, close: 56.06, volume: 35785, }, { date: 1460993880000, open: 56.06, high: 56.1, low: 56.06, close: 56.1, volume: 24029, }, { date: 1460993940000, open: 56.1, high: 56.1, low: 56.05, close: 56.065, volume: 22024, }, { date: 1460994000000, open: 56.06, high: 56.07, low: 56.0428, close: 56.07, volume: 58642, }, { date: 1460994060000, open: 56.07, high: 56.08, low: 56.05, close: 56.0672, volume: 23570, }, { date: 1460994120000, open: 56.07, high: 56.09, low: 56.06, close: 56.09, volume: 32126, }, { date: 1460994180000, open: 56.09, high: 56.1, low: 56.08, close: 56.095, volume: 13732, }, { date: 1460994240000, open: 56.0935, high: 56.1, low: 56.05, close: 56.09, volume: 47234, }, { date: 1460994300000, open: 56.08, high: 56.09, low: 56.0532, close: 56.06, volume: 11518, }, { date: 1460994360000, open: 56.07, high: 56.1, low: 56.07, close: 56.09, volume: 19550, }, { date: 1460994420000, open: 56.085, high: 56.09, low: 56.08, close: 56.08, volume: 8737, }, { date: 1460994480000, open: 56.09, high: 56.0995, low: 56.05, close: 56.065, volume: 43992, }, { date: 1460994540000, open: 56.0635, high: 56.075, low: 56.0235, close: 56.04, volume: 18030, }, { date: 1460994600000, open: 56.0399, high: 56.04, low: 56, close: 56.03, volume: 26820, }, { date: 1460994660000, open: 56.02, high: 56.03, low: 56.0101, close: 56.015, volume: 7122, }, { date: 1460994720000, open: 56.01, high: 56.05, low: 56.01, close: 56.0436, volume: 29858, }, { date: 1460994780000, open: 56.0451, high: 56.0451, low: 56, close: 56.0063, volume: 30678, }, { date: 1460994840000, open: 56, high: 56.03, low: 56, close: 56.025, volume: 13858, }, { date: 1460994900000, open: 56.02, high: 56.07, low: 56.02, close: 56.04, volume: 33944, }, { date: 1460994960000, open: 56.04, high: 56.0519, low: 56.03, close: 56.03, volume: 16239, }, { date: 1460995020000, open: 56.03, high: 56.05, low: 56.03, close: 56.0335, volume: 15579, }, { date: 1460995080000, open: 56.03, high: 56.06, low: 56.025, close: 56.055, volume: 24764, }, { date: 1460995140000, open: 56.0501, high: 56.06, low: 56.025, close: 56.04, volume: 18828, }, { date: 1460995200000, open: 56.03, high: 56.05, low: 56.0239, close: 56.05, volume: 15191, }, { date: 1460995260000, open: 56.05, high: 56.0899, low: 56.05, close: 56.0599, volume: 40171, }, { date: 1460995320000, open: 56.06, high: 56.07, low: 56.05, close: 56.06, volume: 12000, }, { date: 1460995380000, open: 56.06, high: 56.08, low: 56.0501, close: 56.071, volume: 14830, }, { date: 1460995440000, open: 56.079, high: 56.08, low: 56.07, close: 56.08, volume: 17185, }, { date: 1460995500000, open: 56.08, high: 56.11, low: 56.08, close: 56.11, volume: 28850, }, { date: 1460995560000, open: 56.1, high: 56.13, low: 56.1, close: 56.13, volume: 34962, }, { date: 1460995620000, open: 56.1201, high: 56.17, low: 56.11, close: 56.17, volume: 149012, }, { date: 1460995680000, open: 56.17, high: 56.19, low: 56.17, close: 56.18, volume: 72947, }, { date: 1460995740000, open: 56.175, high: 56.18, low: 56.15, close: 56.165, volume: 16322, }, { date: 1460995800000, open: 56.167, high: 56.18, low: 56.1545, close: 56.17, volume: 34863, }, { date: 1460995860000, open: 56.17, high: 56.18, low: 56.16, close: 56.18, volume: 11891, }, { date: 1460995920000, open: 56.18, high: 56.19, low: 56.16, close: 56.175, volume: 34462, }, { date: 1460995980000, open: 56.17, high: 56.19, low: 56.16, close: 56.185, volume: 16850, }, { date: 1460996040000, open: 56.1899, high: 56.23, low: 56.18, close: 56.23, volume: 88096, }, { date: 1460996100000, open: 56.2201, high: 56.23, low: 56.2099, close: 56.2099, volume: 31871, }, { date: 1460996160000, open: 56.2, high: 56.25, low: 56.2, close: 56.23, volume: 209826, }, { date: 1460996220000, open: 56.2201, high: 56.24, low: 56.22, close: 56.24, volume: 45955, }, { date: 1460996280000, open: 56.235, high: 56.25, low: 56.23, close: 56.245, volume: 208013, }, { date: 1460996340000, open: 56.25, high: 56.32, low: 56.24, close: 56.32, volume: 162639, }, { date: 1460996400000, open: 56.32, high: 56.33, low: 56.25, close: 56.259, volume: 62100, }, { date: 1460996460000, open: 56.25, high: 56.33, low: 56.25, close: 56.33, volume: 73381, }, { date: 1460996520000, open: 56.32, high: 56.37, low: 56.3, close: 56.37, volume: 129706, }, { date: 1460996580000, open: 56.37, high: 56.43, low: 56.35, close: 56.3735, volume: 268181, }, { date: 1460996640000, open: 56.38, high: 56.38, low: 56.33, close: 56.36, volume: 88070, }, { date: 1460996700000, open: 56.355, high: 56.355, low: 56.3271, close: 56.33, volume: 19425, }, { date: 1460996760000, open: 56.325, high: 56.34, low: 56.32, close: 56.33, volume: 40874, }, { date: 1460996820000, open: 56.325, high: 56.33, low: 56.32, close: 56.325, volume: 12245, }, { date: 1460996880000, open: 56.325, high: 56.38, low: 56.32, close: 56.38, volume: 57649, }, { date: 1460996940000, open: 56.38, high: 56.41, low: 56.38, close: 56.395, volume: 60674, }, { date: 1460997000000, open: 56.399, high: 56.4, low: 56.385, close: 56.3925, volume: 56405, }, { date: 1460997060000, open: 56.395, high: 56.46, low: 56.39, close: 56.46, volume: 88238, }, { date: 1460997120000, open: 56.451, high: 56.495, low: 56.45, close: 56.495, volume: 90524, }, { date: 1460997180000, open: 56.5, high: 56.59, low: 56.49, close: 56.545, volume: 202060, }, { date: 1460997240000, open: 56.555, high: 56.555, low: 56.46, close: 56.49, volume: 116820, }, { date: 1460997300000, open: 56.486, high: 56.51, low: 56.465, close: 56.5, volume: 111333, }, { date: 1460997360000, open: 56.5, high: 56.51, low: 56.44, close: 56.455, volume: 128108, }, { date: 1460997420000, open: 56.458, high: 56.49, low: 56.4535, close: 56.48, volume: 53836, }, { date: 1460997480000, open: 56.47, high: 56.54, low: 56.47, close: 56.53, volume: 134992, }, { date: 1460997540000, open: 56.5235, high: 56.54, low: 56.48, close: 56.515, volume: 132885, }, { date: 1460997600000, open: 56.5, high: 56.51, low: 56.49, close: 56.5, volume: 57421, }, { date: 1460997660000, open: 56.5, high: 56.52, low: 56.48, close: 56.4862, volume: 24080, }, { date: 1460997720000, open: 56.49, high: 56.49, low: 56.47, close: 56.47, volume: 77088, }, { date: 1460997780000, open: 56.48, high: 56.49, low: 56.44, close: 56.44, volume: 20787, }, { date: 1460997840000, open: 56.45, high: 56.5, low: 56.44, close: 56.48, volume: 60981, }, { date: 1460997900000, open: 56.48, high: 56.5, low: 56.47, close: 56.5, volume: 56260, }, { date: 1460997960000, open: 56.51, high: 56.52, low: 56.51, close: 56.5128, volume: 25027, }, { date: 1460998020000, open: 56.52, high: 56.52, low: 56.505, close: 56.51, volume: 17053, }, { date: 1460998080000, open: 56.51, high: 56.52, low: 56.5, close: 56.51, volume: 41443, }, { date: 1460998140000, open: 56.51, high: 56.52, low: 56.5, close: 56.51, volume: 34880, }, { date: 1460998200000, open: 56.5083, high: 56.51, low: 56.5, close: 56.504, volume: 41051, }, { date: 1460998260000, open: 56.505, high: 56.52, low: 56.485, close: 56.505, volume: 49762, }, { date: 1460998320000, open: 56.51, high: 56.51, low: 56.5, close: 56.51, volume: 8924, }, { date: 1460998380000, open: 56.5, high: 56.52, low: 56.49, close: 56.52, volume: 42532, }, { date: 1460998440000, open: 56.515, high: 56.52, low: 56.48, close: 56.49, volume: 163286, }, { date: 1460998500000, open: 56.49, high: 56.49, low: 56.45, close: 56.45, volume: 81007, }, { date: 1460998560000, open: 56.4435, high: 56.45, low: 56.43, close: 56.43, volume: 44923, }, { date: 1460998620000, open: 56.43, high: 56.435, low: 56.41, close: 56.41, volume: 30075, }, { date: 1460998680000, open: 56.41, high: 56.425, low: 56.4, close: 56.4, volume: 37495, }, { date: 1460998740000, open: 56.4, high: 56.4, low: 56.34, close: 56.35, volume: 33157, }, { date: 1460998800000, open: 56.34, high: 56.38, low: 56.34, close: 56.35, volume: 89200, }, { date: 1460998860000, open: 56.35, high: 56.36, low: 56.325, close: 56.35, volume: 54924, }, { date: 1460998920000, open: 56.36, high: 56.37, low: 56.35, close: 56.357, volume: 22218, }, { date: 1460998980000, open: 56.36, high: 56.36, low: 56.325, close: 56.33, volume: 19658, }, { date: 1460999040000, open: 56.33, high: 56.35, low: 56.33, close: 56.3458, volume: 25868, }, { date: 1460999100000, open: 56.34, high: 56.37, low: 56.33, close: 56.365, volume: 58927, }, { date: 1460999160000, open: 56.38, high: 56.41, low: 56.3735, close: 56.39, volume: 142714, }, { date: 1460999220000, open: 56.39, high: 56.395, low: 56.36, close: 56.36, volume: 40346, }, { date: 1460999280000, open: 56.36, high: 56.3699, low: 56.31, close: 56.31, volume: 40954, }, { date: 1460999340000, open: 56.32, high: 56.33, low: 56.31, close: 56.33, volume: 50317, }, { date: 1460999400000, open: 56.33, high: 56.35, low: 56.33, close: 56.33, volume: 22002, }, { date: 1460999460000, open: 56.34, high: 56.35, low: 56.32, close: 56.3264, volume: 31272, }, { date: 1460999520000, open: 56.33, high: 56.33, low: 56.32, close: 56.32, volume: 16131, }, { date: 1460999580000, open: 56.328, high: 56.345, low: 56.3, close: 56.3301, volume: 53834, }, { date: 1460999640000, open: 56.335, high: 56.34, low: 56.33, close: 56.33, volume: 7072, }, { date: 1460999700000, open: 56.34, high: 56.36, low: 56.33, close: 56.36, volume: 17697, }, { date: 1460999760000, open: 56.35, high: 56.35, low: 56.34, close: 56.35, volume: 14306, }, { date: 1460999820000, open: 56.35, high: 56.35, low: 56.34, close: 56.35, volume: 13504, }, { date: 1460999880000, open: 56.35, high: 56.35, low: 56.3, close: 56.3, volume: 54626, }, { date: 1460999940000, open: 56.31, high: 56.32, low: 56.26, close: 56.26, volume: 56341, }, { date: 1461000000000, open: 56.26, high: 56.27, low: 56.23, close: 56.26, volume: 37948, }, { date: 1461000060000, open: 56.2687, high: 56.29, low: 56.26, close: 56.27, volume: 25464, }, { date: 1461000120000, open: 56.27, high: 56.27, low: 56.24, close: 56.26, volume: 31896, }, { date: 1461000180000, open: 56.2531, high: 56.27, low: 56.25, close: 56.26, volume: 27317, }, { date: 1461000240000, open: 56.27, high: 56.27, low: 56.24, close: 56.25, volume: 60498, }, { date: 1461000300000, open: 56.24, high: 56.33, low: 56.23, close: 56.3, volume: 37487, }, { date: 1461000360000, open: 56.3, high: 56.31, low: 56.29, close: 56.305, volume: 18244, }, { date: 1461000420000, open: 56.3033, high: 56.31, low: 56.28, close: 56.305, volume: 35100, }, { date: 1461000480000, open: 56.3, high: 56.3, low: 56.28, close: 56.2936, volume: 20784, }, { date: 1461000540000, open: 56.3, high: 56.31, low: 56.29, close: 56.299, volume: 38103, }, { date: 1461000600000, open: 56.29, high: 56.3, low: 56.29, close: 56.3, volume: 24469, }, { date: 1461000660000, open: 56.29, high: 56.31, low: 56.275, close: 56.295, volume: 34621, }, { date: 1461000720000, open: 56.2959, high: 56.3, low: 56.27, close: 56.27, volume: 19882, }, { date: 1461000780000, open: 56.275, high: 56.28, low: 56.26, close: 56.27, volume: 23400, }, { date: 1461000840000, open: 56.28, high: 56.28, low: 56.26, close: 56.27, volume: 15174, }, { date: 1461000900000, open: 56.26, high: 56.3099, low: 56.26, close: 56.28, volume: 34956, }, { date: 1461000960000, open: 56.27, high: 56.32, low: 56.26, close: 56.32, volume: 49461, }, { date: 1461001020000, open: 56.31, high: 56.32, low: 56.29, close: 56.295, volume: 22548, }, { date: 1461001080000, open: 56.29, high: 56.32, low: 56.29, close: 56.3, volume: 60921, }, { date: 1461001140000, open: 56.3, high: 56.31, low: 56.28, close: 56.2936, volume: 59330, }, { date: 1461001200000, open: 56.29, high: 56.3336, low: 56.29, close: 56.3025, volume: 103631, }, { date: 1461001260000, open: 56.3, high: 56.3364, low: 56.3, close: 56.335, volume: 30494, }, { date: 1461001320000, open: 56.34, high: 56.41, low: 56.335, close: 56.4, volume: 47693, }, { date: 1461001380000, open: 56.4099, high: 56.43, low: 56.4, close: 56.42, volume: 17853, }, { date: 1461001440000, open: 56.43, high: 56.44, low: 56.42, close: 56.43, volume: 16207, }, { date: 1461001500000, open: 56.43, high: 56.45, low: 56.42, close: 56.45, volume: 31400, }, { date: 1461001560000, open: 56.4468, high: 56.45, low: 56.435, close: 56.44, volume: 23242, }, { date: 1461001620000, open: 56.4399, high: 56.48, low: 56.43, close: 56.47, volume: 35825, }, { date: 1461001680000, open: 56.46, high: 56.47, low: 56.43, close: 56.44, volume: 36558, }, { date: 1461001740000, open: 56.438, high: 56.44, low: 56.4265, close: 56.44, volume: 11117, }, { date: 1461001800000, open: 56.437, high: 56.44, low: 56.405, close: 56.43, volume: 19772, }, { date: 1461001860000, open: 56.4265, high: 56.44, low: 56.41, close: 56.4202, volume: 20578, }, { date: 1461001920000, open: 56.43, high: 56.43, low: 56.41, close: 56.425, volume: 22186, }, { date: 1461001980000, open: 56.43, high: 56.46, low: 56.425, close: 56.46, volume: 12008, }, { date: 1461002040000, open: 56.45, high: 56.47, low: 56.42, close: 56.44, volume: 80257, }, { date: 1461002100000, open: 56.4401, high: 56.46, low: 56.44, close: 56.46, volume: 10807, }, { date: 1461002160000, open: 56.46, high: 56.465, low: 56.45, close: 56.465, volume: 12739, }, { date: 1461002220000, open: 56.464, high: 56.47, low: 56.45, close: 56.45, volume: 18098, }, { date: 1461002280000, open: 56.457, high: 56.47, low: 56.4435, close: 56.4435, volume: 25815, }, { date: 1461002340000, open: 56.45, high: 56.46, low: 56.435, close: 56.45, volume: 15555, }, { date: 1461002400000, open: 56.445, high: 56.48, low: 56.445, close: 56.47, volume: 25537, }, { date: 1461002460000, open: 56.47, high: 56.47, low: 56.45, close: 56.465, volume: 70969, }, { date: 1461002520000, open: 56.46, high: 56.465, low: 56.44, close: 56.4501, volume: 39537, }, { date: 1461002580000, open: 56.455, high: 56.47, low: 56.45, close: 56.46, volume: 38495, }, { date: 1461002640000, open: 56.46, high: 56.5, low: 56.46, close: 56.47, volume: 84788, }, { date: 1461002700000, open: 56.4736, high: 56.5, low: 56.47, close: 56.495, volume: 34321, }, { date: 1461002760000, open: 56.495, high: 56.505, low: 56.47, close: 56.5, volume: 46669, }, { date: 1461002820000, open: 56.505, high: 56.51, low: 56.49, close: 56.495, volume: 23153, }, { date: 1461002880000, open: 56.4936, high: 56.515, low: 56.48, close: 56.511, volume: 38348, }, { date: 1461002940000, open: 56.5111, high: 56.52, low: 56.47, close: 56.47, volume: 32150, }, { date: 1461003000000, open: 56.47, high: 56.495, low: 56.44, close: 56.44, volume: 53397, }, { date: 1461003060000, open: 56.4501, high: 56.47, low: 56.45, close: 56.451, volume: 16570, }, { date: 1461003120000, open: 56.46, high: 56.46, low: 56.44, close: 56.45, volume: 18348, }, { date: 1461003180000, open: 56.45, high: 56.48, low: 56.45, close: 56.48, volume: 34150, }, { date: 1461003240000, open: 56.48, high: 56.49, low: 56.47, close: 56.47, volume: 16188, }, { date: 1461003300000, open: 56.4799, high: 56.5, low: 56.47, close: 56.49, volume: 25157, }, { date: 1461003360000, open: 56.49, high: 56.495, low: 56.47, close: 56.475, volume: 10535, }, { date: 1461003420000, open: 56.48, high: 56.48, low: 56.47, close: 56.475, volume: 26835, }, { date: 1461003480000, open: 56.47, high: 56.48, low: 56.45, close: 56.46, volume: 36938, }, { date: 1461003540000, open: 56.46, high: 56.49, low: 56.46, close: 56.4835, volume: 21754, }, { date: 1461003600000, open: 56.4838, high: 56.485, low: 56.46, close: 56.47, volume: 25138, }, { date: 1461003660000, open: 56.48, high: 56.5, low: 56.48, close: 56.4825, volume: 47681, }, { date: 1461003720000, open: 56.4872, high: 56.52, low: 56.48, close: 56.519, volume: 41000, }, { date: 1461003780000, open: 56.5128, high: 56.52, low: 56.5001, close: 56.505, volume: 33881, }, { date: 1461003840000, open: 56.51, high: 56.51, low: 56.49, close: 56.49, volume: 19260, }, { date: 1461003900000, open: 56.5, high: 56.5, low: 56.46, close: 56.465, volume: 52861, }, { date: 1461003960000, open: 56.465, high: 56.51, low: 56.46, close: 56.495, volume: 66277, }, { date: 1461004020000, open: 56.495, high: 56.495, low: 56.45, close: 56.46, volume: 24003, }, { date: 1461004080000, open: 56.46, high: 56.48, low: 56.46, close: 56.465, volume: 20392, }, { date: 1461004140000, open: 56.46, high: 56.51, low: 56.45, close: 56.46, volume: 90395, }, { date: 1461004200000, open: 56.46, high: 56.48, low: 56.45, close: 56.45, volume: 53623, }, { date: 1461004260000, open: 56.456, high: 56.46, low: 56.44, close: 56.45, volume: 29772, }, { date: 1461004320000, open: 56.4532, high: 56.47, low: 56.45, close: 56.465, volume: 13878, }, { date: 1461004380000, open: 56.47, high: 56.48, low: 56.46, close: 56.48, volume: 20878, }, { date: 1461004440000, open: 56.47, high: 56.485, low: 56.4601, close: 56.485, volume: 48836, }, { date: 1461004500000, open: 56.4802, high: 56.49, low: 56.45, close: 56.455, volume: 56732, }, { date: 1461004560000, open: 56.4599, high: 56.479, low: 56.45, close: 56.479, volume: 19822, }, { date: 1461004620000, open: 56.48, high: 56.52, low: 56.475, close: 56.49, volume: 41255, }, { date: 1461004680000, open: 56.495, high: 56.52, low: 56.49, close: 56.515, volume: 39011, }, { date: 1461004740000, open: 56.515, high: 56.52, low: 56.48, close: 56.48, volume: 21925, }, { date: 1461004800000, open: 56.48, high: 56.5, low: 56.475, close: 56.5, volume: 39754, }, { date: 1461004860000, open: 56.49, high: 56.52, low: 56.49, close: 56.515, volume: 39101, }, { date: 1461004920000, open: 56.515, high: 56.525, low: 56.5, close: 56.5099, volume: 48164, }, { date: 1461004980000, open: 56.51, high: 56.52, low: 56.5005, close: 56.505, volume: 13362, }, { date: 1461005040000, open: 56.503, high: 56.545, low: 56.5, close: 56.54, volume: 38363, }, { date: 1461005100000, open: 56.54, high: 56.54, low: 56.522, close: 56.522, volume: 21135, }, { date: 1461005160000, open: 56.52, high: 56.525, low: 56.5, close: 56.5025, volume: 52068, }, { date: 1461005220000, open: 56.5058, high: 56.51, low: 56.5, close: 56.505, volume: 30907, }, { date: 1461005280000, open: 56.51, high: 56.535, low: 56.5, close: 56.535, volume: 22442, }, { date: 1461005340000, open: 56.54, high: 56.55, low: 56.5, close: 56.54, volume: 76404, }, { date: 1461005400000, open: 56.53, high: 56.54, low: 56.52, close: 56.52, volume: 48621, }, { date: 1461005460000, open: 56.523, high: 56.55, low: 56.505, close: 56.51, volume: 75071, }, { date: 1461005520000, open: 56.5137, high: 56.53, low: 56.505, close: 56.5223, volume: 35859, }, { date: 1461005580000, open: 56.5228, high: 56.527, low: 56.5, close: 56.5171, volume: 38262, }, { date: 1461005640000, open: 56.5138, high: 56.515, low: 56.5, close: 56.51, volume: 17354, }, { date: 1461005700000, open: 56.515, high: 56.52, low: 56.495, close: 56.51, volume: 42073, }, { date: 1461005760000, open: 56.51, high: 56.5199, low: 56.5, close: 56.515, volume: 10928, }, { date: 1461005820000, open: 56.51, high: 56.52, low: 56.47, close: 56.49, volume: 32137, }, { date: 1461005880000, open: 56.48, high: 56.49, low: 56.455, close: 56.465, volume: 37079, }, { date: 1461005940000, open: 56.4699, high: 56.4799, low: 56.44, close: 56.47, volume: 33930, }, { date: 1461006000000, open: 56.47, high: 56.5, low: 56.46, close: 56.5, volume: 22312, }, { date: 1461006060000, open: 56.5, high: 56.5, low: 56.48, close: 56.485, volume: 25986, }, { date: 1461006120000, open: 56.485, high: 56.49, low: 56.47, close: 56.47, volume: 19588, }, { date: 1461006180000, open: 56.47, high: 56.5, low: 56.4624, close: 56.5, volume: 35560, }, { date: 1461006240000, open: 56.51, high: 56.51, low: 56.49, close: 56.4937, volume: 22917, }, { date: 1461006300000, open: 56.4933, high: 56.5, low: 56.48, close: 56.48, volume: 23143, }, { date: 1461006360000, open: 56.47, high: 56.48, low: 56.4601, close: 56.47, volume: 16301, }, { date: 1461006420000, open: 56.48, high: 56.48, low: 56.47, close: 56.47, volume: 12068, }, { date: 1461006480000, open: 56.471, high: 56.471, low: 56.42, close: 56.455, volume: 51020, }, { date: 1461006540000, open: 56.4502, high: 56.49, low: 56.45, close: 56.49, volume: 18372, }, { date: 1461006600000, open: 56.49, high: 56.5, low: 56.47, close: 56.495, volume: 38003, }, { date: 1461006660000, open: 56.4901, high: 56.5, low: 56.48, close: 56.496, volume: 28615, }, { date: 1461006720000, open: 56.4901, high: 56.5, low: 56.46, close: 56.4601, volume: 40709, }, { date: 1461006780000, open: 56.4672, high: 56.495, low: 56.45, close: 56.495, volume: 22013, }, { date: 1461006840000, open: 56.495, high: 56.5, low: 56.48, close: 56.49, volume: 28700, }, { date: 1461006900000, open: 56.485, high: 56.51, low: 56.46, close: 56.46, volume: 45225, }, { date: 1461006960000, open: 56.465, high: 56.505, low: 56.46, close: 56.505, volume: 31536, }, { date: 1461007020000, open: 56.5069, high: 56.52, low: 56.495, close: 56.515, volume: 32388, }, { date: 1461007080000, open: 56.515, high: 56.53, low: 56.5, close: 56.515, volume: 38239, }, { date: 1461007140000, open: 56.52, high: 56.535, low: 56.51, close: 56.52, volume: 52704, }, { date: 1461007200000, open: 56.5201, high: 56.54, low: 56.52, close: 56.5301, volume: 28250, }, { date: 1461007260000, open: 56.54, high: 56.55, low: 56.52, close: 56.525, volume: 42075, }, { date: 1461007320000, open: 56.5294, high: 56.5294, low: 56.4733, close: 56.485, volume: 42014, }, { date: 1461007380000, open: 56.48, high: 56.48, low: 56.45, close: 56.455, volume: 48273, }, { date: 1461007440000, open: 56.4636, high: 56.48, low: 56.4535, close: 56.4701, volume: 34600, }, { date: 1461007500000, open: 56.475, high: 56.475, low: 56.46, close: 56.463, volume: 22282, }, { date: 1461007560000, open: 56.46, high: 56.47, low: 56.45, close: 56.47, volume: 45293, }, { date: 1461007620000, open: 56.465, high: 56.49, low: 56.46, close: 56.472, volume: 41321, }, { date: 1461007680000, open: 56.47, high: 56.495, low: 56.47, close: 56.49, volume: 46436, }, { date: 1461007740000, open: 56.495, high: 56.5, low: 56.47, close: 56.48, volume: 57296, }, { date: 1461007800000, open: 56.48, high: 56.49, low: 56.47, close: 56.49, volume: 16845, }, { date: 1461007860000, open: 56.485, high: 56.4999, low: 56.46, close: 56.4939, volume: 81601, }, { date: 1461007920000, open: 56.4913, high: 56.5, low: 56.49, close: 56.49, volume: 57514, }, { date: 1461007980000, open: 56.49, high: 56.51, low: 56.49, close: 56.495, volume: 89349, }, { date: 1461008040000, open: 56.4905, high: 56.53, low: 56.485, close: 56.53, volume: 307808, }, { date: 1461008100000, open: 56.53, high: 56.54, low: 56.5, close: 56.5, volume: 65549, }, { date: 1461008160000, open: 56.5, high: 56.5099, low: 56.47, close: 56.47, volume: 56294, }, { date: 1461008220000, open: 56.4701, high: 56.51, low: 56.47, close: 56.48, volume: 51122, }, { date: 1461008280000, open: 56.49, high: 56.49, low: 56.47, close: 56.475, volume: 50662, }, { date: 1461008340000, open: 56.4709, high: 56.49, low: 56.47, close: 56.47, volume: 60224, }, { date: 1461008400000, open: 56.475, high: 56.475, low: 56.46, close: 56.468, volume: 24941, }, { date: 1461008460000, open: 56.47, high: 56.495, low: 56.45, close: 56.495, volume: 79524, }, { date: 1461008520000, open: 56.48, high: 56.54, low: 56.48, close: 56.535, volume: 56149, }, { date: 1461008580000, open: 56.535, high: 56.555, low: 56.52, close: 56.55, volume: 109054, }, { date: 1461008640000, open: 56.55, high: 56.55, low: 56.495, close: 56.515, volume: 141813, }, { date: 1461008700000, open: 56.515, high: 56.535, low: 56.5, close: 56.5, volume: 142573, }, { date: 1461008760000, open: 56.505, high: 56.515, low: 56.48, close: 56.505, volume: 67443, }, { date: 1461008820000, open: 56.51, high: 56.525, low: 56.5, close: 56.51, volume: 77970, }, { date: 1461008880000, open: 56.52, high: 56.54, low: 56.5, close: 56.5261, volume: 87080, }, { date: 1461008940000, open: 56.53, high: 56.53, low: 56.5, close: 56.5, volume: 99915, }, { date: 1461009000000, open: 56.5, high: 56.5075, low: 56.46, close: 56.48, volume: 70822, }, { date: 1461009060000, open: 56.48, high: 56.5, low: 56.47, close: 56.5, volume: 112618, }, { date: 1461009120000, open: 56.5, high: 56.51, low: 56.49, close: 56.49, volume: 68952, }, { date: 1461009180000, open: 56.495, high: 56.4999, low: 56.47, close: 56.48, volume: 125145, }, { date: 1461009240000, open: 56.475, high: 56.5, low: 56.4601, close: 56.5, volume: 95920, }, { date: 1461009300000, open: 56.5, high: 56.5, low: 56.47, close: 56.485, volume: 155747, }, { date: 1461009360000, open: 56.49, high: 56.49, low: 56.45, close: 56.46, volume: 181252, }, { date: 1461009420000, open: 56.45, high: 56.475, low: 56.45, close: 56.47, volume: 163982, }, { date: 1461009480000, open: 56.455, high: 56.46, low: 56.44, close: 56.445, volume: 38734, }, { date: 1461009540000, open: 56.44, high: 56.45, low: 56.43, close: 56.445, volume: 121723, }, { date: 1461009600000, open: 56.44, high: 56.48, low: 56.44, close: 56.46, volume: 1898870, }, { date: 1461072600000, open: 56.63, high: 56.77, low: 56.62, close: 56.65, volume: 358554, }, { date: 1461072660000, open: 56.665, high: 56.74, low: 56.64, close: 56.65, volume: 146116, }, { date: 1461072720000, open: 56.645, high: 56.66, low: 56.561, close: 56.59, volume: 172803, }, { date: 1461072780000, open: 56.59, high: 56.6, low: 56.51, close: 56.555, volume: 159441, }, { date: 1461072840000, open: 56.555, high: 56.58, low: 56.475, close: 56.4855, volume: 163869, }, { date: 1461072900000, open: 56.49, high: 56.52, low: 56.36, close: 56.36, volume: 140045, }, { date: 1461072960000, open: 56.36, high: 56.37, low: 56.31, close: 56.36, volume: 145838, }, { date: 1461073020000, open: 56.36, high: 56.36, low: 56.11, close: 56.11, volume: 374767, }, { date: 1461073080000, open: 56.1, high: 56.11, low: 55.91, close: 55.99, volume: 514291, }, { date: 1461073140000, open: 55.99, high: 56.03, low: 55.88, close: 55.97, volume: 283029, }, { date: 1461073200000, open: 55.96, high: 56.05, low: 55.96, close: 56.02, volume: 157151, }, { date: 1461073260000, open: 56.02, high: 56.03, low: 55.75, close: 55.76, volume: 405630, }, { date: 1461073320000, open: 55.76, high: 55.87, low: 55.68, close: 55.85, volume: 267003, }, { date: 1461073380000, open: 55.86, high: 55.96, low: 55.825, close: 55.93, volume: 205257, }, { date: 1461073440000, open: 55.9325, high: 56.04, low: 55.925, close: 56.01, volume: 284401, }, { date: 1461073500000, open: 56.01, high: 56.1, low: 56.005, close: 56.04, volume: 173116, }, { date: 1461073560000, open: 56.04, high: 56.12, low: 56, close: 56.11, volume: 207216, }, { date: 1461073620000, open: 56.11, high: 56.18, low: 56.09, close: 56.12, volume: 203198, }, { date: 1461073680000, open: 56.12, high: 56.221, low: 56.115, close: 56.14, volume: 169136, }, { date: 1461073740000, open: 56.1401, high: 56.17, low: 56.1, close: 56.16, volume: 72483, }, { date: 1461073800000, open: 56.17, high: 56.26, low: 56.1402, close: 56.22, volume: 175357, }, { date: 1461073860000, open: 56.2, high: 56.25, low: 56.17, close: 56.23, volume: 132252, }, { date: 1461073920000, open: 56.24, high: 56.3, low: 56.14, close: 56.14, volume: 134531, }, { date: 1461073980000, open: 56.14, high: 56.145, low: 56.05, close: 56.09, volume: 123200, }, { date: 1461074040000, open: 56.09, high: 56.09, low: 56.03, close: 56.075, volume: 153741, }, { date: 1461074100000, open: 56.075, high: 56.09, low: 56, close: 56.005, volume: 87306, }, { date: 1461074160000, open: 56, high: 56.0562, low: 56, close: 56.04, volume: 86267, }, { date: 1461074220000, open: 56.05, high: 56.06, low: 56.02, close: 56.04, volume: 58736, }, { date: 1461074280000, open: 56.041, high: 56.13, low: 56.041, close: 56.115, volume: 99549, }, { date: 1461074340000, open: 56.125, high: 56.14, low: 56.0538, close: 56.0538, volume: 61633, }, { date: 1461074400000, open: 56.05, high: 56.06, low: 56, close: 56.03, volume: 150006, }, { date: 1461074460000, open: 56.03, high: 56.05, low: 56, close: 56.0001, volume: 207159, }, { date: 1461074520000, open: 56.005, high: 56.16, low: 56, close: 56.11, volume: 343365, }, { date: 1461074580000, open: 56.11, high: 56.17, low: 56.1, close: 56.14, volume: 109922, }, { date: 1461074640000, open: 56.14, high: 56.22, low: 56.13, close: 56.21, volume: 129595, }, { date: 1461074700000, open: 56.205, high: 56.21, low: 56.13, close: 56.2, volume: 243782, }, { date: 1461074760000, open: 56.2, high: 56.21, low: 56.14, close: 56.17, volume: 92802, }, { date: 1461074820000, open: 56.17, high: 56.21, low: 56.155, close: 56.205, volume: 85889, }, { date: 1461074880000, open: 56.21, high: 56.21, low: 56.145, close: 56.175, volume: 267195, }, { date: 1461074940000, open: 56.1734, high: 56.175, low: 56.13, close: 56.13, volume: 61402, }, { date: 1461075000000, open: 56.14, high: 56.15, low: 56.12, close: 56.13, volume: 54244, }, { date: 1461075060000, open: 56.14, high: 56.21, low: 56.133, close: 56.2, volume: 176899, }, { date: 1461075120000, open: 56.2, high: 56.22, low: 56.2, close: 56.2, volume: 55781, }, { date: 1461075180000, open: 56.205, high: 56.245, low: 56.19, close: 56.23, volume: 124342, }, { date: 1461075240000, open: 56.23, high: 56.25, low: 56.19, close: 56.2201, volume: 64303, }, { date: 1461075300000, open: 56.225, high: 56.23, low: 56.19, close: 56.21, volume: 28602, }, { date: 1461075360000, open: 56.21, high: 56.25, low: 56.18, close: 56.185, volume: 55600, }, { date: 1461075420000, open: 56.18, high: 56.18, low: 56.09, close: 56.17, volume: 118672, }, { date: 1461075480000, open: 56.17, high: 56.2, low: 56.14, close: 56.19, volume: 42389, }, { date: 1461075540000, open: 56.195, high: 56.2, low: 56.15, close: 56.16, volume: 41702, }, { date: 1461075600000, open: 56.17, high: 56.19, low: 56.16, close: 56.1741, volume: 112776, }, { date: 1461075660000, open: 56.17, high: 56.225, low: 56.17, close: 56.22, volume: 48119, }, { date: 1461075720000, open: 56.2299, high: 56.24, low: 56.18, close: 56.18, volume: 47054, }, { date: 1461075780000, open: 56.18, high: 56.1999, low: 56.18, close: 56.18, volume: 34640, }, { date: 1461075840000, open: 56.17, high: 56.175, low: 56.12, close: 56.12, volume: 21978, }, { date: 1461075900000, open: 56.1299, high: 56.14, low: 56.07, close: 56.075, volume: 71185, }, { date: 1461075960000, open: 56.075, high: 56.1, low: 56.07, close: 56.08, volume: 49867, }, { date: 1461076020000, open: 56.075, high: 56.105, low: 56.06, close: 56.06, volume: 54527, }, { date: 1461076080000, open: 56.07, high: 56.09, low: 56.05, close: 56.0599, volume: 75736, }, { date: 1461076140000, open: 56.05, high: 56.12, low: 56.03, close: 56.12, volume: 114574, }, { date: 1461076200000, open: 56.12, high: 56.12, low: 56.01, close: 56.03, volume: 110748, }, { date: 1461076260000, open: 56.04, high: 56.14, low: 56.035, close: 56.14, volume: 67078, }, { date: 1461076320000, open: 56.1466, high: 56.185, low: 56.12, close: 56.185, volume: 68131, }, { date: 1461076380000, open: 56.18, high: 56.21, low: 56.18, close: 56.21, volume: 70795, }, { date: 1461076440000, open: 56.22, high: 56.23, low: 56.21, close: 56.22, volume: 25175, }, { date: 1461076500000, open: 56.229, high: 56.255, low: 56.21, close: 56.246, volume: 55580, }, { date: 1461076560000, open: 56.25, high: 56.25, low: 56.15, close: 56.157, volume: 121382, }, { date: 1461076620000, open: 56.15, high: 56.16, low: 56.115, close: 56.14, volume: 39649, }, { date: 1461076680000, open: 56.14, high: 56.16, low: 56.12, close: 56.12, volume: 58194, }, { date: 1461076740000, open: 56.1205, high: 56.13, low: 56.1, close: 56.1, volume: 18242, }, { date: 1461076800000, open: 56.1001, high: 56.15, low: 56.1001, close: 56.13, volume: 23147, }, { date: 1461076860000, open: 56.1301, high: 56.14, low: 56.095, close: 56.1, volume: 27520, }, { date: 1461076920000, open: 56.1, high: 56.109, low: 56.05, close: 56.109, volume: 54049, }, { date: 1461076980000, open: 56.095, high: 56.13, low: 56.09, close: 56.125, volume: 37229, }, { date: 1461077040000, open: 56.12, high: 56.16, low: 56.11, close: 56.15, volume: 54511, }, { date: 1461077100000, open: 56.155, high: 56.17, low: 56.12, close: 56.17, volume: 34958, }, { date: 1461077160000, open: 56.17, high: 56.191, low: 56.16, close: 56.185, volume: 17652, }, { date: 1461077220000, open: 56.18, high: 56.2, low: 56.125, close: 56.15, volume: 52394, }, { date: 1461077280000, open: 56.15, high: 56.16, low: 56.11, close: 56.12, volume: 26583, }, { date: 1461077340000, open: 56.13, high: 56.13, low: 56.065, close: 56.07, volume: 43199, }, { date: 1461077400000, open: 56.0679, high: 56.11, low: 56.06, close: 56.1, volume: 26603, }, { date: 1461077460000, open: 56.1, high: 56.15, low: 56.1, close: 56.1371, volume: 24000, }, { date: 1461077520000, open: 56.13, high: 56.1715, low: 56.12, close: 56.17, volume: 38509, }, { date: 1461077580000, open: 56.17, high: 56.205, low: 56.1535, close: 56.205, volume: 42461, }, { date: 1461077640000, open: 56.2, high: 56.23, low: 56.195, close: 56.22, volume: 31682, }, { date: 1461077700000, open: 56.2268, high: 56.28, low: 56.22, close: 56.26, volume: 71777, }, { date: 1461077760000, open: 56.2535, high: 56.2599, low: 56.22, close: 56.22, volume: 30540, }, { date: 1461077820000, open: 56.23, high: 56.27, low: 56.23, close: 56.25, volume: 23039, }, { date: 1461077880000, open: 56.24, high: 56.25, low: 56.2101, close: 56.233, volume: 22110, }, { date: 1461077940000, open: 56.24, high: 56.28, low: 56.23, close: 56.2601, volume: 34637, }, { date: 1461078000000, open: 56.27, high: 56.27, low: 56.2402, close: 56.245, volume: 32156, }, { date: 1461078060000, open: 56.25, high: 56.27, low: 56.25, close: 56.25, volume: 18145, }, { date: 1461078120000, open: 56.25, high: 56.27, low: 56.23, close: 56.235, volume: 93342, }, { date: 1461078180000, open: 56.24, high: 56.24, low: 56.2, close: 56.2299, volume: 27343, }, { date: 1461078240000, open: 56.22, high: 56.24, low: 56.2, close: 56.22, volume: 21624, }, { date: 1461078300000, open: 56.205, high: 56.25, low: 56.205, close: 56.2464, volume: 23138, }, { date: 1461078360000, open: 56.25, high: 56.29, low: 56.25, close: 56.26, volume: 53692, }, { date: 1461078420000, open: 56.26, high: 56.305, low: 56.26, close: 56.2701, volume: 57113, }, { date: 1461078480000, open: 56.27, high: 56.3, low: 56.27, close: 56.2865, volume: 19902, }, { date: 1461078540000, open: 56.29, high: 56.3, low: 56.28, close: 56.2899, volume: 79030, }, { date: 1461078600000, open: 56.29, high: 56.2961, low: 56.28, close: 56.287, volume: 33426, }, { date: 1461078660000, open: 56.285, high: 56.29, low: 56.235, close: 56.235, volume: 33774, }, { date: 1461078720000, open: 56.235, high: 56.26, low: 56.22, close: 56.26, volume: 10607, }, { date: 1461078780000, open: 56.2564, high: 56.265, low: 56.25, close: 56.26, volume: 10914, }, { date: 1461078840000, open: 56.26, high: 56.27, low: 56.21, close: 56.24, volume: 50079, }, { date: 1461078900000, open: 56.24, high: 56.27, low: 56.23, close: 56.24, volume: 15627, }, { date: 1461078960000, open: 56.24, high: 56.25, low: 56.23, close: 56.24, volume: 11742, }, { date: 1461079020000, open: 56.2311, high: 56.255, low: 56.22, close: 56.25, volume: 27751, }, { date: 1461079080000, open: 56.24, high: 56.245, low: 56.23, close: 56.2336, volume: 12566, }, { date: 1461079140000, open: 56.24, high: 56.26, low: 56.225, close: 56.24, volume: 22404, }, { date: 1461079200000, open: 56.235, high: 56.28, low: 56.225, close: 56.28, volume: 22490, }, { date: 1461079260000, open: 56.27, high: 56.29, low: 56.26, close: 56.2899, volume: 26261, }, { date: 1461079320000, open: 56.2899, high: 56.31, low: 56.28, close: 56.31, volume: 38820, }, { date: 1461079380000, open: 56.305, high: 56.3537, low: 56.3, close: 56.35, volume: 39854, }, { date: 1461079440000, open: 56.36, high: 56.39, low: 56.335, close: 56.335, volume: 31713, }, { date: 1461079500000, open: 56.33, high: 56.34, low: 56.31, close: 56.33, volume: 48664, }, { date: 1461079560000, open: 56.32, high: 56.325, low: 56.2906, close: 56.297, volume: 9892, }, { date: 1461079620000, open: 56.29, high: 56.33, low: 56.28, close: 56.2999, volume: 101975, }, { date: 1461079680000, open: 56.2901, high: 56.305, low: 56.275, close: 56.3, volume: 22214, }, { date: 1461079740000, open: 56.291, high: 56.295, low: 56.28, close: 56.29, volume: 15549, }, { date: 1461079800000, open: 56.2801, high: 56.31, low: 56.275, close: 56.31, volume: 50255, }, { date: 1461079860000, open: 56.305, high: 56.31, low: 56.27, close: 56.3, volume: 26142, }, { date: 1461079920000, open: 56.2964, high: 56.3085, low: 56.29, close: 56.305, volume: 19403, }, { date: 1461079980000, open: 56.305, high: 56.315, low: 56.27, close: 56.27, volume: 59139, }, { date: 1461080040000, open: 56.26, high: 56.31, low: 56.23, close: 56.3001, volume: 61708, }, { date: 1461080100000, open: 56.305, high: 56.33, low: 56.295, close: 56.325, volume: 17785, }, { date: 1461080160000, open: 56.32, high: 56.328, low: 56.3, close: 56.3027, volume: 14322, }, { date: 1461080220000, open: 56.31, high: 56.34, low: 56.3, close: 56.335, volume: 47046, }, { date: 1461080280000, open: 56.3364, high: 56.35, low: 56.3139, close: 56.345, volume: 44909, }, { date: 1461080340000, open: 56.35, high: 56.35, low: 56.33, close: 56.335, volume: 29417, }, { date: 1461080400000, open: 56.335, high: 56.355, low: 56.33, close: 56.35, volume: 40091, }, { date: 1461080460000, open: 56.36, high: 56.36, low: 56.35, close: 56.355, volume: 10203, }, { date: 1461080520000, open: 56.36, high: 56.36, low: 56.335, close: 56.335, volume: 32103, }, { date: 1461080580000, open: 56.335, high: 56.34, low: 56.31, close: 56.31, volume: 31853, }, { date: 1461080640000, open: 56.305, high: 56.31, low: 56.275, close: 56.305, volume: 26606, }, { date: 1461080700000, open: 56.305, high: 56.33, low: 56.3, close: 56.32, volume: 18588, }, { date: 1461080760000, open: 56.32, high: 56.35, low: 56.3, close: 56.34, volume: 49616, }, { date: 1461080820000, open: 56.3433, high: 56.349, low: 56.32, close: 56.3364, volume: 17850, }, { date: 1461080880000, open: 56.335, high: 56.335, low: 56.285, close: 56.3, volume: 36992, }, { date: 1461080940000, open: 56.3, high: 56.31, low: 56.2, close: 56.2, volume: 58249, }, { date: 1461081000000, open: 56.2, high: 56.26, low: 56.2, close: 56.2408, volume: 33883, }, { date: 1461081060000, open: 56.2533, high: 56.255, low: 56.14, close: 56.14, volume: 74900, }, { date: 1461081120000, open: 56.14, high: 56.165, low: 56.13, close: 56.14, volume: 81992, }, { date: 1461081180000, open: 56.14, high: 56.17, low: 56.14, close: 56.17, volume: 33663, }, { date: 1461081240000, open: 56.16, high: 56.175, low: 56.13, close: 56.13, volume: 24963, }, { date: 1461081300000, open: 56.1201, high: 56.15, low: 56.12, close: 56.1299, volume: 34191, }, { date: 1461081360000, open: 56.12, high: 56.12, low: 56.0699, close: 56.0699, volume: 57038, }, { date: 1461081420000, open: 56.0664, high: 56.08, low: 56.025, close: 56.035, volume: 41291, }, { date: 1461081480000, open: 56.03, high: 56.04, low: 56.01, close: 56.025, volume: 100108, }, { date: 1461081540000, open: 56.025, high: 56.07, low: 56.02, close: 56.07, volume: 50197, }, { date: 1461081600000, open: 56.06, high: 56.07, low: 56, close: 56.015, volume: 199940, }, { date: 1461081660000, open: 56.01, high: 56.04, low: 56, close: 56.035, volume: 59176, }, { date: 1461081720000, open: 56.04, high: 56.075, low: 56.04, close: 56.07, volume: 93953, }, { date: 1461081780000, open: 56.07, high: 56.07, low: 56.04, close: 56.045, volume: 87128, }, { date: 1461081840000, open: 56.045, high: 56.08, low: 56.035, close: 56.065, volume: 96007, }, { date: 1461081900000, open: 56.075, high: 56.095, low: 56.05, close: 56.0736, volume: 79244, }, { date: 1461081960000, open: 56.07, high: 56.1, low: 56.07, close: 56.09, volume: 23728, }, { date: 1461082020000, open: 56.09, high: 56.1, low: 56.06, close: 56.1, volume: 56190, }, { date: 1461082080000, open: 56.1044, high: 56.12, low: 56.08, close: 56.12, volume: 87187, }, { date: 1461082140000, open: 56.115, high: 56.1308, low: 56.09, close: 56.125, volume: 33799, }, { date: 1461082200000, open: 56.13, high: 56.13, low: 56.08, close: 56.11, volume: 26781, }, { date: 1461082260000, open: 56.11, high: 56.11, low: 56.09, close: 56.1, volume: 21543, }, { date: 1461082320000, open: 56.1, high: 56.13, low: 56.085, close: 56.13, volume: 55392, }, { date: 1461082380000, open: 56.13, high: 56.18, low: 56.1224, close: 56.1701, volume: 28360, }, { date: 1461082440000, open: 56.179, high: 56.18, low: 56.105, close: 56.105, volume: 30822, }, { date: 1461082500000, open: 56.105, high: 56.14, low: 56.1, close: 56.14, volume: 25318, }, { date: 1461082560000, open: 56.135, high: 56.15, low: 56.119, close: 56.119, volume: 21584, }, { date: 1461082620000, open: 56.12, high: 56.13, low: 56.11, close: 56.13, volume: 17256, }, { date: 1461082680000, open: 56.134, high: 56.14, low: 56.11, close: 56.11, volume: 20405, }, { date: 1461082740000, open: 56.11, high: 56.13, low: 56.095, close: 56.104, volume: 28908, }, { date: 1461082800000, open: 56.1, high: 56.11, low: 56.08, close: 56.09, volume: 76639, }, { date: 1461082860000, open: 56.095, high: 56.155, low: 56.09, close: 56.145, volume: 41486, }, { date: 1461082920000, open: 56.14, high: 56.179, low: 56.14, close: 56.175, volume: 81824, }, { date: 1461082980000, open: 56.17, high: 56.19, low: 56.14, close: 56.18, volume: 92337, }, { date: 1461083040000, open: 56.1864, high: 56.24, low: 56.18, close: 56.235, volume: 103278, }, { date: 1461083100000, open: 56.23, high: 56.23, low: 56.15, close: 56.1511, volume: 20864, }, { date: 1461083160000, open: 56.155, high: 56.16, low: 56.125, close: 56.15, volume: 12976, }, { date: 1461083220000, open: 56.15, high: 56.16, low: 56.14, close: 56.15, volume: 23121, }, { date: 1461083280000, open: 56.15, high: 56.16, low: 56.13, close: 56.15, volume: 13754, }, { date: 1461083340000, open: 56.16, high: 56.19, low: 56.16, close: 56.185, volume: 17103, }, { date: 1461083400000, open: 56.1839, high: 56.215, low: 56.14, close: 56.15, volume: 30489, }, { date: 1461083460000, open: 56.15, high: 56.165, low: 56.13, close: 56.16, volume: 21546, }, { date: 1461083520000, open: 56.17, high: 56.17, low: 56.125, close: 56.166, volume: 47993, }, { date: 1461083580000, open: 56.16, high: 56.18, low: 56.15, close: 56.1798, volume: 19968, }, { date: 1461083640000, open: 56.175, high: 56.18, low: 56.155, close: 56.155, volume: 18369, }, { date: 1461083700000, open: 56.15, high: 56.1501, low: 56.12, close: 56.14, volume: 18375, }, { date: 1461083760000, open: 56.13, high: 56.17, low: 56.129, close: 56.17, volume: 15842, }, { date: 1461083820000, open: 56.165, high: 56.1662, low: 56.145, close: 56.16, volume: 13039, }, { date: 1461083880000, open: 56.155, high: 56.175, low: 56.141, close: 56.17, volume: 23775, }, { date: 1461083940000, open: 56.17, high: 56.175, low: 56.145, close: 56.15, volume: 54249, }, { date: 1461084000000, open: 56.15, high: 56.175, low: 56.14, close: 56.159, volume: 20254, }, { date: 1461084060000, open: 56.16, high: 56.16, low: 56.14, close: 56.14, volume: 16019, }, { date: 1461084120000, open: 56.15, high: 56.16, low: 56.125, close: 56.155, volume: 23063, }, { date: 1461084180000, open: 56.15, high: 56.16, low: 56.1433, close: 56.15, volume: 12063, }, { date: 1461084240000, open: 56.15, high: 56.15, low: 56.12, close: 56.12, volume: 26335, }, { date: 1461084300000, open: 56.125, high: 56.155, low: 56.1202, close: 56.141, volume: 13889, }, { date: 1461084360000, open: 56.1435, high: 56.155, low: 56.12, close: 56.12, volume: 43557, }, { date: 1461084420000, open: 56.125, high: 56.14, low: 56.09, close: 56.09, volume: 42310, }, { date: 1461084480000, open: 56.08, high: 56.085, low: 56.045, close: 56.0799, volume: 36521, }, { date: 1461084540000, open: 56.08, high: 56.085, low: 56.04, close: 56.069, volume: 28268, }, { date: 1461084600000, open: 56.0663, high: 56.09, low: 56.06, close: 56.081, volume: 28336, }, { date: 1461084660000, open: 56.08, high: 56.08, low: 56.06, close: 56.07, volume: 19643, }, { date: 1461084720000, open: 56.07, high: 56.1, low: 56.07, close: 56.0937, volume: 12659, }, { date: 1461084780000, open: 56.09, high: 56.155, low: 56.09, close: 56.155, volume: 37393, }, { date: 1461084840000, open: 56.155, high: 56.27, low: 56.155, close: 56.27, volume: 70634, }, { date: 1461084900000, open: 56.26, high: 56.27, low: 56.23, close: 56.235, volume: 51220, }, { date: 1461084960000, open: 56.23, high: 56.24, low: 56.19, close: 56.1999, volume: 13025, }, { date: 1461085020000, open: 56.194, high: 56.23, low: 56.185, close: 56.22, volume: 40834, }, { date: 1461085080000, open: 56.2234, high: 56.23, low: 56.22, close: 56.228, volume: 8116, }, { date: 1461085140000, open: 56.2242, high: 56.24, low: 56.22, close: 56.235, volume: 34299, }, { date: 1461085200000, open: 56.23, high: 56.2495, low: 56.22, close: 56.2495, volume: 41459, }, { date: 1461085260000, open: 56.25, high: 56.27, low: 56.24, close: 56.26, volume: 24183, }, { date: 1461085320000, open: 56.265, high: 56.305, low: 56.2601, close: 56.305, volume: 47201, }, { date: 1461085380000, open: 56.305, high: 56.3199, low: 56.275, close: 56.2925, volume: 64281, }, { date: 1461085440000, open: 56.2861, high: 56.2999, low: 56.2861, close: 56.295, volume: 35604, }, { date: 1461085500000, open: 56.295, high: 56.3, low: 56.225, close: 56.225, volume: 71788, }, { date: 1461085560000, open: 56.23, high: 56.26, low: 56.2201, close: 56.26, volume: 44990, }, { date: 1461085620000, open: 56.26, high: 56.28, low: 56.25, close: 56.28, volume: 42736, }, { date: 1461085680000, open: 56.28, high: 56.3, low: 56.26, close: 56.26, volume: 59091, }, { date: 1461085740000, open: 56.2601, high: 56.29, low: 56.2601, close: 56.275, volume: 12210, }, { date: 1461085800000, open: 56.27, high: 56.28, low: 56.22, close: 56.22, volume: 38440, }, { date: 1461085860000, open: 56.225, high: 56.24, low: 56.215, close: 56.2243, volume: 18251, }, { date: 1461085920000, open: 56.2236, high: 56.245, low: 56.2164, close: 56.24, volume: 9455, }, { date: 1461085980000, open: 56.24, high: 56.265, low: 56.23, close: 56.265, volume: 16214, }, { date: 1461086040000, open: 56.26, high: 56.28, low: 56.26, close: 56.26, volume: 14000, }, { date: 1461086100000, open: 56.27, high: 56.29, low: 56.265, close: 56.2772, volume: 17437, }, { date: 1461086160000, open: 56.2736, high: 56.3, low: 56.27, close: 56.285, volume: 41959, }, { date: 1461086220000, open: 56.28, high: 56.3, low: 56.27, close: 56.28, volume: 21677, }, { date: 1461086280000, open: 56.27, high: 56.28, low: 56.265, close: 56.27, volume: 16787, }, { date: 1461086340000, open: 56.27, high: 56.275, low: 56.22, close: 56.22, volume: 28091, }, { date: 1461086400000, open: 56.22, high: 56.225, low: 56.2, close: 56.204, volume: 16186, }, { date: 1461086460000, open: 56.2014, high: 56.23, low: 56.2, close: 56.23, volume: 12703, }, { date: 1461086520000, open: 56.23, high: 56.235, low: 56.215, close: 56.23, volume: 6491, }, { date: 1461086580000, open: 56.24, high: 56.29, low: 56.22, close: 56.27, volume: 66115, }, { date: 1461086640000, open: 56.27, high: 56.3, low: 56.27, close: 56.295, volume: 43543, }, { date: 1461086700000, open: 56.2999, high: 56.3, low: 56.28, close: 56.295, volume: 13823, }, { date: 1461086760000, open: 56.29, high: 56.315, low: 56.29, close: 56.305, volume: 38911, }, { date: 1461086820000, open: 56.305, high: 56.33, low: 56.3, close: 56.33, volume: 19475, }, { date: 1461086880000, open: 56.33, high: 56.33, low: 56.31, close: 56.3199, volume: 14462, }, { date: 1461086940000, open: 56.315, high: 56.32, low: 56.31, close: 56.31, volume: 9284, }, { date: 1461087000000, open: 56.311, high: 56.3199, low: 56.245, close: 56.255, volume: 43148, }, { date: 1461087060000, open: 56.25, high: 56.28, low: 56.25, close: 56.275, volume: 25026, }, { date: 1461087120000, open: 56.27, high: 56.2745, low: 56.2, close: 56.215, volume: 57830, }, { date: 1461087180000, open: 56.215, high: 56.24, low: 56.2, close: 56.24, volume: 26066, }, { date: 1461087240000, open: 56.231, high: 56.265, low: 56.231, close: 56.265, volume: 29011, }, { date: 1461087300000, open: 56.265, high: 56.275, low: 56.23, close: 56.23, volume: 38810, }, { date: 1461087360000, open: 56.2236, high: 56.24, low: 56.215, close: 56.235, volume: 13807, }, { date: 1461087420000, open: 56.2301, high: 56.24, low: 56.215, close: 56.22, volume: 17529, }, { date: 1461087480000, open: 56.224, high: 56.255, low: 56.22, close: 56.245, volume: 23219, }, { date: 1461087540000, open: 56.245, high: 56.25, low: 56.2336, close: 56.25, volume: 13515, }, { date: 1461087600000, open: 56.25, high: 56.26, low: 56.24, close: 56.25, volume: 17627, }, { date: 1461087660000, open: 56.245, high: 56.245, low: 56.234, close: 56.245, volume: 8223, }, { date: 1461087720000, open: 56.25, high: 56.25, low: 56.205, close: 56.23, volume: 30382, }, { date: 1461087780000, open: 56.22, high: 56.24, low: 56.215, close: 56.235, volume: 16923, }, { date: 1461087840000, open: 56.24, high: 56.26, low: 56.23, close: 56.245, volume: 60839, }, { date: 1461087900000, open: 56.245, high: 56.245, low: 56.22, close: 56.22, volume: 23454, }, { date: 1461087960000, open: 56.225, high: 56.25, low: 56.225, close: 56.25, volume: 11970, }, { date: 1461088020000, open: 56.245, high: 56.285, low: 56.244, close: 56.275, volume: 29641, }, { date: 1461088080000, open: 56.2769, high: 56.29, low: 56.262, close: 56.29, volume: 21201, }, { date: 1461088140000, open: 56.29, high: 56.325, low: 56.28, close: 56.32, volume: 41334, }, { date: 1461088200000, open: 56.32, high: 56.347, low: 56.31, close: 56.345, volume: 19347, }, { date: 1461088260000, open: 56.3442, high: 56.369, low: 56.335, close: 56.34, volume: 72441, }, { date: 1461088320000, open: 56.34, high: 56.35, low: 56.32, close: 56.34, volume: 31699, }, { date: 1461088380000, open: 56.335, high: 56.345, low: 56.32, close: 56.34, volume: 19714, }, { date: 1461088440000, open: 56.34, high: 56.34, low: 56.32, close: 56.33, volume: 10540, }, { date: 1461088500000, open: 56.335, high: 56.35, low: 56.335, close: 56.34, volume: 12148, }, { date: 1461088560000, open: 56.345, high: 56.36, low: 56.34, close: 56.355, volume: 70775, }, { date: 1461088620000, open: 56.36, high: 56.37, low: 56.35, close: 56.35, volume: 21969, }, { date: 1461088680000, open: 56.36, high: 56.37, low: 56.35, close: 56.355, volume: 15666, }, { date: 1461088740000, open: 56.35, high: 56.375, low: 56.34, close: 56.36, volume: 14111, }, { date: 1461088800000, open: 56.36, high: 56.385, low: 56.355, close: 56.385, volume: 15450, }, { date: 1461088860000, open: 56.385, high: 56.39, low: 56.38, close: 56.39, volume: 7637, }, { date: 1461088920000, open: 56.39, high: 56.45, low: 56.385, close: 56.44, volume: 62070, }, { date: 1461088980000, open: 56.45, high: 56.45, low: 56.43, close: 56.445, volume: 24560, }, { date: 1461089040000, open: 56.44, high: 56.445, low: 56.43, close: 56.44, volume: 13423, }, { date: 1461089100000, open: 56.4399, high: 56.4399, low: 56.375, close: 56.378, volume: 31867, }, { date: 1461089160000, open: 56.371, high: 56.39, low: 56.36, close: 56.385, volume: 18129, }, { date: 1461089220000, open: 56.385, high: 56.41, low: 56.365, close: 56.385, volume: 29720, }, { date: 1461089280000, open: 56.3899, high: 56.4138, low: 56.385, close: 56.3868, volume: 18994, }, { date: 1461089340000, open: 56.385, high: 56.3999, low: 56.38, close: 56.39, volume: 26702, }, { date: 1461089400000, open: 56.3876, high: 56.43, low: 56.3802, close: 56.43, volume: 30352, }, { date: 1461089460000, open: 56.425, high: 56.455, low: 56.41, close: 56.449, volume: 97635, }, { date: 1461089520000, open: 56.44, high: 56.465, low: 56.42, close: 56.465, volume: 42565, }, { date: 1461089580000, open: 56.47, high: 56.48, low: 56.46, close: 56.47, volume: 22546, }, { date: 1461089640000, open: 56.46, high: 56.485, low: 56.45, close: 56.475, volume: 62430, }, { date: 1461089700000, open: 56.48, high: 56.5, low: 56.46, close: 56.49, volume: 37597, }, { date: 1461089760000, open: 56.49, high: 56.5, low: 56.46, close: 56.4664, volume: 33442, }, { date: 1461089820000, open: 56.46, high: 56.475, low: 56.4589, close: 56.47, volume: 19356, }, { date: 1461089880000, open: 56.47, high: 56.474, low: 56.46, close: 56.465, volume: 15280, }, { date: 1461089940000, open: 56.47, high: 56.485, low: 56.465, close: 56.475, volume: 35283, }, { date: 1461090000000, open: 56.475, high: 56.48, low: 56.4401, close: 56.46, volume: 37573, }, { date: 1461090060000, open: 56.455, high: 56.48, low: 56.45, close: 56.475, volume: 20199, }, { date: 1461090120000, open: 56.475, high: 56.4863, low: 56.47, close: 56.48, volume: 82363, }, { date: 1461090180000, open: 56.48, high: 56.48, low: 56.43, close: 56.46, volume: 35970, }, { date: 1461090240000, open: 56.465, high: 56.465, low: 56.43, close: 56.445, volume: 12169, }, { date: 1461090300000, open: 56.4498, high: 56.4498, low: 56.41, close: 56.41, volume: 25139, }, { date: 1461090360000, open: 56.415, high: 56.415, low: 56.37, close: 56.375, volume: 26176, }, { date: 1461090420000, open: 56.3789, high: 56.405, low: 56.37, close: 56.395, volume: 41487, }, { date: 1461090480000, open: 56.4, high: 56.415, low: 56.39, close: 56.405, volume: 27720, }, { date: 1461090540000, open: 56.407, high: 56.43, low: 56.38, close: 56.385, volume: 30626, }, { date: 1461090600000, open: 56.385, high: 56.39, low: 56.36, close: 56.365, volume: 24746, }, { date: 1461090660000, open: 56.365, high: 56.375, low: 56.36, close: 56.365, volume: 19517, }, { date: 1461090720000, open: 56.37, high: 56.375, low: 56.354, close: 56.354, volume: 34761, }, { date: 1461090780000, open: 56.36, high: 56.37, low: 56.335, close: 56.335, volume: 28470, }, { date: 1461090840000, open: 56.33, high: 56.34, low: 56.33, close: 56.3301, volume: 5284, }, { date: 1461090900000, open: 56.33, high: 56.35, low: 56.315, close: 56.32, volume: 43401, }, { date: 1461090960000, open: 56.32, high: 56.33, low: 56.29, close: 56.3, volume: 23033, }, { date: 1461091020000, open: 56.3, high: 56.31, low: 56.29, close: 56.305, volume: 14443, }, { date: 1461091080000, open: 56.309, high: 56.32, low: 56.305, close: 56.315, volume: 9805, }, { date: 1461091140000, open: 56.314, high: 56.34, low: 56.31, close: 56.315, volume: 23875, }, { date: 1461091200000, open: 56.32, high: 56.34, low: 56.315, close: 56.3301, volume: 13627, }, { date: 1461091260000, open: 56.335, high: 56.34, low: 56.325, close: 56.3399, volume: 107429, }, { date: 1461091320000, open: 56.335, high: 56.36, low: 56.31, close: 56.36, volume: 42530, }, { date: 1461091380000, open: 56.35, high: 56.36, low: 56.35, close: 56.355, volume: 147848, }, { date: 1461091440000, open: 56.3563, high: 56.395, low: 56.35, close: 56.38, volume: 76633, }, { date: 1461091500000, open: 56.375, high: 56.41, low: 56.37, close: 56.4076, volume: 53615, }, { date: 1461091560000, open: 56.4093, high: 56.43, low: 56.4093, close: 56.427, volume: 26731, }, { date: 1461091620000, open: 56.42, high: 56.45, low: 56.42, close: 56.435, volume: 41743, }, { date: 1461091680000, open: 56.43, high: 56.4399, low: 56.42, close: 56.43, volume: 23142, }, { date: 1461091740000, open: 56.43, high: 56.43, low: 56.42, close: 56.42, volume: 9974, }, { date: 1461091800000, open: 56.42, high: 56.465, low: 56.415, close: 56.45, volume: 41484, }, { date: 1461091860000, open: 56.45, high: 56.46, low: 56.44, close: 56.447, volume: 16999, }, { date: 1461091920000, open: 56.45, high: 56.46, low: 56.395, close: 56.41, volume: 50819, }, { date: 1461091980000, open: 56.4036, high: 56.41, low: 56.38, close: 56.385, volume: 33530, }, { date: 1461092040000, open: 56.39, high: 56.4, low: 56.38, close: 56.39, volume: 8880, }, { date: 1461092100000, open: 56.395, high: 56.42, low: 56.37, close: 56.4069, volume: 49538, }, { date: 1461092160000, open: 56.4001, high: 56.405, low: 56.38, close: 56.385, volume: 15769, }, { date: 1461092220000, open: 56.395, high: 56.405, low: 56.3842, close: 56.405, volume: 11003, }, { date: 1461092280000, open: 56.4036, high: 56.42, low: 56.39, close: 56.415, volume: 22181, }, { date: 1461092340000, open: 56.42, high: 56.43, low: 56.385, close: 56.395, volume: 66343, }, { date: 1461092400000, open: 56.395, high: 56.3954, low: 56.335, close: 56.34, volume: 45550, }, { date: 1461092460000, open: 56.33, high: 56.33, low: 56.26, close: 56.26, volume: 50805, }, { date: 1461092520000, open: 56.255, high: 56.2563, low: 56.23, close: 56.232, volume: 47319, }, { date: 1461092580000, open: 56.24, high: 56.24, low: 56.21, close: 56.24, volume: 26625, }, { date: 1461092640000, open: 56.23, high: 56.24, low: 56.205, close: 56.23, volume: 22626, }, { date: 1461092700000, open: 56.235, high: 56.25, low: 56.23, close: 56.23, volume: 25679, }, { date: 1461092760000, open: 56.23, high: 56.26, low: 56.225, close: 56.2599, volume: 31865, }, { date: 1461092820000, open: 56.26, high: 56.31, low: 56.251, close: 56.305, volume: 29486, }, { date: 1461092880000, open: 56.3, high: 56.335, low: 56.29, close: 56.29, volume: 55771, }, { date: 1461092940000, open: 56.2971, high: 56.3, low: 56.29, close: 56.295, volume: 10413, }, { date: 1461093000000, open: 56.294, high: 56.3, low: 56.265, close: 56.275, volume: 32873, }, { date: 1461093060000, open: 56.275, high: 56.315, low: 56.27, close: 56.3, volume: 28223, }, { date: 1461093120000, open: 56.31, high: 56.34, low: 56.31, close: 56.325, volume: 22417, }, { date: 1461093180000, open: 56.325, high: 56.34, low: 56.3, close: 56.335, volume: 32896, }, { date: 1461093240000, open: 56.335, high: 56.385, low: 56.335, close: 56.34, volume: 131612, }, { date: 1461093300000, open: 56.345, high: 56.37, low: 56.34, close: 56.365, volume: 48475, }, { date: 1461093360000, open: 56.3628, high: 56.38, low: 56.3401, close: 56.3401, volume: 62203, }, { date: 1461093420000, open: 56.348, high: 56.348, low: 56.31, close: 56.31, volume: 35354, }, { date: 1461093480000, open: 56.31, high: 56.34, low: 56.29, close: 56.325, volume: 32237, }, { date: 1461093540000, open: 56.325, high: 56.35, low: 56.32, close: 56.325, volume: 29937, }, { date: 1461093600000, open: 56.325, high: 56.35, low: 56.3005, close: 56.3005, volume: 31461, }, { date: 1461093660000, open: 56.31, high: 56.32, low: 56.31, close: 56.32, volume: 13290, }, { date: 1461093720000, open: 56.315, high: 56.34, low: 56.3137, close: 56.32, volume: 27676, }, { date: 1461093780000, open: 56.33, high: 56.349, low: 56.32, close: 56.345, volume: 18457, }, { date: 1461093840000, open: 56.34, high: 56.37, low: 56.34, close: 56.355, volume: 57576, }, { date: 1461093900000, open: 56.355, high: 56.37, low: 56.35, close: 56.37, volume: 35298, }, { date: 1461093960000, open: 56.37, high: 56.375, low: 56.35, close: 56.357, volume: 60910, }, { date: 1461094020000, open: 56.355, high: 56.36, low: 56.34, close: 56.36, volume: 70413, }, { date: 1461094080000, open: 56.355, high: 56.36, low: 56.35, close: 56.355, volume: 53999, }, { date: 1461094140000, open: 56.3501, high: 56.355, low: 56.3001, close: 56.32, volume: 53107, }, { date: 1461094200000, open: 56.3105, high: 56.3525, low: 56.3105, close: 56.35, volume: 40812, }, { date: 1461094260000, open: 56.355, high: 56.39, low: 56.34, close: 56.38, volume: 60023, }, { date: 1461094320000, open: 56.375, high: 56.395, low: 56.37, close: 56.3801, volume: 51103, }, { date: 1461094380000, open: 56.385, high: 56.4, low: 56.35, close: 56.36, volume: 57250, }, { date: 1461094440000, open: 56.36, high: 56.37, low: 56.335, close: 56.345, volume: 53152, }, { date: 1461094500000, open: 56.35, high: 56.37, low: 56.345, close: 56.3501, volume: 76961, }, { date: 1461094560000, open: 56.3599, high: 56.365, low: 56.33, close: 56.33, volume: 50236, }, { date: 1461094620000, open: 56.34, high: 56.345, low: 56.31, close: 56.345, volume: 63159, }, { date: 1461094680000, open: 56.345, high: 56.35, low: 56.33, close: 56.335, volume: 19892, }, { date: 1461094740000, open: 56.335, high: 56.355, low: 56.33, close: 56.3405, volume: 36559, }, { date: 1461094800000, open: 56.345, high: 56.37, low: 56.34, close: 56.35, volume: 79019, }, { date: 1461094860000, open: 56.3599, high: 56.3599, low: 56.34, close: 56.345, volume: 35476, }, { date: 1461094920000, open: 56.34, high: 56.37, low: 56.34, close: 56.37, volume: 25167, }, { date: 1461094980000, open: 56.37, high: 56.37, low: 56.36, close: 56.36, volume: 27646, }, { date: 1461095040000, open: 56.37, high: 56.37, low: 56.35, close: 56.3515, volume: 100462, }, { date: 1461095100000, open: 56.35, high: 56.36, low: 56.29, close: 56.325, volume: 84963, }, { date: 1461095160000, open: 56.32, high: 56.33, low: 56.28, close: 56.2801, volume: 54374, }, { date: 1461095220000, open: 56.28, high: 56.29, low: 56.25, close: 56.28, volume: 64594, }, { date: 1461095280000, open: 56.28, high: 56.29, low: 56.27, close: 56.28, volume: 38588, }, { date: 1461095340000, open: 56.28, high: 56.3, low: 56.2699, close: 56.295, volume: 43876, }, { date: 1461095400000, open: 56.3, high: 56.32, low: 56.295, close: 56.315, volume: 48822, }, { date: 1461095460000, open: 56.315, high: 56.34, low: 56.3125, close: 56.335, volume: 77608, }, { date: 1461095520000, open: 56.335, high: 56.36, low: 56.33, close: 56.35, volume: 94774, }, { date: 1461095580000, open: 56.35, high: 56.355, low: 56.29, close: 56.3, volume: 87810, }, { date: 1461095640000, open: 56.295, high: 56.3, low: 56.28, close: 56.28, volume: 64209, }, { date: 1461095700000, open: 56.275, high: 56.295, low: 56.27, close: 56.285, volume: 110531, }, { date: 1461095760000, open: 56.288, high: 56.29, low: 56.26, close: 56.2699, volume: 92929, }, { date: 1461095820000, open: 56.265, high: 56.28, low: 56.25, close: 56.275, volume: 128498, }, { date: 1461095880000, open: 56.275, high: 56.29, low: 56.26, close: 56.265, volume: 164414, }, { date: 1461095940000, open: 56.265, high: 56.31, low: 56.245, close: 56.285, volume: 355244, }, { date: 1461096000000, open: 56.285, high: 56.4, low: 56.285, close: 56.39, volume: 2223313, }, { date: 1461159000000, open: 56.29, high: 56.29, low: 56.28, close: 56.29, volume: 421554, }, { date: 1461159060000, open: 56.285, high: 56.5, low: 56.2301, close: 56.25, volume: 140410, }, { date: 1461159120000, open: 56.25, high: 56.3, low: 56.23, close: 56.2775, volume: 178566, }, { date: 1461159180000, open: 56.275, high: 56.3, low: 56.205, close: 56.27, volume: 122158, }, { date: 1461159240000, open: 56.28, high: 56.37, low: 56.26, close: 56.3301, volume: 95263, }, { date: 1461159300000, open: 56.34, high: 56.36, low: 56.33, close: 56.35, volume: 81874, }, { date: 1461159360000, open: 56.35, high: 56.35, low: 56.29, close: 56.29, volume: 46973, }, { date: 1461159420000, open: 56.3, high: 56.36, low: 56.27, close: 56.34, volume: 126114, }, { date: 1461159480000, open: 56.34, high: 56.35, low: 56.3, close: 56.31, volume: 67055, }, { date: 1461159540000, open: 56.3072, high: 56.39, low: 56.3, close: 56.39, volume: 65616, }, { date: 1461159600000, open: 56.385, high: 56.41, low: 56.3535, close: 56.41, volume: 62803, }, { date: 1461159660000, open: 56.4005, high: 56.41, low: 56.39, close: 56.4, volume: 119220, }, { date: 1461159720000, open: 56.4001, high: 56.41, low: 56.28, close: 56.28, volume: 94429, }, { date: 1461159780000, open: 56.2839, high: 56.31, low: 56.26, close: 56.26, volume: 109087, }, { date: 1461159840000, open: 56.25, high: 56.26, low: 56.24, close: 56.25, volume: 62663, }, { date: 1461159900000, open: 56.24, high: 56.25, low: 56.12, close: 56.13, volume: 132199, }, { date: 1461159960000, open: 56.12, high: 56.12, low: 56.08, close: 56.1035, volume: 62222, }, { date: 1461160020000, open: 56.1, high: 56.11, low: 56.05, close: 56.08, volume: 92025, }, { date: 1461160080000, open: 56.08, high: 56.1, low: 56.07, close: 56.1, volume: 98510, }, { date: 1461160140000, open: 56.095, high: 56.11, low: 56.09, close: 56.1, volume: 116555, }, { date: 1461160200000, open: 56.09, high: 56.11, low: 56.09, close: 56.11, volume: 91122, }, { date: 1461160260000, open: 56.1012, high: 56.16, low: 56.1, close: 56.14, volume: 71180, }, { date: 1461160320000, open: 56.14, high: 56.18, low: 56.14, close: 56.18, volume: 65191, }, { date: 1461160380000, open: 56.18, high: 56.19, low: 56.16, close: 56.178, volume: 84458, }, { date: 1461160440000, open: 56.17, high: 56.235, low: 56.16, close: 56.23, volume: 66112, }, { date: 1461160500000, open: 56.23, high: 56.25, low: 56.225, close: 56.23, volume: 55639, }, { date: 1461160560000, open: 56.23, high: 56.33, low: 56.23, close: 56.32, volume: 115976, }, { date: 1461160620000, open: 56.32, high: 56.32, low: 56.27, close: 56.29, volume: 68862, }, { date: 1461160680000, open: 56.2825, high: 56.33, low: 56.2825, close: 56.32, volume: 55183, }, { date: 1461160740000, open: 56.32, high: 56.33, low: 56.28, close: 56.285, volume: 41070, }, { date: 1461160800000, open: 56.28, high: 56.29, low: 56.25, close: 56.251, volume: 173142, }, { date: 1461160860000, open: 56.25, high: 56.26, low: 56.21, close: 56.2237, volume: 56697, }, { date: 1461160920000, open: 56.23, high: 56.23, low: 56.17, close: 56.2, volume: 35756, }, { date: 1461160980000, open: 56.19, high: 56.21, low: 56.16, close: 56.1861, volume: 47839, }, { date: 1461161040000, open: 56.19, high: 56.2, low: 56.16, close: 56.16, volume: 52658, }, { date: 1461161100000, open: 56.16, high: 56.16, low: 56.12, close: 56.15, volume: 42556, }, { date: 1461161160000, open: 56.155, high: 56.16, low: 56.14, close: 56.16, volume: 106315, }, { date: 1461161220000, open: 56.1562, high: 56.16, low: 56.14, close: 56.155, volume: 49807, }, { date: 1461161280000, open: 56.1598, high: 56.17, low: 56.12, close: 56.1299, volume: 75754, }, { date: 1461161340000, open: 56.13, high: 56.16, low: 56.12, close: 56.155, volume: 79236, }, { date: 1461161400000, open: 56.155, high: 56.16, low: 56.11, close: 56.11, volume: 45861, }, { date: 1461161460000, open: 56.1101, high: 56.115, low: 56.08, close: 56.085, volume: 58198, }, { date: 1461161520000, open: 56.08, high: 56.16, low: 56.08, close: 56.15, volume: 96116, }, { date: 1461161580000, open: 56.1599, high: 56.16, low: 56.14, close: 56.155, volume: 68985, }, { date: 1461161640000, open: 56.16, high: 56.16, low: 56.13, close: 56.135, volume: 38769, }, { date: 1461161700000, open: 56.1301, high: 56.16, low: 56.13, close: 56.16, volume: 81544, }, { date: 1461161760000, open: 56.17, high: 56.22, low: 56.16, close: 56.22, volume: 66753, }, { date: 1461161820000, open: 56.22, high: 56.23, low: 56.145, close: 56.145, volume: 87545, }, { date: 1461161880000, open: 56.145, high: 56.16, low: 56.14, close: 56.145, volume: 39668, }, { date: 1461161940000, open: 56.14, high: 56.15, low: 56.1, close: 56.105, volume: 61121, }, { date: 1461162000000, open: 56.105, high: 56.11, low: 56.1, close: 56.1, volume: 63840, }, { date: 1461162060000, open: 56.1, high: 56.11, low: 56.1, close: 56.105, volume: 50657, }, { date: 1461162120000, open: 56.105, high: 56.11, low: 56.1, close: 56.105, volume: 75139, }, { date: 1461162180000, open: 56.105, high: 56.11, low: 56.05, close: 56.09, volume: 266768, }, { date: 1461162240000, open: 56.0999, high: 56.15, low: 56.0999, close: 56.14, volume: 87360, }, { date: 1461162300000, open: 56.14, high: 56.14, low: 56.07, close: 56.085, volume: 81986, }, { date: 1461162360000, open: 56.085, high: 56.09, low: 56.05, close: 56.07, volume: 46543, }, { date: 1461162420000, open: 56.07, high: 56.1, low: 56.06, close: 56.0838, volume: 38631, }, { date: 1461162480000, open: 56.085, high: 56.09, low: 56.04, close: 56.04, volume: 61687, }, { date: 1461162540000, open: 56.045, high: 56.05, low: 56, close: 56.01, volume: 65511, }, { date: 1461162600000, open: 56.005, high: 56.055, low: 56, close: 56.045, volume: 429591, }, { date: 1461162660000, open: 56.04, high: 56.05, low: 56.015, close: 56.04, volume: 64408, }, { date: 1461162720000, open: 56.03, high: 56.05, low: 56.02, close: 56.035, volume: 60772, }, { date: 1461162780000, open: 56.04, high: 56.05, low: 56.03, close: 56.045, volume: 47692, }, { date: 1461162840000, open: 56.05, high: 56.06, low: 56.03, close: 56.06, volume: 74412, }, { date: 1461162900000, open: 56.06, high: 56.06, low: 56.043, close: 56.055, volume: 36918, }, { date: 1461162960000, open: 56.0528, high: 56.055, low: 56, close: 56.005, volume: 105632, }, { date: 1461163020000, open: 56, high: 56.02, low: 56, close: 56, volume: 64452, }, { date: 1461163080000, open: 56, high: 56.01, low: 55.96, close: 55.965, volume: 185423, }, { date: 1461163140000, open: 55.96, high: 55.9699, low: 55.94, close: 55.95, volume: 89827, }, { date: 1461163200000, open: 55.9542, high: 56.01, low: 55.95, close: 56, volume: 77442, }, { date: 1461163260000, open: 55.9927, high: 56, low: 55.9634, close: 55.98, volume: 54640, }, { date: 1461163320000, open: 55.98, high: 56.0033, low: 55.97, close: 56, volume: 40360, }, { date: 1461163380000, open: 56.01, high: 56.02, low: 55.99, close: 55.99, volume: 85878, }, { date: 1461163440000, open: 55.99, high: 55.9999, low: 55.97, close: 55.97, volume: 18448, }, { date: 1461163500000, open: 55.97, high: 55.9857, low: 55.96, close: 55.96, volume: 47930, }, { date: 1461163560000, open: 55.9662, high: 55.97, low: 55.945, close: 55.965, volume: 57718, }, { date: 1461163620000, open: 55.965, high: 55.97, low: 55.94, close: 55.945, volume: 93745, }, { date: 1461163680000, open: 55.945, high: 55.98, low: 55.935, close: 55.955, volume: 95327, }, { date: 1461163740000, open: 55.955, high: 56, low: 55.95, close: 55.995, volume: 90455, }, { date: 1461163800000, open: 55.99, high: 56.03, low: 55.99, close: 56, volume: 70029, }, { date: 1461163860000, open: 56, high: 56.01, low: 55.98, close: 55.99, volume: 39683, }, { date: 1461163920000, open: 55.99, high: 56.02, low: 55.99, close: 56.005, volume: 37560, }, { date: 1461163980000, open: 56.005, high: 56.02, low: 55.97, close: 55.97, volume: 115310, }, { date: 1461164040000, open: 55.98, high: 55.99, low: 55.9734, close: 55.985, volume: 20719, }, { date: 1461164100000, open: 55.99, high: 56.0063, low: 55.9839, close: 56, volume: 42003, }, { date: 1461164160000, open: 56.01, high: 56.035, low: 55.9942, close: 56.02, volume: 72522, }, { date: 1461164220000, open: 56.03, high: 56.065, low: 56.025, close: 56.055, volume: 54473, }, { date: 1461164280000, open: 56.0533, high: 56.06, low: 56.03, close: 56.055, volume: 49679, }, { date: 1461164340000, open: 56.058, high: 56.06, low: 56.03, close: 56.045, volume: 75360, }, { date: 1461164400000, open: 56.045, high: 56.05, low: 56.03, close: 56.035, volume: 27897, }, { date: 1461164460000, open: 56.04, high: 56.05, low: 56.015, close: 56.025, volume: 52109, }, { date: 1461164520000, open: 56.025, high: 56.03, low: 56, close: 56, volume: 94182, }, { date: 1461164580000, open: 56, high: 56.01, low: 55.98, close: 55.995, volume: 44592, }, { date: 1461164640000, open: 55.995, high: 56.01, low: 55.96, close: 55.995, volume: 93051, }, { date: 1461164700000, open: 55.995, high: 56.01, low: 55.96, close: 55.995, volume: 78876, }, { date: 1461164760000, open: 55.995, high: 56.03, low: 55.9901, close: 56.025, volume: 50082, }, { date: 1461164820000, open: 56.03, high: 56.0524, low: 56.02, close: 56.0364, volume: 55908, }, { date: 1461164880000, open: 56.0403, high: 56.075, low: 56.03, close: 56.045, volume: 229799, }, { date: 1461164940000, open: 56.045, high: 56.06, low: 56.02, close: 56.035, volume: 156551, }, { date: 1461165000000, open: 56.04, high: 56.05, low: 56.03, close: 56.0336, volume: 91537, }, { date: 1461165060000, open: 56.03, high: 56.06, low: 56.03, close: 56.05, volume: 135679, }, { date: 1461165120000, open: 56.06, high: 56.09, low: 56.055, close: 56.0826, volume: 54563, }, { date: 1461165180000, open: 56.09, high: 56.09, low: 56.04, close: 56.0701, volume: 90324, }, { date: 1461165240000, open: 56.07, high: 56.08, low: 56.065, close: 56.075, volume: 24204, }, { date: 1461165300000, open: 56.08, high: 56.12, low: 56.075, close: 56.1, volume: 64675, }, { date: 1461165360000, open: 56.105, high: 56.17, low: 56.105, close: 56.1599, volume: 54670, }, { date: 1461165420000, open: 56.1593, high: 56.17, low: 56.14, close: 56.145, volume: 63596, }, { date: 1461165480000, open: 56.15, high: 56.15, low: 56.14, close: 56.145, volume: 37675, }, { date: 1461165540000, open: 56.15, high: 56.15, low: 56.11, close: 56.12, volume: 53706, }, { date: 1461165600000, open: 56.12, high: 56.15, low: 56.11, close: 56.1439, volume: 49816, }, { date: 1461165660000, open: 56.145, high: 56.155, low: 56.14, close: 56.155, volume: 69811, }, { date: 1461165720000, open: 56.155, high: 56.16, low: 56.1435, close: 56.1465, volume: 22805, }, { date: 1461165780000, open: 56.14, high: 56.15, low: 56.12, close: 56.15, volume: 78596, }, { date: 1461165840000, open: 56.15, high: 56.16, low: 56.145, close: 56.16, volume: 98318, }, { date: 1461165900000, open: 56.15, high: 56.16, low: 56.13, close: 56.14, volume: 43137, }, { date: 1461165960000, open: 56.14, high: 56.16, low: 56.13, close: 56.14, volume: 122371, }, { date: 1461166020000, open: 56.14, high: 56.1511, low: 56.14, close: 56.145, volume: 26642, }, { date: 1461166080000, open: 56.15, high: 56.16, low: 56.145, close: 56.153, volume: 20068, }, { date: 1461166140000, open: 56.15, high: 56.161, low: 56.15, close: 56.155, volume: 134746, }, { date: 1461166200000, open: 56.155, high: 56.16, low: 56.14, close: 56.15, volume: 57780, }, { date: 1461166260000, open: 56.15, high: 56.1575, low: 56.14, close: 56.145, volume: 44956, }, { date: 1461166320000, open: 56.14, high: 56.16, low: 56.14, close: 56.155, volume: 31933, }, { date: 1461166380000, open: 56.1535, high: 56.16, low: 56.14, close: 56.14, volume: 39005, }, { date: 1461166440000, open: 56.145, high: 56.16, low: 56.14, close: 56.15, volume: 47930, }, { date: 1461166500000, open: 56.15, high: 56.155, low: 56.14, close: 56.15, volume: 31955, }, { date: 1461166560000, open: 56.15, high: 56.16, low: 56.15, close: 56.15, volume: 62497, }, { date: 1461166620000, open: 56.155, high: 56.16, low: 56.15, close: 56.155, volume: 9523, }, { date: 1461166680000, open: 56.15, high: 56.155, low: 56.11, close: 56.115, volume: 44316, }, { date: 1461166740000, open: 56.12, high: 56.155, low: 56.115, close: 56.1465, volume: 42870, }, { date: 1461166800000, open: 56.15, high: 56.16, low: 56.14, close: 56.15, volume: 44481, }, { date: 1461166860000, open: 56.15, high: 56.17, low: 56.1433, close: 56.165, volume: 71054, }, { date: 1461166920000, open: 56.165, high: 56.17, low: 56.16, close: 56.165, volume: 25535, }, { date: 1461166980000, open: 56.17, high: 56.17, low: 56.12, close: 56.125, volume: 71952, }, { date: 1461167040000, open: 56.125, high: 56.15, low: 56.105, close: 56.14, volume: 39215, }, { date: 1461167100000, open: 56.14, high: 56.15, low: 56.1375, close: 56.145, volume: 29195, }, { date: 1461167160000, open: 56.145, high: 56.17, low: 56.14, close: 56.165, volume: 79131, }, { date: 1461167220000, open: 56.16, high: 56.16, low: 56.115, close: 56.14, volume: 64831, }, { date: 1461167280000, open: 56.14, high: 56.16, low: 56.1, close: 56.15, volume: 82644, }, { date: 1461167340000, open: 56.155, high: 56.16, low: 56.15, close: 56.155, volume: 17503, }, { date: 1461167400000, open: 56.16, high: 56.16, low: 56.15, close: 56.15, volume: 11051, }, { date: 1461167460000, open: 56.15, high: 56.16, low: 56.08, close: 56.09, volume: 83806, }, { date: 1461167520000, open: 56.1, high: 56.11, low: 56.085, close: 56.1, volume: 28428, }, { date: 1461167580000, open: 56.095, high: 56.11, low: 56.09, close: 56.105, volume: 42857, }, { date: 1461167640000, open: 56.1, high: 56.11, low: 56.0541, close: 56.06, volume: 31009, }, { date: 1461167700000, open: 56.065, high: 56.07, low: 56.06, close: 56.06, volume: 37903, }, { date: 1461167760000, open: 56.065, high: 56.1, low: 56.06, close: 56.09, volume: 30290, }, { date: 1461167820000, open: 56.09, high: 56.1, low: 56.085, close: 56.1, volume: 22418, }, { date: 1461167880000, open: 56.095, high: 56.1, low: 56.09, close: 56.09, volume: 62176, }, { date: 1461167940000, open: 56.09, high: 56.095, low: 56.07, close: 56.085, volume: 29759, }, { date: 1461168000000, open: 56.08, high: 56.1, low: 56.07, close: 56.1, volume: 33055, }, { date: 1461168060000, open: 56.0999, high: 56.1, low: 56.08, close: 56.09, volume: 28997, }, { date: 1461168120000, open: 56.09, high: 56.1, low: 56.09, close: 56.09, volume: 8381, }, { date: 1461168180000, open: 56.09, high: 56.1, low: 56.09, close: 56.1, volume: 31383, }, { date: 1461168240000, open: 56.094, high: 56.1, low: 56.09, close: 56.1, volume: 22890, }, { date: 1461168300000, open: 56.1, high: 56.1, low: 56.09, close: 56.1, volume: 15227, }, { date: 1461168360000, open: 56.1, high: 56.11, low: 56.09, close: 56.095, volume: 182603, }, { date: 1461168420000, open: 56.1, high: 56.1, low: 56.09, close: 56.09, volume: 11405, }, { date: 1461168480000, open: 56.09, high: 56.1, low: 56.06, close: 56.07, volume: 40278, }, { date: 1461168540000, open: 56.075, high: 56.08, low: 56.07, close: 56.07, volume: 8043, }, { date: 1461168600000, open: 56.07, high: 56.08, low: 56.07, close: 56.075, volume: 22203, }, { date: 1461168660000, open: 56.075, high: 56.09, low: 56.07, close: 56.0835, volume: 12090, }, { date: 1461168720000, open: 56.09, high: 56.09, low: 56.05, close: 56.05, volume: 26845, }, { date: 1461168780000, open: 56.0536, high: 56.065, low: 56.045, close: 56.05, volume: 36555, }, { date: 1461168840000, open: 56.05, high: 56.055, low: 56.04, close: 56.04, volume: 20900, }, { date: 1461168900000, open: 56.045, high: 56.08, low: 56.04, close: 56.06, volume: 31308, }, { date: 1461168960000, open: 56.07, high: 56.075, low: 56.005, close: 56.015, volume: 58612, }, { date: 1461169020000, open: 56.016, high: 56.0273, low: 56.01, close: 56.0259, volume: 17619, }, { date: 1461169080000, open: 56.0258, high: 56.03, low: 56.02, close: 56.025, volume: 39198, }, { date: 1461169140000, open: 56.025, high: 56.03, low: 56, close: 56, volume: 49785, }, { date: 1461169200000, open: 56, high: 56.02, low: 56, close: 56.02, volume: 44178, }, { date: 1461169260000, open: 56.024, high: 56.04, low: 56.02, close: 56.02, volume: 35403, }, { date: 1461169320000, open: 56.03, high: 56.04, low: 56.02, close: 56.04, volume: 29585, }, { date: 1461169380000, open: 56.035, high: 56.04, low: 56.03, close: 56.03, volume: 8755, }, { date: 1461169440000, open: 56.03, high: 56.05, low: 56.03, close: 56.041, volume: 18849, }, { date: 1461169500000, open: 56.0438, high: 56.085, low: 56.0438, close: 56.075, volume: 35509, }, { date: 1461169560000, open: 56.08, high: 56.09, low: 56.07, close: 56.08, volume: 22323, }, { date: 1461169620000, open: 56.075, high: 56.08, low: 56.07, close: 56.08, volume: 5723, }, { date: 1461169680000, open: 56.07, high: 56.1, low: 56.07, close: 56.08, volume: 23264, }, { date: 1461169740000, open: 56.085, high: 56.1, low: 56.08, close: 56.09, volume: 15307, }, { date: 1461169800000, open: 56.09, high: 56.12, low: 56.09, close: 56.09, volume: 70795, }, { date: 1461169860000, open: 56.09, high: 56.11, low: 56.09, close: 56.1, volume: 29875, }, { date: 1461169920000, open: 56.0964, high: 56.1, low: 56.05, close: 56.07, volume: 30617, }, { date: 1461169980000, open: 56.065, high: 56.0799, low: 56.06, close: 56.065, volume: 14541, }, { date: 1461170040000, open: 56.065, high: 56.1, low: 56.065, close: 56.1, volume: 27068, }, { date: 1461170100000, open: 56.1, high: 56.1, low: 56.095, close: 56.095, volume: 14410, }, { date: 1461170160000, open: 56.1, high: 56.11, low: 56.1, close: 56.105, volume: 14683, }, { date: 1461170220000, open: 56.1, high: 56.11, low: 56.1, close: 56.1, volume: 10852, }, { date: 1461170280000, open: 56.105, high: 56.13, low: 56.1, close: 56.11, volume: 111444, }, { date: 1461170340000, open: 56.11, high: 56.1371, low: 56.1002, close: 56.13, volume: 36611, }, { date: 1461170400000, open: 56.12, high: 56.15, low: 56.12, close: 56.1499, volume: 18240, }, { date: 1461170460000, open: 56.145, high: 56.16, low: 56.1301, close: 56.16, volume: 53235, }, { date: 1461170520000, open: 56.15, high: 56.15, low: 56.13, close: 56.13, volume: 27589, }, { date: 1461170580000, open: 56.1272, high: 56.1361, low: 56.12, close: 56.13, volume: 25563, }, { date: 1461170640000, open: 56.125, high: 56.13, low: 56.1029, close: 56.1029, volume: 35203, }, { date: 1461170700000, open: 56.1, high: 56.1338, low: 56.1, close: 56.1231, volume: 54983, }, { date: 1461170760000, open: 56.12, high: 56.13, low: 56.11, close: 56.125, volume: 12385, }, { date: 1461170820000, open: 56.125, high: 56.135, low: 56.11, close: 56.129, volume: 26956, }, { date: 1461170880000, open: 56.12, high: 56.12, low: 56.1, close: 56.105, volume: 17255, }, { date: 1461170940000, open: 56.1015, high: 56.13, low: 56.1015, close: 56.11, volume: 24600, }, { date: 1461171000000, open: 56.116, high: 56.12, low: 56.1, close: 56.11, volume: 25454, }, { date: 1461171060000, open: 56.11, high: 56.11, low: 56.1, close: 56.105, volume: 63412, }, { date: 1461171120000, open: 56.1036, high: 56.11, low: 56.1, close: 56.1, volume: 43992, }, { date: 1461171180000, open: 56.1, high: 56.125, low: 56.1, close: 56.1, volume: 30635, }, { date: 1461171240000, open: 56.105, high: 56.12, low: 56.105, close: 56.12, volume: 30427, }, { date: 1461171300000, open: 56.12, high: 56.12, low: 56.095, close: 56.1, volume: 49622, }, { date: 1461171360000, open: 56.1, high: 56.12, low: 56.095, close: 56.11, volume: 49025, }, { date: 1461171420000, open: 56.11, high: 56.12, low: 56.105, close: 56.11, volume: 20027, }, { date: 1461171480000, open: 56.115, high: 56.125, low: 56.11, close: 56.12, volume: 26433, }, { date: 1461171540000, open: 56.11, high: 56.12, low: 56.105, close: 56.11, volume: 15629, }, { date: 1461171600000, open: 56.1199, high: 56.1199, low: 56.07, close: 56.0864, volume: 37378, }, { date: 1461171660000, open: 56.0825, high: 56.1, low: 56.07, close: 56.1, volume: 29675, }, { date: 1461171720000, open: 56.095, high: 56.12, low: 56.09, close: 56.115, volume: 27894, }, { date: 1461171780000, open: 56.11, high: 56.12, low: 56.1, close: 56.1159, volume: 13513, }, { date: 1461171840000, open: 56.115, high: 56.12, low: 56.11, close: 56.11, volume: 26306, }, { date: 1461171900000, open: 56.116, high: 56.12, low: 56.09, close: 56.11, volume: 19388, }, { date: 1461171960000, open: 56.1199, high: 56.1199, low: 56.09, close: 56.093, volume: 33772, }, { date: 1461172020000, open: 56.09, high: 56.095, low: 56.03, close: 56.04, volume: 49468, }, { date: 1461172080000, open: 56.0375, high: 56.0375, low: 56, close: 56.0065, volume: 76136, }, { date: 1461172140000, open: 56.01, high: 56.03, low: 56, close: 56.0101, volume: 67103, }, { date: 1461172200000, open: 56.01, high: 56.0163, low: 56, close: 56.005, volume: 17844, }, { date: 1461172260000, open: 56.01, high: 56.03, low: 56, close: 56.0201, volume: 17276, }, { date: 1461172320000, open: 56.03, high: 56.03, low: 56.015, close: 56.02, volume: 46617, }, { date: 1461172380000, open: 56.03, high: 56.035, low: 56.0132, close: 56.02, volume: 24726, }, { date: 1461172440000, open: 56.0236, high: 56.04, low: 56.02, close: 56.035, volume: 15601, }, { date: 1461172500000, open: 56.04, high: 56.04, low: 56.03, close: 56.035, volume: 96644, }, { date: 1461172560000, open: 56.035, high: 56.05, low: 56.0338, close: 56.0338, volume: 90578, }, { date: 1461172620000, open: 56.035, high: 56.04, low: 56.025, close: 56.035, volume: 19841, }, { date: 1461172680000, open: 56.04, high: 56.07, low: 56.035, close: 56.06, volume: 31843, }, { date: 1461172740000, open: 56.05, high: 56.06, low: 56.05, close: 56.06, volume: 17203, }, { date: 1461172800000, open: 56.0532, high: 56.06, low: 56.05, close: 56.055, volume: 10214, }, { date: 1461172860000, open: 56.0571, high: 56.06, low: 56.01, close: 56.0236, volume: 66582, }, { date: 1461172920000, open: 56.021, high: 56.05, low: 56.0205, close: 56.05, volume: 42305, }, { date: 1461172980000, open: 56.05, high: 56.06, low: 56.045, close: 56.055, volume: 48512, }, { date: 1461173040000, open: 56.05, high: 56.06, low: 56.05, close: 56.06, volume: 9988, }, { date: 1461173100000, open: 56.0577, high: 56.08, low: 56.05, close: 56.075, volume: 33233, }, { date: 1461173160000, open: 56.075, high: 56.115, low: 56.07, close: 56.1075, volume: 92450, }, { date: 1461173220000, open: 56.115, high: 56.115, low: 56.09, close: 56.1, volume: 117499, }, { date: 1461173280000, open: 56.1, high: 56.11, low: 56.06, close: 56.1, volume: 132283, }, { date: 1461173340000, open: 56.1, high: 56.11, low: 56.09, close: 56.1, volume: 290616, }, { date: 1461173400000, open: 56.1, high: 56.11, low: 56.06, close: 56.065, volume: 264616, }, { date: 1461173460000, open: 56.065, high: 56.09, low: 56.055, close: 56.065, volume: 78773, }, { date: 1461173520000, open: 56.065, high: 56.085, low: 56.06, close: 56.085, volume: 59672, }, { date: 1461173580000, open: 56.085, high: 56.095, low: 56.075, close: 56.08, volume: 78018, }, { date: 1461173640000, open: 56.085, high: 56.1, low: 56.07, close: 56.09, volume: 50521, }, { date: 1461173700000, open: 56.1, high: 56.12, low: 56.08, close: 56.0858, volume: 70047, }, { date: 1461173760000, open: 56.085, high: 56.1, low: 56.0799, close: 56.085, volume: 50258, }, { date: 1461173820000, open: 56.09, high: 56.095, low: 56.07, close: 56.075, volume: 70013, }, { date: 1461173880000, open: 56.075, high: 56.08, low: 56.05, close: 56.05, volume: 59659, }, { date: 1461173940000, open: 56.05, high: 56.06, low: 56.05, close: 56.055, volume: 26365, }, { date: 1461174000000, open: 56.05, high: 56.075, low: 56.04, close: 56.045, volume: 72085, }, { date: 1461174060000, open: 56.05, high: 56.06, low: 56.035, close: 56.035, volume: 36744, }, { date: 1461174120000, open: 56.035, high: 56.04, low: 56.01, close: 56.01, volume: 48621, }, { date: 1461174180000, open: 56.01, high: 56.0164, low: 56, close: 56.005, volume: 45690, }, { date: 1461174240000, open: 56.004, high: 56.01, low: 56, close: 56.005, volume: 35910, }, { date: 1461174300000, open: 56.0036, high: 56.01, low: 55.98, close: 56, volume: 45039, }, { date: 1461174360000, open: 56.005, high: 56.01, low: 55.99, close: 56.005, volume: 98151, }, { date: 1461174420000, open: 56.005, high: 56.0188, low: 55.99, close: 56, volume: 25789, }, { date: 1461174480000, open: 56, high: 56.05, low: 55.995, close: 56.04, volume: 57431, }, { date: 1461174540000, open: 56.03, high: 56.05, low: 56.01, close: 56.035, volume: 61221, }, { date: 1461174600000, open: 56.03, high: 56.035, low: 56.02, close: 56.02, volume: 28760, }, { date: 1461174660000, open: 56.03, high: 56.04, low: 56.02, close: 56.02, volume: 24296, }, { date: 1461174720000, open: 56.025, high: 56.05, low: 56.02, close: 56.05, volume: 56435, }, { date: 1461174780000, open: 56.04, high: 56.05, low: 56.03, close: 56.04, volume: 39903, }, { date: 1461174840000, open: 56.04, high: 56.05, low: 56.03, close: 56.03, volume: 39587, }, { date: 1461174900000, open: 56.035, high: 56.05, low: 56.02, close: 56.035, volume: 42901, }, { date: 1461174960000, open: 56.035, high: 56.05, low: 56.03, close: 56.035, volume: 27129, }, { date: 1461175020000, open: 56.03, high: 56.05, low: 56.02, close: 56.035, volume: 21751, }, { date: 1461175080000, open: 56.03, high: 56.035, low: 56.01, close: 56.015, volume: 26766, }, { date: 1461175140000, open: 56.01, high: 56.02, low: 56, close: 56.005, volume: 35957, }, { date: 1461175200000, open: 56.01, high: 56.02, low: 56, close: 56.01, volume: 36083, }, { date: 1461175260000, open: 56.01, high: 56.01, low: 55.99, close: 56.005, volume: 71318, }, { date: 1461175320000, open: 56.005, high: 56.02, low: 55.99, close: 55.995, volume: 45462, }, { date: 1461175380000, open: 55.9959, high: 55.9999, low: 55.97, close: 55.9899, volume: 39425, }, { date: 1461175440000, open: 55.985, high: 56.01, low: 55.985, close: 56, volume: 49157, }, { date: 1461175500000, open: 56.0001, high: 56.02, low: 56, close: 56.01, volume: 32499, }, { date: 1461175560000, open: 56.015, high: 56.02, low: 56, close: 56, volume: 36354, }, { date: 1461175620000, open: 56.0032, high: 56.01, low: 55.99, close: 56, volume: 60421, }, { date: 1461175680000, open: 56, high: 56, low: 55.99, close: 56, volume: 35496, }, { date: 1461175740000, open: 55.9974, high: 56, low: 55.99, close: 55.995, volume: 23513, }, { date: 1461175800000, open: 55.995, high: 56.005, low: 55.98, close: 56, volume: 75494, }, { date: 1461175860000, open: 55.995, high: 56.01, low: 55.99, close: 55.99, volume: 59751, }, { date: 1461175920000, open: 55.99, high: 56, low: 55.99, close: 55.995, volume: 55027, }, { date: 1461175980000, open: 55.9999, high: 56.005, low: 55.99, close: 55.995, volume: 52885, }, { date: 1461176040000, open: 56, high: 56, low: 55.99, close: 55.99, volume: 38682, }, { date: 1461176100000, open: 56, high: 56, low: 55.99, close: 56, volume: 55795, }, { date: 1461176160000, open: 56, high: 56, low: 55.99, close: 55.9953, volume: 107195, }, { date: 1461176220000, open: 55.995, high: 56.005, low: 55.99, close: 55.995, volume: 96075, }, { date: 1461176280000, open: 56, high: 56.015, low: 55.9929, close: 55.995, volume: 147841, }, { date: 1461176340000, open: 55.9965, high: 56, low: 55.99, close: 55.995, volume: 38981, }, { date: 1461176400000, open: 56, high: 56.03, low: 55.9901, close: 56.02, volume: 135413, }, { date: 1461176460000, open: 56.015, high: 56.03, low: 56, close: 56.0264, volume: 20646, }, { date: 1461176520000, open: 56.03, high: 56.1, low: 56.03, close: 56.0901, volume: 54048, }, { date: 1461176580000, open: 56.095, high: 56.105, low: 56.085, close: 56.1, volume: 107865, }, { date: 1461176640000, open: 56.095, high: 56.11, low: 56.06, close: 56.065, volume: 117314, }, { date: 1461176700000, open: 56.0662, high: 56.07, low: 56.025, close: 56.045, volume: 120466, }, { date: 1461176760000, open: 56.05, high: 56.05, low: 56.02, close: 56.035, volume: 69025, }, { date: 1461176820000, open: 56.04, high: 56.04, low: 55.995, close: 56.005, volume: 131048, }, { date: 1461176880000, open: 56.008, high: 56.02, low: 55.99, close: 56.02, volume: 53525, }, { date: 1461176940000, open: 56.01, high: 56.01, low: 55.98, close: 55.995, volume: 48961, }, { date: 1461177000000, open: 55.995, high: 56.02, low: 55.9932, close: 56.02, volume: 41443, }, { date: 1461177060000, open: 56.02, high: 56.05, low: 56.01, close: 56.035, volume: 56776, }, { date: 1461177120000, open: 56.04, high: 56.05, low: 56.015, close: 56.025, volume: 63097, }, { date: 1461177180000, open: 56.0263, high: 56.04, low: 56.02, close: 56.0268, volume: 66473, }, { date: 1461177240000, open: 56.03, high: 56.04, low: 55.98, close: 56.005, volume: 130889, }, { date: 1461177300000, open: 56.01, high: 56.01, low: 55.99, close: 55.995, volume: 104269, }, { date: 1461177360000, open: 55.995, high: 56, low: 55.97, close: 55.975, volume: 265926, }, { date: 1461177420000, open: 55.98, high: 56, low: 55.975, close: 55.995, volume: 82133, }, { date: 1461177480000, open: 56, high: 56, low: 55.98, close: 55.985, volume: 79856, }, { date: 1461177540000, open: 55.984, high: 55.99, low: 55.98, close: 55.985, volume: 44153, }, { date: 1461177600000, open: 55.985, high: 56.02, low: 55.98, close: 56.01, volume: 78484, }, { date: 1461177660000, open: 56, high: 56.01, low: 55.99, close: 56, volume: 81828, }, { date: 1461177720000, open: 56.0028, high: 56.045, low: 55.9936, close: 56.025, volume: 84556, }, { date: 1461177780000, open: 56.025, high: 56.05, low: 56.02, close: 56.045, volume: 40240, }, { date: 1461177840000, open: 56.0475, high: 56.05, low: 56.04, close: 56.045, volume: 32870, }, { date: 1461177900000, open: 56.04, high: 56.05, low: 56.02, close: 56.02, volume: 35162, }, { date: 1461177960000, open: 56.025, high: 56.04, low: 56.01, close: 56.01, volume: 39869, }, { date: 1461178020000, open: 56.02, high: 56.03, low: 56.01, close: 56.015, volume: 51847, }, { date: 1461178080000, open: 56.01, high: 56.01, low: 55.98, close: 55.985, volume: 101329, }, { date: 1461178140000, open: 55.985, high: 56, low: 55.98, close: 55.985, volume: 98152, }, { date: 1461178200000, open: 55.9832, high: 55.99, low: 55.98, close: 55.985, volume: 16829, }, { date: 1461178260000, open: 55.989, high: 56.01, low: 55.98, close: 56, volume: 134433, }, { date: 1461178320000, open: 56, high: 56, low: 55.98, close: 55.985, volume: 95983, }, { date: 1461178380000, open: 55.985, high: 56, low: 55.98, close: 55.98, volume: 80116, }, { date: 1461178440000, open: 55.9836, high: 55.99, low: 55.95, close: 55.95, volume: 62342, }, { date: 1461178500000, open: 55.95, high: 55.96, low: 55.95, close: 55.955, volume: 46995, }, { date: 1461178560000, open: 55.9505, high: 55.98, low: 55.95, close: 55.97, volume: 38268, }, { date: 1461178620000, open: 55.975, high: 55.985, low: 55.96, close: 55.965, volume: 57285, }, { date: 1461178680000, open: 55.96, high: 55.97, low: 55.87, close: 55.875, volume: 182554, }, { date: 1461178740000, open: 55.9, high: 55.93, low: 55.89, close: 55.9264, volume: 117582, }, { date: 1461178800000, open: 55.92, high: 55.93, low: 55.89, close: 55.93, volume: 115230, }, { date: 1461178860000, open: 55.925, high: 56, low: 55.925, close: 56, volume: 68555, }, { date: 1461178920000, open: 55.995, high: 56.005, low: 55.98, close: 55.9864, volume: 123366, }, { date: 1461178980000, open: 55.98, high: 56, low: 55.965, close: 55.99, volume: 122891, }, { date: 1461179040000, open: 55.985, high: 56.0099, low: 55.984, close: 56.005, volume: 58665, }, { date: 1461179100000, open: 56.0001, high: 56.005, low: 55.98, close: 56.005, volume: 82941, }, { date: 1461179160000, open: 56.005, high: 56.01, low: 55.98, close: 55.9864, volume: 72680, }, { date: 1461179220000, open: 55.985, high: 56.01, low: 55.985, close: 56.0066, volume: 37874, }, { date: 1461179280000, open: 56.005, high: 56.03, low: 56, close: 56, volume: 98964, }, { date: 1461179340000, open: 56.0099, high: 56.01, low: 55.9805, close: 55.9939, volume: 63755, }, { date: 1461179400000, open: 55.9901, high: 56.0075, low: 55.99, close: 56.005, volume: 44962, }, { date: 1461179460000, open: 56, high: 56.02, low: 56, close: 56.01, volume: 29923, }, { date: 1461179520000, open: 56.0128, high: 56.015, low: 55.94, close: 55.975, volume: 129149, }, { date: 1461179580000, open: 55.98, high: 55.98, low: 55.94, close: 55.95, volume: 65913, }, { date: 1461179640000, open: 55.95, high: 55.96, low: 55.92, close: 55.94, volume: 110265, }, { date: 1461179700000, open: 55.95, high: 55.96, low: 55.935, close: 55.935, volume: 40519, }, { date: 1461179760000, open: 55.94, high: 55.945, low: 55.89, close: 55.9, volume: 117821, }, { date: 1461179820000, open: 55.905, high: 55.91, low: 55.84, close: 55.85, volume: 102122, }, { date: 1461179880000, open: 55.845, high: 55.87, low: 55.8, close: 55.8, volume: 270141, }, { date: 1461179940000, open: 55.81, high: 55.876, low: 55.805, close: 55.865, volume: 94374, }, { date: 1461180000000, open: 55.86, high: 55.86, low: 55.815, close: 55.83, volume: 82942, }, { date: 1461180060000, open: 55.83, high: 55.83, low: 55.8, close: 55.8162, volume: 83895, }, { date: 1461180120000, open: 55.81, high: 55.82, low: 55.79, close: 55.8099, volume: 66906, }, { date: 1461180180000, open: 55.8, high: 55.815, low: 55.79, close: 55.81, volume: 84983, }, { date: 1461180240000, open: 55.815, high: 55.83, low: 55.8, close: 55.83, volume: 88494, }, { date: 1461180300000, open: 55.825, high: 55.85, low: 55.82, close: 55.83, volume: 83504, }, { date: 1461180360000, open: 55.84, high: 55.84, low: 55.82, close: 55.83, volume: 48743, }, { date: 1461180420000, open: 55.835, high: 55.86, low: 55.835, close: 55.845, volume: 87067, }, { date: 1461180480000, open: 55.845, high: 55.86, low: 55.83, close: 55.85, volume: 58691, }, { date: 1461180540000, open: 55.854, high: 55.87, low: 55.83, close: 55.835, volume: 59300, }, { date: 1461180600000, open: 55.84, high: 55.84, low: 55.78, close: 55.78, volume: 127204, }, { date: 1461180660000, open: 55.785, high: 55.82, low: 55.78, close: 55.795, volume: 165652, }, { date: 1461180720000, open: 55.8, high: 55.805, low: 55.78, close: 55.79, volume: 93393, }, { date: 1461180780000, open: 55.79, high: 55.9, low: 55.71, close: 55.725, volume: 345336, }, { date: 1461180840000, open: 55.73, high: 55.73, low: 55.67, close: 55.685, volume: 317205, }, { date: 1461180900000, open: 55.686, high: 55.7, low: 55.68, close: 55.6901, volume: 135570, }, { date: 1461180960000, open: 55.6999, high: 55.7, low: 55.68, close: 55.68, volume: 168583, }, { date: 1461181020000, open: 55.68, high: 55.695, low: 55.63, close: 55.6336, volume: 246287, }, { date: 1461181080000, open: 55.64, high: 55.68, low: 55.63, close: 55.645, volume: 167430, }, { date: 1461181140000, open: 55.65, high: 55.65, low: 55.58, close: 55.595, volume: 281465, }, { date: 1461181200000, open: 55.59, high: 55.605, low: 55.56, close: 55.57, volume: 174274, }, { date: 1461181260000, open: 55.58, high: 55.6, low: 55.565, close: 55.57, volume: 145470, }, { date: 1461181320000, open: 55.565, high: 55.59, low: 55.55, close: 55.57, volume: 101179, }, { date: 1461181380000, open: 55.57, high: 55.6, low: 55.55, close: 55.59, volume: 117354, }, { date: 1461181440000, open: 55.595, high: 55.6064, low: 55.54, close: 55.55, volume: 172688, }, { date: 1461181500000, open: 55.54, high: 55.54, low: 55.49, close: 55.49, volume: 170450, }, { date: 1461181560000, open: 55.495, high: 55.51, low: 55.49, close: 55.505, volume: 551623, }, { date: 1461181620000, open: 55.51, high: 55.54, low: 55.5, close: 55.535, volume: 135578, }, { date: 1461181680000, open: 55.535, high: 55.54, low: 55.5, close: 55.53, volume: 190103, }, { date: 1461181740000, open: 55.53, high: 55.59, low: 55.495, close: 55.5705, volume: 179830, }, { date: 1461181800000, open: 55.57, high: 55.58, low: 55.51, close: 55.52, volume: 111026, }, { date: 1461181860000, open: 55.52, high: 55.55, low: 55.5, close: 55.524, volume: 212081, }, { date: 1461181920000, open: 55.52, high: 55.53, low: 55.49, close: 55.505, volume: 358237, }, { date: 1461181980000, open: 55.51, high: 55.51, low: 55.49, close: 55.505, volume: 296595, }, { date: 1461182040000, open: 55.505, high: 55.51, low: 55.49, close: 55.5, volume: 185108, }, { date: 1461182100000, open: 55.5, high: 55.5, low: 55.485, close: 55.5, volume: 193689, }, { date: 1461182160000, open: 55.49, high: 55.56, low: 55.49, close: 55.55, volume: 617918, }, { date: 1461182220000, open: 55.55, high: 55.63, low: 55.54, close: 55.595, volume: 374023, }, { date: 1461182280000, open: 55.6, high: 55.63, low: 55.59, close: 55.6199, volume: 175786, }, { date: 1461182340000, open: 55.62, high: 55.63, low: 55.6, close: 55.615, volume: 271159, }, { date: 1461182400000, open: 55.615, high: 55.63, low: 55.55, close: 55.59, volume: 1837931, }, { date: 1461245400000, open: 55.8, high: 55.8, low: 55.57, close: 55.68, volume: 394608, }, { date: 1461245460000, open: 55.62, high: 55.78, low: 55.61, close: 55.67, volume: 141305, }, { date: 1461245520000, open: 55.67, high: 55.74, low: 55.58, close: 55.69, volume: 217016, }, { date: 1461245580000, open: 55.68, high: 55.75, low: 55.68, close: 55.75, volume: 83016, }, { date: 1461245640000, open: 55.75, high: 55.8, low: 55.73, close: 55.77, volume: 64766, }, { date: 1461245700000, open: 55.77, high: 55.88, low: 55.77, close: 55.865, volume: 96922, }, { date: 1461245760000, open: 55.865, high: 55.89, low: 55.83, close: 55.88, volume: 149103, }, { date: 1461245820000, open: 55.89, high: 55.92, low: 55.835, close: 55.855, volume: 106536, }, { date: 1461245880000, open: 55.84, high: 55.9, low: 55.82, close: 55.9, volume: 139417, }, { date: 1461245940000, open: 55.91, high: 55.92, low: 55.865, close: 55.88, volume: 109358, }, { date: 1461246000000, open: 55.883, high: 55.98, low: 55.883, close: 55.9298, volume: 131984, }, { date: 1461246060000, open: 55.925, high: 55.935, low: 55.87, close: 55.9, volume: 108244, }, { date: 1461246120000, open: 55.895, high: 55.97, low: 55.895, close: 55.93, volume: 147380, }, { date: 1461246180000, open: 55.9334, high: 56.01, low: 55.92, close: 56.005, volume: 138520, }, { date: 1461246240000, open: 56.005, high: 56.01, low: 55.96, close: 55.97, volume: 68712, }, { date: 1461246300000, open: 55.965, high: 55.97, low: 55.905, close: 55.95, volume: 68578, }, { date: 1461246360000, open: 55.95, high: 55.955, low: 55.91, close: 55.9241, volume: 131194, }, { date: 1461246420000, open: 55.93, high: 55.93, low: 55.87, close: 55.89, volume: 181244, }, { date: 1461246480000, open: 55.89, high: 55.9, low: 55.795, close: 55.795, volume: 775871, }, { date: 1461246540000, open: 55.795, high: 55.8, low: 55.705, close: 55.72, volume: 176953, }, { date: 1461246600000, open: 55.72, high: 55.73, low: 55.64, close: 55.64, volume: 229244, }, { date: 1461246660000, open: 55.65, high: 55.65, low: 55.5699, close: 55.58, volume: 242726, }, { date: 1461246720000, open: 55.58, high: 55.61, low: 55.57, close: 55.6, volume: 84502, }, { date: 1461246780000, open: 55.609, high: 55.61, low: 55.49, close: 55.495, volume: 217634, }, { date: 1461246840000, open: 55.49, high: 55.525, low: 55.47, close: 55.5063, volume: 191519, }, { date: 1461246900000, open: 55.505, high: 55.538, low: 55.5, close: 55.51, volume: 68235, }, { date: 1461246960000, open: 55.51, high: 55.52, low: 55.415, close: 55.445, volume: 145492, }, { date: 1461247020000, open: 55.445, high: 55.48, low: 55.42, close: 55.455, volume: 80729, }, { date: 1461247080000, open: 55.455, high: 55.54, low: 55.45, close: 55.535, volume: 51016, }, { date: 1461247140000, open: 55.535, high: 55.57, low: 55.53, close: 55.56, volume: 64206, }, { date: 1461247200000, open: 55.55, high: 55.55, low: 55.5, close: 55.51, volume: 90864, }, { date: 1461247260000, open: 55.505, high: 55.54, low: 55.5, close: 55.515, volume: 86736, }, { date: 1461247320000, open: 55.52, high: 55.615, low: 55.5101, close: 55.605, volume: 112084, }, { date: 1461247380000, open: 55.62, high: 55.63, low: 55.585, close: 55.6, volume: 39177, }, { date: 1461247440000, open: 55.5962, high: 55.6, low: 55.57, close: 55.58, volume: 84970, }, { date: 1461247500000, open: 55.58, high: 55.59, low: 55.57, close: 55.575, volume: 71818, }, { date: 1461247560000, open: 55.575, high: 55.61, low: 55.5578, close: 55.61, volume: 155135, }, { date: 1461247620000, open: 55.605, high: 55.62, low: 55.585, close: 55.6, volume: 59065, }, { date: 1461247680000, open: 55.6, high: 55.67, low: 55.59, close: 55.66, volume: 135049, }, { date: 1461247740000, open: 55.67, high: 55.7501, low: 55.66, close: 55.6899, volume: 131528, }, { date: 1461247800000, open: 55.6862, high: 55.74, low: 55.685, close: 55.715, volume: 110709, }, { date: 1461247860000, open: 55.71, high: 55.725, low: 55.705, close: 55.715, volume: 67443, }, { date: 1461247920000, open: 55.715, high: 55.715, low: 55.595, close: 55.605, volume: 183903, }, { date: 1461247980000, open: 55.61, high: 55.64, low: 55.565, close: 55.635, volume: 109718, }, { date: 1461248040000, open: 55.6305, high: 55.64, low: 55.61, close: 55.625, volume: 43534, }, { date: 1461248100000, open: 55.6235, high: 55.66, low: 55.62, close: 55.6599, volume: 82212, }, { date: 1461248160000, open: 55.655, high: 55.655, low: 55.6, close: 55.604, volume: 70935, }, { date: 1461248220000, open: 55.61, high: 55.64, low: 55.58, close: 55.595, volume: 73409, }, { date: 1461248280000, open: 55.59, high: 55.595, low: 55.54, close: 55.54, volume: 56183, }, { date: 1461248340000, open: 55.545, high: 55.56, low: 55.51, close: 55.5538, volume: 89783, }, { date: 1461248400000, open: 55.555, high: 55.62, low: 55.545, close: 55.62, volume: 78668, }, { date: 1461248460000, open: 55.615, high: 55.64, low: 55.61, close: 55.62, volume: 71413, }, { date: 1461248520000, open: 55.615, high: 55.655, low: 55.61, close: 55.6501, volume: 144162, }, { date: 1461248580000, open: 55.66, high: 55.66, low: 55.59, close: 55.63, volume: 112863, }, { date: 1461248640000, open: 55.625, high: 55.65, low: 55.625, close: 55.65, volume: 33921, }, { date: 1461248700000, open: 55.66, high: 55.66, low: 55.62, close: 55.63, volume: 52820, }, { date: 1461248760000, open: 55.63, high: 55.65, low: 55.63, close: 55.63, volume: 69133, }, { date: 1461248820000, open: 55.63, high: 55.655, low: 55.625, close: 55.64, volume: 62336, }, { date: 1461248880000, open: 55.65, high: 55.72, low: 55.645, close: 55.695, volume: 55319, }, { date: 1461248940000, open: 55.6988, high: 55.7, low: 55.675, close: 55.69, volume: 43721, }, { date: 1461249000000, open: 55.695, high: 55.7298, low: 55.69, close: 55.7, volume: 62250, }, { date: 1461249060000, open: 55.705, high: 55.75, low: 55.69, close: 55.7, volume: 65046, }, { date: 1461249120000, open: 55.6972, high: 55.71, low: 55.69, close: 55.695, volume: 37223, }, { date: 1461249180000, open: 55.7, high: 55.715, low: 55.69, close: 55.71, volume: 45710, }, { date: 1461249240000, open: 55.715, high: 55.72, low: 55.69, close: 55.715, volume: 47566, }, { date: 1461249300000, open: 55.71, high: 55.71, low: 55.66, close: 55.71, volume: 52292, }, { date: 1461249360000, open: 55.71, high: 55.7499, low: 55.71, close: 55.74, volume: 45578, }, { date: 1461249420000, open: 55.75, high: 55.785, low: 55.74, close: 55.775, volume: 74976, }, { date: 1461249480000, open: 55.77, high: 55.8, low: 55.72, close: 55.7378, volume: 80686, }, { date: 1461249540000, open: 55.74, high: 55.805, low: 55.73, close: 55.7844, volume: 150947, }, { date: 1461249600000, open: 55.79, high: 55.85, low: 55.79, close: 55.84, volume: 77347, }, { date: 1461249660000, open: 55.85, high: 55.955, low: 55.84, close: 55.955, volume: 97513, }, { date: 1461249720000, open: 55.95, high: 55.97, low: 55.9, close: 55.925, volume: 81231, }, { date: 1461249780000, open: 55.93, high: 56, low: 55.925, close: 55.995, volume: 43332, }, { date: 1461249840000, open: 56, high: 56.03, low: 55.96, close: 55.97, volume: 76708, }, { date: 1461249900000, open: 55.965, high: 55.967, low: 55.9, close: 55.91, volume: 80585, }, { date: 1461249960000, open: 55.91, high: 55.955, low: 55.9, close: 55.95, volume: 52528, }, { date: 1461250020000, open: 55.95, high: 55.9765, low: 55.9401, close: 55.95, volume: 51379, }, { date: 1461250080000, open: 55.95, high: 55.97, low: 55.95, close: 55.95, volume: 42724, }, { date: 1461250140000, open: 55.95, high: 55.96, low: 55.9499, close: 55.955, volume: 33843, }, { date: 1461250200000, open: 55.955, high: 55.97, low: 55.94, close: 55.955, volume: 62949, }, { date: 1461250260000, open: 55.96, high: 55.98, low: 55.95, close: 55.961, volume: 46611, }, { date: 1461250320000, open: 55.965, high: 55.9798, low: 55.94, close: 55.9798, volume: 96018, }, { date: 1461250380000, open: 55.98, high: 56.005, low: 55.97, close: 56, volume: 86256, }, { date: 1461250440000, open: 56, high: 56.01, low: 55.97, close: 55.99, volume: 43681, }, { date: 1461250500000, open: 55.99, high: 56.009, low: 55.9775, close: 55.995, volume: 45996, }, { date: 1461250560000, open: 56, high: 56.02, low: 55.995, close: 56.01, volume: 68503, }, { date: 1461250620000, open: 56.014, high: 56.1, low: 56.01, close: 56.09, volume: 80717, }, { date: 1461250680000, open: 56.09, high: 56.115, low: 56.07, close: 56.095, volume: 41859, }, { date: 1461250740000, open: 56.095, high: 56.11, low: 56.03, close: 56.035, volume: 77287, }, { date: 1461250800000, open: 56.03, high: 56.065, low: 56.01, close: 56.05, volume: 59225, }, { date: 1461250860000, open: 56.05, high: 56.07, low: 56.03, close: 56.055, volume: 73655, }, { date: 1461250920000, open: 56.055, high: 56.15, low: 56.055, close: 56.13, volume: 204758, }, { date: 1461250980000, open: 56.13, high: 56.14, low: 56.09, close: 56.13, volume: 168745, }, { date: 1461251040000, open: 56.13, high: 56.15, low: 56.1, close: 56.1, volume: 140785, }, { date: 1461251100000, open: 56.109, high: 56.12, low: 56.09, close: 56.105, volume: 76646, }, { date: 1461251160000, open: 56.105, high: 56.12, low: 56.095, close: 56.1, volume: 162132, }, { date: 1461251220000, open: 56.092, high: 56.095, low: 56.05, close: 56.055, volume: 49583, }, { date: 1461251280000, open: 56.055, high: 56.07, low: 56.045, close: 56.06, volume: 65826, }, { date: 1461251340000, open: 56.06, high: 56.1, low: 56.055, close: 56.075, volume: 57313, }, { date: 1461251400000, open: 56.0701, high: 56.09, low: 56.055, close: 56.07, volume: 40083, }, { date: 1461251460000, open: 56.0632, high: 56.075, low: 56.05, close: 56.07, volume: 29781, }, { date: 1461251520000, open: 56.07, high: 56.07, low: 56.04, close: 56.05, volume: 48102, }, { date: 1461251580000, open: 56.0525, high: 56.06, low: 56.045, close: 56.045, volume: 32933, }, { date: 1461251640000, open: 56.04, high: 56.04, low: 56, close: 56.0099, volume: 101752, }, { date: 1461251700000, open: 56.01, high: 56.03, low: 56, close: 56.03, volume: 40476, }, { date: 1461251760000, open: 56.03, high: 56.055, low: 56.025, close: 56.025, volume: 50376, }, { date: 1461251820000, open: 56.03, high: 56.045, low: 55.994, close: 56.005, volume: 94829, }, { date: 1461251880000, open: 56.01, high: 56.01, low: 55.9701, close: 55.99, volume: 40753, }, { date: 1461251940000, open: 55.99, high: 56, low: 55.97, close: 55.99, volume: 35290, }, { date: 1461252000000, open: 55.985, high: 56, low: 55.98, close: 56, volume: 27726, }, { date: 1461252060000, open: 55.995, high: 56.02, low: 55.99, close: 56, volume: 26916, }, { date: 1461252120000, open: 56, high: 56.04, low: 55.99, close: 56.03, volume: 37822, }, { date: 1461252180000, open: 56.035, high: 56.09, low: 56.03, close: 56.08, volume: 49490, }, { date: 1461252240000, open: 56.08, high: 56.1, low: 56.08, close: 56.085, volume: 41263, }, { date: 1461252300000, open: 56.0805, high: 56.15, low: 56.08, close: 56.125, volume: 83156, }, { date: 1461252360000, open: 56.12, high: 56.15, low: 56.12, close: 56.145, volume: 43016, }, { date: 1461252420000, open: 56.1465, high: 56.19, low: 56.13, close: 56.19, volume: 104639, }, { date: 1461252480000, open: 56.185, high: 56.23, low: 56.15, close: 56.165, volume: 182747, }, { date: 1461252540000, open: 56.165, high: 56.1764, low: 56.08, close: 56.08, volume: 109778, }, { date: 1461252600000, open: 56.08, high: 56.1099, low: 56.07, close: 56.1, volume: 55115, }, { date: 1461252660000, open: 56.105, high: 56.1101, low: 56.082, close: 56.095, volume: 17631, }, { date: 1461252720000, open: 56.092, high: 56.1099, low: 56.06, close: 56.06, volume: 175284, }, { date: 1461252780000, open: 56.075, high: 56.075, low: 55.98, close: 55.98, volume: 85653, }, { date: 1461252840000, open: 55.98, high: 55.99, low: 55.97, close: 55.985, volume: 29236, }, { date: 1461252900000, open: 55.9801, high: 56, low: 55.95, close: 55.95, volume: 56997, }, { date: 1461252960000, open: 55.95, high: 56, low: 55.95, close: 55.995, volume: 100248, }, { date: 1461253020000, open: 55.99, high: 56.03, low: 55.985, close: 56.03, volume: 60383, }, { date: 1461253080000, open: 56.03, high: 56.035, low: 56.01, close: 56.01, volume: 26521, }, { date: 1461253140000, open: 56.015, high: 56.015, low: 55.97, close: 56.005, volume: 72286, }, { date: 1461253200000, open: 56.01, high: 56.03, low: 55.991, close: 56, volume: 106889, }, { date: 1461253260000, open: 56.01, high: 56.07, low: 56, close: 56.07, volume: 133319, }, { date: 1461253320000, open: 56.07, high: 56.07, low: 56.005, close: 56.005, volume: 38249, }, { date: 1461253380000, open: 56.005, high: 56.01, low: 56, close: 56.005, volume: 27211, }, { date: 1461253440000, open: 56.005, high: 56.0101, low: 55.975, close: 56.005, volume: 75044, }, { date: 1461253500000, open: 56.005, high: 56.055, low: 56.005, close: 56.055, volume: 28396, }, { date: 1461253560000, open: 56.055, high: 56.06, low: 56.0099, close: 56.01, volume: 54087, }, { date: 1461253620000, open: 56.01, high: 56.02, low: 55.97, close: 55.97, volume: 40286, }, { date: 1461253680000, open: 55.98, high: 56, low: 55.965, close: 56, volume: 46108, }, { date: 1461253740000, open: 55.9901, high: 56, low: 55.95, close: 55.95, volume: 64115, }, { date: 1461253800000, open: 55.95, high: 55.985, low: 55.92, close: 55.9616, volume: 89138, }, { date: 1461253860000, open: 55.965, high: 56.01, low: 55.9646, close: 56, volume: 52920, }, { date: 1461253920000, open: 56, high: 56.02, low: 55.98, close: 55.99, volume: 70451, }, { date: 1461253980000, open: 55.985, high: 56.02, low: 55.98, close: 56.01, volume: 65213, }, { date: 1461254040000, open: 56.005, high: 56.0228, low: 55.98, close: 56.0228, volume: 42019, }, { date: 1461254100000, open: 56.025, high: 56.04, low: 56.01, close: 56.02, volume: 77746, }, { date: 1461254160000, open: 56.01, high: 56.0169, low: 55.955, close: 55.98, volume: 48649, }, { date: 1461254220000, open: 55.985, high: 55.99, low: 55.91, close: 55.93, volume: 51500, }, { date: 1461254280000, open: 55.94, high: 55.95, low: 55.92, close: 55.93, volume: 125699, }, { date: 1461254340000, open: 55.93, high: 55.94, low: 55.905, close: 55.905, volume: 48454, }, { date: 1461254400000, open: 55.905, high: 55.905, low: 55.875, close: 55.885, volume: 29341, }, { date: 1461254460000, open: 55.885, high: 55.89, low: 55.87, close: 55.875, volume: 24093, }, { date: 1461254520000, open: 55.875, high: 55.88, low: 55.8, close: 55.82, volume: 48409, }, { date: 1461254580000, open: 55.815, high: 55.83, low: 55.77, close: 55.775, volume: 89921, }, { date: 1461254640000, open: 55.775, high: 55.81, low: 55.75, close: 55.8, volume: 107329, }, { date: 1461254700000, open: 55.805, high: 55.81, low: 55.75, close: 55.78, volume: 83415, }, { date: 1461254760000, open: 55.78, high: 55.82, low: 55.775, close: 55.8125, volume: 110048, }, { date: 1461254820000, open: 55.815, high: 55.82, low: 55.79, close: 55.805, volume: 25390, }, { date: 1461254880000, open: 55.8099, high: 55.83, low: 55.805, close: 55.82, volume: 27765, }, { date: 1461254940000, open: 55.825, high: 55.85, low: 55.815, close: 55.845, volume: 32109, }, { date: 1461255000000, open: 55.8483, high: 55.85, low: 55.83, close: 55.845, volume: 25247, }, { date: 1461255060000, open: 55.84, high: 55.845, low: 55.79, close: 55.795, volume: 39151, }, { date: 1461255120000, open: 55.79, high: 55.795, low: 55.7601, close: 55.7601, volume: 46654, }, { date: 1461255180000, open: 55.76, high: 55.765, low: 55.74, close: 55.75, volume: 37652, }, { date: 1461255240000, open: 55.74, high: 55.74, low: 55.63, close: 55.6399, volume: 109132, }, { date: 1461255300000, open: 55.64, high: 55.6571, low: 55.63, close: 55.645, volume: 86752, }, { date: 1461255360000, open: 55.645, high: 55.68, low: 55.64, close: 55.675, volume: 32382, }, { date: 1461255420000, open: 55.68, high: 55.7065, low: 55.6701, close: 55.69, volume: 35223, }, { date: 1461255480000, open: 55.685, high: 55.705, low: 55.6805, close: 55.7, volume: 20633, }, { date: 1461255540000, open: 55.705, high: 55.75, low: 55.7, close: 55.745, volume: 37576, }, { date: 1461255600000, open: 55.7455, high: 55.77, low: 55.74, close: 55.75, volume: 32691, }, { date: 1461255660000, open: 55.75, high: 55.755, low: 55.74, close: 55.7445, volume: 39767, }, { date: 1461255720000, open: 55.74, high: 55.75, low: 55.73, close: 55.74, volume: 149002, }, { date: 1461255780000, open: 55.7399, high: 55.745, low: 55.71, close: 55.715, volume: 35026, }, { date: 1461255840000, open: 55.715, high: 55.73, low: 55.7109, close: 55.725, volume: 26383, }, { date: 1461255900000, open: 55.72, high: 55.75, low: 55.705, close: 55.705, volume: 35128, }, { date: 1461255960000, open: 55.71, high: 55.72, low: 55.695, close: 55.695, volume: 52586, }, { date: 1461256020000, open: 55.69, high: 55.69, low: 55.65, close: 55.6625, volume: 43676, }, { date: 1461256080000, open: 55.665, high: 55.69, low: 55.665, close: 55.675, volume: 31197, }, { date: 1461256140000, open: 55.6701, high: 55.71, low: 55.67, close: 55.705, volume: 38184, }, { date: 1461256200000, open: 55.71, high: 55.715, low: 55.7, close: 55.705, volume: 25896, }, { date: 1461256260000, open: 55.705, high: 55.77, low: 55.705, close: 55.755, volume: 31257, }, { date: 1461256320000, open: 55.7599, high: 55.79, low: 55.75, close: 55.79, volume: 23034, }, { date: 1461256380000, open: 55.79, high: 55.84, low: 55.7801, close: 55.83, volume: 29098, }, { date: 1461256440000, open: 55.835, high: 55.85, low: 55.815, close: 55.84, volume: 39996, }, { date: 1461256500000, open: 55.845, high: 55.8822, low: 55.84, close: 55.88, volume: 40175, }, { date: 1461256560000, open: 55.88, high: 55.905, low: 55.87, close: 55.87, volume: 74395, }, { date: 1461256620000, open: 55.87, high: 55.87, low: 55.84, close: 55.8425, volume: 38269, }, { date: 1461256680000, open: 55.849, high: 55.85, low: 55.82, close: 55.835, volume: 28312, }, { date: 1461256740000, open: 55.84, high: 55.84, low: 55.805, close: 55.825, volume: 99465, }, { date: 1461256800000, open: 55.82, high: 55.83, low: 55.81, close: 55.815, volume: 43201, }, { date: 1461256860000, open: 55.8101, high: 55.83, low: 55.81, close: 55.815, volume: 31374, }, { date: 1461256920000, open: 55.82, high: 55.87, low: 55.8105, close: 55.865, volume: 54435, }, { date: 1461256980000, open: 55.8699, high: 55.87, low: 55.85, close: 55.86, volume: 62130, }, { date: 1461257040000, open: 55.855, high: 55.86, low: 55.81, close: 55.835, volume: 53192, }, { date: 1461257100000, open: 55.835, high: 55.865, low: 55.83, close: 55.855, volume: 26228, }, { date: 1461257160000, open: 55.86, high: 55.86, low: 55.82, close: 55.825, volume: 93669, }, { date: 1461257220000, open: 55.82, high: 55.84, low: 55.814, close: 55.83, volume: 45959, }, { date: 1461257280000, open: 55.835, high: 55.85, low: 55.82, close: 55.84, volume: 124813, }, { date: 1461257340000, open: 55.835, high: 55.84, low: 55.81, close: 55.815, volume: 114743, }, { date: 1461257400000, open: 55.8199, high: 55.83, low: 55.8, close: 55.82, volume: 232189, }, { date: 1461257460000, open: 55.825, high: 55.83, low: 55.74, close: 55.765, volume: 245105, }, { date: 1461257520000, open: 55.765, high: 55.8, low: 55.75, close: 55.785, volume: 78409, }, { date: 1461257580000, open: 55.785, high: 55.796, low: 55.78, close: 55.785, volume: 16902, }, { date: 1461257640000, open: 55.79, high: 55.815, low: 55.78, close: 55.815, volume: 26160, }, { date: 1461257700000, open: 55.82, high: 55.84, low: 55.82, close: 55.825, volume: 37453, }, { date: 1461257760000, open: 55.82, high: 55.85, low: 55.82, close: 55.85, volume: 44430, }, { date: 1461257820000, open: 55.85, high: 55.86, low: 55.82, close: 55.8239, volume: 36728, }, { date: 1461257880000, open: 55.8225, high: 55.86, low: 55.8225, close: 55.855, volume: 22171, }, { date: 1461257940000, open: 55.86, high: 55.9, low: 55.86, close: 55.89, volume: 66939, }, { date: 1461258000000, open: 55.89, high: 55.8975, low: 55.835, close: 55.855, volume: 56257, }, { date: 1461258060000, open: 55.855, high: 55.86, low: 55.83, close: 55.845, volume: 76002, }, { date: 1461258120000, open: 55.845, high: 55.86, low: 55.84, close: 55.856, volume: 75575, }, { date: 1461258180000, open: 55.86, high: 55.916, low: 55.855, close: 55.875, volume: 120386, }, { date: 1461258240000, open: 55.88, high: 55.88, low: 55.83, close: 55.835, volume: 79369, }, { date: 1461258300000, open: 55.8371, high: 55.8371, low: 55.77, close: 55.77, volume: 92115, }, { date: 1461258360000, open: 55.77, high: 55.775, low: 55.72, close: 55.725, volume: 167136, }, { date: 1461258420000, open: 55.725, high: 55.75, low: 55.7, close: 55.735, volume: 55107, }, { date: 1461258480000, open: 55.7399, high: 55.7653, low: 55.735, close: 55.755, volume: 30799, }, { date: 1461258540000, open: 55.755, high: 55.76, low: 55.73, close: 55.735, volume: 29132, }, { date: 1461258600000, open: 55.735, high: 55.7499, low: 55.73, close: 55.7399, volume: 21811, }, { date: 1461258660000, open: 55.7301, high: 55.76, low: 55.73, close: 55.755, volume: 49770, }, { date: 1461258720000, open: 55.76, high: 55.785, low: 55.72, close: 55.74, volume: 78120, }, { date: 1461258780000, open: 55.733, high: 55.775, low: 55.725, close: 55.775, volume: 121151, }, { date: 1461258840000, open: 55.77, high: 55.78, low: 55.75, close: 55.765, volume: 32873, }, { date: 1461258900000, open: 55.7799, high: 55.78, low: 55.75, close: 55.75, volume: 39231, }, { date: 1461258960000, open: 55.7598, high: 55.7598, low: 55.74, close: 55.74, volume: 44719, }, { date: 1461259020000, open: 55.74, high: 55.74, low: 55.725, close: 55.73, volume: 42230, }, { date: 1461259080000, open: 55.735, high: 55.735, low: 55.685, close: 55.7, volume: 120464, }, { date: 1461259140000, open: 55.7, high: 55.72, low: 55.69, close: 55.705, volume: 79792, }, { date: 1461259200000, open: 55.71, high: 55.775, low: 55.71, close: 55.775, volume: 41725, }, { date: 1461259260000, open: 55.78, high: 55.84, low: 55.775, close: 55.835, volume: 74290, }, { date: 1461259320000, open: 55.8399, high: 55.88, low: 55.83, close: 55.88, volume: 31914, }, { date: 1461259380000, open: 55.88, high: 55.89, low: 55.8505, close: 55.8636, volume: 43445, }, { date: 1461259440000, open: 55.866, high: 55.87, low: 55.81, close: 55.81, volume: 44274, }, { date: 1461259500000, open: 55.8101, high: 55.82, low: 55.795, close: 55.8198, volume: 37824, }, { date: 1461259560000, open: 55.82, high: 55.84, low: 55.8, close: 55.805, volume: 61072, }, { date: 1461259620000, open: 55.81, high: 55.82, low: 55.8, close: 55.815, volume: 65064, }, { date: 1461259680000, open: 55.82, high: 55.82, low: 55.745, close: 55.767, volume: 60566, }, { date: 1461259740000, open: 55.76, high: 55.76, low: 55.73, close: 55.745, volume: 36453, }, { date: 1461259800000, open: 55.75, high: 55.76, low: 55.72, close: 55.725, volume: 65190, }, { date: 1461259860000, open: 55.726, high: 55.795, low: 55.72, close: 55.75, volume: 53660, }, { date: 1461259920000, open: 55.75, high: 55.8, low: 55.75, close: 55.795, volume: 35941, }, { date: 1461259980000, open: 55.8, high: 55.81, low: 55.75, close: 55.75, volume: 56224, }, { date: 1461260040000, open: 55.76, high: 55.76, low: 55.725, close: 55.725, volume: 59347, }, { date: 1461260100000, open: 55.72, high: 55.75, low: 55.72, close: 55.735, volume: 42448, }, { date: 1461260160000, open: 55.73, high: 55.7661, low: 55.73, close: 55.76, volume: 52794, }, { date: 1461260220000, open: 55.76, high: 55.855, low: 55.76, close: 55.824, volume: 123135, }, { date: 1461260280000, open: 55.82, high: 55.885, low: 55.82, close: 55.87, volume: 74212, }, { date: 1461260340000, open: 55.88, high: 55.88, low: 55.82, close: 55.825, volume: 67275, }, { date: 1461260400000, open: 55.83, high: 55.91, low: 55.825, close: 55.91, volume: 38859, }, { date: 1461260460000, open: 55.905, high: 55.97, low: 55.895, close: 55.96, volume: 73616, }, { date: 1461260520000, open: 55.955, high: 55.99, low: 55.93, close: 55.99, volume: 61296, }, { date: 1461260580000, open: 55.98, high: 55.98, low: 55.91, close: 55.955, volume: 61953, }, { date: 1461260640000, open: 55.96, high: 55.96, low: 55.89, close: 55.9, volume: 55560, }, { date: 1461260700000, open: 55.89, high: 55.94, low: 55.89, close: 55.925, volume: 49439, }, { date: 1461260760000, open: 55.9236, high: 55.94, low: 55.9, close: 55.905, volume: 38957, }, { date: 1461260820000, open: 55.91, high: 55.94, low: 55.9, close: 55.925, volume: 32540, }, { date: 1461260880000, open: 55.925, high: 55.925, low: 55.89, close: 55.89, volume: 38201, }, { date: 1461260940000, open: 55.89, high: 55.89, low: 55.86, close: 55.8899, volume: 60017, }, { date: 1461261000000, open: 55.88, high: 55.88, low: 55.84, close: 55.845, volume: 32786, }, { date: 1461261060000, open: 55.845, high: 55.87, low: 55.845, close: 55.8661, volume: 29159, }, { date: 1461261120000, open: 55.87, high: 55.87, low: 55.85, close: 55.855, volume: 44093, }, { date: 1461261180000, open: 55.85, high: 55.85, low: 55.775, close: 55.8, volume: 71108, }, { date: 1461261240000, open: 55.805, high: 55.84, low: 55.8, close: 55.835, volume: 51226, }, { date: 1461261300000, open: 55.835, high: 55.85, low: 55.8035, close: 55.81, volume: 46188, }, { date: 1461261360000, open: 55.815, high: 55.826, low: 55.795, close: 55.815, volume: 32113, }, { date: 1461261420000, open: 55.8153, high: 55.85, low: 55.81, close: 55.83, volume: 79265, }, { date: 1461261480000, open: 55.82, high: 55.85, low: 55.815, close: 55.825, volume: 26492, }, { date: 1461261540000, open: 55.825, high: 55.85, low: 55.82, close: 55.845, volume: 51482, }, { date: 1461261600000, open: 55.845, high: 55.85, low: 55.81, close: 55.83, volume: 34381, }, { date: 1461261660000, open: 55.8238, high: 55.825, low: 55.81, close: 55.81, volume: 12269, }, { date: 1461261720000, open: 55.8199, high: 55.855, low: 55.81, close: 55.815, volume: 81803, }, { date: 1461261780000, open: 55.845, high: 55.845, low: 55.78, close: 55.8, volume: 129902, }, { date: 1461261840000, open: 55.795, high: 55.795, low: 55.77, close: 55.785, volume: 32236, }, { date: 1461261900000, open: 55.79, high: 55.79, low: 55.73, close: 55.745, volume: 101588, }, { date: 1461261960000, open: 55.745, high: 55.76, low: 55.72, close: 55.76, volume: 76989, }, { date: 1461262020000, open: 55.76, high: 55.81, low: 55.755, close: 55.805, volume: 44967, }, { date: 1461262080000, open: 55.81, high: 55.84, low: 55.8, close: 55.8256, volume: 73870, }, { date: 1461262140000, open: 55.825, high: 55.8501, low: 55.81, close: 55.815, volume: 22216, }, { date: 1461262200000, open: 55.8139, high: 55.82, low: 55.79, close: 55.795, volume: 16995, }, { date: 1461262260000, open: 55.8, high: 55.84, low: 55.8, close: 55.84, volume: 50586, }, { date: 1461262320000, open: 55.835, high: 55.84, low: 55.83, close: 55.835, volume: 38015, }, { date: 1461262380000, open: 55.835, high: 55.845, low: 55.83, close: 55.835, volume: 40249, }, { date: 1461262440000, open: 55.835, high: 55.87, low: 55.83, close: 55.86, volume: 91517, }, { date: 1461262500000, open: 55.865, high: 55.87, low: 55.85, close: 55.8599, volume: 32498, }, { date: 1461262560000, open: 55.855, high: 55.88, low: 55.85, close: 55.865, volume: 36734, }, { date: 1461262620000, open: 55.87, high: 55.87, low: 55.795, close: 55.796, volume: 75570, }, { date: 1461262680000, open: 55.8, high: 55.815, low: 55.77, close: 55.8, volume: 67602, }, { date: 1461262740000, open: 55.795, high: 55.7999, low: 55.77, close: 55.78, volume: 45298, }, { date: 1461262800000, open: 55.774, high: 55.785, low: 55.745, close: 55.775, volume: 84494, }, { date: 1461262860000, open: 55.773, high: 55.78, low: 55.77, close: 55.775, volume: 49260, }, { date: 1461262920000, open: 55.78, high: 55.83, low: 55.78, close: 55.825, volume: 46659, }, { date: 1461262980000, open: 55.83, high: 55.84, low: 55.81, close: 55.81, volume: 48360, }, { date: 1461263040000, open: 55.819, high: 55.83, low: 55.81, close: 55.825, volume: 22116, }, { date: 1461263100000, open: 55.8231, high: 55.83, low: 55.79, close: 55.79, volume: 50731, }, { date: 1461263160000, open: 55.79, high: 55.81, low: 55.785, close: 55.79, volume: 75531, }, { date: 1461263220000, open: 55.8, high: 55.81, low: 55.75, close: 55.76, volume: 51468, }, { date: 1461263280000, open: 55.76, high: 55.795, low: 55.755, close: 55.785, volume: 51770, }, { date: 1461263340000, open: 55.78, high: 55.785, low: 55.73, close: 55.755, volume: 98904, }, { date: 1461263400000, open: 55.76, high: 55.77, low: 55.71, close: 55.735, volume: 70259, }, { date: 1461263460000, open: 55.74, high: 55.74, low: 55.71, close: 55.72, volume: 38437, }, { date: 1461263520000, open: 55.715, high: 55.72, low: 55.67, close: 55.695, volume: 43750, }, { date: 1461263580000, open: 55.695, high: 55.7, low: 55.68, close: 55.695, volume: 30344, }, { date: 1461263640000, open: 55.695, high: 55.7, low: 55.68, close: 55.6899, volume: 126853, }, { date: 1461263700000, open: 55.69, high: 55.69, low: 55.65, close: 55.65, volume: 42233, }, { date: 1461263760000, open: 55.655, high: 55.66, low: 55.62, close: 55.645, volume: 100126, }, { date: 1461263820000, open: 55.645, high: 55.67, low: 55.6301, close: 55.6588, volume: 63469, }, { date: 1461263880000, open: 55.655, high: 55.68, low: 55.65, close: 55.675, volume: 73834, }, { date: 1461263940000, open: 55.68, high: 55.7, low: 55.67, close: 55.695, volume: 179331, }, { date: 1461264000000, open: 55.695, high: 55.695, low: 55.66, close: 55.675, volume: 52116, }, { date: 1461264060000, open: 55.67, high: 55.67, low: 55.6483, close: 55.655, volume: 58433, }, { date: 1461264120000, open: 55.655, high: 55.66, low: 55.64, close: 55.655, volume: 39310, }, { date: 1461264180000, open: 55.66, high: 55.68, low: 55.65, close: 55.655, volume: 58586, }, { date: 1461264240000, open: 55.651, high: 55.6869, low: 55.65, close: 55.675, volume: 60925, }, { date: 1461264300000, open: 55.675, high: 55.68, low: 55.65, close: 55.67, volume: 81549, }, { date: 1461264360000, open: 55.68, high: 55.71, low: 55.65, close: 55.7099, volume: 78868, }, { date: 1461264420000, open: 55.71, high: 55.74, low: 55.6669, close: 55.715, volume: 99555, }, { date: 1461264480000, open: 55.719, high: 55.75, low: 55.71, close: 55.715, volume: 91080, }, { date: 1461264540000, open: 55.71, high: 55.77, low: 55.71, close: 55.77, volume: 55847, }, { date: 1461264600000, open: 55.77, high: 55.8199, low: 55.75, close: 55.8199, volume: 67410, }, { date: 1461264660000, open: 55.82, high: 55.85, low: 55.8, close: 55.81, volume: 74058, }, { date: 1461264720000, open: 55.81, high: 55.81, low: 55.78, close: 55.795, volume: 74069, }, { date: 1461264780000, open: 55.7937, high: 55.82, low: 55.7675, close: 55.8, volume: 102611, }, { date: 1461264840000, open: 55.79, high: 55.85, low: 55.79, close: 55.84, volume: 36677, }, { date: 1461264900000, open: 55.84, high: 55.87, low: 55.825, close: 55.85, volume: 73441, }, { date: 1461264960000, open: 55.85, high: 55.86, low: 55.84, close: 55.86, volume: 25481, }, { date: 1461265020000, open: 55.855, high: 55.8699, low: 55.84, close: 55.86, volume: 40103, }, { date: 1461265080000, open: 55.853, high: 55.88, low: 55.845, close: 55.88, volume: 44850, }, { date: 1461265140000, open: 55.87, high: 55.875, low: 55.85, close: 55.86, volume: 25913, }, { date: 1461265200000, open: 55.855, high: 55.87, low: 55.85, close: 55.85, volume: 40506, }, { date: 1461265260000, open: 55.85, high: 55.92, low: 55.85, close: 55.87, volume: 122344, }, { date: 1461265320000, open: 55.87, high: 55.91, low: 55.87, close: 55.895, volume: 71358, }, { date: 1461265380000, open: 55.895, high: 55.905, low: 55.89, close: 55.895, volume: 35998, }, { date: 1461265440000, open: 55.89, high: 55.895, low: 55.88, close: 55.8899, volume: 38505, }, { date: 1461265500000, open: 55.89, high: 55.895, low: 55.8501, close: 55.86, volume: 60704, }, { date: 1461265560000, open: 55.86, high: 55.87, low: 55.85, close: 55.86, volume: 32762, }, { date: 1461265620000, open: 55.8662, high: 55.88, low: 55.86, close: 55.88, volume: 30230, }, { date: 1461265680000, open: 55.88, high: 55.88, low: 55.84, close: 55.8499, volume: 43111, }, { date: 1461265740000, open: 55.84, high: 55.855, low: 55.84, close: 55.85, volume: 21125, }, { date: 1461265800000, open: 55.8499, high: 55.85, low: 55.795, close: 55.8, volume: 94214, }, { date: 1461265860000, open: 55.8, high: 55.81, low: 55.7732, close: 55.805, volume: 69013, }, { date: 1461265920000, open: 55.81, high: 55.84, low: 55.81, close: 55.83, volume: 37506, }, { date: 1461265980000, open: 55.84, high: 55.865, low: 55.815, close: 55.86, volume: 83026, }, { date: 1461266040000, open: 55.855, high: 55.855, low: 55.825, close: 55.825, volume: 40372, }, { date: 1461266100000, open: 55.825, high: 55.83, low: 55.8, close: 55.825, volume: 43155, }, { date: 1461266160000, open: 55.825, high: 55.845, low: 55.825, close: 55.8334, volume: 74007, }, { date: 1461266220000, open: 55.835, high: 55.84, low: 55.81, close: 55.815, volume: 83119, }, { date: 1461266280000, open: 55.815, high: 55.85, low: 55.8101, close: 55.84, volume: 60554, }, { date: 1461266340000, open: 55.84, high: 55.89, low: 55.835, close: 55.85, volume: 109592, }, { date: 1461266400000, open: 55.8575, high: 55.865, low: 55.82, close: 55.845, volume: 88875, }, { date: 1461266460000, open: 55.84, high: 55.8598, low: 55.79, close: 55.8, volume: 95750, }, { date: 1461266520000, open: 55.8, high: 55.81, low: 55.795, close: 55.81, volume: 42252, }, { date: 1461266580000, open: 55.81, high: 55.82, low: 55.8, close: 55.81, volume: 33658, }, { date: 1461266640000, open: 55.805, high: 55.84, low: 55.8, close: 55.805, volume: 53860, }, { date: 1461266700000, open: 55.81, high: 55.82, low: 55.8, close: 55.8101, volume: 43336, }, { date: 1461266760000, open: 55.82, high: 55.82, low: 55.81, close: 55.815, volume: 13623, }, { date: 1461266820000, open: 55.81, high: 55.9, low: 55.81, close: 55.885, volume: 175861, }, { date: 1461266880000, open: 55.88, high: 55.895, low: 55.825, close: 55.83, volume: 84630, }, { date: 1461266940000, open: 55.83, high: 55.84, low: 55.81, close: 55.825, volume: 82844, }, { date: 1461267000000, open: 55.83, high: 55.83, low: 55.76, close: 55.76, volume: 88915, }, { date: 1461267060000, open: 55.76, high: 55.825, low: 55.76, close: 55.825, volume: 61923, }, { date: 1461267120000, open: 55.83, high: 55.83, low: 55.79, close: 55.815, volume: 62037, }, { date: 1461267180000, open: 55.82, high: 55.82, low: 55.81, close: 55.815, volume: 94795, }, { date: 1461267240000, open: 55.815, high: 55.85, low: 55.81, close: 55.8301, volume: 146278, }, { date: 1461267300000, open: 55.835, high: 55.84, low: 55.78, close: 55.8199, volume: 102822, }, { date: 1461267360000, open: 55.815, high: 55.85, low: 55.815, close: 55.835, volume: 43585, }, { date: 1461267420000, open: 55.84, high: 55.85, low: 55.78, close: 55.7807, volume: 98790, }, { date: 1461267480000, open: 55.789, high: 55.79, low: 55.71, close: 55.745, volume: 174401, }, { date: 1461267540000, open: 55.745, high: 55.75, low: 55.72, close: 55.72, volume: 168036, }, { date: 1461267600000, open: 55.72, high: 55.74, low: 55.72, close: 55.735, volume: 41797, }, { date: 1461267660000, open: 55.74, high: 55.76, low: 55.7301, close: 55.755, volume: 95381, }, { date: 1461267720000, open: 55.75, high: 55.76, low: 55.73, close: 55.74, volume: 110939, }, { date: 1461267780000, open: 55.74, high: 55.81, low: 55.74, close: 55.8, volume: 108102, }, { date: 1461267840000, open: 55.7961, high: 55.81, low: 55.78, close: 55.79, volume: 106253, }, { date: 1461267900000, open: 55.79, high: 55.82, low: 55.78, close: 55.785, volume: 117510, }, { date: 1461267960000, open: 55.79, high: 55.83, low: 55.78, close: 55.81, volume: 83443, }, { date: 1461268020000, open: 55.807, high: 55.85, low: 55.805, close: 55.845, volume: 92398, }, { date: 1461268080000, open: 55.845, high: 55.905, low: 55.83, close: 55.88, volume: 177055, }, { date: 1461268140000, open: 55.88, high: 55.91, low: 55.87, close: 55.8901, volume: 129152, }, { date: 1461268200000, open: 55.89, high: 55.91, low: 55.78, close: 55.815, volume: 254899, }, { date: 1461268260000, open: 55.815, high: 55.8185, low: 55.78, close: 55.785, volume: 153761, }, { date: 1461268320000, open: 55.78, high: 55.83, low: 55.78, close: 55.83, volume: 200688, }, { date: 1461268380000, open: 55.83, high: 55.85, low: 55.82, close: 55.8499, volume: 171543, }, { date: 1461268440000, open: 55.84, high: 55.85, low: 55.79, close: 55.8, volume: 298806, }, { date: 1461268500000, open: 55.81, high: 55.82, low: 55.77, close: 55.81, volume: 182780, }, { date: 1461268560000, open: 55.8299, high: 55.8699, low: 55.825, close: 55.83, volume: 161528, }, { date: 1461268620000, open: 55.825, high: 55.83, low: 55.77, close: 55.8, volume: 157422, }, { date: 1461268680000, open: 55.795, high: 55.9, low: 55.79, close: 55.895, volume: 244563, }, { date: 1461268740000, open: 55.88, high: 55.91, low: 55.84, close: 55.91, volume: 195862, }, { date: 1461268800000, open: 55.91, high: 55.97, low: 55.78, close: 55.78, volume: 1898247, }, { date: 1461331800000, open: 51.91, high: 52.39, low: 51.91, close: 52.0372, volume: 1281425, }, { date: 1461331860000, open: 52.0391, high: 52.4297, low: 51.6, close: 51.93, volume: 2576487, }, { date: 1461331920000, open: 51.9499, high: 52.04, low: 51.88, close: 52.01, volume: 914745, }, { date: 1461331980000, open: 52, high: 52.24, low: 51.97, close: 52.24, volume: 910543, }, { date: 1461332040000, open: 52.23, high: 52.36, low: 52.18, close: 52.185, volume: 1099524, }, { date: 1461332100000, open: 52.1881, high: 52.2, low: 51.95, close: 52.04, volume: 909875, }, { date: 1461332160000, open: 52.05, high: 52.17, low: 52.02, close: 52.12, volume: 851342, }, { date: 1461332220000, open: 52.11, high: 52.165, low: 52.08, close: 52.13, volume: 696716, }, { date: 1461332280000, open: 52.12, high: 52.16, low: 52.05, close: 52.1025, volume: 1093318, }, { date: 1461332340000, open: 52.105, high: 52.17, low: 51.98, close: 51.98, volume: 830334, }, { date: 1461332400000, open: 51.98, high: 52, low: 51.83, close: 51.985, volume: 617500, }, { date: 1461332460000, open: 51.9825, high: 52.01, low: 51.92, close: 51.92, volume: 768535, }, { date: 1461332520000, open: 51.92, high: 51.92, low: 51.76, close: 51.82, volume: 1059816, }, { date: 1461332580000, open: 51.82, high: 51.85, low: 51.74, close: 51.76, volume: 658340, }, { date: 1461332640000, open: 51.761, high: 51.84, low: 51.71, close: 51.7668, volume: 643321, }, { date: 1461332700000, open: 51.7699, high: 51.92, low: 51.76, close: 51.92, volume: 448515, }, { date: 1461332760000, open: 51.915, high: 51.92, low: 51.79, close: 51.86, volume: 531793, }, { date: 1461332820000, open: 51.85, high: 51.88, low: 51.76, close: 51.82, volume: 572053, }, { date: 1461332880000, open: 51.82, high: 51.86, low: 51.75, close: 51.8264, volume: 548497, }, { date: 1461332940000, open: 51.82, high: 51.83, low: 51.7, close: 51.8, volume: 716711, }, { date: 1461333000000, open: 51.802, high: 51.81, low: 51.74, close: 51.765, volume: 646384, }, { date: 1461333060000, open: 51.77, high: 51.83, low: 51.73, close: 51.805, volume: 467219, }, { date: 1461333120000, open: 51.8001, high: 51.88, low: 51.79, close: 51.84, volume: 416480, }, { date: 1461333180000, open: 51.844, high: 51.86, low: 51.72, close: 51.776, volume: 505924, }, { date: 1461333240000, open: 51.779, high: 51.81, low: 51.75, close: 51.754, volume: 405140, }, { date: 1461333300000, open: 51.76, high: 51.79, low: 51.7, close: 51.71, volume: 408571, }, { date: 1461333360000, open: 51.71, high: 51.8, low: 51.66, close: 51.79, volume: 477046, }, { date: 1461333420000, open: 51.79, high: 51.8, low: 51.73, close: 51.77, volume: 225295, }, { date: 1461333480000, open: 51.78, high: 51.78, low: 51.66, close: 51.665, volume: 453534, }, { date: 1461333540000, open: 51.665, high: 51.75, low: 51.665, close: 51.74, volume: 469351, }, { date: 1461333600000, open: 51.74, high: 51.8, low: 51.72, close: 51.725, volume: 440023, }, { date: 1461333660000, open: 51.725, high: 51.74, low: 51.65, close: 51.715, volume: 536180, }, { date: 1461333720000, open: 51.72, high: 51.75, low: 51.61, close: 51.664, volume: 444081, }, { date: 1461333780000, open: 51.67, high: 51.69, low: 51.47, close: 51.48, volume: 774994, }, { date: 1461333840000, open: 51.48, high: 51.56, low: 51.44, close: 51.4871, volume: 611171, }, { date: 1461333900000, open: 51.4875, high: 51.49, low: 51.37, close: 51.39, volume: 629226, }, { date: 1461333960000, open: 51.385, high: 51.5, low: 51.38, close: 51.5, volume: 397870, }, { date: 1461334020000, open: 51.5, high: 51.53, low: 51.42, close: 51.43, volume: 481457, }, { date: 1461334080000, open: 51.44, high: 51.44, low: 51.22, close: 51.3299, volume: 733578, }, { date: 1461334140000, open: 51.325, high: 51.33, low: 51.24, close: 51.29, volume: 543476, }, { date: 1461334200000, open: 51.295, high: 51.31, low: 51.1475, close: 51.195, volume: 535136, }, { date: 1461334260000, open: 51.19, high: 51.24, low: 51.05, close: 51.2199, volume: 894538, }, { date: 1461334320000, open: 51.21, high: 51.25, low: 51.08, close: 51.09, volume: 432634, }, { date: 1461334380000, open: 51.09, high: 51.1, low: 51, close: 51.03, volume: 834964, }, { date: 1461334440000, open: 51.025, high: 51.08, low: 50.91, close: 51.02, volume: 1122957, }, { date: 1461334500000, open: 51.025, high: 51.05, low: 50.95, close: 50.96, volume: 554285, }, { date: 1461334560000, open: 50.96, high: 50.97, low: 50.91, close: 50.935, volume: 506717, }, { date: 1461334620000, open: 50.93, high: 51.04, low: 50.84, close: 51.01, volume: 641365, }, { date: 1461334680000, open: 51, high: 51.06, low: 50.91, close: 50.915, volume: 464943, }, { date: 1461334740000, open: 50.91, high: 50.92, low: 50.81, close: 50.855, volume: 719575, }, { date: 1461334800000, open: 50.85, high: 50.87, low: 50.77, close: 50.84, volume: 594998, }, { date: 1461334860000, open: 50.845, high: 50.9299, low: 50.8, close: 50.82, volume: 514504, }, { date: 1461334920000, open: 50.815, high: 50.9, low: 50.81, close: 50.8695, volume: 387227, }, { date: 1461334980000, open: 50.87, high: 51.05, low: 50.86, close: 51.02, volume: 533873, }, { date: 1461335040000, open: 51.03, high: 51.03, low: 50.91, close: 50.93, volume: 302635, }, { date: 1461335100000, open: 50.9361, high: 51.04, low: 50.93, close: 51.03, volume: 331151, }, { date: 1461335160000, open: 51.03, high: 51.14, low: 51.025, close: 51.08, volume: 534770, }, { date: 1461335220000, open: 51.09, high: 51.09, low: 50.96, close: 50.9664, volume: 413187, }, { date: 1461335280000, open: 50.96, high: 51.1, low: 50.96, close: 51.1, volume: 407016, }, { date: 1461335340000, open: 51.085, high: 51.24, low: 51.075, close: 51.23, volume: 424365, }, { date: 1461335400000, open: 51.225, high: 51.25, low: 51.135, close: 51.1905, volume: 516277, }, { date: 1461335460000, open: 51.195, high: 51.2899, low: 51.19, close: 51.28, volume: 451323, }, { date: 1461335520000, open: 51.2865, high: 51.35, low: 51.25, close: 51.349, volume: 285453, }, { date: 1461335580000, open: 51.34, high: 51.39, low: 51.29, close: 51.35, volume: 420866, }, { date: 1461335640000, open: 51.341, high: 51.38, low: 51.3, close: 51.38, volume: 254408, }, { date: 1461335700000, open: 51.38, high: 51.44, low: 51.34, close: 51.3865, volume: 309157, }, { date: 1461335760000, open: 51.39, high: 51.395, low: 51.3, close: 51.32, volume: 414776, }, { date: 1461335820000, open: 51.32, high: 51.33, low: 51.26, close: 51.31, volume: 258505, }, { date: 1461335880000, open: 51.31, high: 51.32, low: 51.17, close: 51.2, volume: 339059, }, { date: 1461335940000, open: 51.2099, high: 51.27, low: 51.1789, close: 51.235, volume: 248748, }, { date: 1461336000000, open: 51.235, high: 51.26, low: 51.23, close: 51.2499, volume: 239823, }, { date: 1461336060000, open: 51.245, high: 51.28, low: 51.23, close: 51.235, volume: 310514, }, { date: 1461336120000, open: 51.23, high: 51.27, low: 51.13, close: 51.14, volume: 594159, }, { date: 1461336180000, open: 51.13, high: 51.13, low: 51.04, close: 51.06, volume: 544086, }, { date: 1461336240000, open: 51.06, high: 51.11, low: 51.045, close: 51.09, volume: 277588, }, { date: 1461336300000, open: 51.1, high: 51.12, low: 51.06, close: 51.07, volume: 208071, }, { date: 1461336360000, open: 51.065, high: 51.17, low: 51.045, close: 51.105, volume: 473496, }, { date: 1461336420000, open: 51.105, high: 51.14, low: 51.08, close: 51.13, volume: 268392, }, { date: 1461336480000, open: 51.13, high: 51.27, low: 51.12, close: 51.26, volume: 302020, }, { date: 1461336540000, open: 51.265, high: 51.27, low: 51.235, close: 51.255, volume: 291067, }, { date: 1461336600000, open: 51.26, high: 51.349, low: 51.24, close: 51.292, volume: 367436, }, { date: 1461336660000, open: 51.295, high: 51.35, low: 51.285, close: 51.3399, volume: 199717, }, { date: 1461336720000, open: 51.34, high: 51.39, low: 51.32, close: 51.38, volume: 197105, }, { date: 1461336780000, open: 51.39, high: 51.44, low: 51.37, close: 51.425, volume: 533782, }, { date: 1461336840000, open: 51.43, high: 51.49, low: 51.37, close: 51.49, volume: 440948, }, { date: 1461336900000, open: 51.49, high: 51.589, low: 51.48, close: 51.589, volume: 459596, }, { date: 1461336960000, open: 51.58, high: 51.67, low: 51.565, close: 51.605, volume: 883440, }, { date: 1461337020000, open: 51.61, high: 51.61, low: 51.47, close: 51.491, volume: 438890, }, { date: 1461337080000, open: 51.4999, high: 51.59, low: 51.49, close: 51.514, volume: 348092, }, { date: 1461337140000, open: 51.5399, high: 51.61, low: 51.5363, close: 51.565, volume: 219806, }, { date: 1461337200000, open: 51.566, high: 51.63, low: 51.53, close: 51.59, volume: 589063, }, { date: 1461337260000, open: 51.58, high: 51.62, low: 51.52, close: 51.54, volume: 451846, }, { date: 1461337320000, open: 51.54, high: 51.63, low: 51.538, close: 51.6, volume: 448767, }, { date: 1461337380000, open: 51.605, high: 51.61, low: 51.555, close: 51.57, volume: 285671, }, { date: 1461337440000, open: 51.573, high: 51.61, low: 51.56, close: 51.58, volume: 394607, }, { date: 1461337500000, open: 51.589, high: 51.6, low: 51.56, close: 51.58, volume: 280484, }, { date: 1461337560000, open: 51.58, high: 51.65, low: 51.58, close: 51.595, volume: 657649, }, { date: 1461337620000, open: 51.6, high: 51.6, low: 51.42, close: 51.498, volume: 454111, }, { date: 1461337680000, open: 51.5, high: 51.59, low: 51.47, close: 51.57, volume: 309031, }, { date: 1461337740000, open: 51.57, high: 51.595, low: 51.535, close: 51.59, volume: 185810, }, { date: 1461337800000, open: 51.5866, high: 51.65, low: 51.55, close: 51.649, volume: 259047, }, { date: 1461337860000, open: 51.65, high: 51.65, low: 51.59, close: 51.61, volume: 189738, }, { date: 1461337920000, open: 51.61, high: 51.67, low: 51.59, close: 51.6354, volume: 441155, }, { date: 1461337980000, open: 51.635, high: 51.64, low: 51.59, close: 51.595, volume: 305518, }, { date: 1461338040000, open: 51.5957, high: 51.69, low: 51.59, close: 51.685, volume: 391325, }, { date: 1461338100000, open: 51.68, high: 51.73, low: 51.655, close: 51.68, volume: 352988, }, { date: 1461338160000, open: 51.68, high: 51.74, low: 51.665, close: 51.73, volume: 343289, }, { date: 1461338220000, open: 51.73, high: 51.75, low: 51.7, close: 51.7299, volume: 314970, }, { date: 1461338280000, open: 51.73, high: 51.74, low: 51.61, close: 51.645, volume: 226271, }, { date: 1461338340000, open: 51.649, high: 51.71, low: 51.64, close: 51.704, volume: 164331, }, { date: 1461338400000, open: 51.7, high: 51.75, low: 51.69, close: 51.6999, volume: 281598, }, { date: 1461338460000, open: 51.6957, high: 51.72, low: 51.66, close: 51.679, volume: 191657, }, { date: 1461338520000, open: 51.6764, high: 51.73, low: 51.65, close: 51.71, volume: 333500, }, { date: 1461338580000, open: 51.71, high: 51.74, low: 51.705, close: 51.725, volume: 121691, }, { date: 1461338640000, open: 51.73, high: 51.78, low: 51.72, close: 51.765, volume: 335402, }, { date: 1461338700000, open: 51.77, high: 51.88, low: 51.765, close: 51.87, volume: 478933, }, { date: 1461338760000, open: 51.86, high: 51.95, low: 51.86, close: 51.9401, volume: 404937, }, { date: 1461338820000, open: 51.9432, high: 51.98, low: 51.9, close: 51.97, volume: 633851, }, { date: 1461338880000, open: 51.97, high: 52.005, low: 51.96, close: 51.9971, volume: 541125, }, { date: 1461338940000, open: 51.9901, high: 52, low: 51.85, close: 51.8557, volume: 548298, }, { date: 1461339000000, open: 51.855, high: 51.87, low: 51.72, close: 51.7568, volume: 384513, }, { date: 1461339060000, open: 51.76, high: 51.8, low: 51.7599, close: 51.77, volume: 341178, }, { date: 1461339120000, open: 51.78, high: 51.89, low: 51.7701, close: 51.855, volume: 308936, }, { date: 1461339180000, open: 51.855, high: 51.87, low: 51.8, close: 51.85, volume: 279776, }, { date: 1461339240000, open: 51.85, high: 51.86, low: 51.8, close: 51.81, volume: 314400, }, { date: 1461339300000, open: 51.8168, high: 51.91, low: 51.81, close: 51.91, volume: 217129, }, { date: 1461339360000, open: 51.905, high: 52, low: 51.9, close: 51.955, volume: 293420, }, { date: 1461339420000, open: 51.955, high: 52.09, low: 51.95, close: 52.005, volume: 407924, }, { date: 1461339480000, open: 52.005, high: 52.09, low: 51.9899, close: 52.045, volume: 501549, }, { date: 1461339540000, open: 52.0465, high: 52.1, low: 52, close: 52, volume: 218270, }, { date: 1461339600000, open: 52, high: 52.1, low: 51.98, close: 51.995, volume: 491296, }, { date: 1461339660000, open: 51.999, high: 52.07, low: 51.99, close: 52.0601, volume: 138617, }, { date: 1461339720000, open: 52.06, high: 52.08, low: 51.99, close: 52.04, volume: 141544, }, { date: 1461339780000, open: 52.04, high: 52.06, low: 52.02, close: 52.02, volume: 76011, }, { date: 1461339840000, open: 52.02, high: 52.0264, low: 52, close: 52.005, volume: 99228, }, { date: 1461339900000, open: 52.01, high: 52.04, low: 51.99, close: 52.0359, volume: 252194, }, { date: 1461339960000, open: 52.03, high: 52.0336, low: 51.99, close: 51.995, volume: 228580, }, { date: 1461340020000, open: 51.9901, high: 52.03, low: 51.98, close: 52, volume: 197122, }, { date: 1461340080000, open: 52.01, high: 52.05, low: 52.005, close: 52.0359, volume: 204625, }, { date: 1461340140000, open: 52.03, high: 52.035, low: 51.99, close: 52.0264, volume: 225259, }, { date: 1461340200000, open: 52.026, high: 52.07, low: 51.99, close: 52.05, volume: 208344, }, { date: 1461340260000, open: 52.05, high: 52.16, low: 52.04, close: 52.1295, volume: 307954, }, { date: 1461340320000, open: 52.125, high: 52.13, low: 52.09, close: 52.1, volume: 237527, }, { date: 1461340380000, open: 52.1, high: 52.16, low: 52.1, close: 52.1572, volume: 175756, }, { date: 1461340440000, open: 52.16, high: 52.21, low: 52.16, close: 52.21, volume: 188784, }, { date: 1461340500000, open: 52.21, high: 52.225, low: 52.2, close: 52.21, volume: 188184, }, { date: 1461340560000, open: 52.205, high: 52.22, low: 52.18, close: 52.19, volume: 237482, }, { date: 1461340620000, open: 52.19, high: 52.22, low: 52.19, close: 52.2, volume: 160352, }, { date: 1461340680000, open: 52.205, high: 52.21, low: 52.07, close: 52.08, volume: 312004, }, { date: 1461340740000, open: 52.08, high: 52.12, low: 52.02, close: 52.0501, volume: 132226, }, { date: 1461340800000, open: 52.06, high: 52.11, low: 52.045, close: 52.1, volume: 175336, }, { date: 1461340860000, open: 52.1, high: 52.1, low: 52.03, close: 52.065, volume: 166493, }, { date: 1461340920000, open: 52.065, high: 52.1, low: 52.05, close: 52.09, volume: 189291, }, { date: 1461340980000, open: 52.09, high: 52.15, low: 52.08, close: 52.14, volume: 142035, }, { date: 1461341040000, open: 52.14, high: 52.15, low: 52.1, close: 52.115, volume: 101557, }, { date: 1461341100000, open: 52.11, high: 52.119, low: 52.06, close: 52.0611, volume: 129446, }, { date: 1461341160000, open: 52.06, high: 52.07, low: 52.05, close: 52.055, volume: 178672, }, { date: 1461341220000, open: 52.055, high: 52.06, low: 52, close: 52.005, volume: 191384, }, { date: 1461341280000, open: 52.005, high: 52.035, low: 52, close: 52.02, volume: 115932, }, { date: 1461341340000, open: 52.0201, high: 52.06, low: 52.01, close: 52.05, volume: 273179, }, { date: 1461341400000, open: 52.05, high: 52.06, low: 52.01, close: 52.03, volume: 308884, }, { date: 1461341460000, open: 52.025, high: 52.05, low: 52.01, close: 52.01, volume: 160025, }, { date: 1461341520000, open: 52.015, high: 52.05, low: 52.01, close: 52.04, volume: 144714, }, { date: 1461341580000, open: 52.0499, high: 52.06, low: 52.005, close: 52.02, volume: 434582, }, { date: 1461341640000, open: 52.01, high: 52.0199, low: 51.96, close: 52, volume: 214529, }, { date: 1461341700000, open: 51.995, high: 52, low: 51.855, close: 51.8601, volume: 212980, }, { date: 1461341760000, open: 51.87, high: 51.87, low: 51.83, close: 51.8401, volume: 111738, }, { date: 1461341820000, open: 51.845, high: 51.91, low: 51.84, close: 51.89, volume: 156009, }, { date: 1461341880000, open: 51.89, high: 51.93, low: 51.88, close: 51.9291, volume: 139087, }, { date: 1461341940000, open: 51.93, high: 51.95, low: 51.92, close: 51.93, volume: 116998, }, { date: 1461342000000, open: 51.93, high: 51.94, low: 51.92, close: 51.935, volume: 73439, }, { date: 1461342060000, open: 51.9335, high: 51.94, low: 51.88, close: 51.885, volume: 152044, }, { date: 1461342120000, open: 51.88, high: 51.96, low: 51.88, close: 51.9091, volume: 104610, }, { date: 1461342180000, open: 51.905, high: 51.92, low: 51.9, close: 51.905, volume: 66838, }, { date: 1461342240000, open: 51.905, high: 51.95, low: 51.9, close: 51.9399, volume: 128296, }, { date: 1461342300000, open: 51.931, high: 51.95, low: 51.9, close: 51.915, volume: 118888, }, { date: 1461342360000, open: 51.915, high: 51.94, low: 51.91, close: 51.925, volume: 70877, }, { date: 1461342420000, open: 51.9259, high: 51.94, low: 51.925, close: 51.935, volume: 57476, }, { date: 1461342480000, open: 51.93, high: 51.94, low: 51.9, close: 51.9099, volume: 101770, }, { date: 1461342540000, open: 51.91, high: 51.91, low: 51.87, close: 51.88, volume: 160199, }, { date: 1461342600000, open: 51.88, high: 51.885, low: 51.84, close: 51.85, volume: 180997, }, { date: 1461342660000, open: 51.845, high: 51.89, low: 51.84, close: 51.886, volume: 90150, }, { date: 1461342720000, open: 51.89, high: 51.9, low: 51.88, close: 51.8989, volume: 124365, }, { date: 1461342780000, open: 51.9, high: 51.9, low: 51.89, close: 51.895, volume: 28919, }, { date: 1461342840000, open: 51.895, high: 52, low: 51.89, close: 51.985, volume: 174595, }, { date: 1461342900000, open: 51.99, high: 52.05, low: 51.97, close: 51.98, volume: 166384, }, { date: 1461342960000, open: 51.975, high: 52.01, low: 51.975, close: 51.98, volume: 76318, }, { date: 1461343020000, open: 51.98, high: 51.9899, low: 51.91, close: 51.9301, volume: 117443, }, { date: 1461343080000, open: 51.9366, high: 51.95, low: 51.9, close: 51.905, volume: 158163, }, { date: 1461343140000, open: 51.905, high: 51.91, low: 51.85, close: 51.88, volume: 206137, }, { date: 1461343200000, open: 51.88, high: 51.92, low: 51.86, close: 51.9189, volume: 92427, }, { date: 1461343260000, open: 51.91, high: 51.92, low: 51.89, close: 51.8901, volume: 99352, }, { date: 1461343320000, open: 51.891, high: 51.91, low: 51.865, close: 51.9, volume: 53799, }, { date: 1461343380000, open: 51.905, high: 51.93, low: 51.88, close: 51.8899, volume: 783042, }, { date: 1461343440000, open: 51.88, high: 51.9, low: 51.87, close: 51.8977, volume: 31297, }, { date: 1461343500000, open: 51.893, high: 51.9, low: 51.85, close: 51.85, volume: 141480, }, { date: 1461343560000, open: 51.85, high: 51.8571, low: 51.67, close: 51.685, volume: 239668, }, { date: 1461343620000, open: 51.69, high: 51.69, low: 51.6, close: 51.645, volume: 297881, }, { date: 1461343680000, open: 51.64, high: 51.6466, low: 51.56, close: 51.57, volume: 110782, }, { date: 1461343740000, open: 51.57, high: 51.675, low: 51.57, close: 51.675, volume: 105488, }, { date: 1461343800000, open: 51.6799, high: 51.69, low: 51.59, close: 51.6099, volume: 168975, }, { date: 1461343860000, open: 51.6093, high: 51.64, low: 51.595, close: 51.635, volume: 293818, }, { date: 1461343920000, open: 51.64, high: 51.69, low: 51.63, close: 51.64, volume: 94861, }, { date: 1461343980000, open: 51.64, high: 51.685, low: 51.62, close: 51.685, volume: 102578, }, { date: 1461344040000, open: 51.69, high: 51.695, low: 51.65, close: 51.655, volume: 135461, }, { date: 1461344100000, open: 51.6565, high: 51.6565, low: 51.62, close: 51.62, volume: 203734, }, { date: 1461344160000, open: 51.62, high: 51.64, low: 51.6, close: 51.63, volume: 108532, }, { date: 1461344220000, open: 51.6399, high: 51.64, low: 51.61, close: 51.62, volume: 127543, }, { date: 1461344280000, open: 51.63, high: 51.74, low: 51.625, close: 51.735, volume: 169039, }, { date: 1461344340000, open: 51.73, high: 51.76, low: 51.7, close: 51.7554, volume: 142228, }, { date: 1461344400000, open: 51.755, high: 51.76, low: 51.7, close: 51.7, volume: 148574, }, { date: 1461344460000, open: 51.7, high: 51.78, low: 51.68, close: 51.755, volume: 139796, }, { date: 1461344520000, open: 51.75, high: 51.76, low: 51.7, close: 51.7, volume: 131424, }, { date: 1461344580000, open: 51.7, high: 51.72, low: 51.7, close: 51.715, volume: 154148, }, { date: 1461344640000, open: 51.715, high: 51.76, low: 51.715, close: 51.75, volume: 103533, }, { date: 1461344700000, open: 51.751, high: 51.76, low: 51.73, close: 51.735, volume: 141939, }, { date: 1461344760000, open: 51.74, high: 51.76, low: 51.72, close: 51.755, volume: 155449, }, { date: 1461344820000, open: 51.755, high: 51.79, low: 51.74, close: 51.7866, volume: 166760, }, { date: 1461344880000, open: 51.785, high: 51.8, low: 51.705, close: 51.715, volume: 89610, }, { date: 1461344940000, open: 51.71, high: 51.72, low: 51.69, close: 51.698, volume: 181843, }, { date: 1461345000000, open: 51.7, high: 51.72, low: 51.69, close: 51.7, volume: 65620, }, { date: 1461345060000, open: 51.7, high: 51.71, low: 51.695, close: 51.705, volume: 94065, }, { date: 1461345120000, open: 51.705, high: 51.73, low: 51.7, close: 51.7266, volume: 90049, }, { date: 1461345180000, open: 51.72, high: 51.73, low: 51.71, close: 51.7239, volume: 66219, }, { date: 1461345240000, open: 51.725, high: 51.78, low: 51.71, close: 51.775, volume: 88242, }, { date: 1461345300000, open: 51.78, high: 51.81, low: 51.755, close: 51.76, volume: 155997, }, { date: 1461345360000, open: 51.765, high: 51.77, low: 51.73, close: 51.73, volume: 92778, }, { date: 1461345420000, open: 51.7331, high: 51.7341, low: 51.71, close: 51.725, volume: 79673, }, { date: 1461345480000, open: 51.72, high: 51.81, low: 51.72, close: 51.81, volume: 128555, }, { date: 1461345540000, open: 51.805, high: 51.82, low: 51.78, close: 51.815, volume: 116234, }, { date: 1461345600000, open: 51.815, high: 51.8797, low: 51.8, close: 51.8692, volume: 268108, }, { date: 1461345660000, open: 51.87, high: 51.875, low: 51.82, close: 51.825, volume: 150499, }, { date: 1461345720000, open: 51.825, high: 51.83, low: 51.78, close: 51.7864, volume: 105153, }, { date: 1461345780000, open: 51.78, high: 51.7866, low: 51.7364, close: 51.755, volume: 169239, }, { date: 1461345840000, open: 51.76, high: 51.76, low: 51.74, close: 51.75, volume: 76496, }, { date: 1461345900000, open: 51.7501, high: 51.79, low: 51.75, close: 51.783, volume: 103735, }, { date: 1461345960000, open: 51.7867, high: 51.805, low: 51.78, close: 51.79, volume: 64451, }, { date: 1461346020000, open: 51.7834, high: 51.79, low: 51.72, close: 51.7299, volume: 128775, }, { date: 1461346080000, open: 51.73, high: 51.75, low: 51.72, close: 51.75, volume: 147957, }, { date: 1461346140000, open: 51.75, high: 51.75, low: 51.7101, close: 51.715, volume: 100285, }, { date: 1461346200000, open: 51.715, high: 51.72, low: 51.65, close: 51.685, volume: 149935, }, { date: 1461346260000, open: 51.69, high: 51.73, low: 51.68, close: 51.726, volume: 89266, }, { date: 1461346320000, open: 51.72, high: 51.74, low: 51.71, close: 51.725, volume: 52486, }, { date: 1461346380000, open: 51.725, high: 51.73, low: 51.7, close: 51.705, volume: 50941, }, { date: 1461346440000, open: 51.705, high: 51.71, low: 51.7, close: 51.7, volume: 55145, }, { date: 1461346500000, open: 51.71, high: 51.77, low: 51.7, close: 51.73, volume: 153854, }, { date: 1461346560000, open: 51.729, high: 51.74, low: 51.72, close: 51.723, volume: 62937, }, { date: 1461346620000, open: 51.725, high: 51.73, low: 51.7, close: 51.705, volume: 87215, }, { date: 1461346680000, open: 51.705, high: 51.715, low: 51.7, close: 51.715, volume: 137270, }, { date: 1461346740000, open: 51.72, high: 51.77, low: 51.71, close: 51.74, volume: 185420, }, { date: 1461346800000, open: 51.75, high: 51.76, low: 51.71, close: 51.716, volume: 81222, }, { date: 1461346860000, open: 51.71, high: 51.73, low: 51.69, close: 51.7, volume: 182293, }, { date: 1461346920000, open: 51.694, high: 51.757, low: 51.694, close: 51.75, volume: 79470, }, { date: 1461346980000, open: 51.745, high: 51.84, low: 51.745, close: 51.82, volume: 152872, }, { date: 1461347040000, open: 51.8105, high: 51.88, low: 51.81, close: 51.88, volume: 94510, }, { date: 1461347100000, open: 51.885, high: 51.9791, low: 51.885, close: 51.92, volume: 268070, }, { date: 1461347160000, open: 51.91, high: 52, low: 51.9, close: 51.9334, volume: 216559, }, { date: 1461347220000, open: 51.935, high: 51.94, low: 51.92, close: 51.93, volume: 124193, }, { date: 1461347280000, open: 51.925, high: 52.0191, low: 51.91, close: 52, volume: 352596, }, { date: 1461347340000, open: 52.005, high: 52.09, low: 52, close: 52.09, volume: 186215, }, { date: 1461347400000, open: 52.085, high: 52.11, low: 52.02, close: 52.03, volume: 184159, }, { date: 1461347460000, open: 52.035, high: 52.04, low: 52, close: 52, volume: 157347, }, { date: 1461347520000, open: 52.005, high: 52.03, low: 52, close: 52.005, volume: 371358, }, { date: 1461347580000, open: 52.005, high: 52.05, low: 52, close: 52.025, volume: 189892, }, { date: 1461347640000, open: 52.03, high: 52.07, low: 52.02, close: 52.05, volume: 116965, }, { date: 1461347700000, open: 52.045, high: 52.09, low: 52, close: 52.065, volume: 144324, }, { date: 1461347760000, open: 52.075, high: 52.075, low: 51.98, close: 51.98, volume: 216479, }, { date: 1461347820000, open: 51.9825, high: 51.9825, low: 51.88, close: 51.8864, volume: 131951, }, { date: 1461347880000, open: 51.885, high: 51.89, low: 51.85, close: 51.86, volume: 84369, }, { date: 1461347940000, open: 51.86, high: 51.9, low: 51.85, close: 51.8999, volume: 67561, }, { date: 1461348000000, open: 51.9, high: 51.92, low: 51.89, close: 51.8934, volume: 208483, }, { date: 1461348060000, open: 51.9, high: 51.92, low: 51.9, close: 51.92, volume: 130256, }, { date: 1461348120000, open: 51.92, high: 51.9799, low: 51.91, close: 51.9768, volume: 117329, }, { date: 1461348180000, open: 51.974, high: 52.02, low: 51.97, close: 52.0167, volume: 89310, }, { date: 1461348240000, open: 52.0101, high: 52.04, low: 51.99, close: 52.0368, volume: 93394, }, { date: 1461348300000, open: 52.04, high: 52.04, low: 51.98, close: 51.98, volume: 138891, }, { date: 1461348360000, open: 51.97, high: 51.975, low: 51.95, close: 51.9595, volume: 66278, }, { date: 1461348420000, open: 51.95, high: 51.955, low: 51.86, close: 51.87, volume: 145279, }, { date: 1461348480000, open: 51.87, high: 51.88, low: 51.85, close: 51.8534, volume: 137210, }, { date: 1461348540000, open: 51.8562, high: 51.87, low: 51.845, close: 51.86, volume: 127092, }, { date: 1461348600000, open: 51.856, high: 51.865, low: 51.79, close: 51.8099, volume: 151494, }, { date: 1461348660000, open: 51.81, high: 51.87, low: 51.8, close: 51.865, volume: 113002, }, { date: 1461348720000, open: 51.8699, high: 51.87, low: 51.85, close: 51.865, volume: 49517, }, { date: 1461348780000, open: 51.865, high: 51.93, low: 51.865, close: 51.88, volume: 92541, }, { date: 1461348840000, open: 51.8734, high: 51.89, low: 51.87, close: 51.885, volume: 65299, }, { date: 1461348900000, open: 51.8862, high: 51.92, low: 51.88, close: 51.9199, volume: 80162, }, { date: 1461348960000, open: 51.92, high: 51.95, low: 51.91, close: 51.915, volume: 90041, }, { date: 1461349020000, open: 51.915, high: 51.92, low: 51.9, close: 51.905, volume: 57830, }, { date: 1461349080000, open: 51.91, high: 51.94, low: 51.9, close: 51.936, volume: 142049, }, { date: 1461349140000, open: 51.935, high: 52.01, low: 51.92, close: 52.0095, volume: 102152, }, { date: 1461349200000, open: 52.01, high: 52.03, low: 52, close: 52.005, volume: 100990, }, { date: 1461349260000, open: 52.0029, high: 52.0099, low: 51.94, close: 52, volume: 171254, }, { date: 1461349320000, open: 52, high: 52.01, low: 51.98, close: 51.9864, volume: 83013, }, { date: 1461349380000, open: 51.99, high: 52.015, low: 51.98, close: 52, volume: 131754, }, { date: 1461349440000, open: 51.995, high: 52.08, low: 51.995, close: 52.06, volume: 142860, }, { date: 1461349500000, open: 52.0666, high: 52.0699, low: 52.02, close: 52.02, volume: 93912, }, { date: 1461349560000, open: 52.03, high: 52.05, low: 52.02, close: 52.035, volume: 150261, }, { date: 1461349620000, open: 52.04, high: 52.07, low: 52.03, close: 52.065, volume: 87871, }, { date: 1461349680000, open: 52.06, high: 52.09, low: 52.04, close: 52.055, volume: 161890, }, { date: 1461349740000, open: 52.05, high: 52.06, low: 52, close: 52, volume: 158379, }, { date: 1461349800000, open: 52, high: 52.01, low: 51.97, close: 52.0075, volume: 128292, }, { date: 1461349860000, open: 52.01, high: 52.02, low: 51.99, close: 52.01, volume: 76306, }, { date: 1461349920000, open: 52.01, high: 52.03, low: 52, close: 52.015, volume: 117064, }, { date: 1461349980000, open: 52.017, high: 52.04, low: 52.01, close: 52.035, volume: 57315, }, { date: 1461350040000, open: 52.03, high: 52.03, low: 51.975, close: 51.985, volume: 148255, }, { date: 1461350100000, open: 51.98, high: 51.99, low: 51.91, close: 51.92, volume: 136579, }, { date: 1461350160000, open: 51.9299, high: 51.99, low: 51.92, close: 51.98, volume: 73032, }, { date: 1461350220000, open: 51.9899, high: 52.02, low: 51.98, close: 52.005, volume: 165938, }, { date: 1461350280000, open: 52.0065, high: 52.03, low: 52, close: 52.025, volume: 250968, }, { date: 1461350340000, open: 52.025, high: 52.05, low: 52.02, close: 52.04, volume: 98092, }, { date: 1461350400000, open: 52.05, high: 52.06, low: 52.04, close: 52.055, volume: 320919, }, { date: 1461350460000, open: 52.055, high: 52.085, low: 52.05, close: 52.065, volume: 84598, }, { date: 1461350520000, open: 52.065, high: 52.07, low: 52.02, close: 52.025, volume: 75803, }, { date: 1461350580000, open: 52.03, high: 52.03, low: 52, close: 52.005, volume: 200200, }, { date: 1461350640000, open: 52.005, high: 52.08, low: 52, close: 52.065, volume: 126503, }, { date: 1461350700000, open: 52.065, high: 52.08, low: 52.04, close: 52.08, volume: 95626, }, { date: 1461350760000, open: 52.0735, high: 52.0765, low: 52.02, close: 52.02, volume: 71761, }, { date: 1461350820000, open: 52.03, high: 52.04, low: 51.99, close: 52, volume: 191581, }, { date: 1461350880000, open: 52.01, high: 52.02, low: 51.995, close: 52.015, volume: 61511, }, { date: 1461350940000, open: 52.014, high: 52.08, low: 52.01, close: 52.08, volume: 105583, }, { date: 1461351000000, open: 52.07, high: 52.0762, low: 52.02, close: 52.03, volume: 100041, }, { date: 1461351060000, open: 52.03, high: 52.035, low: 52, close: 52.015, volume: 196547, }, { date: 1461351120000, open: 52.01, high: 52.02, low: 52, close: 52.02, volume: 88662, }, { date: 1461351180000, open: 52.015, high: 52.03, low: 52.01, close: 52.0165, volume: 67623, }, { date: 1461351240000, open: 52.02, high: 52.02, low: 52.005, close: 52.015, volume: 81597, }, { date: 1461351300000, open: 52.015, high: 52.04, low: 52.01, close: 52.025, volume: 165666, }, { date: 1461351360000, open: 52.028, high: 52.04, low: 52.01, close: 52.02, volume: 110480, }, { date: 1461351420000, open: 52.02, high: 52.05, low: 52.01, close: 52.0389, volume: 86499, }, { date: 1461351480000, open: 52.03, high: 52.04, low: 51.99, close: 52, volume: 138786, }, { date: 1461351540000, open: 51.9959, high: 52.01, low: 51.99, close: 51.9915, volume: 135821, }, { date: 1461351600000, open: 51.995, high: 52.01, low: 51.98, close: 52, volume: 142480, }, { date: 1461351660000, open: 52, high: 52.005, low: 51.94, close: 51.945, volume: 206199, }, { date: 1461351720000, open: 51.945, high: 51.96, low: 51.88, close: 51.8999, volume: 181709, }, { date: 1461351780000, open: 51.8999, high: 51.91, low: 51.85, close: 51.88, volume: 169963, }, { date: 1461351840000, open: 51.8735, high: 51.895, low: 51.85, close: 51.855, volume: 148425, }, { date: 1461351900000, open: 51.8558, high: 51.86, low: 51.83, close: 51.84, volume: 249979, }, { date: 1461351960000, open: 51.8499, high: 51.91, low: 51.82, close: 51.9038, volume: 131629, }, { date: 1461352020000, open: 51.91, high: 51.93, low: 51.88, close: 51.9235, volume: 122860, }, { date: 1461352080000, open: 51.926, high: 51.9275, low: 51.91, close: 51.92, volume: 42366, }, { date: 1461352140000, open: 51.91, high: 51.93, low: 51.89, close: 51.9192, volume: 133930, }, { date: 1461352200000, open: 51.915, high: 51.92, low: 51.88, close: 51.9199, volume: 113100, }, { date: 1461352260000, open: 51.93, high: 52, low: 51.92, close: 51.97, volume: 157267, }, { date: 1461352320000, open: 51.97, high: 51.99, low: 51.94, close: 51.975, volume: 171714, }, { date: 1461352380000, open: 51.97, high: 51.98, low: 51.88, close: 51.88, volume: 213437, }, { date: 1461352440000, open: 51.88, high: 51.92, low: 51.88, close: 51.905, volume: 201202, }, { date: 1461352500000, open: 51.905, high: 51.9065, low: 51.78, close: 51.783, volume: 295549, }, { date: 1461352560000, open: 51.79, high: 51.8, low: 51.7, close: 51.71, volume: 281972, }, { date: 1461352620000, open: 51.705, high: 51.71, low: 51.65, close: 51.6834, volume: 313058, }, { date: 1461352680000, open: 51.6872, high: 51.7299, low: 51.64, close: 51.72, volume: 218059, }, { date: 1461352740000, open: 51.72, high: 51.7599, low: 51.7, close: 51.7398, volume: 166679, }, { date: 1461352800000, open: 51.74, high: 51.7699, low: 51.72, close: 51.72, volume: 123014, }, { date: 1461352860000, open: 51.72, high: 51.74, low: 51.7, close: 51.725, volume: 131390, }, { date: 1461352920000, open: 51.72, high: 51.73, low: 51.67, close: 51.7299, volume: 217898, }, { date: 1461352980000, open: 51.725, high: 51.75, low: 51.7, close: 51.7499, volume: 162296, }, { date: 1461353040000, open: 51.745, high: 51.745, low: 51.68, close: 51.735, volume: 246690, }, { date: 1461353100000, open: 51.7399, high: 51.79, low: 51.72, close: 51.7362, volume: 261164, }, { date: 1461353160000, open: 51.736, high: 51.76, low: 51.72, close: 51.74, volume: 106987, }, { date: 1461353220000, open: 51.735, high: 51.78, low: 51.735, close: 51.77, volume: 156943, }, { date: 1461353280000, open: 51.766, high: 51.8, low: 51.76, close: 51.7897, volume: 190798, }, { date: 1461353340000, open: 51.785, high: 51.82, low: 51.77, close: 51.81, volume: 112470, }, { date: 1461353400000, open: 51.82, high: 51.85, low: 51.81, close: 51.83, volume: 179079, }, { date: 1461353460000, open: 51.8301, high: 51.8399, low: 51.75, close: 51.7665, volume: 253217, }, { date: 1461353520000, open: 51.765, high: 51.795, low: 51.75, close: 51.775, volume: 159931, }, { date: 1461353580000, open: 51.775, high: 51.79, low: 51.75, close: 51.79, volume: 152949, }, { date: 1461353640000, open: 51.785, high: 51.81, low: 51.77, close: 51.805, volume: 171978, }, { date: 1461353700000, open: 51.8, high: 51.8087, low: 51.74, close: 51.74, volume: 195472, }, { date: 1461353760000, open: 51.7401, high: 51.75, low: 51.72, close: 51.74, volume: 132009, }, { date: 1461353820000, open: 51.735, high: 51.76, low: 51.73, close: 51.75, volume: 99995, }, { date: 1461353880000, open: 51.745, high: 51.77, low: 51.74, close: 51.7599, volume: 247220, }, { date: 1461353940000, open: 51.75, high: 51.76, low: 51.7, close: 51.725, volume: 196597, }, { date: 1461354000000, open: 51.72, high: 51.735, low: 51.69, close: 51.695, volume: 215646, }, { date: 1461354060000, open: 51.69, high: 51.7, low: 51.65, close: 51.69, volume: 302547, }, { date: 1461354120000, open: 51.68, high: 51.69, low: 51.61, close: 51.635, volume: 322216, }, { date: 1461354180000, open: 51.64, high: 51.67, low: 51.61, close: 51.65, volume: 349391, }, { date: 1461354240000, open: 51.655, high: 51.66, low: 51.64, close: 51.645, volume: 203356, }, { date: 1461354300000, open: 51.649, high: 51.66, low: 51.63, close: 51.65, volume: 214053, }, { date: 1461354360000, open: 51.65, high: 51.7, low: 51.64, close: 51.685, volume: 320156, }, { date: 1461354420000, open: 51.685, high: 51.69, low: 51.68, close: 51.685, volume: 328772, }, { date: 1461354480000, open: 51.685, high: 51.69, low: 51.66, close: 51.665, volume: 289212, }, { date: 1461354540000, open: 51.667, high: 51.68, low: 51.64, close: 51.65, volume: 380628, }, { date: 1461354600000, open: 51.655, high: 51.76, low: 51.65, close: 51.745, volume: 323415, }, { date: 1461354660000, open: 51.755, high: 51.77, low: 51.71, close: 51.7365, volume: 620111, }, { date: 1461354720000, open: 51.7365, high: 51.77, low: 51.73, close: 51.76, volume: 326603, }, { date: 1461354780000, open: 51.76, high: 51.83, low: 51.76, close: 51.815, volume: 339264, }, { date: 1461354840000, open: 51.815, high: 51.82, low: 51.77, close: 51.785, volume: 316305, }, { date: 1461354900000, open: 51.785, high: 51.79, low: 51.7601, close: 51.77, volume: 441181, }, { date: 1461354960000, open: 51.78, high: 51.79, low: 51.76, close: 51.77, volume: 545388, }, { date: 1461355020000, open: 51.77, high: 51.82, low: 51.765, close: 51.805, volume: 437874, }, { date: 1461355080000, open: 51.8, high: 51.81, low: 51.77, close: 51.777, volume: 450747, }, { date: 1461355140000, open: 51.775, high: 51.78, low: 51.72, close: 51.77, volume: 749366, }, { date: 1461355200000, open: 51.765, high: 51.87, low: 51.73, close: 51.78, volume: 6084639, }, { date: 1461591000000, open: 51.78, high: 51.84, low: 51.77, close: 51.81, volume: 588218, }, { date: 1461591060000, open: 51.79, high: 51.86, low: 51.72, close: 51.74, volume: 160350, }, { date: 1461591120000, open: 51.745, high: 51.76, low: 51.64, close: 51.645, volume: 257999, }, { date: 1461591180000, open: 51.6405, high: 51.75, low: 51.63, close: 51.7499, volume: 229991, }, { date: 1461591240000, open: 51.745, high: 51.8025, low: 51.73, close: 51.75, volume: 198480, }, { date: 1461591300000, open: 51.745, high: 51.82, low: 51.74, close: 51.82, volume: 186251, }, { date: 1461591360000, open: 51.83, high: 51.91, low: 51.822, close: 51.88, volume: 227322, }, { date: 1461591420000, open: 51.88, high: 51.905, low: 51.83, close: 51.88, volume: 157948, }, { date: 1461591480000, open: 51.88, high: 51.96, low: 51.88, close: 51.94, volume: 196169, }, { date: 1461591540000, open: 51.9499, high: 51.99, low: 51.94, close: 51.9401, volume: 155545, }, { date: 1461591600000, open: 51.945, high: 51.95, low: 51.92, close: 51.9205, volume: 120714, }, { date: 1461591660000, open: 51.925, high: 51.925, low: 51.78, close: 51.8163, volume: 263986, }, { date: 1461591720000, open: 51.815, high: 51.82, low: 51.71, close: 51.77, volume: 142780, }, { date: 1461591780000, open: 51.775, high: 51.82, low: 51.77, close: 51.791, volume: 131694, }, { date: 1461591840000, open: 51.795, high: 51.96, low: 51.7909, close: 51.9101, volume: 168698, }, { date: 1461591900000, open: 51.915, high: 51.92, low: 51.84, close: 51.855, volume: 87433, }, { date: 1461591960000, open: 51.855, high: 51.89, low: 51.85, close: 51.885, volume: 57411, }, { date: 1461592020000, open: 51.885, high: 51.92, low: 51.86, close: 51.91, volume: 115549, }, { date: 1461592080000, open: 51.91, high: 51.95, low: 51.88, close: 51.9131, volume: 98045, }, { date: 1461592140000, open: 51.91, high: 51.92, low: 51.8538, close: 51.87, volume: 74013, }, { date: 1461592200000, open: 51.8662, high: 51.88, low: 51.83, close: 51.872, volume: 125514, }, { date: 1461592260000, open: 51.87, high: 51.88, low: 51.84, close: 51.8499, volume: 86575, }, { date: 1461592320000, open: 51.845, high: 51.87, low: 51.8, close: 51.865, volume: 88289, }, { date: 1461592380000, open: 51.87, high: 51.88, low: 51.855, close: 51.865, volume: 58772, }, { date: 1461592440000, open: 51.86, high: 51.87, low: 51.83, close: 51.8501, volume: 75701, }, { date: 1461592500000, open: 51.85, high: 51.865, low: 51.83, close: 51.865, volume: 63213, }, { date: 1461592560000, open: 51.865, high: 51.89, low: 51.85, close: 51.875, volume: 152798, }, { date: 1461592620000, open: 51.87, high: 51.89, low: 51.855, close: 51.885, volume: 99840, }, { date: 1461592680000, open: 51.88, high: 51.945, low: 51.87, close: 51.945, volume: 156791, }, { date: 1461592740000, open: 51.95, high: 52, low: 51.94, close: 51.97, volume: 158906, }, { date: 1461592800000, open: 51.972, high: 52, low: 51.91, close: 52, volume: 134219, }, { date: 1461592860000, open: 52, high: 52.08, low: 51.99, close: 52.06, volume: 248854, }, { date: 1461592920000, open: 52.06, high: 52.075, low: 52.01, close: 52.02, volume: 186843, }, { date: 1461592980000, open: 52.02, high: 52.03, low: 51.97, close: 52.03, volume: 141652, }, { date: 1461593040000, open: 52.03, high: 52.1, low: 52.03, close: 52.04, volume: 113084, }, { date: 1461593100000, open: 52.045, high: 52.06, low: 51.97, close: 51.975, volume: 116502, }, { date: 1461593160000, open: 51.98, high: 52.01, low: 51.9748, close: 52, volume: 143056, }, { date: 1461593220000, open: 52, high: 52.015, low: 51.995, close: 52, volume: 125342, }, { date: 1461593280000, open: 52, high: 52.045, low: 51.99, close: 52.025, volume: 55946, }, { date: 1461593340000, open: 52.02, high: 52.03, low: 51.99, close: 52, volume: 83913, }, { date: 1461593400000, open: 52, high: 52, low: 51.97, close: 51.975, volume: 60483, }, { date: 1461593460000, open: 51.97, high: 51.99, low: 51.951, close: 51.98, volume: 125233, }, { date: 1461593520000, open: 51.975, high: 51.98, low: 51.955, close: 51.975, volume: 89984, }, { date: 1461593580000, open: 51.9762, high: 51.98, low: 51.94, close: 51.98, volume: 112733, }, { date: 1461593640000, open: 51.975, high: 52, low: 51.97, close: 51.975, volume: 80854, }, { date: 1461593700000, open: 51.97, high: 52.01, low: 51.97, close: 51.985, volume: 246192, }, { date: 1461593760000, open: 51.98, high: 52.01, low: 51.98, close: 52.005, volume: 104896, }, { date: 1461593820000, open: 52.01, high: 52.04, low: 52, close: 52.035, volume: 115504, }, { date: 1461593880000, open: 52.04, high: 52.05, low: 52.03, close: 52.035, volume: 106171, }, { date: 1461593940000, open: 52.035, high: 52.04, low: 52, close: 52, volume: 92269, }, { date: 1461594000000, open: 52, high: 52.01, low: 51.9975, close: 52, volume: 86877, }, { date: 1461594060000, open: 52, high: 52.005, low: 51.94, close: 51.94, volume: 123366, }, { date: 1461594120000, open: 51.9499, high: 51.9615, low: 51.94, close: 51.94, volume: 54268, }, { date: 1461594180000, open: 51.9489, high: 51.97, low: 51.91, close: 51.915, volume: 63386, }, { date: 1461594240000, open: 51.918, high: 51.92, low: 51.88, close: 51.88, volume: 54103, }, { date: 1461594300000, open: 51.89, high: 51.89, low: 51.865, close: 51.865, volume: 51689, }, { date: 1461594360000, open: 51.865, high: 51.93, low: 51.86, close: 51.8899, volume: 87384, }, { date: 1461594420000, open: 51.89, high: 51.9, low: 51.85, close: 51.855, volume: 69694, }, { date: 1461594480000, open: 51.86, high: 51.88, low: 51.82, close: 51.87, volume: 163214, }, { date: 1461594540000, open: 51.87, high: 51.91, low: 51.8601, close: 51.895, volume: 143582, }, { date: 1461594600000, open: 51.895, high: 51.92, low: 51.89, close: 51.905, volume: 35931, }, { date: 1461594660000, open: 51.9099, high: 51.91, low: 51.87, close: 51.885, volume: 114183, }, { date: 1461594720000, open: 51.89, high: 51.91, low: 51.86, close: 51.8799, volume: 101912, }, { date: 1461594780000, open: 51.88, high: 51.88, low: 51.86, close: 51.865, volume: 80242, }, { date: 1461594840000, open: 51.87, high: 51.88, low: 51.86, close: 51.86, volume: 92566, }, { date: 1461594900000, open: 51.865, high: 51.94, low: 51.8635, close: 51.94, volume: 198274, }, { date: 1461594960000, open: 51.94, high: 51.99, low: 51.93, close: 51.95, volume: 193026, }, { date: 1461595020000, open: 51.96, high: 51.98, low: 51.94, close: 51.96, volume: 81441, }, { date: 1461595080000, open: 51.955, high: 51.96, low: 51.95, close: 51.955, volume: 49397, }, { date: 1461595140000, open: 51.955, high: 51.955, low: 51.9, close: 51.9464, volume: 136371, }, { date: 1461595200000, open: 51.95, high: 51.95, low: 51.89, close: 51.92, volume: 80190, }, { date: 1461595260000, open: 51.93, high: 51.945, low: 51.91, close: 51.94, volume: 71739, }, { date: 1461595320000, open: 51.945, high: 52, low: 51.93, close: 51.98, volume: 81048, }, { date: 1461595380000, open: 51.98, high: 52.01, low: 51.95, close: 51.955, volume: 223866, }, { date: 1461595440000, open: 51.955, high: 51.96, low: 51.89, close: 51.9, volume: 184175, }, { date: 1461595500000, open: 51.8987, high: 51.9, low: 51.86, close: 51.889, volume: 112705, }, { date: 1461595560000, open: 51.885, high: 51.889, low: 51.825, close: 51.8276, volume: 154690, }, { date: 1461595620000, open: 51.83, high: 51.89, low: 51.83, close: 51.89, volume: 122451, }, { date: 1461595680000, open: 51.89, high: 51.97, low: 51.8801, close: 51.97, volume: 86409, }, { date: 1461595740000, open: 51.97, high: 51.98, low: 51.95, close: 51.9675, volume: 70494, }, { date: 1461595800000, open: 51.965, high: 51.97, low: 51.92, close: 51.935, volume: 70270, }, { date: 1461595860000, open: 51.935, high: 51.95, low: 51.87, close: 51.87, volume: 80454, }, { date: 1461595920000, open: 51.8764, high: 51.9, low: 51.87, close: 51.89, volume: 116636, }, { date: 1461595980000, open: 51.89, high: 51.9, low: 51.87, close: 51.875, volume: 79035, }, { date: 1461596040000, open: 51.87, high: 51.89, low: 51.82, close: 51.89, volume: 127682, }, { date: 1461596100000, open: 51.89, high: 51.9, low: 51.85, close: 51.855, volume: 37057, }, { date: 1461596160000, open: 51.855, high: 51.86, low: 51.83, close: 51.84, volume: 67143, }, { date: 1461596220000, open: 51.84, high: 51.85, low: 51.84, close: 51.845, volume: 24678, }, { date: 1461596280000, open: 51.8415, high: 51.85, low: 51.82, close: 51.8266, volume: 133909, }, { date: 1461596340000, open: 51.82, high: 51.8469, low: 51.82, close: 51.84, volume: 88710, }, { date: 1461596400000, open: 51.84, high: 51.845, low: 51.81, close: 51.83, volume: 63574, }, { date: 1461596460000, open: 51.8289, high: 51.88, low: 51.82, close: 51.8799, volume: 65122, }, { date: 1461596520000, open: 51.88, high: 51.9301, low: 51.88, close: 51.895, volume: 57609, }, { date: 1461596580000, open: 51.895, high: 51.91, low: 51.88, close: 51.91, volume: 47384, }, { date: 1461596640000, open: 51.91, high: 51.92, low: 51.88, close: 51.89, volume: 75404, }, { date: 1461596700000, open: 51.9, high: 51.92, low: 51.87, close: 51.87, volume: 78708, }, { date: 1461596760000, open: 51.87, high: 51.885, low: 51.87, close: 51.885, volume: 58179, }, { date: 1461596820000, open: 51.8867, high: 51.89, low: 51.875, close: 51.88, volume: 34033, }, { date: 1461596880000, open: 51.885, high: 51.885, low: 51.82, close: 51.82, volume: 64833, }, { date: 1461596940000, open: 51.82, high: 51.8271, low: 51.77, close: 51.78, volume: 169937, }, { date: 1461597000000, open: 51.7799, high: 51.81, low: 51.74, close: 51.75, volume: 144209, }, { date: 1461597060000, open: 51.74, high: 51.75, low: 51.7, close: 51.745, volume: 138045, }, { date: 1461597120000, open: 51.74, high: 51.83, low: 51.74, close: 51.83, volume: 137588, }, { date: 1461597180000, open: 51.83, high: 51.845, low: 51.82, close: 51.8369, volume: 35000, }, { date: 1461597240000, open: 51.83, high: 51.8765, low: 51.83, close: 51.86, volume: 84606, }, { date: 1461597300000, open: 51.87, high: 51.88, low: 51.85, close: 51.875, volume: 72782, }, { date: 1461597360000, open: 51.87, high: 51.9, low: 51.8601, close: 51.9, volume: 41478, }, { date: 1461597420000, open: 51.9, high: 51.91, low: 51.86, close: 51.865, volume: 65696, }, { date: 1461597480000, open: 51.865, high: 51.875, low: 51.84, close: 51.86, volume: 54606, }, { date: 1461597540000, open: 51.8663, high: 51.93, low: 51.86, close: 51.9, volume: 90485, }, { date: 1461597600000, open: 51.9, high: 51.905, low: 51.85, close: 51.86, volume: 67488, }, { date: 1461597660000, open: 51.855, high: 51.8665, low: 51.835, close: 51.855, volume: 99995, }, { date: 1461597720000, open: 51.855, high: 51.91, low: 51.85, close: 51.885, volume: 45698, }, { date: 1461597780000, open: 51.8801, high: 51.91, low: 51.87, close: 51.9045, volume: 53207, }, { date: 1461597840000, open: 51.9, high: 51.91, low: 51.88, close: 51.91, volume: 29845, }, { date: 1461597900000, open: 51.91, high: 51.91, low: 51.875, close: 51.8766, volume: 48574, }, { date: 1461597960000, open: 51.87, high: 51.9, low: 51.87, close: 51.885, volume: 60468, }, { date: 1461598020000, open: 51.885, high: 51.93, low: 51.88, close: 51.91, volume: 49900, }, { date: 1461598080000, open: 51.915, high: 51.94, low: 51.91, close: 51.93, volume: 62538, }, { date: 1461598140000, open: 51.93, high: 51.94, low: 51.9101, close: 51.93, volume: 32941, }, { date: 1461598200000, open: 51.93, high: 51.965, low: 51.9244, close: 51.96, volume: 80266, }, { date: 1461598260000, open: 51.965, high: 51.97, low: 51.94, close: 51.94, volume: 62038, }, { date: 1461598320000, open: 51.94, high: 51.945, low: 51.91, close: 51.915, volume: 37106, }, { date: 1461598380000, open: 51.915, high: 51.94, low: 51.91, close: 51.9301, volume: 35830, }, { date: 1461598440000, open: 51.93, high: 51.94, low: 51.91, close: 51.91, volume: 26277, }, { date: 1461598500000, open: 51.9137, high: 51.9255, low: 51.91, close: 51.915, volume: 31793, }, { date: 1461598560000, open: 51.91, high: 51.915, low: 51.9, close: 51.9, volume: 29035, }, { date: 1461598620000, open: 51.9065, high: 51.92, low: 51.86, close: 51.865, volume: 114281, }, { date: 1461598680000, open: 51.87, high: 51.87, low: 51.84, close: 51.84, volume: 35480, }, { date: 1461598740000, open: 51.84, high: 51.86, low: 51.83, close: 51.845, volume: 70153, }, { date: 1461598800000, open: 51.845, high: 51.86, low: 51.84, close: 51.85, volume: 18227, }, { date: 1461598860000, open: 51.855, high: 51.9, low: 51.8546, close: 51.9, volume: 56411, }, { date: 1461598920000, open: 51.9, high: 51.9, low: 51.86, close: 51.8662, volume: 53162, }, { date: 1461598980000, open: 51.8638, high: 51.9, low: 51.8638, close: 51.8964, volume: 35289, }, { date: 1461599040000, open: 51.895, high: 51.905, low: 51.8701, close: 51.9, volume: 82923, }, { date: 1461599100000, open: 51.9, high: 51.9, low: 51.83, close: 51.8399, volume: 120728, }, { date: 1461599160000, open: 51.84, high: 51.85, low: 51.83, close: 51.84, volume: 69673, }, { date: 1461599220000, open: 51.84, high: 51.87, low: 51.835, close: 51.86, volume: 45558, }, { date: 1461599280000, open: 51.8564, high: 51.89, low: 51.85, close: 51.875, volume: 86329, }, { date: 1461599340000, open: 51.875, high: 51.88, low: 51.8501, close: 51.86, volume: 36846, }, { date: 1461599400000, open: 51.86, high: 51.86, low: 51.81, close: 51.83, volume: 77893, }, { date: 1461599460000, open: 51.83, high: 51.84, low: 51.82, close: 51.84, volume: 55625, }, { date: 1461599520000, open: 51.84, high: 51.86, low: 51.84, close: 51.85, volume: 18939, }, { date: 1461599580000, open: 51.8558, high: 51.87, low: 51.84, close: 51.865, volume: 96700, }, { date: 1461599640000, open: 51.865, high: 51.885, low: 51.855, close: 51.8599, volume: 50090, }, { date: 1461599700000, open: 51.86, high: 51.86, low: 51.835, close: 51.845, volume: 41664, }, { date: 1461599760000, open: 51.84, high: 51.86, low: 51.835, close: 51.86, volume: 60130, }, { date: 1461599820000, open: 51.86, high: 51.88, low: 51.85, close: 51.85, volume: 41365, }, { date: 1461599880000, open: 51.855, high: 51.87, low: 51.845, close: 51.8665, volume: 36150, }, { date: 1461599940000, open: 51.87, high: 51.885, low: 51.855, close: 51.855, volume: 21056, }, { date: 1461600000000, open: 51.85, high: 51.86, low: 51.84, close: 51.841, volume: 60663, }, { date: 1461600060000, open: 51.8465, high: 51.87, low: 51.84, close: 51.85, volume: 54523, }, { date: 1461600120000, open: 51.855, high: 51.86, low: 51.845, close: 51.8599, volume: 24644, }, { date: 1461600180000, open: 51.85, high: 51.87, low: 51.85, close: 51.86, volume: 28913, }, { date: 1461600240000, open: 51.86, high: 51.87, low: 51.845, close: 51.8501, volume: 53611, }, { date: 1461600300000, open: 51.86, high: 51.87, low: 51.85, close: 51.855, volume: 29360, }, { date: 1461600360000, open: 51.8501, high: 51.86, low: 51.845, close: 51.85, volume: 37580, }, { date: 1461600420000, open: 51.86, high: 51.87, low: 51.845, close: 51.86, volume: 126334, }, { date: 1461600480000, open: 51.86, high: 51.88, low: 51.86, close: 51.865, volume: 38440, }, { date: 1461600540000, open: 51.867, high: 51.87, low: 51.855, close: 51.865, volume: 44223, }, { date: 1461600600000, open: 51.8601, high: 51.87, low: 51.86, close: 51.87, volume: 10877, }, { date: 1461600660000, open: 51.87, high: 51.87, low: 51.85, close: 51.855, volume: 21681, }, { date: 1461600720000, open: 51.855, high: 51.86, low: 51.825, close: 51.84, volume: 57843, }, { date: 1461600780000, open: 51.8335, high: 51.85, low: 51.83, close: 51.8364, volume: 43688, }, { date: 1461600840000, open: 51.84, high: 51.85, low: 51.83, close: 51.84, volume: 49728, }, { date: 1461600900000, open: 51.847, high: 51.85, low: 51.83, close: 51.85, volume: 38291, }, { date: 1461600960000, open: 51.85, high: 51.86, low: 51.84, close: 51.8599, volume: 48331, }, { date: 1461601020000, open: 51.855, high: 51.86, low: 51.84, close: 51.85, volume: 30618, }, { date: 1461601080000, open: 51.855, high: 51.86, low: 51.84, close: 51.8485, volume: 14961, }, { date: 1461601140000, open: 51.846, high: 51.85, low: 51.83, close: 51.83, volume: 23982, }, { date: 1461601200000, open: 51.83, high: 51.86, low: 51.83, close: 51.835, volume: 58489, }, { date: 1461601260000, open: 51.84, high: 51.85, low: 51.83, close: 51.8499, volume: 23762, }, { date: 1461601320000, open: 51.84, high: 51.86, low: 51.83, close: 51.85, volume: 66504, }, { date: 1461601380000, open: 51.855, high: 51.86, low: 51.83, close: 51.8336, volume: 51170, }, { date: 1461601440000, open: 51.83, high: 51.83, low: 51.78, close: 51.8064, volume: 138471, }, { date: 1461601500000, open: 51.802, high: 51.81, low: 51.795, close: 51.8, volume: 44940, }, { date: 1461601560000, open: 51.8099, high: 51.81, low: 51.79, close: 51.79, volume: 31029, }, { date: 1461601620000, open: 51.7999, high: 51.8, low: 51.78, close: 51.795, volume: 37291, }, { date: 1461601680000, open: 51.7999, high: 51.8, low: 51.79, close: 51.7991, volume: 13993, }, { date: 1461601740000, open: 51.795, high: 51.81, low: 51.79, close: 51.8064, volume: 34046, }, { date: 1461601800000, open: 51.8, high: 51.81, low: 51.79, close: 51.8062, volume: 37496, }, { date: 1461601860000, open: 51.8089, high: 51.845, low: 51.795, close: 51.83, volume: 59988, }, { date: 1461601920000, open: 51.825, high: 51.83, low: 51.8201, close: 51.8299, volume: 12796, }, { date: 1461601980000, open: 51.83, high: 51.83, low: 51.82, close: 51.8264, volume: 19402, }, { date: 1461602040000, open: 51.825, high: 51.84, low: 51.82, close: 51.82, volume: 59170, }, { date: 1461602100000, open: 51.8299, high: 51.8565, low: 51.82, close: 51.85, volume: 132005, }, { date: 1461602160000, open: 51.85, high: 51.85, low: 51.831, close: 51.84, volume: 40366, }, { date: 1461602220000, open: 51.845, high: 51.85, low: 51.835, close: 51.85, volume: 31938, }, { date: 1461602280000, open: 51.84, high: 51.85, low: 51.83, close: 51.8399, volume: 25190, }, { date: 1461602340000, open: 51.83, high: 51.8338, low: 51.8, close: 51.8068, volume: 60873, }, { date: 1461602400000, open: 51.805, high: 51.82, low: 51.8, close: 51.805, volume: 26238, }, { date: 1461602460000, open: 51.809, high: 51.82, low: 51.8, close: 51.8162, volume: 13625, }, { date: 1461602520000, open: 51.82, high: 51.82, low: 51.81, close: 51.815, volume: 13126, }, { date: 1461602580000, open: 51.815, high: 51.82, low: 51.81, close: 51.81, volume: 19232, }, { date: 1461602640000, open: 51.815, high: 51.82, low: 51.8, close: 51.8, volume: 73933, }, { date: 1461602700000, open: 51.8064, high: 51.83, low: 51.8, close: 51.83, volume: 61768, }, { date: 1461602760000, open: 51.825, high: 51.83, low: 51.815, close: 51.83, volume: 15899, }, { date: 1461602820000, open: 51.83, high: 51.835, low: 51.82, close: 51.822, volume: 25708, }, { date: 1461602880000, open: 51.8298, high: 51.84, low: 51.8298, close: 51.835, volume: 13337, }, { date: 1461602940000, open: 51.835, high: 51.85, low: 51.835, close: 51.84, volume: 24676, }, { date: 1461603000000, open: 51.84, high: 51.86, low: 51.83, close: 51.841, volume: 84935, }, { date: 1461603060000, open: 51.85, high: 51.86, low: 51.84, close: 51.8587, volume: 34104, }, { date: 1461603120000, open: 51.85, high: 51.905, low: 51.85, close: 51.89, volume: 86207, }, { date: 1461603180000, open: 51.895, high: 51.94, low: 51.895, close: 51.935, volume: 45274, }, { date: 1461603240000, open: 51.935, high: 52.01, low: 51.935, close: 51.965, volume: 159621, }, { date: 1461603300000, open: 51.97, high: 51.97, low: 51.945, close: 51.955, volume: 89000, }, { date: 1461603360000, open: 51.9559, high: 51.96, low: 51.94, close: 51.9572, volume: 46596, }, { date: 1461603420000, open: 51.9533, high: 52.015, low: 51.94, close: 51.95, volume: 183157, }, { date: 1461603480000, open: 51.95, high: 51.95, low: 51.93, close: 51.94, volume: 52026, }, { date: 1461603540000, open: 51.94, high: 51.9436, low: 51.93, close: 51.935, volume: 21253, }, { date: 1461603600000, open: 51.94, high: 51.96, low: 51.94, close: 51.95, volume: 32147, }, { date: 1461603660000, open: 51.9499, high: 51.95, low: 51.94, close: 51.94, volume: 28809, }, { date: 1461603720000, open: 51.94, high: 51.95, low: 51.935, close: 51.946, volume: 36368, }, { date: 1461603780000, open: 51.95, high: 51.955, low: 51.94, close: 51.95, volume: 33375, }, { date: 1461603840000, open: 51.95, high: 51.96, low: 51.94, close: 51.95, volume: 82868, }, { date: 1461603900000, open: 51.945, high: 51.95, low: 51.9268, close: 51.9268, volume: 33896, }, { date: 1461603960000, open: 51.92, high: 51.9201, low: 51.91, close: 51.91, volume: 26951, }, { date: 1461604020000, open: 51.91, high: 51.92, low: 51.9, close: 51.9099, volume: 20472, }, { date: 1461604080000, open: 51.91, high: 51.92, low: 51.9, close: 51.915, volume: 30094, }, { date: 1461604140000, open: 51.91, high: 51.93, low: 51.9062, close: 51.9201, volume: 34102, }, { date: 1461604200000, open: 51.93, high: 51.9399, low: 51.905, close: 51.905, volume: 26875, }, { date: 1461604260000, open: 51.909, high: 51.91, low: 51.86, close: 51.87, volume: 80791, }, { date: 1461604320000, open: 51.875, high: 51.9, low: 51.87, close: 51.8858, volume: 72816, }, { date: 1461604380000, open: 51.885, high: 51.885, low: 51.87, close: 51.8762, volume: 20519, }, { date: 1461604440000, open: 51.88, high: 51.89, low: 51.87, close: 51.8861, volume: 44907, }, { date: 1461604500000, open: 51.89, high: 51.91, low: 51.88, close: 51.8999, volume: 54950, }, { date: 1461604560000, open: 51.89, high: 51.91, low: 51.89, close: 51.9, volume: 15987, }, { date: 1461604620000, open: 51.9, high: 51.9075, low: 51.86, close: 51.86, volume: 27110, }, { date: 1461604680000, open: 51.864, high: 51.87, low: 51.85, close: 51.85, volume: 28106, }, { date: 1461604740000, open: 51.85, high: 51.86, low: 51.84, close: 51.84, volume: 56947, }, { date: 1461604800000, open: 51.84, high: 51.8575, low: 51.84, close: 51.8568, volume: 47085, }, { date: 1461604860000, open: 51.86, high: 51.87, low: 51.85, close: 51.865, volume: 47146, }, { date: 1461604920000, open: 51.863, high: 51.87, low: 51.86, close: 51.87, volume: 12992, }, { date: 1461604980000, open: 51.86, high: 51.87, low: 51.86, close: 51.86, volume: 11299, }, { date: 1461605040000, open: 51.86, high: 51.869, low: 51.85, close: 51.85, volume: 32693, }, { date: 1461605100000, open: 51.855, high: 51.87, low: 51.84, close: 51.86, volume: 159681, }, { date: 1461605160000, open: 51.86, high: 51.93, low: 51.86, close: 51.93, volume: 64783, }, { date: 1461605220000, open: 51.93, high: 51.95, low: 51.915, close: 51.93, volume: 34476, }, { date: 1461605280000, open: 51.925, high: 51.93, low: 51.87, close: 51.8705, volume: 54068, }, { date: 1461605340000, open: 51.8741, high: 51.91, low: 51.87, close: 51.9099, volume: 30025, }, { date: 1461605400000, open: 51.9, high: 51.91, low: 51.895, close: 51.91, volume: 35890, }, { date: 1461605460000, open: 51.904, high: 51.91, low: 51.9, close: 51.9, volume: 9840, }, { date: 1461605520000, open: 51.91, high: 51.91, low: 51.89, close: 51.9, volume: 36720, }, { date: 1461605580000, open: 51.9, high: 51.9025, low: 51.87, close: 51.875, volume: 60551, }, { date: 1461605640000, open: 51.875, high: 51.88, low: 51.87, close: 51.875, volume: 10501, }, { date: 1461605700000, open: 51.88, high: 51.89, low: 51.86, close: 51.885, volume: 37997, }, { date: 1461605760000, open: 51.89, high: 51.89, low: 51.87, close: 51.875, volume: 64496, }, { date: 1461605820000, open: 51.875, high: 51.8799, low: 51.86, close: 51.865, volume: 15190, }, { date: 1461605880000, open: 51.865, high: 51.865, low: 51.84, close: 51.85, volume: 72900, }, { date: 1461605940000, open: 51.8565, high: 51.86, low: 51.85, close: 51.855, volume: 95304, }, { date: 1461606000000, open: 51.8555, high: 51.88, low: 51.85, close: 51.87, volume: 23175, }, { date: 1461606060000, open: 51.87, high: 51.9, low: 51.87, close: 51.89, volume: 44359, }, { date: 1461606120000, open: 51.89, high: 51.89, low: 51.88, close: 51.89, volume: 12124, }, { date: 1461606180000, open: 51.886, high: 51.89, low: 51.88, close: 51.88, volume: 14525, }, { date: 1461606240000, open: 51.875, high: 51.875, low: 51.85, close: 51.85, volume: 17050, }, { date: 1461606300000, open: 51.855, high: 51.86, low: 51.845, close: 51.85, volume: 48772, }, { date: 1461606360000, open: 51.855, high: 51.86, low: 51.85, close: 51.85, volume: 14894, }, { date: 1461606420000, open: 51.855, high: 51.86, low: 51.85, close: 51.855, volume: 13428, }, { date: 1461606480000, open: 51.85, high: 51.86, low: 51.84, close: 51.8585, volume: 36574, }, { date: 1461606540000, open: 51.86, high: 51.885, low: 51.855, close: 51.87, volume: 48116, }, { date: 1461606600000, open: 51.8799, high: 51.88, low: 51.865, close: 51.879, volume: 38682, }, { date: 1461606660000, open: 51.8701, high: 51.88, low: 51.87, close: 51.875, volume: 13408, }, { date: 1461606720000, open: 51.875, high: 51.9099, low: 51.875, close: 51.895, volume: 60754, }, { date: 1461606780000, open: 51.8968, high: 51.9, low: 51.88, close: 51.8801, volume: 32509, }, { date: 1461606840000, open: 51.88, high: 51.9, low: 51.8705, close: 51.9, volume: 42643, }, { date: 1461606900000, open: 51.9, high: 51.91, low: 51.9, close: 51.905, volume: 38389, }, { date: 1461606960000, open: 51.905, high: 51.91, low: 51.9, close: 51.905, volume: 80748, }, { date: 1461607020000, open: 51.91, high: 51.91, low: 51.89, close: 51.906, volume: 56688, }, { date: 1461607080000, open: 51.9001, high: 51.91, low: 51.88, close: 51.885, volume: 31568, }, { date: 1461607140000, open: 51.885, high: 51.9, low: 51.88, close: 51.895, volume: 23395, }, { date: 1461607200000, open: 51.895, high: 51.9, low: 51.88, close: 51.88, volume: 26251, }, { date: 1461607260000, open: 51.89, high: 51.91, low: 51.8833, close: 51.91, volume: 40370, }, { date: 1461607320000, open: 51.9099, high: 51.93, low: 51.9, close: 51.92, volume: 123270, }, { date: 1461607380000, open: 51.9165, high: 51.93, low: 51.91, close: 51.925, volume: 22757, }, { date: 1461607440000, open: 51.925, high: 51.95, low: 51.925, close: 51.949, volume: 89793, }, { date: 1461607500000, open: 51.95, high: 51.95, low: 51.92, close: 51.925, volume: 20100, }, { date: 1461607560000, open: 51.925, high: 51.925, low: 51.86, close: 51.86, volume: 87470, }, { date: 1461607620000, open: 51.865, high: 51.88, low: 51.85, close: 51.85, volume: 60588, }, { date: 1461607680000, open: 51.85, high: 51.88, low: 51.85, close: 51.87, volume: 70267, }, { date: 1461607740000, open: 51.875, high: 51.89, low: 51.87, close: 51.875, volume: 42345, }, { date: 1461607800000, open: 51.875, high: 51.88, low: 51.87, close: 51.87, volume: 27357, }, { date: 1461607860000, open: 51.88, high: 51.88, low: 51.84, close: 51.85, volume: 108797, }, { date: 1461607920000, open: 51.85, high: 51.86, low: 51.85, close: 51.86, volume: 21878, }, { date: 1461607980000, open: 51.85, high: 51.86, low: 51.8, close: 51.819, volume: 102452, }, { date: 1461608040000, open: 51.81, high: 51.83, low: 51.81, close: 51.825, volume: 55523, }, { date: 1461608100000, open: 51.826, high: 51.85, low: 51.82, close: 51.85, volume: 61836, }, { date: 1461608160000, open: 51.85, high: 51.86, low: 51.84, close: 51.845, volume: 32594, }, { date: 1461608220000, open: 51.85, high: 51.8575, low: 51.84, close: 51.845, volume: 29647, }, { date: 1461608280000, open: 51.8499, high: 51.85, low: 51.84, close: 51.84, volume: 25657, }, { date: 1461608340000, open: 51.84, high: 51.855, low: 51.84, close: 51.855, volume: 47548, }, { date: 1461608400000, open: 51.85, high: 51.8599, low: 51.84, close: 51.845, volume: 64259, }, { date: 1461608460000, open: 51.85, high: 51.86, low: 51.85, close: 51.85, volume: 38494, }, { date: 1461608520000, open: 51.85, high: 51.85, low: 51.83, close: 51.841, volume: 43206, }, { date: 1461608580000, open: 51.845, high: 51.85, low: 51.84, close: 51.845, volume: 14362, }, { date: 1461608640000, open: 51.845, high: 51.86, low: 51.84, close: 51.85, volume: 62432, }, { date: 1461608700000, open: 51.85, high: 51.89, low: 51.845, close: 51.88, volume: 52188, }, { date: 1461608760000, open: 51.89, high: 51.915, low: 51.87, close: 51.91, volume: 59112, }, { date: 1461608820000, open: 51.9, high: 51.92, low: 51.88, close: 51.915, volume: 34941, }, { date: 1461608880000, open: 51.915, high: 51.915, low: 51.89, close: 51.9, volume: 36768, }, { date: 1461608940000, open: 51.9044, high: 51.92, low: 51.9, close: 51.915, volume: 66967, }, { date: 1461609000000, open: 51.915, high: 51.94, low: 51.91, close: 51.935, volume: 36072, }, { date: 1461609060000, open: 51.94, high: 51.94, low: 51.92, close: 51.935, volume: 40957, }, { date: 1461609120000, open: 51.935, high: 51.94, low: 51.93, close: 51.93, volume: 9187, }, { date: 1461609180000, open: 51.93, high: 51.96, low: 51.92, close: 51.9468, volume: 122256, }, { date: 1461609240000, open: 51.945, high: 51.9667, low: 51.935, close: 51.945, volume: 46496, }, { date: 1461609300000, open: 51.941, high: 51.95, low: 51.94, close: 51.95, volume: 27358, }, { date: 1461609360000, open: 51.95, high: 51.955, low: 51.94, close: 51.95, volume: 78516, }, { date: 1461609420000, open: 51.96, high: 51.97, low: 51.95, close: 51.9571, volume: 36045, }, { date: 1461609480000, open: 51.955, high: 51.97, low: 51.941, close: 51.96, volume: 51523, }, { date: 1461609540000, open: 51.96, high: 52, low: 51.96, close: 52, volume: 128743, }, { date: 1461609600000, open: 51.9901, high: 52, low: 51.97, close: 51.98, volume: 72969, }, { date: 1461609660000, open: 51.98, high: 51.99, low: 51.98, close: 51.985, volume: 14844, }, { date: 1461609720000, open: 51.9899, high: 51.99, low: 51.98, close: 51.99, volume: 29410, }, { date: 1461609780000, open: 51.9899, high: 51.99, low: 51.955, close: 51.96, volume: 95416, }, { date: 1461609840000, open: 51.965, high: 52, low: 51.96, close: 51.9902, volume: 57364, }, { date: 1461609900000, open: 51.999, high: 52.01, low: 51.99, close: 51.99, volume: 110265, }, { date: 1461609960000, open: 51.995, high: 52, low: 51.99, close: 52, volume: 20683, }, { date: 1461610020000, open: 52, high: 52, low: 51.99, close: 52, volume: 41021, }, { date: 1461610080000, open: 51.995, high: 52, low: 51.99, close: 51.99, volume: 30276, }, { date: 1461610140000, open: 52, high: 52, low: 51.99, close: 52, volume: 22421, }, { date: 1461610200000, open: 52, high: 52, low: 51.95, close: 51.96, volume: 90663, }, { date: 1461610260000, open: 51.955, high: 51.96, low: 51.94, close: 51.945, volume: 30190, }, { date: 1461610320000, open: 51.945, high: 51.95, low: 51.94, close: 51.9475, volume: 23475, }, { date: 1461610380000, open: 51.95, high: 51.95, low: 51.93, close: 51.9365, volume: 36480, }, { date: 1461610440000, open: 51.94, high: 51.95, low: 51.935, close: 51.95, volume: 39243, }, { date: 1461610500000, open: 51.95, high: 51.98, low: 51.93, close: 51.975, volume: 98290, }, { date: 1461610560000, open: 51.98, high: 51.98, low: 51.95, close: 51.975, volume: 32713, }, { date: 1461610620000, open: 51.98, high: 52.005, low: 51.9701, close: 51.995, volume: 65255, }, { date: 1461610680000, open: 52, high: 52.02, low: 51.99, close: 52, volume: 98182, }, { date: 1461610740000, open: 52, high: 52.015, low: 51.99, close: 51.99, volume: 38008, }, { date: 1461610800000, open: 51.991, high: 52.005, low: 51.98, close: 51.9899, volume: 44925, }, { date: 1461610860000, open: 51.99, high: 51.99, low: 51.97, close: 51.9867, volume: 38142, }, { date: 1461610920000, open: 51.99, high: 51.9998, low: 51.98, close: 51.9899, volume: 30614, }, { date: 1461610980000, open: 51.99, high: 51.99, low: 51.95, close: 51.9568, volume: 36806, }, { date: 1461611040000, open: 51.9599, high: 51.9599, low: 51.945, close: 51.95, volume: 46413, }, { date: 1461611100000, open: 51.95, high: 51.97, low: 51.9341, close: 51.935, volume: 124097, }, { date: 1461611160000, open: 51.935, high: 51.95, low: 51.93, close: 51.9325, volume: 48434, }, { date: 1461611220000, open: 51.9398, high: 51.95, low: 51.93, close: 51.9495, volume: 50811, }, { date: 1461611280000, open: 51.95, high: 51.97, low: 51.94, close: 51.9499, volume: 77131, }, { date: 1461611340000, open: 51.94, high: 51.9499, low: 51.93, close: 51.9376, volume: 25202, }, { date: 1461611400000, open: 51.93, high: 51.9399, low: 51.87, close: 51.89, volume: 133573, }, { date: 1461611460000, open: 51.88, high: 51.92, low: 51.87, close: 51.915, volume: 61359, }, { date: 1461611520000, open: 51.92, high: 51.93, low: 51.905, close: 51.915, volume: 50181, }, { date: 1461611580000, open: 51.9201, high: 51.925, low: 51.89, close: 51.91, volume: 43905, }, { date: 1461611640000, open: 51.908, high: 51.93, low: 51.9, close: 51.93, volume: 25243, }, { date: 1461611700000, open: 51.925, high: 51.93, low: 51.9, close: 51.925, volume: 41217, }, { date: 1461611760000, open: 51.925, high: 51.93, low: 51.91, close: 51.92, volume: 29919, }, { date: 1461611820000, open: 51.92, high: 51.93, low: 51.91, close: 51.91, volume: 21313, }, { date: 1461611880000, open: 51.915, high: 51.92, low: 51.9, close: 51.905, volume: 63659, }, { date: 1461611940000, open: 51.905, high: 51.91, low: 51.87, close: 51.88, volume: 41530, }, { date: 1461612000000, open: 51.88, high: 51.92, low: 51.87, close: 51.92, volume: 74171, }, { date: 1461612060000, open: 51.915, high: 51.93, low: 51.891, close: 51.905, volume: 51902, }, { date: 1461612120000, open: 51.905, high: 51.93, low: 51.9, close: 51.93, volume: 24093, }, { date: 1461612180000, open: 51.92, high: 51.925, low: 51.91, close: 51.91, volume: 35625, }, { date: 1461612240000, open: 51.9134, high: 51.93, low: 51.905, close: 51.9101, volume: 37307, }, { date: 1461612300000, open: 51.92, high: 51.95, low: 51.91, close: 51.945, volume: 55034, }, { date: 1461612360000, open: 51.95, high: 51.95, low: 51.89, close: 51.89, volume: 165949, }, { date: 1461612420000, open: 51.9, high: 51.9, low: 51.88, close: 51.8999, volume: 60860, }, { date: 1461612480000, open: 51.9, high: 51.91, low: 51.875, close: 51.885, volume: 87251, }, { date: 1461612540000, open: 51.885, high: 51.91, low: 51.885, close: 51.9, volume: 44293, }, { date: 1461612600000, open: 51.91, high: 51.91, low: 51.89, close: 51.905, volume: 40367, }, { date: 1461612660000, open: 51.905, high: 51.91, low: 51.8801, close: 51.905, volume: 64493, }, { date: 1461612720000, open: 51.905, high: 51.93, low: 51.9, close: 51.9272, volume: 59799, }, { date: 1461612780000, open: 51.929, high: 51.95, low: 51.9201, close: 51.945, volume: 51555, }, { date: 1461612840000, open: 51.9499, high: 51.96, low: 51.93, close: 51.955, volume: 94230, }, { date: 1461612900000, open: 51.9585, high: 51.97, low: 51.94, close: 51.96, volume: 88037, }, { date: 1461612960000, open: 51.9598, high: 52.01, low: 51.95, close: 52, volume: 241179, }, { date: 1461613020000, open: 52, high: 52.01, low: 51.99, close: 52, volume: 147541, }, { date: 1461613080000, open: 51.995, high: 52, low: 51.95, close: 51.95, volume: 109697, }, { date: 1461613140000, open: 51.95, high: 51.96, low: 51.92, close: 51.92, volume: 53994, }, { date: 1461613200000, open: 51.93, high: 51.94, low: 51.915, close: 51.94, volume: 112228, }, { date: 1461613260000, open: 51.94, high: 51.97, low: 51.93, close: 51.965, volume: 121263, }, { date: 1461613320000, open: 51.965, high: 52, low: 51.9615, close: 51.98, volume: 95651, }, { date: 1461613380000, open: 51.98, high: 52, low: 51.98, close: 51.995, volume: 57242, }, { date: 1461613440000, open: 51.9901, high: 52, low: 51.98, close: 51.99, volume: 52551, }, { date: 1461613500000, open: 51.995, high: 52, low: 51.98, close: 51.985, volume: 77291, }, { date: 1461613560000, open: 51.98, high: 52.01, low: 51.97, close: 52.005, volume: 127738, }, { date: 1461613620000, open: 52.01, high: 52.02, low: 52, close: 52, volume: 99896, }, { date: 1461613680000, open: 52, high: 52.01, low: 51.99, close: 52.005, volume: 181608, }, { date: 1461613740000, open: 52.01, high: 52.03, low: 52.0001, close: 52.015, volume: 132220, }, { date: 1461613800000, open: 52.01, high: 52.03, low: 52.01, close: 52.03, volume: 80022, }, { date: 1461613860000, open: 52.02, high: 52.04, low: 52.01, close: 52.01, volume: 122634, }, { date: 1461613920000, open: 52.01, high: 52.02, low: 52, close: 52.01, volume: 130967, }, { date: 1461613980000, open: 52.01, high: 52.01, low: 52, close: 52, volume: 97397, }, { date: 1461614040000, open: 52.01, high: 52.01, low: 51.96, close: 51.97, volume: 151256, }, { date: 1461614100000, open: 51.97, high: 52, low: 51.965, close: 51.99, volume: 134503, }, { date: 1461614160000, open: 51.99, high: 52.01, low: 51.98, close: 52.01, volume: 125610, }, { date: 1461614220000, open: 52.01, high: 52.03, low: 52, close: 52.03, volume: 165358, }, { date: 1461614280000, open: 52.025, high: 52.05, low: 52.02, close: 52.05, volume: 140282, }, { date: 1461614340000, open: 52.04, high: 52.06, low: 52.02, close: 52.04, volume: 255190, }, { date: 1461614400000, open: 52.04, high: 52.13, low: 52.035, close: 52.11, volume: 1716650, }, { date: 1461677400000, open: 52.26, high: 52.29, low: 52.23, close: 52.25, volume: 341414, }, { date: 1461677460000, open: 52.24, high: 52.35, low: 52.215, close: 52.28, volume: 118991, }, { date: 1461677520000, open: 52.28, high: 52.28, low: 52.165, close: 52.21, volume: 68832, }, { date: 1461677580000, open: 52.21, high: 52.27, low: 52.19, close: 52.19, volume: 51440, }, { date: 1461677640000, open: 52.19, high: 52.23, low: 52.16, close: 52.16, volume: 55784, }, { date: 1461677700000, open: 52.16, high: 52.165, low: 52.0899, close: 52.0899, volume: 85914, }, { date: 1461677760000, open: 52.0801, high: 52.11, low: 51.97, close: 51.98, volume: 134229, }, { date: 1461677820000, open: 51.99, high: 52.06, low: 51.93, close: 52.06, volume: 115736, }, { date: 1461677880000, open: 52.07, high: 52.07, low: 52.02, close: 52.045, volume: 85184, }, { date: 1461677940000, open: 52.05, high: 52.05, low: 51.99, close: 51.995, volume: 71923, }, { date: 1461678000000, open: 52, high: 52, low: 51.95, close: 51.95, volume: 74423, }, { date: 1461678060000, open: 51.94, high: 51.96, low: 51.9, close: 51.94, volume: 89548, }, { date: 1461678120000, open: 51.945, high: 51.985, low: 51.94, close: 51.95, volume: 75591, }, { date: 1461678180000, open: 51.9499, high: 51.9499, low: 51.8, close: 51.8, volume: 180273, }, { date: 1461678240000, open: 51.805, high: 51.87, low: 51.79, close: 51.86, volume: 153161, }, { date: 1461678300000, open: 51.86, high: 51.91, low: 51.82, close: 51.9, volume: 170911, }, { date: 1461678360000, open: 51.9, high: 51.98, low: 51.9, close: 51.96, volume: 124832, }, { date: 1461678420000, open: 51.96, high: 51.965, low: 51.9, close: 51.9161, volume: 97148, }, { date: 1461678480000, open: 51.92, high: 51.93, low: 51.85, close: 51.92, volume: 72061, }, { date: 1461678540000, open: 51.92, high: 51.96, low: 51.86, close: 51.87, volume: 88790, }, { date: 1461678600000, open: 51.87, high: 51.9, low: 51.84, close: 51.855, volume: 105365, }, { date: 1461678660000, open: 51.85, high: 51.86, low: 51.78, close: 51.785, volume: 127577, }, { date: 1461678720000, open: 51.79, high: 51.82, low: 51.7899, close: 51.81, volume: 70054, }, { date: 1461678780000, open: 51.81, high: 51.85, low: 51.8, close: 51.845, volume: 94726, }, { date: 1461678840000, open: 51.85, high: 51.85, low: 51.8242, close: 51.83, volume: 107978, }, { date: 1461678900000, open: 51.83, high: 51.94, low: 51.825, close: 51.92, volume: 117804, }, { date: 1461678960000, open: 51.925, high: 51.95, low: 51.88, close: 51.95, volume: 102652, }, { date: 1461679020000, open: 51.945, high: 51.96, low: 51.88, close: 51.8999, volume: 133381, }, { date: 1461679080000, open: 51.895, high: 51.895, low: 51.85, close: 51.86, volume: 79650, }, { date: 1461679140000, open: 51.855, high: 51.86, low: 51.82, close: 51.83, volume: 114242, }, { date: 1461679200000, open: 51.825, high: 51.835, low: 51.79, close: 51.82, volume: 121551, }, { date: 1461679260000, open: 51.815, high: 51.89, low: 51.81, close: 51.8731, volume: 61928, }, { date: 1461679320000, open: 51.8763, high: 51.88, low: 51.85, close: 51.88, volume: 39590, }, { date: 1461679380000, open: 51.8799, high: 51.93, low: 51.875, close: 51.91, volume: 49591, }, { date: 1461679440000, open: 51.92, high: 51.94, low: 51.87, close: 51.885, volume: 58928, }, { date: 1461679500000, open: 51.8869, high: 51.91, low: 51.86, close: 51.8764, volume: 44726, }, { date: 1461679560000, open: 51.875, high: 51.89, low: 51.8699, close: 51.88, volume: 80941, }, { date: 1461679620000, open: 51.885, high: 51.885, low: 51.8475, close: 51.85, volume: 64140, }, { date: 1461679680000, open: 51.86, high: 51.86, low: 51.81, close: 51.8199, volume: 48610, }, { date: 1461679740000, open: 51.815, high: 51.83, low: 51.8, close: 51.815, volume: 92663, }, { date: 1461679800000, open: 51.82, high: 51.83, low: 51.785, close: 51.785, volume: 54840, }, { date: 1461679860000, open: 51.79, high: 51.79, low: 51.77, close: 51.785, volume: 116842, }, { date: 1461679920000, open: 51.785, high: 51.8, low: 51.77, close: 51.799, volume: 55250, }, { date: 1461679980000, open: 51.795, high: 51.8, low: 51.76, close: 51.7799, volume: 59317, }, { date: 1461680040000, open: 51.77, high: 51.775, low: 51.73, close: 51.74, volume: 113546, }, { date: 1461680100000, open: 51.745, high: 51.76, low: 51.73, close: 51.74, volume: 66761, }, { date: 1461680160000, open: 51.74, high: 51.75, low: 51.73, close: 51.73, volume: 44689, }, { date: 1461680220000, open: 51.735, high: 51.79, low: 51.735, close: 51.7875, volume: 91886, }, { date: 1461680280000, open: 51.785, high: 51.79, low: 51.73, close: 51.744, volume: 77044, }, { date: 1461680340000, open: 51.7463, high: 51.75, low: 51.72, close: 51.74, volume: 80114, }, { date: 1461680400000, open: 51.74, high: 51.749, low: 51.72, close: 51.72, volume: 26223, }, { date: 1461680460000, open: 51.72, high: 51.73, low: 51.7, close: 51.71, volume: 141478, }, { date: 1461680520000, open: 51.7162, high: 51.73, low: 51.71, close: 51.719, volume: 52825, }, { date: 1461680580000, open: 51.711, high: 51.72, low: 51.63, close: 51.63, volume: 128384, }, { date: 1461680640000, open: 51.63, high: 51.6699, low: 51.62, close: 51.655, volume: 74214, }, { date: 1461680700000, open: 51.66, high: 51.68, low: 51.65, close: 51.66, volume: 58192, }, { date: 1461680760000, open: 51.67, high: 51.6962, low: 51.66, close: 51.69, volume: 76746, }, { date: 1461680820000, open: 51.68, high: 51.68, low: 51.62, close: 51.65, volume: 77636, }, { date: 1461680880000, open: 51.65, high: 51.665, low: 51.64, close: 51.64, volume: 77174, }, { date: 1461680940000, open: 51.645, high: 51.65, low: 51.6105, close: 51.615, volume: 73646, }, { date: 1461681000000, open: 51.615, high: 51.62, low: 51.57, close: 51.59, volume: 105041, }, { date: 1461681060000, open: 51.58, high: 51.6, low: 51.56, close: 51.57, volume: 69467, }, { date: 1461681120000, open: 51.5799, high: 51.6, low: 51.57, close: 51.6, volume: 42348, }, { date: 1461681180000, open: 51.59, high: 51.61, low: 51.55, close: 51.61, volume: 170713, }, { date: 1461681240000, open: 51.62, high: 51.65, low: 51.61, close: 51.646, volume: 59629, }, { date: 1461681300000, open: 51.645, high: 51.65, low: 51.61, close: 51.62, volume: 90211, }, { date: 1461681360000, open: 51.615, high: 51.64, low: 51.61, close: 51.63, volume: 38537, }, { date: 1461681420000, open: 51.634, high: 51.635, low: 51.604, close: 51.6265, volume: 31776, }, { date: 1461681480000, open: 51.63, high: 51.635, low: 51.6, close: 51.605, volume: 35661, }, { date: 1461681540000, open: 51.6072, high: 51.61, low: 51.5755, close: 51.5755, volume: 49129, }, { date: 1461681600000, open: 51.5762, high: 51.5762, low: 51.51, close: 51.5138, volume: 94513, }, { date: 1461681660000, open: 51.52, high: 51.53, low: 51.505, close: 51.51, volume: 80093, }, { date: 1461681720000, open: 51.52, high: 51.58, low: 51.51, close: 51.58, volume: 76254, }, { date: 1461681780000, open: 51.58, high: 51.61, low: 51.57, close: 51.605, volume: 123141, }, { date: 1461681840000, open: 51.61, high: 51.63, low: 51.59, close: 51.6, volume: 55938, }, { date: 1461681900000, open: 51.595, high: 51.605, low: 51.58, close: 51.5899, volume: 33460, }, { date: 1461681960000, open: 51.585, high: 51.59, low: 51.555, close: 51.58, volume: 68566, }, { date: 1461682020000, open: 51.58, high: 51.58, low: 51.52, close: 51.53, volume: 42917, }, { date: 1461682080000, open: 51.521, high: 51.54, low: 51.505, close: 51.51, volume: 55024, }, { date: 1461682140000, open: 51.51, high: 51.51, low: 51.48, close: 51.48, volume: 250405, }, { date: 1461682200000, open: 51.48, high: 51.488, low: 51.42, close: 51.44, volume: 82841, }, { date: 1461682260000, open: 51.435, high: 51.44, low: 51.38, close: 51.405, volume: 115093, }, { date: 1461682320000, open: 51.4099, high: 51.43, low: 51.4, close: 51.4, volume: 85716, }, { date: 1461682380000, open: 51.41, high: 51.41, low: 51.36, close: 51.365, volume: 95856, }, { date: 1461682440000, open: 51.365, high: 51.41, low: 51.36, close: 51.405, volume: 141377, }, { date: 1461682500000, open: 51.4099, high: 51.43, low: 51.4, close: 51.4099, volume: 116039, }, { date: 1461682560000, open: 51.42, high: 51.46, low: 51.4, close: 51.46, volume: 97021, }, { date: 1461682620000, open: 51.455, high: 51.49, low: 51.4221, close: 51.49, volume: 54512, }, { date: 1461682680000, open: 51.48, high: 51.54, low: 51.48, close: 51.537, volume: 77517, }, { date: 1461682740000, open: 51.535, high: 51.535, low: 51.46, close: 51.495, volume: 67543, }, { date: 1461682800000, open: 51.49, high: 51.51, low: 51.475, close: 51.48, volume: 43737, }, { date: 1461682860000, open: 51.49, high: 51.54, low: 51.48, close: 51.5301, volume: 29428, }, { date: 1461682920000, open: 51.5371, high: 51.549, low: 51.47, close: 51.5, volume: 63751, }, { date: 1461682980000, open: 51.5, high: 51.52, low: 51.495, close: 51.51, volume: 31159, }, { date: 1461683040000, open: 51.52, high: 51.575, low: 51.52, close: 51.56, volume: 58288, }, { date: 1461683100000, open: 51.555, high: 51.57, low: 51.54, close: 51.56, volume: 38422, }, { date: 1461683160000, open: 51.56, high: 51.57, low: 51.51, close: 51.535, volume: 102909, }, { date: 1461683220000, open: 51.5336, high: 51.61, low: 51.5315, close: 51.61, volume: 63360, }, { date: 1461683280000, open: 51.61, high: 51.61, low: 51.495, close: 51.52, volume: 157634, }, { date: 1461683340000, open: 51.525, high: 51.57, low: 51.52, close: 51.565, volume: 26296, }, { date: 1461683400000, open: 51.5601, high: 51.579, low: 51.54, close: 51.55, volume: 25682, }, { date: 1461683460000, open: 51.5469, high: 51.585, low: 51.545, close: 51.585, volume: 31660, }, { date: 1461683520000, open: 51.59, high: 51.6, low: 51.565, close: 51.58, volume: 36007, }, { date: 1461683580000, open: 51.5861, high: 51.5861, low: 51.56, close: 51.5665, volume: 23907, }, { date: 1461683640000, open: 51.5635, high: 51.58, low: 51.54, close: 51.54, volume: 47242, }, { date: 1461683700000, open: 51.545, high: 51.545, low: 51.51, close: 51.52, volume: 42696, }, { date: 1461683760000, open: 51.51, high: 51.52, low: 51.47, close: 51.48, volume: 104260, }, { date: 1461683820000, open: 51.48, high: 51.4865, low: 51.45, close: 51.455, volume: 31009, }, { date: 1461683880000, open: 51.455, high: 51.46, low: 51.44, close: 51.44, volume: 36785, }, { date: 1461683940000, open: 51.449, high: 51.45, low: 51.44, close: 51.445, volume: 44972, }, { date: 1461684000000, open: 51.4435, high: 51.47, low: 51.44, close: 51.455, volume: 76408, }, { date: 1461684060000, open: 51.455, high: 51.49, low: 51.455, close: 51.485, volume: 104421, }, { date: 1461684120000, open: 51.485, high: 51.51, low: 51.48, close: 51.5, volume: 54802, }, { date: 1461684180000, open: 51.51, high: 51.52, low: 51.48, close: 51.48, volume: 55340, }, { date: 1461684240000, open: 51.485, high: 51.49, low: 51.45, close: 51.454, volume: 50757, }, { date: 1461684300000, open: 51.45, high: 51.47, low: 51.41, close: 51.41, volume: 262017, }, { date: 1461684360000, open: 51.415, high: 51.42, low: 51.4, close: 51.415, volume: 51651, }, { date: 1461684420000, open: 51.42, high: 51.43, low: 51.41, close: 51.425, volume: 143986, }, { date: 1461684480000, open: 51.425, high: 51.44, low: 51.42, close: 51.435, volume: 55472, }, { date: 1461684540000, open: 51.435, high: 51.44, low: 51.41, close: 51.4165, volume: 47710, }, { date: 1461684600000, open: 51.41, high: 51.43, low: 51.4, close: 51.42, volume: 53583, }, { date: 1461684660000, open: 51.43, high: 51.445, low: 51.41, close: 51.42, volume: 45049, }, { date: 1461684720000, open: 51.415, high: 51.43, low: 51.41, close: 51.42, volume: 45048, }, { date: 1461684780000, open: 51.4101, high: 51.42, low: 51.39, close: 51.3999, volume: 79320, }, { date: 1461684840000, open: 51.39, high: 51.3998, low: 51.35, close: 51.3601, volume: 92361, }, { date: 1461684900000, open: 51.365, high: 51.3674, low: 51.33, close: 51.345, volume: 103547, }, { date: 1461684960000, open: 51.35, high: 51.35, low: 51.32, close: 51.35, volume: 102378, }, { date: 1461685020000, open: 51.35, high: 51.4, low: 51.3467, close: 51.39, volume: 84090, }, { date: 1461685080000, open: 51.39, high: 51.41, low: 51.39, close: 51.41, volume: 59329, }, { date: 1461685140000, open: 51.4067, high: 51.41, low: 51.38, close: 51.38, volume: 37022, }, { date: 1461685200000, open: 51.3899, high: 51.4, low: 51.38, close: 51.39, volume: 42234, }, { date: 1461685260000, open: 51.39, high: 51.44, low: 51.38, close: 51.44, volume: 65274, }, { date: 1461685320000, open: 51.44, high: 51.46, low: 51.42, close: 51.4238, volume: 54913, }, { date: 1461685380000, open: 51.43, high: 51.44, low: 51.41, close: 51.42, volume: 25916, }, { date: 1461685440000, open: 51.42, high: 51.44, low: 51.4172, close: 51.435, volume: 24808, }, { date: 1461685500000, open: 51.435, high: 51.44, low: 51.4, close: 51.415, volume: 25958, }, { date: 1461685560000, open: 51.41, high: 51.41, low: 51.35, close: 51.36, volume: 62207, }, { date: 1461685620000, open: 51.37, high: 51.38, low: 51.3528, close: 51.38, volume: 41964, }, { date: 1461685680000, open: 51.375, high: 51.4, low: 51.37, close: 51.395, volume: 29580, }, { date: 1461685740000, open: 51.395, high: 51.395, low: 51.36, close: 51.38, volume: 56814, }, { date: 1461685800000, open: 51.38, high: 51.43, low: 51.37, close: 51.43, volume: 50857, }, { date: 1461685860000, open: 51.43, high: 51.47, low: 51.42, close: 51.465, volume: 48822, }, { date: 1461685920000, open: 51.4633, high: 51.465, low: 51.43, close: 51.445, volume: 51183, }, { date: 1461685980000, open: 51.44, high: 51.4499, low: 51.42, close: 51.4274, volume: 21516, }, { date: 1461686040000, open: 51.42, high: 51.44, low: 51.42, close: 51.44, volume: 24055, }, { date: 1461686100000, open: 51.44, high: 51.47, low: 51.435, close: 51.47, volume: 44433, }, { date: 1461686160000, open: 51.47, high: 51.49, low: 51.455, close: 51.46, volume: 38331, }, { date: 1461686220000, open: 51.455, high: 51.49, low: 51.45, close: 51.47, volume: 55572, }, { date: 1461686280000, open: 51.46, high: 51.47, low: 51.45, close: 51.45, volume: 26775, }, { date: 1461686340000, open: 51.455, high: 51.46, low: 51.44, close: 51.45, volume: 30079, }, { date: 1461686400000, open: 51.44, high: 51.45, low: 51.44, close: 51.445, volume: 24803, }, { date: 1461686460000, open: 51.445, high: 51.49, low: 51.44, close: 51.4801, volume: 58246, }, { date: 1461686520000, open: 51.48, high: 51.49, low: 51.47, close: 51.48, volume: 27284, }, { date: 1461686580000, open: 51.475, high: 51.5, low: 51.47, close: 51.495, volume: 34723, }, { date: 1461686640000, open: 51.4912, high: 51.54, low: 51.4912, close: 51.53, volume: 53092, }, { date: 1461686700000, open: 51.54, high: 51.54, low: 51.52, close: 51.525, volume: 20404, }, { date: 1461686760000, open: 51.5235, high: 51.53, low: 51.495, close: 51.502, volume: 95128, }, { date: 1461686820000, open: 51.5, high: 51.51, low: 51.5, close: 51.505, volume: 17875, }, { date: 1461686880000, open: 51.505, high: 51.51, low: 51.48, close: 51.49, volume: 40577, }, { date: 1461686940000, open: 51.4899, high: 51.51, low: 51.48, close: 51.5, volume: 27770, }, { date: 1461687000000, open: 51.505, high: 51.51, low: 51.5, close: 51.51, volume: 49680, }, { date: 1461687060000, open: 51.509, high: 51.52, low: 51.5, close: 51.52, volume: 43809, }, { date: 1461687120000, open: 51.52, high: 51.55, low: 51.515, close: 51.527, volume: 26857, }, { date: 1461687180000, open: 51.525, high: 51.53, low: 51.48, close: 51.48, volume: 42443, }, { date: 1461687240000, open: 51.485, high: 51.51, low: 51.48, close: 51.4999, volume: 31967, }, { date: 1461687300000, open: 51.4999, high: 51.5, low: 51.49, close: 51.495, volume: 12176, }, { date: 1461687360000, open: 51.4999, high: 51.5, low: 51.47, close: 51.47, volume: 24410, }, { date: 1461687420000, open: 51.48, high: 51.485, low: 51.45, close: 51.4699, volume: 40343, }, { date: 1461687480000, open: 51.465, high: 51.48, low: 51.45, close: 51.475, volume: 37158, }, { date: 1461687540000, open: 51.479, high: 51.5, low: 51.47, close: 51.49, volume: 50578, }, { date: 1461687600000, open: 51.49, high: 51.515, low: 51.485, close: 51.51, volume: 53423, }, { date: 1461687660000, open: 51.5115, high: 51.5115, low: 51.5, close: 51.505, volume: 8377, }, { date: 1461687720000, open: 51.51, high: 51.53, low: 51.49, close: 51.5, volume: 61460, }, { date: 1461687780000, open: 51.49, high: 51.505, low: 51.483, close: 51.485, volume: 60126, }, { date: 1461687840000, open: 51.485, high: 51.4864, low: 51.46, close: 51.48, volume: 50862, }, { date: 1461687900000, open: 51.4835, high: 51.49, low: 51.47, close: 51.4899, volume: 29306, }, { date: 1461687960000, open: 51.485, high: 51.49, low: 51.46, close: 51.466, volume: 46179, }, { date: 1461688020000, open: 51.4699, high: 51.47, low: 51.46, close: 51.4639, volume: 36140, }, { date: 1461688080000, open: 51.4612, high: 51.48, low: 51.46, close: 51.47, volume: 29775, }, { date: 1461688140000, open: 51.465, high: 51.47, low: 51.45, close: 51.45, volume: 16316, }, { date: 1461688200000, open: 51.46, high: 51.46, low: 51.45, close: 51.455, volume: 23567, }, { date: 1461688260000, open: 51.455, high: 51.46, low: 51.45, close: 51.45, volume: 15973, }, { date: 1461688320000, open: 51.455, high: 51.46, low: 51.43, close: 51.44, volume: 55523, }, { date: 1461688380000, open: 51.435, high: 51.44, low: 51.415, close: 51.425, volume: 35326, }, { date: 1461688440000, open: 51.4267, high: 51.44, low: 51.41, close: 51.42, volume: 48857, }, { date: 1461688500000, open: 51.41, high: 51.42, low: 51.41, close: 51.42, volume: 12885, }, { date: 1461688560000, open: 51.415, high: 51.42, low: 51.4, close: 51.42, volume: 37845, }, { date: 1461688620000, open: 51.415, high: 51.42, low: 51.38, close: 51.385, volume: 55487, }, { date: 1461688680000, open: 51.385, high: 51.3899, low: 51.35, close: 51.35, volume: 31888, }, { date: 1461688740000, open: 51.35, high: 51.37, low: 51.35, close: 51.35, volume: 54996, }, { date: 1461688800000, open: 51.355, high: 51.38, low: 51.35, close: 51.3755, volume: 74038, }, { date: 1461688860000, open: 51.3716, high: 51.4, low: 51.37, close: 51.39, volume: 53750, }, { date: 1461688920000, open: 51.39, high: 51.4, low: 51.38, close: 51.3901, volume: 20274, }, { date: 1461688980000, open: 51.3961, high: 51.4, low: 51.39, close: 51.4, volume: 25127, }, { date: 1461689040000, open: 51.399, high: 51.42, low: 51.39, close: 51.42, volume: 30873, }, { date: 1461689100000, open: 51.415, high: 51.42, low: 51.38, close: 51.38, volume: 64417, }, { date: 1461689160000, open: 51.384, high: 51.41, low: 51.384, close: 51.4, volume: 18695, }, { date: 1461689220000, open: 51.4088, high: 51.41, low: 51.38, close: 51.3898, volume: 103745, }, { date: 1461689280000, open: 51.3844, high: 51.41, low: 51.3844, close: 51.4, volume: 55625, }, { date: 1461689340000, open: 51.4, high: 51.405, low: 51.39, close: 51.39, volume: 63058, }, { date: 1461689400000, open: 51.3901, high: 51.4, low: 51.39, close: 51.395, volume: 26496, }, { date: 1461689460000, open: 51.4, high: 51.43, low: 51.38, close: 51.385, volume: 155744, }, { date: 1461689520000, open: 51.385, high: 51.409, low: 51.37, close: 51.405, volume: 59165, }, { date: 1461689580000, open: 51.405, high: 51.41, low: 51.4, close: 51.41, volume: 11822, }, { date: 1461689640000, open: 51.4, high: 51.4099, low: 51.39, close: 51.39, volume: 41844, }, { date: 1461689700000, open: 51.395, high: 51.3999, low: 51.39, close: 51.39, volume: 9987, }, { date: 1461689760000, open: 51.39, high: 51.4, low: 51.36, close: 51.365, volume: 72272, }, { date: 1461689820000, open: 51.36, high: 51.37, low: 51.35, close: 51.365, volume: 97996, }, { date: 1461689880000, open: 51.365, high: 51.39, low: 51.35, close: 51.37, volume: 91379, }, { date: 1461689940000, open: 51.37, high: 51.38, low: 51.36, close: 51.365, volume: 17633, }, { date: 1461690000000, open: 51.365, high: 51.37, low: 51.31, close: 51.315, volume: 118326, }, { date: 1461690060000, open: 51.31, high: 51.34, low: 51.31, close: 51.33, volume: 63666, }, { date: 1461690120000, open: 51.325, high: 51.33, low: 51.31, close: 51.31, volume: 25720, }, { date: 1461690180000, open: 51.31, high: 51.32, low: 51.29, close: 51.3001, volume: 67821, }, { date: 1461690240000, open: 51.3, high: 51.32, low: 51.29, close: 51.29, volume: 101723, }, { date: 1461690300000, open: 51.3, high: 51.3, low: 51.26, close: 51.265, volume: 104192, }, { date: 1461690360000, open: 51.2699, high: 51.28, low: 51.25, close: 51.2665, volume: 91124, }, { date: 1461690420000, open: 51.26, high: 51.28, low: 51.25, close: 51.26, volume: 43026, }, { date: 1461690480000, open: 51.26, high: 51.27, low: 51.25, close: 51.255, volume: 19380, }, { date: 1461690540000, open: 51.255, high: 51.27, low: 51.25, close: 51.27, volume: 42793, }, { date: 1461690600000, open: 51.2701, high: 51.3, low: 51.26, close: 51.291, volume: 51508, }, { date: 1461690660000, open: 51.3, high: 51.3, low: 51.28, close: 51.28, volume: 14956, }, { date: 1461690720000, open: 51.28, high: 51.285, low: 51.25, close: 51.26, volume: 64228, }, { date: 1461690780000, open: 51.255, high: 51.265, low: 51.24, close: 51.24, volume: 124828, }, { date: 1461690840000, open: 51.245, high: 51.26, low: 51.24, close: 51.25, volume: 42980, }, { date: 1461690900000, open: 51.24, high: 51.25, low: 51.22, close: 51.23, volume: 67033, }, { date: 1461690960000, open: 51.22, high: 51.235, low: 51.22, close: 51.23, volume: 23187, }, { date: 1461691020000, open: 51.225, high: 51.25, low: 51.225, close: 51.2423, volume: 30272, }, { date: 1461691080000, open: 51.2474, high: 51.275, low: 51.24, close: 51.275, volume: 71152, }, { date: 1461691140000, open: 51.27, high: 51.28, low: 51.25, close: 51.25, volume: 36039, }, { date: 1461691200000, open: 51.255, high: 51.26, low: 51.24, close: 51.245, volume: 26090, }, { date: 1461691260000, open: 51.2499, high: 51.33, low: 51.24, close: 51.33, volume: 169187, }, { date: 1461691320000, open: 51.325, high: 51.35, low: 51.31, close: 51.335, volume: 76173, }, { date: 1461691380000, open: 51.335, high: 51.34, low: 51.33, close: 51.34, volume: 8174, }, { date: 1461691440000, open: 51.335, high: 51.34, low: 51.32, close: 51.325, volume: 29302, }, { date: 1461691500000, open: 51.33, high: 51.35, low: 51.325, close: 51.345, volume: 33042, }, { date: 1461691560000, open: 51.345, high: 51.3465, low: 51.31, close: 51.32, volume: 52948, }, { date: 1461691620000, open: 51.3235, high: 51.34, low: 51.32, close: 51.3259, volume: 23122, }, { date: 1461691680000, open: 51.32, high: 51.33, low: 51.32, close: 51.325, volume: 16873, }, { date: 1461691740000, open: 51.33, high: 51.33, low: 51.32, close: 51.33, volume: 12906, }, { date: 1461691800000, open: 51.325, high: 51.35, low: 51.32, close: 51.35, volume: 51922, }, { date: 1461691860000, open: 51.34, high: 51.36, low: 51.34, close: 51.344, volume: 42999, }, { date: 1461691920000, open: 51.35, high: 51.365, low: 51.34, close: 51.355, volume: 73149, }, { date: 1461691980000, open: 51.35, high: 51.36, low: 51.35, close: 51.3501, volume: 19223, }, { date: 1461692040000, open: 51.36, high: 51.41, low: 51.355, close: 51.3902, volume: 66688, }, { date: 1461692100000, open: 51.396, high: 51.4, low: 51.34, close: 51.345, volume: 39025, }, { date: 1461692160000, open: 51.34, high: 51.35, low: 51.33, close: 51.33, volume: 30643, }, { date: 1461692220000, open: 51.3301, high: 51.35, low: 51.33, close: 51.3499, volume: 26575, }, { date: 1461692280000, open: 51.35, high: 51.35, low: 51.34, close: 51.35, volume: 19434, }, { date: 1461692340000, open: 51.35, high: 51.35, low: 51.34, close: 51.35, volume: 19485, }, { date: 1461692400000, open: 51.3497, high: 51.385, low: 51.345, close: 51.37, volume: 59474, }, { date: 1461692460000, open: 51.365, high: 51.38, low: 51.3401, close: 51.3401, volume: 49698, }, { date: 1461692520000, open: 51.3499, high: 51.36, low: 51.27, close: 51.275, volume: 126510, }, { date: 1461692580000, open: 51.2799, high: 51.3, low: 51.275, close: 51.2899, volume: 27153, }, { date: 1461692640000, open: 51.28, high: 51.29, low: 51.255, close: 51.26, volume: 138312, }, { date: 1461692700000, open: 51.26, high: 51.3, low: 51.255, close: 51.26, volume: 80268, }, { date: 1461692760000, open: 51.265, high: 51.3, low: 51.265, close: 51.2999, volume: 33419, }, { date: 1461692820000, open: 51.292, high: 51.295, low: 51.2801, close: 51.2801, volume: 17175, }, { date: 1461692880000, open: 51.29, high: 51.3, low: 51.28, close: 51.2999, volume: 28855, }, { date: 1461692940000, open: 51.295, high: 51.295, low: 51.26, close: 51.275, volume: 64582, }, { date: 1461693000000, open: 51.2712, high: 51.28, low: 51.27, close: 51.275, volume: 18615, }, { date: 1461693060000, open: 51.275, high: 51.28, low: 51.25, close: 51.26, volume: 36116, }, { date: 1461693120000, open: 51.255, high: 51.28, low: 51.255, close: 51.275, volume: 26105, }, { date: 1461693180000, open: 51.2701, high: 51.29, low: 51.2549, close: 51.2899, volume: 68387, }, { date: 1461693240000, open: 51.29, high: 51.3, low: 51.265, close: 51.265, volume: 76742, }, { date: 1461693300000, open: 51.27, high: 51.29, low: 51.26, close: 51.275, volume: 42525, }, { date: 1461693360000, open: 51.279, high: 51.29, low: 51.26, close: 51.27, volume: 58889, }, { date: 1461693420000, open: 51.27, high: 51.28, low: 51.25, close: 51.25, volume: 81920, }, { date: 1461693480000, open: 51.26, high: 51.26, low: 51.25, close: 51.255, volume: 21222, }, { date: 1461693540000, open: 51.255, high: 51.265, low: 51.225, close: 51.235, volume: 149244, }, { date: 1461693600000, open: 51.2315, high: 51.24, low: 51.22, close: 51.235, volume: 69064, }, { date: 1461693660000, open: 51.237, high: 51.24, low: 51.22, close: 51.225, volume: 58582, }, { date: 1461693720000, open: 51.2232, high: 51.245, low: 51.22, close: 51.225, volume: 58339, }, { date: 1461693780000, open: 51.23, high: 51.24, low: 51.22, close: 51.24, volume: 57332, }, { date: 1461693840000, open: 51.24, high: 51.24, low: 51.23, close: 51.23, volume: 35436, }, { date: 1461693900000, open: 51.23, high: 51.2375, low: 51.21, close: 51.215, volume: 38542, }, { date: 1461693960000, open: 51.22, high: 51.22, low: 51.19, close: 51.1931, volume: 72699, }, { date: 1461694020000, open: 51.19, high: 51.2, low: 51.19, close: 51.19, volume: 133086, }, { date: 1461694080000, open: 51.195, high: 51.2, low: 51.13, close: 51.135, volume: 94408, }, { date: 1461694140000, open: 51.13, high: 51.14, low: 51.1, close: 51.128, volume: 177851, }, { date: 1461694200000, open: 51.13, high: 51.135, low: 51.1, close: 51.105, volume: 112702, }, { date: 1461694260000, open: 51.1099, high: 51.12, low: 51.09, close: 51.1199, volume: 136583, }, { date: 1461694320000, open: 51.12, high: 51.14, low: 51.11, close: 51.135, volume: 83169, }, { date: 1461694380000, open: 51.14, high: 51.14, low: 51.12, close: 51.131, volume: 100262, }, { date: 1461694440000, open: 51.135, high: 51.145, low: 51.1301, close: 51.145, volume: 45608, }, { date: 1461694500000, open: 51.145, high: 51.17, low: 51.135, close: 51.165, volume: 84293, }, { date: 1461694560000, open: 51.165, high: 51.21, low: 51.165, close: 51.201, volume: 66062, }, { date: 1461694620000, open: 51.205, high: 51.225, low: 51.19, close: 51.205, volume: 93173, }, { date: 1461694680000, open: 51.21, high: 51.225, low: 51.2001, close: 51.22, volume: 67432, }, { date: 1461694740000, open: 51.22, high: 51.27, low: 51.21, close: 51.22, volume: 106052, }, { date: 1461694800000, open: 51.22, high: 51.24, low: 51.21, close: 51.2101, volume: 174594, }, { date: 1461694860000, open: 51.22, high: 51.22, low: 51.195, close: 51.195, volume: 50657, }, { date: 1461694920000, open: 51.2, high: 51.2, low: 51.18, close: 51.1801, volume: 56105, }, { date: 1461694980000, open: 51.19, high: 51.19, low: 51.1601, close: 51.18, volume: 41722, }, { date: 1461695040000, open: 51.17, high: 51.225, low: 51.17, close: 51.225, volume: 54861, }, { date: 1461695100000, open: 51.22, high: 51.22, low: 51.2, close: 51.2165, volume: 38914, }, { date: 1461695160000, open: 51.215, high: 51.23, low: 51.21, close: 51.23, volume: 30223, }, { date: 1461695220000, open: 51.23, high: 51.27, low: 51.225, close: 51.27, volume: 77630, }, { date: 1461695280000, open: 51.27, high: 51.27, low: 51.24, close: 51.25, volume: 65995, }, { date: 1461695340000, open: 51.245, high: 51.245, low: 51.19, close: 51.195, volume: 67261, }, { date: 1461695400000, open: 51.195, high: 51.21, low: 51.15, close: 51.16, volume: 103488, }, { date: 1461695460000, open: 51.1501, high: 51.18, low: 51.14, close: 51.1784, volume: 69263, }, { date: 1461695520000, open: 51.175, high: 51.19, low: 51.17, close: 51.175, volume: 52015, }, { date: 1461695580000, open: 51.18, high: 51.1871, low: 51.17, close: 51.175, volume: 24689, }, { date: 1461695640000, open: 51.171, high: 51.21, low: 51.171, close: 51.205, volume: 81524, }, { date: 1461695700000, open: 51.2015, high: 51.21, low: 51.19, close: 51.205, volume: 58260, }, { date: 1461695760000, open: 51.2074, high: 51.21, low: 51.19, close: 51.2, volume: 73273, }, { date: 1461695820000, open: 51.2, high: 51.23, low: 51.2, close: 51.225, volume: 60278, }, { date: 1461695880000, open: 51.225, high: 51.23, low: 51.21, close: 51.22, volume: 57308, }, { date: 1461695940000, open: 51.225, high: 51.23, low: 51.2101, close: 51.2238, volume: 40786, }, { date: 1461696000000, open: 51.225, high: 51.245, low: 51.22, close: 51.235, volume: 57466, }, { date: 1461696060000, open: 51.235, high: 51.24, low: 51.23, close: 51.24, volume: 22579, }, { date: 1461696120000, open: 51.235, high: 51.24, low: 51.23, close: 51.235, volume: 25375, }, { date: 1461696180000, open: 51.24, high: 51.25, low: 51.235, close: 51.245, volume: 49874, }, { date: 1461696240000, open: 51.25, high: 51.2575, low: 51.23, close: 51.235, volume: 125767, }, { date: 1461696300000, open: 51.235, high: 51.24, low: 51.21, close: 51.215, volume: 34525, }, { date: 1461696360000, open: 51.215, high: 51.23, low: 51.21, close: 51.225, volume: 48440, }, { date: 1461696420000, open: 51.225, high: 51.25, low: 51.2201, close: 51.225, volume: 108924, }, { date: 1461696480000, open: 51.225, high: 51.23, low: 51.22, close: 51.225, volume: 19035, }, { date: 1461696540000, open: 51.225, high: 51.25, low: 51.22, close: 51.23, volume: 77256, }, { date: 1461696600000, open: 51.235, high: 51.26, low: 51.2301, close: 51.2365, volume: 90845, }, { date: 1461696660000, open: 51.235, high: 51.24, low: 51.21, close: 51.21, volume: 61294, }, { date: 1461696720000, open: 51.215, high: 51.22, low: 51.19, close: 51.19, volume: 63690, }, { date: 1461696780000, open: 51.1999, high: 51.205, low: 51.18, close: 51.2, volume: 50132, }, { date: 1461696840000, open: 51.2, high: 51.22, low: 51.2, close: 51.2001, volume: 46719, }, { date: 1461696900000, open: 51.205, high: 51.21, low: 51.19, close: 51.2, volume: 38890, }, { date: 1461696960000, open: 51.205, high: 51.21, low: 51.2, close: 51.2065, volume: 23843, }, { date: 1461697020000, open: 51.205, high: 51.21, low: 51.19, close: 51.195, volume: 67367, }, { date: 1461697080000, open: 51.1999, high: 51.1999, low: 51.15, close: 51.18, volume: 96229, }, { date: 1461697140000, open: 51.185, high: 51.2, low: 51.18, close: 51.2, volume: 28338, }, { date: 1461697200000, open: 51.195, high: 51.2, low: 51.18, close: 51.185, volume: 33740, }, { date: 1461697260000, open: 51.18, high: 51.22, low: 51.18, close: 51.195, volume: 124582, }, { date: 1461697320000, open: 51.197, high: 51.24, low: 51.197, close: 51.2201, volume: 81428, }, { date: 1461697380000, open: 51.22, high: 51.24, low: 51.2, close: 51.205, volume: 107658, }, { date: 1461697440000, open: 51.205, high: 51.22, low: 51.2001, close: 51.215, volume: 40912, }, { date: 1461697500000, open: 51.215, high: 51.26, low: 51.215, close: 51.255, volume: 194425, }, { date: 1461697560000, open: 51.26, high: 51.26, low: 51.25, close: 51.255, volume: 33390, }, { date: 1461697620000, open: 51.255, high: 51.265, low: 51.2501, close: 51.26, volume: 53701, }, { date: 1461697680000, open: 51.26, high: 51.29, low: 51.25, close: 51.29, volume: 243703, }, { date: 1461697740000, open: 51.29, high: 51.315, low: 51.28, close: 51.3, volume: 62867, }, { date: 1461697800000, open: 51.3, high: 51.31, low: 51.29, close: 51.2901, volume: 147220, }, { date: 1461697860000, open: 51.295, high: 51.3099, low: 51.26, close: 51.26, volume: 136632, }, { date: 1461697920000, open: 51.27, high: 51.27, low: 51.26, close: 51.26, volume: 31462, }, { date: 1461697980000, open: 51.26, high: 51.27, low: 51.25, close: 51.25, volume: 42617, }, { date: 1461698040000, open: 51.25, high: 51.26, low: 51.25, close: 51.255, volume: 25352, }, { date: 1461698100000, open: 51.25, high: 51.26, low: 51.25, close: 51.26, volume: 205260, }, { date: 1461698160000, open: 51.26, high: 51.265, low: 51.25, close: 51.255, volume: 94549, }, { date: 1461698220000, open: 51.255, high: 51.26, low: 51.22, close: 51.2299, volume: 93539, }, { date: 1461698280000, open: 51.23, high: 51.26, low: 51.22, close: 51.235, volume: 97214, }, { date: 1461698340000, open: 51.23, high: 51.24, low: 51.21, close: 51.235, volume: 60386, }, { date: 1461698400000, open: 51.23, high: 51.245, low: 51.21, close: 51.215, volume: 99774, }, { date: 1461698460000, open: 51.215, high: 51.24, low: 51.2, close: 51.23, volume: 70136, }, { date: 1461698520000, open: 51.24, high: 51.27, low: 51.23, close: 51.25, volume: 90773, }, { date: 1461698580000, open: 51.25, high: 51.26, low: 51.21, close: 51.21, volume: 40651, }, { date: 1461698640000, open: 51.2165, high: 51.24, low: 51.21, close: 51.215, volume: 103463, }, { date: 1461698700000, open: 51.21, high: 51.21, low: 51.16, close: 51.1965, volume: 78583, }, { date: 1461698760000, open: 51.19, high: 51.21, low: 51.18, close: 51.185, volume: 83558, }, { date: 1461698820000, open: 51.185, high: 51.19, low: 51.17, close: 51.18, volume: 93121, }, { date: 1461698880000, open: 51.18, high: 51.19, low: 51.16, close: 51.185, volume: 87356, }, { date: 1461698940000, open: 51.185, high: 51.2, low: 51.17, close: 51.1701, volume: 63671, }, { date: 1461699000000, open: 51.17, high: 51.19, low: 51.16, close: 51.18, volume: 112298, }, { date: 1461699060000, open: 51.18, high: 51.2, low: 51.17, close: 51.1701, volume: 82741, }, { date: 1461699120000, open: 51.17, high: 51.2, low: 51.16, close: 51.2, volume: 126850, }, { date: 1461699180000, open: 51.194, high: 51.26, low: 51.18, close: 51.221, volume: 173750, }, { date: 1461699240000, open: 51.2266, high: 51.25, low: 51.225, close: 51.245, volume: 110146, }, { date: 1461699300000, open: 51.25, high: 51.25, low: 51.2, close: 51.2066, volume: 91103, }, { date: 1461699360000, open: 51.2099, high: 51.23, low: 51.2, close: 51.23, volume: 72088, }, { date: 1461699420000, open: 51.23, high: 51.29, low: 51.23, close: 51.255, volume: 104966, }, { date: 1461699480000, open: 51.255, high: 51.28, low: 51.25, close: 51.275, volume: 46451, }, { date: 1461699540000, open: 51.279, high: 51.3, low: 51.27, close: 51.29, volume: 63624, }, { date: 1461699600000, open: 51.299, high: 51.3, low: 51.285, close: 51.3, volume: 235141, }, { date: 1461699660000, open: 51.295, high: 51.33, low: 51.29, close: 51.33, volume: 229261, }, { date: 1461699720000, open: 51.33, high: 51.35, low: 51.32, close: 51.345, volume: 114393, }, { date: 1461699780000, open: 51.35, high: 51.37, low: 51.335, close: 51.342, volume: 173830, }, { date: 1461699840000, open: 51.34, high: 51.37, low: 51.32, close: 51.32, volume: 114021, }, { date: 1461699900000, open: 51.32, high: 51.36, low: 51.315, close: 51.355, volume: 94187, }, { date: 1461699960000, open: 51.355, high: 51.39, low: 51.35, close: 51.39, volume: 253167, }, { date: 1461700020000, open: 51.39, high: 51.39, low: 51.3325, close: 51.341, volume: 100681, }, { date: 1461700080000, open: 51.3431, high: 51.37, low: 51.3431, close: 51.36, volume: 83335, }, { date: 1461700140000, open: 51.37, high: 51.37, low: 51.34, close: 51.355, volume: 84954, }, { date: 1461700200000, open: 51.355, high: 51.41, low: 51.3501, close: 51.38, volume: 149678, }, { date: 1461700260000, open: 51.38, high: 51.4, low: 51.34, close: 51.345, volume: 144252, }, { date: 1461700320000, open: 51.345, high: 51.38, low: 51.34, close: 51.365, volume: 147299, }, { date: 1461700380000, open: 51.36, high: 51.38, low: 51.36, close: 51.3664, volume: 116945, }, { date: 1461700440000, open: 51.365, high: 51.38, low: 51.36, close: 51.37, volume: 133964, }, { date: 1461700500000, open: 51.365, high: 51.37, low: 51.36, close: 51.36, volume: 82837, }, { date: 1461700560000, open: 51.36, high: 51.37, low: 51.34, close: 51.355, volume: 167073, }, { date: 1461700620000, open: 51.355, high: 51.36, low: 51.35, close: 51.36, volume: 145386, }, { date: 1461700680000, open: 51.355, high: 51.38, low: 51.35, close: 51.37, volume: 281815, }, { date: 1461700740000, open: 51.365, high: 51.38, low: 51.36, close: 51.38, volume: 262208, }, { date: 1461700800000, open: 51.375, high: 51.44, low: 51.37, close: 51.44, volume: 2950673, }, { date: 1461763800000, open: 51.48, high: 51.495, low: 51.48, close: 51.49, volume: 546773, }, { date: 1461763860000, open: 51.495, high: 51.5, low: 51.38, close: 51.39, volume: 163829, }, { date: 1461763920000, open: 51.4, high: 51.4, low: 51.2699, close: 51.28, volume: 134670, }, { date: 1461763980000, open: 51.28, high: 51.29, low: 51.245, close: 51.28, volume: 151479, }, { date: 1461764040000, open: 51.274, high: 51.34, low: 51.26, close: 51.34, volume: 108716, }, { date: 1461764100000, open: 51.33, high: 51.4, low: 51.33, close: 51.37, volume: 68384, }, { date: 1461764160000, open: 51.37, high: 51.37, low: 51.34, close: 51.353, volume: 86360, }, { date: 1461764220000, open: 51.36, high: 51.37, low: 51.32, close: 51.33, volume: 152664, }, { date: 1461764280000, open: 51.33, high: 51.3711, low: 51.33, close: 51.35, volume: 118469, }, { date: 1461764340000, open: 51.355, high: 51.3799, low: 51.3499, close: 51.36, volume: 260691, }, { date: 1461764400000, open: 51.3601, high: 51.365, low: 51.31, close: 51.33, volume: 77674, }, { date: 1461764460000, open: 51.34, high: 51.34, low: 51.28, close: 51.31, volume: 91033, }, { date: 1461764520000, open: 51.31, high: 51.365, low: 51.3, close: 51.35, volume: 203975, }, { date: 1461764580000, open: 51.35, high: 51.36, low: 51.24, close: 51.245, volume: 164021, }, { date: 1461764640000, open: 51.25, high: 51.27, low: 51.22, close: 51.27, volume: 195282, }, { date: 1461764700000, open: 51.2665, high: 51.27, low: 51.16, close: 51.17, volume: 103043, }, { date: 1461764760000, open: 51.165, high: 51.2, low: 51.1601, close: 51.195, volume: 112338, }, { date: 1461764820000, open: 51.195, high: 51.2, low: 51.12, close: 51.13, volume: 122031, }, { date: 1461764880000, open: 51.13, high: 51.14, low: 51.12, close: 51.13, volume: 84016, }, { date: 1461764940000, open: 51.125, high: 51.15, low: 51.11, close: 51.115, volume: 143612, }, { date: 1461765000000, open: 51.115, high: 51.115, low: 51.07, close: 51.09, volume: 253743, }, { date: 1461765060000, open: 51.09, high: 51.16, low: 51.07, close: 51.1482, volume: 159653, }, { date: 1461765120000, open: 51.15, high: 51.195, low: 51.12, close: 51.13, volume: 108530, }, { date: 1461765180000, open: 51.13, high: 51.14, low: 51.1, close: 51.125, volume: 68276, }, { date: 1461765240000, open: 51.13, high: 51.13, low: 51.0899, close: 51.09, volume: 86166, }, { date: 1461765300000, open: 51.0936, high: 51.1, low: 51.075, close: 51.1, volume: 47248, }, { date: 1461765360000, open: 51.1, high: 51.12, low: 51.09, close: 51.1166, volume: 101507, }, { date: 1461765420000, open: 51.11, high: 51.11, low: 51.09, close: 51.11, volume: 94287, }, { date: 1461765480000, open: 51.105, high: 51.17, low: 51.105, close: 51.16, volume: 170212, }, { date: 1461765540000, open: 51.165, high: 51.165, low: 51.11, close: 51.12, volume: 146729, }, { date: 1461765600000, open: 51.12, high: 51.12, low: 51.09, close: 51.105, volume: 82802, }, { date: 1461765660000, open: 51.1, high: 51.135, low: 51.09, close: 51.115, volume: 177633, }, { date: 1461765720000, open: 51.11, high: 51.13, low: 51.11, close: 51.125, volume: 81520, }, { date: 1461765780000, open: 51.125, high: 51.14, low: 51.115, close: 51.135, volume: 155634, }, { date: 1461765840000, open: 51.135, high: 51.14, low: 51.13, close: 51.14, volume: 147597, }, { date: 1461765900000, open: 51.135, high: 51.14, low: 51.11, close: 51.135, volume: 122213, }, { date: 1461765960000, open: 51.13, high: 51.14, low: 51.12, close: 51.125, volume: 79935, }, { date: 1461766020000, open: 51.125, high: 51.13, low: 51.1, close: 51.1, volume: 119890, }, { date: 1461766080000, open: 51.1, high: 51.11, low: 51.08, close: 51.09, volume: 158341, }, { date: 1461766140000, open: 51.09, high: 51.11, low: 51.09, close: 51.1099, volume: 53404, }, { date: 1461766200000, open: 51.105, high: 51.11, low: 51.09, close: 51.09, volume: 93424, }, { date: 1461766260000, open: 51.095, high: 51.13, low: 51.095, close: 51.115, volume: 95180, }, { date: 1461766320000, open: 51.115, high: 51.12, low: 51.085, close: 51.085, volume: 40666, }, { date: 1461766380000, open: 51.08, high: 51.15, low: 51.08, close: 51.0964, volume: 147959, }, { date: 1461766440000, open: 51.1, high: 51.1, low: 51.03, close: 51.043, volume: 141433, }, { date: 1461766500000, open: 51.05, high: 51.08, low: 51.04, close: 51.06, volume: 121861, }, { date: 1461766560000, open: 51.05, high: 51.06, low: 51, close: 51.005, volume: 248215, }, { date: 1461766620000, open: 51.005, high: 51.01, low: 50.9675, close: 51, volume: 330780, }, { date: 1461766680000, open: 51, high: 51.01, low: 50.99, close: 50.99, volume: 124089, }, { date: 1461766740000, open: 50.99, high: 51, low: 50.94, close: 50.9735, volume: 65285, }, { date: 1461766800000, open: 50.975, high: 50.975, low: 50.92, close: 50.9201, volume: 92963, }, { date: 1461766860000, open: 50.925, high: 50.94, low: 50.89, close: 50.905, volume: 132362, }, { date: 1461766920000, open: 50.905, high: 50.92, low: 50.86, close: 50.8601, volume: 137104, }, { date: 1461766980000, open: 50.87, high: 50.9, low: 50.865, close: 50.87, volume: 55850, }, { date: 1461767040000, open: 50.87, high: 50.945, low: 50.87, close: 50.9364, volume: 58460, }, { date: 1461767100000, open: 50.935, high: 50.94, low: 50.8835, close: 50.9, volume: 76176, }, { date: 1461767160000, open: 50.895, high: 50.9, low: 50.86, close: 50.87, volume: 72037, }, { date: 1461767220000, open: 50.87, high: 50.875, low: 50.81, close: 50.83, volume: 187158, }, { date: 1461767280000, open: 50.84, high: 50.845, low: 50.77, close: 50.795, volume: 166608, }, { date: 1461767340000, open: 50.795, high: 50.805, low: 50.78, close: 50.795, volume: 97773, }, { date: 1461767400000, open: 50.795, high: 50.805, low: 50.72, close: 50.7394, volume: 360640, }, { date: 1461767460000, open: 50.73, high: 50.81, low: 50.68, close: 50.8001, volume: 229563, }, { date: 1461767520000, open: 50.805, high: 50.89, low: 50.8, close: 50.865, volume: 159664, }, { date: 1461767580000, open: 50.865, high: 50.9, low: 50.86, close: 50.88, volume: 95535, }, { date: 1461767640000, open: 50.88, high: 50.935, low: 50.88, close: 50.915, volume: 85221, }, { date: 1461767700000, open: 50.915, high: 50.95, low: 50.895, close: 50.92, volume: 250412, }, { date: 1461767760000, open: 50.915, high: 50.96, low: 50.911, close: 50.92, volume: 61927, }, { date: 1461767820000, open: 50.925, high: 50.93, low: 50.88, close: 50.89, volume: 100097, }, { date: 1461767880000, open: 50.9, high: 50.9, low: 50.84, close: 50.85, volume: 103058, }, { date: 1461767940000, open: 50.85, high: 50.86, low: 50.78, close: 50.8, volume: 111618, }, { date: 1461768000000, open: 50.795, high: 50.81, low: 50.76, close: 50.8, volume: 335634, }, { date: 1461768060000, open: 50.81, high: 50.835, low: 50.75, close: 50.7565, volume: 121757, }, { date: 1461768120000, open: 50.754, high: 50.78, low: 50.745, close: 50.7601, volume: 84462, }, { date: 1461768180000, open: 50.765, high: 50.78, low: 50.71, close: 50.71, volume: 97206, }, { date: 1461768240000, open: 50.71, high: 50.72, low: 50.7, close: 50.7135, volume: 65100, }, { date: 1461768300000, open: 50.715, high: 50.72, low: 50.63, close: 50.63, volume: 240490, }, { date: 1461768360000, open: 50.63, high: 50.635, low: 50.59, close: 50.605, volume: 227340, }, { date: 1461768420000, open: 50.6, high: 50.6283, low: 50.57, close: 50.6, volume: 206653, }, { date: 1461768480000, open: 50.59, high: 50.6, low: 50.55, close: 50.595, volume: 141892, }, { date: 1461768540000, open: 50.595, high: 50.68, low: 50.59, close: 50.67, volume: 159688, }, { date: 1461768600000, open: 50.675, high: 50.7, low: 50.66, close: 50.6975, volume: 70725, }, { date: 1461768660000, open: 50.69, high: 50.8, low: 50.69, close: 50.8, volume: 149106, }, { date: 1461768720000, open: 50.8, high: 50.8, low: 50.765, close: 50.77, volume: 87794, }, { date: 1461768780000, open: 50.7732, high: 50.79, low: 50.76, close: 50.77, volume: 55952, }, { date: 1461768840000, open: 50.78, high: 50.78, low: 50.77, close: 50.78, volume: 42947, }, { date: 1461768900000, open: 50.78, high: 50.8, low: 50.77, close: 50.795, volume: 62669, }, { date: 1461768960000, open: 50.795, high: 50.8, low: 50.75, close: 50.76, volume: 81524, }, { date: 1461769020000, open: 50.76, high: 50.8, low: 50.71, close: 50.8, volume: 63531, }, { date: 1461769080000, open: 50.7999, high: 50.85, low: 50.79, close: 50.849, volume: 81082, }, { date: 1461769140000, open: 50.845, high: 50.85, low: 50.82, close: 50.825, volume: 51949, }, { date: 1461769200000, open: 50.83, high: 50.83, low: 50.79, close: 50.805, volume: 77604, }, { date: 1461769260000, open: 50.805, high: 50.81, low: 50.8, close: 50.805, volume: 122570, }, { date: 1461769320000, open: 50.8, high: 50.85, low: 50.8, close: 50.825, volume: 190433, }, { date: 1461769380000, open: 50.82, high: 50.83, low: 50.77, close: 50.8, volume: 84802, }, { date: 1461769440000, open: 50.8, high: 50.81, low: 50.79, close: 50.8, volume: 46270, }, { date: 1461769500000, open: 50.81, high: 50.82, low: 50.8, close: 50.81, volume: 61957, }, { date: 1461769560000, open: 50.82, high: 50.82, low: 50.76, close: 50.76, volume: 67023, }, { date: 1461769620000, open: 50.7699, high: 50.7699, low: 50.69, close: 50.71, volume: 65532, }, { date: 1461769680000, open: 50.7099, high: 50.77, low: 50.703, close: 50.7301, volume: 84168, }, { date: 1461769740000, open: 50.735, high: 50.75, low: 50.73, close: 50.74, volume: 35596, }, { date: 1461769800000, open: 50.745, high: 50.8, low: 50.74, close: 50.79, volume: 44315, }, { date: 1461769860000, open: 50.7862, high: 50.795, low: 50.75, close: 50.79, volume: 69130, }, { date: 1461769920000, open: 50.79, high: 50.79, low: 50.735, close: 50.74, volume: 48482, }, { date: 1461769980000, open: 50.74, high: 50.77, low: 50.73, close: 50.765, volume: 44033, }, { date: 1461770040000, open: 50.765, high: 50.82, low: 50.7612, close: 50.8, volume: 174638, }, { date: 1461770100000, open: 50.8, high: 50.85, low: 50.8, close: 50.8443, volume: 70777, }, { date: 1461770160000, open: 50.84, high: 50.9, low: 50.83, close: 50.9, volume: 117921, }, { date: 1461770220000, open: 50.9, high: 50.95, low: 50.87, close: 50.94, volume: 207318, }, { date: 1461770280000, open: 50.9368, high: 50.95, low: 50.93, close: 50.94, volume: 113294, }, { date: 1461770340000, open: 50.94, high: 50.96, low: 50.94, close: 50.94, volume: 100561, }, { date: 1461770400000, open: 50.95, high: 51.02, low: 50.95, close: 51.01, volume: 86181, }, { date: 1461770460000, open: 51.0101, high: 51.035, low: 50.99, close: 51.03, volume: 106090, }, { date: 1461770520000, open: 51.03, high: 51.03, low: 50.97, close: 50.975, volume: 106256, }, { date: 1461770580000, open: 50.975, high: 50.98, low: 50.94, close: 50.959, volume: 53640, }, { date: 1461770640000, open: 50.95, high: 50.97, low: 50.9325, close: 50.97, volume: 64206, }, { date: 1461770700000, open: 50.97, high: 50.98, low: 50.945, close: 50.975, volume: 50979, }, { date: 1461770760000, open: 50.98, high: 51.02, low: 50.9762, close: 51.005, volume: 69892, }, { date: 1461770820000, open: 51, high: 51.03, low: 51, close: 51.015, volume: 73670, }, { date: 1461770880000, open: 51.0106, high: 51.02, low: 50.965, close: 50.97, volume: 66729, }, { date: 1461770940000, open: 50.965, high: 51, low: 50.96, close: 50.996, volume: 19251, }, { date: 1461771000000, open: 51, high: 51, low: 50.97, close: 50.975, volume: 70037, }, { date: 1461771060000, open: 50.98, high: 51, low: 50.92, close: 50.925, volume: 83153, }, { date: 1461771120000, open: 50.93, high: 50.97, low: 50.92, close: 50.93, volume: 112518, }, { date: 1461771180000, open: 50.93, high: 50.95, low: 50.91, close: 50.9271, volume: 121520, }, { date: 1461771240000, open: 50.92, high: 50.95, low: 50.92, close: 50.935, volume: 40624, }, { date: 1461771300000, open: 50.9331, high: 50.95, low: 50.93, close: 50.945, volume: 79579, }, { date: 1461771360000, open: 50.95, high: 50.96, low: 50.94, close: 50.945, volume: 102436, }, { date: 1461771420000, open: 50.945, high: 50.96, low: 50.94, close: 50.96, volume: 75844, }, { date: 1461771480000, open: 50.96, high: 50.98, low: 50.96, close: 50.965, volume: 45382, }, { date: 1461771540000, open: 50.965, high: 50.97, low: 50.95, close: 50.955, volume: 46659, }, { date: 1461771600000, open: 50.95, high: 50.97, low: 50.95, close: 50.965, volume: 29628, }, { date: 1461771660000, open: 50.965, high: 50.99, low: 50.965, close: 50.99, volume: 77939, }, { date: 1461771720000, open: 50.99, high: 51.005, low: 50.9875, close: 50.99, volume: 125279, }, { date: 1461771780000, open: 50.99, high: 51, low: 50.96, close: 50.964, volume: 85029, }, { date: 1461771840000, open: 50.97, high: 50.975, low: 50.95, close: 50.95, volume: 35890, }, { date: 1461771900000, open: 50.95, high: 50.97, low: 50.94, close: 50.965, volume: 47369, }, { date: 1461771960000, open: 50.965, high: 50.99, low: 50.95, close: 50.98, volume: 56498, }, { date: 1461772020000, open: 50.99, high: 50.99, low: 50.92, close: 50.925, volume: 82509, }, { date: 1461772080000, open: 50.92, high: 50.927, low: 50.89, close: 50.9, volume: 52985, }, { date: 1461772140000, open: 50.895, high: 50.91, low: 50.89, close: 50.91, volume: 28256, }, { date: 1461772200000, open: 50.91, high: 50.93, low: 50.9, close: 50.92, volume: 31408, }, { date: 1461772260000, open: 50.925, high: 50.93, low: 50.92, close: 50.92, volume: 35231, }, { date: 1461772320000, open: 50.92, high: 50.97, low: 50.92, close: 50.965, volume: 53208, }, { date: 1461772380000, open: 50.97, high: 50.99, low: 50.97, close: 50.985, volume: 38707, }, { date: 1461772440000, open: 50.99, high: 51, low: 50.98, close: 50.9899, volume: 70663, }, { date: 1461772500000, open: 50.98, high: 50.99, low: 50.95, close: 50.955, volume: 43151, }, { date: 1461772560000, open: 50.95, high: 50.97, low: 50.95, close: 50.96, volume: 41211, }, { date: 1461772620000, open: 50.9699, high: 50.975, low: 50.95, close: 50.97, volume: 91224, }, { date: 1461772680000, open: 50.9615, high: 50.9699, low: 50.95, close: 50.955, volume: 32623, }, { date: 1461772740000, open: 50.955, high: 50.97, low: 50.95, close: 50.97, volume: 42395, }, { date: 1461772800000, open: 50.97, high: 50.97, low: 50.96, close: 50.97, volume: 64555, }, { date: 1461772860000, open: 50.96, high: 50.97, low: 50.95, close: 50.96, volume: 73075, }, { date: 1461772920000, open: 50.9599, high: 50.96, low: 50.92, close: 50.93, volume: 67430, }, { date: 1461772980000, open: 50.93, high: 50.94, low: 50.9, close: 50.9, volume: 129244, }, { date: 1461773040000, open: 50.9, high: 50.91, low: 50.9, close: 50.9, volume: 14770, }, { date: 1461773100000, open: 50.9, high: 50.91, low: 50.9, close: 50.9, volume: 43930, }, { date: 1461773160000, open: 50.905, high: 50.9099, low: 50.85, close: 50.85, volume: 57613, }, { date: 1461773220000, open: 50.85, high: 50.86, low: 50.83, close: 50.835, volume: 53671, }, { date: 1461773280000, open: 50.83, high: 50.84, low: 50.8, close: 50.8, volume: 69494, }, { date: 1461773340000, open: 50.8065, high: 50.81, low: 50.76, close: 50.77, volume: 110882, }, { date: 1461773400000, open: 50.775, high: 50.78, low: 50.77, close: 50.771, volume: 41524, }, { date: 1461773460000, open: 50.77, high: 50.795, low: 50.75, close: 50.755, volume: 84627, }, { date: 1461773520000, open: 50.7599, high: 50.78, low: 50.75, close: 50.76, volume: 60314, }, { date: 1461773580000, open: 50.77, high: 50.775, low: 50.75, close: 50.765, volume: 58247, }, { date: 1461773640000, open: 50.7635, high: 50.77, low: 50.75, close: 50.75, volume: 74728, }, { date: 1461773700000, open: 50.75, high: 50.76, low: 50.71, close: 50.72, volume: 114091, }, { date: 1461773760000, open: 50.72, high: 50.73, low: 50.71, close: 50.71, volume: 34304, }, { date: 1461773820000, open: 50.715, high: 50.73, low: 50.71, close: 50.715, volume: 58197, }, { date: 1461773880000, open: 50.7101, high: 50.72, low: 50.7, close: 50.715, volume: 53987, }, { date: 1461773940000, open: 50.715, high: 50.77, low: 50.7, close: 50.7635, volume: 72733, }, { date: 1461774000000, open: 50.765, high: 50.77, low: 50.76, close: 50.77, volume: 12431, }, { date: 1461774060000, open: 50.77, high: 50.815, low: 50.76, close: 50.79, volume: 134679, }, { date: 1461774120000, open: 50.79, high: 50.86, low: 50.785, close: 50.8485, volume: 98992, }, { date: 1461774180000, open: 50.84, high: 50.91, low: 50.83, close: 50.89, volume: 69143, }, { date: 1461774240000, open: 50.89, high: 50.8999, low: 50.86, close: 50.875, volume: 33263, }, { date: 1461774300000, open: 50.875, high: 50.88, low: 50.87, close: 50.8743, volume: 44584, }, { date: 1461774360000, open: 50.88, high: 50.88, low: 50.87, close: 50.8764, volume: 31907, }, { date: 1461774420000, open: 50.87, high: 50.88, low: 50.87, close: 50.875, volume: 20350, }, { date: 1461774480000, open: 50.88, high: 50.94, low: 50.875, close: 50.935, volume: 79990, }, { date: 1461774540000, open: 50.935, high: 50.94, low: 50.925, close: 50.93, volume: 49628, }, { date: 1461774600000, open: 50.93, high: 50.95, low: 50.93, close: 50.935, volume: 54678, }, { date: 1461774660000, open: 50.935, high: 50.95, low: 50.93, close: 50.945, volume: 64743, }, { date: 1461774720000, open: 50.95, high: 50.955, low: 50.93, close: 50.9536, volume: 95624, }, { date: 1461774780000, open: 50.955, high: 50.96, low: 50.93, close: 50.935, volume: 90701, }, { date: 1461774840000, open: 50.93, high: 50.9399, low: 50.9, close: 50.9, volume: 52529, }, { date: 1461774900000, open: 50.905, high: 50.91, low: 50.9, close: 50.905, volume: 19098, }, { date: 1461774960000, open: 50.905, high: 50.91, low: 50.9, close: 50.9, volume: 27275, }, { date: 1461775020000, open: 50.9, high: 50.91, low: 50.9, close: 50.9, volume: 34795, }, { date: 1461775080000, open: 50.9, high: 50.905, low: 50.86, close: 50.86, volume: 73572, }, { date: 1461775140000, open: 50.865, high: 50.87, low: 50.84, close: 50.865, volume: 71864, }, { date: 1461775200000, open: 50.865, high: 50.9, low: 50.8625, close: 50.87, volume: 127532, }, { date: 1461775260000, open: 50.875, high: 50.88, low: 50.86, close: 50.87, volume: 44333, }, { date: 1461775320000, open: 50.87, high: 50.88, low: 50.85, close: 50.865, volume: 101278, }, { date: 1461775380000, open: 50.86, high: 50.87, low: 50.83, close: 50.83, volume: 51251, }, { date: 1461775440000, open: 50.83, high: 50.84, low: 50.79, close: 50.8, volume: 77830, }, { date: 1461775500000, open: 50.795, high: 50.83, low: 50.79, close: 50.815, volume: 66549, }, { date: 1461775560000, open: 50.815, high: 50.84, low: 50.815, close: 50.8357, volume: 42949, }, { date: 1461775620000, open: 50.83, high: 50.85, low: 50.83, close: 50.83, volume: 32531, }, { date: 1461775680000, open: 50.84, high: 50.84, low: 50.83, close: 50.835, volume: 8218, }, { date: 1461775740000, open: 50.835, high: 50.87, low: 50.83, close: 50.865, volume: 42559, }, { date: 1461775800000, open: 50.868, high: 50.9, low: 50.86, close: 50.8901, volume: 56942, }, { date: 1461775860000, open: 50.9, high: 50.91, low: 50.86, close: 50.865, volume: 86074, }, { date: 1461775920000, open: 50.865, high: 50.94, low: 50.865, close: 50.93, volume: 49193, }, { date: 1461775980000, open: 50.9368, high: 50.94, low: 50.9, close: 50.9072, volume: 35140, }, { date: 1461776040000, open: 50.9035, high: 50.92, low: 50.9, close: 50.915, volume: 20236, }, { date: 1461776100000, open: 50.91, high: 50.93, low: 50.9, close: 50.905, volume: 27764, }, { date: 1461776160000, open: 50.905, high: 50.91, low: 50.9, close: 50.91, volume: 14914, }, { date: 1461776220000, open: 50.91, high: 50.91, low: 50.88, close: 50.88, volume: 18695, }, { date: 1461776280000, open: 50.885, high: 50.8862, low: 50.87, close: 50.87, volume: 17452, }, { date: 1461776340000, open: 50.87, high: 50.88, low: 50.87, close: 50.875, volume: 9819, }, { date: 1461776400000, open: 50.88, high: 50.88, low: 50.87, close: 50.87, volume: 27100, }, { date: 1461776460000, open: 50.875, high: 50.88, low: 50.86, close: 50.865, volume: 28316, }, { date: 1461776520000, open: 50.8657, high: 50.9, low: 50.86, close: 50.8775, volume: 76273, }, { date: 1461776580000, open: 50.875, high: 50.88, low: 50.855, close: 50.86, volume: 34953, }, { date: 1461776640000, open: 50.86, high: 50.88, low: 50.86, close: 50.88, volume: 27751, }, { date: 1461776700000, open: 50.87, high: 50.875, low: 50.83, close: 50.83, volume: 84008, }, { date: 1461776760000, open: 50.835, high: 50.84, low: 50.8, close: 50.805, volume: 46987, }, { date: 1461776820000, open: 50.8, high: 50.81, low: 50.75, close: 50.76, volume: 118821, }, { date: 1461776880000, open: 50.755, high: 50.76, low: 50.7, close: 50.705, volume: 99961, }, { date: 1461776940000, open: 50.705, high: 50.71, low: 50.675, close: 50.675, volume: 87407, }, { date: 1461777000000, open: 50.67, high: 50.68, low: 50.65, close: 50.655, volume: 124052, }, { date: 1461777060000, open: 50.655, high: 50.66, low: 50.63, close: 50.63, volume: 61899, }, { date: 1461777120000, open: 50.64, high: 50.66, low: 50.63, close: 50.635, volume: 100374, }, { date: 1461777180000, open: 50.6399, high: 50.66, low: 50.6, close: 50.605, volume: 141517, }, { date: 1461777240000, open: 50.625, high: 50.65, low: 50.6, close: 50.6436, volume: 72905, }, { date: 1461777300000, open: 50.65, high: 50.72, low: 50.65, close: 50.6929, volume: 58447, }, { date: 1461777360000, open: 50.7, high: 50.765, low: 50.69, close: 50.7474, volume: 63631, }, { date: 1461777420000, open: 50.7464, high: 50.815, low: 50.745, close: 50.78, volume: 62401, }, { date: 1461777480000, open: 50.78, high: 50.81, low: 50.78, close: 50.8, volume: 26447, }, { date: 1461777540000, open: 50.8036, high: 50.81, low: 50.78, close: 50.785, volume: 38046, }, { date: 1461777600000, open: 50.79, high: 50.81, low: 50.78, close: 50.79, volume: 21311, }, { date: 1461777660000, open: 50.78, high: 50.79, low: 50.7299, close: 50.7299, volume: 57337, }, { date: 1461777720000, open: 50.73, high: 50.77, low: 50.725, close: 50.75, volume: 43249, }, { date: 1461777780000, open: 50.755, high: 50.76, low: 50.75, close: 50.755, volume: 20207, }, { date: 1461777840000, open: 50.7571, high: 50.76, low: 50.67, close: 50.675, volume: 117805, }, { date: 1461777900000, open: 50.675, high: 50.69, low: 50.61, close: 50.625, volume: 114960, }, { date: 1461777960000, open: 50.63, high: 50.65, low: 50.58, close: 50.6005, volume: 179335, }, { date: 1461778020000, open: 50.6, high: 50.625, low: 50.6, close: 50.62, volume: 40380, }, { date: 1461778080000, open: 50.62, high: 50.6568, low: 50.61, close: 50.656, volume: 74011, }, { date: 1461778140000, open: 50.655, high: 50.67, low: 50.635, close: 50.66, volume: 63654, }, { date: 1461778200000, open: 50.66, high: 50.67, low: 50.64, close: 50.65, volume: 41004, }, { date: 1461778260000, open: 50.64, high: 50.6799, low: 50.64, close: 50.6735, volume: 37144, }, { date: 1461778320000, open: 50.6742, high: 50.7199, low: 50.67, close: 50.717, volume: 53140, }, { date: 1461778380000, open: 50.715, high: 50.79, low: 50.715, close: 50.7671, volume: 58191, }, { date: 1461778440000, open: 50.765, high: 50.77, low: 50.76, close: 50.77, volume: 26237, }, { date: 1461778500000, open: 50.77, high: 50.8, low: 50.76, close: 50.8, volume: 38410, }, { date: 1461778560000, open: 50.79, high: 50.82, low: 50.79, close: 50.79, volume: 59465, }, { date: 1461778620000, open: 50.79, high: 50.79, low: 50.76, close: 50.76, volume: 37252, }, { date: 1461778680000, open: 50.765, high: 50.77, low: 50.75, close: 50.75, volume: 30447, }, { date: 1461778740000, open: 50.75, high: 50.7501, low: 50.6536, close: 50.6536, volume: 91849, }, { date: 1461778800000, open: 50.6533, high: 50.68, low: 50.65, close: 50.678, volume: 82383, }, { date: 1461778860000, open: 50.675, high: 50.72, low: 50.675, close: 50.715, volume: 85996, }, { date: 1461778920000, open: 50.685, high: 50.72, low: 50.67, close: 50.67, volume: 70375, }, { date: 1461778980000, open: 50.67, high: 50.69, low: 50.66, close: 50.68, volume: 65430, }, { date: 1461779040000, open: 50.68, high: 50.73, low: 50.68, close: 50.705, volume: 65061, }, { date: 1461779100000, open: 50.7005, high: 50.74, low: 50.69, close: 50.735, volume: 73471, }, { date: 1461779160000, open: 50.735, high: 50.795, low: 50.73, close: 50.7854, volume: 38338, }, { date: 1461779220000, open: 50.787, high: 50.79, low: 50.78, close: 50.7895, volume: 27699, }, { date: 1461779280000, open: 50.79, high: 50.82, low: 50.785, close: 50.81, volume: 49879, }, { date: 1461779340000, open: 50.81, high: 50.85, low: 50.79, close: 50.79, volume: 132081, }, { date: 1461779400000, open: 50.7999, high: 50.8, low: 50.79, close: 50.795, volume: 22727, }, { date: 1461779460000, open: 50.795, high: 50.84, low: 50.795, close: 50.84, volume: 37461, }, { date: 1461779520000, open: 50.84, high: 50.84, low: 50.795, close: 50.795, volume: 43102, }, { date: 1461779580000, open: 50.8, high: 50.84, low: 50.79, close: 50.83, volume: 31247, }, { date: 1461779640000, open: 50.825, high: 50.8299, low: 50.79, close: 50.79, volume: 24541, }, { date: 1461779700000, open: 50.797, high: 50.81, low: 50.79, close: 50.79, volume: 52201, }, { date: 1461779760000, open: 50.795, high: 50.7973, low: 50.69, close: 50.69, volume: 145926, }, { date: 1461779820000, open: 50.69, high: 50.7664, low: 50.68, close: 50.75, volume: 63659, }, { date: 1461779880000, open: 50.76, high: 50.77, low: 50.74, close: 50.77, volume: 34851, }, { date: 1461779940000, open: 50.7685, high: 50.7685, low: 50.72, close: 50.73, volume: 47459, }, { date: 1461780000000, open: 50.735, high: 50.7399, low: 50.69, close: 50.7, volume: 49672, }, { date: 1461780060000, open: 50.715, high: 50.72, low: 50.57, close: 50.62, volume: 285998, }, { date: 1461780120000, open: 50.62, high: 50.68, low: 50.56, close: 50.59, volume: 135379, }, { date: 1461780180000, open: 50.585, high: 50.71, low: 50.58, close: 50.675, volume: 114775, }, { date: 1461780240000, open: 50.67, high: 50.71, low: 50.61, close: 50.655, volume: 119150, }, { date: 1461780300000, open: 50.66, high: 50.67, low: 50.62, close: 50.624, volume: 101793, }, { date: 1461780360000, open: 50.625, high: 50.77, low: 50.624, close: 50.75, volume: 124699, }, { date: 1461780420000, open: 50.755, high: 50.88, low: 50.745, close: 50.85, volume: 135859, }, { date: 1461780480000, open: 50.845, high: 50.845, low: 50.755, close: 50.77, volume: 88519, }, { date: 1461780540000, open: 50.78, high: 50.86, low: 50.765, close: 50.8, volume: 76028, }, { date: 1461780600000, open: 50.81, high: 50.915, low: 50.79, close: 50.88, volume: 114079, }, { date: 1461780660000, open: 50.88, high: 50.93, low: 50.87, close: 50.93, volume: 112280, }, { date: 1461780720000, open: 50.94, high: 50.94, low: 50.875, close: 50.885, volume: 117062, }, { date: 1461780780000, open: 50.883, high: 50.8889, low: 50.825, close: 50.85, volume: 135760, }, { date: 1461780840000, open: 50.85, high: 50.855, low: 50.84, close: 50.84, volume: 130763, }, { date: 1461780900000, open: 50.8441, high: 50.85, low: 50.795, close: 50.8, volume: 63984, }, { date: 1461780960000, open: 50.8, high: 50.85, low: 50.8, close: 50.84, volume: 108273, }, { date: 1461781020000, open: 50.84, high: 50.855, low: 50.83, close: 50.845, volume: 63145, }, { date: 1461781080000, open: 50.845, high: 50.85, low: 50.8, close: 50.805, volume: 76710, }, { date: 1461781140000, open: 50.8, high: 50.8091, low: 50.75, close: 50.795, volume: 100822, }, { date: 1461781200000, open: 50.8, high: 50.85, low: 50.7928, close: 50.84, volume: 84854, }, { date: 1461781260000, open: 50.83, high: 50.857, low: 50.8, close: 50.857, volume: 81170, }, { date: 1461781320000, open: 50.85, high: 50.895, low: 50.835, close: 50.89, volume: 47241, }, { date: 1461781380000, open: 50.9, high: 50.915, low: 50.88, close: 50.8805, volume: 49329, }, { date: 1461781440000, open: 50.885, high: 50.89, low: 50.85, close: 50.8535, volume: 45068, }, { date: 1461781500000, open: 50.855, high: 50.86, low: 50.79, close: 50.82, volume: 73672, }, { date: 1461781560000, open: 50.82, high: 50.82, low: 50.7699, close: 50.78, volume: 132131, }, { date: 1461781620000, open: 50.78, high: 50.82, low: 50.765, close: 50.81, volume: 124629, }, { date: 1461781680000, open: 50.81, high: 50.85, low: 50.8, close: 50.81, volume: 53304, }, { date: 1461781740000, open: 50.81, high: 50.83, low: 50.78, close: 50.7996, volume: 114490, }, { date: 1461781800000, open: 50.8, high: 50.8, low: 50.76, close: 50.78, volume: 55205, }, { date: 1461781860000, open: 50.78, high: 50.785, low: 50.75, close: 50.76, volume: 43172, }, { date: 1461781920000, open: 50.755, high: 50.78, low: 50.7515, close: 50.765, volume: 78118, }, { date: 1461781980000, open: 50.77, high: 50.785, low: 50.7, close: 50.7099, volume: 82209, }, { date: 1461782040000, open: 50.7, high: 50.7, low: 50.66, close: 50.665, volume: 88480, }, { date: 1461782100000, open: 50.6699, high: 50.675, low: 50.655, close: 50.66, volume: 129808, }, { date: 1461782160000, open: 50.6699, high: 50.77, low: 50.6641, close: 50.755, volume: 97495, }, { date: 1461782220000, open: 50.755, high: 50.76, low: 50.7201, close: 50.735, volume: 37889, }, { date: 1461782280000, open: 50.7372, high: 50.7372, low: 50.66, close: 50.675, volume: 103436, }, { date: 1461782340000, open: 50.675, high: 50.69, low: 50.655, close: 50.675, volume: 62047, }, { date: 1461782400000, open: 50.68, high: 50.68, low: 50.66, close: 50.665, volume: 32867, }, { date: 1461782460000, open: 50.67, high: 50.67, low: 50.63, close: 50.635, volume: 176298, }, { date: 1461782520000, open: 50.633, high: 50.74, low: 50.633, close: 50.7, volume: 117781, }, { date: 1461782580000, open: 50.709, high: 50.72, low: 50.695, close: 50.715, volume: 51976, }, { date: 1461782640000, open: 50.71, high: 50.715, low: 50.6801, close: 50.6801, volume: 108922, }, { date: 1461782700000, open: 50.69, high: 50.69, low: 50.62, close: 50.62, volume: 63032, }, { date: 1461782760000, open: 50.625, high: 50.69, low: 50.62, close: 50.69, volume: 79602, }, { date: 1461782820000, open: 50.685, high: 50.695, low: 50.645, close: 50.655, volume: 63334, }, { date: 1461782880000, open: 50.655, high: 50.67, low: 50.635, close: 50.64, volume: 46696, }, { date: 1461782940000, open: 50.635, high: 50.66, low: 50.63, close: 50.655, volume: 49225, }, { date: 1461783000000, open: 50.65, high: 50.7, low: 50.65, close: 50.685, volume: 63520, }, { date: 1461783060000, open: 50.685, high: 50.69, low: 50.65, close: 50.68, volume: 45141, }, { date: 1461783120000, open: 50.68, high: 50.71, low: 50.67, close: 50.705, volume: 122417, }, { date: 1461783180000, open: 50.7, high: 50.72, low: 50.7, close: 50.71, volume: 68193, }, { date: 1461783240000, open: 50.705, high: 50.72, low: 50.705, close: 50.715, volume: 68263, }, { date: 1461783300000, open: 50.71, high: 50.73, low: 50.71, close: 50.72, volume: 108212, }, { date: 1461783360000, open: 50.725, high: 50.76, low: 50.71, close: 50.7135, volume: 223520, }, { date: 1461783420000, open: 50.715, high: 50.72, low: 50.66, close: 50.665, volume: 50852, }, { date: 1461783480000, open: 50.665, high: 50.685, low: 50.66, close: 50.68, volume: 77107, }, { date: 1461783540000, open: 50.68, high: 50.69, low: 50.675, close: 50.68, volume: 50399, }, { date: 1461783600000, open: 50.68, high: 50.72, low: 50.68, close: 50.7, volume: 113561, }, { date: 1461783660000, open: 50.705, high: 50.71, low: 50.67, close: 50.68, volume: 79230, }, { date: 1461783720000, open: 50.685, high: 50.765, low: 50.665, close: 50.75, volume: 187177, }, { date: 1461783780000, open: 50.755, high: 50.79, low: 50.75, close: 50.785, volume: 97318, }, { date: 1461783840000, open: 50.79, high: 50.8, low: 50.7736, close: 50.8, volume: 137905, }, { date: 1461783900000, open: 50.8, high: 50.805, low: 50.77, close: 50.79, volume: 140377, }, { date: 1461783960000, open: 50.79, high: 50.8001, low: 50.78, close: 50.785, volume: 65863, }, { date: 1461784020000, open: 50.79, high: 50.79, low: 50.75, close: 50.755, volume: 82546, }, { date: 1461784080000, open: 50.76, high: 50.8, low: 50.75, close: 50.795, volume: 84186, }, { date: 1461784140000, open: 50.795, high: 50.822, low: 50.79, close: 50.805, volume: 118657, }, { date: 1461784200000, open: 50.805, high: 50.805, low: 50.76, close: 50.8, volume: 70278, }, { date: 1461784260000, open: 50.796, high: 50.81, low: 50.783, close: 50.79, volume: 109124, }, { date: 1461784320000, open: 50.795, high: 50.81, low: 50.78, close: 50.78, volume: 68778, }, { date: 1461784380000, open: 50.79, high: 50.8, low: 50.765, close: 50.783, volume: 70801, }, { date: 1461784440000, open: 50.7842, high: 50.85, low: 50.78, close: 50.84, volume: 181439, }, { date: 1461784500000, open: 50.84, high: 50.84, low: 50.8, close: 50.8258, volume: 63077, }, { date: 1461784560000, open: 50.825, high: 50.85, low: 50.82, close: 50.827, volume: 65914, }, { date: 1461784620000, open: 50.83, high: 50.85, low: 50.82, close: 50.83, volume: 47988, }, { date: 1461784680000, open: 50.835, high: 50.86, low: 50.83, close: 50.85, volume: 190304, }, { date: 1461784740000, open: 50.85, high: 50.87, low: 50.81, close: 50.83, volume: 202984, }, { date: 1461784800000, open: 50.83, high: 50.83, low: 50.82, close: 50.83, volume: 122038, }, { date: 1461784860000, open: 50.83, high: 50.86, low: 50.82, close: 50.85, volume: 199446, }, { date: 1461784920000, open: 50.85, high: 50.9, low: 50.84, close: 50.885, volume: 68994, }, { date: 1461784980000, open: 50.885, high: 51.1, low: 50.8545, close: 50.8695, volume: 172386, }, { date: 1461785040000, open: 50.87, high: 50.87, low: 50.86, close: 50.865, volume: 49371, }, { date: 1461785100000, open: 50.865, high: 50.9, low: 50.865, close: 50.9, volume: 118278, }, { date: 1461785160000, open: 50.895, high: 50.9, low: 50.88, close: 50.8865, volume: 87765, }, { date: 1461785220000, open: 50.88, high: 50.89, low: 50.87, close: 50.8701, volume: 46294, }, { date: 1461785280000, open: 50.87, high: 50.875, low: 50.8435, close: 50.865, volume: 104587, }, { date: 1461785340000, open: 50.865, high: 50.87, low: 50.82, close: 50.825, volume: 104835, }, { date: 1461785400000, open: 50.825, high: 50.84, low: 50.78, close: 50.78, volume: 273401, }, { date: 1461785460000, open: 50.785, high: 50.82, low: 50.78, close: 50.805, volume: 215696, }, { date: 1461785520000, open: 50.8099, high: 50.8158, low: 50.8, close: 50.815, volume: 47710, }, { date: 1461785580000, open: 50.815, high: 50.875, low: 50.8101, close: 50.875, volume: 70646, }, { date: 1461785640000, open: 50.8799, high: 50.9, low: 50.86, close: 50.885, volume: 133569, }, { date: 1461785700000, open: 50.88, high: 50.9, low: 50.88, close: 50.8905, volume: 52148, }, { date: 1461785760000, open: 50.9, high: 50.9, low: 50.88, close: 50.883, volume: 60025, }, { date: 1461785820000, open: 50.885, high: 50.9, low: 50.88, close: 50.9, volume: 117357, }, { date: 1461785880000, open: 50.9, high: 50.91, low: 50.89, close: 50.8965, volume: 131701, }, { date: 1461785940000, open: 50.9, high: 50.96, low: 50.8901, close: 50.945, volume: 223032, }, { date: 1461786000000, open: 50.945, high: 50.96, low: 50.94, close: 50.945, volume: 115519, }, { date: 1461786060000, open: 50.95, high: 50.959, low: 50.91, close: 50.925, volume: 162941, }, { date: 1461786120000, open: 50.92, high: 50.93, low: 50.89, close: 50.9, volume: 50125, }, { date: 1461786180000, open: 50.895, high: 50.895, low: 50.855, close: 50.86, volume: 201300, }, { date: 1461786240000, open: 50.865, high: 50.89, low: 50.85, close: 50.885, volume: 201664, }, { date: 1461786300000, open: 50.89, high: 50.93, low: 50.8801, close: 50.91, volume: 91414, }, { date: 1461786360000, open: 50.91, high: 50.98, low: 50.905, close: 50.98, volume: 115513, }, { date: 1461786420000, open: 50.98, high: 50.98, low: 50.94, close: 50.949, volume: 142650, }, { date: 1461786480000, open: 50.945, high: 50.965, low: 50.91, close: 50.92, volume: 173689, }, { date: 1461786540000, open: 50.93, high: 50.95, low: 50.92, close: 50.948, volume: 114290, }, { date: 1461786600000, open: 50.945, high: 50.98, low: 50.94, close: 50.975, volume: 112347, }, { date: 1461786660000, open: 50.97, high: 50.975, low: 50.88, close: 50.88, volume: 220538, }, { date: 1461786720000, open: 50.88, high: 50.9, low: 50.88, close: 50.885, volume: 120061, }, { date: 1461786780000, open: 50.885, high: 50.89, low: 50.87, close: 50.88, volume: 128413, }, { date: 1461786840000, open: 50.885, high: 50.89, low: 50.88, close: 50.885, volume: 83951, }, { date: 1461786900000, open: 50.885, high: 50.91, low: 50.87, close: 50.9, volume: 190571, }, { date: 1461786960000, open: 50.91, high: 50.91, low: 50.9, close: 50.9, volume: 108528, }, { date: 1461787020000, open: 50.905, high: 50.93, low: 50.89, close: 50.9299, volume: 230888, }, { date: 1461787080000, open: 50.93, high: 50.93, low: 50.91, close: 50.91, volume: 156162, }, { date: 1461787140000, open: 50.92, high: 50.92, low: 50.88, close: 50.885, volume: 235939, }, { date: 1461787200000, open: 50.885, high: 50.95, low: 50.86, close: 50.94, volume: 3508181, }, { date: 1461850200000, open: 50.62, high: 50.7, low: 50.62, close: 50.68, volume: 362874, }, { date: 1461850260000, open: 50.688, high: 50.77, low: 50.6, close: 50.615, volume: 205859, }, { date: 1461850320000, open: 50.6138, high: 50.62, low: 50.56, close: 50.56, volume: 103291, }, { date: 1461850380000, open: 50.56, high: 50.59, low: 50.53, close: 50.59, volume: 93887, }, { date: 1461850440000, open: 50.59, high: 50.6, low: 50.36, close: 50.43, volume: 209588, }, { date: 1461850500000, open: 50.43, high: 50.52, low: 50.4265, close: 50.47, volume: 96416, }, { date: 1461850560000, open: 50.47, high: 50.6, low: 50.46, close: 50.57, volume: 92282, }, { date: 1461850620000, open: 50.57, high: 50.59, low: 50.52, close: 50.54, volume: 89046, }, { date: 1461850680000, open: 50.54, high: 50.56, low: 50.48, close: 50.56, volume: 160894, }, { date: 1461850740000, open: 50.55, high: 50.57, low: 50.475, close: 50.5499, volume: 157492, }, { date: 1461850800000, open: 50.545, high: 50.61, low: 50.522, close: 50.53, volume: 108126, }, { date: 1461850860000, open: 50.53, high: 50.605, low: 50.51, close: 50.56, volume: 203343, }, { date: 1461850920000, open: 50.56, high: 50.6, low: 50.56, close: 50.58, volume: 265382, }, { date: 1461850980000, open: 50.572, high: 50.58, low: 50.55, close: 50.56, volume: 126095, }, { date: 1461851040000, open: 50.5601, high: 50.59, low: 50.55, close: 50.57, volume: 73798, }, { date: 1461851100000, open: 50.5628, high: 50.58, low: 50.51, close: 50.51, volume: 80575, }, { date: 1461851160000, open: 50.515, high: 50.57, low: 50.515, close: 50.56, volume: 95879, }, { date: 1461851220000, open: 50.57, high: 50.57, low: 50.53, close: 50.54, volume: 92729, }, { date: 1461851280000, open: 50.54, high: 50.5499, low: 50.51, close: 50.525, volume: 80307, }, { date: 1461851340000, open: 50.53, high: 50.71, low: 50.53, close: 50.64, volume: 407422, }, { date: 1461851400000, open: 50.64, high: 50.695, low: 50.64, close: 50.65, volume: 100084, }, { date: 1461851460000, open: 50.65, high: 50.655, low: 50.595, close: 50.615, volume: 77637, }, { date: 1461851520000, open: 50.615, high: 50.63, low: 50.59, close: 50.6, volume: 70822, }, { date: 1461851580000, open: 50.6, high: 50.6, low: 50.52, close: 50.52, volume: 64133, }, { date: 1461851640000, open: 50.525, high: 50.59, low: 50.5, close: 50.545, volume: 61358, }, { date: 1461851700000, open: 50.55, high: 50.56, low: 50.54, close: 50.54, volume: 32417, }, { date: 1461851760000, open: 50.54, high: 50.55, low: 50.5, close: 50.51, volume: 85382, }, { date: 1461851820000, open: 50.505, high: 50.53, low: 50.48, close: 50.51, volume: 71173, }, { date: 1461851880000, open: 50.51, high: 50.565, low: 50.51, close: 50.54, volume: 35147, }, { date: 1461851940000, open: 50.545, high: 50.6, low: 50.545, close: 50.595, volume: 45630, }, { date: 1461852000000, open: 50.5999, high: 50.61, low: 50.56, close: 50.575, volume: 95452, }, { date: 1461852060000, open: 50.58, high: 50.6, low: 50.56, close: 50.595, volume: 65340, }, { date: 1461852120000, open: 50.595, high: 50.6, low: 50.57, close: 50.57, volume: 91587, }, { date: 1461852180000, open: 50.575, high: 50.6, low: 50.56, close: 50.59, volume: 84492, }, { date: 1461852240000, open: 50.595, high: 50.61, low: 50.51, close: 50.51, volume: 134871, }, { date: 1461852300000, open: 50.515, high: 50.56, low: 50.51, close: 50.55, volume: 40814, }, { date: 1461852360000, open: 50.55, high: 50.56, low: 50.5301, close: 50.55, volume: 72676, }, { date: 1461852420000, open: 50.55, high: 50.56, low: 50.55, close: 50.56, volume: 52193, }, { date: 1461852480000, open: 50.555, high: 50.56, low: 50.5, close: 50.505, volume: 42888, }, { date: 1461852540000, open: 50.505, high: 50.52, low: 50.45, close: 50.45, volume: 93210, }, { date: 1461852600000, open: 50.4584, high: 50.46, low: 50.41, close: 50.44, volume: 79316, }, { date: 1461852660000, open: 50.445, high: 50.445, low: 50.36, close: 50.375, volume: 127200, }, { date: 1461852720000, open: 50.3799, high: 50.42, low: 50.3, close: 50.31, volume: 133004, }, { date: 1461852780000, open: 50.31, high: 50.44, low: 50.3, close: 50.4201, volume: 89766, }, { date: 1461852840000, open: 50.42, high: 50.4462, low: 50.3924, close: 50.445, volume: 67062, }, { date: 1461852900000, open: 50.45, high: 50.45, low: 50.41, close: 50.41, volume: 51079, }, { date: 1461852960000, open: 50.41, high: 50.415, low: 50.34, close: 50.34, volume: 100248, }, { date: 1461853020000, open: 50.35, high: 50.3958, low: 50.33, close: 50.385, volume: 116520, }, { date: 1461853080000, open: 50.39, high: 50.405, low: 50.375, close: 50.389, volume: 54942, }, { date: 1461853140000, open: 50.39, high: 50.44, low: 50.38, close: 50.44, volume: 153856, }, { date: 1461853200000, open: 50.435, high: 50.44, low: 50.41, close: 50.425, volume: 35609, }, { date: 1461853260000, open: 50.42, high: 50.455, low: 50.42, close: 50.455, volume: 103076, }, { date: 1461853320000, open: 50.4562, high: 50.4562, low: 50.4, close: 50.4, volume: 89085, }, { date: 1461853380000, open: 50.405, high: 50.42, low: 50.37, close: 50.37, volume: 68682, }, { date: 1461853440000, open: 50.37, high: 50.39, low: 50.355, close: 50.37, volume: 80103, }, { date: 1461853500000, open: 50.3699, high: 50.44, low: 50.36, close: 50.44, volume: 60084, }, { date: 1461853560000, open: 50.44, high: 50.505, low: 50.44, close: 50.48, volume: 92849, }, { date: 1461853620000, open: 50.48, high: 50.51, low: 50.48, close: 50.5, volume: 42855, }, { date: 1461853680000, open: 50.4932, high: 50.5, low: 50.47, close: 50.47, volume: 50732, }, { date: 1461853740000, open: 50.475, high: 50.49, low: 50.44, close: 50.44, volume: 57545, }, { date: 1461853800000, open: 50.44, high: 50.46, low: 50.42, close: 50.455, volume: 77793, }, { date: 1461853860000, open: 50.455, high: 50.49, low: 50.45, close: 50.47, volume: 108891, }, { date: 1461853920000, open: 50.475, high: 50.49, low: 50.47, close: 50.475, volume: 56247, }, { date: 1461853980000, open: 50.475, high: 50.5, low: 50.47, close: 50.475, volume: 73148, }, { date: 1461854040000, open: 50.48, high: 50.48, low: 50.42, close: 50.43, volume: 83732, }, { date: 1461854100000, open: 50.42, high: 50.49, low: 50.42, close: 50.455, volume: 68619, }, { date: 1461854160000, open: 50.46, high: 50.49, low: 50.44, close: 50.48, volume: 48140, }, { date: 1461854220000, open: 50.485, high: 50.515, low: 50.46, close: 50.51, volume: 117848, }, { date: 1461854280000, open: 50.5107, high: 50.53, low: 50.4901, close: 50.515, volume: 58625, }, { date: 1461854340000, open: 50.515, high: 50.515, low: 50.44, close: 50.44, volume: 32806, }, { date: 1461854400000, open: 50.445, high: 50.45, low: 50.42, close: 50.42, volume: 42684, }, { date: 1461854460000, open: 50.42, high: 50.46, low: 50.4, close: 50.4399, volume: 62513, }, { date: 1461854520000, open: 50.44, high: 50.45, low: 50.43, close: 50.445, volume: 40692, }, { date: 1461854580000, open: 50.4499, high: 50.47, low: 50.42, close: 50.42, volume: 56498, }, { date: 1461854640000, open: 50.4262, high: 50.45, low: 50.415, close: 50.4333, volume: 50217, }, { date: 1461854700000, open: 50.435, high: 50.45, low: 50.42, close: 50.445, volume: 66527, }, { date: 1461854760000, open: 50.45, high: 50.45, low: 50.42, close: 50.43, volume: 65141, }, { date: 1461854820000, open: 50.4341, high: 50.45, low: 50.42, close: 50.445, volume: 65061, }, { date: 1461854880000, open: 50.445, high: 50.46, low: 50.445, close: 50.45, volume: 115532, }, { date: 1461854940000, open: 50.455, high: 50.46, low: 50.43, close: 50.45, volume: 81908, }, { date: 1461855000000, open: 50.45, high: 50.5, low: 50.445, close: 50.5, volume: 95265, }, { date: 1461855060000, open: 50.5, high: 50.52, low: 50.4967, close: 50.515, volume: 52823, }, { date: 1461855120000, open: 50.515, high: 50.56, low: 50.51, close: 50.5401, volume: 77926, }, { date: 1461855180000, open: 50.5409, high: 50.55, low: 50.5, close: 50.51, volume: 49825, }, { date: 1461855240000, open: 50.5099, high: 50.53, low: 50.5, close: 50.5099, volume: 34568, }, { date: 1461855300000, open: 50.505, high: 50.52, low: 50.5, close: 50.5035, volume: 33194, }, { date: 1461855360000, open: 50.5, high: 50.51, low: 50.4801, close: 50.49, volume: 60726, }, { date: 1461855420000, open: 50.4975, high: 50.51, low: 50.47, close: 50.485, volume: 51257, }, { date: 1461855480000, open: 50.49, high: 50.51, low: 50.47, close: 50.49, volume: 117313, }, { date: 1461855540000, open: 50.49, high: 50.515, low: 50.48, close: 50.495, volume: 79201, }, { date: 1461855600000, open: 50.5, high: 50.51, low: 50.49, close: 50.51, volume: 32603, }, { date: 1461855660000, open: 50.51, high: 50.52, low: 50.49, close: 50.51, volume: 91991, }, { date: 1461855720000, open: 50.51, high: 50.5299, low: 50.5, close: 50.515, volume: 25794, }, { date: 1461855780000, open: 50.515, high: 50.52, low: 50.5, close: 50.5, volume: 29739, }, { date: 1461855840000, open: 50.505, high: 50.53, low: 50.5, close: 50.525, volume: 45836, }, { date: 1461855900000, open: 50.54, high: 50.55, low: 50.53, close: 50.55, volume: 92272, }, { date: 1461855960000, open: 50.545, high: 50.57, low: 50.54, close: 50.5699, volume: 85107, }, { date: 1461856020000, open: 50.56, high: 50.59, low: 50.56, close: 50.58, volume: 53308, }, { date: 1461856080000, open: 50.58, high: 50.59, low: 50.56, close: 50.57, volume: 15261, }, { date: 1461856140000, open: 50.57, high: 50.57, low: 50.54, close: 50.57, volume: 35726, }, { date: 1461856200000, open: 50.565, high: 50.58, low: 50.545, close: 50.545, volume: 37090, }, { date: 1461856260000, open: 50.54, high: 50.56, low: 50.53, close: 50.56, volume: 40567, }, { date: 1461856320000, open: 50.56, high: 50.58, low: 50.5428, close: 50.575, volume: 66059, }, { date: 1461856380000, open: 50.57, high: 50.59, low: 50.56, close: 50.5841, volume: 52451, }, { date: 1461856440000, open: 50.58, high: 50.605, low: 50.57, close: 50.57, volume: 78340, }, { date: 1461856500000, open: 50.5699, high: 50.575, low: 50.56, close: 50.56, volume: 44554, }, { date: 1461856560000, open: 50.5602, high: 50.6, low: 50.5602, close: 50.59, volume: 34301, }, { date: 1461856620000, open: 50.595, high: 50.5999, low: 50.555, close: 50.5554, volume: 39912, }, { date: 1461856680000, open: 50.555, high: 50.6, low: 50.55, close: 50.59, volume: 31505, }, { date: 1461856740000, open: 50.6, high: 50.6, low: 50.565, close: 50.565, volume: 26028, }, { date: 1461856800000, open: 50.56, high: 50.59, low: 50.55, close: 50.59, volume: 24022, }, { date: 1461856860000, open: 50.6, high: 50.61, low: 50.58, close: 50.605, volume: 56942, }, { date: 1461856920000, open: 50.605, high: 50.62, low: 50.59, close: 50.62, volume: 44976, }, { date: 1461856980000, open: 50.6147, high: 50.62, low: 50.6, close: 50.6199, volume: 25550, }, { date: 1461857040000, open: 50.6199, high: 50.64, low: 50.61, close: 50.6301, volume: 37060, }, { date: 1461857100000, open: 50.64, high: 50.65, low: 50.63, close: 50.645, volume: 64888, }, { date: 1461857160000, open: 50.65, high: 50.65, low: 50.63, close: 50.65, volume: 51583, }, { date: 1461857220000, open: 50.65, high: 50.65, low: 50.63, close: 50.64, volume: 27508, }, { date: 1461857280000, open: 50.6471, high: 50.6471, low: 50.62, close: 50.626, volume: 15765, }, { date: 1461857340000, open: 50.62, high: 50.63, low: 50.615, close: 50.6165, volume: 22894, }, { date: 1461857400000, open: 50.61, high: 50.62, low: 50.61, close: 50.61, volume: 18466, }, { date: 1461857460000, open: 50.61, high: 50.62, low: 50.61, close: 50.616, volume: 28857, }, { date: 1461857520000, open: 50.61, high: 50.62, low: 50.61, close: 50.61, volume: 15896, }, { date: 1461857580000, open: 50.6172, high: 50.62, low: 50.59, close: 50.5901, volume: 27929, }, { date: 1461857640000, open: 50.5935, high: 50.6, low: 50.55, close: 50.56, volume: 43870, }, { date: 1461857700000, open: 50.57, high: 50.62, low: 50.567, close: 50.611, volume: 20569, }, { date: 1461857760000, open: 50.62, high: 50.63, low: 50.6, close: 50.63, volume: 30079, }, { date: 1461857820000, open: 50.625, high: 50.66, low: 50.62, close: 50.6299, volume: 82256, }, { date: 1461857880000, open: 50.63, high: 50.63, low: 50.61, close: 50.61, volume: 29448, }, { date: 1461857940000, open: 50.615, high: 50.65, low: 50.61, close: 50.63, volume: 59514, }, { date: 1461858000000, open: 50.63, high: 50.64, low: 50.61, close: 50.63, volume: 43497, }, { date: 1461858060000, open: 50.63, high: 50.63, low: 50.61, close: 50.615, volume: 27159, }, { date: 1461858120000, open: 50.615, high: 50.62, low: 50.58, close: 50.58, volume: 66268, }, { date: 1461858180000, open: 50.585, high: 50.6, low: 50.584, close: 50.5942, volume: 17913, }, { date: 1461858240000, open: 50.59, high: 50.6, low: 50.59, close: 50.59, volume: 16714, }, { date: 1461858300000, open: 50.59, high: 50.6, low: 50.59, close: 50.59, volume: 30127, }, { date: 1461858360000, open: 50.595, high: 50.61, low: 50.59, close: 50.59, volume: 34228, }, { date: 1461858420000, open: 50.5999, high: 50.6, low: 50.57, close: 50.575, volume: 28301, }, { date: 1461858480000, open: 50.575, high: 50.58, low: 50.55, close: 50.57, volume: 48303, }, { date: 1461858540000, open: 50.57, high: 50.57, low: 50.56, close: 50.57, volume: 12637, }, { date: 1461858600000, open: 50.57, high: 50.57, low: 50.55, close: 50.55, volume: 28387, }, { date: 1461858660000, open: 50.56, high: 50.58, low: 50.55, close: 50.5799, volume: 33777, }, { date: 1461858720000, open: 50.575, high: 50.5799, low: 50.535, close: 50.54, volume: 53307, }, { date: 1461858780000, open: 50.54, high: 50.56, low: 50.53, close: 50.55, volume: 23277, }, { date: 1461858840000, open: 50.55, high: 50.56, low: 50.545, close: 50.5564, volume: 17796, }, { date: 1461858900000, open: 50.55, high: 50.575, low: 50.54, close: 50.545, volume: 49953, }, { date: 1461858960000, open: 50.549, high: 50.565, low: 50.545, close: 50.5537, volume: 20134, }, { date: 1461859020000, open: 50.55, high: 50.58, low: 50.55, close: 50.57, volume: 18548, }, { date: 1461859080000, open: 50.57, high: 50.57, low: 50.55, close: 50.5577, volume: 29869, }, { date: 1461859140000, open: 50.555, high: 50.56, low: 50.53, close: 50.545, volume: 41326, }, { date: 1461859200000, open: 50.55, high: 50.57, low: 50.54, close: 50.5601, volume: 33320, }, { date: 1461859260000, open: 50.5601, high: 50.59, low: 50.56, close: 50.58, volume: 59892, }, { date: 1461859320000, open: 50.58, high: 50.6, low: 50.58, close: 50.59, volume: 56709, }, { date: 1461859380000, open: 50.59, high: 50.59, low: 50.58, close: 50.59, volume: 7465, }, { date: 1461859440000, open: 50.59, high: 50.62, low: 50.57, close: 50.58, volume: 82293, }, { date: 1461859500000, open: 50.585, high: 50.59, low: 50.54, close: 50.55, volume: 54187, }, { date: 1461859560000, open: 50.55, high: 50.58, low: 50.55, close: 50.5728, volume: 20221, }, { date: 1461859620000, open: 50.57, high: 50.575, low: 50.55, close: 50.55, volume: 17135, }, { date: 1461859680000, open: 50.55, high: 50.56, low: 50.53, close: 50.535, volume: 17693, }, { date: 1461859740000, open: 50.53, high: 50.54, low: 50.53, close: 50.535, volume: 16522, }, { date: 1461859800000, open: 50.535, high: 50.54, low: 50.53, close: 50.535, volume: 20326, }, { date: 1461859860000, open: 50.535, high: 50.54, low: 50.5, close: 50.5, volume: 71713, }, { date: 1461859920000, open: 50.51, high: 50.51, low: 50.47, close: 50.485, volume: 114100, }, { date: 1461859980000, open: 50.48, high: 50.49, low: 50.46, close: 50.465, volume: 56150, }, { date: 1461860040000, open: 50.465, high: 50.48, low: 50.45, close: 50.455, volume: 36453, }, { date: 1461860100000, open: 50.4559, high: 50.46, low: 50.4335, close: 50.44, volume: 50517, }, { date: 1461860160000, open: 50.442, high: 50.45, low: 50.43, close: 50.44, volume: 46156, }, { date: 1461860220000, open: 50.43, high: 50.44, low: 50.42, close: 50.435, volume: 39714, }, { date: 1461860280000, open: 50.43, high: 50.4399, low: 50.41, close: 50.42, volume: 32182, }, { date: 1461860340000, open: 50.42, high: 50.43, low: 50.37, close: 50.38, volume: 78612, }, { date: 1461860400000, open: 50.38, high: 50.3899, low: 50.37, close: 50.37, volume: 19828, }, { date: 1461860460000, open: 50.37, high: 50.3799, low: 50.34, close: 50.355, volume: 135527, }, { date: 1461860520000, open: 50.35, high: 50.36, low: 50.33, close: 50.3301, volume: 56685, }, { date: 1461860580000, open: 50.335, high: 50.36, low: 50.33, close: 50.3313, volume: 45756, }, { date: 1461860640000, open: 50.33, high: 50.34, low: 50.33, close: 50.33, volume: 26701, }, { date: 1461860700000, open: 50.3399, high: 50.36, low: 50.33, close: 50.33, volume: 63107, }, { date: 1461860760000, open: 50.34, high: 50.36, low: 50.32, close: 50.345, volume: 278841, }, { date: 1461860820000, open: 50.345, high: 50.3598, low: 50.33, close: 50.34, volume: 39224, }, { date: 1461860880000, open: 50.34, high: 50.36, low: 50.34, close: 50.34, volume: 52822, }, { date: 1461860940000, open: 50.34, high: 50.35, low: 50.34, close: 50.34, volume: 49410, }, { date: 1461861000000, open: 50.34, high: 50.35, low: 50.33, close: 50.33, volume: 40939, }, { date: 1461861060000, open: 50.335, high: 50.3399, low: 50.31, close: 50.31, volume: 37770, }, { date: 1461861120000, open: 50.31, high: 50.3101, low: 50.29, close: 50.295, volume: 90923, }, { date: 1461861180000, open: 50.3, high: 50.35, low: 50.295, close: 50.315, volume: 64570, }, { date: 1461861240000, open: 50.315, high: 50.35, low: 50.3, close: 50.301, volume: 66936, }, { date: 1461861300000, open: 50.3099, high: 50.31, low: 50.29, close: 50.29, volume: 59247, }, { date: 1461861360000, open: 50.295, high: 50.3, low: 50.26, close: 50.28, volume: 107509, }, { date: 1461861420000, open: 50.285, high: 50.29, low: 50.26, close: 50.26, volume: 85924, }, { date: 1461861480000, open: 50.26, high: 50.28, low: 50.26, close: 50.275, volume: 90196, }, { date: 1461861540000, open: 50.27, high: 50.28, low: 50.27, close: 50.28, volume: 28884, }, { date: 1461861600000, open: 50.275, high: 50.29, low: 50.27, close: 50.275, volume: 53798, }, { date: 1461861660000, open: 50.27, high: 50.28, low: 50.26, close: 50.26, volume: 50292, }, { date: 1461861720000, open: 50.27, high: 50.29, low: 50.26, close: 50.27, volume: 67364, }, { date: 1461861780000, open: 50.27, high: 50.285, low: 50.26, close: 50.26, volume: 65505, }, { date: 1461861840000, open: 50.26, high: 50.28, low: 50.26, close: 50.2799, volume: 49376, }, { date: 1461861900000, open: 50.28, high: 50.29, low: 50.27, close: 50.29, volume: 37262, }, { date: 1461861960000, open: 50.29, high: 50.325, low: 50.285, close: 50.31, volume: 45522, }, { date: 1461862020000, open: 50.31, high: 50.32, low: 50.3, close: 50.316, volume: 59421, }, { date: 1461862080000, open: 50.3199, high: 50.326, low: 50.3, close: 50.3199, volume: 41762, }, { date: 1461862140000, open: 50.31, high: 50.3199, low: 50.29, close: 50.29, volume: 32042, }, { date: 1461862200000, open: 50.29, high: 50.3, low: 50.27, close: 50.27, volume: 29040, }, { date: 1461862260000, open: 50.275, high: 50.2799, low: 50.26, close: 50.27, volume: 45003, }, { date: 1461862320000, open: 50.265, high: 50.3, low: 50.26, close: 50.295, volume: 34479, }, { date: 1461862380000, open: 50.3, high: 50.31, low: 50.29, close: 50.295, volume: 49639, }, { date: 1461862440000, open: 50.3, high: 50.305, low: 50.29, close: 50.2967, volume: 54906, }, { date: 1461862500000, open: 50.29, high: 50.3, low: 50.27, close: 50.275, volume: 43365, }, { date: 1461862560000, open: 50.28, high: 50.31, low: 50.27, close: 50.3, volume: 41683, }, { date: 1461862620000, open: 50.3, high: 50.3099, low: 50.28, close: 50.2838, volume: 51092, }, { date: 1461862680000, open: 50.2899, high: 50.3, low: 50.28, close: 50.295, volume: 25174, }, { date: 1461862740000, open: 50.29, high: 50.299, low: 50.28, close: 50.285, volume: 37623, }, { date: 1461862800000, open: 50.285, high: 50.29, low: 50.28, close: 50.285, volume: 33407, }, { date: 1461862860000, open: 50.29, high: 50.3, low: 50.285, close: 50.29, volume: 81186, }, { date: 1461862920000, open: 50.291, high: 50.3, low: 50.28, close: 50.295, volume: 84673, }, { date: 1461862980000, open: 50.3, high: 50.3, low: 50.29, close: 50.295, volume: 147910, }, { date: 1461863040000, open: 50.295, high: 50.31, low: 50.29, close: 50.295, volume: 253371, }, { date: 1461863100000, open: 50.294, high: 50.31, low: 50.27, close: 50.28, volume: 134815, }, { date: 1461863160000, open: 50.28, high: 50.29, low: 50.26, close: 50.28, volume: 135762, }, { date: 1461863220000, open: 50.275, high: 50.285, low: 50.27, close: 50.275, volume: 59564, }, { date: 1461863280000, open: 50.28, high: 50.29, low: 50.275, close: 50.28, volume: 79163, }, { date: 1461863340000, open: 50.285, high: 50.29, low: 50.27, close: 50.275, volume: 69269, }, { date: 1461863400000, open: 50.274, high: 50.28, low: 50.265, close: 50.275, volume: 44095, }, { date: 1461863460000, open: 50.28, high: 50.28, low: 50.25, close: 50.2601, volume: 70083, }, { date: 1461863520000, open: 50.2638, high: 50.28, low: 50.245, close: 50.2765, volume: 305012, }, { date: 1461863580000, open: 50.275, high: 50.285, low: 50.25, close: 50.28, volume: 68266, }, { date: 1461863640000, open: 50.2899, high: 50.3, low: 50.28, close: 50.295, volume: 50281, }, { date: 1461863700000, open: 50.3, high: 50.31, low: 50.29, close: 50.31, volume: 49211, }, { date: 1461863760000, open: 50.31, high: 50.31, low: 50.25, close: 50.26, volume: 128015, }, { date: 1461863820000, open: 50.255, high: 50.27, low: 50.25, close: 50.265, volume: 59425, }, { date: 1461863880000, open: 50.26, high: 50.27, low: 50.26, close: 50.27, volume: 13744, }, { date: 1461863940000, open: 50.27, high: 50.28, low: 50.26, close: 50.27, volume: 34048, }, { date: 1461864000000, open: 50.275, high: 50.28, low: 50.27, close: 50.2734, volume: 24320, }, { date: 1461864060000, open: 50.27, high: 50.27, low: 50.1, close: 50.145, volume: 520783, }, { date: 1461864120000, open: 50.145, high: 50.16, low: 50.09, close: 50.135, volume: 137595, }, { date: 1461864180000, open: 50.135, high: 50.17, low: 50.095, close: 50.17, volume: 104940, }, { date: 1461864240000, open: 50.165, high: 50.17, low: 50.15, close: 50.15, volume: 34941, }, { date: 1461864300000, open: 50.16, high: 50.17, low: 50.15, close: 50.17, volume: 29116, }, { date: 1461864360000, open: 50.17, high: 50.2, low: 50.16, close: 50.2, volume: 33318, }, { date: 1461864420000, open: 50.2, high: 50.2, low: 50.18, close: 50.185, volume: 61962, }, { date: 1461864480000, open: 50.1899, high: 50.2, low: 50.18, close: 50.185, volume: 79066, }, { date: 1461864540000, open: 50.185, high: 50.2, low: 50.18, close: 50.2, volume: 30402, }, { date: 1461864600000, open: 50.2, high: 50.21, low: 50.19, close: 50.2028, volume: 51295, }, { date: 1461864660000, open: 50.2, high: 50.2099, low: 50.19, close: 50.195, volume: 16904, }, { date: 1461864720000, open: 50.191, high: 50.21, low: 50.19, close: 50.205, volume: 50301, }, { date: 1461864780000, open: 50.205, high: 50.24, low: 50.2, close: 50.24, volume: 62535, }, { date: 1461864840000, open: 50.25, high: 50.28, low: 50.23, close: 50.235, volume: 58179, }, { date: 1461864900000, open: 50.2384, high: 50.24, low: 50.21, close: 50.215, volume: 49289, }, { date: 1461864960000, open: 50.21, high: 50.23, low: 50.2, close: 50.23, volume: 35417, }, { date: 1461865020000, open: 50.2228, high: 50.235, low: 50.2, close: 50.2, volume: 33952, }, { date: 1461865080000, open: 50.2, high: 50.21, low: 50.2, close: 50.2, volume: 21550, }, { date: 1461865140000, open: 50.2, high: 50.21, low: 50.19, close: 50.199, volume: 40176, }, { date: 1461865200000, open: 50.19, high: 50.2, low: 50.17, close: 50.185, volume: 32751, }, { date: 1461865260000, open: 50.19, high: 50.19, low: 50.13, close: 50.15, volume: 56109, }, { date: 1461865320000, open: 50.145, high: 50.15, low: 50.13, close: 50.15, volume: 47986, }, { date: 1461865380000, open: 50.145, high: 50.19, low: 50.145, close: 50.175, volume: 64069, }, { date: 1461865440000, open: 50.175, high: 50.19, low: 50.175, close: 50.18, volume: 24022, }, { date: 1461865500000, open: 50.18, high: 50.19, low: 50.18, close: 50.1861, volume: 18160, }, { date: 1461865560000, open: 50.185, high: 50.19, low: 50.14, close: 50.145, volume: 28316, }, { date: 1461865620000, open: 50.14, high: 50.145, low: 50.115, close: 50.1301, volume: 73736, }, { date: 1461865680000, open: 50.14, high: 50.159, low: 50.135, close: 50.145, volume: 64202, }, { date: 1461865740000, open: 50.145, high: 50.16, low: 50.135, close: 50.155, volume: 70139, }, { date: 1461865800000, open: 50.16, high: 50.16, low: 50.13, close: 50.135, volume: 43965, }, { date: 1461865860000, open: 50.14, high: 50.195, low: 50.135, close: 50.19, volume: 50912, }, { date: 1461865920000, open: 50.1999, high: 50.22, low: 50.1928, close: 50.22, volume: 32539, }, { date: 1461865980000, open: 50.215, high: 50.22, low: 50.189, close: 50.1899, volume: 48503, }, { date: 1461866040000, open: 50.182, high: 50.195, low: 50.174, close: 50.1801, volume: 19968, }, { date: 1461866100000, open: 50.185, high: 50.19, low: 50.16, close: 50.18, volume: 44410, }, { date: 1461866160000, open: 50.185, high: 50.2, low: 50.18, close: 50.19, volume: 25592, }, { date: 1461866220000, open: 50.19, high: 50.19, low: 50.17, close: 50.17, volume: 57816, }, { date: 1461866280000, open: 50.1738, high: 50.176, low: 50.15, close: 50.15, volume: 42513, }, { date: 1461866340000, open: 50.15, high: 50.1599, low: 50.13, close: 50.15, volume: 110757, }, { date: 1461866400000, open: 50.15, high: 50.18, low: 50.14, close: 50.15, volume: 78105, }, { date: 1461866460000, open: 50.155, high: 50.195, low: 50.155, close: 50.18, volume: 82572, }, { date: 1461866520000, open: 50.165, high: 50.2, low: 50.13, close: 50.195, volume: 162461, }, { date: 1461866580000, open: 50.195, high: 50.24, low: 50.19, close: 50.24, volume: 209572, }, { date: 1461866640000, open: 50.24, high: 50.24, low: 50.21, close: 50.23, volume: 107050, }, { date: 1461866700000, open: 50.23, high: 50.23, low: 50.16, close: 50.18, volume: 87345, }, { date: 1461866760000, open: 50.185, high: 50.22, low: 50.18, close: 50.195, volume: 31133, }, { date: 1461866820000, open: 50.2, high: 50.235, low: 50.195, close: 50.21, volume: 38358, }, { date: 1461866880000, open: 50.21, high: 50.235, low: 50.2036, close: 50.23, volume: 23318, }, { date: 1461866940000, open: 50.23, high: 50.26, low: 50.185, close: 50.22, volume: 129826, }, { date: 1461867000000, open: 50.22, high: 50.26, low: 50.215, close: 50.245, volume: 99951, }, { date: 1461867060000, open: 50.2499, high: 50.25, low: 50.24, close: 50.24, volume: 32234, }, { date: 1461867120000, open: 50.24, high: 50.245, low: 50.19, close: 50.22, volume: 55861, }, { date: 1461867180000, open: 50.22, high: 50.245, low: 50.215, close: 50.2301, volume: 45346, }, { date: 1461867240000, open: 50.23, high: 50.25, low: 50.22, close: 50.24, volume: 34317, }, { date: 1461867300000, open: 50.23, high: 50.235, low: 50.21, close: 50.23, volume: 42900, }, { date: 1461867360000, open: 50.235, high: 50.25, low: 50.22, close: 50.235, volume: 56868, }, { date: 1461867420000, open: 50.2401, high: 50.25, low: 50.22, close: 50.2201, volume: 22535, }, { date: 1461867480000, open: 50.22, high: 50.24, low: 50.2, close: 50.23, volume: 30410, }, { date: 1461867540000, open: 50.2371, high: 50.24, low: 50.2, close: 50.215, volume: 43258, }, { date: 1461867600000, open: 50.2259, high: 50.23, low: 50.2, close: 50.2, volume: 33955, }, { date: 1461867660000, open: 50.2033, high: 50.2033, low: 50.155, close: 50.155, volume: 46794, }, { date: 1461867720000, open: 50.155, high: 50.18, low: 50.14, close: 50.145, volume: 130878, }, { date: 1461867780000, open: 50.14, high: 50.16, low: 50.1338, close: 50.14, volume: 57245, }, { date: 1461867840000, open: 50.14, high: 50.16, low: 50.12, close: 50.16, volume: 50818, }, { date: 1461867900000, open: 50.16, high: 50.2368, low: 50.15, close: 50.23, volume: 60481, }, { date: 1461867960000, open: 50.235, high: 50.235, low: 50.18, close: 50.19, volume: 41654, }, { date: 1461868020000, open: 50.19, high: 50.22, low: 50.185, close: 50.21, volume: 37632, }, { date: 1461868080000, open: 50.205, high: 50.225, low: 50.205, close: 50.225, volume: 29662, }, { date: 1461868140000, open: 50.224, high: 50.25, low: 50.22, close: 50.2264, volume: 56307, }, { date: 1461868200000, open: 50.225, high: 50.235, low: 50.22, close: 50.235, volume: 49232, }, { date: 1461868260000, open: 50.231, high: 50.2377, low: 50.21, close: 50.21, volume: 32028, }, { date: 1461868320000, open: 50.21, high: 50.22, low: 50.17, close: 50.17, volume: 67681, }, { date: 1461868380000, open: 50.17, high: 50.18, low: 50.16, close: 50.165, volume: 36089, }, { date: 1461868440000, open: 50.165, high: 50.17, low: 50.14, close: 50.145, volume: 47563, }, { date: 1461868500000, open: 50.145, high: 50.16, low: 50.14, close: 50.16, volume: 32011, }, { date: 1461868560000, open: 50.1562, high: 50.2, low: 50.155, close: 50.1895, volume: 43954, }, { date: 1461868620000, open: 50.18, high: 50.185, low: 50.13, close: 50.13, volume: 50706, }, { date: 1461868680000, open: 50.13, high: 50.14, low: 50.08, close: 50.11, volume: 159732, }, { date: 1461868740000, open: 50.11, high: 50.12, low: 50.1, close: 50.1095, volume: 45707, }, { date: 1461868800000, open: 50.105, high: 50.12, low: 50.09, close: 50.095, volume: 57589, }, { date: 1461868860000, open: 50.09, high: 50.12, low: 50.09, close: 50.115, volume: 47807, }, { date: 1461868920000, open: 50.12, high: 50.14, low: 50.1, close: 50.1015, volume: 64173, }, { date: 1461868980000, open: 50.11, high: 50.15, low: 50.09, close: 50.15, volume: 59761, }, { date: 1461869040000, open: 50.145, high: 50.16, low: 50.13, close: 50.13, volume: 39159, }, { date: 1461869100000, open: 50.135, high: 50.14, low: 50.09, close: 50.095, volume: 38707, }, { date: 1461869160000, open: 50.09, high: 50.09, low: 50.05, close: 50.07, volume: 150765, }, { date: 1461869220000, open: 50.075, high: 50.0899, low: 50.06, close: 50.0638, volume: 64144, }, { date: 1461869280000, open: 50.07, high: 50.08, low: 50.045, close: 50.0562, volume: 106589, }, { date: 1461869340000, open: 50.05, high: 50.06, low: 50.02, close: 50.021, volume: 123258, }, { date: 1461869400000, open: 50.025, high: 50.04, low: 50.02, close: 50.025, volume: 51810, }, { date: 1461869460000, open: 50.035, high: 50.0372, low: 50.01, close: 50.02, volume: 83725, }, { date: 1461869520000, open: 50.01, high: 50.02, low: 49.99, close: 50, volume: 486148, }, { date: 1461869580000, open: 49.993, high: 50, low: 49.96, close: 49.975, volume: 89504, }, { date: 1461869640000, open: 49.9762, high: 50.04, low: 49.95, close: 50.025, volume: 147532, }, { date: 1461869700000, open: 50.03, high: 50.0599, low: 50.02, close: 50.045, volume: 104896, }, { date: 1461869760000, open: 50.04, high: 50.05, low: 50, close: 50.003, volume: 75353, }, { date: 1461869820000, open: 50, high: 50.04, low: 49.98, close: 50.04, volume: 136096, }, { date: 1461869880000, open: 50.045, high: 50.09, low: 50.04, close: 50.085, volume: 59466, }, { date: 1461869940000, open: 50.085, high: 50.11, low: 50.05, close: 50.05, volume: 81348, }, { date: 1461870000000, open: 50.0565, high: 50.1, low: 50.045, close: 50.07, volume: 87537, }, { date: 1461870060000, open: 50.075, high: 50.085, low: 50.04, close: 50.05, volume: 94216, }, { date: 1461870120000, open: 50.05, high: 50.06, low: 50.04, close: 50.05, volume: 36160, }, { date: 1461870180000, open: 50.05, high: 50.05, low: 50.02, close: 50.035, volume: 59491, }, { date: 1461870240000, open: 50.04, high: 50.06, low: 50.03, close: 50.05, volume: 97251, }, { date: 1461870300000, open: 50.05, high: 50.05, low: 49.98, close: 50.005, volume: 110332, }, { date: 1461870360000, open: 50.01, high: 50.02, low: 49.975, close: 49.9801, volume: 52853, }, { date: 1461870420000, open: 49.985, high: 49.99, low: 49.94, close: 49.95, volume: 93732, }, { date: 1461870480000, open: 49.96, high: 49.98, low: 49.945, close: 49.975, volume: 72064, }, { date: 1461870540000, open: 49.9735, high: 49.98, low: 49.94, close: 49.955, volume: 132372, }, { date: 1461870600000, open: 49.955, high: 49.96, low: 49.9, close: 49.9, volume: 134507, }, { date: 1461870660000, open: 49.905, high: 49.91, low: 49.86, close: 49.885, volume: 129414, }, { date: 1461870720000, open: 49.88, high: 49.9, low: 49.85, close: 49.895, volume: 118341, }, { date: 1461870780000, open: 49.89, high: 49.9, low: 49.84, close: 49.844, volume: 62011, }, { date: 1461870840000, open: 49.85, high: 49.86, low: 49.8, close: 49.84, volume: 118913, }, { date: 1461870900000, open: 49.835, high: 49.86, low: 49.79, close: 49.8, volume: 184477, }, { date: 1461870960000, open: 49.8, high: 49.8, low: 49.74, close: 49.7713, volume: 329813, }, { date: 1461871020000, open: 49.78, high: 49.78, low: 49.74, close: 49.755, volume: 131549, }, { date: 1461871080000, open: 49.76, high: 49.79, low: 49.75, close: 49.77, volume: 77775, }, { date: 1461871140000, open: 49.77, high: 49.78, low: 49.67, close: 49.68, volume: 226597, }, { date: 1461871200000, open: 49.68, high: 49.7275, low: 49.67, close: 49.71, volume: 90263, }, { date: 1461871260000, open: 49.7, high: 49.71, low: 49.68, close: 49.6806, volume: 323146, }, { date: 1461871320000, open: 49.685, high: 49.69, low: 49.67, close: 49.67, volume: 65815, }, { date: 1461871380000, open: 49.6799, high: 49.68, low: 49.63, close: 49.68, volume: 467942, }, { date: 1461871440000, open: 49.68, high: 49.71, low: 49.65, close: 49.66, volume: 182616, }, { date: 1461871500000, open: 49.66, high: 49.67, low: 49.64, close: 49.645, volume: 154174, }, { date: 1461871560000, open: 49.65, high: 49.705, low: 49.56, close: 49.62, volume: 461561, }, { date: 1461871620000, open: 49.625, high: 49.69, low: 49.625, close: 49.67, volume: 137580, }, { date: 1461871680000, open: 49.6799, high: 49.71, low: 49.645, close: 49.695, volume: 248029, }, { date: 1461871740000, open: 49.695, high: 49.71, low: 49.65, close: 49.66, volume: 160612, }, { date: 1461871800000, open: 49.66, high: 49.69, low: 49.595, close: 49.63, volume: 254745, }, { date: 1461871860000, open: 49.63, high: 49.71, low: 49.6299, close: 49.695, volume: 228788, }, { date: 1461871920000, open: 49.6999, high: 49.7, low: 49.6337, close: 49.65, volume: 286179, }, { date: 1461871980000, open: 49.66, high: 49.67, low: 49.62, close: 49.645, volume: 113576, }, { date: 1461872040000, open: 49.64, high: 49.66, low: 49.595, close: 49.63, volume: 164349, }, { date: 1461872100000, open: 49.63, high: 49.68, low: 49.62, close: 49.68, volume: 254257, }, { date: 1461872160000, open: 49.6701, high: 49.72, low: 49.67, close: 49.72, volume: 221103, }, { date: 1461872220000, open: 49.715, high: 49.77, low: 49.71, close: 49.74, volume: 242301, }, { date: 1461872280000, open: 49.735, high: 49.75, low: 49.655, close: 49.7, volume: 432658, }, { date: 1461872340000, open: 49.7, high: 49.76, low: 49.695, close: 49.76, volume: 314640, }, { date: 1461872400000, open: 49.76, high: 49.79, low: 49.75, close: 49.78, volume: 301311, }, { date: 1461872460000, open: 49.7739, high: 49.83, low: 49.7739, close: 49.83, volume: 239502, }, { date: 1461872520000, open: 49.8228, high: 49.83, low: 49.75, close: 49.79, volume: 176981, }, { date: 1461872580000, open: 49.79, high: 49.825, low: 49.75, close: 49.8, volume: 250012, }, { date: 1461872640000, open: 49.8, high: 49.92, low: 49.77, close: 49.845, volume: 417051, }, { date: 1461872700000, open: 49.85, high: 49.86, low: 49.82, close: 49.83, volume: 186654, }, { date: 1461872760000, open: 49.835, high: 49.91, low: 49.825, close: 49.9, volume: 283596, }, { date: 1461872820000, open: 49.89, high: 49.94, low: 49.875, close: 49.92, volume: 435304, }, { date: 1461872880000, open: 49.92, high: 49.995, low: 49.91, close: 49.965, volume: 309432, }, { date: 1461872940000, open: 49.96, high: 49.97, low: 49.89, close: 49.945, volume: 513054, }, { date: 1461873000000, open: 49.945, high: 49.95, low: 49.89, close: 49.92, volume: 312862, }, { date: 1461873060000, open: 49.925, high: 49.93, low: 49.88, close: 49.89, volume: 316675, }, { date: 1461873120000, open: 49.89, high: 49.9, low: 49.86, close: 49.895, volume: 399632, }, { date: 1461873180000, open: 49.895, high: 49.91, low: 49.84, close: 49.86, volume: 327313, }, { date: 1461873240000, open: 49.865, high: 49.9, low: 49.85, close: 49.855, volume: 278717, }, { date: 1461873300000, open: 49.8599, high: 49.87, low: 49.82, close: 49.865, volume: 225020, }, { date: 1461873360000, open: 49.86, high: 49.8681, low: 49.82, close: 49.835, volume: 293086, }, { date: 1461873420000, open: 49.835, high: 49.84, low: 49.79, close: 49.795, volume: 264958, }, { date: 1461873480000, open: 49.801, high: 49.81, low: 49.75, close: 49.775, volume: 311663, }, { date: 1461873540000, open: 49.785, high: 49.84, low: 49.775, close: 49.81, volume: 399231, }, { date: 1461873600000, open: 49.835, high: 49.97, low: 49.8, close: 49.93, volume: 2816676, }, { date: 1461936600000, open: 49.35, high: 49.45, low: 49.35, close: 49.418, volume: 1387226, }, { date: 1461936660000, open: 49.4299, high: 50.03, low: 49.41, close: 50.01, volume: 526715, }, { date: 1461936720000, open: 50.01, high: 50.02, low: 49.66, close: 49.67, volume: 311338, }, { date: 1461936780000, open: 49.68, high: 49.68, low: 49.51, close: 49.58, volume: 196149, }, { date: 1461936840000, open: 49.57, high: 49.77, low: 49.57, close: 49.73, volume: 170026, }, { date: 1461936900000, open: 49.731, high: 49.79, low: 49.68, close: 49.7811, volume: 90592, }, { date: 1461936960000, open: 49.79, high: 49.83, low: 49.76, close: 49.78, volume: 169369, }, { date: 1461937020000, open: 49.7701, high: 49.845, low: 49.7701, close: 49.84, volume: 199125, }, { date: 1461937080000, open: 49.84, high: 49.89, low: 49.82, close: 49.87, volume: 197767, }, { date: 1461937140000, open: 49.87, high: 49.885, low: 49.83, close: 49.87, volume: 198041, }, { date: 1461937200000, open: 49.87, high: 49.91, low: 49.83, close: 49.9, volume: 215750, }, { date: 1461937260000, open: 49.91, high: 50.01, low: 49.9, close: 50.005, volume: 224804, }, { date: 1461937320000, open: 50.005, high: 50.02, low: 49.9, close: 49.93, volume: 265413, }, { date: 1461937380000, open: 49.93, high: 50, low: 49.905, close: 49.96, volume: 351369, }, { date: 1461937440000, open: 49.96, high: 50.04, low: 49.96, close: 50.01, volume: 258349, }, { date: 1461937500000, open: 50.01, high: 50.1, low: 50, close: 50.015, volume: 202308, }, { date: 1461937560000, open: 50.01, high: 50.06, low: 49.99, close: 50.05, volume: 177037, }, { date: 1461937620000, open: 50.055, high: 50.055, low: 49.97, close: 49.97, volume: 236185, }, { date: 1461937680000, open: 49.97, high: 49.975, low: 49.9001, close: 49.93, volume: 152517, }, { date: 1461937740000, open: 49.925, high: 49.94, low: 49.81, close: 49.84, volume: 163153, }, { date: 1461937800000, open: 49.8599, high: 49.93, low: 49.85, close: 49.8699, volume: 100197, }, { date: 1461937860000, open: 49.86, high: 49.92, low: 49.775, close: 49.82, volume: 200154, }, { date: 1461937920000, open: 49.8299, high: 49.8299, low: 49.69, close: 49.74, volume: 128728, }, { date: 1461937980000, open: 49.735, high: 49.82, low: 49.7302, close: 49.763, volume: 100055, }, { date: 1461938040000, open: 49.76, high: 49.8, low: 49.7528, close: 49.795, volume: 95844, }, { date: 1461938100000, open: 49.795, high: 49.9, low: 49.79, close: 49.885, volume: 122257, }, { date: 1461938160000, open: 49.8801, high: 49.92, low: 49.85, close: 49.865, volume: 112947, }, { date: 1461938220000, open: 49.865, high: 49.9, low: 49.81, close: 49.9, volume: 165915, }, { date: 1461938280000, open: 49.89, high: 49.97, low: 49.89, close: 49.895, volume: 142760, }, { date: 1461938340000, open: 49.9, high: 49.905, low: 49.825, close: 49.85, volume: 83922, }, { date: 1461938400000, open: 49.85, high: 49.8653, low: 49.79, close: 49.81, volume: 68251, }, { date: 1461938460000, open: 49.81, high: 49.83, low: 49.77, close: 49.81, volume: 160381, }, { date: 1461938520000, open: 49.81, high: 49.81, low: 49.7, close: 49.71, volume: 120270, }, { date: 1461938580000, open: 49.7001, high: 49.82, low: 49.69, close: 49.79, volume: 104701, }, { date: 1461938640000, open: 49.785, high: 49.91, low: 49.77, close: 49.91, volume: 181987, }, { date: 1461938700000, open: 49.9, high: 49.94, low: 49.87, close: 49.91, volume: 90937, }, { date: 1461938760000, open: 49.91, high: 49.92, low: 49.84, close: 49.84, volume: 95593, }, { date: 1461938820000, open: 49.835, high: 49.89, low: 49.83, close: 49.88, volume: 58929, }, { date: 1461938880000, open: 49.88, high: 49.895, low: 49.785, close: 49.895, volume: 68370, }, { date: 1461938940000, open: 49.89, high: 49.92, low: 49.86, close: 49.86, volume: 78053, }, { date: 1461939000000, open: 49.865, high: 49.895, low: 49.86, close: 49.88, volume: 94555, }, { date: 1461939060000, open: 49.885, high: 49.97, low: 49.88, close: 49.9475, volume: 163396, }, { date: 1461939120000, open: 49.95, high: 49.97, low: 49.94, close: 49.95, volume: 102980, }, { date: 1461939180000, open: 49.94, high: 49.97, low: 49.83, close: 49.845, volume: 138107, }, { date: 1461939240000, open: 49.85, high: 49.905, low: 49.82, close: 49.9, volume: 114194, }, { date: 1461939300000, open: 49.91, high: 49.92, low: 49.8, close: 49.82, volume: 105946, }, { date: 1461939360000, open: 49.825, high: 49.825, low: 49.73, close: 49.74, volume: 121197, }, { date: 1461939420000, open: 49.745, high: 49.79, low: 49.745, close: 49.78, volume: 93801, }, { date: 1461939480000, open: 49.78, high: 49.78, low: 49.74, close: 49.75, volume: 116580, }, { date: 1461939540000, open: 49.75, high: 49.75, low: 49.68, close: 49.69, volume: 174268, }, { date: 1461939600000, open: 49.685, high: 49.74, low: 49.66, close: 49.67, volume: 124858, }, { date: 1461939660000, open: 49.6725, high: 49.68, low: 49.64, close: 49.66, volume: 146532, }, { date: 1461939720000, open: 49.665, high: 49.67, low: 49.5901, close: 49.6, volume: 89172, }, { date: 1461939780000, open: 49.6, high: 49.71, low: 49.56, close: 49.6956, volume: 164944, }, { date: 1461939840000, open: 49.7, high: 49.78, low: 49.67, close: 49.7709, volume: 118238, }, { date: 1461939900000, open: 49.78, high: 49.78, low: 49.72, close: 49.72, volume: 102968, }, { date: 1461939960000, open: 49.72, high: 49.73, low: 49.63, close: 49.65, volume: 68897, }, { date: 1461940020000, open: 49.66, high: 49.73, low: 49.62, close: 49.64, volume: 139744, }, { date: 1461940080000, open: 49.6399, high: 49.655, low: 49.565, close: 49.63, volume: 113362, }, { date: 1461940140000, open: 49.63, high: 49.73, low: 49.63, close: 49.72, volume: 133401, }, { date: 1461940200000, open: 49.72, high: 49.74, low: 49.665, close: 49.71, volume: 134562, }, { date: 1461940260000, open: 49.72, high: 49.77, low: 49.7, close: 49.76, volume: 124672, }, { date: 1461940320000, open: 49.75, high: 49.75, low: 49.71, close: 49.725, volume: 97009, }, { date: 1461940380000, open: 49.705, high: 49.76, low: 49.69, close: 49.7329, volume: 110163, }, { date: 1461940440000, open: 49.735, high: 49.765, low: 49.72, close: 49.72, volume: 94168, }, { date: 1461940500000, open: 49.725, high: 49.7756, low: 49.72, close: 49.7756, volume: 103340, }, { date: 1461940560000, open: 49.77, high: 49.86, low: 49.76, close: 49.8399, volume: 156331, }, { date: 1461940620000, open: 49.835, high: 49.9, low: 49.83, close: 49.85, volume: 136792, }, { date: 1461940680000, open: 49.85, high: 49.97, low: 49.84, close: 49.94, volume: 180050, }, { date: 1461940740000, open: 49.94, high: 49.95, low: 49.92, close: 49.94, volume: 138543, }, { date: 1461940800000, open: 49.94, high: 49.95, low: 49.9, close: 49.95, volume: 76543, }, { date: 1461940860000, open: 49.95, high: 49.985, low: 49.91, close: 49.9715, volume: 263974, }, { date: 1461940920000, open: 49.975, high: 49.98, low: 49.92, close: 49.935, volume: 147240, }, { date: 1461940980000, open: 49.93, high: 49.965, low: 49.92, close: 49.93, volume: 124468, }, { date: 1461941040000, open: 49.94, high: 49.94, low: 49.865, close: 49.91, volume: 141272, }, { date: 1461941100000, open: 49.91, high: 49.97, low: 49.905, close: 49.96, volume: 128975, }, { date: 1461941160000, open: 49.965, high: 50.02, low: 49.95, close: 49.98, volume: 319403, }, { date: 1461941220000, open: 49.99, high: 50.065, low: 49.98, close: 50.05, volume: 264452, }, { date: 1461941280000, open: 50.05, high: 50.075, low: 50.03, close: 50.06, volume: 369081, }, { date: 1461941340000, open: 50.0595, high: 50.1, low: 50.04, close: 50.0425, volume: 177629, }, { date: 1461941400000, open: 50.0499, high: 50.09, low: 50.04, close: 50.06, volume: 112670, }, { date: 1461941460000, open: 50.07, high: 50.08, low: 50.025, close: 50.03, volume: 348865, }, { date: 1461941520000, open: 50.02, high: 50.03, low: 49.91, close: 49.91, volume: 163438, }, { date: 1461941580000, open: 49.913, high: 49.97, low: 49.9, close: 49.9, volume: 151751, }, { date: 1461941640000, open: 49.9, high: 49.93, low: 49.84, close: 49.85, volume: 138713, }, { date: 1461941700000, open: 49.84, high: 49.87, low: 49.81, close: 49.86, volume: 85222, }, { date: 1461941760000, open: 49.86, high: 49.92, low: 49.85, close: 49.89, volume: 193365, }, { date: 1461941820000, open: 49.9, high: 49.9, low: 49.855, close: 49.88, volume: 150885, }, { date: 1461941880000, open: 49.875, high: 49.91, low: 49.86, close: 49.9, volume: 189905, }, { date: 1461941940000, open: 49.895, high: 49.92, low: 49.88, close: 49.894, volume: 63803, }, { date: 1461942000000, open: 49.9, high: 49.95, low: 49.895, close: 49.93, volume: 97198, }, { date: 1461942060000, open: 49.92, high: 49.95, low: 49.91, close: 49.9443, volume: 82026, }, { date: 1461942120000, open: 49.95, high: 49.96, low: 49.93, close: 49.945, volume: 111763, }, { date: 1461942180000, open: 49.95, high: 49.96, low: 49.8, close: 49.83, volume: 124397, }, { date: 1461942240000, open: 49.84, high: 49.87, low: 49.82, close: 49.83, volume: 100596, }, { date: 1461942300000, open: 49.83, high: 49.89, low: 49.83, close: 49.845, volume: 50490, }, { date: 1461942360000, open: 49.845, high: 49.89, low: 49.84, close: 49.885, volume: 55919, }, { date: 1461942420000, open: 49.89, high: 49.92, low: 49.87, close: 49.875, volume: 68317, }, { date: 1461942480000, open: 49.875, high: 49.92, low: 49.8701, close: 49.905, volume: 47920, }, { date: 1461942540000, open: 49.905, high: 49.91, low: 49.85, close: 49.865, volume: 51570, }, { date: 1461942600000, open: 49.8665, high: 49.92, low: 49.865, close: 49.91, volume: 74049, }, { date: 1461942660000, open: 49.91, high: 49.92, low: 49.87, close: 49.885, volume: 96474, }, { date: 1461942720000, open: 49.89, high: 49.91, low: 49.88, close: 49.89, volume: 126110, }, { date: 1461942780000, open: 49.9, high: 49.91, low: 49.875, close: 49.905, volume: 57802, }, { date: 1461942840000, open: 49.905, high: 49.94, low: 49.9, close: 49.94, volume: 91013, }, { date: 1461942900000, open: 49.935, high: 49.97, low: 49.93, close: 49.9499, volume: 107649, }, { date: 1461942960000, open: 49.945, high: 49.95, low: 49.8741, close: 49.8799, volume: 74571, }, { date: 1461943020000, open: 49.879, high: 49.94, low: 49.87, close: 49.885, volume: 173823, }, { date: 1461943080000, open: 49.88, high: 49.9, low: 49.84, close: 49.875, volume: 118725, }, { date: 1461943140000, open: 49.88, high: 49.92, low: 49.87, close: 49.92, volume: 174945, }, { date: 1461943200000, open: 49.915, high: 49.92, low: 49.84, close: 49.88, volume: 90880, }, { date: 1461943260000, open: 49.88, high: 49.93, low: 49.87, close: 49.92, volume: 182376, }, { date: 1461943320000, open: 49.915, high: 49.93, low: 49.89, close: 49.91, volume: 167685, }, { date: 1461943380000, open: 49.905, high: 49.91, low: 49.89, close: 49.9, volume: 140784, }, { date: 1461943440000, open: 49.89, high: 49.92, low: 49.89, close: 49.91, volume: 58280, }, { date: 1461943500000, open: 49.915, high: 49.92, low: 49.845, close: 49.855, volume: 114051, }, { date: 1461943560000, open: 49.87, high: 49.9, low: 49.86, close: 49.88, volume: 62409, }, { date: 1461943620000, open: 49.88, high: 49.91, low: 49.84, close: 49.845, volume: 102269, }, { date: 1461943680000, open: 49.85, high: 49.9, low: 49.85, close: 49.9, volume: 58846, }, { date: 1461943740000, open: 49.895, high: 49.9, low: 49.87, close: 49.88, volume: 85893, }, { date: 1461943800000, open: 49.86, high: 49.87, low: 49.8, close: 49.82, volume: 159479, }, { date: 1461943860000, open: 49.82, high: 49.82, low: 49.79, close: 49.8, volume: 115453, }, { date: 1461943920000, open: 49.81, high: 49.81, low: 49.76, close: 49.77, volume: 96813, }, { date: 1461943980000, open: 49.765, high: 49.8, low: 49.75, close: 49.76, volume: 52387, }, { date: 1461944040000, open: 49.75, high: 49.79, low: 49.75, close: 49.78, volume: 66012, }, { date: 1461944100000, open: 49.7743, high: 49.84, low: 49.77, close: 49.81, volume: 125751, }, { date: 1461944160000, open: 49.81, high: 49.82, low: 49.77, close: 49.77, volume: 82283, }, { date: 1461944220000, open: 49.77, high: 49.78, low: 49.745, close: 49.775, volume: 132913, }, { date: 1461944280000, open: 49.78, high: 49.8176, low: 49.77, close: 49.78, volume: 75727, }, { date: 1461944340000, open: 49.785, high: 49.81, low: 49.775, close: 49.785, volume: 73941, }, { date: 1461944400000, open: 49.785, high: 49.81, low: 49.76, close: 49.8, volume: 113801, }, { date: 1461944460000, open: 49.791, high: 49.795, low: 49.745, close: 49.75, volume: 123932, }, { date: 1461944520000, open: 49.755, high: 49.805, low: 49.75, close: 49.795, volume: 55178, }, { date: 1461944580000, open: 49.7935, high: 49.81, low: 49.75, close: 49.7601, volume: 110463, }, { date: 1461944640000, open: 49.769, high: 49.8, low: 49.75, close: 49.795, volume: 63646, }, { date: 1461944700000, open: 49.8, high: 49.805, low: 49.7425, close: 49.77, volume: 133547, }, { date: 1461944760000, open: 49.7742, high: 49.83, low: 49.76, close: 49.79, volume: 125299, }, { date: 1461944820000, open: 49.795, high: 49.795, low: 49.74, close: 49.76, volume: 165034, }, { date: 1461944880000, open: 49.76, high: 49.81, low: 49.73, close: 49.8, volume: 142208, }, { date: 1461944940000, open: 49.81, high: 49.8171, low: 49.78, close: 49.805, volume: 68582, }, { date: 1461945000000, open: 49.81, high: 49.838, low: 49.8, close: 49.83, volume: 81295, }, { date: 1461945060000, open: 49.83, high: 49.835, low: 49.79, close: 49.82, volume: 95238, }, { date: 1461945120000, open: 49.82, high: 49.9, low: 49.8, close: 49.89, volume: 143967, }, { date: 1461945180000, open: 49.895, high: 49.9, low: 49.85, close: 49.88, volume: 274013, }, { date: 1461945240000, open: 49.88, high: 49.92, low: 49.84, close: 49.88, volume: 118868, }, { date: 1461945300000, open: 49.885, high: 49.89, low: 49.83, close: 49.83, volume: 95868, }, { date: 1461945360000, open: 49.835, high: 49.88, low: 49.815, close: 49.875, volume: 79386, }, { date: 1461945420000, open: 49.875, high: 49.9, low: 49.845, close: 49.855, volume: 140067, }, { date: 1461945480000, open: 49.86, high: 49.9, low: 49.86, close: 49.89, volume: 36490, }, { date: 1461945540000, open: 49.8991, high: 49.9, low: 49.87, close: 49.9, volume: 42814, }, { date: 1461945600000, open: 49.9, high: 49.91, low: 49.87, close: 49.8865, volume: 59328, }, { date: 1461945660000, open: 49.875, high: 49.92, low: 49.86, close: 49.91, volume: 68964, }, { date: 1461945720000, open: 49.91, high: 49.94, low: 49.885, close: 49.925, volume: 99796, }, { date: 1461945780000, open: 49.9285, high: 49.945, low: 49.91, close: 49.92, volume: 84886, }, { date: 1461945840000, open: 49.92, high: 49.93, low: 49.91, close: 49.921, volume: 30362, }, { date: 1461945900000, open: 49.9295, high: 49.95, low: 49.9, close: 49.95, volume: 36497, }, { date: 1461945960000, open: 49.93, high: 49.95, low: 49.92, close: 49.93, volume: 54169, }, { date: 1461946020000, open: 49.9365, high: 49.99, low: 49.93, close: 49.98, volume: 79203, }, { date: 1461946080000, open: 49.98, high: 50.02, low: 49.97, close: 50.005, volume: 140715, }, { date: 1461946140000, open: 50, high: 50.005, low: 49.96, close: 49.975, volume: 51806, }, { date: 1461946200000, open: 49.97, high: 49.98, low: 49.93, close: 49.97, volume: 52951, }, { date: 1461946260000, open: 49.96, high: 49.97, low: 49.945, close: 49.96, volume: 99087, }, { date: 1461946320000, open: 49.96, high: 49.97, low: 49.91, close: 49.94, volume: 47486, }, { date: 1461946380000, open: 49.94, high: 49.95, low: 49.91, close: 49.92, volume: 22154, }, { date: 1461946440000, open: 49.91, high: 49.94, low: 49.9, close: 49.935, volume: 34510, }, { date: 1461946500000, open: 49.93, high: 49.94, low: 49.84, close: 49.85, volume: 133807, }, { date: 1461946560000, open: 49.84, high: 49.935, low: 49.83, close: 49.93, volume: 133528, }, { date: 1461946620000, open: 49.92, high: 49.97, low: 49.92, close: 49.9546, volume: 77671, }, { date: 1461946680000, open: 49.955, high: 49.99, low: 49.935, close: 49.98, volume: 79088, }, { date: 1461946740000, open: 49.9701, high: 50.005, low: 49.955, close: 50, volume: 29000, }, { date: 1461946800000, open: 50.0065, high: 50.035, low: 49.98, close: 50.01, volume: 104832, }, { date: 1461946860000, open: 50.015, high: 50.0499, low: 50.0101, close: 50.035, volume: 99183, }, { date: 1461946920000, open: 50.035, high: 50.07, low: 50.025, close: 50.07, volume: 54127, }, { date: 1461946980000, open: 50.075, high: 50.075, low: 50.034, close: 50.04, volume: 79489, }, { date: 1461947040000, open: 50.05, high: 50.06, low: 50.02, close: 50.03, volume: 38547, }, { date: 1461947100000, open: 50.03, high: 50.04, low: 49.995, close: 50, volume: 92969, }, { date: 1461947160000, open: 50.005, high: 50.01, low: 49.96, close: 50, volume: 89250, }, { date: 1461947220000, open: 50, high: 50.01, low: 49.98, close: 49.98, volume: 31219, }, { date: 1461947280000, open: 49.985, high: 50.02, low: 49.98, close: 50.005, volume: 47515, }, { date: 1461947340000, open: 50.005, high: 50.01, low: 49.95, close: 49.9501, volume: 28363, }, { date: 1461947400000, open: 49.95, high: 49.98, low: 49.93, close: 49.96, volume: 56381, }, { date: 1461947460000, open: 49.97, high: 49.97, low: 49.92, close: 49.93, volume: 47276, }, { date: 1461947520000, open: 49.93, high: 49.955, low: 49.9, close: 49.9, volume: 62762, }, { date: 1461947580000, open: 49.909, high: 49.95, low: 49.9, close: 49.9336, volume: 29240, }, { date: 1461947640000, open: 49.9372, high: 49.96, low: 49.93, close: 49.948, volume: 68407, }, { date: 1461947700000, open: 49.945, high: 49.98, low: 49.935, close: 49.965, volume: 51583, }, { date: 1461947760000, open: 49.965, high: 49.975, low: 49.92, close: 49.92, volume: 29182, }, { date: 1461947820000, open: 49.93, high: 49.98, low: 49.92, close: 49.975, volume: 111894, }, { date: 1461947880000, open: 49.97, high: 50.005, low: 49.965, close: 50.005, volume: 102501, }, { date: 1461947940000, open: 50.0099, high: 50.07, low: 50.0001, close: 50.005, volume: 141342, }, { date: 1461948000000, open: 50.015, high: 50.0199, low: 49.96, close: 49.985, volume: 54416, }, { date: 1461948060000, open: 49.985, high: 50.005, low: 49.96, close: 49.97, volume: 90262, }, { date: 1461948120000, open: 49.965, high: 50.01, low: 49.96, close: 49.99, volume: 73404, }, { date: 1461948180000, open: 49.995, high: 50.005, low: 49.96, close: 49.985, volume: 136687, }, { date: 1461948240000, open: 49.985, high: 50.02, low: 49.97, close: 49.99, volume: 108807, }, { date: 1461948300000, open: 49.9963, high: 50.01, low: 49.98, close: 49.99, volume: 48222, }, { date: 1461948360000, open: 49.995, high: 50.03, low: 49.99, close: 50.025, volume: 86664, }, { date: 1461948420000, open: 50.026, high: 50.03, low: 49.98, close: 49.9838, volume: 65418, }, { date: 1461948480000, open: 49.98, high: 50.03, low: 49.97, close: 50.025, volume: 48791, }, { date: 1461948540000, open: 50.03, high: 50.03, low: 50.01, close: 50.025, volume: 21000, }, { date: 1461948600000, open: 50.02, high: 50.03, low: 49.98, close: 50.0136, volume: 55789, }, { date: 1461948660000, open: 50.025, high: 50.035, low: 49.99, close: 50, volume: 67724, }, { date: 1461948720000, open: 50, high: 50.02, low: 49.99, close: 49.99, volume: 72739, }, { date: 1461948780000, open: 49.99, high: 49.995, low: 49.94, close: 49.985, volume: 93933, }, { date: 1461948840000, open: 49.99, high: 50.02, low: 49.98, close: 50.01, volume: 117548, }, { date: 1461948900000, open: 50.01, high: 50.03, low: 50, close: 50.015, volume: 43516, }, { date: 1461948960000, open: 50.015, high: 50.02, low: 49.99, close: 50.005, volume: 69207, }, { date: 1461949020000, open: 50.0001, high: 50.02, low: 50, close: 50, volume: 83415, }, { date: 1461949080000, open: 50.005, high: 50.015, low: 50, close: 50, volume: 70883, }, { date: 1461949140000, open: 50.009, high: 50.01, low: 49.97, close: 49.985, volume: 172680, }, { date: 1461949200000, open: 49.985, high: 50, low: 49.982, close: 49.982, volume: 25599, }, { date: 1461949260000, open: 49.9865, high: 49.99, low: 49.92, close: 49.965, volume: 152720, }, { date: 1461949320000, open: 49.965, high: 50.02, low: 49.965, close: 49.985, volume: 91256, }, { date: 1461949380000, open: 50.015, high: 50.02, low: 49.985, close: 50.0052, volume: 30968, }, { date: 1461949440000, open: 50.005, high: 50.01, low: 50, close: 50.005, volume: 19010, }, { date: 1461949500000, open: 50.005, high: 50.005, low: 49.98, close: 49.99, volume: 39816, }, { date: 1461949560000, open: 49.99, high: 50.03, low: 49.99, close: 50.025, volume: 41340, }, { date: 1461949620000, open: 50.03, high: 50.05, low: 50.02, close: 50.04, volume: 66272, }, { date: 1461949680000, open: 50.04, high: 50.04, low: 50.02, close: 50.02, volume: 23149, }, { date: 1461949740000, open: 50.02, high: 50.03, low: 49.98, close: 49.98, volume: 27116, }, { date: 1461949800000, open: 49.98, high: 50.017, low: 49.98, close: 49.99, volume: 81019, }, { date: 1461949860000, open: 49.99, high: 50.005, low: 49.98, close: 50, volume: 101296, }, { date: 1461949920000, open: 50, high: 50.04, low: 49.985, close: 50.04, volume: 68391, }, { date: 1461949980000, open: 50.025, high: 50.06, low: 50.025, close: 50.055, volume: 110100, }, { date: 1461950040000, open: 50.05, high: 50.1, low: 50.05, close: 50.085, volume: 85780, }, { date: 1461950100000, open: 50.08, high: 50.13, low: 50.075, close: 50.11, volume: 83481, }, { date: 1461950160000, open: 50.1135, high: 50.12, low: 50.08, close: 50.12, volume: 54365, }, { date: 1461950220000, open: 50.13, high: 50.13, low: 50.095, close: 50.114, volume: 48590, }, { date: 1461950280000, open: 50.11, high: 50.12, low: 50.1, close: 50.115, volume: 20738, }, { date: 1461950340000, open: 50.115, high: 50.14, low: 50.11, close: 50.135, volume: 33748, }, { date: 1461950400000, open: 50.1301, high: 50.21, low: 50.13, close: 50.205, volume: 98890, }, { date: 1461950460000, open: 50.2, high: 50.205, low: 50.16, close: 50.199, volume: 87437, }, { date: 1461950520000, open: 50.19, high: 50.21, low: 50.19, close: 50.205, volume: 50827, }, { date: 1461950580000, open: 50.205, high: 50.21, low: 50.17, close: 50.175, volume: 92346, }, { date: 1461950640000, open: 50.17, high: 50.22, low: 50.165, close: 50.21, volume: 139257, }, { date: 1461950700000, open: 50.21, high: 50.25, low: 50.21, close: 50.23, volume: 80543, }, { date: 1461950760000, open: 50.235, high: 50.24, low: 50.19, close: 50.195, volume: 77505, }, { date: 1461950820000, open: 50.195, high: 50.2, low: 50.19, close: 50.195, volume: 16507, }, { date: 1461950880000, open: 50.193, high: 50.205, low: 50.18, close: 50.205, volume: 74506, }, { date: 1461950940000, open: 50.205, high: 50.22, low: 50.2, close: 50.215, volume: 84884, }, { date: 1461951000000, open: 50.215, high: 50.23, low: 50.19, close: 50.211, volume: 81080, }, { date: 1461951060000, open: 50.215, high: 50.23, low: 50.18, close: 50.185, volume: 75343, }, { date: 1461951120000, open: 50.185, high: 50.2, low: 50.17, close: 50.175, volume: 160468, }, { date: 1461951180000, open: 50.1799, high: 50.2, low: 50.17, close: 50.195, volume: 64476, }, { date: 1461951240000, open: 50.2, high: 50.2, low: 50.18, close: 50.195, volume: 51404, }, { date: 1461951300000, open: 50.2, high: 50.2, low: 50.16, close: 50.1799, volume: 64151, }, { date: 1461951360000, open: 50.17, high: 50.18, low: 50.17, close: 50.175, volume: 26224, }, { date: 1461951420000, open: 50.175, high: 50.2, low: 50.17, close: 50.19, volume: 63732, }, { date: 1461951480000, open: 50.195, high: 50.195, low: 50.12, close: 50.12, volume: 94158, }, { date: 1461951540000, open: 50.125, high: 50.1263, low: 50.09, close: 50.099, volume: 39181, }, { date: 1461951600000, open: 50.095, high: 50.13, low: 50.0701, close: 50.125, volume: 74251, }, { date: 1461951660000, open: 50.121, high: 50.125, low: 50.06, close: 50.085, volume: 39262, }, { date: 1461951720000, open: 50.0861, high: 50.0871, low: 50.055, close: 50.065, volume: 52958, }, { date: 1461951780000, open: 50.07, high: 50.07, low: 50.02, close: 50.025, volume: 23348, }, { date: 1461951840000, open: 50.02, high: 50.046, low: 50.0101, close: 50.0271, volume: 35909, }, { date: 1461951900000, open: 50.0299, high: 50.05, low: 50.02, close: 50.035, volume: 30602, }, { date: 1461951960000, open: 50.035, high: 50.05, low: 50.025, close: 50.03, volume: 108355, }, { date: 1461952020000, open: 50.04, high: 50.06, low: 50.025, close: 50.036, volume: 67288, }, { date: 1461952080000, open: 50.0335, high: 50.04, low: 50.005, close: 50.03, volume: 90342, }, { date: 1461952140000, open: 50.03, high: 50.06, low: 50.01, close: 50.06, volume: 59028, }, { date: 1461952200000, open: 50.06, high: 50.07, low: 50.045, close: 50.055, volume: 32135, }, { date: 1461952260000, open: 50.06, high: 50.07, low: 50.035, close: 50.035, volume: 29260, }, { date: 1461952320000, open: 50.035, high: 50.035, low: 50, close: 50.005, volume: 40081, }, { date: 1461952380000, open: 50.005, high: 50.05, low: 50.0035, close: 50.035, volume: 73310, }, { date: 1461952440000, open: 50.035, high: 50.05, low: 50.03, close: 50.045, volume: 19755, }, { date: 1461952500000, open: 50.045, high: 50.06, low: 50.02, close: 50.02, volume: 42459, }, { date: 1461952560000, open: 50.02, high: 50.02, low: 49.96, close: 49.96, volume: 93467, }, { date: 1461952620000, open: 49.96, high: 50.01, low: 49.93, close: 49.995, volume: 120296, }, { date: 1461952680000, open: 49.995, high: 50.01, low: 49.99, close: 49.99, volume: 36976, }, { date: 1461952740000, open: 49.99, high: 49.995, low: 49.96, close: 49.975, volume: 66270, }, { date: 1461952800000, open: 49.98, high: 50, low: 49.96, close: 49.985, volume: 56904, }, { date: 1461952860000, open: 49.98, high: 50.025, low: 49.97, close: 50.02, volume: 39113, }, { date: 1461952920000, open: 50.02, high: 50.025, low: 50, close: 50.005, volume: 32349, }, { date: 1461952980000, open: 50.005, high: 50.02, low: 49.99, close: 50.02, volume: 48536, }, { date: 1461953040000, open: 50.015, high: 50.025, low: 49.98, close: 50.015, volume: 72174, }, { date: 1461953100000, open: 50.0103, high: 50.05, low: 50, close: 50.05, volume: 83286, }, { date: 1461953160000, open: 50.0438, high: 50.045, low: 50, close: 50, volume: 22414, }, { date: 1461953220000, open: 50, high: 50.01, low: 49.97, close: 49.9742, volume: 83123, }, { date: 1461953280000, open: 49.975, high: 49.975, low: 49.93, close: 49.94, volume: 61880, }, { date: 1461953340000, open: 49.935, high: 49.97, low: 49.92, close: 49.965, volume: 82903, }, { date: 1461953400000, open: 49.96, high: 49.97, low: 49.9, close: 49.905, volume: 105020, }, { date: 1461953460000, open: 49.905, high: 49.91, low: 49.88, close: 49.88, volume: 33930, }, { date: 1461953520000, open: 49.88, high: 49.9262, low: 49.87, close: 49.92, volume: 93477, }, { date: 1461953580000, open: 49.92, high: 49.9565, low: 49.915, close: 49.95, volume: 87818, }, { date: 1461953640000, open: 49.9499, high: 49.9499, low: 49.905, close: 49.905, volume: 31184, }, { date: 1461953700000, open: 49.9099, high: 49.94, low: 49.89, close: 49.94, volume: 37747, }, { date: 1461953760000, open: 49.94, high: 49.95, low: 49.89, close: 49.9, volume: 32447, }, { date: 1461953820000, open: 49.9059, high: 49.935, low: 49.9, close: 49.915, volume: 82250, }, { date: 1461953880000, open: 49.915, high: 49.92, low: 49.86, close: 49.895, volume: 78814, }, { date: 1461953940000, open: 49.9, high: 49.94, low: 49.89, close: 49.925, volume: 103250, }, { date: 1461954000000, open: 49.93, high: 49.94, low: 49.925, close: 49.935, volume: 10734, }, { date: 1461954060000, open: 49.935, high: 49.96, low: 49.93, close: 49.935, volume: 83336, }, { date: 1461954120000, open: 49.93, high: 49.945, low: 49.91, close: 49.9108, volume: 63628, }, { date: 1461954180000, open: 49.92, high: 49.9799, low: 49.915, close: 49.955, volume: 46953, }, { date: 1461954240000, open: 49.956, high: 49.97, low: 49.95, close: 49.965, volume: 25481, }, { date: 1461954300000, open: 49.965, high: 49.97, low: 49.92, close: 49.925, volume: 48359, }, { date: 1461954360000, open: 49.925, high: 49.93, low: 49.88, close: 49.91, volume: 71525, }, { date: 1461954420000, open: 49.915, high: 49.93, low: 49.89, close: 49.899, volume: 21739, }, { date: 1461954480000, open: 49.89, high: 49.95, low: 49.89, close: 49.92, volume: 91562, }, { date: 1461954540000, open: 49.925, high: 49.93, low: 49.89, close: 49.9, volume: 39383, }, { date: 1461954600000, open: 49.905, high: 49.955, low: 49.9, close: 49.9, volume: 61012, }, { date: 1461954660000, open: 49.9, high: 49.9, low: 49.86, close: 49.88, volume: 41009, }, { date: 1461954720000, open: 49.88, high: 49.88, low: 49.83, close: 49.835, volume: 40651, }, { date: 1461954780000, open: 49.83, high: 49.8723, low: 49.805, close: 49.845, volume: 47983, }, { date: 1461954840000, open: 49.85, high: 49.8701, low: 49.835, close: 49.84, volume: 33978, }, { date: 1461954900000, open: 49.84, high: 49.875, low: 49.84, close: 49.865, volume: 22208, }, { date: 1461954960000, open: 49.86, high: 49.86, low: 49.83, close: 49.84, volume: 37461, }, { date: 1461955020000, open: 49.84, high: 49.85, low: 49.82, close: 49.835, volume: 36563, }, { date: 1461955080000, open: 49.835, high: 49.835, low: 49.81, close: 49.82, volume: 36948, }, { date: 1461955140000, open: 49.82, high: 49.83, low: 49.79, close: 49.8, volume: 92933, }, { date: 1461955200000, open: 49.8, high: 49.84, low: 49.79, close: 49.8, volume: 61102, }, { date: 1461955260000, open: 49.8, high: 49.845, low: 49.795, close: 49.845, volume: 22658, }, { date: 1461955320000, open: 49.84, high: 49.85, low: 49.81, close: 49.83, volume: 119650, }, { date: 1461955380000, open: 49.83, high: 49.865, low: 49.81, close: 49.84, volume: 124543, }, { date: 1461955440000, open: 49.83, high: 49.86, low: 49.83, close: 49.835, volume: 64699, }, { date: 1461955500000, open: 49.835, high: 49.84, low: 49.81, close: 49.835, volume: 23935, }, { date: 1461955560000, open: 49.84, high: 49.86, low: 49.81, close: 49.86, volume: 39411, }, { date: 1461955620000, open: 49.855, high: 49.865, low: 49.85, close: 49.855, volume: 25021, }, { date: 1461955680000, open: 49.855, high: 49.885, low: 49.85, close: 49.87, volume: 109466, }, { date: 1461955740000, open: 49.88, high: 49.88, low: 49.86, close: 49.87, volume: 83972, }, { date: 1461955800000, open: 49.865, high: 49.89, low: 49.86, close: 49.88, volume: 160861, }, { date: 1461955860000, open: 49.89, high: 49.89, low: 49.8, close: 49.8129, volume: 68152, }, { date: 1461955920000, open: 49.81, high: 49.87, low: 49.81, close: 49.845, volume: 86848, }, { date: 1461955980000, open: 49.85, high: 49.87, low: 49.8, close: 49.82, volume: 84361, }, { date: 1461956040000, open: 49.83, high: 49.85, low: 49.81, close: 49.845, volume: 23189, }, { date: 1461956100000, open: 49.85, high: 49.85, low: 49.82, close: 49.84, volume: 50739, }, { date: 1461956160000, open: 49.84, high: 49.86, low: 49.81, close: 49.83, volume: 89480, }, { date: 1461956220000, open: 49.835, high: 49.85, low: 49.78, close: 49.795, volume: 148716, }, { date: 1461956280000, open: 49.79, high: 49.825, low: 49.79, close: 49.8058, volume: 48386, }, { date: 1461956340000, open: 49.81, high: 49.84, low: 49.78, close: 49.79, volume: 71144, }, { date: 1461956400000, open: 49.795, high: 49.825, low: 49.78, close: 49.825, volume: 41210, }, { date: 1461956460000, open: 49.83, high: 49.835, low: 49.791, close: 49.81, volume: 136758, }, { date: 1461956520000, open: 49.805, high: 49.84, low: 49.8, close: 49.83, volume: 78491, }, { date: 1461956580000, open: 49.835, high: 49.84, low: 49.79, close: 49.79, volume: 64303, }, { date: 1461956640000, open: 49.8, high: 49.82, low: 49.79, close: 49.8, volume: 38092, }, { date: 1461956700000, open: 49.8, high: 49.805, low: 49.76, close: 49.78, volume: 118485, }, { date: 1461956760000, open: 49.775, high: 49.795, low: 49.75, close: 49.79, volume: 57394, }, { date: 1461956820000, open: 49.795, high: 49.8299, low: 49.79, close: 49.82, volume: 47534, }, { date: 1461956880000, open: 49.82, high: 49.84, low: 49.815, close: 49.84, volume: 97126, }, { date: 1461956940000, open: 49.84, high: 49.85, low: 49.79, close: 49.79, volume: 123615, }, { date: 1461957000000, open: 49.795, high: 49.82, low: 49.78, close: 49.795, volume: 87855, }, { date: 1461957060000, open: 49.795, high: 49.8, low: 49.75, close: 49.775, volume: 82391, }, { date: 1461957120000, open: 49.775, high: 49.835, low: 49.76, close: 49.835, volume: 57712, }, { date: 1461957180000, open: 49.8399, high: 49.85, low: 49.79, close: 49.82, volume: 131280, }, { date: 1461957240000, open: 49.83, high: 49.85, low: 49.815, close: 49.84, volume: 101700, }, { date: 1461957300000, open: 49.845, high: 49.88, low: 49.835, close: 49.86, volume: 124955, }, { date: 1461957360000, open: 49.8561, high: 49.86, low: 49.84, close: 49.845, volume: 87694, }, { date: 1461957420000, open: 49.8499, high: 49.85, low: 49.795, close: 49.82, volume: 70811, }, { date: 1461957480000, open: 49.82, high: 49.83, low: 49.79, close: 49.806, volume: 30351, }, { date: 1461957540000, open: 49.81, high: 49.84, low: 49.8, close: 49.805, volume: 48612, }, { date: 1461957600000, open: 49.81, high: 49.825, low: 49.8, close: 49.82, volume: 30233, }, { date: 1461957660000, open: 49.8277, high: 49.83, low: 49.79, close: 49.82, volume: 119299, }, { date: 1461957720000, open: 49.82, high: 49.84, low: 49.8, close: 49.835, volume: 62440, }, { date: 1461957780000, open: 49.8399, high: 49.855, low: 49.81, close: 49.84, volume: 137109, }, { date: 1461957840000, open: 49.835, high: 49.86, low: 49.78, close: 49.785, volume: 176645, }, { date: 1461957900000, open: 49.7839, high: 49.825, low: 49.77, close: 49.78, volume: 78262, }, { date: 1461957960000, open: 49.78, high: 49.8025, low: 49.775, close: 49.79, volume: 91842, }, { date: 1461958020000, open: 49.797, high: 49.81, low: 49.76, close: 49.76, volume: 78809, }, { date: 1461958080000, open: 49.765, high: 49.79, low: 49.74, close: 49.79, volume: 52624, }, { date: 1461958140000, open: 49.79, high: 49.795, low: 49.72, close: 49.726, volume: 55312, }, { date: 1461958200000, open: 49.7237, high: 49.745, low: 49.71, close: 49.73, volume: 86506, }, { date: 1461958260000, open: 49.73, high: 49.78, low: 49.73, close: 49.78, volume: 98958, }, { date: 1461958320000, open: 49.79, high: 49.815, low: 49.78, close: 49.8028, volume: 71102, }, { date: 1461958380000, open: 49.8, high: 49.86, low: 49.8, close: 49.85, volume: 285015, }, { date: 1461958440000, open: 49.85, high: 49.88, low: 49.815, close: 49.815, volume: 152601, }, { date: 1461958500000, open: 49.815, high: 49.9, low: 49.815, close: 49.9, volume: 96252, }, { date: 1461958560000, open: 49.9, high: 49.9, low: 49.88, close: 49.9, volume: 133108, }, { date: 1461958620000, open: 49.9, high: 49.93, low: 49.89, close: 49.9, volume: 156212, }, { date: 1461958680000, open: 49.9, high: 49.94, low: 49.895, close: 49.925, volume: 118279, }, { date: 1461958740000, open: 49.93, high: 49.96, low: 49.92, close: 49.95, volume: 356166, }, { date: 1461958800000, open: 49.95, high: 49.95, low: 49.885, close: 49.92, volume: 142555, }, { date: 1461958860000, open: 49.92, high: 49.96, low: 49.91, close: 49.935, volume: 159392, }, { date: 1461958920000, open: 49.935, high: 49.965, low: 49.93, close: 49.935, volume: 77225, }, { date: 1461958980000, open: 49.935, high: 49.96, low: 49.93, close: 49.945, volume: 94686, }, { date: 1461959040000, open: 49.945, high: 49.95, low: 49.89, close: 49.925, volume: 109829, }, { date: 1461959100000, open: 49.92, high: 49.94, low: 49.915, close: 49.94, volume: 50436, }, { date: 1461959160000, open: 49.94, high: 50.02, low: 49.92, close: 50.02, volume: 254783, }, { date: 1461959220000, open: 50.015, high: 50.04, low: 49.99, close: 49.995, volume: 223303, }, { date: 1461959280000, open: 49.99, high: 50, low: 49.93, close: 49.935, volume: 132883, }, { date: 1461959340000, open: 49.935, high: 49.94, low: 49.88, close: 49.89, volume: 136876, }, { date: 1461959400000, open: 49.9, high: 49.9, low: 49.85, close: 49.855, volume: 223623, }, { date: 1461959460000, open: 49.855, high: 49.88, low: 49.82, close: 49.875, volume: 210430, }, { date: 1461959520000, open: 49.88, high: 49.9262, low: 49.87, close: 49.91, volume: 172327, }, { date: 1461959580000, open: 49.905, high: 49.91, low: 49.86, close: 49.86, volume: 130391, }, { date: 1461959640000, open: 49.86, high: 49.87, low: 49.82, close: 49.835, volume: 162371, }, { date: 1461959700000, open: 49.835, high: 49.89, low: 49.83, close: 49.89, volume: 175742, }, { date: 1461959760000, open: 49.895, high: 49.93, low: 49.85, close: 49.875, volume: 239317, }, { date: 1461959820000, open: 49.88, high: 49.88, low: 49.84, close: 49.865, volume: 221813, }, { date: 1461959880000, open: 49.87, high: 49.885, low: 49.835, close: 49.86, volume: 302425, }, { date: 1461959940000, open: 49.865, high: 49.87, low: 49.83, close: 49.855, volume: 357334, }, { date: 1461960000000, open: 49.855, high: 49.91, low: 49.83, close: 49.87, volume: 4207530, }, { date: 1462195800000, open: 50, high: 50.05, low: 50, close: 50.03, volume: 383038, }, { date: 1462195860000, open: 50.04, high: 50.11, low: 49.95, close: 49.95, volume: 151710, }, { date: 1462195920000, open: 49.96, high: 50.01, low: 49.93, close: 49.95, volume: 103879, }, { date: 1462195980000, open: 49.94, high: 49.955, low: 49.88, close: 49.89, volume: 55286, }, { date: 1462196040000, open: 49.88, high: 49.885, low: 49.7799, close: 49.81, volume: 73269, }, { date: 1462196100000, open: 49.8, high: 49.905, low: 49.785, close: 49.87, volume: 55416, }, { date: 1462196160000, open: 49.865, high: 49.95, low: 49.865, close: 49.91, volume: 58493, }, { date: 1462196220000, open: 49.91, high: 49.97, low: 49.88, close: 49.945, volume: 73315, }, { date: 1462196280000, open: 49.94, high: 50.02, low: 49.9175, close: 49.9999, volume: 86652, }, { date: 1462196340000, open: 49.9976, high: 50.06, low: 49.99, close: 50.03, volume: 255704, }, { date: 1462196400000, open: 50.035, high: 50.06, low: 49.98, close: 49.98, volume: 112429, }, { date: 1462196460000, open: 49.98, high: 50.11, low: 49.96, close: 50.08, volume: 193309, }, { date: 1462196520000, open: 50.08, high: 50.0999, low: 50.04, close: 50.0568, volume: 53118, }, { date: 1462196580000, open: 50.055, high: 50.06, low: 50.01, close: 50.01, volume: 84372, }, { date: 1462196640000, open: 50.0168, high: 50.025, low: 49.975, close: 50, volume: 61643, }, { date: 1462196700000, open: 49.991, high: 50.0563, low: 49.975, close: 50.0305, volume: 50654, }, { date: 1462196760000, open: 50.03, high: 50.0301, low: 49.93, close: 49.97, volume: 74299, }, { date: 1462196820000, open: 49.975, high: 49.9763, low: 49.88, close: 49.93, volume: 52612, }, { date: 1462196880000, open: 49.93, high: 49.93, low: 49.84, close: 49.84, volume: 69513, }, { date: 1462196940000, open: 49.84, high: 49.89, low: 49.83, close: 49.89, volume: 85516, }, { date: 1462197000000, open: 49.88, high: 49.97, low: 49.871, close: 49.95, volume: 91160, }, { date: 1462197060000, open: 49.94, high: 49.945, low: 49.84, close: 49.86, volume: 124052, }, { date: 1462197120000, open: 49.85, high: 49.94, low: 49.83, close: 49.9254, volume: 89502, }, { date: 1462197180000, open: 49.9295, high: 49.99, low: 49.92, close: 49.985, volume: 60248, }, { date: 1462197240000, open: 49.98, high: 50.02, low: 49.97, close: 49.99, volume: 113258, }, { date: 1462197300000, open: 49.98, high: 50, low: 49.96, close: 49.98, volume: 172259, }, { date: 1462197360000, open: 49.98, high: 50.01, low: 49.97, close: 49.97, volume: 173436, }, { date: 1462197420000, open: 49.965, high: 49.985, low: 49.94, close: 49.945, volume: 92568, }, { date: 1462197480000, open: 49.95, high: 50.01, low: 49.95, close: 49.956, volume: 155964, }, { date: 1462197540000, open: 49.96, high: 49.97, low: 49.93, close: 49.96, volume: 141365, }, { date: 1462197600000, open: 49.95, high: 49.9976, low: 49.895, close: 49.895, volume: 88592, }, { date: 1462197660000, open: 49.89, high: 49.95, low: 49.88, close: 49.89, volume: 151973, }, { date: 1462197720000, open: 49.88, high: 49.94, low: 49.88, close: 49.902, volume: 108413, }, { date: 1462197780000, open: 49.905, high: 49.94, low: 49.87, close: 49.93, volume: 127842, }, { date: 1462197840000, open: 49.92, high: 49.93, low: 49.86, close: 49.89, volume: 89591, }, { date: 1462197900000, open: 49.88, high: 49.92, low: 49.86, close: 49.9, volume: 102258, }, { date: 1462197960000, open: 49.909, high: 49.929, low: 49.89, close: 49.929, volume: 51808, }, { date: 1462198020000, open: 49.92, high: 49.93, low: 49.89, close: 49.91, volume: 85321, }, { date: 1462198080000, open: 49.9001, high: 49.95, low: 49.895, close: 49.94, volume: 45907, }, { date: 1462198140000, open: 49.935, high: 49.95, low: 49.915, close: 49.93, volume: 52314, }, { date: 1462198200000, open: 49.93, high: 49.9905, low: 49.93, close: 49.985, volume: 54111, }, { date: 1462198260000, open: 49.985, high: 50, low: 49.97, close: 49.98, volume: 54760, }, { date: 1462198320000, open: 49.985, high: 50.02, low: 49.97, close: 49.9971, volume: 83728, }, { date: 1462198380000, open: 49.995, high: 50, low: 49.97, close: 49.9763, volume: 42395, }, { date: 1462198440000, open: 49.98, high: 49.99, low: 49.96, close: 49.9865, volume: 115119, }, { date: 1462198500000, open: 49.98, high: 50.01, low: 49.965, close: 50, volume: 73596, }, { date: 1462198560000, open: 50, high: 50.04, low: 49.985, close: 50.02, volume: 70429, }, { date: 1462198620000, open: 50.0299, high: 50.08, low: 50.02, close: 50.07, volume: 330882, }, { date: 1462198680000, open: 50.07, high: 50.075, low: 50.06, close: 50.06, volume: 95022, }, { date: 1462198740000, open: 50.065, high: 50.075, low: 50.06, close: 50.0657, volume: 78907, }, { date: 1462198800000, open: 50.065, high: 50.075, low: 50.03, close: 50.04, volume: 62861, }, { date: 1462198860000, open: 50.0463, high: 50.05, low: 50, close: 50.045, volume: 99696, }, { date: 1462198920000, open: 50.045, high: 50.0799, low: 50.04, close: 50.07, volume: 50227, }, { date: 1462198980000, open: 50.07, high: 50.08, low: 50.02, close: 50.02, volume: 114235, }, { date: 1462199040000, open: 50.03, high: 50.0499, low: 50.0015, close: 50.04, volume: 62061, }, { date: 1462199100000, open: 50.04, high: 50.06, low: 50.0215, close: 50.055, volume: 47183, }, { date: 1462199160000, open: 50.06, high: 50.15, low: 50.05, close: 50.13, volume: 299184, }, { date: 1462199220000, open: 50.1262, high: 50.18, low: 50.12, close: 50.16, volume: 82964, }, { date: 1462199280000, open: 50.1639, high: 50.1698, low: 50.12, close: 50.12, volume: 77628, }, { date: 1462199340000, open: 50.1299, high: 50.15, low: 50.09, close: 50.15, volume: 84933, }, { date: 1462199400000, open: 50.15, high: 50.15, low: 50.1, close: 50.12, volume: 54899, }, { date: 1462199460000, open: 50.115, high: 50.16, low: 50.115, close: 50.145, volume: 83590, }, { date: 1462199520000, open: 50.145, high: 50.18, low: 50.145, close: 50.165, volume: 107569, }, { date: 1462199580000, open: 50.17, high: 50.2, low: 50.14, close: 50.196, volume: 99641, }, { date: 1462199640000, open: 50.19, high: 50.195, low: 50.15, close: 50.16, volume: 112181, }, { date: 1462199700000, open: 50.16, high: 50.1667, low: 50.14, close: 50.16, volume: 69920, }, { date: 1462199760000, open: 50.16, high: 50.16, low: 50.11, close: 50.12, volume: 76237, }, { date: 1462199820000, open: 50.12, high: 50.1464, low: 50.1199, close: 50.14, volume: 46259, }, { date: 1462199880000, open: 50.14, high: 50.15, low: 50.11, close: 50.11, volume: 89815, }, { date: 1462199940000, open: 50.1196, high: 50.14, low: 50.11, close: 50.125, volume: 59629, }, { date: 1462200000000, open: 50.12, high: 50.15, low: 50.09, close: 50.09, volume: 74167, }, { date: 1462200060000, open: 50.0899, high: 50.09, low: 50.0699, close: 50.086, volume: 57212, }, { date: 1462200120000, open: 50.084, high: 50.1, low: 50.082, close: 50.09, volume: 54807, }, { date: 1462200180000, open: 50.1, high: 50.12, low: 50.09, close: 50.1142, volume: 52031, }, { date: 1462200240000, open: 50.12, high: 50.129, low: 50.1, close: 50.1, volume: 37065, }, { date: 1462200300000, open: 50.1099, high: 50.11, low: 50.04, close: 50.05, volume: 80763, }, { date: 1462200360000, open: 50.05, high: 50.1, low: 50.05, close: 50.0735, volume: 73852, }, { date: 1462200420000, open: 50.07, high: 50.11, low: 50.06, close: 50.09, volume: 93871, }, { date: 1462200480000, open: 50.09, high: 50.11, low: 50.07, close: 50.0965, volume: 138529, }, { date: 1462200540000, open: 50.095, high: 50.11, low: 50.09, close: 50.11, volume: 19333, }, { date: 1462200600000, open: 50.1, high: 50.14, low: 50.1, close: 50.135, volume: 56889, }, { date: 1462200660000, open: 50.135, high: 50.14, low: 50.13, close: 50.13, volume: 50752, }, { date: 1462200720000, open: 50.13, high: 50.1399, low: 50.06, close: 50.06, volume: 77398, }, { date: 1462200780000, open: 50.06, high: 50.07, low: 50, close: 50.01, volume: 78016, }, { date: 1462200840000, open: 50.005, high: 50.01, low: 49.98, close: 49.99, volume: 227024, }, { date: 1462200900000, open: 49.995, high: 50, low: 49.94, close: 49.967, volume: 122010, }, { date: 1462200960000, open: 49.97, high: 49.98, low: 49.91, close: 49.91, volume: 72187, }, { date: 1462201020000, open: 49.91, high: 49.955, low: 49.91, close: 49.945, volume: 63928, }, { date: 1462201080000, open: 49.9455, high: 49.9455, low: 49.91, close: 49.91, volume: 33585, }, { date: 1462201140000, open: 49.91, high: 49.95, low: 49.9, close: 49.925, volume: 76455, }, { date: 1462201200000, open: 49.925, high: 49.93, low: 49.9, close: 49.9165, volume: 67779, }, { date: 1462201260000, open: 49.919, high: 49.94, low: 49.91, close: 49.92, volume: 87559, }, { date: 1462201320000, open: 49.925, high: 49.94, low: 49.92, close: 49.93, volume: 72901, }, { date: 1462201380000, open: 49.94, high: 49.94, low: 49.92, close: 49.93, volume: 26630, }, { date: 1462201440000, open: 49.935, high: 49.956, low: 49.92, close: 49.935, volume: 40355, }, { date: 1462201500000, open: 49.932, high: 49.94, low: 49.88, close: 49.885, volume: 71179, }, { date: 1462201560000, open: 49.88, high: 49.91, low: 49.87, close: 49.9, volume: 118324, }, { date: 1462201620000, open: 49.895, high: 49.91, low: 49.87, close: 49.8765, volume: 36907, }, { date: 1462201680000, open: 49.87, high: 49.875, low: 49.85, close: 49.8589, volume: 30398, }, { date: 1462201740000, open: 49.85, high: 49.9, low: 49.85, close: 49.895, volume: 43911, }, { date: 1462201800000, open: 49.8998, high: 49.92, low: 49.88, close: 49.9, volume: 68404, }, { date: 1462201860000, open: 49.89, high: 49.98, low: 49.89, close: 49.965, volume: 61847, }, { date: 1462201920000, open: 49.96, high: 49.995, low: 49.93, close: 49.94, volume: 43387, }, { date: 1462201980000, open: 49.94, high: 49.97, low: 49.935, close: 49.95, volume: 47098, }, { date: 1462202040000, open: 49.96, high: 49.99, low: 49.93, close: 49.94, volume: 51119, }, { date: 1462202100000, open: 49.94, high: 49.95, low: 49.92, close: 49.93, volume: 37486, }, { date: 1462202160000, open: 49.93, high: 49.94, low: 49.9, close: 49.92, volume: 43050, }, { date: 1462202220000, open: 49.925, high: 49.93, low: 49.91, close: 49.9175, volume: 75377, }, { date: 1462202280000, open: 49.91, high: 49.97, low: 49.91, close: 49.96, volume: 30266, }, { date: 1462202340000, open: 49.96, high: 49.9899, low: 49.954, close: 49.965, volume: 38202, }, { date: 1462202400000, open: 49.96, high: 49.9852, low: 49.95, close: 49.9808, volume: 28933, }, { date: 1462202460000, open: 49.9808, high: 49.99, low: 49.95, close: 49.951, volume: 47695, }, { date: 1462202520000, open: 49.9567, high: 49.96, low: 49.93, close: 49.945, volume: 28467, }, { date: 1462202580000, open: 49.945, high: 49.99, low: 49.92, close: 49.985, volume: 50848, }, { date: 1462202640000, open: 49.985, high: 49.99, low: 49.95, close: 49.95, volume: 74207, }, { date: 1462202700000, open: 49.96, high: 49.977, low: 49.92, close: 49.925, volume: 132677, }, { date: 1462202760000, open: 49.92, high: 49.927, low: 49.88, close: 49.89, volume: 53495, }, { date: 1462202820000, open: 49.89, high: 49.9099, low: 49.88, close: 49.886, volume: 80791, }, { date: 1462202880000, open: 49.885, high: 49.9289, low: 49.87, close: 49.915, volume: 63205, }, { date: 1462202940000, open: 49.915, high: 49.94, low: 49.9, close: 49.94, volume: 41704, }, { date: 1462203000000, open: 49.93, high: 49.985, low: 49.93, close: 49.97, volume: 73299, }, { date: 1462203060000, open: 49.9799, high: 50, low: 49.96, close: 50, volume: 26455, }, { date: 1462203120000, open: 50, high: 50.01, low: 49.97, close: 49.97, volume: 33864, }, { date: 1462203180000, open: 49.97, high: 49.975, low: 49.96, close: 49.97, volume: 43015, }, { date: 1462203240000, open: 49.97, high: 49.97, low: 49.96, close: 49.96, volume: 19568, }, { date: 1462203300000, open: 49.9676, high: 50, low: 49.96, close: 49.99, volume: 45334, }, { date: 1462203360000, open: 49.9901, high: 50, low: 49.955, close: 49.96, volume: 48719, }, { date: 1462203420000, open: 49.96, high: 49.97, low: 49.9516, close: 49.965, volume: 11222, }, { date: 1462203480000, open: 49.965, high: 50.02, low: 49.96, close: 50.0075, volume: 70311, }, { date: 1462203540000, open: 50.005, high: 50.03, low: 49.99, close: 50.0261, volume: 32086, }, { date: 1462203600000, open: 50.03, high: 50.035, low: 50.02, close: 50.0201, volume: 22722, }, { date: 1462203660000, open: 50.02, high: 50.03, low: 50.01, close: 50.01, volume: 52278, }, { date: 1462203720000, open: 50.01, high: 50.02, low: 50, close: 50.0095, volume: 32807, }, { date: 1462203780000, open: 50.005, high: 50.015, low: 50, close: 50.005, volume: 29600, }, { date: 1462203840000, open: 50.005, high: 50.04, low: 49.995, close: 50.028, volume: 62627, }, { date: 1462203900000, open: 50.02, high: 50.03, low: 49.96, close: 49.99, volume: 89772, }, { date: 1462203960000, open: 49.985, high: 49.9998, low: 49.96, close: 49.96, volume: 27477, }, { date: 1462204020000, open: 49.96, high: 49.96, low: 49.925, close: 49.94, volume: 32903, }, { date: 1462204080000, open: 49.95, high: 49.96, low: 49.94, close: 49.95, volume: 28860, }, { date: 1462204140000, open: 49.95, high: 49.967, low: 49.93, close: 49.967, volume: 21759, }, { date: 1462204200000, open: 49.9635, high: 49.98, low: 49.95, close: 49.97, volume: 60051, }, { date: 1462204260000, open: 49.98, high: 49.98, low: 49.97, close: 49.975, volume: 9734, }, { date: 1462204320000, open: 49.9701, high: 49.975, low: 49.92, close: 49.95, volume: 51141, }, { date: 1462204380000, open: 49.955, high: 49.955, low: 49.925, close: 49.93, volume: 29814, }, { date: 1462204440000, open: 49.92, high: 49.97, low: 49.92, close: 49.97, volume: 75962, }, { date: 1462204500000, open: 49.97, high: 49.995, low: 49.95, close: 49.95, volume: 83880, }, { date: 1462204560000, open: 49.9599, high: 49.9799, low: 49.95, close: 49.965, volume: 44091, }, { date: 1462204620000, open: 49.96, high: 50.019, low: 49.96, close: 49.99, volume: 76264, }, { date: 1462204680000, open: 49.985, high: 49.99, low: 49.9425, close: 49.9601, volume: 33720, }, { date: 1462204740000, open: 49.9689, high: 49.97, low: 49.93, close: 49.93, volume: 31629, }, { date: 1462204800000, open: 49.9201, high: 49.9201, low: 49.905, close: 49.91, volume: 42481, }, { date: 1462204860000, open: 49.91, high: 49.91, low: 49.85, close: 49.8701, volume: 173203, }, { date: 1462204920000, open: 49.875, high: 49.93, low: 49.8735, close: 49.929, volume: 40586, }, { date: 1462204980000, open: 49.93, high: 49.95, low: 49.9, close: 49.94, volume: 176314, }, { date: 1462205040000, open: 49.94, high: 49.965, low: 49.94, close: 49.95, volume: 69635, }, { date: 1462205100000, open: 49.945, high: 49.97, low: 49.94, close: 49.97, volume: 31492, }, { date: 1462205160000, open: 49.97, high: 49.999, low: 49.96, close: 49.99, volume: 42553, }, { date: 1462205220000, open: 49.99, high: 50, low: 49.97, close: 49.99, volume: 53746, }, { date: 1462205280000, open: 49.9938, high: 50.0099, low: 49.98, close: 49.9868, volume: 43952, }, { date: 1462205340000, open: 49.985, high: 50.01, low: 49.98, close: 50, volume: 150610, }, { date: 1462205400000, open: 49.995, high: 50.0299, low: 49.995, close: 50, volume: 69635, }, { date: 1462205460000, open: 50.005, high: 50.03, low: 50, close: 50.025, volume: 59470, }, { date: 1462205520000, open: 50.025, high: 50.045, low: 50.02, close: 50.045, volume: 61870, }, { date: 1462205580000, open: 50.0465, high: 50.07, low: 50.03, close: 50.03, volume: 242095, }, { date: 1462205640000, open: 50.03, high: 50.03, low: 49.99, close: 50, volume: 24634, }, { date: 1462205700000, open: 50, high: 50.01, low: 50, close: 50.005, volume: 17981, }, { date: 1462205760000, open: 50.005, high: 50.005, low: 49.97, close: 49.97, volume: 60062, }, { date: 1462205820000, open: 49.975, high: 49.98, low: 49.97, close: 49.975, volume: 14996, }, { date: 1462205880000, open: 49.9761, high: 50, low: 49.97, close: 50, volume: 34480, }, { date: 1462205940000, open: 49.995, high: 50, low: 49.99, close: 49.99, volume: 55279, }, { date: 1462206000000, open: 49.995, high: 49.999, low: 49.98, close: 49.98, volume: 26135, }, { date: 1462206060000, open: 49.985, high: 50, low: 49.98, close: 49.995, volume: 54542, }, { date: 1462206120000, open: 49.99, high: 50.016, low: 49.99, close: 50.01, volume: 46633, }, { date: 1462206180000, open: 50.0135, high: 50.0135, low: 49.97, close: 49.975, volume: 51402, }, { date: 1462206240000, open: 49.975, high: 49.99, low: 49.955, close: 49.96, volume: 49359, }, { date: 1462206300000, open: 49.95, high: 49.98, low: 49.95, close: 49.97, volume: 24635, }, { date: 1462206360000, open: 49.97, high: 49.98, low: 49.96, close: 49.96, volume: 14157, }, { date: 1462206420000, open: 49.96, high: 49.96, low: 49.9301, close: 49.945, volume: 25797, }, { date: 1462206480000, open: 49.94, high: 49.95, low: 49.92, close: 49.925, volume: 25283, }, { date: 1462206540000, open: 49.925, high: 49.94, low: 49.92, close: 49.93, volume: 27998, }, { date: 1462206600000, open: 49.92, high: 49.93, low: 49.91, close: 49.93, volume: 30191, }, { date: 1462206660000, open: 49.94, high: 49.9668, low: 49.94, close: 49.96, volume: 38762, }, { date: 1462206720000, open: 49.96, high: 49.966, low: 49.95, close: 49.95, volume: 25694, }, { date: 1462206780000, open: 49.95, high: 49.96, low: 49.94, close: 49.96, volume: 15568, }, { date: 1462206840000, open: 49.96, high: 50.015, low: 49.955, close: 50.01, volume: 58300, }, { date: 1462206900000, open: 50.01, high: 50.02, low: 50.01, close: 50.01, volume: 32662, }, { date: 1462206960000, open: 50.015, high: 50.04, low: 50.015, close: 50.02, volume: 27421, }, { date: 1462207020000, open: 50.02, high: 50.02, low: 49.97, close: 49.9799, volume: 29390, }, { date: 1462207080000, open: 49.97, high: 49.99, low: 49.97, close: 49.979, volume: 18658, }, { date: 1462207140000, open: 49.97, high: 49.99, low: 49.97, close: 49.985, volume: 14245, }, { date: 1462207200000, open: 49.985, high: 50, low: 49.97, close: 49.97, volume: 38179, }, { date: 1462207260000, open: 49.9701, high: 50, low: 49.97, close: 49.97, volume: 43761, }, { date: 1462207320000, open: 49.9799, high: 49.98, low: 49.96, close: 49.98, volume: 102966, }, { date: 1462207380000, open: 49.98, high: 49.995, low: 49.95, close: 49.95, volume: 51528, }, { date: 1462207440000, open: 49.951, high: 49.955, low: 49.935, close: 49.94, volume: 27753, }, { date: 1462207500000, open: 49.94, high: 49.98, low: 49.94, close: 49.9599, volume: 67542, }, { date: 1462207560000, open: 49.95, high: 49.96, low: 49.94, close: 49.96, volume: 23413, }, { date: 1462207620000, open: 49.96, high: 50.0071, low: 49.96, close: 50.0071, volume: 68846, }, { date: 1462207680000, open: 50, high: 50.02, low: 50, close: 50.01, volume: 67002, }, { date: 1462207740000, open: 50.015, high: 50.015, low: 49.99, close: 49.995, volume: 65707, }, { date: 1462207800000, open: 49.99, high: 50, low: 49.98, close: 49.99, volume: 43383, }, { date: 1462207860000, open: 49.995, high: 50, low: 49.98, close: 49.99, volume: 15296, }, { date: 1462207920000, open: 49.985, high: 49.99, low: 49.98, close: 49.99, volume: 19015, }, { date: 1462207980000, open: 49.99, high: 50.02, low: 49.98, close: 50.015, volume: 43954, }, { date: 1462208040000, open: 50.015, high: 50.055, low: 50.01, close: 50.0468, volume: 66019, }, { date: 1462208100000, open: 50.05, high: 50.06, low: 50.04, close: 50.05, volume: 36506, }, { date: 1462208160000, open: 50.045, high: 50.055, low: 50.04, close: 50.04, volume: 176238, }, { date: 1462208220000, open: 50.04, high: 50.06, low: 50.025, close: 50.025, volume: 57360, }, { date: 1462208280000, open: 50.03, high: 50.03, low: 49.98, close: 49.99, volume: 34135, }, { date: 1462208340000, open: 49.98, high: 50, low: 49.98, close: 50, volume: 21953, }, { date: 1462208400000, open: 49.995, high: 50.03, low: 49.99, close: 50.03, volume: 58199, }, { date: 1462208460000, open: 50.02, high: 50.03, low: 50, close: 50.01, volume: 39514, }, { date: 1462208520000, open: 50.01, high: 50.03, low: 50.01, close: 50.025, volume: 34770, }, { date: 1462208580000, open: 50.03, high: 50.03, low: 49.9917, close: 50.01, volume: 79230, }, { date: 1462208640000, open: 50.02, high: 50.025, low: 49.99, close: 50, volume: 167724, }, { date: 1462208700000, open: 49.99, high: 50, low: 49.97, close: 49.98, volume: 50661, }, { date: 1462208760000, open: 49.975, high: 50, low: 49.97, close: 49.9915, volume: 20363, }, { date: 1462208820000, open: 50, high: 50.01, low: 49.99, close: 50.01, volume: 88475, }, { date: 1462208880000, open: 50.005, high: 50.03, low: 49.99, close: 50, volume: 43532, }, { date: 1462208940000, open: 50.005, high: 50.005, low: 49.99, close: 49.995, volume: 23248, }, { date: 1462209000000, open: 50, high: 50, low: 49.99, close: 49.995, volume: 11605, }, { date: 1462209060000, open: 49.995, high: 50.035, low: 49.99, close: 50.035, volume: 128323, }, { date: 1462209120000, open: 50.03, high: 50.09, low: 50.03, close: 50.09, volume: 23832, }, { date: 1462209180000, open: 50.09, high: 50.135, low: 50.0899, close: 50.135, volume: 83153, }, { date: 1462209240000, open: 50.14, high: 50.18, low: 50.14, close: 50.1772, volume: 59374, }, { date: 1462209300000, open: 50.18, high: 50.2199, low: 50.17, close: 50.21, volume: 97604, }, { date: 1462209360000, open: 50.21, high: 50.24, low: 50.2035, close: 50.214, volume: 66495, }, { date: 1462209420000, open: 50.22, high: 50.23, low: 50.21, close: 50.2264, volume: 302275, }, { date: 1462209480000, open: 50.229, high: 50.249, low: 50.22, close: 50.24, volume: 68214, }, { date: 1462209540000, open: 50.24, high: 50.31, low: 50.24, close: 50.3, volume: 112581, }, { date: 1462209600000, open: 50.305, high: 50.31, low: 50.2992, close: 50.3061, volume: 66327, }, { date: 1462209660000, open: 50.3, high: 50.31, low: 50.3, close: 50.305, volume: 60533, }, { date: 1462209720000, open: 50.31, high: 50.34, low: 50.3, close: 50.335, volume: 157674, }, { date: 1462209780000, open: 50.3368, high: 50.36, low: 50.32, close: 50.325, volume: 107222, }, { date: 1462209840000, open: 50.315, high: 50.32, low: 50.25, close: 50.25, volume: 56936, }, { date: 1462209900000, open: 50.26, high: 50.31, low: 50.255, close: 50.3, volume: 95359, }, { date: 1462209960000, open: 50.3061, high: 50.32, low: 50.27, close: 50.28, volume: 53993, }, { date: 1462210020000, open: 50.275, high: 50.28, low: 50.27, close: 50.275, volume: 35828, }, { date: 1462210080000, open: 50.275, high: 50.2899, low: 50.27, close: 50.28, volume: 24367, }, { date: 1462210140000, open: 50.29, high: 50.3, low: 50.28, close: 50.299, volume: 27681, }, { date: 1462210200000, open: 50.295, high: 50.33, low: 50.295, close: 50.325, volume: 50349, }, { date: 1462210260000, open: 50.3204, high: 50.37, low: 50.32, close: 50.33, volume: 89757, }, { date: 1462210320000, open: 50.32, high: 50.3261, low: 50.28, close: 50.285, volume: 36574, }, { date: 1462210380000, open: 50.29, high: 50.31, low: 50.285, close: 50.305, volume: 21320, }, { date: 1462210440000, open: 50.3061, high: 50.3468, low: 50.3, close: 50.345, volume: 31967, }, { date: 1462210500000, open: 50.345, high: 50.405, low: 50.345, close: 50.4, volume: 163334, }, { date: 1462210560000, open: 50.405, high: 50.4099, low: 50.39, close: 50.4, volume: 91658, }, { date: 1462210620000, open: 50.4, high: 50.44, low: 50.4, close: 50.44, volume: 141099, }, { date: 1462210680000, open: 50.44, high: 50.47, low: 50.4355, close: 50.455, volume: 188915, }, { date: 1462210740000, open: 50.45, high: 50.46, low: 50.41, close: 50.415, volume: 74593, }, { date: 1462210800000, open: 50.415, high: 50.43, low: 50.41, close: 50.425, volume: 73084, }, { date: 1462210860000, open: 50.43, high: 50.48, low: 50.42, close: 50.48, volume: 88868, }, { date: 1462210920000, open: 50.48, high: 50.5, low: 50.48, close: 50.495, volume: 131915, }, { date: 1462210980000, open: 50.495, high: 50.52, low: 50.48, close: 50.5195, volume: 134666, }, { date: 1462211040000, open: 50.5164, high: 50.54, low: 50.51, close: 50.54, volume: 97069, }, { date: 1462211100000, open: 50.54, high: 50.55, low: 50.5299, close: 50.535, volume: 123365, }, { date: 1462211160000, open: 50.53, high: 50.53, low: 50.51, close: 50.515, volume: 71038, }, { date: 1462211220000, open: 50.52, high: 50.53, low: 50.43, close: 50.435, volume: 97679, }, { date: 1462211280000, open: 50.445, high: 50.5, low: 50.435, close: 50.48, volume: 107166, }, { date: 1462211340000, open: 50.489, high: 50.53, low: 50.489, close: 50.53, volume: 108572, }, { date: 1462211400000, open: 50.5261, high: 50.55, low: 50.52, close: 50.54, volume: 115336, }, { date: 1462211460000, open: 50.54, high: 50.545, low: 50.53, close: 50.535, volume: 53259, }, { date: 1462211520000, open: 50.5336, high: 50.54, low: 50.49, close: 50.49, volume: 74549, }, { date: 1462211580000, open: 50.49, high: 50.4965, low: 50.46, close: 50.46, volume: 76228, }, { date: 1462211640000, open: 50.47, high: 50.52, low: 50.47, close: 50.51, volume: 71081, }, { date: 1462211700000, open: 50.5136, high: 50.5275, low: 50.51, close: 50.52, volume: 34096, }, { date: 1462211760000, open: 50.53, high: 50.53, low: 50.52, close: 50.53, volume: 52755, }, { date: 1462211820000, open: 50.5262, high: 50.54, low: 50.52, close: 50.53, volume: 79888, }, { date: 1462211880000, open: 50.53, high: 50.589, low: 50.53, close: 50.585, volume: 129080, }, { date: 1462211940000, open: 50.58, high: 50.59, low: 50.53, close: 50.535, volume: 97488, }, { date: 1462212000000, open: 50.53, high: 50.54, low: 50.51, close: 50.515, volume: 76128, }, { date: 1462212060000, open: 50.52, high: 50.53, low: 50.49, close: 50.495, volume: 81237, }, { date: 1462212120000, open: 50.495, high: 50.5, low: 50.49, close: 50.49, volume: 23706, }, { date: 1462212180000, open: 50.49, high: 50.5, low: 50.49, close: 50.49, volume: 25104, }, { date: 1462212240000, open: 50.495, high: 50.52, low: 50.49, close: 50.515, volume: 61722, }, { date: 1462212300000, open: 50.52, high: 50.56, low: 50.52, close: 50.555, volume: 78302, }, { date: 1462212360000, open: 50.56, high: 50.59, low: 50.56, close: 50.585, volume: 133662, }, { date: 1462212420000, open: 50.59, high: 50.6, low: 50.59, close: 50.595, volume: 65073, }, { date: 1462212480000, open: 50.59, high: 50.62, low: 50.59, close: 50.62, volume: 65571, }, { date: 1462212540000, open: 50.6129, high: 50.64, low: 50.6129, close: 50.63, volume: 150715, }, { date: 1462212600000, open: 50.63, high: 50.64, low: 50.61, close: 50.61, volume: 123197, }, { date: 1462212660000, open: 50.61, high: 50.63, low: 50.605, close: 50.63, volume: 36624, }, { date: 1462212720000, open: 50.6295, high: 50.65, low: 50.6295, close: 50.65, volume: 61779, }, { date: 1462212780000, open: 50.65, high: 50.67, low: 50.635, close: 50.637, volume: 111500, }, { date: 1462212840000, open: 50.63, high: 50.63, low: 50.59, close: 50.6, volume: 88467, }, { date: 1462212900000, open: 50.6, high: 50.629, low: 50.59, close: 50.6207, volume: 37352, }, { date: 1462212960000, open: 50.63, high: 50.63, low: 50.62, close: 50.62, volume: 67099, }, { date: 1462213020000, open: 50.62, high: 50.63, low: 50.61, close: 50.62, volume: 46942, }, { date: 1462213080000, open: 50.6274, high: 50.64, low: 50.61, close: 50.61, volume: 60069, }, { date: 1462213140000, open: 50.615, high: 50.63, low: 50.59, close: 50.605, volume: 93512, }, { date: 1462213200000, open: 50.605, high: 50.61, low: 50.59, close: 50.595, volume: 33400, }, { date: 1462213260000, open: 50.6, high: 50.6399, low: 50.595, close: 50.6399, volume: 33702, }, { date: 1462213320000, open: 50.63, high: 50.64, low: 50.61, close: 50.61, volume: 113649, }, { date: 1462213380000, open: 50.61, high: 50.6101, low: 50.6, close: 50.61, volume: 43128, }, { date: 1462213440000, open: 50.62, high: 50.625, low: 50.61, close: 50.61, volume: 23815, }, { date: 1462213500000, open: 50.61, high: 50.62, low: 50.59, close: 50.6074, volume: 88530, }, { date: 1462213560000, open: 50.6026, high: 50.619, low: 50.59, close: 50.5935, volume: 41174, }, { date: 1462213620000, open: 50.6, high: 50.61, low: 50.58, close: 50.59, volume: 51799, }, { date: 1462213680000, open: 50.595, high: 50.595, low: 50.55, close: 50.5533, volume: 47531, }, { date: 1462213740000, open: 50.555, high: 50.57, low: 50.55, close: 50.565, volume: 72984, }, { date: 1462213800000, open: 50.56, high: 50.61, low: 50.56, close: 50.6, volume: 81088, }, { date: 1462213860000, open: 50.6, high: 50.61, low: 50.595, close: 50.61, volume: 61154, }, { date: 1462213920000, open: 50.6167, high: 50.63, low: 50.61, close: 50.616, volume: 27960, }, { date: 1462213980000, open: 50.6165, high: 50.62, low: 50.56, close: 50.565, volume: 88769, }, { date: 1462214040000, open: 50.56, high: 50.57, low: 50.56, close: 50.57, volume: 61664, }, { date: 1462214100000, open: 50.56, high: 50.57, low: 50.56, close: 50.57, volume: 33457, }, { date: 1462214160000, open: 50.5657, high: 50.61, low: 50.5657, close: 50.6067, volume: 38679, }, { date: 1462214220000, open: 50.605, high: 50.62, low: 50.605, close: 50.6163, volume: 28644, }, { date: 1462214280000, open: 50.62, high: 50.62, low: 50.59, close: 50.605, volume: 81257, }, { date: 1462214340000, open: 50.6, high: 50.61, low: 50.58, close: 50.59, volume: 45033, }, { date: 1462214400000, open: 50.5899, high: 50.59, low: 50.57, close: 50.58, volume: 28136, }, { date: 1462214460000, open: 50.57, high: 50.58, low: 50.56, close: 50.565, volume: 36647, }, { date: 1462214520000, open: 50.5601, high: 50.57, low: 50.56, close: 50.565, volume: 21623, }, { date: 1462214580000, open: 50.56, high: 50.58, low: 50.56, close: 50.57, volume: 34798, }, { date: 1462214640000, open: 50.57, high: 50.59, low: 50.57, close: 50.588, volume: 54767, }, { date: 1462214700000, open: 50.59, high: 50.63, low: 50.59, close: 50.6245, volume: 91508, }, { date: 1462214760000, open: 50.621, high: 50.63, low: 50.62, close: 50.62, volume: 27892, }, { date: 1462214820000, open: 50.62, high: 50.63, low: 50.62, close: 50.6299, volume: 44027, }, { date: 1462214880000, open: 50.62, high: 50.65, low: 50.62, close: 50.65, volume: 47875, }, { date: 1462214940000, open: 50.64, high: 50.67, low: 50.64, close: 50.647, volume: 169872, }, { date: 1462215000000, open: 50.64, high: 50.69, low: 50.63, close: 50.68, volume: 107566, }, { date: 1462215060000, open: 50.67, high: 50.68, low: 50.66, close: 50.6769, volume: 43839, }, { date: 1462215120000, open: 50.68, high: 50.7, low: 50.67, close: 50.67, volume: 99761, }, { date: 1462215180000, open: 50.6738, high: 50.68, low: 50.66, close: 50.67, volume: 37683, }, { date: 1462215240000, open: 50.665, high: 50.665, low: 50.63, close: 50.655, volume: 153463, }, { date: 1462215300000, open: 50.65, high: 50.66, low: 50.64, close: 50.645, volume: 109467, }, { date: 1462215360000, open: 50.6401, high: 50.65, low: 50.64, close: 50.65, volume: 54627, }, { date: 1462215420000, open: 50.65, high: 50.69, low: 50.64, close: 50.685, volume: 86575, }, { date: 1462215480000, open: 50.69, high: 50.745, low: 50.68, close: 50.73, volume: 114430, }, { date: 1462215540000, open: 50.72, high: 50.73, low: 50.71, close: 50.71, volume: 24848, }, { date: 1462215600000, open: 50.7137, high: 50.72, low: 50.71, close: 50.715, volume: 28641, }, { date: 1462215660000, open: 50.72, high: 50.72, low: 50.71, close: 50.715, volume: 80936, }, { date: 1462215720000, open: 50.715, high: 50.73, low: 50.7, close: 50.705, volume: 147803, }, { date: 1462215780000, open: 50.71, high: 50.72, low: 50.69, close: 50.71, volume: 77205, }, { date: 1462215840000, open: 50.71, high: 50.72, low: 50.7, close: 50.715, volume: 62003, }, { date: 1462215900000, open: 50.7, high: 50.73, low: 50.7, close: 50.73, volume: 63127, }, { date: 1462215960000, open: 50.721, high: 50.73, low: 50.72, close: 50.72, volume: 68755, }, { date: 1462216020000, open: 50.715, high: 50.72, low: 50.67, close: 50.6729, volume: 79729, }, { date: 1462216080000, open: 50.675, high: 50.6964, low: 50.6701, close: 50.685, volume: 76882, }, { date: 1462216140000, open: 50.685, high: 50.7, low: 50.68, close: 50.695, volume: 58335, }, { date: 1462216200000, open: 50.69, high: 50.71, low: 50.69, close: 50.71, volume: 40624, }, { date: 1462216260000, open: 50.705, high: 50.71, low: 50.69, close: 50.695, volume: 47258, }, { date: 1462216320000, open: 50.695, high: 50.7, low: 50.685, close: 50.6995, volume: 98896, }, { date: 1462216380000, open: 50.69, high: 50.7, low: 50.67, close: 50.675, volume: 94653, }, { date: 1462216440000, open: 50.68, high: 50.7, low: 50.67, close: 50.69, volume: 45407, }, { date: 1462216500000, open: 50.695, high: 50.75, low: 50.695, close: 50.74, volume: 108576, }, { date: 1462216560000, open: 50.75, high: 50.75, low: 50.73, close: 50.735, volume: 224575, }, { date: 1462216620000, open: 50.73, high: 50.74, low: 50.71, close: 50.71, volume: 74616, }, { date: 1462216680000, open: 50.72, high: 50.72, low: 50.6902, close: 50.7, volume: 49485, }, { date: 1462216740000, open: 50.7, high: 50.71, low: 50.67, close: 50.67, volume: 57140, }, { date: 1462216800000, open: 50.67, high: 50.68, low: 50.66, close: 50.68, volume: 52434, }, { date: 1462216860000, open: 50.68, high: 50.68, low: 50.64, close: 50.645, volume: 81933, }, { date: 1462216920000, open: 50.645, high: 50.67, low: 50.64, close: 50.6653, volume: 49693, }, { date: 1462216980000, open: 50.67, high: 50.6761, low: 50.66, close: 50.675, volume: 63647, }, { date: 1462217040000, open: 50.675, high: 50.6799, low: 50.65, close: 50.655, volume: 105850, }, { date: 1462217100000, open: 50.65, high: 50.679, low: 50.65, close: 50.65, volume: 92948, }, { date: 1462217160000, open: 50.65, high: 50.66, low: 50.65, close: 50.6535, volume: 21237, }, { date: 1462217220000, open: 50.6586, high: 50.66, low: 50.61, close: 50.61, volume: 76515, }, { date: 1462217280000, open: 50.615, high: 50.62, low: 50.6, close: 50.62, volume: 106158, }, { date: 1462217340000, open: 50.61, high: 50.65, low: 50.61, close: 50.65, volume: 60526, }, { date: 1462217400000, open: 50.65, high: 50.66, low: 50.64, close: 50.64, volume: 61668, }, { date: 1462217460000, open: 50.64, high: 50.655, low: 50.61, close: 50.6133, volume: 163654, }, { date: 1462217520000, open: 50.61, high: 50.61, low: 50.59, close: 50.59, volume: 55304, }, { date: 1462217580000, open: 50.59, high: 50.595, low: 50.56, close: 50.56, volume: 77721, }, { date: 1462217640000, open: 50.56, high: 50.56, low: 50.52, close: 50.55, volume: 109330, }, { date: 1462217700000, open: 50.55, high: 50.58, low: 50.54, close: 50.542, volume: 117971, }, { date: 1462217760000, open: 50.545, high: 50.62, low: 50.54, close: 50.62, volume: 131595, }, { date: 1462217820000, open: 50.62, high: 50.64, low: 50.61, close: 50.6399, volume: 136715, }, { date: 1462217880000, open: 50.635, high: 50.68, low: 50.61, close: 50.68, volume: 166729, }, { date: 1462217940000, open: 50.68, high: 50.685, low: 50.64, close: 50.645, volume: 115458, }, { date: 1462218000000, open: 50.64, high: 50.67, low: 50.64, close: 50.66, volume: 105382, }, { date: 1462218060000, open: 50.655, high: 50.67, low: 50.65, close: 50.67, volume: 61760, }, { date: 1462218120000, open: 50.665, high: 50.665, low: 50.65, close: 50.66, volume: 69076, }, { date: 1462218180000, open: 50.65, high: 50.67, low: 50.64, close: 50.645, volume: 129846, }, { date: 1462218240000, open: 50.645, high: 50.75, low: 50.64, close: 50.72, volume: 193414, }, { date: 1462218300000, open: 50.71, high: 50.73, low: 50.69, close: 50.69, volume: 57083, }, { date: 1462218360000, open: 50.695, high: 50.695, low: 50.65, close: 50.667, volume: 129321, }, { date: 1462218420000, open: 50.665, high: 50.69, low: 50.66, close: 50.69, volume: 94844, }, { date: 1462218480000, open: 50.69, high: 50.705, low: 50.67, close: 50.67, volume: 112032, }, { date: 1462218540000, open: 50.67, high: 50.675, low: 50.63, close: 50.6533, volume: 122329, }, { date: 1462218600000, open: 50.656, high: 50.67, low: 50.64, close: 50.65, volume: 64886, }, { date: 1462218660000, open: 50.645, high: 50.66, low: 50.64, close: 50.65, volume: 97294, }, { date: 1462218720000, open: 50.6475, high: 50.65, low: 50.61, close: 50.64, volume: 127183, }, { date: 1462218780000, open: 50.635, high: 50.66, low: 50.63, close: 50.65, volume: 82215, }, { date: 1462218840000, open: 50.65, high: 50.66, low: 50.62, close: 50.65, volume: 134991, }, { date: 1462218900000, open: 50.65, high: 50.67, low: 50.65, close: 50.665, volume: 114461, }, { date: 1462218960000, open: 50.6601, high: 50.67, low: 50.64, close: 50.64, volume: 193977, }, { date: 1462219020000, open: 50.64, high: 50.65, low: 50.61, close: 50.635, volume: 133969, }, { date: 1462219080000, open: 50.635, high: 50.66, low: 50.615, close: 50.65, volume: 150671, }, { date: 1462219140000, open: 50.65, high: 50.66, low: 50.64, close: 50.645, volume: 227904, }, { date: 1462219200000, open: 50.64, high: 50.65, low: 50.59, close: 50.61, volume: 2042513, }, { date: 1462282200000, open: 50.34, high: 50.41, low: 50.3, close: 50.31, volume: 278530, }, { date: 1462282260000, open: 50.31, high: 50.35, low: 50.16, close: 50.21, volume: 99527, }, { date: 1462282320000, open: 50.21, high: 50.231, low: 50.1799, close: 50.18, volume: 59633, }, { date: 1462282380000, open: 50.19, high: 50.225, low: 50.145, close: 50.225, volume: 70851, }, { date: 1462282440000, open: 50.22, high: 50.25, low: 50.16, close: 50.16, volume: 65430, }, { date: 1462282500000, open: 50.16, high: 50.2, low: 50.1, close: 50.175, volume: 102411, }, { date: 1462282560000, open: 50.18, high: 50.24, low: 50.17, close: 50.23, volume: 68389, }, { date: 1462282620000, open: 50.23, high: 50.29, low: 50.19, close: 50.285, volume: 138112, }, { date: 1462282680000, open: 50.2799, high: 50.28, low: 50.21, close: 50.24, volume: 49474, }, { date: 1462282740000, open: 50.236, high: 50.3, low: 50.22, close: 50.26, volume: 95098, }, { date: 1462282800000, open: 50.26, high: 50.29, low: 50.24, close: 50.25, volume: 52896, }, { date: 1462282860000, open: 50.2416, high: 50.25, low: 50.16, close: 50.215, volume: 92093, }, { date: 1462282920000, open: 50.215, high: 50.22, low: 50.2, close: 50.21, volume: 35945, }, { date: 1462282980000, open: 50.2162, high: 50.23, low: 50.16, close: 50.16, volume: 56347, }, { date: 1462283040000, open: 50.15, high: 50.19, low: 50.14, close: 50.17, volume: 58094, }, { date: 1462283100000, open: 50.175, high: 50.18, low: 50.07, close: 50.08, volume: 68382, }, { date: 1462283160000, open: 50.075, high: 50.11, low: 50.03, close: 50.11, volume: 132055, }, { date: 1462283220000, open: 50.1, high: 50.12, low: 50.03, close: 50.05, volume: 88005, }, { date: 1462283280000, open: 50.05, high: 50.096, low: 50.02, close: 50.02, volume: 88338, }, { date: 1462283340000, open: 50.02, high: 50.04, low: 49.96, close: 50.0395, volume: 146919, }, { date: 1462283400000, open: 50.04, high: 50.04, low: 49.95, close: 49.97, volume: 96254, }, { date: 1462283460000, open: 49.97, high: 50, low: 49.92, close: 49.95, volume: 81110, }, { date: 1462283520000, open: 49.94, high: 49.95, low: 49.9, close: 49.9269, volume: 74616, }, { date: 1462283580000, open: 49.92, high: 49.93, low: 49.87, close: 49.895, volume: 79397, }, { date: 1462283640000, open: 49.89, high: 49.9699, low: 49.89, close: 49.955, volume: 61579, }, { date: 1462283700000, open: 49.95, high: 49.97, low: 49.91, close: 49.925, volume: 32367, }, { date: 1462283760000, open: 49.92, high: 50.005, low: 49.92, close: 49.97, volume: 90385, }, { date: 1462283820000, open: 49.97, high: 50.05, low: 49.95, close: 50.01, volume: 136819, }, { date: 1462283880000, open: 50.015, high: 50.06, low: 50.01, close: 50.035, volume: 81739, }, { date: 1462283940000, open: 50.03, high: 50.07, low: 50.02, close: 50.065, volume: 52931, }, { date: 1462284000000, open: 50.06, high: 50.06, low: 50.01, close: 50.04, volume: 135210, }, { date: 1462284060000, open: 50.03, high: 50.03, low: 49.931, close: 49.947, volume: 96228, }, { date: 1462284120000, open: 49.9499, high: 49.95, low: 49.91, close: 49.95, volume: 59533, }, { date: 1462284180000, open: 49.95, high: 49.95, low: 49.91, close: 49.91, volume: 58676, }, { date: 1462284240000, open: 49.91, high: 49.945, low: 49.9, close: 49.93, volume: 70884, }, { date: 1462284300000, open: 49.93, high: 49.93, low: 49.89, close: 49.93, volume: 69465, }, { date: 1462284360000, open: 49.9265, high: 49.95, low: 49.91, close: 49.94, volume: 53616, }, { date: 1462284420000, open: 49.94, high: 49.9476, low: 49.9, close: 49.91, volume: 55350, }, { date: 1462284480000, open: 49.91, high: 49.92, low: 49.9, close: 49.9089, volume: 61659, }, { date: 1462284540000, open: 49.905, high: 49.9199, low: 49.88, close: 49.89, volume: 78078, }, { date: 1462284600000, open: 49.9, high: 49.9, low: 49.87, close: 49.875, volume: 112263, }, { date: 1462284660000, open: 49.87, high: 49.87, low: 49.84, close: 49.85, volume: 100745, }, { date: 1462284720000, open: 49.86, high: 49.8701, low: 49.835, close: 49.835, volume: 107790, }, { date: 1462284780000, open: 49.84, high: 49.845, low: 49.77, close: 49.78, volume: 180614, }, { date: 1462284840000, open: 49.7701, high: 49.775, low: 49.73, close: 49.75, volume: 104650, }, { date: 1462284900000, open: 49.76, high: 49.78, low: 49.72, close: 49.775, volume: 62814, }, { date: 1462284960000, open: 49.7799, high: 49.84, low: 49.765, close: 49.8399, volume: 75747, }, { date: 1462285020000, open: 49.84, high: 49.87, low: 49.84, close: 49.8599, volume: 48064, }, { date: 1462285080000, open: 49.855, high: 49.9001, low: 49.855, close: 49.9, volume: 53400, }, { date: 1462285140000, open: 49.885, high: 49.91, low: 49.86, close: 49.91, volume: 71865, }, { date: 1462285200000, open: 49.9, high: 49.919, low: 49.89, close: 49.91, volume: 61239, }, { date: 1462285260000, open: 49.915, high: 49.93, low: 49.89, close: 49.92, volume: 89001, }, { date: 1462285320000, open: 49.92, high: 49.95, low: 49.89, close: 49.9, volume: 52264, }, { date: 1462285380000, open: 49.9, high: 49.92, low: 49.88, close: 49.915, volume: 40189, }, { date: 1462285440000, open: 49.92, high: 49.93, low: 49.9, close: 49.9099, volume: 36422, }, { date: 1462285500000, open: 49.9, high: 49.98, low: 49.89, close: 49.97, volume: 72764, }, { date: 1462285560000, open: 49.9701, high: 49.9701, low: 49.93, close: 49.94, volume: 38641, }, { date: 1462285620000, open: 49.945, high: 49.95, low: 49.9, close: 49.91, volume: 61244, }, { date: 1462285680000, open: 49.92, high: 49.94, low: 49.89, close: 49.925, volume: 44391, }, { date: 1462285740000, open: 49.9298, high: 49.94, low: 49.9, close: 49.94, volume: 83362, }, { date: 1462285800000, open: 49.94, high: 49.97, low: 49.9, close: 49.91, volume: 149969, }, { date: 1462285860000, open: 49.915, high: 49.92, low: 49.88, close: 49.9166, volume: 85478, }, { date: 1462285920000, open: 49.92, high: 49.92, low: 49.87, close: 49.9, volume: 66278, }, { date: 1462285980000, open: 49.9, high: 49.94, low: 49.89, close: 49.9, volume: 59043, }, { date: 1462286040000, open: 49.905, high: 49.95, low: 49.905, close: 49.95, volume: 23017, }, { date: 1462286100000, open: 49.95, high: 49.98, low: 49.95, close: 49.97, volume: 76925, }, { date: 1462286160000, open: 49.965, high: 49.98, low: 49.94, close: 49.945, volume: 99288, }, { date: 1462286220000, open: 49.94, high: 49.99, low: 49.935, close: 49.98, volume: 77677, }, { date: 1462286280000, open: 49.9773, high: 50.01, low: 49.9773, close: 49.985, volume: 41177, }, { date: 1462286340000, open: 49.9836, high: 49.995, low: 49.96, close: 49.975, volume: 51441, }, { date: 1462286400000, open: 49.975, high: 50.015, low: 49.975, close: 50.005, volume: 100544, }, { date: 1462286460000, open: 50.005, high: 50.02, low: 49.95, close: 49.95, volume: 63875, }, { date: 1462286520000, open: 49.945, high: 50.01, low: 49.94, close: 50.005, volume: 53089, }, { date: 1462286580000, open: 50.0099, high: 50.05, low: 50, close: 50.045, volume: 40383, }, { date: 1462286640000, open: 50.0435, high: 50.07, low: 50.035, close: 50.061, volume: 55670, }, { date: 1462286700000, open: 50.07, high: 50.09, low: 50.05, close: 50.055, volume: 52049, }, { date: 1462286760000, open: 50.05, high: 50.08, low: 50.04, close: 50.05, volume: 93493, }, { date: 1462286820000, open: 50.05, high: 50.07, low: 50.045, close: 50.05, volume: 28258, }, { date: 1462286880000, open: 50.045, high: 50.06, low: 50.03, close: 50.0434, volume: 70159, }, { date: 1462286940000, open: 50.04, high: 50.08, low: 50.04, close: 50.06, volume: 52791, }, { date: 1462287000000, open: 50.06, high: 50.065, low: 50.01, close: 50.025, volume: 70337, }, { date: 1462287060000, open: 50.02, high: 50.055, low: 50.02, close: 50.032, volume: 23476, }, { date: 1462287120000, open: 50.04, high: 50.05, low: 50.01, close: 50.037, volume: 45403, }, { date: 1462287180000, open: 50.0372, high: 50.0372, low: 49.97, close: 49.97, volume: 34463, }, { date: 1462287240000, open: 49.976, high: 49.98, low: 49.94, close: 49.95, volume: 43513, }, { date: 1462287300000, open: 49.957, high: 49.9599, low: 49.92, close: 49.94, volume: 37459, }, { date: 1462287360000, open: 49.94, high: 49.97, low: 49.91, close: 49.935, volume: 109777, }, { date: 1462287420000, open: 49.94, high: 49.945, low: 49.9, close: 49.91, volume: 44564, }, { date: 1462287480000, open: 49.9, high: 49.9, low: 49.87, close: 49.885, volume: 58506, }, { date: 1462287540000, open: 49.88, high: 49.89, low: 49.85, close: 49.8799, volume: 50068, }, { date: 1462287600000, open: 49.875, high: 49.94, low: 49.86, close: 49.93, volume: 55359, }, { date: 1462287660000, open: 49.93, high: 49.935, low: 49.89, close: 49.9158, volume: 43492, }, { date: 1462287720000, open: 49.91, high: 49.91, low: 49.8333, close: 49.87, volume: 51928, }, { date: 1462287780000, open: 49.86, high: 49.8789, low: 49.84, close: 49.87, volume: 43204, }, { date: 1462287840000, open: 49.87, high: 49.96, low: 49.8658, close: 49.95, volume: 75549, }, { date: 1462287900000, open: 49.955, high: 49.97, low: 49.95, close: 49.965, volume: 26805, }, { date: 1462287960000, open: 49.97, high: 49.99, low: 49.9431, close: 49.989, volume: 68690, }, { date: 1462288020000, open: 49.98, high: 50, low: 49.97, close: 49.99, volume: 40927, }, { date: 1462288080000, open: 49.99, high: 50.025, low: 49.99, close: 50.02, volume: 34319, }, { date: 1462288140000, open: 50.02, high: 50.025, low: 49.97, close: 49.9832, volume: 31452, }, { date: 1462288200000, open: 49.99, high: 49.99, low: 49.969, close: 49.97, volume: 42674, }, { date: 1462288260000, open: 49.97, high: 49.975, low: 49.915, close: 49.92, volume: 34514, }, { date: 1462288320000, open: 49.93, high: 49.93, low: 49.9, close: 49.915, volume: 37255, }, { date: 1462288380000, open: 49.92, high: 49.94, low: 49.91, close: 49.937, volume: 64019, }, { date: 1462288440000, open: 49.9369, high: 49.95, low: 49.92, close: 49.945, volume: 44020, }, { date: 1462288500000, open: 49.9431, high: 49.96, low: 49.9334, close: 49.94, volume: 41702, }, { date: 1462288560000, open: 49.94, high: 49.95, low: 49.92, close: 49.94, volume: 65770, }, { date: 1462288620000, open: 49.95, high: 49.95, low: 49.89, close: 49.89, volume: 46841, }, { date: 1462288680000, open: 49.896, high: 49.9166, low: 49.875, close: 49.88, volume: 51638, }, { date: 1462288740000, open: 49.875, high: 49.94, low: 49.87, close: 49.9, volume: 128127, }, { date: 1462288800000, open: 49.905, high: 49.905, low: 49.87, close: 49.875, volume: 40708, }, { date: 1462288860000, open: 49.87, high: 49.88, low: 49.86, close: 49.865, volume: 55574, }, { date: 1462288920000, open: 49.867, high: 49.88, low: 49.85, close: 49.85, volume: 29316, }, { date: 1462288980000, open: 49.855, high: 49.86, low: 49.84, close: 49.85, volume: 37856, }, { date: 1462289040000, open: 49.84, high: 49.86, low: 49.84, close: 49.86, volume: 31933, }, { date: 1462289100000, open: 49.86, high: 49.86, low: 49.8, close: 49.825, volume: 68046, }, { date: 1462289160000, open: 49.8273, high: 49.83, low: 49.8, close: 49.815, volume: 41064, }, { date: 1462289220000, open: 49.82, high: 49.855, low: 49.815, close: 49.85, volume: 39874, }, { date: 1462289280000, open: 49.85, high: 49.88, low: 49.8429, close: 49.88, volume: 34030, }, { date: 1462289340000, open: 49.8799, high: 49.88, low: 49.85, close: 49.855, volume: 74839, }, { date: 1462289400000, open: 49.8665, high: 49.87, low: 49.825, close: 49.825, volume: 64261, }, { date: 1462289460000, open: 49.83, high: 49.83, low: 49.74, close: 49.7743, volume: 152128, }, { date: 1462289520000, open: 49.77, high: 49.77, low: 49.71, close: 49.7512, volume: 80282, }, { date: 1462289580000, open: 49.7566, high: 49.79, low: 49.75, close: 49.79, volume: 53422, }, { date: 1462289640000, open: 49.79, high: 49.79, low: 49.77, close: 49.78, volume: 14025, }, { date: 1462289700000, open: 49.78, high: 49.7835, low: 49.76, close: 49.7798, volume: 23895, }, { date: 1462289760000, open: 49.78, high: 49.82, low: 49.78, close: 49.795, volume: 35300, }, { date: 1462289820000, open: 49.79, high: 49.8, low: 49.78, close: 49.8, volume: 17685, }, { date: 1462289880000, open: 49.81, high: 49.82, low: 49.8, close: 49.8, volume: 23192, }, { date: 1462289940000, open: 49.8, high: 49.81, low: 49.775, close: 49.79, volume: 38021, }, { date: 1462290000000, open: 49.79, high: 49.79, low: 49.78, close: 49.7864, volume: 22970, }, { date: 1462290060000, open: 49.78, high: 49.786, low: 49.765, close: 49.7799, volume: 34380, }, { date: 1462290120000, open: 49.7735, high: 49.78, low: 49.76, close: 49.77, volume: 19486, }, { date: 1462290180000, open: 49.7771, high: 49.81, low: 49.775, close: 49.81, volume: 29303, }, { date: 1462290240000, open: 49.805, high: 49.815, low: 49.79, close: 49.8005, volume: 27015, }, { date: 1462290300000, open: 49.81, high: 49.84, low: 49.805, close: 49.83, volume: 34673, }, { date: 1462290360000, open: 49.84, high: 49.85, low: 49.82, close: 49.84, volume: 85920, }, { date: 1462290420000, open: 49.841, high: 49.86, low: 49.84, close: 49.855, volume: 41682, }, { date: 1462290480000, open: 49.855, high: 49.88, low: 49.85, close: 49.87, volume: 24011, }, { date: 1462290540000, open: 49.875, high: 49.91, low: 49.8738, close: 49.907, volume: 48282, }, { date: 1462290600000, open: 49.91, high: 49.91, low: 49.87, close: 49.875, volume: 57441, }, { date: 1462290660000, open: 49.88, high: 49.89, low: 49.84, close: 49.84, volume: 61918, }, { date: 1462290720000, open: 49.853, high: 49.859, low: 49.81, close: 49.81, volume: 42496, }, { date: 1462290780000, open: 49.805, high: 49.82, low: 49.79, close: 49.8099, volume: 43496, }, { date: 1462290840000, open: 49.8085, high: 49.82, low: 49.79, close: 49.8, volume: 30971, }, { date: 1462290900000, open: 49.807, high: 49.85, low: 49.8, close: 49.84, volume: 33615, }, { date: 1462290960000, open: 49.84, high: 49.84, low: 49.8, close: 49.805, volume: 34495, }, { date: 1462291020000, open: 49.805, high: 49.805, low: 49.77, close: 49.785, volume: 38033, }, { date: 1462291080000, open: 49.79, high: 49.795, low: 49.76, close: 49.7699, volume: 47883, }, { date: 1462291140000, open: 49.7601, high: 49.8, low: 49.7601, close: 49.8, volume: 41063, }, { date: 1462291200000, open: 49.8, high: 49.8199, low: 49.76, close: 49.76, volume: 56277, }, { date: 1462291260000, open: 49.77, high: 49.79, low: 49.77, close: 49.78, volume: 18903, }, { date: 1462291320000, open: 49.78, high: 49.81, low: 49.77, close: 49.785, volume: 45529, }, { date: 1462291380000, open: 49.785, high: 49.785, low: 49.74, close: 49.76, volume: 72463, }, { date: 1462291440000, open: 49.75, high: 49.7599, low: 49.72, close: 49.72, volume: 18067, }, { date: 1462291500000, open: 49.72, high: 49.725, low: 49.7, close: 49.71, volume: 61256, }, { date: 1462291560000, open: 49.7, high: 49.71, low: 49.69, close: 49.71, volume: 63727, }, { date: 1462291620000, open: 49.7, high: 49.705, low: 49.68, close: 49.69, volume: 55343, }, { date: 1462291680000, open: 49.6964, high: 49.6999, low: 49.6599, close: 49.68, volume: 46612, }, { date: 1462291740000, open: 49.6765, high: 49.71, low: 49.67, close: 49.69, volume: 75439, }, { date: 1462291800000, open: 49.7, high: 49.72, low: 49.685, close: 49.71, volume: 25062, }, { date: 1462291860000, open: 49.7059, high: 49.71, low: 49.66, close: 49.6899, volume: 59439, }, { date: 1462291920000, open: 49.6854, high: 49.73, low: 49.6854, close: 49.7199, volume: 44614, }, { date: 1462291980000, open: 49.71, high: 49.72, low: 49.7, close: 49.7, volume: 29683, }, { date: 1462292040000, open: 49.7, high: 49.705, low: 49.66, close: 49.66, volume: 75833, }, { date: 1462292100000, open: 49.67, high: 49.69, low: 49.64, close: 49.6599, volume: 85386, }, { date: 1462292160000, open: 49.65, high: 49.6777, low: 49.65, close: 49.6665, volume: 21273, }, { date: 1462292220000, open: 49.66, high: 49.69, low: 49.66, close: 49.69, volume: 26666, }, { date: 1462292280000, open: 49.68, high: 49.715, low: 49.68, close: 49.705, volume: 64542, }, { date: 1462292340000, open: 49.71, high: 49.71, low: 49.67, close: 49.68, volume: 40799, }, { date: 1462292400000, open: 49.68, high: 49.7, low: 49.67, close: 49.6959, volume: 21240, }, { date: 1462292460000, open: 49.695, high: 49.74, low: 49.69, close: 49.74, volume: 27503, }, { date: 1462292520000, open: 49.73, high: 49.74, low: 49.68, close: 49.69, volume: 53168, }, { date: 1462292580000, open: 49.71, high: 49.75, low: 49.71, close: 49.75, volume: 13807, }, { date: 1462292640000, open: 49.745, high: 49.775, low: 49.745, close: 49.76, volume: 26413, }, { date: 1462292700000, open: 49.76, high: 49.769, low: 49.74, close: 49.74, volume: 43267, }, { date: 1462292760000, open: 49.7462, high: 49.78, low: 49.7462, close: 49.775, volume: 43127, }, { date: 1462292820000, open: 49.775, high: 49.7899, low: 49.76, close: 49.78, volume: 59622, }, { date: 1462292880000, open: 49.78, high: 49.82, low: 49.77, close: 49.8092, volume: 56499, }, { date: 1462292940000, open: 49.8, high: 49.82, low: 49.79, close: 49.8, volume: 41640, }, { date: 1462293000000, open: 49.805, high: 49.81, low: 49.8, close: 49.81, volume: 57895, }, { date: 1462293060000, open: 49.81, high: 49.84, low: 49.81, close: 49.84, volume: 77913, }, { date: 1462293120000, open: 49.84, high: 49.86, low: 49.83, close: 49.8573, volume: 32019, }, { date: 1462293180000, open: 49.855, high: 49.86, low: 49.83, close: 49.835, volume: 33465, }, { date: 1462293240000, open: 49.83, high: 49.84, low: 49.82, close: 49.84, volume: 27745, }, { date: 1462293300000, open: 49.83, high: 49.85, low: 49.83, close: 49.84, volume: 12028, }, { date: 1462293360000, open: 49.85, high: 49.86, low: 49.81, close: 49.83, volume: 51900, }, { date: 1462293420000, open: 49.82, high: 49.85, low: 49.82, close: 49.85, volume: 28390, }, { date: 1462293480000, open: 49.85, high: 49.87, low: 49.84, close: 49.86, volume: 38423, }, { date: 1462293540000, open: 49.86, high: 49.87, low: 49.84, close: 49.86, volume: 22093, }, { date: 1462293600000, open: 49.86, high: 49.87, low: 49.85, close: 49.859, volume: 12917, }, { date: 1462293660000, open: 49.85, high: 49.89, low: 49.85, close: 49.887, volume: 39148, }, { date: 1462293720000, open: 49.8864, high: 49.93, low: 49.8864, close: 49.92, volume: 53503, }, { date: 1462293780000, open: 49.9236, high: 49.93, low: 49.92, close: 49.925, volume: 19617, }, { date: 1462293840000, open: 49.93, high: 49.94, low: 49.91, close: 49.91, volume: 42580, }, { date: 1462293900000, open: 49.91, high: 49.92, low: 49.89, close: 49.9, volume: 18188, }, { date: 1462293960000, open: 49.89, high: 49.9, low: 49.87, close: 49.88, volume: 160174, }, { date: 1462294020000, open: 49.885, high: 49.93, low: 49.88, close: 49.926, volume: 19565, }, { date: 1462294080000, open: 49.9241, high: 49.935, low: 49.88, close: 49.89, volume: 41023, }, { date: 1462294140000, open: 49.885, high: 49.89, low: 49.87, close: 49.8899, volume: 29108, }, { date: 1462294200000, open: 49.885, high: 49.91, low: 49.8826, close: 49.9099, volume: 14990, }, { date: 1462294260000, open: 49.9099, high: 49.94, low: 49.9, close: 49.936, volume: 19732, }, { date: 1462294320000, open: 49.93, high: 49.96, low: 49.93, close: 49.959, volume: 53944, }, { date: 1462294380000, open: 49.95, high: 49.96, low: 49.9, close: 49.9092, volume: 45988, }, { date: 1462294440000, open: 49.915, high: 49.94, low: 49.91, close: 49.94, volume: 11120, }, { date: 1462294500000, open: 49.94, high: 49.94, low: 49.9, close: 49.9, volume: 46393, }, { date: 1462294560000, open: 49.905, high: 49.93, low: 49.9027, close: 49.9262, volume: 11733, }, { date: 1462294620000, open: 49.93, high: 49.95, low: 49.92, close: 49.94, volume: 21991, }, { date: 1462294680000, open: 49.94, high: 49.94, low: 49.92, close: 49.92, volume: 24449, }, { date: 1462294740000, open: 49.93, high: 49.94, low: 49.91, close: 49.92, volume: 47919, }, { date: 1462294800000, open: 49.92, high: 49.935, low: 49.91, close: 49.91, volume: 44687, }, { date: 1462294860000, open: 49.91, high: 49.92, low: 49.895, close: 49.9, volume: 74581, }, { date: 1462294920000, open: 49.9, high: 49.91, low: 49.87, close: 49.87, volume: 30674, }, { date: 1462294980000, open: 49.87, high: 49.88, low: 49.87, close: 49.875, volume: 35494, }, { date: 1462295040000, open: 49.875, high: 49.877, low: 49.87, close: 49.8767, volume: 22472, }, { date: 1462295100000, open: 49.88, high: 49.88, low: 49.85, close: 49.85, volume: 66105, }, { date: 1462295160000, open: 49.8532, high: 49.86, low: 49.84, close: 49.85, volume: 24163, }, { date: 1462295220000, open: 49.84, high: 49.85, low: 49.83, close: 49.835, volume: 43438, }, { date: 1462295280000, open: 49.835, high: 49.88, low: 49.83, close: 49.88, volume: 81043, }, { date: 1462295340000, open: 49.875, high: 49.93, low: 49.87, close: 49.9299, volume: 23234, }, { date: 1462295400000, open: 49.9276, high: 49.96, low: 49.9276, close: 49.955, volume: 19039, }, { date: 1462295460000, open: 49.96, high: 49.98, low: 49.94, close: 49.94, volume: 54100, }, { date: 1462295520000, open: 49.94, high: 49.945, low: 49.92, close: 49.9259, volume: 51781, }, { date: 1462295580000, open: 49.92, high: 49.94, low: 49.92, close: 49.93, volume: 25940, }, { date: 1462295640000, open: 49.93, high: 49.93, low: 49.9, close: 49.9, volume: 26407, }, { date: 1462295700000, open: 49.905, high: 49.92, low: 49.89, close: 49.895, volume: 38726, }, { date: 1462295760000, open: 49.895, high: 49.94, low: 49.895, close: 49.9399, volume: 19677, }, { date: 1462295820000, open: 49.94, high: 49.97, low: 49.93, close: 49.96, volume: 22787, }, { date: 1462295880000, open: 49.95, high: 49.96, low: 49.92, close: 49.9252, volume: 24773, }, { date: 1462295940000, open: 49.93, high: 49.95, low: 49.93, close: 49.95, volume: 13271, }, { date: 1462296000000, open: 49.9468, high: 49.97, low: 49.945, close: 49.9601, volume: 33532, }, { date: 1462296060000, open: 49.9695, high: 49.97, low: 49.93, close: 49.945, volume: 25070, }, { date: 1462296120000, open: 49.945, high: 49.97, low: 49.94, close: 49.965, volume: 24071, }, { date: 1462296180000, open: 49.965, high: 50, low: 49.965, close: 49.98, volume: 46170, }, { date: 1462296240000, open: 49.975, high: 49.98, low: 49.94, close: 49.94, volume: 16777, }, { date: 1462296300000, open: 49.945, high: 49.975, low: 49.92, close: 49.9699, volume: 58064, }, { date: 1462296360000, open: 49.9659, high: 49.98, low: 49.95, close: 49.98, volume: 31590, }, { date: 1462296420000, open: 49.985, high: 50.03, low: 49.985, close: 49.99, volume: 57074, }, { date: 1462296480000, open: 49.9999, high: 50.01, low: 49.99, close: 50, volume: 26668, }, { date: 1462296540000, open: 50.0024, high: 50.01, low: 49.985, close: 49.985, volume: 30432, }, { date: 1462296600000, open: 49.9801, high: 50.015, low: 49.98, close: 50.015, volume: 66359, }, { date: 1462296660000, open: 50.015, high: 50.03, low: 50.015, close: 50.025, volume: 29697, }, { date: 1462296720000, open: 50.02, high: 50.02, low: 49.98, close: 50, volume: 37207, }, { date: 1462296780000, open: 49.99, high: 50.01, low: 49.9801, close: 49.99, volume: 66624, }, { date: 1462296840000, open: 49.98, high: 49.9876, low: 49.94, close: 49.9564, volume: 36364, }, { date: 1462296900000, open: 49.96, high: 50, low: 49.96, close: 50, volume: 19614, }, { date: 1462296960000, open: 49.997, high: 50, low: 49.97, close: 49.976, volume: 25224, }, { date: 1462297020000, open: 49.97, high: 49.975, low: 49.94, close: 49.95, volume: 42172, }, { date: 1462297080000, open: 49.94, high: 49.96, low: 49.94, close: 49.94, volume: 16309, }, { date: 1462297140000, open: 49.94, high: 50, low: 49.93, close: 49.9862, volume: 104018, }, { date: 1462297200000, open: 49.99, high: 50.01, low: 49.975, close: 50, volume: 45759, }, { date: 1462297260000, open: 50.001, high: 50.01, low: 49.99, close: 50, volume: 70534, }, { date: 1462297320000, open: 50, high: 50.005, low: 49.985, close: 49.99, volume: 38923, }, { date: 1462297380000, open: 49.9992, high: 50.005, low: 49.99, close: 49.99, volume: 38990, }, { date: 1462297440000, open: 49.995, high: 50, low: 49.98, close: 49.985, volume: 31969, }, { date: 1462297500000, open: 49.98, high: 49.985, low: 49.93, close: 49.94, volume: 64368, }, { date: 1462297560000, open: 49.945, high: 49.99, low: 49.945, close: 49.985, volume: 22600, }, { date: 1462297620000, open: 49.99, high: 50, low: 49.98, close: 49.995, volume: 21101, }, { date: 1462297680000, open: 50, high: 50.0098, low: 49.991, close: 50, volume: 29891, }, { date: 1462297740000, open: 50, high: 50, low: 49.98, close: 49.99, volume: 26326, }, { date: 1462297800000, open: 49.99, high: 50.01, low: 49.98, close: 50, volume: 72156, }, { date: 1462297860000, open: 50, high: 50.005, low: 49.99, close: 49.995, volume: 24330, }, { date: 1462297920000, open: 49.995, high: 50.01, low: 49.995, close: 50.01, volume: 39104, }, { date: 1462297980000, open: 50.01, high: 50.05, low: 49.99, close: 50.025, volume: 85578, }, { date: 1462298040000, open: 50.03, high: 50.0373, low: 50.01, close: 50.015, volume: 52867, }, { date: 1462298100000, open: 50.01, high: 50.0198, low: 49.99, close: 50, volume: 73653, }, { date: 1462298160000, open: 50.005, high: 50.01, low: 49.995, close: 50, volume: 165507, }, { date: 1462298220000, open: 50.0032, high: 50.015, low: 49.995, close: 50, volume: 32074, }, { date: 1462298280000, open: 49.99, high: 50, low: 49.98, close: 49.98, volume: 29795, }, { date: 1462298340000, open: 49.98, high: 49.985, low: 49.97, close: 49.9727, volume: 27362, }, { date: 1462298400000, open: 49.975, high: 49.978, low: 49.95, close: 49.96, volume: 38146, }, { date: 1462298460000, open: 49.96, high: 49.9871, low: 49.95, close: 49.9871, volume: 37091, }, { date: 1462298520000, open: 49.98, high: 50, low: 49.98, close: 49.98, volume: 32224, }, { date: 1462298580000, open: 49.98, high: 49.98, low: 49.94, close: 49.955, volume: 39649, }, { date: 1462298640000, open: 49.96, high: 49.97, low: 49.95, close: 49.96, volume: 23857, }, { date: 1462298700000, open: 49.95, high: 49.99, low: 49.95, close: 49.988, volume: 57183, }, { date: 1462298760000, open: 49.98, high: 49.99, low: 49.97, close: 49.98, volume: 121942, }, { date: 1462298820000, open: 49.97, high: 49.98, low: 49.95, close: 49.955, volume: 70313, }, { date: 1462298880000, open: 49.955, high: 49.97, low: 49.95, close: 49.9699, volume: 13668, }, { date: 1462298940000, open: 49.9699, high: 49.97, low: 49.95, close: 49.955, volume: 28542, }, { date: 1462299000000, open: 49.96, high: 49.99, low: 49.955, close: 49.98, volume: 45275, }, { date: 1462299060000, open: 49.97, high: 50.005, low: 49.97, close: 50.005, volume: 76889, }, { date: 1462299120000, open: 50.0036, high: 50.005, low: 49.98, close: 49.98, volume: 38210, }, { date: 1462299180000, open: 49.98, high: 49.98, low: 49.88, close: 49.88, volume: 114169, }, { date: 1462299240000, open: 49.87, high: 49.8798, low: 49.84, close: 49.855, volume: 59217, }, { date: 1462299300000, open: 49.85, high: 49.8699, low: 49.85, close: 49.86, volume: 19962, }, { date: 1462299360000, open: 49.869, high: 49.9, low: 49.8601, close: 49.89, volume: 14436, }, { date: 1462299420000, open: 49.89, high: 49.9, low: 49.86, close: 49.87, volume: 39463, }, { date: 1462299480000, open: 49.87, high: 49.88, low: 49.8, close: 49.81, volume: 77503, }, { date: 1462299540000, open: 49.8, high: 49.83, low: 49.8, close: 49.81, volume: 41273, }, { date: 1462299600000, open: 49.8, high: 49.8064, low: 49.76, close: 49.77, volume: 75871, }, { date: 1462299660000, open: 49.7798, high: 49.78, low: 49.77, close: 49.7764, volume: 22030, }, { date: 1462299720000, open: 49.771, high: 49.81, low: 49.75, close: 49.8, volume: 48068, }, { date: 1462299780000, open: 49.8077, high: 49.81, low: 49.79, close: 49.8, volume: 24200, }, { date: 1462299840000, open: 49.8, high: 49.8136, low: 49.78, close: 49.79, volume: 41266, }, { date: 1462299900000, open: 49.8, high: 49.86, low: 49.8, close: 49.84, volume: 35107, }, { date: 1462299960000, open: 49.835, high: 49.845, low: 49.82, close: 49.845, volume: 32927, }, { date: 1462300020000, open: 49.845, high: 49.85, low: 49.82, close: 49.83, volume: 18915, }, { date: 1462300080000, open: 49.825, high: 49.84, low: 49.805, close: 49.84, volume: 29993, }, { date: 1462300140000, open: 49.83, high: 49.9, low: 49.83, close: 49.8799, volume: 54054, }, { date: 1462300200000, open: 49.87, high: 49.88, low: 49.82, close: 49.88, volume: 47534, }, { date: 1462300260000, open: 49.88, high: 49.88, low: 49.85, close: 49.855, volume: 15913, }, { date: 1462300320000, open: 49.86, high: 49.88, low: 49.86, close: 49.87, volume: 19181, }, { date: 1462300380000, open: 49.87, high: 49.87, low: 49.85, close: 49.85, volume: 14659, }, { date: 1462300440000, open: 49.855, high: 49.88, low: 49.85, close: 49.87, volume: 34431, }, { date: 1462300500000, open: 49.87, high: 49.87, low: 49.815, close: 49.82, volume: 81180, }, { date: 1462300560000, open: 49.83, high: 49.92, low: 49.83, close: 49.9, volume: 157587, }, { date: 1462300620000, open: 49.895, high: 49.9199, low: 49.88, close: 49.895, volume: 29924, }, { date: 1462300680000, open: 49.89, high: 49.89, low: 49.88, close: 49.885, volume: 20014, }, { date: 1462300740000, open: 49.885, high: 49.8975, low: 49.86, close: 49.875, volume: 32799, }, { date: 1462300800000, open: 49.88, high: 49.91, low: 49.875, close: 49.88, volume: 21933, }, { date: 1462300860000, open: 49.88, high: 49.8829, low: 49.8, close: 49.8271, volume: 62189, }, { date: 1462300920000, open: 49.825, high: 49.83, low: 49.81, close: 49.81, volume: 26930, }, { date: 1462300980000, open: 49.81, high: 49.85, low: 49.8, close: 49.84, volume: 31520, }, { date: 1462301040000, open: 49.83, high: 49.84, low: 49.8, close: 49.8271, volume: 27790, }, { date: 1462301100000, open: 49.83, high: 49.85, low: 49.81, close: 49.85, volume: 17643, }, { date: 1462301160000, open: 49.84, high: 49.85, low: 49.811, close: 49.8462, volume: 43072, }, { date: 1462301220000, open: 49.849, high: 49.86, low: 49.82, close: 49.85, volume: 24109, }, { date: 1462301280000, open: 49.859, high: 49.89, low: 49.8536, close: 49.8558, volume: 48154, }, { date: 1462301340000, open: 49.855, high: 49.865, low: 49.8, close: 49.8099, volume: 77665, }, { date: 1462301400000, open: 49.8, high: 49.8, low: 49.73, close: 49.77, volume: 90029, }, { date: 1462301460000, open: 49.78, high: 49.79, low: 49.73, close: 49.73, volume: 39586, }, { date: 1462301520000, open: 49.73, high: 49.79, low: 49.73, close: 49.78, volume: 33808, }, { date: 1462301580000, open: 49.77, high: 49.7799, low: 49.75, close: 49.7745, volume: 57691, }, { date: 1462301640000, open: 49.77, high: 49.775, low: 49.755, close: 49.755, volume: 45988, }, { date: 1462301700000, open: 49.75, high: 49.8, low: 49.72, close: 49.7999, volume: 62003, }, { date: 1462301760000, open: 49.795, high: 49.81, low: 49.79, close: 49.8, volume: 30739, }, { date: 1462301820000, open: 49.795, high: 49.81, low: 49.765, close: 49.7822, volume: 44895, }, { date: 1462301880000, open: 49.78, high: 49.8, low: 49.77, close: 49.77, volume: 41719, }, { date: 1462301940000, open: 49.77, high: 49.835, low: 49.765, close: 49.83, volume: 44715, }, { date: 1462302000000, open: 49.83, high: 49.84, low: 49.81, close: 49.82, volume: 37787, }, { date: 1462302060000, open: 49.8238, high: 49.84, low: 49.82, close: 49.82, volume: 28855, }, { date: 1462302120000, open: 49.82, high: 49.8282, low: 49.785, close: 49.79, volume: 36733, }, { date: 1462302180000, open: 49.79, high: 49.85, low: 49.79, close: 49.82, volume: 29830, }, { date: 1462302240000, open: 49.82, high: 49.83, low: 49.8, close: 49.8192, volume: 36702, }, { date: 1462302300000, open: 49.8159, high: 49.82, low: 49.8, close: 49.815, volume: 19259, }, { date: 1462302360000, open: 49.815, high: 49.86, low: 49.815, close: 49.8435, volume: 48363, }, { date: 1462302420000, open: 49.8412, high: 49.85, low: 49.81, close: 49.82, volume: 39882, }, { date: 1462302480000, open: 49.83, high: 49.845, low: 49.83, close: 49.83, volume: 17381, }, { date: 1462302540000, open: 49.8436, high: 49.8464, low: 49.83, close: 49.84, volume: 28866, }, { date: 1462302600000, open: 49.845, high: 49.855, low: 49.83, close: 49.85, volume: 43089, }, { date: 1462302660000, open: 49.8573, high: 49.87, low: 49.845, close: 49.845, volume: 50646, }, { date: 1462302720000, open: 49.845, high: 49.845, low: 49.77, close: 49.77, volume: 90305, }, { date: 1462302780000, open: 49.7787, high: 49.78, low: 49.73, close: 49.74, volume: 57729, }, { date: 1462302840000, open: 49.75, high: 49.76, low: 49.73, close: 49.7473, volume: 27321, }, { date: 1462302900000, open: 49.75, high: 49.76, low: 49.72, close: 49.725, volume: 47981, }, { date: 1462302960000, open: 49.73, high: 49.76, low: 49.72, close: 49.76, volume: 36462, }, { date: 1462303020000, open: 49.751, high: 49.76, low: 49.73, close: 49.7536, volume: 59134, }, { date: 1462303080000, open: 49.755, high: 49.77, low: 49.74, close: 49.7499, volume: 52083, }, { date: 1462303140000, open: 49.75, high: 49.8, low: 49.745, close: 49.79, volume: 48992, }, { date: 1462303200000, open: 49.78, high: 49.8, low: 49.78, close: 49.79, volume: 24692, }, { date: 1462303260000, open: 49.79, high: 49.81, low: 49.78, close: 49.805, volume: 25315, }, { date: 1462303320000, open: 49.8021, high: 49.83, low: 49.8, close: 49.815, volume: 54007, }, { date: 1462303380000, open: 49.81, high: 49.815, low: 49.76, close: 49.7799, volume: 78988, }, { date: 1462303440000, open: 49.77, high: 49.77, low: 49.71, close: 49.7165, volume: 137651, }, { date: 1462303500000, open: 49.71, high: 49.75, low: 49.71, close: 49.74, volume: 34810, }, { date: 1462303560000, open: 49.74, high: 49.76, low: 49.73, close: 49.76, volume: 44455, }, { date: 1462303620000, open: 49.76, high: 49.8, low: 49.76, close: 49.79, volume: 51179, }, { date: 1462303680000, open: 49.78, high: 49.795, low: 49.77, close: 49.775, volume: 24708, }, { date: 1462303740000, open: 49.775, high: 49.82, low: 49.76, close: 49.805, volume: 108617, }, { date: 1462303800000, open: 49.81, high: 49.8199, low: 49.75, close: 49.75, volume: 65273, }, { date: 1462303860000, open: 49.755, high: 49.78, low: 49.73, close: 49.78, volume: 55646, }, { date: 1462303920000, open: 49.7799, high: 49.795, low: 49.74, close: 49.745, volume: 51398, }, { date: 1462303980000, open: 49.745, high: 49.75, low: 49.69, close: 49.69, volume: 96182, }, { date: 1462304040000, open: 49.68, high: 49.6964, low: 49.61, close: 49.67, volume: 136351, }, { date: 1462304100000, open: 49.67, high: 49.71, low: 49.665, close: 49.7, volume: 76097, }, { date: 1462304160000, open: 49.705, high: 49.73, low: 49.675, close: 49.68, volume: 68882, }, { date: 1462304220000, open: 49.67, high: 49.68, low: 49.63, close: 49.675, volume: 105484, }, { date: 1462304280000, open: 49.68, high: 49.73, low: 49.675, close: 49.68, volume: 99017, }, { date: 1462304340000, open: 49.69, high: 49.705, low: 49.68, close: 49.6959, volume: 47308, }, { date: 1462304400000, open: 49.69, high: 49.7, low: 49.66, close: 49.68, volume: 56501, }, { date: 1462304460000, open: 49.68, high: 49.69, low: 49.65, close: 49.66, volume: 80404, }, { date: 1462304520000, open: 49.65, high: 49.6501, low: 49.6, close: 49.6199, volume: 97964, }, { date: 1462304580000, open: 49.62, high: 49.64, low: 49.6, close: 49.615, volume: 98001, }, { date: 1462304640000, open: 49.614, high: 49.66, low: 49.6, close: 49.64, volume: 93245, }, { date: 1462304700000, open: 49.64, high: 49.66, low: 49.605, close: 49.66, volume: 87834, }, { date: 1462304760000, open: 49.66, high: 49.72, low: 49.65, close: 49.69, volume: 118407, }, { date: 1462304820000, open: 49.68, high: 49.7, low: 49.67, close: 49.675, volume: 94495, }, { date: 1462304880000, open: 49.67, high: 49.72, low: 49.67, close: 49.72, volume: 63806, }, { date: 1462304940000, open: 49.7171, high: 49.7695, low: 49.71, close: 49.765, volume: 112030, }, { date: 1462305000000, open: 49.76, high: 49.79, low: 49.74, close: 49.785, volume: 160269, }, { date: 1462305060000, open: 49.78, high: 49.81, low: 49.745, close: 49.78, volume: 95163, }, { date: 1462305120000, open: 49.78, high: 49.8, low: 49.7725, close: 49.8, volume: 97460, }, { date: 1462305180000, open: 49.795, high: 49.8, low: 49.77, close: 49.7801, volume: 133705, }, { date: 1462305240000, open: 49.79, high: 49.79, low: 49.729, close: 49.76, volume: 168220, }, { date: 1462305300000, open: 49.755, high: 49.8, low: 49.75, close: 49.8, volume: 91063, }, { date: 1462305360000, open: 49.8, high: 49.8, low: 49.74, close: 49.74, volume: 166095, }, { date: 1462305420000, open: 49.74, high: 49.75, low: 49.73, close: 49.745, volume: 138299, }, { date: 1462305480000, open: 49.745, high: 49.77, low: 49.725, close: 49.7399, volume: 211212, }, { date: 1462305540000, open: 49.73, high: 49.735, low: 49.68, close: 49.7, volume: 330367, }, { date: 1462305600000, open: 49.685, high: 49.79, low: 49.68, close: 49.78, volume: 2468533, }, { date: 1462368600000, open: 49.84, high: 49.9, low: 49.82, close: 49.85, volume: 376187, }, { date: 1462368660000, open: 49.89, high: 49.89, low: 49.68, close: 49.8325, volume: 175133, }, { date: 1462368720000, open: 49.83, high: 49.88, low: 49.8, close: 49.82, volume: 106704, }, { date: 1462368780000, open: 49.82, high: 49.85, low: 49.78, close: 49.78, volume: 68478, }, { date: 1462368840000, open: 49.77, high: 49.82, low: 49.75, close: 49.77, volume: 66450, }, { date: 1462368900000, open: 49.76, high: 49.78, low: 49.685, close: 49.69, volume: 52779, }, { date: 1462368960000, open: 49.69, high: 49.7, low: 49.64, close: 49.65, volume: 56156, }, { date: 1462369020000, open: 49.65, high: 49.68, low: 49.59, close: 49.6, volume: 69884, }, { date: 1462369080000, open: 49.6, high: 49.61, low: 49.56, close: 49.57, volume: 48353, }, { date: 1462369140000, open: 49.57, high: 49.59, low: 49.46, close: 49.57, volume: 219847, }, { date: 1462369200000, open: 49.575, high: 49.575, low: 49.479, close: 49.525, volume: 116374, }, { date: 1462369260000, open: 49.53, high: 49.57, low: 49.51, close: 49.56, volume: 47282, }, { date: 1462369320000, open: 49.57, high: 49.57, low: 49.5311, close: 49.55, volume: 36764, }, { date: 1462369380000, open: 49.54, high: 49.575, low: 49.4939, close: 49.55, volume: 62273, }, { date: 1462369440000, open: 49.55, high: 49.585, low: 49.52, close: 49.56, volume: 130128, }, { date: 1462369500000, open: 49.56, high: 49.6, low: 49.535, close: 49.59, volume: 91620, }, { date: 1462369560000, open: 49.595, high: 49.6063, low: 49.53, close: 49.55, volume: 151075, }, { date: 1462369620000, open: 49.55, high: 49.56, low: 49.51, close: 49.529, volume: 74028, }, { date: 1462369680000, open: 49.5257, high: 49.53, low: 49.4699, close: 49.5197, volume: 158673, }, { date: 1462369740000, open: 49.51, high: 49.57, low: 49.49, close: 49.56, volume: 49020, }, { date: 1462369800000, open: 49.56, high: 49.61, low: 49.52, close: 49.58, volume: 77336, }, { date: 1462369860000, open: 49.5829, high: 49.67, low: 49.56, close: 49.67, volume: 107758, }, { date: 1462369920000, open: 49.67, high: 49.67, low: 49.58, close: 49.6, volume: 68481, }, { date: 1462369980000, open: 49.59, high: 49.636, low: 49.586, close: 49.59, volume: 47743, }, { date: 1462370040000, open: 49.59, high: 49.6099, low: 49.57, close: 49.575, volume: 43710, }, { date: 1462370100000, open: 49.575, high: 49.59, low: 49.54, close: 49.59, volume: 43452, }, { date: 1462370160000, open: 49.585, high: 49.69, low: 49.585, close: 49.65, volume: 74891, }, { date: 1462370220000, open: 49.66, high: 49.71, low: 49.64, close: 49.65, volume: 59912, }, { date: 1462370280000, open: 49.65, high: 49.705, low: 49.64, close: 49.7, volume: 43065, }, { date: 1462370340000, open: 49.7, high: 49.72, low: 49.6735, close: 49.695, volume: 39650, }, { date: 1462370400000, open: 49.695, high: 49.76, low: 49.6908, close: 49.74, volume: 96328, }, { date: 1462370460000, open: 49.74, high: 49.78, low: 49.71, close: 49.77, volume: 72774, }, { date: 1462370520000, open: 49.77, high: 49.79, low: 49.73, close: 49.73, volume: 50455, }, { date: 1462370580000, open: 49.73, high: 49.7399, low: 49.66, close: 49.7, volume: 67443, }, { date: 1462370640000, open: 49.7, high: 49.715, low: 49.645, close: 49.67, volume: 44465, }, { date: 1462370700000, open: 49.67, high: 49.73, low: 49.66, close: 49.685, volume: 54855, }, { date: 1462370760000, open: 49.68, high: 49.69, low: 49.67, close: 49.69, volume: 16982, }, { date: 1462370820000, open: 49.68, high: 49.72, low: 49.62, close: 49.64, volume: 49058, }, { date: 1462370880000, open: 49.65, high: 49.67, low: 49.641, close: 49.6599, volume: 15492, }, { date: 1462370940000, open: 49.6562, high: 49.72, low: 49.65, close: 49.72, volume: 62035, }, { date: 1462371000000, open: 49.71, high: 49.73, low: 49.695, close: 49.713, volume: 78439, }, { date: 1462371060000, open: 49.71, high: 49.79, low: 49.7, close: 49.7499, volume: 80524, }, { date: 1462371120000, open: 49.75, high: 49.8, low: 49.735, close: 49.77, volume: 105241, }, { date: 1462371180000, open: 49.77, high: 49.78, low: 49.7, close: 49.7, volume: 51964, }, { date: 1462371240000, open: 49.71, high: 49.715, low: 49.7, close: 49.705, volume: 41971, }, { date: 1462371300000, open: 49.705, high: 49.725, low: 49.69, close: 49.72, volume: 48984, }, { date: 1462371360000, open: 49.7252, high: 49.75, low: 49.7252, close: 49.73, volume: 25067, }, { date: 1462371420000, open: 49.73, high: 49.77, low: 49.715, close: 49.77, volume: 45630, }, { date: 1462371480000, open: 49.77, high: 49.78, low: 49.77, close: 49.775, volume: 16731, }, { date: 1462371540000, open: 49.7667, high: 49.78, low: 49.74, close: 49.76, volume: 85974, }, { date: 1462371600000, open: 49.77, high: 49.825, low: 49.76, close: 49.82, volume: 55016, }, { date: 1462371660000, open: 49.8161, high: 49.82, low: 49.78, close: 49.7832, volume: 37245, }, { date: 1462371720000, open: 49.7872, high: 49.84, low: 49.7872, close: 49.84, volume: 31629, }, { date: 1462371780000, open: 49.83, high: 49.845, low: 49.82, close: 49.83, volume: 34162, }, { date: 1462371840000, open: 49.82, high: 49.84, low: 49.8, close: 49.815, volume: 50505, }, { date: 1462371900000, open: 49.82, high: 49.85, low: 49.81, close: 49.84, volume: 49413, }, { date: 1462371960000, open: 49.845, high: 49.85, low: 49.79, close: 49.81, volume: 60942, }, { date: 1462372020000, open: 49.805, high: 49.84, low: 49.8, close: 49.801, volume: 62268, }, { date: 1462372080000, open: 49.81, high: 49.87, low: 49.81, close: 49.83, volume: 87992, }, { date: 1462372140000, open: 49.835, high: 49.87, low: 49.835, close: 49.87, volume: 26158, }, { date: 1462372200000, open: 49.8633, high: 49.91, low: 49.84, close: 49.9, volume: 141581, }, { date: 1462372260000, open: 49.9, high: 49.9399, low: 49.89, close: 49.925, volume: 99747, }, { date: 1462372320000, open: 49.92, high: 49.94, low: 49.9039, close: 49.93, volume: 67896, }, { date: 1462372380000, open: 49.92, high: 49.95, low: 49.91, close: 49.945, volume: 79490, }, { date: 1462372440000, open: 49.95, high: 49.99, low: 49.9402, close: 49.965, volume: 92052, }, { date: 1462372500000, open: 49.97, high: 49.995, low: 49.93, close: 49.93, volume: 106546, }, { date: 1462372560000, open: 49.932, high: 49.94, low: 49.87, close: 49.875, volume: 140096, }, { date: 1462372620000, open: 49.8701, high: 49.9, low: 49.835, close: 49.895, volume: 56996, }, { date: 1462372680000, open: 49.8955, high: 49.935, low: 49.895, close: 49.93, volume: 77582, }, { date: 1462372740000, open: 49.939, high: 49.97, low: 49.925, close: 49.94, volume: 54748, }, { date: 1462372800000, open: 49.94, high: 49.98, low: 49.9, close: 49.95, volume: 70242, }, { date: 1462372860000, open: 49.955, high: 49.9645, low: 49.905, close: 49.92, volume: 38445, }, { date: 1462372920000, open: 49.92, high: 49.92, low: 49.875, close: 49.91, volume: 27243, }, { date: 1462372980000, open: 49.91, high: 49.915, low: 49.845, close: 49.85, volume: 38686, }, { date: 1462373040000, open: 49.85, high: 49.865, low: 49.8, close: 49.865, volume: 42012, }, { date: 1462373100000, open: 49.86, high: 49.87, low: 49.83, close: 49.86, volume: 31483, }, { date: 1462373160000, open: 49.86, high: 49.89, low: 49.84, close: 49.89, volume: 21292, }, { date: 1462373220000, open: 49.89, high: 49.9, low: 49.87, close: 49.87, volume: 129812, }, { date: 1462373280000, open: 49.87, high: 49.88, low: 49.835, close: 49.86, volume: 31779, }, { date: 1462373340000, open: 49.87, high: 49.89, low: 49.85, close: 49.89, volume: 70127, }, { date: 1462373400000, open: 49.89, high: 49.94, low: 49.88, close: 49.895, volume: 84321, }, { date: 1462373460000, open: 49.89, high: 49.9099, low: 49.875, close: 49.875, volume: 47809, }, { date: 1462373520000, open: 49.875, high: 49.875, low: 49.835, close: 49.845, volume: 93063, }, { date: 1462373580000, open: 49.845, high: 49.85, low: 49.84, close: 49.84, volume: 28747, }, { date: 1462373640000, open: 49.8499, high: 49.89, low: 49.84, close: 49.89, volume: 54699, }, { date: 1462373700000, open: 49.88, high: 49.88, low: 49.83, close: 49.865, volume: 34268, }, { date: 1462373760000, open: 49.86, high: 49.865, low: 49.83, close: 49.84, volume: 27501, }, { date: 1462373820000, open: 49.845, high: 49.86, low: 49.825, close: 49.8301, volume: 31820, }, { date: 1462373880000, open: 49.84, high: 49.9, low: 49.83, close: 49.86, volume: 38207, }, { date: 1462373940000, open: 49.86, high: 49.866, low: 49.83, close: 49.835, volume: 39658, }, { date: 1462374000000, open: 49.83, high: 49.855, low: 49.82, close: 49.835, volume: 38659, }, { date: 1462374060000, open: 49.835, high: 49.85, low: 49.825, close: 49.83, volume: 21314, }, { date: 1462374120000, open: 49.84, high: 49.8561, low: 49.825, close: 49.825, volume: 25128, }, { date: 1462374180000, open: 49.82, high: 49.9, low: 49.8, close: 49.89, volume: 39225, }, { date: 1462374240000, open: 49.8801, high: 49.89, low: 49.86, close: 49.875, volume: 37939, }, { date: 1462374300000, open: 49.87, high: 49.8799, low: 49.835, close: 49.845, volume: 30671, }, { date: 1462374360000, open: 49.86, high: 49.86, low: 49.82, close: 49.845, volume: 30946, }, { date: 1462374420000, open: 49.84, high: 49.865, low: 49.84, close: 49.85, volume: 31907, }, { date: 1462374480000, open: 49.8436, high: 49.85, low: 49.82, close: 49.845, volume: 10859, }, { date: 1462374540000, open: 49.84, high: 49.845, low: 49.81, close: 49.82, volume: 19324, }, { date: 1462374600000, open: 49.83, high: 49.88, low: 49.81, close: 49.822, volume: 72652, }, { date: 1462374660000, open: 49.83, high: 49.83, low: 49.805, close: 49.82, volume: 16901, }, { date: 1462374720000, open: 49.82, high: 49.83, low: 49.78, close: 49.78, volume: 45559, }, { date: 1462374780000, open: 49.78, high: 49.839, low: 49.78, close: 49.8199, volume: 32087, }, { date: 1462374840000, open: 49.82, high: 49.84, low: 49.8001, close: 49.835, volume: 27221, }, { date: 1462374900000, open: 49.8361, high: 49.85, low: 49.8, close: 49.805, volume: 51577, }, { date: 1462374960000, open: 49.8, high: 49.8199, low: 49.76, close: 49.76, volume: 47474, }, { date: 1462375020000, open: 49.76, high: 49.77, low: 49.695, close: 49.7401, volume: 79491, }, { date: 1462375080000, open: 49.74, high: 49.77, low: 49.73, close: 49.7545, volume: 43073, }, { date: 1462375140000, open: 49.755, high: 49.77, low: 49.7501, close: 49.7601, volume: 16634, }, { date: 1462375200000, open: 49.77, high: 49.77, low: 49.7501, close: 49.77, volume: 27851, }, { date: 1462375260000, open: 49.76, high: 49.77, low: 49.725, close: 49.73, volume: 31918, }, { date: 1462375320000, open: 49.727, high: 49.73, low: 49.7, close: 49.7025, volume: 23044, }, { date: 1462375380000, open: 49.7, high: 49.77, low: 49.69, close: 49.76, volume: 48682, }, { date: 1462375440000, open: 49.77, high: 49.7701, low: 49.73, close: 49.73, volume: 31070, }, { date: 1462375500000, open: 49.73, high: 49.7699, low: 49.73, close: 49.7564, volume: 33970, }, { date: 1462375560000, open: 49.75, high: 49.82, low: 49.75, close: 49.82, volume: 63097, }, { date: 1462375620000, open: 49.825, high: 49.825, low: 49.77, close: 49.78, volume: 54789, }, { date: 1462375680000, open: 49.775, high: 49.78, low: 49.75, close: 49.77, volume: 25031, }, { date: 1462375740000, open: 49.77, high: 49.78, low: 49.74, close: 49.74, volume: 35625, }, { date: 1462375800000, open: 49.74, high: 49.8, low: 49.7301, close: 49.76, volume: 41253, }, { date: 1462375860000, open: 49.76, high: 49.78, low: 49.74, close: 49.74, volume: 30147, }, { date: 1462375920000, open: 49.74, high: 49.79, low: 49.71, close: 49.783, volume: 38031, }, { date: 1462375980000, open: 49.7899, high: 49.795, low: 49.77, close: 49.78, volume: 29453, }, { date: 1462376040000, open: 49.785, high: 49.799, low: 49.76, close: 49.78, volume: 33248, }, { date: 1462376100000, open: 49.79, high: 49.8399, low: 49.79, close: 49.819, volume: 75790, }, { date: 1462376160000, open: 49.82, high: 49.845, low: 49.815, close: 49.82, volume: 35400, }, { date: 1462376220000, open: 49.82, high: 49.83, low: 49.7824, close: 49.8099, volume: 40852, }, { date: 1462376280000, open: 49.8, high: 49.81, low: 49.7646, close: 49.8, volume: 39267, }, { date: 1462376340000, open: 49.795, high: 49.845, low: 49.785, close: 49.845, volume: 39126, }, { date: 1462376400000, open: 49.84, high: 49.855, low: 49.8101, close: 49.8299, volume: 59492, }, { date: 1462376460000, open: 49.82, high: 49.8365, low: 49.795, close: 49.8, volume: 41740, }, { date: 1462376520000, open: 49.81, high: 49.83, low: 49.805, close: 49.82, volume: 21155, }, { date: 1462376580000, open: 49.82, high: 49.82, low: 49.8, close: 49.82, volume: 12496, }, { date: 1462376640000, open: 49.815, high: 49.83, low: 49.795, close: 49.8058, volume: 40383, }, { date: 1462376700000, open: 49.8, high: 49.85, low: 49.8, close: 49.84, volume: 34815, }, { date: 1462376760000, open: 49.845, high: 49.87, low: 49.84, close: 49.86, volume: 59813, }, { date: 1462376820000, open: 49.86, high: 49.887, low: 49.86, close: 49.88, volume: 50625, }, { date: 1462376880000, open: 49.8799, high: 49.89, low: 49.86, close: 49.89, volume: 20854, }, { date: 1462376940000, open: 49.9, high: 49.92, low: 49.89, close: 49.905, volume: 74487, }, { date: 1462377000000, open: 49.89, high: 49.94, low: 49.89, close: 49.94, volume: 31908, }, { date: 1462377060000, open: 49.94, high: 49.95, low: 49.91, close: 49.91, volume: 52406, }, { date: 1462377120000, open: 49.9058, high: 49.9058, low: 49.86, close: 49.88, volume: 38290, }, { date: 1462377180000, open: 49.8799, high: 49.89, low: 49.86, close: 49.89, volume: 29373, }, { date: 1462377240000, open: 49.885, high: 49.925, low: 49.874, close: 49.91, volume: 50218, }, { date: 1462377300000, open: 49.91, high: 49.91, low: 49.88, close: 49.885, volume: 15336, }, { date: 1462377360000, open: 49.885, high: 49.885, low: 49.85, close: 49.875, volume: 26858, }, { date: 1462377420000, open: 49.8799, high: 49.905, low: 49.86, close: 49.9, volume: 52981, }, { date: 1462377480000, open: 49.9, high: 49.905, low: 49.86, close: 49.885, volume: 36003, }, { date: 1462377540000, open: 49.885, high: 49.94, low: 49.88, close: 49.93, volume: 68117, }, { date: 1462377600000, open: 49.93, high: 49.96, low: 49.92, close: 49.94, volume: 72396, }, { date: 1462377660000, open: 49.9569, high: 49.96, low: 49.88, close: 49.885, volume: 45837, }, { date: 1462377720000, open: 49.88, high: 49.91, low: 49.88, close: 49.901, volume: 20967, }, { date: 1462377780000, open: 49.9, high: 49.91, low: 49.9, close: 49.905, volume: 30434, }, { date: 1462377840000, open: 49.905, high: 49.91, low: 49.9, close: 49.91, volume: 22035, }, { date: 1462377900000, open: 49.91, high: 49.93, low: 49.9, close: 49.915, volume: 37135, }, { date: 1462377960000, open: 49.92, high: 49.97, low: 49.91, close: 49.96, volume: 44316, }, { date: 1462378020000, open: 49.96, high: 49.99, low: 49.95, close: 49.9899, volume: 56348, }, { date: 1462378080000, open: 49.98, high: 50, low: 49.975, close: 49.98, volume: 41301, }, { date: 1462378140000, open: 49.985, high: 49.985, low: 49.96, close: 49.9765, volume: 30198, }, { date: 1462378200000, open: 49.97, high: 49.9799, low: 49.96, close: 49.97, volume: 45795, }, { date: 1462378260000, open: 49.97, high: 49.99, low: 49.965, close: 49.99, volume: 13995, }, { date: 1462378320000, open: 49.9864, high: 50.01, low: 49.985, close: 49.991, volume: 116544, }, { date: 1462378380000, open: 49.9994, high: 50.02, low: 49.98, close: 50.02, volume: 133498, }, { date: 1462378440000, open: 50.02, high: 50.03, low: 50, close: 50.0064, volume: 50699, }, { date: 1462378500000, open: 50.01, high: 50.025, low: 50, close: 50, volume: 51251, }, { date: 1462378560000, open: 50, high: 50.005, low: 49.99, close: 49.995, volume: 34497, }, { date: 1462378620000, open: 50, high: 50.01, low: 49.96, close: 50.005, volume: 65003, }, { date: 1462378680000, open: 50.0099, high: 50.01, low: 49.985, close: 49.985, volume: 28114, }, { date: 1462378740000, open: 49.99, high: 50, low: 49.981, close: 49.99, volume: 14877, }, { date: 1462378800000, open: 49.9999, high: 49.9999, low: 49.98, close: 49.985, volume: 17662, }, { date: 1462378860000, open: 49.98, high: 50.01, low: 49.98, close: 50.01, volume: 38861, }, { date: 1462378920000, open: 50, high: 50.005, low: 49.99, close: 49.99, volume: 23654, }, { date: 1462378980000, open: 49.987, high: 50.03, low: 49.97, close: 50.01, volume: 46807, }, { date: 1462379040000, open: 50.015, high: 50.015, low: 49.985, close: 50, volume: 47784, }, { date: 1462379100000, open: 49.9931, high: 50.0299, low: 49.99, close: 50.025, volume: 25066, }, { date: 1462379160000, open: 50.02, high: 50.06, low: 50.0101, close: 50.055, volume: 78854, }, { date: 1462379220000, open: 50.05, high: 50.055, low: 50.03, close: 50.0499, volume: 38477, }, { date: 1462379280000, open: 50.049, high: 50.06, low: 50.0198, close: 50.0198, volume: 44342, }, { date: 1462379340000, open: 50.02, high: 50.02, low: 50, close: 50.0003, volume: 41047, }, { date: 1462379400000, open: 50.005, high: 50.015, low: 50, close: 50.0063, volume: 44752, }, { date: 1462379460000, open: 50, high: 50.01, low: 49.98, close: 49.99, volume: 41014, }, { date: 1462379520000, open: 49.995, high: 50.01, low: 49.99, close: 50.01, volume: 15808, }, { date: 1462379580000, open: 50, high: 50.0095, low: 49.99, close: 49.995, volume: 44160, }, { date: 1462379640000, open: 49.995, high: 50.01, low: 49.99, close: 50.01, volume: 18292, }, { date: 1462379700000, open: 50, high: 50.005, low: 49.98, close: 49.98, volume: 45885, }, { date: 1462379760000, open: 49.99, high: 49.9986, low: 49.97, close: 49.9764, volume: 24490, }, { date: 1462379820000, open: 49.97, high: 49.98, low: 49.95, close: 49.98, volume: 31198, }, { date: 1462379880000, open: 49.98, high: 50, low: 49.965, close: 50, volume: 26231, }, { date: 1462379940000, open: 49.99, high: 50, low: 49.9701, close: 49.995, volume: 49426, }, { date: 1462380000000, open: 49.99, high: 50.02, low: 49.98, close: 49.99, volume: 97539, }, { date: 1462380060000, open: 49.995, high: 50, low: 49.975, close: 49.987, volume: 98725, }, { date: 1462380120000, open: 49.98, high: 50, low: 49.965, close: 49.97, volume: 51750, }, { date: 1462380180000, open: 49.97, high: 49.985, low: 49.97, close: 49.98, volume: 11485, }, { date: 1462380240000, open: 49.9864, high: 50, low: 49.98, close: 49.9911, volume: 17100, }, { date: 1462380300000, open: 49.9937, high: 49.9999, low: 49.96, close: 49.975, volume: 51474, }, { date: 1462380360000, open: 49.975, high: 49.99, low: 49.97, close: 49.97, volume: 25962, }, { date: 1462380420000, open: 49.97, high: 50, low: 49.97, close: 49.99, volume: 34279, }, { date: 1462380480000, open: 49.98, high: 49.989, low: 49.96, close: 49.96, volume: 19567, }, { date: 1462380540000, open: 49.97, high: 49.98, low: 49.96, close: 49.975, volume: 17001, }, { date: 1462380600000, open: 49.98, high: 49.98, low: 49.97, close: 49.97, volume: 10616, }, { date: 1462380660000, open: 49.975, high: 49.98, low: 49.952, close: 49.9615, volume: 52613, }, { date: 1462380720000, open: 49.9664, high: 49.99, low: 49.95, close: 49.98, volume: 45711, }, { date: 1462380780000, open: 49.9718, high: 49.99, low: 49.97, close: 49.99, volume: 20599, }, { date: 1462380840000, open: 49.99, high: 49.99, low: 49.97, close: 49.98, volume: 7565, }, { date: 1462380900000, open: 49.98, high: 50, low: 49.975, close: 49.98, volume: 30909, }, { date: 1462380960000, open: 49.98, high: 49.99, low: 49.96, close: 49.975, volume: 57671, }, { date: 1462381020000, open: 49.975, high: 49.98, low: 49.97, close: 49.9799, volume: 4428, }, { date: 1462381080000, open: 49.975, high: 49.98, low: 49.97, close: 49.9755, volume: 7457, }, { date: 1462381140000, open: 49.975, high: 49.985, low: 49.96, close: 49.96, volume: 72037, }, { date: 1462381200000, open: 49.96, high: 49.98, low: 49.95, close: 49.9725, volume: 68698, }, { date: 1462381260000, open: 49.975, high: 49.98, low: 49.96, close: 49.965, volume: 169344, }, { date: 1462381320000, open: 49.96, high: 49.99, low: 49.96, close: 49.985, volume: 32674, }, { date: 1462381380000, open: 49.985, high: 50, low: 49.98, close: 49.99, volume: 59421, }, { date: 1462381440000, open: 49.995, high: 50.05, low: 49.99, close: 50.04, volume: 84280, }, { date: 1462381500000, open: 50.04, high: 50.04, low: 49.99, close: 50.01, volume: 45781, }, { date: 1462381560000, open: 50.0159, high: 50.02, low: 49.99, close: 50, volume: 24711, }, { date: 1462381620000, open: 49.99, high: 50.03, low: 49.99, close: 50.01, volume: 35855, }, { date: 1462381680000, open: 50.005, high: 50.02, low: 50, close: 50.02, volume: 13786, }, { date: 1462381740000, open: 50.02, high: 50.03, low: 49.99, close: 49.99, volume: 18934, }, { date: 1462381800000, open: 49.99, high: 50.01, low: 49.99, close: 49.995, volume: 65585, }, { date: 1462381860000, open: 49.9999, high: 49.9999, low: 49.95, close: 49.965, volume: 99092, }, { date: 1462381920000, open: 49.961, high: 49.99, low: 49.96, close: 49.965, volume: 53534, }, { date: 1462381980000, open: 49.965, high: 49.97, low: 49.95, close: 49.965, volume: 27583, }, { date: 1462382040000, open: 49.97, high: 49.98, low: 49.94, close: 49.95, volume: 42270, }, { date: 1462382100000, open: 49.95, high: 49.95, low: 49.94, close: 49.944, volume: 16037, }, { date: 1462382160000, open: 49.95, high: 50, low: 49.945, close: 49.9999, volume: 68723, }, { date: 1462382220000, open: 49.995, high: 50, low: 49.99, close: 49.995, volume: 83073, }, { date: 1462382280000, open: 50, high: 50, low: 49.97, close: 49.975, volume: 33768, }, { date: 1462382340000, open: 49.975, high: 49.98, low: 49.96, close: 49.965, volume: 24332, }, { date: 1462382400000, open: 49.965, high: 49.98, low: 49.95, close: 49.975, volume: 63435, }, { date: 1462382460000, open: 49.975, high: 49.98, low: 49.97, close: 49.975, volume: 13318, }, { date: 1462382520000, open: 49.97, high: 49.98, low: 49.955, close: 49.955, volume: 32390, }, { date: 1462382580000, open: 49.955, high: 49.9599, low: 49.94, close: 49.95, volume: 27186, }, { date: 1462382640000, open: 49.945, high: 49.945, low: 49.93, close: 49.938, volume: 50974, }, { date: 1462382700000, open: 49.94, high: 49.95, low: 49.93, close: 49.93, volume: 23650, }, { date: 1462382760000, open: 49.93, high: 49.93, low: 49.89, close: 49.9, volume: 57353, }, { date: 1462382820000, open: 49.8992, high: 49.92, low: 49.89, close: 49.92, volume: 24947, }, { date: 1462382880000, open: 49.91, high: 49.92, low: 49.905, close: 49.9133, volume: 14895, }, { date: 1462382940000, open: 49.9127, high: 49.95, low: 49.91, close: 49.93, volume: 46834, }, { date: 1462383000000, open: 49.93, high: 49.97, low: 49.93, close: 49.97, volume: 13175, }, { date: 1462383060000, open: 49.975, high: 49.98, low: 49.94, close: 49.96, volume: 37778, }, { date: 1462383120000, open: 49.96, high: 49.9899, low: 49.95, close: 49.96, volume: 51112, }, { date: 1462383180000, open: 49.955, high: 49.9699, low: 49.935, close: 49.96, volume: 26313, }, { date: 1462383240000, open: 49.965, high: 49.9799, low: 49.94, close: 49.94, volume: 46354, }, { date: 1462383300000, open: 49.94, high: 49.955, low: 49.94, close: 49.955, volume: 19166, }, { date: 1462383360000, open: 49.955, high: 49.966, low: 49.92, close: 49.92, volume: 20517, }, { date: 1462383420000, open: 49.93, high: 49.97, low: 49.93, close: 49.97, volume: 26111, }, { date: 1462383480000, open: 49.9636, high: 49.9799, low: 49.94, close: 49.94, volume: 39627, }, { date: 1462383540000, open: 49.94, high: 49.95, low: 49.89, close: 49.89, volume: 49813, }, { date: 1462383600000, open: 49.895, high: 49.93, low: 49.89, close: 49.926, volume: 29424, }, { date: 1462383660000, open: 49.92, high: 49.94, low: 49.91, close: 49.93, volume: 29865, }, { date: 1462383720000, open: 49.9301, high: 49.99, low: 49.9301, close: 49.99, volume: 90396, }, { date: 1462383780000, open: 49.9871, high: 49.99, low: 49.96, close: 49.966, volume: 65141, }, { date: 1462383840000, open: 49.97, high: 49.98, low: 49.95, close: 49.95, volume: 30315, }, { date: 1462383900000, open: 49.95, high: 49.96, low: 49.925, close: 49.94, volume: 25304, }, { date: 1462383960000, open: 49.9365, high: 49.9365, low: 49.91, close: 49.91, volume: 41739, }, { date: 1462384020000, open: 49.91, high: 49.92, low: 49.885, close: 49.92, volume: 52725, }, { date: 1462384080000, open: 49.91, high: 49.93, low: 49.91, close: 49.921, volume: 25272, }, { date: 1462384140000, open: 49.92, high: 49.96, low: 49.915, close: 49.94, volume: 41575, }, { date: 1462384200000, open: 49.935, high: 49.96, low: 49.9338, close: 49.955, volume: 17650, }, { date: 1462384260000, open: 49.96, high: 49.99, low: 49.955, close: 49.97, volume: 75321, }, { date: 1462384320000, open: 49.97, high: 49.98, low: 49.95, close: 49.95, volume: 24126, }, { date: 1462384380000, open: 49.95, high: 49.96, low: 49.93, close: 49.95, volume: 26863, }, { date: 1462384440000, open: 49.95, high: 49.96, low: 49.9239, close: 49.94, volume: 19566, }, { date: 1462384500000, open: 49.94, high: 49.95, low: 49.92, close: 49.94, volume: 32586, }, { date: 1462384560000, open: 49.94, high: 49.97, low: 49.94, close: 49.94, volume: 79768, }, { date: 1462384620000, open: 49.9371, high: 49.94, low: 49.89, close: 49.9029, volume: 42153, }, { date: 1462384680000, open: 49.9, high: 49.91, low: 49.9, close: 49.9, volume: 24748, }, { date: 1462384740000, open: 49.9, high: 49.91, low: 49.89, close: 49.905, volume: 27604, }, { date: 1462384800000, open: 49.8971, high: 49.8971, low: 49.87, close: 49.875, volume: 42664, }, { date: 1462384860000, open: 49.88, high: 49.88, low: 49.8301, close: 49.8468, volume: 75523, }, { date: 1462384920000, open: 49.8465, high: 49.86, low: 49.815, close: 49.859, volume: 57517, }, { date: 1462384980000, open: 49.85, high: 49.85, low: 49.82, close: 49.82, volume: 30051, }, { date: 1462385040000, open: 49.82, high: 49.86, low: 49.81, close: 49.855, volume: 35130, }, { date: 1462385100000, open: 49.86, high: 49.86, low: 49.84, close: 49.8401, volume: 22465, }, { date: 1462385160000, open: 49.84, high: 49.85, low: 49.81, close: 49.82, volume: 43594, }, { date: 1462385220000, open: 49.81, high: 49.83, low: 49.8, close: 49.815, volume: 64133, }, { date: 1462385280000, open: 49.8101, high: 49.84, low: 49.8101, close: 49.825, volume: 33226, }, { date: 1462385340000, open: 49.825, high: 49.854, low: 49.82, close: 49.82, volume: 26359, }, { date: 1462385400000, open: 49.82, high: 49.865, low: 49.82, close: 49.86, volume: 42682, }, { date: 1462385460000, open: 49.8599, high: 49.87, low: 49.84, close: 49.856, volume: 30313, }, { date: 1462385520000, open: 49.85, high: 49.85, low: 49.81, close: 49.81, volume: 21145, }, { date: 1462385580000, open: 49.81, high: 49.81, low: 49.8, close: 49.8, volume: 18580, }, { date: 1462385640000, open: 49.8099, high: 49.83, low: 49.805, close: 49.83, volume: 18883, }, { date: 1462385700000, open: 49.825, high: 49.85, low: 49.82, close: 49.85, volume: 30434, }, { date: 1462385760000, open: 49.845, high: 49.845, low: 49.82, close: 49.8201, volume: 18424, }, { date: 1462385820000, open: 49.822, high: 49.85, low: 49.82, close: 49.85, volume: 35817, }, { date: 1462385880000, open: 49.8467, high: 49.89, low: 49.8415, close: 49.885, volume: 51948, }, { date: 1462385940000, open: 49.88, high: 49.9, low: 49.86, close: 49.87, volume: 28836, }, { date: 1462386000000, open: 49.87, high: 49.895, low: 49.86, close: 49.89, volume: 27275, }, { date: 1462386060000, open: 49.89, high: 49.9, low: 49.87, close: 49.8701, volume: 47923, }, { date: 1462386120000, open: 49.87, high: 49.87, low: 49.85, close: 49.865, volume: 14163, }, { date: 1462386180000, open: 49.867, high: 49.92, low: 49.86, close: 49.885, volume: 60686, }, { date: 1462386240000, open: 49.885, high: 49.93, low: 49.885, close: 49.915, volume: 31185, }, { date: 1462386300000, open: 49.92, high: 49.92, low: 49.885, close: 49.91, volume: 26943, }, { date: 1462386360000, open: 49.915, high: 49.945, low: 49.9001, close: 49.94, volume: 53295, }, { date: 1462386420000, open: 49.93, high: 49.932, low: 49.885, close: 49.91, volume: 30047, }, { date: 1462386480000, open: 49.91, high: 49.91, low: 49.87, close: 49.8899, volume: 22374, }, { date: 1462386540000, open: 49.885, high: 49.9199, low: 49.88, close: 49.89, volume: 27312, }, { date: 1462386600000, open: 49.8864, high: 49.904, low: 49.88, close: 49.895, volume: 26483, }, { date: 1462386660000, open: 49.89, high: 49.91, low: 49.88, close: 49.9039, volume: 130507, }, { date: 1462386720000, open: 49.9, high: 49.9167, low: 49.895, close: 49.9099, volume: 47245, }, { date: 1462386780000, open: 49.905, high: 49.94, low: 49.9, close: 49.935, volume: 39956, }, { date: 1462386840000, open: 49.935, high: 49.96, low: 49.93, close: 49.955, volume: 80801, }, { date: 1462386900000, open: 49.955, high: 49.96, low: 49.94, close: 49.9564, volume: 75807, }, { date: 1462386960000, open: 49.95, high: 49.985, low: 49.95, close: 49.97, volume: 111128, }, { date: 1462387020000, open: 49.965, high: 49.97, low: 49.95, close: 49.955, volume: 28322, }, { date: 1462387080000, open: 49.95, high: 49.965, low: 49.915, close: 49.945, volume: 35045, }, { date: 1462387140000, open: 49.9499, high: 49.9664, low: 49.9, close: 49.91, volume: 54581, }, { date: 1462387200000, open: 49.91, high: 49.92, low: 49.91, close: 49.92, volume: 12752, }, { date: 1462387260000, open: 49.93, high: 49.945, low: 49.88, close: 49.8899, volume: 28527, }, { date: 1462387320000, open: 49.885, high: 49.8956, low: 49.84, close: 49.84, volume: 49331, }, { date: 1462387380000, open: 49.845, high: 49.865, low: 49.84, close: 49.865, volume: 19315, }, { date: 1462387440000, open: 49.8699, high: 49.8699, low: 49.8236, close: 49.825, volume: 24320, }, { date: 1462387500000, open: 49.825, high: 49.84, low: 49.82, close: 49.835, volume: 18457, }, { date: 1462387560000, open: 49.8367, high: 49.85, low: 49.83, close: 49.845, volume: 23988, }, { date: 1462387620000, open: 49.85, high: 49.859, low: 49.815, close: 49.82, volume: 38013, }, { date: 1462387680000, open: 49.83, high: 49.85, low: 49.8264, close: 49.835, volume: 11140, }, { date: 1462387740000, open: 49.835, high: 49.87, low: 49.835, close: 49.8633, volume: 54496, }, { date: 1462387800000, open: 49.864, high: 49.88, low: 49.84, close: 49.85, volume: 31259, }, { date: 1462387860000, open: 49.8598, high: 49.8598, low: 49.82, close: 49.847, volume: 29020, }, { date: 1462387920000, open: 49.84, high: 49.85, low: 49.835, close: 49.845, volume: 15076, }, { date: 1462387980000, open: 49.84, high: 49.87, low: 49.84, close: 49.84, volume: 24839, }, { date: 1462388040000, open: 49.8499, high: 49.9025, low: 49.845, close: 49.8999, volume: 29898, }, { date: 1462388100000, open: 49.89, high: 49.895, low: 49.8301, close: 49.84, volume: 41722, }, { date: 1462388160000, open: 49.84, high: 49.86, low: 49.821, close: 49.85, volume: 30696, }, { date: 1462388220000, open: 49.85, high: 49.8599, low: 49.85, close: 49.85, volume: 10019, }, { date: 1462388280000, open: 49.85, high: 49.853, low: 49.8256, close: 49.8299, volume: 30839, }, { date: 1462388340000, open: 49.82, high: 49.8399, low: 49.815, close: 49.8298, volume: 50212, }, { date: 1462388400000, open: 49.825, high: 49.85, low: 49.825, close: 49.849, volume: 8461, }, { date: 1462388460000, open: 49.84, high: 49.86, low: 49.83, close: 49.8556, volume: 35788, }, { date: 1462388520000, open: 49.85, high: 49.88, low: 49.85, close: 49.87, volume: 41657, }, { date: 1462388580000, open: 49.87, high: 49.8799, low: 49.85, close: 49.86, volume: 10171, }, { date: 1462388640000, open: 49.855, high: 49.855, low: 49.79, close: 49.8, volume: 148352, }, { date: 1462388700000, open: 49.81, high: 49.85, low: 49.8, close: 49.84, volume: 26188, }, { date: 1462388760000, open: 49.845, high: 49.845, low: 49.82, close: 49.8236, volume: 20620, }, { date: 1462388820000, open: 49.825, high: 49.83, low: 49.8, close: 49.82, volume: 19694, }, { date: 1462388880000, open: 49.82, high: 49.83, low: 49.805, close: 49.81, volume: 21364, }, { date: 1462388940000, open: 49.8001, high: 49.8573, low: 49.8001, close: 49.857, volume: 24739, }, { date: 1462389000000, open: 49.855, high: 49.856, low: 49.84, close: 49.85, volume: 22572, }, { date: 1462389060000, open: 49.86, high: 49.86, low: 49.78, close: 49.8201, volume: 100580, }, { date: 1462389120000, open: 49.83, high: 49.85, low: 49.83, close: 49.83, volume: 17283, }, { date: 1462389180000, open: 49.831, high: 49.86, low: 49.83, close: 49.86, volume: 43611, }, { date: 1462389240000, open: 49.855, high: 49.87, low: 49.85, close: 49.861, volume: 15054, }, { date: 1462389300000, open: 49.86, high: 49.9161, low: 49.86, close: 49.91, volume: 55046, }, { date: 1462389360000, open: 49.9199, high: 49.92, low: 49.88, close: 49.885, volume: 32450, }, { date: 1462389420000, open: 49.8833, high: 49.8999, low: 49.87, close: 49.87, volume: 33752, }, { date: 1462389480000, open: 49.88, high: 49.8999, low: 49.88, close: 49.8801, volume: 27573, }, { date: 1462389540000, open: 49.887, high: 49.9, low: 49.88, close: 49.8901, volume: 32521, }, { date: 1462389600000, open: 49.89, high: 49.895, low: 49.855, close: 49.855, volume: 56401, }, { date: 1462389660000, open: 49.85, high: 49.86, low: 49.84, close: 49.8599, volume: 35084, }, { date: 1462389720000, open: 49.86, high: 49.9168, low: 49.86, close: 49.9168, volume: 46118, }, { date: 1462389780000, open: 49.92, high: 49.92, low: 49.89, close: 49.91, volume: 38173, }, { date: 1462389840000, open: 49.9067, high: 49.93, low: 49.9, close: 49.93, volume: 21910, }, { date: 1462389900000, open: 49.9272, high: 49.9399, low: 49.9, close: 49.91, volume: 65695, }, { date: 1462389960000, open: 49.91, high: 49.9199, low: 49.89, close: 49.9064, volume: 33733, }, { date: 1462390020000, open: 49.9075, high: 49.92, low: 49.906, close: 49.915, volume: 29228, }, { date: 1462390080000, open: 49.9185, high: 49.9185, low: 49.88, close: 49.885, volume: 32653, }, { date: 1462390140000, open: 49.8899, high: 49.8899, low: 49.86, close: 49.86, volume: 42848, }, { date: 1462390200000, open: 49.86, high: 49.88, low: 49.86, close: 49.87, volume: 40494, }, { date: 1462390260000, open: 49.87, high: 49.895, low: 49.865, close: 49.865, volume: 57012, }, { date: 1462390320000, open: 49.8699, high: 49.8799, low: 49.86, close: 49.86, volume: 39338, }, { date: 1462390380000, open: 49.8664, high: 49.88, low: 49.86, close: 49.88, volume: 52845, }, { date: 1462390440000, open: 49.88, high: 49.89, low: 49.855, close: 49.86, volume: 67746, }, { date: 1462390500000, open: 49.86, high: 49.88, low: 49.86, close: 49.87, volume: 42669, }, { date: 1462390560000, open: 49.865, high: 49.91, low: 49.8601, close: 49.9, volume: 82016, }, { date: 1462390620000, open: 49.89, high: 49.89, low: 49.87, close: 49.87, volume: 41939, }, { date: 1462390680000, open: 49.87, high: 49.88, low: 49.85, close: 49.86, volume: 75763, }, { date: 1462390740000, open: 49.86, high: 49.875, low: 49.85, close: 49.87, volume: 29703, }, { date: 1462390800000, open: 49.87, high: 49.8899, low: 49.861, close: 49.874, volume: 62583, }, { date: 1462390860000, open: 49.87, high: 49.89, low: 49.87, close: 49.89, volume: 37206, }, { date: 1462390920000, open: 49.895, high: 49.957, low: 49.891, close: 49.94, volume: 162093, }, { date: 1462390980000, open: 49.94, high: 49.9499, low: 49.9, close: 49.92, volume: 75116, }, { date: 1462391040000, open: 49.925, high: 49.93, low: 49.89, close: 49.895, volume: 76899, }, { date: 1462391100000, open: 49.89, high: 49.91, low: 49.89, close: 49.905, volume: 46129, }, { date: 1462391160000, open: 49.91, high: 49.94, low: 49.9, close: 49.9165, volume: 107999, }, { date: 1462391220000, open: 49.92, high: 49.975, low: 49.92, close: 49.95, volume: 114380, }, { date: 1462391280000, open: 49.955, high: 49.98, low: 49.95, close: 49.97, volume: 73290, }, { date: 1462391340000, open: 49.97, high: 50.01, low: 49.97, close: 50.005, volume: 86611, }, { date: 1462391400000, open: 50.01, high: 50.01, low: 49.97, close: 49.97, volume: 57579, }, { date: 1462391460000, open: 49.965, high: 49.97, low: 49.94, close: 49.95, volume: 74638, }, { date: 1462391520000, open: 49.95, high: 49.97, low: 49.93, close: 49.935, volume: 91139, }, { date: 1462391580000, open: 49.93, high: 49.95, low: 49.93, close: 49.94, volume: 51523, }, { date: 1462391640000, open: 49.95, high: 49.98, low: 49.945, close: 49.955, volume: 88233, }, { date: 1462391700000, open: 49.955, high: 49.96, low: 49.94, close: 49.9401, volume: 55301, }, { date: 1462391760000, open: 49.94, high: 49.94, low: 49.9, close: 49.935, volume: 162451, }, { date: 1462391820000, open: 49.935, high: 49.94, low: 49.92, close: 49.94, volume: 122575, }, { date: 1462391880000, open: 49.9325, high: 49.96, low: 49.915, close: 49.915, volume: 169135, }, { date: 1462391940000, open: 49.91, high: 49.915, low: 49.88, close: 49.9, volume: 166066, }, { date: 1462392000000, open: 49.905, high: 49.9068, low: 49.86, close: 49.87, volume: 3168745, }, { date: 1462455000000, open: 49.87, high: 49.89, low: 49.87, close: 49.88, volume: 393136, }, { date: 1462455060000, open: 49.89, high: 49.9585, low: 49.74, close: 49.75, volume: 157040, }, { date: 1462455120000, open: 49.76, high: 49.875, low: 49.76, close: 49.85, volume: 131622, }, { date: 1462455180000, open: 49.85, high: 49.875, low: 49.79, close: 49.82, volume: 113400, }, { date: 1462455240000, open: 49.825, high: 49.92, low: 49.82, close: 49.85, volume: 69014, }, { date: 1462455300000, open: 49.8599, high: 49.91, low: 49.83, close: 49.9075, volume: 121545, }, { date: 1462455360000, open: 49.9, high: 49.915, low: 49.85, close: 49.9, volume: 82198, }, { date: 1462455420000, open: 49.9, high: 49.9, low: 49.85, close: 49.89, volume: 148275, }, { date: 1462455480000, open: 49.89, high: 49.95, low: 49.89, close: 49.94, volume: 120703, }, { date: 1462455540000, open: 49.94, high: 49.965, low: 49.93, close: 49.94, volume: 55292, }, { date: 1462455600000, open: 49.94, high: 49.96, low: 49.9301, close: 49.955, volume: 73422, }, { date: 1462455660000, open: 49.95, high: 49.97, low: 49.91, close: 49.9599, volume: 83169, }, { date: 1462455720000, open: 49.96, high: 49.97, low: 49.89, close: 49.92, volume: 64027, }, { date: 1462455780000, open: 49.92, high: 50, low: 49.92, close: 49.9875, volume: 95210, }, { date: 1462455840000, open: 49.99, high: 50.05, low: 49.95, close: 50.01, volume: 235434, }, { date: 1462455900000, open: 50.01, high: 50.0201, low: 49.985, close: 50.01, volume: 35881, }, { date: 1462455960000, open: 50.015, high: 50.02, low: 49.95, close: 49.96, volume: 125305, }, { date: 1462456020000, open: 49.96, high: 49.9799, low: 49.92, close: 49.9301, volume: 45219, }, { date: 1462456080000, open: 49.93, high: 49.95, low: 49.91, close: 49.94, volume: 80793, }, { date: 1462456140000, open: 49.94, high: 49.99, low: 49.94, close: 49.955, volume: 72081, }, { date: 1462456200000, open: 49.96, high: 49.99, low: 49.94, close: 49.985, volume: 27961, }, { date: 1462456260000, open: 49.9834, high: 49.9834, low: 49.94, close: 49.96, volume: 49732, }, { date: 1462456320000, open: 49.96, high: 49.96, low: 49.89, close: 49.92, volume: 48716, }, { date: 1462456380000, open: 49.92, high: 49.95, low: 49.905, close: 49.93, volume: 33774, }, { date: 1462456440000, open: 49.92, high: 49.945, low: 49.91, close: 49.93, volume: 31017, }, { date: 1462456500000, open: 49.93, high: 49.98, low: 49.93, close: 49.955, volume: 36861, }, { date: 1462456560000, open: 49.95, high: 49.96, low: 49.89, close: 49.89, volume: 53847, }, { date: 1462456620000, open: 49.88, high: 49.8858, low: 49.85, close: 49.86, volume: 58825, }, { date: 1462456680000, open: 49.87, high: 49.87, low: 49.76, close: 49.76, volume: 68164, }, { date: 1462456740000, open: 49.76, high: 49.79, low: 49.73, close: 49.77, volume: 61066, }, { date: 1462456800000, open: 49.77, high: 49.79, low: 49.75, close: 49.75, volume: 68015, }, { date: 1462456860000, open: 49.748, high: 49.78, low: 49.748, close: 49.78, volume: 39768, }, { date: 1462456920000, open: 49.7745, high: 49.79, low: 49.765, close: 49.77, volume: 36114, }, { date: 1462456980000, open: 49.77, high: 49.84, low: 49.77, close: 49.82, volume: 81943, }, { date: 1462457040000, open: 49.83, high: 49.8603, low: 49.81, close: 49.845, volume: 36359, }, { date: 1462457100000, open: 49.84, high: 49.9, low: 49.82, close: 49.89, volume: 32860, }, { date: 1462457160000, open: 49.89, high: 49.93, low: 49.88, close: 49.91, volume: 37875, }, { date: 1462457220000, open: 49.919, high: 49.93, low: 49.91, close: 49.92, volume: 32386, }, { date: 1462457280000, open: 49.92, high: 49.92, low: 49.87, close: 49.88, volume: 36847, }, { date: 1462457340000, open: 49.889, high: 49.9, low: 49.881, close: 49.9, volume: 28542, }, { date: 1462457400000, open: 49.895, high: 49.93, low: 49.89, close: 49.9099, volume: 43903, }, { date: 1462457460000, open: 49.91, high: 49.9355, low: 49.909, close: 49.935, volume: 29826, }, { date: 1462457520000, open: 49.93, high: 49.9599, low: 49.92, close: 49.925, volume: 152601, }, { date: 1462457580000, open: 49.925, high: 50.0101, low: 49.92, close: 50.0101, volume: 94363, }, { date: 1462457640000, open: 50.015, high: 50.02, low: 49.96, close: 49.97, volume: 47029, }, { date: 1462457700000, open: 49.97, high: 50, low: 49.96, close: 50, volume: 30149, }, { date: 1462457760000, open: 50, high: 50, low: 49.95, close: 49.97, volume: 59535, }, { date: 1462457820000, open: 49.97, high: 50, low: 49.945, close: 50, volume: 110827, }, { date: 1462457880000, open: 50, high: 50.0276, low: 49.99, close: 50, volume: 52295, }, { date: 1462457940000, open: 49.99, high: 50.02, low: 49.99, close: 50.015, volume: 61867, }, { date: 1462458000000, open: 50.015, high: 50.02, low: 49.99, close: 50, volume: 58383, }, { date: 1462458060000, open: 50.01, high: 50.01, low: 49.96, close: 49.97, volume: 82030, }, { date: 1462458120000, open: 49.965, high: 49.98, low: 49.96, close: 49.9666, volume: 44088, }, { date: 1462458180000, open: 49.96, high: 49.98, low: 49.9167, close: 49.98, volume: 81482, }, { date: 1462458240000, open: 49.98, high: 50, low: 49.97, close: 49.9942, volume: 61199, }, { date: 1462458300000, open: 49.995, high: 50.01, low: 49.99, close: 49.9999, volume: 69572, }, { date: 1462458360000, open: 49.99, high: 50.01, low: 49.98, close: 50, volume: 66375, }, { date: 1462458420000, open: 50, high: 50.02, low: 49.99, close: 50, volume: 89685, }, { date: 1462458480000, open: 50.005, high: 50.005, low: 49.9, close: 49.9, volume: 107838, }, { date: 1462458540000, open: 49.905, high: 49.915, low: 49.87, close: 49.87, volume: 53263, }, { date: 1462458600000, open: 49.87, high: 49.905, low: 49.85, close: 49.9, volume: 95315, }, { date: 1462458660000, open: 49.9, high: 49.94, low: 49.9, close: 49.925, volume: 73766, }, { date: 1462458720000, open: 49.93, high: 49.99, low: 49.93, close: 49.96, volume: 89208, }, { date: 1462458780000, open: 49.96, high: 50, low: 49.95, close: 49.99, volume: 90200, }, { date: 1462458840000, open: 49.99, high: 49.995, low: 49.9599, close: 49.99, volume: 51680, }, { date: 1462458900000, open: 49.98, high: 50.03, low: 49.98, close: 50.03, volume: 159166, }, { date: 1462458960000, open: 50.03, high: 50.03, low: 50.01, close: 50.025, volume: 102093, }, { date: 1462459020000, open: 50.025, high: 50.03, low: 50, close: 50.02, volume: 56415, }, { date: 1462459080000, open: 50.02, high: 50.02, low: 49.955, close: 49.9838, volume: 46333, }, { date: 1462459140000, open: 49.9899, high: 49.9899, low: 49.96, close: 49.965, volume: 16392, }, { date: 1462459200000, open: 49.96, high: 49.9701, low: 49.94, close: 49.955, volume: 50921, }, { date: 1462459260000, open: 49.96, high: 49.9786, low: 49.92, close: 49.925, volume: 84180, }, { date: 1462459320000, open: 49.915, high: 49.945, low: 49.91, close: 49.945, volume: 58112, }, { date: 1462459380000, open: 49.945, high: 49.99, low: 49.945, close: 49.99, volume: 45467, }, { date: 1462459440000, open: 49.9899, high: 49.9899, low: 49.96, close: 49.961, volume: 26997, }, { date: 1462459500000, open: 49.965, high: 50.0262, low: 49.96, close: 50.015, volume: 62847, }, { date: 1462459560000, open: 50.015, high: 50.02, low: 49.995, close: 50.0066, volume: 39367, }, { date: 1462459620000, open: 50.01, high: 50.04, low: 50.01, close: 50.02, volume: 39814, }, { date: 1462459680000, open: 50.0235, high: 50.0399, low: 50.015, close: 50.03, volume: 37410, }, { date: 1462459740000, open: 50.0355, high: 50.08, low: 50.03, close: 50.04, volume: 126551, }, { date: 1462459800000, open: 50.045, high: 50.08, low: 50.04, close: 50.0662, volume: 63011, }, { date: 1462459860000, open: 50.06, high: 50.07, low: 50.06, close: 50.063, volume: 20232, }, { date: 1462459920000, open: 50.06, high: 50.08, low: 50.06, close: 50.07, volume: 55566, }, { date: 1462459980000, open: 50.075, high: 50.0764, low: 50.03, close: 50.0333, volume: 34345, }, { date: 1462460040000, open: 50.03, high: 50.05, low: 50.01, close: 50.045, volume: 35222, }, { date: 1462460100000, open: 50.05, high: 50.06, low: 50.04, close: 50.05, volume: 23765, }, { date: 1462460160000, open: 50.05, high: 50.0555, low: 50.0372, close: 50.0372, volume: 20014, }, { date: 1462460220000, open: 50.0399, high: 50.06, low: 50.02, close: 50.045, volume: 26355, }, { date: 1462460280000, open: 50.045, high: 50.06, low: 50.04, close: 50.045, volume: 22574, }, { date: 1462460340000, open: 50.045, high: 50.09, low: 50.045, close: 50.085, volume: 62563, }, { date: 1462460400000, open: 50.0899, high: 50.09, low: 50.07, close: 50.09, volume: 24788, }, { date: 1462460460000, open: 50.09, high: 50.13, low: 50.08, close: 50.13, volume: 95401, }, { date: 1462460520000, open: 50.125, high: 50.13, low: 50.055, close: 50.095, volume: 60101, }, { date: 1462460580000, open: 50.085, high: 50.14, low: 50.085, close: 50.1, volume: 47501, }, { date: 1462460640000, open: 50.1, high: 50.1, low: 50.08, close: 50.09, volume: 20880, }, { date: 1462460700000, open: 50.085, high: 50.12, low: 50.06, close: 50.1101, volume: 102085, }, { date: 1462460760000, open: 50.11, high: 50.13, low: 50.1, close: 50.125, volume: 32148, }, { date: 1462460820000, open: 50.12, high: 50.13, low: 50.11, close: 50.11, volume: 55801, }, { date: 1462460880000, open: 50.1, high: 50.12, low: 50.09, close: 50.11, volume: 55534, }, { date: 1462460940000, open: 50.11, high: 50.13, low: 50.07, close: 50.07, volume: 69029, }, { date: 1462461000000, open: 50.08, high: 50.115, low: 50.075, close: 50.11, volume: 55827, }, { date: 1462461060000, open: 50.105, high: 50.12, low: 50.1, close: 50.1, volume: 50183, }, { date: 1462461120000, open: 50.095, high: 50.1, low: 50.08, close: 50.1, volume: 33017, }, { date: 1462461180000, open: 50.101, high: 50.13, low: 50.1, close: 50.115, volume: 29770, }, { date: 1462461240000, open: 50.1135, high: 50.13, low: 50.1, close: 50.1, volume: 19684, }, { date: 1462461300000, open: 50.1, high: 50.11, low: 50.09, close: 50.095, volume: 34033, }, { date: 1462461360000, open: 50.095, high: 50.11, low: 50.09, close: 50.105, volume: 38819, }, { date: 1462461420000, open: 50.1068, high: 50.11, low: 50.09, close: 50.1035, volume: 40601, }, { date: 1462461480000, open: 50.1072, high: 50.13, low: 50.1, close: 50.13, volume: 39639, }, { date: 1462461540000, open: 50.12, high: 50.18, low: 50.12, close: 50.165, volume: 74790, }, { date: 1462461600000, open: 50.165, high: 50.175, low: 50.16, close: 50.165, volume: 57345, }, { date: 1462461660000, open: 50.16, high: 50.18, low: 50.15, close: 50.15, volume: 48719, }, { date: 1462461720000, open: 50.1599, high: 50.19, low: 50.15, close: 50.15, volume: 62787, }, { date: 1462461780000, open: 50.14, high: 50.146, low: 50.085, close: 50.106, volume: 108716, }, { date: 1462461840000, open: 50.1, high: 50.11, low: 50.07, close: 50.07, volume: 71202, }, { date: 1462461900000, open: 50.07, high: 50.09, low: 50.06, close: 50.06, volume: 55134, }, { date: 1462461960000, open: 50.06, high: 50.12, low: 50.05, close: 50.115, volume: 43462, }, { date: 1462462020000, open: 50.11, high: 50.115, low: 50.09, close: 50.1, volume: 35749, }, { date: 1462462080000, open: 50.1, high: 50.125, low: 50.095, close: 50.115, volume: 26517, }, { date: 1462462140000, open: 50.115, high: 50.145, low: 50.11, close: 50.12, volume: 39098, }, { date: 1462462200000, open: 50.12, high: 50.125, low: 50.11, close: 50.115, volume: 21771, }, { date: 1462462260000, open: 50.119, high: 50.15, low: 50.11, close: 50.147, volume: 25822, }, { date: 1462462320000, open: 50.1497, high: 50.17, low: 50.14, close: 50.1699, volume: 13973, }, { date: 1462462380000, open: 50.16, high: 50.1658, low: 50.15, close: 50.16, volume: 27106, }, { date: 1462462440000, open: 50.16, high: 50.165, low: 50.15, close: 50.15, volume: 28268, }, { date: 1462462500000, open: 50.155, high: 50.18, low: 50.13, close: 50.18, volume: 31204, }, { date: 1462462560000, open: 50.19, high: 50.24, low: 50.19, close: 50.23, volume: 63069, }, { date: 1462462620000, open: 50.2255, high: 50.25, low: 50.22, close: 50.2301, volume: 22060, }, { date: 1462462680000, open: 50.23, high: 50.24, low: 50.22, close: 50.23, volume: 24735, }, { date: 1462462740000, open: 50.235, high: 50.235, low: 50.195, close: 50.21, volume: 31589, }, { date: 1462462800000, open: 50.2199, high: 50.25, low: 50.2, close: 50.25, volume: 64324, }, { date: 1462462860000, open: 50.25, high: 50.255, low: 50.231, close: 50.231, volume: 23069, }, { date: 1462462920000, open: 50.235, high: 50.25, low: 50.23, close: 50.25, volume: 50825, }, { date: 1462462980000, open: 50.249, high: 50.27, low: 50.225, close: 50.23, volume: 68397, }, { date: 1462463040000, open: 50.23, high: 50.275, low: 50.23, close: 50.25, volume: 53368, }, { date: 1462463100000, open: 50.26, high: 50.285, low: 50.26, close: 50.285, volume: 34036, }, { date: 1462463160000, open: 50.2899, high: 50.29, low: 50.26, close: 50.28, volume: 26734, }, { date: 1462463220000, open: 50.29, high: 50.3, low: 50.265, close: 50.2731, volume: 38338, }, { date: 1462463280000, open: 50.28, high: 50.29, low: 50.26, close: 50.285, volume: 42211, }, { date: 1462463340000, open: 50.28, high: 50.29, low: 50.245, close: 50.2501, volume: 47675, }, { date: 1462463400000, open: 50.25, high: 50.26, low: 50.235, close: 50.255, volume: 39578, }, { date: 1462463460000, open: 50.25, high: 50.26, low: 50.24, close: 50.255, volume: 13347, }, { date: 1462463520000, open: 50.255, high: 50.26, low: 50.24, close: 50.245, volume: 21086, }, { date: 1462463580000, open: 50.24, high: 50.249, low: 50.22, close: 50.23, volume: 33384, }, { date: 1462463640000, open: 50.22, high: 50.2499, low: 50.22, close: 50.2499, volume: 22645, }, { date: 1462463700000, open: 50.25, high: 50.25, low: 50.21, close: 50.215, volume: 39953, }, { date: 1462463760000, open: 50.215, high: 50.22, low: 50.2, close: 50.2, volume: 22979, }, { date: 1462463820000, open: 50.2, high: 50.225, low: 50.2, close: 50.22, volume: 60966, }, { date: 1462463880000, open: 50.225, high: 50.225, low: 50.185, close: 50.185, volume: 34127, }, { date: 1462463940000, open: 50.185, high: 50.195, low: 50.17, close: 50.17, volume: 51087, }, { date: 1462464000000, open: 50.172, high: 50.18, low: 50.15, close: 50.15, volume: 48437, }, { date: 1462464060000, open: 50.15, high: 50.16, low: 50.14, close: 50.145, volume: 42228, }, { date: 1462464120000, open: 50.14, high: 50.15, low: 50.12, close: 50.12, volume: 21056, }, { date: 1462464180000, open: 50.12, high: 50.1269, low: 50.11, close: 50.115, volume: 21521, }, { date: 1462464240000, open: 50.12, high: 50.13, low: 50.11, close: 50.1169, volume: 37630, }, { date: 1462464300000, open: 50.1161, high: 50.12, low: 50.1, close: 50.1, volume: 31983, }, { date: 1462464360000, open: 50.1, high: 50.105, low: 50.08, close: 50.105, volume: 62509, }, { date: 1462464420000, open: 50.105, high: 50.115, low: 50.1, close: 50.115, volume: 43806, }, { date: 1462464480000, open: 50.11, high: 50.12, low: 50.1, close: 50.11, volume: 33881, }, { date: 1462464540000, open: 50.11, high: 50.115, low: 50.09, close: 50.095, volume: 9531, }, { date: 1462464600000, open: 50.0998, high: 50.105, low: 50.05, close: 50.0664, volume: 50790, }, { date: 1462464660000, open: 50.0635, high: 50.1, low: 50.0635, close: 50.085, volume: 27525, }, { date: 1462464720000, open: 50.09, high: 50.09, low: 50.05, close: 50.07, volume: 30045, }, { date: 1462464780000, open: 50.07, high: 50.095, low: 50.07, close: 50.09, volume: 22683, }, { date: 1462464840000, open: 50.0801, high: 50.1, low: 50.08, close: 50.095, volume: 31833, }, { date: 1462464900000, open: 50.09, high: 50.11, low: 50.08, close: 50.1, volume: 19375, }, { date: 1462464960000, open: 50.1, high: 50.11, low: 50.085, close: 50.085, volume: 22642, }, { date: 1462465020000, open: 50.085, high: 50.11, low: 50.0801, close: 50.105, volume: 25183, }, { date: 1462465080000, open: 50.11, high: 50.11, low: 50.1, close: 50.11, volume: 35278, }, { date: 1462465140000, open: 50.11, high: 50.11, low: 50.05, close: 50.0661, volume: 31279, }, { date: 1462465200000, open: 50.07, high: 50.09, low: 50.0664, close: 50.08, volume: 16244, }, { date: 1462465260000, open: 50.08, high: 50.085, low: 50.05, close: 50.075, volume: 33439, }, { date: 1462465320000, open: 50.08, high: 50.08, low: 50.05, close: 50.055, volume: 24223, }, { date: 1462465380000, open: 50.0599, high: 50.065, low: 50.02, close: 50.04, volume: 66373, }, { date: 1462465440000, open: 50.035, high: 50.05, low: 50.02, close: 50.04, volume: 30239, }, { date: 1462465500000, open: 50.045, high: 50.055, low: 50.035, close: 50.05, volume: 22275, }, { date: 1462465560000, open: 50.05, high: 50.055, low: 50.02, close: 50.03, volume: 22722, }, { date: 1462465620000, open: 50.04, high: 50.07, low: 50.04, close: 50.06, volume: 16488, }, { date: 1462465680000, open: 50.055, high: 50.0569, low: 50.035, close: 50.035, volume: 15591, }, { date: 1462465740000, open: 50.03, high: 50.0572, low: 50.015, close: 50.0501, volume: 19413, }, { date: 1462465800000, open: 50.06, high: 50.08, low: 50.06, close: 50.06, volume: 13250, }, { date: 1462465860000, open: 50.05, high: 50.06, low: 50.04, close: 50.045, volume: 32014, }, { date: 1462465920000, open: 50.04, high: 50.045, low: 50, close: 50, volume: 80234, }, { date: 1462465980000, open: 50, high: 50.005, low: 49.985, close: 50, volume: 46610, }, { date: 1462466040000, open: 50, high: 50.03, low: 50, close: 50.015, volume: 27377, }, { date: 1462466100000, open: 50.015, high: 50.03, low: 50.0132, close: 50.015, volume: 27104, }, { date: 1462466160000, open: 50.015, high: 50.019, low: 49.99, close: 49.995, volume: 24248, }, { date: 1462466220000, open: 49.995, high: 50.015, low: 49.99, close: 50.01, volume: 67528, }, { date: 1462466280000, open: 50.015, high: 50.025, low: 50, close: 50, volume: 26555, }, { date: 1462466340000, open: 50.0011, high: 50.0171, low: 49.99, close: 49.99, volume: 18294, }, { date: 1462466400000, open: 49.995, high: 50, low: 49.99, close: 49.995, volume: 7142, }, { date: 1462466460000, open: 49.995, high: 50.03, low: 49.9938, close: 50.025, volume: 35182, }, { date: 1462466520000, open: 50.025, high: 50.025, low: 50.01, close: 50.015, volume: 10385, }, { date: 1462466580000, open: 50.015, high: 50.025, low: 50, close: 50.015, volume: 28564, }, { date: 1462466640000, open: 50.015, high: 50.029, low: 50.005, close: 50.005, volume: 32536, }, { date: 1462466700000, open: 50.005, high: 50.04, low: 50, close: 50.03, volume: 27389, }, { date: 1462466760000, open: 50.0298, high: 50.0298, low: 49.985, close: 49.995, volume: 37977, }, { date: 1462466820000, open: 50, high: 50.05, low: 49.9966, close: 50.04, volume: 28981, }, { date: 1462466880000, open: 50.04, high: 50.065, low: 50.035, close: 50.06, volume: 25376, }, { date: 1462466940000, open: 50.06, high: 50.07, low: 50.0505, close: 50.055, volume: 16226, }, { date: 1462467000000, open: 50.055, high: 50.08, low: 50.055, close: 50.07, volume: 17930, }, { date: 1462467060000, open: 50.07, high: 50.075, low: 50.06, close: 50.06, volume: 7244, }, { date: 1462467120000, open: 50.07, high: 50.08, low: 50.0601, close: 50.07, volume: 30485, }, { date: 1462467180000, open: 50.06, high: 50.08, low: 50.06, close: 50.07, volume: 17709, }, { date: 1462467240000, open: 50.0663, high: 50.079, low: 50.06, close: 50.07, volume: 16643, }, { date: 1462467300000, open: 50.06, high: 50.09, low: 50.06, close: 50.075, volume: 41826, }, { date: 1462467360000, open: 50.08, high: 50.0855, low: 50.065, close: 50.065, volume: 27236, }, { date: 1462467420000, open: 50.065, high: 50.08, low: 50.05, close: 50.08, volume: 30092, }, { date: 1462467480000, open: 50.075, high: 50.08, low: 50.035, close: 50.045, volume: 28410, }, { date: 1462467540000, open: 50.045, high: 50.06, low: 50.04, close: 50.045, volume: 18489, }, { date: 1462467600000, open: 50.045, high: 50.06, low: 50.025, close: 50.025, volume: 18633, }, { date: 1462467660000, open: 50.03, high: 50.05, low: 50.02, close: 50.036, volume: 24167, }, { date: 1462467720000, open: 50.03, high: 50.04, low: 50.01, close: 50.03, volume: 21309, }, { date: 1462467780000, open: 50.02, high: 50.03, low: 50, close: 50.005, volume: 22310, }, { date: 1462467840000, open: 50, high: 50.03, low: 50, close: 50.02, volume: 18975, }, { date: 1462467900000, open: 50.025, high: 50.04, low: 50, close: 50, volume: 16994, }, { date: 1462467960000, open: 50, high: 50.02, low: 50, close: 50.02, volume: 34045, }, { date: 1462468020000, open: 50.03, high: 50.03, low: 50, close: 50, volume: 16766, }, { date: 1462468080000, open: 50, high: 50.01, low: 50, close: 50, volume: 17991, }, { date: 1462468140000, open: 50, high: 50.015, low: 50, close: 50, volume: 10964, }, { date: 1462468200000, open: 50, high: 50.01, low: 49.98, close: 49.985, volume: 39639, }, { date: 1462468260000, open: 49.98, high: 50.02, low: 49.97, close: 50, volume: 87836, }, { date: 1462468320000, open: 49.99, high: 50.01, low: 49.98, close: 50.01, volume: 50978, }, { date: 1462468380000, open: 50, high: 50.015, low: 49.985, close: 50, volume: 24457, }, { date: 1462468440000, open: 50, high: 50.015, low: 49.98, close: 49.99, volume: 52051, }, { date: 1462468500000, open: 49.99, high: 50, low: 49.965, close: 50, volume: 73848, }, { date: 1462468560000, open: 49.99, high: 49.99, low: 49.96, close: 49.975, volume: 110369, }, { date: 1462468620000, open: 49.97, high: 49.9765, low: 49.96, close: 49.965, volume: 85298, }, { date: 1462468680000, open: 49.966, high: 49.98, low: 49.965, close: 49.9799, volume: 8644, }, { date: 1462468740000, open: 49.98, high: 49.985, low: 49.935, close: 49.9439, volume: 45638, }, { date: 1462468800000, open: 49.94, high: 49.965, low: 49.9342, close: 49.965, volume: 21418, }, { date: 1462468860000, open: 49.96, high: 49.97, low: 49.92, close: 49.93, volume: 24117, }, { date: 1462468920000, open: 49.92, high: 49.94, low: 49.91, close: 49.916, volume: 59261, }, { date: 1462468980000, open: 49.9162, high: 49.945, low: 49.9162, close: 49.935, volume: 52757, }, { date: 1462469040000, open: 49.93, high: 49.93, low: 49.88, close: 49.91, volume: 74830, }, { date: 1462469100000, open: 49.915, high: 49.9199, low: 49.87, close: 49.87, volume: 56586, }, { date: 1462469160000, open: 49.8637, high: 49.887, low: 49.855, close: 49.887, volume: 35263, }, { date: 1462469220000, open: 49.8837, high: 49.91, low: 49.88, close: 49.91, volume: 31995, }, { date: 1462469280000, open: 49.91, high: 49.92, low: 49.89, close: 49.915, volume: 46214, }, { date: 1462469340000, open: 49.9136, high: 49.915, low: 49.88, close: 49.9061, volume: 51293, }, { date: 1462469400000, open: 49.905, high: 49.92, low: 49.905, close: 49.915, volume: 3463, }, { date: 1462469460000, open: 49.915, high: 49.93, low: 49.89, close: 49.92, volume: 54836, }, { date: 1462469520000, open: 49.925, high: 49.9399, low: 49.905, close: 49.9399, volume: 60193, }, { date: 1462469580000, open: 49.935, high: 49.94, low: 49.9205, close: 49.9264, volume: 24129, }, { date: 1462469640000, open: 49.92, high: 49.93, low: 49.89, close: 49.8929, volume: 33930, }, { date: 1462469700000, open: 49.8917, high: 49.9, low: 49.88, close: 49.89, volume: 19155, }, { date: 1462469760000, open: 49.885, high: 49.91, low: 49.88, close: 49.9, volume: 67748, }, { date: 1462469820000, open: 49.9074, high: 49.9074, low: 49.88, close: 49.89, volume: 50390, }, { date: 1462469880000, open: 49.89, high: 49.91, low: 49.88, close: 49.8877, volume: 42100, }, { date: 1462469940000, open: 49.8808, high: 49.925, low: 49.875, close: 49.92, volume: 43778, }, { date: 1462470000000, open: 49.925, high: 49.925, low: 49.9, close: 49.905, volume: 32718, }, { date: 1462470060000, open: 49.9099, high: 49.93, low: 49.9, close: 49.91, volume: 19348, }, { date: 1462470120000, open: 49.905, high: 49.93, low: 49.9, close: 49.92, volume: 25795, }, { date: 1462470180000, open: 49.92, high: 49.93, low: 49.91, close: 49.9234, volume: 13265, }, { date: 1462470240000, open: 49.925, high: 49.93, low: 49.91, close: 49.9129, volume: 29496, }, { date: 1462470300000, open: 49.91, high: 49.925, low: 49.905, close: 49.9172, volume: 34511, }, { date: 1462470360000, open: 49.9101, high: 49.9376, low: 49.91, close: 49.925, volume: 25601, }, { date: 1462470420000, open: 49.925, high: 49.943, low: 49.915, close: 49.943, volume: 20163, }, { date: 1462470480000, open: 49.94, high: 49.9599, low: 49.93, close: 49.945, volume: 50732, }, { date: 1462470540000, open: 49.95, high: 49.96, low: 49.9199, close: 49.92, volume: 29108, }, { date: 1462470600000, open: 49.92, high: 49.9399, low: 49.91, close: 49.911, volume: 20443, }, { date: 1462470660000, open: 49.91, high: 49.96, low: 49.905, close: 49.9599, volume: 29111, }, { date: 1462470720000, open: 49.95, high: 49.955, low: 49.92, close: 49.925, volume: 20041, }, { date: 1462470780000, open: 49.925, high: 49.94, low: 49.92, close: 49.94, volume: 17765, }, { date: 1462470840000, open: 49.95, high: 49.97, low: 49.945, close: 49.97, volume: 30900, }, { date: 1462470900000, open: 49.9665, high: 49.97, low: 49.92, close: 49.92, volume: 26696, }, { date: 1462470960000, open: 49.91, high: 49.915, low: 49.875, close: 49.9083, volume: 37389, }, { date: 1462471020000, open: 49.9099, high: 49.93, low: 49.88, close: 49.9202, volume: 73289, }, { date: 1462471080000, open: 49.9261, high: 49.94, low: 49.9101, close: 49.927, volume: 31048, }, { date: 1462471140000, open: 49.925, high: 49.945, low: 49.92, close: 49.94, volume: 33698, }, { date: 1462471200000, open: 49.93, high: 49.95, low: 49.92, close: 49.94, volume: 39260, }, { date: 1462471260000, open: 49.94, high: 49.945, low: 49.88, close: 49.895, volume: 53055, }, { date: 1462471320000, open: 49.895, high: 49.925, low: 49.895, close: 49.92, volume: 17897, }, { date: 1462471380000, open: 49.925, high: 49.96, low: 49.91, close: 49.935, volume: 32021, }, { date: 1462471440000, open: 49.93, high: 49.9799, low: 49.92, close: 49.96, volume: 27249, }, { date: 1462471500000, open: 49.96, high: 49.975, low: 49.95, close: 49.97, volume: 36322, }, { date: 1462471560000, open: 49.97, high: 49.98, low: 49.951, close: 49.97, volume: 57495, }, { date: 1462471620000, open: 49.975, high: 49.99, low: 49.97, close: 49.99, volume: 34923, }, { date: 1462471680000, open: 49.9899, high: 49.9999, low: 49.97, close: 49.99, volume: 43946, }, { date: 1462471740000, open: 49.99, high: 50.01, low: 49.97, close: 49.97, volume: 54681, }, { date: 1462471800000, open: 49.97, high: 49.97, low: 49.94, close: 49.956, volume: 29406, }, { date: 1462471860000, open: 49.9501, high: 49.995, low: 49.95, close: 49.97, volume: 51153, }, { date: 1462471920000, open: 49.96, high: 49.97, low: 49.94, close: 49.94, volume: 19180, }, { date: 1462471980000, open: 49.94, high: 49.9699, low: 49.93, close: 49.94, volume: 38022, }, { date: 1462472040000, open: 49.9472, high: 49.97, low: 49.94, close: 49.96, volume: 22125, }, { date: 1462472100000, open: 49.965, high: 49.99, low: 49.96, close: 49.99, volume: 44108, }, { date: 1462472160000, open: 49.99, high: 50.01, low: 49.9865, close: 50, volume: 49751, }, { date: 1462472220000, open: 50.01, high: 50.01, low: 49.97, close: 50, volume: 36251, }, { date: 1462472280000, open: 50, high: 50.02, low: 49.99, close: 50.02, volume: 54045, }, { date: 1462472340000, open: 50.02, high: 50.02, low: 49.97, close: 49.97, volume: 32703, }, { date: 1462472400000, open: 49.9737, high: 49.98, low: 49.94, close: 49.955, volume: 32578, }, { date: 1462472460000, open: 49.9585, high: 49.98, low: 49.94, close: 49.98, volume: 24697, }, { date: 1462472520000, open: 49.98, high: 50.02, low: 49.98, close: 49.9992, volume: 61681, }, { date: 1462472580000, open: 49.995, high: 50, low: 49.97, close: 49.9999, volume: 32034, }, { date: 1462472640000, open: 49.9901, high: 50.02, low: 49.99, close: 50.01, volume: 22780, }, { date: 1462472700000, open: 50.015, high: 50.04, low: 50.01, close: 50.015, volume: 43826, }, { date: 1462472760000, open: 50.0103, high: 50.0103, low: 49.985, close: 49.985, volume: 18970, }, { date: 1462472820000, open: 49.985, high: 50.03, low: 49.985, close: 50.03, volume: 24816, }, { date: 1462472880000, open: 50.03, high: 50.03, low: 50, close: 50, volume: 27526, }, { date: 1462472940000, open: 50.005, high: 50.005, low: 49.99, close: 49.995, volume: 17206, }, { date: 1462473000000, open: 49.99, high: 49.9999, low: 49.95, close: 49.95, volume: 35210, }, { date: 1462473060000, open: 49.95, high: 49.975, low: 49.95, close: 49.975, volume: 35860, }, { date: 1462473120000, open: 49.98, high: 49.9899, low: 49.95, close: 49.96, volume: 26402, }, { date: 1462473180000, open: 49.955, high: 49.97, low: 49.94, close: 49.94, volume: 39815, }, { date: 1462473240000, open: 49.94, high: 49.965, low: 49.93, close: 49.965, volume: 15643, }, { date: 1462473300000, open: 49.96, high: 49.9665, low: 49.925, close: 49.94, volume: 27095, }, { date: 1462473360000, open: 49.935, high: 49.96, low: 49.93, close: 49.96, volume: 29501, }, { date: 1462473420000, open: 49.96, high: 49.96, low: 49.92, close: 49.935, volume: 46895, }, { date: 1462473480000, open: 49.933, high: 49.94, low: 49.93, close: 49.94, volume: 17595, }, { date: 1462473540000, open: 49.935, high: 49.955, low: 49.93, close: 49.935, volume: 27966, }, { date: 1462473600000, open: 49.935, high: 49.94, low: 49.93, close: 49.935, volume: 16513, }, { date: 1462473660000, open: 49.93, high: 49.935, low: 49.91, close: 49.92, volume: 29831, }, { date: 1462473720000, open: 49.92, high: 49.98, low: 49.911, close: 49.98, volume: 55659, }, { date: 1462473780000, open: 49.99, high: 50, low: 49.97, close: 49.98, volume: 26000, }, { date: 1462473840000, open: 49.98, high: 49.9899, low: 49.955, close: 49.965, volume: 27301, }, { date: 1462473900000, open: 49.965, high: 50, low: 49.965, close: 49.985, volume: 25956, }, { date: 1462473960000, open: 49.99, high: 50, low: 49.98, close: 49.985, volume: 46587, }, { date: 1462474020000, open: 49.98, high: 49.985, low: 49.975, close: 49.985, volume: 43631, }, { date: 1462474080000, open: 49.985, high: 50, low: 49.94, close: 49.94, volume: 87771, }, { date: 1462474140000, open: 49.94, high: 49.98, low: 49.93, close: 49.9725, volume: 37775, }, { date: 1462474200000, open: 49.97, high: 49.9799, low: 49.955, close: 49.955, volume: 26501, }, { date: 1462474260000, open: 49.951, high: 49.99, low: 49.95, close: 49.98, volume: 44545, }, { date: 1462474320000, open: 49.97, high: 49.985, low: 49.965, close: 49.97, volume: 21491, }, { date: 1462474380000, open: 49.97, high: 49.98, low: 49.96, close: 49.98, volume: 30452, }, { date: 1462474440000, open: 49.985, high: 50, low: 49.98, close: 49.98, volume: 55905, }, { date: 1462474500000, open: 49.98, high: 49.9899, low: 49.97, close: 49.9763, volume: 21515, }, { date: 1462474560000, open: 49.975, high: 50, low: 49.96, close: 50, volume: 8939, }, { date: 1462474620000, open: 49.99, high: 49.995, low: 49.95, close: 49.97, volume: 44398, }, { date: 1462474680000, open: 49.965, high: 49.99, low: 49.96, close: 49.98, volume: 46251, }, { date: 1462474740000, open: 49.98, high: 50, low: 49.98, close: 49.985, volume: 20558, }, { date: 1462474800000, open: 49.9899, high: 49.9899, low: 49.91, close: 49.92, volume: 46870, }, { date: 1462474860000, open: 49.92, high: 49.94, low: 49.92, close: 49.925, volume: 24828, }, { date: 1462474920000, open: 49.9269, high: 49.9471, low: 49.9232, close: 49.93, volume: 43753, }, { date: 1462474980000, open: 49.935, high: 49.935, low: 49.89, close: 49.9, volume: 77499, }, { date: 1462475040000, open: 49.9, high: 49.94, low: 49.895, close: 49.92, volume: 74041, }, { date: 1462475100000, open: 49.91, high: 49.93, low: 49.8936, close: 49.92, volume: 56506, }, { date: 1462475160000, open: 49.92, high: 49.9299, low: 49.8818, close: 49.8818, volume: 43277, }, { date: 1462475220000, open: 49.88, high: 49.92, low: 49.88, close: 49.9025, volume: 43328, }, { date: 1462475280000, open: 49.9, high: 49.91, low: 49.88, close: 49.9, volume: 30554, }, { date: 1462475340000, open: 49.8939, high: 49.905, low: 49.89, close: 49.896, volume: 21500, }, { date: 1462475400000, open: 49.8968, high: 49.9, low: 49.8862, close: 49.895, volume: 41370, }, { date: 1462475460000, open: 49.8999, high: 49.905, low: 49.88, close: 49.905, volume: 28194, }, { date: 1462475520000, open: 49.91, high: 49.915, low: 49.8942, close: 49.905, volume: 38548, }, { date: 1462475580000, open: 49.9, high: 49.91, low: 49.89, close: 49.9033, volume: 36448, }, { date: 1462475640000, open: 49.91, high: 49.94, low: 49.895, close: 49.9399, volume: 58848, }, { date: 1462475700000, open: 49.935, high: 49.94, low: 49.92, close: 49.93, volume: 29545, }, { date: 1462475760000, open: 49.925, high: 49.93, low: 49.905, close: 49.905, volume: 29498, }, { date: 1462475820000, open: 49.9, high: 49.93, low: 49.88, close: 49.89, volume: 72159, }, { date: 1462475880000, open: 49.89, high: 49.9, low: 49.88, close: 49.885, volume: 23605, }, { date: 1462475940000, open: 49.885, high: 49.89, low: 49.86, close: 49.89, volume: 42034, }, { date: 1462476000000, open: 49.885, high: 49.94, low: 49.885, close: 49.9147, volume: 56209, }, { date: 1462476060000, open: 49.915, high: 49.9372, low: 49.91, close: 49.93, volume: 22822, }, { date: 1462476120000, open: 49.9353, high: 49.957, low: 49.91, close: 49.957, volume: 83767, }, { date: 1462476180000, open: 49.955, high: 49.96, low: 49.94, close: 49.95, volume: 53826, }, { date: 1462476240000, open: 49.95, high: 49.95, low: 49.93, close: 49.94, volume: 25636, }, { date: 1462476300000, open: 49.945, high: 49.96, low: 49.935, close: 49.95, volume: 67131, }, { date: 1462476360000, open: 49.955, high: 49.98, low: 49.93, close: 49.94, volume: 49833, }, { date: 1462476420000, open: 49.94, high: 49.945, low: 49.9031, close: 49.925, volume: 45079, }, { date: 1462476480000, open: 49.92, high: 49.93, low: 49.91, close: 49.9224, volume: 12525, }, { date: 1462476540000, open: 49.93, high: 49.94, low: 49.92, close: 49.94, volume: 50152, }, { date: 1462476600000, open: 49.9364, high: 49.96, low: 49.925, close: 49.942, volume: 62184, }, { date: 1462476660000, open: 49.9437, high: 49.95, low: 49.93, close: 49.95, volume: 64137, }, { date: 1462476720000, open: 49.95, high: 49.95, low: 49.91, close: 49.92, volume: 18577, }, { date: 1462476780000, open: 49.92, high: 49.95, low: 49.915, close: 49.9499, volume: 53769, }, { date: 1462476840000, open: 49.949, high: 49.95, low: 49.91, close: 49.92, volume: 40091, }, { date: 1462476900000, open: 49.9231, high: 49.95, low: 49.92, close: 49.95, volume: 27972, }, { date: 1462476960000, open: 49.95, high: 49.95, low: 49.9035, close: 49.91, volume: 55850, }, { date: 1462477020000, open: 49.9099, high: 49.92, low: 49.89, close: 49.9101, volume: 60694, }, { date: 1462477080000, open: 49.91, high: 49.915, low: 49.9, close: 49.91, volume: 33355, }, { date: 1462477140000, open: 49.915, high: 49.92, low: 49.87, close: 49.876, volume: 61734, }, { date: 1462477200000, open: 49.87, high: 49.92, low: 49.86, close: 49.915, volume: 69492, }, { date: 1462477260000, open: 49.91, high: 49.935, low: 49.9, close: 49.91, volume: 72806, }, { date: 1462477320000, open: 49.91, high: 49.94, low: 49.895, close: 49.935, volume: 83105, }, { date: 1462477380000, open: 49.935, high: 49.94, low: 49.91, close: 49.93, volume: 54369, }, { date: 1462477440000, open: 49.93, high: 49.94, low: 49.915, close: 49.92, volume: 32745, }, { date: 1462477500000, open: 49.925, high: 49.95, low: 49.9, close: 49.93, volume: 100215, }, { date: 1462477560000, open: 49.93, high: 49.98, low: 49.93, close: 49.94, volume: 114474, }, { date: 1462477620000, open: 49.94, high: 49.99, low: 49.93, close: 49.985, volume: 75025, }, { date: 1462477680000, open: 49.985, high: 49.99, low: 49.97, close: 49.975, volume: 86967, }, { date: 1462477740000, open: 49.97, high: 49.99, low: 49.93, close: 49.93, volume: 99224, }, { date: 1462477800000, open: 49.93, high: 49.96, low: 49.92, close: 49.94, volume: 122633, }, { date: 1462477860000, open: 49.94, high: 49.9599, low: 49.92, close: 49.93, volume: 93720, }, { date: 1462477920000, open: 49.925, high: 49.93, low: 49.89, close: 49.89, volume: 160662, }, { date: 1462477980000, open: 49.9, high: 49.905, low: 49.85, close: 49.865, volume: 418984, }, { date: 1462478040000, open: 49.87, high: 49.88, low: 49.835, close: 49.8525, volume: 301979, }, { date: 1462478100000, open: 49.85, high: 49.87, low: 49.84, close: 49.861, volume: 123008, }, { date: 1462478160000, open: 49.865, high: 49.865, low: 49.815, close: 49.84, volume: 445600, }, { date: 1462478220000, open: 49.84, high: 49.87, low: 49.84, close: 49.87, volume: 173961, }, { date: 1462478280000, open: 49.87, high: 49.88, low: 49.86, close: 49.87, volume: 252921, }, { date: 1462478340000, open: 49.87, high: 49.9, low: 49.87, close: 49.875, volume: 347217, }, { date: 1462478400000, open: 49.875, high: 49.95, low: 49.875, close: 49.94, volume: 3056017, }, { date: 1462541400000, open: 49.92, high: 49.92, low: 49.75, close: 49.78, volume: 522036, }, { date: 1462541460000, open: 49.78, high: 49.905, low: 49.75, close: 49.78, volume: 90888, }, { date: 1462541520000, open: 49.78, high: 49.84, low: 49.78, close: 49.79, volume: 101044, }, { date: 1462541580000, open: 49.807, high: 49.86, low: 49.79, close: 49.8, volume: 38545, }, { date: 1462541640000, open: 49.8, high: 49.83, low: 49.78, close: 49.82, volume: 66423, }, { date: 1462541700000, open: 49.82, high: 49.82, low: 49.78, close: 49.79, volume: 66245, }, { date: 1462541760000, open: 49.785, high: 49.825, low: 49.7767, close: 49.7767, volume: 70788, }, { date: 1462541820000, open: 49.78, high: 49.82, low: 49.78, close: 49.81, volume: 107874, }, { date: 1462541880000, open: 49.8, high: 49.81, low: 49.75, close: 49.78, volume: 236650, }, { date: 1462541940000, open: 49.785, high: 49.9, low: 49.77, close: 49.89, volume: 96856, }, { date: 1462542000000, open: 49.88, high: 49.94, low: 49.86, close: 49.94, volume: 264785, }, { date: 1462542060000, open: 49.94, high: 49.9499, low: 49.88, close: 49.89, volume: 52583, }, { date: 1462542120000, open: 49.89, high: 49.92, low: 49.87, close: 49.91, volume: 83239, }, { date: 1462542180000, open: 49.91, high: 50, low: 49.91, close: 49.97, volume: 122473, }, { date: 1462542240000, open: 49.965, high: 49.99, low: 49.94, close: 49.97, volume: 67448, }, { date: 1462542300000, open: 49.97, high: 49.985, low: 49.9, close: 49.93, volume: 122265, }, { date: 1462542360000, open: 49.93, high: 49.94, low: 49.9, close: 49.9, volume: 62915, }, { date: 1462542420000, open: 49.9, high: 49.91, low: 49.8636, close: 49.89, volume: 58806, }, { date: 1462542480000, open: 49.88, high: 49.93, low: 49.88, close: 49.895, volume: 46884, }, { date: 1462542540000, open: 49.9, high: 49.91, low: 49.87, close: 49.87, volume: 63904, }, { date: 1462542600000, open: 49.87, high: 49.895, low: 49.85, close: 49.85, volume: 57226, }, { date: 1462542660000, open: 49.85, high: 49.865, low: 49.82, close: 49.865, volume: 152425, }, { date: 1462542720000, open: 49.86, high: 49.875, low: 49.83, close: 49.8556, volume: 80598, }, { date: 1462542780000, open: 49.86, high: 49.88, low: 49.85, close: 49.88, volume: 103419, }, { date: 1462542840000, open: 49.88, high: 49.895, low: 49.86, close: 49.86, volume: 48795, }, { date: 1462542900000, open: 49.87, high: 49.89, low: 49.845, close: 49.8535, volume: 68800, }, { date: 1462542960000, open: 49.86, high: 49.87, low: 49.85, close: 49.85, volume: 88757, }, { date: 1462543020000, open: 49.85, high: 49.88, low: 49.85, close: 49.85, volume: 103618, }, { date: 1462543080000, open: 49.85, high: 49.87, low: 49.82, close: 49.86, volume: 71400, }, { date: 1462543140000, open: 49.86, high: 49.86, low: 49.85, close: 49.85, volume: 41950, }, { date: 1462543200000, open: 49.85, high: 49.88, low: 49.85, close: 49.875, volume: 69527, }, { date: 1462543260000, open: 49.885, high: 49.8855, low: 49.86, close: 49.87, volume: 37384, }, { date: 1462543320000, open: 49.8659, high: 49.87, low: 49.85, close: 49.85, volume: 44143, }, { date: 1462543380000, open: 49.855, high: 49.88, low: 49.85, close: 49.88, volume: 57116, }, { date: 1462543440000, open: 49.875, high: 49.9, low: 49.87, close: 49.9, volume: 34178, }, { date: 1462543500000, open: 49.9036, high: 49.9036, low: 49.88, close: 49.885, volume: 15054, }, { date: 1462543560000, open: 49.89, high: 49.9, low: 49.85, close: 49.865, volume: 56335, }, { date: 1462543620000, open: 49.865, high: 49.93, low: 49.855, close: 49.93, volume: 48905, }, { date: 1462543680000, open: 49.93, high: 49.955, low: 49.93, close: 49.935, volume: 63140, }, { date: 1462543740000, open: 49.94, high: 49.945, low: 49.92, close: 49.945, volume: 28526, }, { date: 1462543800000, open: 49.95, high: 49.95, low: 49.92, close: 49.92, volume: 29963, }, { date: 1462543860000, open: 49.9299, high: 49.93, low: 49.89, close: 49.905, volume: 37089, }, { date: 1462543920000, open: 49.9, high: 49.9099, low: 49.85, close: 49.85, volume: 40795, }, { date: 1462543980000, open: 49.85, high: 49.895, low: 49.85, close: 49.8791, volume: 56498, }, { date: 1462544040000, open: 49.87, high: 49.9, low: 49.87, close: 49.875, volume: 53558, }, { date: 1462544100000, open: 49.88, high: 49.9, low: 49.86, close: 49.86, volume: 41300, }, { date: 1462544160000, open: 49.865, high: 49.8865, low: 49.85, close: 49.86, volume: 49114, }, { date: 1462544220000, open: 49.8601, high: 49.88, low: 49.84, close: 49.87, volume: 110809, }, { date: 1462544280000, open: 49.87, high: 49.88, low: 49.845, close: 49.855, volume: 72195, }, { date: 1462544340000, open: 49.855, high: 49.87, low: 49.85, close: 49.86, volume: 79105, }, { date: 1462544400000, open: 49.855, high: 49.8699, low: 49.83, close: 49.855, volume: 84091, }, { date: 1462544460000, open: 49.855, high: 49.86, low: 49.84, close: 49.8475, volume: 100367, }, { date: 1462544520000, open: 49.8499, high: 49.86, low: 49.84, close: 49.85, volume: 52167, }, { date: 1462544580000, open: 49.85, high: 49.86, low: 49.84, close: 49.86, volume: 34464, }, { date: 1462544640000, open: 49.86, high: 49.87, low: 49.85, close: 49.865, volume: 108877, }, { date: 1462544700000, open: 49.8538, high: 49.86, low: 49.79, close: 49.8, volume: 153917, }, { date: 1462544760000, open: 49.795, high: 49.82, low: 49.76, close: 49.82, volume: 93044, }, { date: 1462544820000, open: 49.8267, high: 49.85, low: 49.81, close: 49.8199, volume: 52422, }, { date: 1462544880000, open: 49.815, high: 49.83, low: 49.795, close: 49.83, volume: 46235, }, { date: 1462544940000, open: 49.83, high: 49.85, low: 49.82, close: 49.85, volume: 36974, }, { date: 1462545000000, open: 49.85, high: 49.89, low: 49.84, close: 49.86, volume: 42784, }, { date: 1462545060000, open: 49.85, high: 49.86, low: 49.82, close: 49.82, volume: 51960, }, { date: 1462545120000, open: 49.825, high: 49.84, low: 49.82, close: 49.839, volume: 41120, }, { date: 1462545180000, open: 49.835, high: 49.91, low: 49.83, close: 49.9, volume: 66238, }, { date: 1462545240000, open: 49.8999, high: 49.93, low: 49.89, close: 49.91, volume: 56955, }, { date: 1462545300000, open: 49.9199, high: 49.9199, low: 49.87, close: 49.87, volume: 30735, }, { date: 1462545360000, open: 49.875, high: 49.89, low: 49.87, close: 49.87, volume: 48017, }, { date: 1462545420000, open: 49.875, high: 49.877, low: 49.82, close: 49.825, volume: 47033, }, { date: 1462545480000, open: 49.83, high: 49.835, low: 49.79, close: 49.8, volume: 121483, }, { date: 1462545540000, open: 49.79, high: 49.84, low: 49.79, close: 49.84, volume: 52448, }, { date: 1462545600000, open: 49.83, high: 49.83, low: 49.79, close: 49.81, volume: 55976, }, { date: 1462545660000, open: 49.815, high: 49.815, low: 49.77, close: 49.805, volume: 118289, }, { date: 1462545720000, open: 49.801, high: 49.84, low: 49.79, close: 49.81, volume: 55402, }, { date: 1462545780000, open: 49.81, high: 49.82, low: 49.77, close: 49.78, volume: 48334, }, { date: 1462545840000, open: 49.78, high: 49.835, low: 49.77, close: 49.82, volume: 80757, }, { date: 1462545900000, open: 49.82, high: 49.82, low: 49.79, close: 49.81, volume: 41924, }, { date: 1462545960000, open: 49.805, high: 49.805, low: 49.76, close: 49.77, volume: 133261, }, { date: 1462546020000, open: 49.77, high: 49.79, low: 49.77, close: 49.7885, volume: 122427, }, { date: 1462546080000, open: 49.79, high: 49.8, low: 49.76, close: 49.775, volume: 154400, }, { date: 1462546140000, open: 49.78, high: 49.79, low: 49.77, close: 49.775, volume: 59542, }, { date: 1462546200000, open: 49.78, high: 49.78, low: 49.75, close: 49.75, volume: 85127, }, { date: 1462546260000, open: 49.755, high: 49.79, low: 49.75, close: 49.775, volume: 131504, }, { date: 1462546320000, open: 49.77, high: 49.7765, low: 49.72, close: 49.72, volume: 89872, }, { date: 1462546380000, open: 49.73, high: 49.75, low: 49.72, close: 49.75, volume: 71432, }, { date: 1462546440000, open: 49.75, high: 49.77, low: 49.74, close: 49.76, volume: 63300, }, { date: 1462546500000, open: 49.765, high: 49.8, low: 49.75, close: 49.8, volume: 104504, }, { date: 1462546560000, open: 49.8, high: 49.8185, low: 49.79, close: 49.8, volume: 49211, }, { date: 1462546620000, open: 49.8, high: 49.805, low: 49.77, close: 49.7765, volume: 33806, }, { date: 1462546680000, open: 49.78, high: 49.78, low: 49.74, close: 49.75, volume: 56203, }, { date: 1462546740000, open: 49.75, high: 49.8, low: 49.74, close: 49.8, volume: 59653, }, { date: 1462546800000, open: 49.81, high: 49.81, low: 49.77, close: 49.77, volume: 37110, }, { date: 1462546860000, open: 49.77, high: 49.795, low: 49.76, close: 49.78, volume: 53764, }, { date: 1462546920000, open: 49.78, high: 49.805, low: 49.775, close: 49.7899, volume: 37470, }, { date: 1462546980000, open: 49.79, high: 49.79, low: 49.75, close: 49.76, volume: 34264, }, { date: 1462547040000, open: 49.75, high: 49.755, low: 49.7136, close: 49.715, volume: 31618, }, { date: 1462547100000, open: 49.72, high: 49.73, low: 49.66, close: 49.665, volume: 108084, }, { date: 1462547160000, open: 49.6699, high: 49.68, low: 49.66, close: 49.675, volume: 37309, }, { date: 1462547220000, open: 49.675, high: 49.68, low: 49.66, close: 49.68, volume: 117384, }, { date: 1462547280000, open: 49.68, high: 49.7, low: 49.68, close: 49.699, volume: 25792, }, { date: 1462547340000, open: 49.69, high: 49.72, low: 49.69, close: 49.705, volume: 54188, }, { date: 1462547400000, open: 49.7067, high: 49.7067, low: 49.67, close: 49.68, volume: 57546, }, { date: 1462547460000, open: 49.675, high: 49.75, low: 49.675, close: 49.75, volume: 55844, }, { date: 1462547520000, open: 49.75, high: 49.785, low: 49.73, close: 49.77, volume: 62730, }, { date: 1462547580000, open: 49.77, high: 49.78, low: 49.75, close: 49.7599, volume: 23215, }, { date: 1462547640000, open: 49.75, high: 49.7568, low: 49.74, close: 49.7568, volume: 13432, }, { date: 1462547700000, open: 49.7545, high: 49.76, low: 49.75, close: 49.76, volume: 14326, }, { date: 1462547760000, open: 49.755, high: 49.765, low: 49.74, close: 49.74, volume: 57630, }, { date: 1462547820000, open: 49.74, high: 49.76, low: 49.71, close: 49.72, volume: 68163, }, { date: 1462547880000, open: 49.7201, high: 49.74, low: 49.72, close: 49.725, volume: 36259, }, { date: 1462547940000, open: 49.725, high: 49.83, low: 49.725, close: 49.8, volume: 50959, }, { date: 1462548000000, open: 49.8, high: 49.8, low: 49.78, close: 49.79, volume: 28503, }, { date: 1462548060000, open: 49.795, high: 49.81, low: 49.77, close: 49.805, volume: 40022, }, { date: 1462548120000, open: 49.8001, high: 49.85, low: 49.795, close: 49.8, volume: 69487, }, { date: 1462548180000, open: 49.8, high: 49.8, low: 49.775, close: 49.78, volume: 14880, }, { date: 1462548240000, open: 49.79, high: 49.835, low: 49.7801, close: 49.8101, volume: 23258, }, { date: 1462548300000, open: 49.815, high: 49.8158, low: 49.78, close: 49.8067, volume: 30242, }, { date: 1462548360000, open: 49.81, high: 49.83, low: 49.79, close: 49.81, volume: 33804, }, { date: 1462548420000, open: 49.8, high: 49.82, low: 49.79, close: 49.8156, volume: 30257, }, { date: 1462548480000, open: 49.82, high: 49.88, low: 49.82, close: 49.84, volume: 102934, }, { date: 1462548540000, open: 49.84, high: 49.87, low: 49.84, close: 49.85, volume: 26301, }, { date: 1462548600000, open: 49.8572, high: 49.8572, low: 49.82, close: 49.84, volume: 23495, }, { date: 1462548660000, open: 49.835, high: 49.84, low: 49.79, close: 49.79, volume: 26763, }, { date: 1462548720000, open: 49.795, high: 49.83, low: 49.7901, close: 49.83, volume: 26852, }, { date: 1462548780000, open: 49.83, high: 49.8399, low: 49.81, close: 49.81, volume: 30137, }, { date: 1462548840000, open: 49.805, high: 49.83, low: 49.79, close: 49.82, volume: 28387, }, { date: 1462548900000, open: 49.815, high: 49.82, low: 49.78, close: 49.79, volume: 21907, }, { date: 1462548960000, open: 49.79, high: 49.79, low: 49.76, close: 49.79, volume: 20656, }, { date: 1462549020000, open: 49.78, high: 49.79, low: 49.77, close: 49.78, volume: 13888, }, { date: 1462549080000, open: 49.785, high: 49.79, low: 49.765, close: 49.77, volume: 21947, }, { date: 1462549140000, open: 49.775, high: 49.805, low: 49.775, close: 49.805, volume: 24232, }, { date: 1462549200000, open: 49.8061, high: 49.81, low: 49.79, close: 49.7933, volume: 28808, }, { date: 1462549260000, open: 49.79, high: 49.8, low: 49.78, close: 49.78, volume: 19536, }, { date: 1462549320000, open: 49.785, high: 49.8, low: 49.78, close: 49.8, volume: 31847, }, { date: 1462549380000, open: 49.8, high: 49.816, low: 49.79, close: 49.795, volume: 20209, }, { date: 1462549440000, open: 49.795, high: 49.8, low: 49.77, close: 49.78, volume: 47141, }, { date: 1462549500000, open: 49.78, high: 49.79, low: 49.77, close: 49.785, volume: 60360, }, { date: 1462549560000, open: 49.78, high: 49.79, low: 49.78, close: 49.7801, volume: 8269, }, { date: 1462549620000, open: 49.78, high: 49.79, low: 49.76, close: 49.775, volume: 37404, }, { date: 1462549680000, open: 49.77, high: 49.78, low: 49.76, close: 49.76, volume: 68899, }, { date: 1462549740000, open: 49.76, high: 49.77, low: 49.74, close: 49.7667, volume: 61360, }, { date: 1462549800000, open: 49.77, high: 49.78, low: 49.75, close: 49.765, volume: 110738, }, { date: 1462549860000, open: 49.765, high: 49.77, low: 49.76, close: 49.76, volume: 39396, }, { date: 1462549920000, open: 49.76, high: 49.77, low: 49.75, close: 49.75, volume: 46424, }, { date: 1462549980000, open: 49.75, high: 49.7525, low: 49.73, close: 49.73, volume: 46976, }, { date: 1462550040000, open: 49.735, high: 49.74, low: 49.71, close: 49.7199, volume: 68800, }, { date: 1462550100000, open: 49.715, high: 49.72, low: 49.68, close: 49.7, volume: 69624, }, { date: 1462550160000, open: 49.71, high: 49.77, low: 49.71, close: 49.765, volume: 49524, }, { date: 1462550220000, open: 49.77, high: 49.77, low: 49.74, close: 49.755, volume: 94587, }, { date: 1462550280000, open: 49.755, high: 49.77, low: 49.74, close: 49.75, volume: 55945, }, { date: 1462550340000, open: 49.76, high: 49.79, low: 49.75, close: 49.79, volume: 17823, }, { date: 1462550400000, open: 49.79, high: 49.795, low: 49.75, close: 49.76, volume: 41978, }, { date: 1462550460000, open: 49.755, high: 49.755, low: 49.72, close: 49.74, volume: 65796, }, { date: 1462550520000, open: 49.74, high: 49.74, low: 49.71, close: 49.7364, volume: 19325, }, { date: 1462550580000, open: 49.74, high: 49.75, low: 49.73, close: 49.73, volume: 20170, }, { date: 1462550640000, open: 49.73, high: 49.74, low: 49.72, close: 49.73, volume: 11308, }, { date: 1462550700000, open: 49.73, high: 49.75, low: 49.72, close: 49.72, volume: 25233, }, { date: 1462550760000, open: 49.725, high: 49.729, low: 49.695, close: 49.725, volume: 46677, }, { date: 1462550820000, open: 49.72, high: 49.75, low: 49.71, close: 49.75, volume: 10741, }, { date: 1462550880000, open: 49.7462, high: 49.765, low: 49.7462, close: 49.76, volume: 10165, }, { date: 1462550940000, open: 49.76, high: 49.7699, low: 49.75, close: 49.7565, volume: 9531, }, { date: 1462551000000, open: 49.755, high: 49.76, low: 49.75, close: 49.755, volume: 2939, }, { date: 1462551060000, open: 49.755, high: 49.76, low: 49.74, close: 49.7535, volume: 17161, }, { date: 1462551120000, open: 49.75, high: 49.75, low: 49.74, close: 49.75, volume: 9288, }, { date: 1462551180000, open: 49.745, high: 49.75, low: 49.72, close: 49.75, volume: 35117, }, { date: 1462551240000, open: 49.7499, high: 49.75, low: 49.74, close: 49.7464, volume: 18888, }, { date: 1462551300000, open: 49.745, high: 49.77, low: 49.74, close: 49.77, volume: 9983, }, { date: 1462551360000, open: 49.76, high: 49.7659, low: 49.73, close: 49.74, volume: 63866, }, { date: 1462551420000, open: 49.745, high: 49.755, low: 49.73, close: 49.73, volume: 31143, }, { date: 1462551480000, open: 49.73, high: 49.735, low: 49.71, close: 49.7235, volume: 45958, }, { date: 1462551540000, open: 49.725, high: 49.74, low: 49.72, close: 49.74, volume: 10482, }, { date: 1462551600000, open: 49.74, high: 49.755, low: 49.74, close: 49.75, volume: 38533, }, { date: 1462551660000, open: 49.75, high: 49.76, low: 49.7, close: 49.71, volume: 69081, }, { date: 1462551720000, open: 49.705, high: 49.74, low: 49.705, close: 49.73, volume: 12915, }, { date: 1462551780000, open: 49.7356, high: 49.76, low: 49.7356, close: 49.74, volume: 33498, }, { date: 1462551840000, open: 49.75, high: 49.76, low: 49.74, close: 49.75, volume: 19611, }, { date: 1462551900000, open: 49.755, high: 49.755, low: 49.73, close: 49.735, volume: 63519, }, { date: 1462551960000, open: 49.735, high: 49.74, low: 49.7, close: 49.71, volume: 44235, }, { date: 1462552020000, open: 49.71, high: 49.75, low: 49.71, close: 49.73, volume: 25044, }, { date: 1462552080000, open: 49.735, high: 49.75, low: 49.73, close: 49.745, volume: 27751, }, { date: 1462552140000, open: 49.73, high: 49.75, low: 49.73, close: 49.735, volume: 7485, }, { date: 1462552200000, open: 49.7301, high: 49.74, low: 49.72, close: 49.74, volume: 25312, }, { date: 1462552260000, open: 49.735, high: 49.75, low: 49.73, close: 49.74, volume: 13498, }, { date: 1462552320000, open: 49.74, high: 49.745, low: 49.72, close: 49.725, volume: 34494, }, { date: 1462552380000, open: 49.72, high: 49.74, low: 49.72, close: 49.73, volume: 8797, }, { date: 1462552440000, open: 49.735, high: 49.74, low: 49.72, close: 49.73, volume: 27264, }, { date: 1462552500000, open: 49.72, high: 49.75, low: 49.72, close: 49.75, volume: 15303, }, { date: 1462552560000, open: 49.745, high: 49.75, low: 49.73, close: 49.74, volume: 22037, }, { date: 1462552620000, open: 49.745, high: 49.75, low: 49.74, close: 49.745, volume: 28221, }, { date: 1462552680000, open: 49.75, high: 49.755, low: 49.73, close: 49.75, volume: 51864, }, { date: 1462552740000, open: 49.75, high: 49.755, low: 49.74, close: 49.745, volume: 42761, }, { date: 1462552800000, open: 49.745, high: 49.76, low: 49.73, close: 49.75, volume: 111587, }, { date: 1462552860000, open: 49.745, high: 49.76, low: 49.72, close: 49.7299, volume: 92446, }, { date: 1462552920000, open: 49.725, high: 49.73, low: 49.72, close: 49.73, volume: 12077, }, { date: 1462552980000, open: 49.74, high: 49.76, low: 49.735, close: 49.755, volume: 24071, }, { date: 1462553040000, open: 49.76, high: 49.79, low: 49.76, close: 49.78, volume: 23876, }, { date: 1462553100000, open: 49.785, high: 49.82, low: 49.78, close: 49.82, volume: 29745, }, { date: 1462553160000, open: 49.816, high: 49.816, low: 49.8, close: 49.8, volume: 43202, }, { date: 1462553220000, open: 49.81, high: 49.83, low: 49.79, close: 49.79, volume: 41814, }, { date: 1462553280000, open: 49.7966, high: 49.81, low: 49.78, close: 49.79, volume: 32007, }, { date: 1462553340000, open: 49.793, high: 49.81, low: 49.79, close: 49.8071, volume: 11451, }, { date: 1462553400000, open: 49.802, high: 49.81, low: 49.8, close: 49.8, volume: 8500, }, { date: 1462553460000, open: 49.8, high: 49.8, low: 49.78, close: 49.785, volume: 17523, }, { date: 1462553520000, open: 49.78, high: 49.79, low: 49.77, close: 49.7707, volume: 13978, }, { date: 1462553580000, open: 49.77, high: 49.775, low: 49.75, close: 49.775, volume: 24486, }, { date: 1462553640000, open: 49.775, high: 49.78, low: 49.77, close: 49.77, volume: 13100, }, { date: 1462553700000, open: 49.775, high: 49.785, low: 49.77, close: 49.775, volume: 20483, }, { date: 1462553760000, open: 49.775, high: 49.8, low: 49.77, close: 49.7965, volume: 32106, }, { date: 1462553820000, open: 49.8, high: 49.87, low: 49.8, close: 49.857, volume: 44772, }, { date: 1462553880000, open: 49.8536, high: 49.86, low: 49.84, close: 49.85, volume: 104649, }, { date: 1462553940000, open: 49.85, high: 49.8656, low: 49.8325, close: 49.8601, volume: 55424, }, { date: 1462554000000, open: 49.865, high: 49.88, low: 49.833, close: 49.88, volume: 45519, }, { date: 1462554060000, open: 49.88, high: 49.9, low: 49.872, close: 49.88, volume: 35506, }, { date: 1462554120000, open: 49.88, high: 49.9, low: 49.87, close: 49.895, volume: 19472, }, { date: 1462554180000, open: 49.89, high: 49.9, low: 49.89, close: 49.895, volume: 7963, }, { date: 1462554240000, open: 49.89, high: 49.895, low: 49.87, close: 49.875, volume: 24782, }, { date: 1462554300000, open: 49.875, high: 49.8758, low: 49.86, close: 49.87, volume: 10350, }, { date: 1462554360000, open: 49.87, high: 49.89, low: 49.87, close: 49.89, volume: 21161, }, { date: 1462554420000, open: 49.89, high: 49.93, low: 49.88, close: 49.93, volume: 66554, }, { date: 1462554480000, open: 49.93, high: 49.95, low: 49.92, close: 49.94, volume: 48541, }, { date: 1462554540000, open: 49.945, high: 49.945, low: 49.91, close: 49.91, volume: 12725, }, { date: 1462554600000, open: 49.915, high: 49.92, low: 49.91, close: 49.9176, volume: 15540, }, { date: 1462554660000, open: 49.91, high: 49.945, low: 49.91, close: 49.945, volume: 21866, }, { date: 1462554720000, open: 49.94, high: 49.95, low: 49.925, close: 49.935, volume: 89940, }, { date: 1462554780000, open: 49.93, high: 49.94, low: 49.88, close: 49.9133, volume: 71383, }, { date: 1462554840000, open: 49.92, high: 49.96, low: 49.92, close: 49.94, volume: 42099, }, { date: 1462554900000, open: 49.94, high: 49.94, low: 49.91, close: 49.9152, volume: 11256, }, { date: 1462554960000, open: 49.915, high: 49.93, low: 49.915, close: 49.92, volume: 12108, }, { date: 1462555020000, open: 49.93, high: 49.9899, low: 49.9298, close: 49.9736, volume: 35137, }, { date: 1462555080000, open: 49.97, high: 49.98, low: 49.97, close: 49.98, volume: 37072, }, { date: 1462555140000, open: 49.985, high: 50, low: 49.98, close: 49.9901, volume: 21888, }, { date: 1462555200000, open: 50, high: 50, low: 49.97, close: 49.975, volume: 12273, }, { date: 1462555260000, open: 49.97, high: 49.99, low: 49.97, close: 49.99, volume: 19970, }, { date: 1462555320000, open: 49.99, high: 49.99, low: 49.96, close: 49.96, volume: 29662, }, { date: 1462555380000, open: 49.96, high: 49.97, low: 49.96, close: 49.965, volume: 12286, }, { date: 1462555440000, open: 49.96, high: 49.98, low: 49.955, close: 49.97, volume: 14858, }, { date: 1462555500000, open: 49.98, high: 50.01, low: 49.97, close: 50, volume: 121246, }, { date: 1462555560000, open: 50.005, high: 50.005, low: 49.965, close: 49.9963, volume: 31144, }, { date: 1462555620000, open: 50, high: 50.02, low: 49.993, close: 49.995, volume: 38079, }, { date: 1462555680000, open: 49.995, high: 50.01, low: 49.98, close: 50, volume: 19200, }, { date: 1462555740000, open: 50, high: 50.025, low: 49.995, close: 50.01, volume: 28832, }, { date: 1462555800000, open: 50.0199, high: 50.0199, low: 49.97, close: 49.98, volume: 30840, }, { date: 1462555860000, open: 49.985, high: 49.99, low: 49.96, close: 49.97, volume: 44019, }, { date: 1462555920000, open: 49.97, high: 49.99, low: 49.97, close: 49.97, volume: 24172, }, { date: 1462555980000, open: 49.97, high: 49.98, low: 49.96, close: 49.9601, volume: 13855, }, { date: 1462556040000, open: 49.96, high: 49.97, low: 49.95, close: 49.95, volume: 12130, }, { date: 1462556100000, open: 49.95, high: 49.99, low: 49.95, close: 49.98, volume: 23847, }, { date: 1462556160000, open: 49.985, high: 50.01, low: 49.98, close: 50.0031, volume: 55711, }, { date: 1462556220000, open: 50.0001, high: 50.01, low: 49.99, close: 50, volume: 24411, }, { date: 1462556280000, open: 50.01, high: 50.01, low: 49.9736, close: 49.9799, volume: 26829, }, { date: 1462556340000, open: 49.98, high: 50, low: 49.96, close: 49.99, volume: 57432, }, { date: 1462556400000, open: 49.9944, high: 50.015, low: 49.99, close: 50.01, volume: 21391, }, { date: 1462556460000, open: 50.01, high: 50.025, low: 50, close: 50.005, volume: 46600, }, { date: 1462556520000, open: 50.005, high: 50.01, low: 49.97, close: 49.97, volume: 40955, }, { date: 1462556580000, open: 49.979, high: 49.9868, low: 49.97, close: 49.985, volume: 12977, }, { date: 1462556640000, open: 49.985, high: 49.99, low: 49.98, close: 49.9801, volume: 10674, }, { date: 1462556700000, open: 49.98, high: 50.0073, low: 49.98, close: 49.98, volume: 49923, }, { date: 1462556760000, open: 49.985, high: 50, low: 49.98, close: 49.99, volume: 18769, }, { date: 1462556820000, open: 49.99, high: 50.03, low: 49.98, close: 50, volume: 60525, }, { date: 1462556880000, open: 50.005, high: 50.0387, low: 50, close: 50.0387, volume: 80464, }, { date: 1462556940000, open: 50.0301, high: 50.045, low: 50, close: 50.04, volume: 55703, }, { date: 1462557000000, open: 50.045, high: 50.09, low: 50.02, close: 50.06, volume: 121732, }, { date: 1462557060000, open: 50.0542, high: 50.0856, low: 50.04, close: 50.0856, volume: 71757, }, { date: 1462557120000, open: 50.081, high: 50.09, low: 50.08, close: 50.0836, volume: 48477, }, { date: 1462557180000, open: 50.08, high: 50.08, low: 50.07, close: 50.075, volume: 8245, }, { date: 1462557240000, open: 50.075, high: 50.085, low: 50.07, close: 50.085, volume: 17124, }, { date: 1462557300000, open: 50.085, high: 50.13, low: 50.085, close: 50.1, volume: 72216, }, { date: 1462557360000, open: 50.1, high: 50.1, low: 50.06, close: 50.08, volume: 82000, }, { date: 1462557420000, open: 50.08, high: 50.1, low: 50.07, close: 50.09, volume: 76920, }, { date: 1462557480000, open: 50.0936, high: 50.1299, low: 50.08, close: 50.11, volume: 52797, }, { date: 1462557540000, open: 50.1125, high: 50.16, low: 50.1125, close: 50.16, volume: 66158, }, { date: 1462557600000, open: 50.155, high: 50.2199, low: 50.15, close: 50.21, volume: 46314, }, { date: 1462557660000, open: 50.215, high: 50.22, low: 50.17, close: 50.1701, volume: 26107, }, { date: 1462557720000, open: 50.18, high: 50.21, low: 50.17, close: 50.19, volume: 43529, }, { date: 1462557780000, open: 50.1865, high: 50.205, low: 50.16, close: 50.195, volume: 32086, }, { date: 1462557840000, open: 50.19, high: 50.198, low: 50.17, close: 50.19, volume: 28464, }, { date: 1462557900000, open: 50.18, high: 50.24, low: 50.17, close: 50.21, volume: 65102, }, { date: 1462557960000, open: 50.2064, high: 50.25, low: 50.2, close: 50.24, volume: 57614, }, { date: 1462558020000, open: 50.245, high: 50.26, low: 50.24, close: 50.26, volume: 51693, }, { date: 1462558080000, open: 50.26, high: 50.29, low: 50.25, close: 50.28, volume: 60021, }, { date: 1462558140000, open: 50.28, high: 50.31, low: 50.27, close: 50.2801, volume: 49518, }, { date: 1462558200000, open: 50.285, high: 50.32, low: 50.28, close: 50.3, volume: 74891, }, { date: 1462558260000, open: 50.305, high: 50.32, low: 50.275, close: 50.29, volume: 33636, }, { date: 1462558320000, open: 50.28, high: 50.31, low: 50.27, close: 50.3, volume: 39413, }, { date: 1462558380000, open: 50.3, high: 50.31, low: 50.26, close: 50.26, volume: 28469, }, { date: 1462558440000, open: 50.26, high: 50.28, low: 50.25, close: 50.25, volume: 43002, }, { date: 1462558500000, open: 50.26, high: 50.29, low: 50.25, close: 50.29, volume: 23944, }, { date: 1462558560000, open: 50.298, high: 50.305, low: 50.27, close: 50.3, volume: 57470, }, { date: 1462558620000, open: 50.3, high: 50.32, low: 50.2901, close: 50.305, volume: 44622, }, { date: 1462558680000, open: 50.305, high: 50.32, low: 50.28, close: 50.28, volume: 66126, }, { date: 1462558740000, open: 50.28, high: 50.31, low: 50.27, close: 50.31, volume: 39828, }, { date: 1462558800000, open: 50.3, high: 50.3, low: 50.27, close: 50.29, volume: 31464, }, { date: 1462558860000, open: 50.29, high: 50.295, low: 50.275, close: 50.28, volume: 20021, }, { date: 1462558920000, open: 50.275, high: 50.285, low: 50.2338, close: 50.26, volume: 42620, }, { date: 1462558980000, open: 50.265, high: 50.28, low: 50.24, close: 50.245, volume: 36999, }, { date: 1462559040000, open: 50.245, high: 50.245, low: 50.21, close: 50.225, volume: 34141, }, { date: 1462559100000, open: 50.22, high: 50.23, low: 50.185, close: 50.19, volume: 44834, }, { date: 1462559160000, open: 50.193, high: 50.2, low: 50.185, close: 50.199, volume: 11357, }, { date: 1462559220000, open: 50.19, high: 50.22, low: 50.18, close: 50.195, volume: 40033, }, { date: 1462559280000, open: 50.2, high: 50.23, low: 50.195, close: 50.21, volume: 59710, }, { date: 1462559340000, open: 50.215, high: 50.24, low: 50.2001, close: 50.232, volume: 55015, }, { date: 1462559400000, open: 50.23, high: 50.235, low: 50.18, close: 50.19, volume: 72386, }, { date: 1462559460000, open: 50.19, high: 50.195, low: 50.15, close: 50.179, volume: 73827, }, { date: 1462559520000, open: 50.175, high: 50.18, low: 50.16, close: 50.16, volume: 17009, }, { date: 1462559580000, open: 50.165, high: 50.185, low: 50.16, close: 50.16, volume: 40129, }, { date: 1462559640000, open: 50.16, high: 50.17, low: 50.135, close: 50.16, volume: 27500, }, { date: 1462559700000, open: 50.15, high: 50.16, low: 50.14, close: 50.15, volume: 18153, }, { date: 1462559760000, open: 50.15, high: 50.165, low: 50.13, close: 50.135, volume: 33656, }, { date: 1462559820000, open: 50.125, high: 50.14, low: 50.11, close: 50.135, volume: 29250, }, { date: 1462559880000, open: 50.135, high: 50.18, low: 50.135, close: 50.1765, volume: 50127, }, { date: 1462559940000, open: 50.17, high: 50.18, low: 50.16, close: 50.175, volume: 40824, }, { date: 1462560000000, open: 50.18, high: 50.2, low: 50.175, close: 50.19, volume: 34442, }, { date: 1462560060000, open: 50.185, high: 50.205, low: 50.17, close: 50.1999, volume: 79029, }, { date: 1462560120000, open: 50.205, high: 50.26, low: 50.2, close: 50.25, volume: 88131, }, { date: 1462560180000, open: 50.26, high: 50.28, low: 50.245, close: 50.28, volume: 69653, }, { date: 1462560240000, open: 50.28, high: 50.31, low: 50.275, close: 50.305, volume: 74950, }, { date: 1462560300000, open: 50.305, high: 50.33, low: 50.28, close: 50.3, volume: 64196, }, { date: 1462560360000, open: 50.295, high: 50.3, low: 50.27, close: 50.285, volume: 78860, }, { date: 1462560420000, open: 50.285, high: 50.3, low: 50.26, close: 50.261, volume: 29804, }, { date: 1462560480000, open: 50.2601, high: 50.28, low: 50.24, close: 50.28, volume: 59480, }, { date: 1462560540000, open: 50.27, high: 50.27, low: 50.22, close: 50.2338, volume: 32492, }, { date: 1462560600000, open: 50.235, high: 50.24, low: 50.21, close: 50.2199, volume: 48362, }, { date: 1462560660000, open: 50.21, high: 50.24, low: 50.21, close: 50.24, volume: 122646, }, { date: 1462560720000, open: 50.235, high: 50.24, low: 50.2235, close: 50.2235, volume: 88629, }, { date: 1462560780000, open: 50.2236, high: 50.23, low: 50.2, close: 50.225, volume: 36858, }, { date: 1462560840000, open: 50.23, high: 50.24, low: 50.22, close: 50.235, volume: 25169, }, { date: 1462560900000, open: 50.2332, high: 50.28, low: 50.2332, close: 50.25, volume: 66915, }, { date: 1462560960000, open: 50.25, high: 50.25, low: 50.215, close: 50.215, volume: 18936, }, { date: 1462561020000, open: 50.215, high: 50.23, low: 50.21, close: 50.23, volume: 19295, }, { date: 1462561080000, open: 50.23, high: 50.25, low: 50.22, close: 50.23, volume: 36251, }, { date: 1462561140000, open: 50.225, high: 50.24, low: 50.22, close: 50.235, volume: 20846, }, { date: 1462561200000, open: 50.24, high: 50.25, low: 50.22, close: 50.25, volume: 93892, }, { date: 1462561260000, open: 50.25, high: 50.26, low: 50.23, close: 50.25, volume: 404855, }, { date: 1462561320000, open: 50.25, high: 50.285, low: 50.24, close: 50.285, volume: 58467, }, { date: 1462561380000, open: 50.2801, high: 50.2801, low: 50.25, close: 50.26, volume: 41889, }, { date: 1462561440000, open: 50.27, high: 50.28, low: 50.25, close: 50.25, volume: 33326, }, { date: 1462561500000, open: 50.25, high: 50.27, low: 50.25, close: 50.27, volume: 36870, }, { date: 1462561560000, open: 50.26, high: 50.28, low: 50.25, close: 50.255, volume: 49509, }, { date: 1462561620000, open: 50.26, high: 50.26, low: 50.25, close: 50.26, volume: 31978, }, { date: 1462561680000, open: 50.2562, high: 50.26, low: 50.25, close: 50.25, volume: 20850, }, { date: 1462561740000, open: 50.255, high: 50.278, low: 50.25, close: 50.25, volume: 110701, }, { date: 1462561800000, open: 50.2599, high: 50.27, low: 50.22, close: 50.2264, volume: 112213, }, { date: 1462561860000, open: 50.23, high: 50.295, low: 50.225, close: 50.29, volume: 69790, }, { date: 1462561920000, open: 50.29, high: 50.315, low: 50.29, close: 50.31, volume: 73631, }, { date: 1462561980000, open: 50.31, high: 50.32, low: 50.3001, close: 50.3199, volume: 67072, }, { date: 1462562040000, open: 50.31, high: 50.3101, low: 50.29, close: 50.3, volume: 70373, }, { date: 1462562100000, open: 50.295, high: 50.295, low: 50.24, close: 50.25, volume: 85303, }, { date: 1462562160000, open: 50.255, high: 50.27, low: 50.23, close: 50.24, volume: 64469, }, { date: 1462562220000, open: 50.25, high: 50.28, low: 50.25, close: 50.255, volume: 28005, }, { date: 1462562280000, open: 50.2501, high: 50.28, low: 50.2501, close: 50.275, volume: 18825, }, { date: 1462562340000, open: 50.28, high: 50.31, low: 50.275, close: 50.3, volume: 45745, }, { date: 1462562400000, open: 50.305, high: 50.305, low: 50.27, close: 50.27, volume: 37304, }, { date: 1462562460000, open: 50.27, high: 50.28, low: 50.26, close: 50.28, volume: 21141, }, { date: 1462562520000, open: 50.29, high: 50.295, low: 50.26, close: 50.285, volume: 38071, }, { date: 1462562580000, open: 50.2801, high: 50.295, low: 50.255, close: 50.27, volume: 43723, }, { date: 1462562640000, open: 50.27, high: 50.3, low: 50.27, close: 50.3, volume: 31283, }, { date: 1462562700000, open: 50.3, high: 50.31, low: 50.27, close: 50.29, volume: 69629, }, { date: 1462562760000, open: 50.28, high: 50.31, low: 50.28, close: 50.3, volume: 52735, }, { date: 1462562820000, open: 50.3, high: 50.305, low: 50.28, close: 50.29, volume: 23375, }, { date: 1462562880000, open: 50.29, high: 50.31, low: 50.28, close: 50.2899, volume: 63264, }, { date: 1462562940000, open: 50.28, high: 50.31, low: 50.27, close: 50.305, volume: 24592, }, { date: 1462563000000, open: 50.31, high: 50.31, low: 50.28, close: 50.2889, volume: 26883, }, { date: 1462563060000, open: 50.28, high: 50.29, low: 50.25, close: 50.25, volume: 48553, }, { date: 1462563120000, open: 50.245, high: 50.2499, low: 50.21, close: 50.21, volume: 61006, }, { date: 1462563180000, open: 50.22, high: 50.24, low: 50.21, close: 50.21, volume: 71603, }, { date: 1462563240000, open: 50.21, high: 50.211, low: 50.18, close: 50.1901, volume: 56905, }, { date: 1462563300000, open: 50.2, high: 50.21, low: 50.19, close: 50.2099, volume: 35336, }, { date: 1462563360000, open: 50.2, high: 50.21, low: 50.19, close: 50.208, volume: 35216, }, { date: 1462563420000, open: 50.21, high: 50.235, low: 50.2, close: 50.21, volume: 87654, }, { date: 1462563480000, open: 50.2, high: 50.25, low: 50.19, close: 50.2242, volume: 97738, }, { date: 1462563540000, open: 50.23, high: 50.245, low: 50.22, close: 50.22, volume: 40049, }, { date: 1462563600000, open: 50.225, high: 50.25, low: 50.2106, close: 50.23, volume: 50348, }, { date: 1462563660000, open: 50.23, high: 50.25, low: 50.22, close: 50.25, volume: 81244, }, { date: 1462563720000, open: 50.24, high: 50.27, low: 50.24, close: 50.245, volume: 52392, }, { date: 1462563780000, open: 50.25, high: 50.27, low: 50.24, close: 50.255, volume: 40152, }, { date: 1462563840000, open: 50.25, high: 50.26, low: 50.24, close: 50.255, volume: 56050, }, { date: 1462563900000, open: 50.26, high: 50.26, low: 50.24, close: 50.24, volume: 41947, }, { date: 1462563960000, open: 50.24, high: 50.27, low: 50.24, close: 50.25, volume: 177680, }, { date: 1462564020000, open: 50.25, high: 50.26, low: 50.225, close: 50.2364, volume: 67917, }, { date: 1462564080000, open: 50.235, high: 50.24, low: 50.2, close: 50.2, volume: 104405, }, { date: 1462564140000, open: 50.205, high: 50.21, low: 50.19, close: 50.2099, volume: 56730, }, { date: 1462564200000, open: 50.205, high: 50.22, low: 50.17, close: 50.215, volume: 91831, }, { date: 1462564260000, open: 50.21, high: 50.24, low: 50.175, close: 50.23, volume: 94161, }, { date: 1462564320000, open: 50.23, high: 50.26, low: 50.225, close: 50.235, volume: 69505, }, { date: 1462564380000, open: 50.24, high: 50.29, low: 50.235, close: 50.29, volume: 81509, }, { date: 1462564440000, open: 50.29, high: 50.3, low: 50.28, close: 50.29, volume: 111442, }, { date: 1462564500000, open: 50.285, high: 50.32, low: 50.28, close: 50.31, volume: 101394, }, { date: 1462564560000, open: 50.31, high: 50.33, low: 50.3, close: 50.325, volume: 130797, }, { date: 1462564620000, open: 50.325, high: 50.34, low: 50.31, close: 50.33, volume: 135468, }, { date: 1462564680000, open: 50.335, high: 50.34, low: 50.32, close: 50.33, volume: 148279, }, { date: 1462564740000, open: 50.33, high: 50.345, low: 50.31, close: 50.3299, volume: 175403, }, { date: 1462564800000, open: 50.325, high: 50.39, low: 50.32, close: 50.39, volume: 2080946, }, { date: 1462800600000, open: 50.49, high: 50.5, low: 50.45, close: 50.465, volume: 268024, }, { date: 1462800660000, open: 50.4501, high: 50.5, low: 50.37, close: 50.37, volume: 98767, }, { date: 1462800720000, open: 50.38, high: 50.5, low: 50.37, close: 50.46, volume: 84999, }, { date: 1462800780000, open: 50.45, high: 50.47, low: 50.42, close: 50.44, volume: 40236, }, { date: 1462800840000, open: 50.45, high: 50.48, low: 50.42, close: 50.48, volume: 43672, }, { date: 1462800900000, open: 50.479, high: 50.49, low: 50.43, close: 50.43, volume: 22774, }, { date: 1462800960000, open: 50.43, high: 50.43, low: 50.375, close: 50.41, volume: 144331, }, { date: 1462801020000, open: 50.41, high: 50.44, low: 50.38, close: 50.44, volume: 85592, }, { date: 1462801080000, open: 50.4302, high: 50.46, low: 50.4, close: 50.445, volume: 63865, }, { date: 1462801140000, open: 50.45, high: 50.4864, low: 50.4301, close: 50.485, volume: 56008, }, { date: 1462801200000, open: 50.485, high: 50.5, low: 50.44, close: 50.44, volume: 61375, }, { date: 1462801260000, open: 50.43, high: 50.44, low: 50.36, close: 50.405, volume: 95962, }, { date: 1462801320000, open: 50.4056, high: 50.44, low: 50.39, close: 50.4301, volume: 82157, }, { date: 1462801380000, open: 50.43, high: 50.43, low: 50.38, close: 50.395, volume: 54126, }, { date: 1462801440000, open: 50.405, high: 50.46, low: 50.405, close: 50.46, volume: 76302, }, { date: 1462801500000, open: 50.455, high: 50.555, low: 50.455, close: 50.52, volume: 99856, }, { date: 1462801560000, open: 50.52, high: 50.578, low: 50.515, close: 50.57, volume: 50054, }, { date: 1462801620000, open: 50.57, high: 50.58, low: 50.55, close: 50.56, volume: 55612, }, { date: 1462801680000, open: 50.5537, high: 50.56, low: 50.55, close: 50.56, volume: 23403, }, { date: 1462801740000, open: 50.56, high: 50.566, low: 50.5, close: 50.5, volume: 50784, }, { date: 1462801800000, open: 50.51, high: 50.51, low: 50.43, close: 50.445, volume: 40384, }, { date: 1462801860000, open: 50.44, high: 50.48, low: 50.43, close: 50.45, volume: 193895, }, { date: 1462801920000, open: 50.45, high: 50.47, low: 50.44, close: 50.455, volume: 35113, }, { date: 1462801980000, open: 50.45, high: 50.485, low: 50.44, close: 50.48, volume: 40157, }, { date: 1462802040000, open: 50.48, high: 50.54, low: 50.47, close: 50.5062, volume: 64504, }, { date: 1462802100000, open: 50.51, high: 50.53, low: 50.46, close: 50.495, volume: 50775, }, { date: 1462802160000, open: 50.49, high: 50.51, low: 50.48, close: 50.495, volume: 42529, }, { date: 1462802220000, open: 50.49, high: 50.51, low: 50.48, close: 50.5, volume: 24254, }, { date: 1462802280000, open: 50.5, high: 50.52, low: 50.49, close: 50.52, volume: 35331, }, { date: 1462802340000, open: 50.52, high: 50.52, low: 50.4728, close: 50.49, volume: 45259, }, { date: 1462802400000, open: 50.48, high: 50.56, low: 50.48, close: 50.559, volume: 63099, }, { date: 1462802460000, open: 50.56, high: 50.56, low: 50.4875, close: 50.4875, volume: 47565, }, { date: 1462802520000, open: 50.49, high: 50.5699, low: 50.485, close: 50.545, volume: 42054, }, { date: 1462802580000, open: 50.55, high: 50.585, low: 50.5, close: 50.515, volume: 92604, }, { date: 1462802640000, open: 50.515, high: 50.54, low: 50.5, close: 50.53, volume: 39381, }, { date: 1462802700000, open: 50.53, high: 50.53, low: 50.485, close: 50.5, volume: 29286, }, { date: 1462802760000, open: 50.5, high: 50.53, low: 50.5, close: 50.53, volume: 23414, }, { date: 1462802820000, open: 50.53, high: 50.555, low: 50.52, close: 50.54, volume: 29356, }, { date: 1462802880000, open: 50.5406, high: 50.5406, low: 50.51, close: 50.5235, volume: 30497, }, { date: 1462802940000, open: 50.5296, high: 50.55, low: 50.52, close: 50.55, volume: 26241, }, { date: 1462803000000, open: 50.55, high: 50.565, low: 50.5363, close: 50.54, volume: 42502, }, { date: 1462803060000, open: 50.53, high: 50.54, low: 50.491, close: 50.495, volume: 47817, }, { date: 1462803120000, open: 50.49, high: 50.51, low: 50.47, close: 50.5, volume: 48586, }, { date: 1462803180000, open: 50.51, high: 50.535, low: 50.48, close: 50.48, volume: 35381, }, { date: 1462803240000, open: 50.4899, high: 50.49, low: 50.45, close: 50.46, volume: 44840, }, { date: 1462803300000, open: 50.46, high: 50.47, low: 50.45, close: 50.45, volume: 73753, }, { date: 1462803360000, open: 50.453, high: 50.48, low: 50.444, close: 50.46, volume: 51535, }, { date: 1462803420000, open: 50.45, high: 50.465, low: 50.42, close: 50.435, volume: 51905, }, { date: 1462803480000, open: 50.44, high: 50.47, low: 50.43, close: 50.45, volume: 18834, }, { date: 1462803540000, open: 50.45, high: 50.45, low: 50.405, close: 50.41, volume: 28096, }, { date: 1462803600000, open: 50.4, high: 50.43, low: 50.4, close: 50.415, volume: 52311, }, { date: 1462803660000, open: 50.415, high: 50.445, low: 50.4, close: 50.43, volume: 86203, }, { date: 1462803720000, open: 50.43, high: 50.45, low: 50.4, close: 50.4, volume: 52002, }, { date: 1462803780000, open: 50.4065, high: 50.42, low: 50.39, close: 50.4, volume: 32427, }, { date: 1462803840000, open: 50.4, high: 50.4, low: 50.36, close: 50.37, volume: 49522, }, { date: 1462803900000, open: 50.37, high: 50.38, low: 50.32, close: 50.32, volume: 65789, }, { date: 1462803960000, open: 50.325, high: 50.37, low: 50.3, close: 50.34, volume: 139339, }, { date: 1462804020000, open: 50.335, high: 50.35, low: 50.29, close: 50.305, volume: 52789, }, { date: 1462804080000, open: 50.31, high: 50.3138, low: 50.295, close: 50.31, volume: 33644, }, { date: 1462804140000, open: 50.3062, high: 50.3062, low: 50.25, close: 50.27, volume: 41646, }, { date: 1462804200000, open: 50.2655, high: 50.29, low: 50.25, close: 50.27, volume: 28012, }, { date: 1462804260000, open: 50.27, high: 50.3, low: 50.245, close: 50.27, volume: 57404, }, { date: 1462804320000, open: 50.27, high: 50.31, low: 50.269, close: 50.28, volume: 52753, }, { date: 1462804380000, open: 50.27, high: 50.29, low: 50.245, close: 50.255, volume: 46937, }, { date: 1462804440000, open: 50.26, high: 50.27, low: 50.245, close: 50.25, volume: 53176, }, { date: 1462804500000, open: 50.2501, high: 50.26, low: 50.24, close: 50.25, volume: 38831, }, { date: 1462804560000, open: 50.25, high: 50.26, low: 50.23, close: 50.24, volume: 30255, }, { date: 1462804620000, open: 50.24, high: 50.25, low: 50.22, close: 50.25, volume: 35470, }, { date: 1462804680000, open: 50.245, high: 50.25, low: 50.205, close: 50.205, volume: 32976, }, { date: 1462804740000, open: 50.21, high: 50.21, low: 50.15, close: 50.18, volume: 50367, }, { date: 1462804800000, open: 50.18, high: 50.19, low: 50.1701, close: 50.18, volume: 30070, }, { date: 1462804860000, open: 50.1701, high: 50.185, low: 50.16, close: 50.174, volume: 34189, }, { date: 1462804920000, open: 50.17, high: 50.19, low: 50.165, close: 50.18, volume: 41448, }, { date: 1462804980000, open: 50.185, high: 50.2, low: 50.17, close: 50.195, volume: 31115, }, { date: 1462805040000, open: 50.1999, high: 50.2556, low: 50.195, close: 50.225, volume: 53015, }, { date: 1462805100000, open: 50.23, high: 50.23, low: 50.15, close: 50.17, volume: 55536, }, { date: 1462805160000, open: 50.17, high: 50.18, low: 50.14, close: 50.14, volume: 46703, }, { date: 1462805220000, open: 50.1422, high: 50.17, low: 50.1364, close: 50.15, volume: 40326, }, { date: 1462805280000, open: 50.15, high: 50.21, low: 50.15, close: 50.2, volume: 22032, }, { date: 1462805340000, open: 50.19, high: 50.2, low: 50.16, close: 50.169, volume: 42096, }, { date: 1462805400000, open: 50.16, high: 50.2, low: 50.16, close: 50.19, volume: 34463, }, { date: 1462805460000, open: 50.185, high: 50.185, low: 50.17, close: 50.17, volume: 26725, }, { date: 1462805520000, open: 50.17, high: 50.18, low: 50.14, close: 50.14, volume: 53171, }, { date: 1462805580000, open: 50.1435, high: 50.17, low: 50.14, close: 50.155, volume: 16231, }, { date: 1462805640000, open: 50.16, high: 50.17, low: 50.12, close: 50.14, volume: 33912, }, { date: 1462805700000, open: 50.1443, high: 50.17, low: 50.125, close: 50.125, volume: 44264, }, { date: 1462805760000, open: 50.1375, high: 50.155, low: 50.13, close: 50.145, volume: 30255, }, { date: 1462805820000, open: 50.14, high: 50.16, low: 50.1201, close: 50.15, volume: 60770, }, { date: 1462805880000, open: 50.15, high: 50.15, low: 50.13, close: 50.13, volume: 18773, }, { date: 1462805940000, open: 50.1365, high: 50.15, low: 50.12, close: 50.13, volume: 29964, }, { date: 1462806000000, open: 50.125, high: 50.14, low: 50.08, close: 50.11, volume: 62237, }, { date: 1462806060000, open: 50.1036, high: 50.11, low: 50.08, close: 50.105, volume: 48449, }, { date: 1462806120000, open: 50.1015, high: 50.11, low: 50.08, close: 50.1, volume: 47693, }, { date: 1462806180000, open: 50.1, high: 50.16, low: 50.095, close: 50.1323, volume: 45249, }, { date: 1462806240000, open: 50.14, high: 50.16, low: 50.13, close: 50.16, volume: 18646, }, { date: 1462806300000, open: 50.16, high: 50.17, low: 50.12, close: 50.13, volume: 62803, }, { date: 1462806360000, open: 50.1201, high: 50.15, low: 50.11, close: 50.125, volume: 36299, }, { date: 1462806420000, open: 50.12, high: 50.15, low: 50.11, close: 50.1101, volume: 36233, }, { date: 1462806480000, open: 50.11, high: 50.15, low: 50.1, close: 50.15, volume: 64580, }, { date: 1462806540000, open: 50.15, high: 50.19, low: 50.15, close: 50.15, volume: 55459, }, { date: 1462806600000, open: 50.15, high: 50.18, low: 50.14, close: 50.15, volume: 33381, }, { date: 1462806660000, open: 50.1466, high: 50.16, low: 50.12, close: 50.12, volume: 34237, }, { date: 1462806720000, open: 50.12, high: 50.15, low: 50.085, close: 50.085, volume: 55378, }, { date: 1462806780000, open: 50.09, high: 50.1, low: 50.05, close: 50.06, volume: 34456, }, { date: 1462806840000, open: 50.06, high: 50.065, low: 50, close: 50.01, volume: 140160, }, { date: 1462806900000, open: 50.02, high: 50.03, low: 50.01, close: 50.01, volume: 28692, }, { date: 1462806960000, open: 50.015, high: 50.03, low: 50, close: 50.03, volume: 30949, }, { date: 1462807020000, open: 50.04, high: 50.04, low: 50.01, close: 50.04, volume: 33984, }, { date: 1462807080000, open: 50.035, high: 50.05, low: 50.03, close: 50.035, volume: 23169, }, { date: 1462807140000, open: 50.03, high: 50.04, low: 50.02, close: 50.035, volume: 35859, }, { date: 1462807200000, open: 50.0365, high: 50.07, low: 50.0362, close: 50.065, volume: 36950, }, { date: 1462807260000, open: 50.06, high: 50.11, low: 50.06, close: 50.11, volume: 44519, }, { date: 1462807320000, open: 50.11, high: 50.11, low: 50.09, close: 50.11, volume: 45582, }, { date: 1462807380000, open: 50.11, high: 50.12, low: 50.09, close: 50.1, volume: 39189, }, { date: 1462807440000, open: 50.0999, high: 50.12, low: 50.09, close: 50.115, volume: 9395, }, { date: 1462807500000, open: 50.11, high: 50.15, low: 50.11, close: 50.149, volume: 45821, }, { date: 1462807560000, open: 50.14, high: 50.15, low: 50.13, close: 50.13, volume: 33776, }, { date: 1462807620000, open: 50.13, high: 50.17, low: 50.13, close: 50.17, volume: 26763, }, { date: 1462807680000, open: 50.17, high: 50.18, low: 50.16, close: 50.1622, volume: 39611, }, { date: 1462807740000, open: 50.165, high: 50.19, low: 50.16, close: 50.19, volume: 46253, }, { date: 1462807800000, open: 50.185, high: 50.19, low: 50.17, close: 50.175, volume: 62986, }, { date: 1462807860000, open: 50.175, high: 50.18, low: 50.16, close: 50.16, volume: 40658, }, { date: 1462807920000, open: 50.165, high: 50.17, low: 50.16, close: 50.165, volume: 17397, }, { date: 1462807980000, open: 50.17, high: 50.18, low: 50.16, close: 50.16, volume: 37022, }, { date: 1462808040000, open: 50.16, high: 50.18, low: 50.14, close: 50.14, volume: 49178, }, { date: 1462808100000, open: 50.145, high: 50.185, low: 50.131, close: 50.185, volume: 51915, }, { date: 1462808160000, open: 50.185, high: 50.2, low: 50.17, close: 50.2, volume: 23338, }, { date: 1462808220000, open: 50.2, high: 50.22, low: 50.2, close: 50.22, volume: 17517, }, { date: 1462808280000, open: 50.215, high: 50.218, low: 50.191, close: 50.195, volume: 32422, }, { date: 1462808340000, open: 50.19, high: 50.25, low: 50.19, close: 50.24, volume: 34453, }, { date: 1462808400000, open: 50.245, high: 50.25, low: 50.23, close: 50.25, volume: 15006, }, { date: 1462808460000, open: 50.25, high: 50.26, low: 50.23, close: 50.25, volume: 61230, }, { date: 1462808520000, open: 50.245, high: 50.26, low: 50.22, close: 50.24, volume: 30872, }, { date: 1462808580000, open: 50.25, high: 50.27, low: 50.24, close: 50.245, volume: 49572, }, { date: 1462808640000, open: 50.2475, high: 50.25, low: 50.24, close: 50.245, volume: 12201, }, { date: 1462808700000, open: 50.25, high: 50.29, low: 50.245, close: 50.2899, volume: 25287, }, { date: 1462808760000, open: 50.29, high: 50.31, low: 50.28, close: 50.3, volume: 24218, }, { date: 1462808820000, open: 50.305, high: 50.33, low: 50.305, close: 50.33, volume: 46281, }, { date: 1462808880000, open: 50.325, high: 50.34, low: 50.313, close: 50.33, volume: 57446, }, { date: 1462808940000, open: 50.33, high: 50.34, low: 50.32, close: 50.34, volume: 20739, }, { date: 1462809000000, open: 50.3328, high: 50.34, low: 50.32, close: 50.32, volume: 25937, }, { date: 1462809060000, open: 50.32, high: 50.33, low: 50.31, close: 50.33, volume: 26459, }, { date: 1462809120000, open: 50.3239, high: 50.345, low: 50.3239, close: 50.34, volume: 42581, }, { date: 1462809180000, open: 50.34, high: 50.34, low: 50.3239, close: 50.335, volume: 13971, }, { date: 1462809240000, open: 50.335, high: 50.34, low: 50.31, close: 50.3102, volume: 34317, }, { date: 1462809300000, open: 50.31, high: 50.3165, low: 50.2501, close: 50.255, volume: 39249, }, { date: 1462809360000, open: 50.26, high: 50.26, low: 50.23, close: 50.2334, volume: 15209, }, { date: 1462809420000, open: 50.23, high: 50.25, low: 50.23, close: 50.23, volume: 25570, }, { date: 1462809480000, open: 50.2365, high: 50.245, low: 50.22, close: 50.23, volume: 23800, }, { date: 1462809540000, open: 50.24, high: 50.245, low: 50.23, close: 50.23, volume: 11506, }, { date: 1462809600000, open: 50.23, high: 50.245, low: 50.22, close: 50.22, volume: 37831, }, { date: 1462809660000, open: 50.21, high: 50.24, low: 50.1901, close: 50.2369, volume: 26341, }, { date: 1462809720000, open: 50.235, high: 50.25, low: 50.22, close: 50.22, volume: 23909, }, { date: 1462809780000, open: 50.2255, high: 50.24, low: 50.2, close: 50.2, volume: 33018, }, { date: 1462809840000, open: 50.205, high: 50.2055, low: 50.18, close: 50.185, volume: 16093, }, { date: 1462809900000, open: 50.2, high: 50.21, low: 50.175, close: 50.2, volume: 34403, }, { date: 1462809960000, open: 50.209, high: 50.24, low: 50.209, close: 50.23, volume: 25289, }, { date: 1462810020000, open: 50.23, high: 50.26, low: 50.225, close: 50.23, volume: 43924, }, { date: 1462810080000, open: 50.23, high: 50.23, low: 50.195, close: 50.21, volume: 21493, }, { date: 1462810140000, open: 50.215, high: 50.23, low: 50.21, close: 50.215, volume: 79888, }, { date: 1462810200000, open: 50.21, high: 50.22, low: 50.17, close: 50.185, volume: 54210, }, { date: 1462810260000, open: 50.18, high: 50.19, low: 50.16, close: 50.19, volume: 78090, }, { date: 1462810320000, open: 50.1939, high: 50.195, low: 50.17, close: 50.18, volume: 62626, }, { date: 1462810380000, open: 50.18, high: 50.1865, low: 50.17, close: 50.18, volume: 24091, }, { date: 1462810440000, open: 50.185, high: 50.22, low: 50.185, close: 50.219, volume: 28809, }, { date: 1462810500000, open: 50.215, high: 50.23, low: 50.21, close: 50.23, volume: 16443, }, { date: 1462810560000, open: 50.23, high: 50.23, low: 50.22, close: 50.2225, volume: 10745, }, { date: 1462810620000, open: 50.225, high: 50.25, low: 50.22, close: 50.25, volume: 37532, }, { date: 1462810680000, open: 50.245, high: 50.26, low: 50.23, close: 50.2399, volume: 75764, }, { date: 1462810740000, open: 50.24, high: 50.26, low: 50.235, close: 50.25, volume: 13311, }, { date: 1462810800000, open: 50.25, high: 50.26, low: 50.24, close: 50.25, volume: 40787, }, { date: 1462810860000, open: 50.25, high: 50.25, low: 50.23, close: 50.245, volume: 21223, }, { date: 1462810920000, open: 50.24, high: 50.29, low: 50.24, close: 50.285, volume: 28423, }, { date: 1462810980000, open: 50.29, high: 50.31, low: 50.265, close: 50.27, volume: 43054, }, { date: 1462811040000, open: 50.26, high: 50.28, low: 50.25, close: 50.275, volume: 12423, }, { date: 1462811100000, open: 50.275, high: 50.29, low: 50.265, close: 50.279, volume: 20163, }, { date: 1462811160000, open: 50.28, high: 50.29, low: 50.255, close: 50.26, volume: 16347, }, { date: 1462811220000, open: 50.26, high: 50.29, low: 50.255, close: 50.28, volume: 21438, }, { date: 1462811280000, open: 50.285, high: 50.29, low: 50.26, close: 50.275, volume: 9384, }, { date: 1462811340000, open: 50.28, high: 50.29, low: 50.26, close: 50.29, volume: 13710, }, { date: 1462811400000, open: 50.29, high: 50.29, low: 50.24, close: 50.26, volume: 47295, }, { date: 1462811460000, open: 50.25, high: 50.26, low: 50.24, close: 50.24, volume: 11596, }, { date: 1462811520000, open: 50.249, high: 50.255, low: 50.23, close: 50.23, volume: 16093, }, { date: 1462811580000, open: 50.24, high: 50.2465, low: 50.235, close: 50.24, volume: 11040, }, { date: 1462811640000, open: 50.2399, high: 50.26, low: 50.2399, close: 50.25, volume: 6065, }, { date: 1462811700000, open: 50.25, high: 50.27, low: 50.25, close: 50.26, volume: 17345, }, { date: 1462811760000, open: 50.27, high: 50.27, low: 50.23, close: 50.231, volume: 9914, }, { date: 1462811820000, open: 50.24, high: 50.25, low: 50.2, close: 50.2, volume: 31098, }, { date: 1462811880000, open: 50.21, high: 50.22, low: 50.205, close: 50.21, volume: 6640, }, { date: 1462811940000, open: 50.21, high: 50.211, low: 50.2, close: 50.2035, volume: 12735, }, { date: 1462812000000, open: 50.2, high: 50.22, low: 50.2, close: 50.21, volume: 12735, }, { date: 1462812060000, open: 50.2, high: 50.22, low: 50.19, close: 50.21, volume: 8863, }, { date: 1462812120000, open: 50.21, high: 50.22, low: 50.2, close: 50.2, volume: 12138, }, { date: 1462812180000, open: 50.21, high: 50.24, low: 50.21, close: 50.236, volume: 10114, }, { date: 1462812240000, open: 50.2341, high: 50.25, low: 50.215, close: 50.215, volume: 16384, }, { date: 1462812300000, open: 50.214, high: 50.22, low: 50.2, close: 50.21, volume: 14400, }, { date: 1462812360000, open: 50.21, high: 50.22, low: 50.19, close: 50.19, volume: 12670, }, { date: 1462812420000, open: 50.195, high: 50.23, low: 50.195, close: 50.225, volume: 14106, }, { date: 1462812480000, open: 50.22, high: 50.22, low: 50.19, close: 50.22, volume: 21086, }, { date: 1462812540000, open: 50.215, high: 50.23, low: 50.21, close: 50.21, volume: 13657, }, { date: 1462812600000, open: 50.21, high: 50.235, low: 50.205, close: 50.22, volume: 18041, }, { date: 1462812660000, open: 50.22, high: 50.22, low: 50.185, close: 50.19, volume: 25797, }, { date: 1462812720000, open: 50.2, high: 50.21, low: 50.19, close: 50.1999, volume: 6407, }, { date: 1462812780000, open: 50.195, high: 50.21, low: 50.19, close: 50.19, volume: 17833, }, { date: 1462812840000, open: 50.19, high: 50.2, low: 50.1825, close: 50.185, volume: 16202, }, { date: 1462812900000, open: 50.19, high: 50.21, low: 50.19, close: 50.1929, volume: 15523, }, { date: 1462812960000, open: 50.19, high: 50.2, low: 50.18, close: 50.1965, volume: 14562, }, { date: 1462813020000, open: 50.2, high: 50.21, low: 50.2, close: 50.2, volume: 5664, }, { date: 1462813080000, open: 50.21, high: 50.23, low: 50.2, close: 50.2, volume: 41173, }, { date: 1462813140000, open: 50.2, high: 50.21, low: 50.19, close: 50.19, volume: 6656, }, { date: 1462813200000, open: 50.19, high: 50.21, low: 50.19, close: 50.195, volume: 29578, }, { date: 1462813260000, open: 50.19, high: 50.19, low: 50.17, close: 50.175, volume: 8274, }, { date: 1462813320000, open: 50.18, high: 50.2, low: 50.18, close: 50.2, volume: 10472, }, { date: 1462813380000, open: 50.1925, high: 50.2, low: 50.18, close: 50.185, volume: 15411, }, { date: 1462813440000, open: 50.19, high: 50.2, low: 50.18, close: 50.1999, volume: 20731, }, { date: 1462813500000, open: 50.2, high: 50.205, low: 50.19, close: 50.2, volume: 18982, }, { date: 1462813560000, open: 50.2038, high: 50.2099, low: 50.18, close: 50.19, volume: 21553, }, { date: 1462813620000, open: 50.19, high: 50.2, low: 50.1825, close: 50.2, volume: 10529, }, { date: 1462813680000, open: 50.195, high: 50.21, low: 50.19, close: 50.195, volume: 27138, }, { date: 1462813740000, open: 50.2, high: 50.205, low: 50.18, close: 50.185, volume: 55821, }, { date: 1462813800000, open: 50.185, high: 50.195, low: 50.18, close: 50.185, volume: 21854, }, { date: 1462813860000, open: 50.19, high: 50.2, low: 50.17, close: 50.195, volume: 44912, }, { date: 1462813920000, open: 50.195, high: 50.21, low: 50.19, close: 50.21, volume: 15037, }, { date: 1462813980000, open: 50.21, high: 50.25, low: 50.21, close: 50.2497, volume: 19634, }, { date: 1462814040000, open: 50.25, high: 50.25, low: 50.21, close: 50.21, volume: 35335, }, { date: 1462814100000, open: 50.21, high: 50.25, low: 50.2, close: 50.22, volume: 27614, }, { date: 1462814160000, open: 50.22, high: 50.25, low: 50.21, close: 50.25, volume: 10778, }, { date: 1462814220000, open: 50.25, high: 50.25, low: 50.21, close: 50.23, volume: 33420, }, { date: 1462814280000, open: 50.23, high: 50.24, low: 50.22, close: 50.22, volume: 11080, }, { date: 1462814340000, open: 50.22, high: 50.2371, low: 50.21, close: 50.2371, volume: 20795, }, { date: 1462814400000, open: 50.24, high: 50.25, low: 50.22, close: 50.23, volume: 33223, }, { date: 1462814460000, open: 50.23, high: 50.2386, low: 50.19, close: 50.205, volume: 120992, }, { date: 1462814520000, open: 50.2038, high: 50.21, low: 50.17, close: 50.1801, volume: 16992, }, { date: 1462814580000, open: 50.19, high: 50.21, low: 50.19, close: 50.19, volume: 12490, }, { date: 1462814640000, open: 50.1895, high: 50.19, low: 50.18, close: 50.18, volume: 21472, }, { date: 1462814700000, open: 50.17, high: 50.18, low: 50.1525, close: 50.155, volume: 30398, }, { date: 1462814760000, open: 50.15, high: 50.185, low: 50.15, close: 50.18, volume: 13929, }, { date: 1462814820000, open: 50.18, high: 50.18, low: 50.17, close: 50.18, volume: 11681, }, { date: 1462814880000, open: 50.18, high: 50.19, low: 50.17, close: 50.18, volume: 25213, }, { date: 1462814940000, open: 50.185, high: 50.19, low: 50.18, close: 50.185, volume: 3011, }, { date: 1462815000000, open: 50.1801, high: 50.2, low: 50.18, close: 50.2, volume: 12455, }, { date: 1462815060000, open: 50.2, high: 50.22, low: 50.19, close: 50.22, volume: 47959, }, { date: 1462815120000, open: 50.22, high: 50.24, low: 50.2125, close: 50.24, volume: 8731, }, { date: 1462815180000, open: 50.24, high: 50.24, low: 50.21, close: 50.22, volume: 23471, }, { date: 1462815240000, open: 50.22, high: 50.225, low: 50.2, close: 50.225, volume: 12473, }, { date: 1462815300000, open: 50.22, high: 50.225, low: 50.2, close: 50.21, volume: 19823, }, { date: 1462815360000, open: 50.21, high: 50.231, low: 50.21, close: 50.23, volume: 15396, }, { date: 1462815420000, open: 50.23, high: 50.24, low: 50.22, close: 50.2201, volume: 13023, }, { date: 1462815480000, open: 50.22, high: 50.2256, low: 50.2, close: 50.2035, volume: 10413, }, { date: 1462815540000, open: 50.2, high: 50.22, low: 50.18, close: 50.203, volume: 21833, }, { date: 1462815600000, open: 50.2, high: 50.2, low: 50.155, close: 50.18, volume: 18819, }, { date: 1462815660000, open: 50.1899, high: 50.2, low: 50.1801, close: 50.2, volume: 9635, }, { date: 1462815720000, open: 50.2, high: 50.2, low: 50.175, close: 50.18, volume: 29953, }, { date: 1462815780000, open: 50.18, high: 50.19, low: 50.18, close: 50.19, volume: 10349, }, { date: 1462815840000, open: 50.1984, high: 50.22, low: 50.195, close: 50.2199, volume: 8027, }, { date: 1462815900000, open: 50.21, high: 50.22, low: 50.2, close: 50.2, volume: 12478, }, { date: 1462815960000, open: 50.204, high: 50.21, low: 50.18, close: 50.19, volume: 21358, }, { date: 1462816020000, open: 50.2, high: 50.22, low: 50.19, close: 50.22, volume: 10537, }, { date: 1462816080000, open: 50.22, high: 50.23, low: 50.203, close: 50.21, volume: 16403, }, { date: 1462816140000, open: 50.21, high: 50.21, low: 50.2, close: 50.2, volume: 4499, }, { date: 1462816200000, open: 50.2, high: 50.23, low: 50.19, close: 50.22, volume: 48077, }, { date: 1462816260000, open: 50.22, high: 50.23, low: 50.21, close: 50.215, volume: 14658, }, { date: 1462816320000, open: 50.21, high: 50.2265, low: 50.2, close: 50.21, volume: 17153, }, { date: 1462816380000, open: 50.215, high: 50.23, low: 50.21, close: 50.2139, volume: 8603, }, { date: 1462816440000, open: 50.21, high: 50.21, low: 50.195, close: 50.2068, volume: 36870, }, { date: 1462816500000, open: 50.2, high: 50.21, low: 50.195, close: 50.2, volume: 36591, }, { date: 1462816560000, open: 50.2, high: 50.2, low: 50.16, close: 50.16, volume: 20438, }, { date: 1462816620000, open: 50.16, high: 50.178, low: 50.16, close: 50.17, volume: 27944, }, { date: 1462816680000, open: 50.16, high: 50.17, low: 50.15, close: 50.157, volume: 32668, }, { date: 1462816740000, open: 50.16, high: 50.16, low: 50.15, close: 50.15, volume: 20502, }, { date: 1462816800000, open: 50.16, high: 50.16, low: 50.15, close: 50.1559, volume: 16161, }, { date: 1462816860000, open: 50.1538, high: 50.2, low: 50.15, close: 50.1801, volume: 48700, }, { date: 1462816920000, open: 50.19, high: 50.19, low: 50.17, close: 50.19, volume: 11268, }, { date: 1462816980000, open: 50.181, high: 50.19, low: 50.18, close: 50.184, volume: 8114, }, { date: 1462817040000, open: 50.19, high: 50.19, low: 50.16, close: 50.165, volume: 32682, }, { date: 1462817100000, open: 50.17, high: 50.179, low: 50.1569, close: 50.16, volume: 15476, }, { date: 1462817160000, open: 50.17, high: 50.17, low: 50.145, close: 50.151, volume: 47463, }, { date: 1462817220000, open: 50.16, high: 50.17, low: 50.1339, close: 50.15, volume: 30266, }, { date: 1462817280000, open: 50.15, high: 50.16, low: 50.12, close: 50.15, volume: 37390, }, { date: 1462817340000, open: 50.15, high: 50.155, low: 50.11, close: 50.125, volume: 30095, }, { date: 1462817400000, open: 50.13, high: 50.155, low: 50.125, close: 50.15, volume: 25665, }, { date: 1462817460000, open: 50.14, high: 50.145, low: 50.1, close: 50.103, volume: 102003, }, { date: 1462817520000, open: 50.11, high: 50.119, low: 50.09, close: 50.1011, volume: 28249, }, { date: 1462817580000, open: 50.1, high: 50.115, low: 50.09, close: 50.107, volume: 33701, }, { date: 1462817640000, open: 50.11, high: 50.135, low: 50.1, close: 50.135, volume: 31307, }, { date: 1462817700000, open: 50.13, high: 50.135, low: 50.12, close: 50.12, volume: 19125, }, { date: 1462817760000, open: 50.1299, high: 50.13, low: 50.1, close: 50.11, volume: 41294, }, { date: 1462817820000, open: 50.1, high: 50.11, low: 50.0684, close: 50.0839, volume: 46052, }, { date: 1462817880000, open: 50.08, high: 50.09, low: 50.08, close: 50.0841, volume: 5803, }, { date: 1462817940000, open: 50.09, high: 50.11, low: 50.082, close: 50.11, volume: 24551, }, { date: 1462818000000, open: 50.11, high: 50.13, low: 50.1, close: 50.12, volume: 24514, }, { date: 1462818060000, open: 50.125, high: 50.125, low: 50.11, close: 50.12, volume: 13233, }, { date: 1462818120000, open: 50.12, high: 50.15, low: 50.12, close: 50.14, volume: 29266, }, { date: 1462818180000, open: 50.14, high: 50.15, low: 50.13, close: 50.15, volume: 10644, }, { date: 1462818240000, open: 50.145, high: 50.15, low: 50.14, close: 50.145, volume: 17487, }, { date: 1462818300000, open: 50.14, high: 50.14, low: 50.12, close: 50.135, volume: 45880, }, { date: 1462818360000, open: 50.135, high: 50.16, low: 50.13, close: 50.145, volume: 16731, }, { date: 1462818420000, open: 50.15, high: 50.17, low: 50.145, close: 50.17, volume: 11674, }, { date: 1462818480000, open: 50.165, high: 50.175, low: 50.16, close: 50.17, volume: 15846, }, { date: 1462818540000, open: 50.1665, high: 50.175, low: 50.14, close: 50.15, volume: 37426, }, { date: 1462818600000, open: 50.153, high: 50.17, low: 50.15, close: 50.1636, volume: 21379, }, { date: 1462818660000, open: 50.16, high: 50.19, low: 50.16, close: 50.17, volume: 39598, }, { date: 1462818720000, open: 50.17, high: 50.18, low: 50.1605, close: 50.18, volume: 16904, }, { date: 1462818780000, open: 50.175, high: 50.1799, low: 50.16, close: 50.1672, volume: 22340, }, { date: 1462818840000, open: 50.162, high: 50.19, low: 50.16, close: 50.1841, volume: 19267, }, { date: 1462818900000, open: 50.187, high: 50.19, low: 50.16, close: 50.16, volume: 18647, }, { date: 1462818960000, open: 50.16, high: 50.22, low: 50.16, close: 50.19, volume: 148490, }, { date: 1462819020000, open: 50.2, high: 50.2, low: 50.175, close: 50.2, volume: 20827, }, { date: 1462819080000, open: 50.2, high: 50.205, low: 50.19, close: 50.205, volume: 11610, }, { date: 1462819140000, open: 50.205, high: 50.23, low: 50.2, close: 50.21, volume: 20157, }, { date: 1462819200000, open: 50.21, high: 50.23, low: 50.205, close: 50.21, volume: 21672, }, { date: 1462819260000, open: 50.21, high: 50.22, low: 50.1901, close: 50.21, volume: 18191, }, { date: 1462819320000, open: 50.219, high: 50.23, low: 50.21, close: 50.225, volume: 8274, }, { date: 1462819380000, open: 50.22, high: 50.225, low: 50.205, close: 50.21, volume: 55640, }, { date: 1462819440000, open: 50.21, high: 50.22, low: 50.21, close: 50.21, volume: 14738, }, { date: 1462819500000, open: 50.21, high: 50.225, low: 50.21, close: 50.2201, volume: 15155, }, { date: 1462819560000, open: 50.225, high: 50.2399, low: 50.225, close: 50.2299, volume: 14564, }, { date: 1462819620000, open: 50.23, high: 50.24, low: 50.225, close: 50.235, volume: 9300, }, { date: 1462819680000, open: 50.235, high: 50.235, low: 50.22, close: 50.2265, volume: 10891, }, { date: 1462819740000, open: 50.23, high: 50.24, low: 50.2235, close: 50.23, volume: 14093, }, { date: 1462819800000, open: 50.23, high: 50.24, low: 50.22, close: 50.2241, volume: 27996, }, { date: 1462819860000, open: 50.23, high: 50.27, low: 50.22, close: 50.251, volume: 28463, }, { date: 1462819920000, open: 50.25, high: 50.29, low: 50.24, close: 50.285, volume: 38923, }, { date: 1462819980000, open: 50.285, high: 50.3, low: 50.26, close: 50.27, volume: 24015, }, { date: 1462820040000, open: 50.27, high: 50.27, low: 50.24, close: 50.26, volume: 38577, }, { date: 1462820100000, open: 50.2638, high: 50.28, low: 50.2638, close: 50.27, volume: 12102, }, { date: 1462820160000, open: 50.27, high: 50.3, low: 50.27, close: 50.2799, volume: 45255, }, { date: 1462820220000, open: 50.28, high: 50.3, low: 50.28, close: 50.3, volume: 22203, }, { date: 1462820280000, open: 50.295, high: 50.297, low: 50.279, close: 50.29, volume: 28511, }, { date: 1462820340000, open: 50.2861, high: 50.3, low: 50.285, close: 50.3, volume: 23349, }, { date: 1462820400000, open: 50.3, high: 50.305, low: 50.28, close: 50.29, volume: 44903, }, { date: 1462820460000, open: 50.3, high: 50.3, low: 50.29, close: 50.2972, volume: 7334, }, { date: 1462820520000, open: 50.2966, high: 50.2966, low: 50.26, close: 50.269, volume: 35491, }, { date: 1462820580000, open: 50.2699, high: 50.27, low: 50.25, close: 50.26, volume: 29714, }, { date: 1462820640000, open: 50.26, high: 50.2799, low: 50.25, close: 50.25, volume: 23658, }, { date: 1462820700000, open: 50.26, high: 50.2667, low: 50.235, close: 50.245, volume: 43387, }, { date: 1462820760000, open: 50.245, high: 50.245, low: 50.22, close: 50.225, volume: 17617, }, { date: 1462820820000, open: 50.2239, high: 50.23, low: 50.2, close: 50.205, volume: 24717, }, { date: 1462820880000, open: 50.205, high: 50.229, low: 50.2, close: 50.2067, volume: 32308, }, { date: 1462820940000, open: 50.205, high: 50.21, low: 50.175, close: 50.18, volume: 41011, }, { date: 1462821000000, open: 50.18, high: 50.2, low: 50.17, close: 50.185, volume: 39722, }, { date: 1462821060000, open: 50.19, high: 50.2, low: 50.18, close: 50.1865, volume: 32239, }, { date: 1462821120000, open: 50.195, high: 50.21, low: 50.185, close: 50.21, volume: 15385, }, { date: 1462821180000, open: 50.205, high: 50.23, low: 50.2001, close: 50.2201, volume: 13856, }, { date: 1462821240000, open: 50.229, high: 50.23, low: 50.205, close: 50.21, volume: 29122, }, { date: 1462821300000, open: 50.22, high: 50.225, low: 50.19, close: 50.2, volume: 30063, }, { date: 1462821360000, open: 50.21, high: 50.215, low: 50.18, close: 50.186, volume: 59471, }, { date: 1462821420000, open: 50.19, high: 50.215, low: 50.18, close: 50.21, volume: 25830, }, { date: 1462821480000, open: 50.21, high: 50.21, low: 50.18, close: 50.18, volume: 31859, }, { date: 1462821540000, open: 50.19, high: 50.2, low: 50.185, close: 50.195, volume: 20103, }, { date: 1462821600000, open: 50.2, high: 50.21, low: 50.186, close: 50.2, volume: 31486, }, { date: 1462821660000, open: 50.199, high: 50.22, low: 50.19, close: 50.215, volume: 43336, }, { date: 1462821720000, open: 50.2121, high: 50.215, low: 50.19, close: 50.215, volume: 41940, }, { date: 1462821780000, open: 50.212, high: 50.23, low: 50.21, close: 50.23, volume: 34273, }, { date: 1462821840000, open: 50.23, high: 50.24, low: 50.224, close: 50.23, volume: 20454, }, { date: 1462821900000, open: 50.2399, high: 50.245, low: 50.23, close: 50.24, volume: 32701, }, { date: 1462821960000, open: 50.23, high: 50.25, low: 50.23, close: 50.24, volume: 24572, }, { date: 1462822020000, open: 50.25, high: 50.25, low: 50.22, close: 50.225, volume: 36546, }, { date: 1462822080000, open: 50.22, high: 50.2382, low: 50.22, close: 50.22, volume: 33234, }, { date: 1462822140000, open: 50.225, high: 50.24, low: 50.21, close: 50.2299, volume: 49938, }, { date: 1462822200000, open: 50.23, high: 50.23, low: 50.19, close: 50.205, volume: 24154, }, { date: 1462822260000, open: 50.21, high: 50.24, low: 50.19, close: 50.19, volume: 45467, }, { date: 1462822320000, open: 50.195, high: 50.205, low: 50.18, close: 50.185, volume: 31527, }, { date: 1462822380000, open: 50.18, high: 50.18, low: 50.16, close: 50.1601, volume: 28069, }, { date: 1462822440000, open: 50.16, high: 50.16, low: 50.13, close: 50.135, volume: 34898, }, { date: 1462822500000, open: 50.13, high: 50.155, low: 50.13, close: 50.15, volume: 42807, }, { date: 1462822560000, open: 50.15, high: 50.18, low: 50.15, close: 50.175, volume: 49295, }, { date: 1462822620000, open: 50.178, high: 50.18, low: 50.12, close: 50.169, volume: 104829, }, { date: 1462822680000, open: 50.1659, high: 50.18, low: 50.15, close: 50.15, volume: 26771, }, { date: 1462822740000, open: 50.15, high: 50.185, low: 50.15, close: 50.165, volume: 47885, }, { date: 1462822800000, open: 50.16, high: 50.18, low: 50.16, close: 50.16, volume: 26401, }, { date: 1462822860000, open: 50.164, high: 50.18, low: 50.16, close: 50.165, volume: 40975, }, { date: 1462822920000, open: 50.17, high: 50.18, low: 50.1686, close: 50.175, volume: 37874, }, { date: 1462822980000, open: 50.1721, high: 50.195, low: 50.17, close: 50.19, volume: 43000, }, { date: 1462823040000, open: 50.19, high: 50.19, low: 50.16, close: 50.17, volume: 48367, }, { date: 1462823100000, open: 50.1692, high: 50.175, low: 50.13, close: 50.13, volume: 72425, }, { date: 1462823160000, open: 50.135, high: 50.19, low: 50.1301, close: 50.16, volume: 72628, }, { date: 1462823220000, open: 50.16, high: 50.2, low: 50.16, close: 50.175, volume: 57663, }, { date: 1462823280000, open: 50.1701, high: 50.18, low: 50.17, close: 50.18, volume: 19370, }, { date: 1462823340000, open: 50.17, high: 50.19, low: 50.16, close: 50.185, volume: 84062, }, { date: 1462823400000, open: 50.18, high: 50.19, low: 50.14, close: 50.145, volume: 66360, }, { date: 1462823460000, open: 50.14, high: 50.15, low: 50.12, close: 50.125, volume: 47712, }, { date: 1462823520000, open: 50.12, high: 50.13, low: 50.1, close: 50.12, volume: 47713, }, { date: 1462823580000, open: 50.12, high: 50.12, low: 50.085, close: 50.085, volume: 43748, }, { date: 1462823640000, open: 50.09, high: 50.1, low: 50.08, close: 50.1, volume: 23085, }, { date: 1462823700000, open: 50.1, high: 50.11, low: 50.06, close: 50.08, volume: 85834, }, { date: 1462823760000, open: 50.075, high: 50.085, low: 50.06, close: 50.085, volume: 90944, }, { date: 1462823820000, open: 50.075, high: 50.085, low: 50.07, close: 50.075, volume: 82769, }, { date: 1462823880000, open: 50.075, high: 50.08, low: 50.06, close: 50.07, volume: 60284, }, { date: 1462823940000, open: 50.07, high: 50.08, low: 50.07, close: 50.075, volume: 83370, }, { date: 1462824000000, open: 50.08, high: 50.11, low: 50.07, close: 50.07, volume: 2126916, }, { date: 1462887000000, open: 50.33, high: 50.37, low: 50.3, close: 50.31, volume: 251148, }, { date: 1462887060000, open: 50.35, high: 50.48, low: 50.26, close: 50.37, volume: 127320, }, { date: 1462887120000, open: 50.36, high: 50.43, low: 50.35, close: 50.35, volume: 107909, }, { date: 1462887180000, open: 50.3599, high: 50.37, low: 50.29, close: 50.37, volume: 68303, }, { date: 1462887240000, open: 50.3715, high: 50.43, low: 50.34, close: 50.41, volume: 47964, }, { date: 1462887300000, open: 50.4, high: 50.48, low: 50.38, close: 50.47, volume: 52319, }, { date: 1462887360000, open: 50.48, high: 50.51, low: 50.4, close: 50.42, volume: 124822, }, { date: 1462887420000, open: 50.42, high: 50.43, low: 50.36, close: 50.38, volume: 36982, }, { date: 1462887480000, open: 50.38, high: 50.41, low: 50.35, close: 50.375, volume: 61035, }, { date: 1462887540000, open: 50.3801, high: 50.3801, low: 50.26, close: 50.26, volume: 64909, }, { date: 1462887600000, open: 50.26, high: 50.27, low: 50.19, close: 50.22, volume: 93210, }, { date: 1462887660000, open: 50.229, high: 50.28, low: 50.21, close: 50.2365, volume: 66661, }, { date: 1462887720000, open: 50.2399, high: 50.28, low: 50.22, close: 50.28, volume: 37301, }, { date: 1462887780000, open: 50.27, high: 50.27, low: 50.2162, close: 50.24, volume: 58652, }, { date: 1462887840000, open: 50.23, high: 50.37, low: 50.195, close: 50.35, volume: 96471, }, { date: 1462887900000, open: 50.34, high: 50.42, low: 50.32, close: 50.38, volume: 110458, }, { date: 1462887960000, open: 50.3793, high: 50.42, low: 50.36, close: 50.38, volume: 112330, }, { date: 1462888020000, open: 50.4, high: 50.42, low: 50.38, close: 50.38, volume: 65790, }, { date: 1462888080000, open: 50.3899, high: 50.44, low: 50.38, close: 50.43, volume: 28327, }, { date: 1462888140000, open: 50.43, high: 50.49, low: 50.43, close: 50.46, volume: 56625, }, { date: 1462888200000, open: 50.46, high: 50.49, low: 50.435, close: 50.4833, volume: 37670, }, { date: 1462888260000, open: 50.48, high: 50.54, low: 50.45, close: 50.5001, volume: 85293, }, { date: 1462888320000, open: 50.5, high: 50.54, low: 50.47, close: 50.531, volume: 41962, }, { date: 1462888380000, open: 50.535, high: 50.56, low: 50.53, close: 50.545, volume: 77431, }, { date: 1462888440000, open: 50.545, high: 50.57, low: 50.51, close: 50.53, volume: 73751, }, { date: 1462888500000, open: 50.529, high: 50.55, low: 50.52, close: 50.545, volume: 96138, }, { date: 1462888560000, open: 50.55, high: 50.55, low: 50.52, close: 50.525, volume: 41258, }, { date: 1462888620000, open: 50.53, high: 50.54, low: 50.47, close: 50.47, volume: 77159, }, { date: 1462888680000, open: 50.465, high: 50.47, low: 50.42, close: 50.42, volume: 44324, }, { date: 1462888740000, open: 50.42, high: 50.44, low: 50.41, close: 50.42, volume: 83607, }, { date: 1462888800000, open: 50.43, high: 50.43, low: 50.37, close: 50.38, volume: 77456, }, { date: 1462888860000, open: 50.38, high: 50.39, low: 50.36, close: 50.37, volume: 40612, }, { date: 1462888920000, open: 50.375, high: 50.38, low: 50.36, close: 50.375, volume: 32756, }, { date: 1462888980000, open: 50.3799, high: 50.41, low: 50.36, close: 50.41, volume: 59584, }, { date: 1462889040000, open: 50.41, high: 50.45, low: 50.41, close: 50.44, volume: 33073, }, { date: 1462889100000, open: 50.44, high: 50.44, low: 50.39, close: 50.3911, volume: 38063, }, { date: 1462889160000, open: 50.39, high: 50.4, low: 50.36, close: 50.39, volume: 33355, }, { date: 1462889220000, open: 50.39, high: 50.43, low: 50.39, close: 50.405, volume: 46611, }, { date: 1462889280000, open: 50.405, high: 50.44, low: 50.4, close: 50.42, volume: 34186, }, { date: 1462889340000, open: 50.41, high: 50.42, low: 50.3901, close: 50.41, volume: 47510, }, { date: 1462889400000, open: 50.4066, high: 50.4066, low: 50.355, close: 50.37, volume: 58201, }, { date: 1462889460000, open: 50.37, high: 50.41, low: 50.3515, close: 50.41, volume: 30347, }, { date: 1462889520000, open: 50.41, high: 50.44, low: 50.4, close: 50.4, volume: 36016, }, { date: 1462889580000, open: 50.3999, high: 50.435, low: 50.39, close: 50.435, volume: 19281, }, { date: 1462889640000, open: 50.439, high: 50.46, low: 50.43, close: 50.45, volume: 62320, }, { date: 1462889700000, open: 50.445, high: 50.45, low: 50.39, close: 50.42, volume: 89766, }, { date: 1462889760000, open: 50.42, high: 50.46, low: 50.41, close: 50.44, volume: 63563, }, { date: 1462889820000, open: 50.4401, high: 50.45, low: 50.425, close: 50.425, volume: 29961, }, { date: 1462889880000, open: 50.43, high: 50.46, low: 50.42, close: 50.43, volume: 31379, }, { date: 1462889940000, open: 50.435, high: 50.465, low: 50.4339, close: 50.465, volume: 58390, }, { date: 1462890000000, open: 50.465, high: 50.47, low: 50.42, close: 50.44, volume: 63062, }, { date: 1462890060000, open: 50.431, high: 50.44, low: 50.4, close: 50.435, volume: 35750, }, { date: 1462890120000, open: 50.4301, high: 50.47, low: 50.405, close: 50.41, volume: 58225, }, { date: 1462890180000, open: 50.415, high: 50.42, low: 50.4, close: 50.41, volume: 30472, }, { date: 1462890240000, open: 50.41, high: 50.41, low: 50.38, close: 50.4001, volume: 89048, }, { date: 1462890300000, open: 50.405, high: 50.41, low: 50.355, close: 50.38, volume: 47642, }, { date: 1462890360000, open: 50.38, high: 50.38, low: 50.355, close: 50.36, volume: 17117, }, { date: 1462890420000, open: 50.37, high: 50.3768, low: 50.3401, close: 50.37, volume: 69384, }, { date: 1462890480000, open: 50.37, high: 50.37, low: 50.35, close: 50.35, volume: 21678, }, { date: 1462890540000, open: 50.345, high: 50.36, low: 50.34, close: 50.355, volume: 24168, }, { date: 1462890600000, open: 50.355, high: 50.415, low: 50.35, close: 50.4089, volume: 46737, }, { date: 1462890660000, open: 50.4, high: 50.41, low: 50.36, close: 50.37, volume: 51920, }, { date: 1462890720000, open: 50.37, high: 50.375, low: 50.35, close: 50.355, volume: 32615, }, { date: 1462890780000, open: 50.35, high: 50.38, low: 50.35, close: 50.369, volume: 38874, }, { date: 1462890840000, open: 50.37, high: 50.4, low: 50.37, close: 50.395, volume: 31557, }, { date: 1462890900000, open: 50.3915, high: 50.42, low: 50.39, close: 50.395, volume: 24401, }, { date: 1462890960000, open: 50.395, high: 50.46, low: 50.39, close: 50.44, volume: 36039, }, { date: 1462891020000, open: 50.4401, high: 50.47, low: 50.43, close: 50.465, volume: 61161, }, { date: 1462891080000, open: 50.46, high: 50.475, low: 50.455, close: 50.465, volume: 43307, }, { date: 1462891140000, open: 50.46, high: 50.46, low: 50.44, close: 50.44, volume: 27551, }, { date: 1462891200000, open: 50.44, high: 50.47, low: 50.44, close: 50.47, volume: 9153, }, { date: 1462891260000, open: 50.465, high: 50.49, low: 50.46, close: 50.475, volume: 44824, }, { date: 1462891320000, open: 50.48, high: 50.5, low: 50.47, close: 50.48, volume: 24774, }, { date: 1462891380000, open: 50.48, high: 50.52, low: 50.48, close: 50.51, volume: 34298, }, { date: 1462891440000, open: 50.51, high: 50.53, low: 50.49, close: 50.53, volume: 34403, }, { date: 1462891500000, open: 50.525, high: 50.55, low: 50.523, close: 50.535, volume: 45727, }, { date: 1462891560000, open: 50.5309, high: 50.55, low: 50.53, close: 50.53, volume: 50999, }, { date: 1462891620000, open: 50.54, high: 50.58, low: 50.53, close: 50.57, volume: 83000, }, { date: 1462891680000, open: 50.57, high: 50.5915, low: 50.56, close: 50.56, volume: 57974, }, { date: 1462891740000, open: 50.56, high: 50.57, low: 50.55, close: 50.55, volume: 34777, }, { date: 1462891800000, open: 50.55, high: 50.61, low: 50.55, close: 50.605, volume: 41887, }, { date: 1462891860000, open: 50.605, high: 50.62, low: 50.59, close: 50.615, volume: 64383, }, { date: 1462891920000, open: 50.61, high: 50.62, low: 50.59, close: 50.61, volume: 50208, }, { date: 1462891980000, open: 50.61, high: 50.61, low: 50.595, close: 50.605, volume: 26734, }, { date: 1462892040000, open: 50.61, high: 50.61, low: 50.5799, close: 50.605, volume: 24628, }, { date: 1462892100000, open: 50.61, high: 50.649, low: 50.6, close: 50.645, volume: 42300, }, { date: 1462892160000, open: 50.65, high: 50.65, low: 50.6, close: 50.611, volume: 68207, }, { date: 1462892220000, open: 50.6101, high: 50.62, low: 50.58, close: 50.591, volume: 37478, }, { date: 1462892280000, open: 50.5947, high: 50.6, low: 50.5601, close: 50.58, volume: 41233, }, { date: 1462892340000, open: 50.585, high: 50.59, low: 50.56, close: 50.565, volume: 24256, }, { date: 1462892400000, open: 50.565, high: 50.59, low: 50.565, close: 50.58, volume: 34040, }, { date: 1462892460000, open: 50.5731, high: 50.611, low: 50.5731, close: 50.611, volume: 50146, }, { date: 1462892520000, open: 50.6199, high: 50.6199, low: 50.59, close: 50.591, volume: 26310, }, { date: 1462892580000, open: 50.6, high: 50.61, low: 50.58, close: 50.585, volume: 44969, }, { date: 1462892640000, open: 50.581, high: 50.595, low: 50.58, close: 50.585, volume: 24041, }, { date: 1462892700000, open: 50.59, high: 50.6, low: 50.58, close: 50.58, volume: 29183, }, { date: 1462892760000, open: 50.58, high: 50.59, low: 50.555, close: 50.56, volume: 39005, }, { date: 1462892820000, open: 50.57, high: 50.6099, low: 50.565, close: 50.58, volume: 46216, }, { date: 1462892880000, open: 50.57, high: 50.6095, low: 50.57, close: 50.5775, volume: 23660, }, { date: 1462892940000, open: 50.59, high: 50.61, low: 50.58, close: 50.58, volume: 40291, }, { date: 1462893000000, open: 50.59, high: 50.62, low: 50.59, close: 50.615, volume: 20999, }, { date: 1462893060000, open: 50.615, high: 50.62, low: 50.59, close: 50.595, volume: 35398, }, { date: 1462893120000, open: 50.59, high: 50.59, low: 50.55, close: 50.57, volume: 59197, }, { date: 1462893180000, open: 50.57, high: 50.58, low: 50.53, close: 50.54, volume: 36973, }, { date: 1462893240000, open: 50.5399, high: 50.55, low: 50.51, close: 50.515, volume: 30800, }, { date: 1462893300000, open: 50.515, high: 50.525, low: 50.5, close: 50.5199, volume: 58524, }, { date: 1462893360000, open: 50.515, high: 50.52, low: 50.49, close: 50.5, volume: 35165, }, { date: 1462893420000, open: 50.49, high: 50.54, low: 50.49, close: 50.525, volume: 31751, }, { date: 1462893480000, open: 50.52, high: 50.525, low: 50.4964, close: 50.4964, volume: 14737, }, { date: 1462893540000, open: 50.4955, high: 50.52, low: 50.49, close: 50.5, volume: 30170, }, { date: 1462893600000, open: 50.501, high: 50.55, low: 50.5, close: 50.55, volume: 29838, }, { date: 1462893660000, open: 50.5491, high: 50.5699, low: 50.53, close: 50.5699, volume: 37027, }, { date: 1462893720000, open: 50.56, high: 50.56, low: 50.53, close: 50.53, volume: 23154, }, { date: 1462893780000, open: 50.53, high: 50.5365, low: 50.485, close: 50.4864, volume: 33903, }, { date: 1462893840000, open: 50.48, high: 50.52, low: 50.48, close: 50.49, volume: 20693, }, { date: 1462893900000, open: 50.485, high: 50.5, low: 50.4701, close: 50.5, volume: 12749, }, { date: 1462893960000, open: 50.51, high: 50.52, low: 50.5, close: 50.51, volume: 15340, }, { date: 1462894020000, open: 50.5, high: 50.51, low: 50.49, close: 50.4999, volume: 18868, }, { date: 1462894080000, open: 50.5, high: 50.52, low: 50.4901, close: 50.52, volume: 13684, }, { date: 1462894140000, open: 50.52, high: 50.52, low: 50.5, close: 50.52, volume: 14070, }, { date: 1462894200000, open: 50.515, high: 50.55, low: 50.515, close: 50.545, volume: 26225, }, { date: 1462894260000, open: 50.54, high: 50.58, low: 50.54, close: 50.56, volume: 57809, }, { date: 1462894320000, open: 50.565, high: 50.57, low: 50.54, close: 50.555, volume: 32388, }, { date: 1462894380000, open: 50.555, high: 50.58, low: 50.555, close: 50.58, volume: 15247, }, { date: 1462894440000, open: 50.575, high: 50.6, low: 50.57, close: 50.5759, volume: 39003, }, { date: 1462894500000, open: 50.5755, high: 50.63, low: 50.57, close: 50.62, volume: 36978, }, { date: 1462894560000, open: 50.63, high: 50.645, low: 50.62, close: 50.63, volume: 38433, }, { date: 1462894620000, open: 50.62, high: 50.63, low: 50.6, close: 50.6, volume: 51484, }, { date: 1462894680000, open: 50.6, high: 50.62, low: 50.6, close: 50.61, volume: 22608, }, { date: 1462894740000, open: 50.6001, high: 50.63, low: 50.6, close: 50.61, volume: 20250, }, { date: 1462894800000, open: 50.61, high: 50.6599, low: 50.61, close: 50.64, volume: 51859, }, { date: 1462894860000, open: 50.63, high: 50.64, low: 50.62, close: 50.63, volume: 22079, }, { date: 1462894920000, open: 50.6341, high: 50.65, low: 50.63, close: 50.6431, volume: 25880, }, { date: 1462894980000, open: 50.64, high: 50.645, low: 50.57, close: 50.58, volume: 45722, }, { date: 1462895040000, open: 50.581, high: 50.62, low: 50.581, close: 50.613, volume: 25713, }, { date: 1462895100000, open: 50.62, high: 50.626, low: 50.59, close: 50.6, volume: 30951, }, { date: 1462895160000, open: 50.6, high: 50.62, low: 50.59, close: 50.59, volume: 38034, }, { date: 1462895220000, open: 50.59, high: 50.63, low: 50.58, close: 50.63, volume: 40176, }, { date: 1462895280000, open: 50.6201, high: 50.625, low: 50.6, close: 50.6, volume: 46307, }, { date: 1462895340000, open: 50.595, high: 50.6036, low: 50.57, close: 50.6036, volume: 39719, }, { date: 1462895400000, open: 50.605, high: 50.605, low: 50.582, close: 50.6, volume: 57439, }, { date: 1462895460000, open: 50.6, high: 50.64, low: 50.59, close: 50.6356, volume: 32397, }, { date: 1462895520000, open: 50.64, high: 50.66, low: 50.635, close: 50.65, volume: 45512, }, { date: 1462895580000, open: 50.643, high: 50.67, low: 50.6301, close: 50.635, volume: 27856, }, { date: 1462895640000, open: 50.6301, high: 50.6599, low: 50.63, close: 50.65, volume: 39409, }, { date: 1462895700000, open: 50.65, high: 50.66, low: 50.64, close: 50.6464, volume: 53057, }, { date: 1462895760000, open: 50.65, high: 50.67, low: 50.64, close: 50.65, volume: 74912, }, { date: 1462895820000, open: 50.66, high: 50.66, low: 50.65, close: 50.65, volume: 11934, }, { date: 1462895880000, open: 50.655, high: 50.655, low: 50.63, close: 50.635, volume: 41990, }, { date: 1462895940000, open: 50.64, high: 50.66, low: 50.6349, close: 50.655, volume: 20099, }, { date: 1462896000000, open: 50.655, high: 50.68, low: 50.651, close: 50.67, volume: 50398, }, { date: 1462896060000, open: 50.68, high: 50.699, low: 50.67, close: 50.695, volume: 57258, }, { date: 1462896120000, open: 50.695, high: 50.73, low: 50.695, close: 50.73, volume: 96286, }, { date: 1462896180000, open: 50.725, high: 50.74, low: 50.72, close: 50.7358, volume: 104488, }, { date: 1462896240000, open: 50.7331, high: 50.74, low: 50.72, close: 50.73, volume: 38429, }, { date: 1462896300000, open: 50.725, high: 50.75, low: 50.7231, close: 50.7231, volume: 47818, }, { date: 1462896360000, open: 50.73, high: 50.73, low: 50.7, close: 50.7, volume: 52255, }, { date: 1462896420000, open: 50.69, high: 50.72, low: 50.69, close: 50.7175, volume: 21930, }, { date: 1462896480000, open: 50.72, high: 50.72, low: 50.695, close: 50.7, volume: 23787, }, { date: 1462896540000, open: 50.7, high: 50.71, low: 50.69, close: 50.7, volume: 47704, }, { date: 1462896600000, open: 50.7, high: 50.707, low: 50.69, close: 50.705, volume: 25080, }, { date: 1462896660000, open: 50.71, high: 50.73, low: 50.7, close: 50.73, volume: 25356, }, { date: 1462896720000, open: 50.73, high: 50.74, low: 50.701, close: 50.701, volume: 24767, }, { date: 1462896780000, open: 50.705, high: 50.71, low: 50.69, close: 50.71, volume: 38270, }, { date: 1462896840000, open: 50.71, high: 50.72, low: 50.69, close: 50.695, volume: 11717, }, { date: 1462896900000, open: 50.6975, high: 50.715, low: 50.69, close: 50.7, volume: 12745, }, { date: 1462896960000, open: 50.7, high: 50.74, low: 50.69, close: 50.72, volume: 103856, }, { date: 1462897020000, open: 50.72, high: 50.725, low: 50.69, close: 50.7031, volume: 30451, }, { date: 1462897080000, open: 50.705, high: 50.72, low: 50.7, close: 50.71, volume: 67398, }, { date: 1462897140000, open: 50.71, high: 50.71, low: 50.69, close: 50.7, volume: 31603, }, { date: 1462897200000, open: 50.7052, high: 50.7287, low: 50.695, close: 50.7271, volume: 19478, }, { date: 1462897260000, open: 50.73, high: 50.74, low: 50.715, close: 50.74, volume: 37400, }, { date: 1462897320000, open: 50.74, high: 50.74, low: 50.72, close: 50.73, volume: 10980, }, { date: 1462897380000, open: 50.73, high: 50.77, low: 50.7225, close: 50.765, volume: 70393, }, { date: 1462897440000, open: 50.77, high: 50.77, low: 50.75, close: 50.75, volume: 37900, }, { date: 1462897500000, open: 50.75, high: 50.76, low: 50.745, close: 50.75, volume: 17342, }, { date: 1462897560000, open: 50.7542, high: 50.7665, low: 50.745, close: 50.7559, volume: 25282, }, { date: 1462897620000, open: 50.755, high: 50.77, low: 50.75, close: 50.75, volume: 20081, }, { date: 1462897680000, open: 50.755, high: 50.76, low: 50.7401, close: 50.75, volume: 30470, }, { date: 1462897740000, open: 50.7435, high: 50.75, low: 50.74, close: 50.7436, volume: 17031, }, { date: 1462897800000, open: 50.745, high: 50.765, low: 50.74, close: 50.76, volume: 27327, }, { date: 1462897860000, open: 50.76, high: 50.77, low: 50.74, close: 50.755, volume: 36647, }, { date: 1462897920000, open: 50.75, high: 50.7799, low: 50.75, close: 50.775, volume: 24471, }, { date: 1462897980000, open: 50.77, high: 50.775, low: 50.76, close: 50.77, volume: 13423, }, { date: 1462898040000, open: 50.7701, high: 50.78, low: 50.7405, close: 50.75, volume: 32314, }, { date: 1462898100000, open: 50.75, high: 50.76, low: 50.74, close: 50.76, volume: 43440, }, { date: 1462898160000, open: 50.7504, high: 50.7665, low: 50.75, close: 50.76, volume: 31881, }, { date: 1462898220000, open: 50.7599, high: 50.78, low: 50.75, close: 50.78, volume: 25336, }, { date: 1462898280000, open: 50.7799, high: 50.79, low: 50.76, close: 50.771, volume: 35358, }, { date: 1462898340000, open: 50.7742, high: 50.785, low: 50.76, close: 50.76, volume: 14699, }, { date: 1462898400000, open: 50.765, high: 50.78, low: 50.75, close: 50.778, volume: 52337, }, { date: 1462898460000, open: 50.78, high: 50.79, low: 50.77, close: 50.79, volume: 23568, }, { date: 1462898520000, open: 50.79, high: 50.805, low: 50.78, close: 50.8013, volume: 31484, }, { date: 1462898580000, open: 50.805, high: 50.805, low: 50.78, close: 50.79, volume: 11784, }, { date: 1462898640000, open: 50.79, high: 50.8, low: 50.79, close: 50.8, volume: 10101, }, { date: 1462898700000, open: 50.795, high: 50.805, low: 50.78, close: 50.78, volume: 40814, }, { date: 1462898760000, open: 50.79, high: 50.79, low: 50.77, close: 50.779, volume: 14971, }, { date: 1462898820000, open: 50.775, high: 50.8, low: 50.7699, close: 50.7799, volume: 30005, }, { date: 1462898880000, open: 50.7799, high: 50.8, low: 50.7799, close: 50.7832, volume: 28420, }, { date: 1462898940000, open: 50.79, high: 50.81, low: 50.79, close: 50.8068, volume: 27475, }, { date: 1462899000000, open: 50.805, high: 50.81, low: 50.7832, close: 50.7832, volume: 28633, }, { date: 1462899060000, open: 50.79, high: 50.79, low: 50.76, close: 50.77, volume: 22601, }, { date: 1462899120000, open: 50.77, high: 50.78, low: 50.76, close: 50.7789, volume: 11395, }, { date: 1462899180000, open: 50.775, high: 50.8, low: 50.77, close: 50.8, volume: 29529, }, { date: 1462899240000, open: 50.8, high: 50.82, low: 50.7925, close: 50.7925, volume: 66154, }, { date: 1462899300000, open: 50.7965, high: 50.82, low: 50.795, close: 50.82, volume: 25517, }, { date: 1462899360000, open: 50.82, high: 50.84, low: 50.82, close: 50.8399, volume: 41995, }, { date: 1462899420000, open: 50.84, high: 50.85, low: 50.82, close: 50.822, volume: 48401, }, { date: 1462899480000, open: 50.82, high: 50.83, low: 50.8, close: 50.825, volume: 25711, }, { date: 1462899540000, open: 50.81, high: 50.82, low: 50.8, close: 50.82, volume: 47987, }, { date: 1462899600000, open: 50.8299, high: 50.845, low: 50.8, close: 50.84, volume: 45482, }, { date: 1462899660000, open: 50.84, high: 50.84, low: 50.8, close: 50.82, volume: 49323, }, { date: 1462899720000, open: 50.82, high: 50.845, low: 50.82, close: 50.83, volume: 23888, }, { date: 1462899780000, open: 50.83, high: 50.84, low: 50.83, close: 50.83, volume: 17408, }, { date: 1462899840000, open: 50.835, high: 50.85, low: 50.83, close: 50.84, volume: 16932, }, { date: 1462899900000, open: 50.845, high: 50.845, low: 50.83, close: 50.84, volume: 24568, }, { date: 1462899960000, open: 50.85, high: 50.87, low: 50.84, close: 50.86, volume: 70103, }, { date: 1462900020000, open: 50.8563, high: 50.87, low: 50.84, close: 50.842, volume: 37089, }, { date: 1462900080000, open: 50.84, high: 50.86, low: 50.83, close: 50.855, volume: 22352, }, { date: 1462900140000, open: 50.8515, high: 50.87, low: 50.8465, close: 50.87, volume: 36861, }, { date: 1462900200000, open: 50.865, high: 50.88, low: 50.85, close: 50.88, volume: 47100, }, { date: 1462900260000, open: 50.87, high: 50.895, low: 50.87, close: 50.87, volume: 59398, }, { date: 1462900320000, open: 50.862, high: 50.88, low: 50.86, close: 50.86, volume: 27819, }, { date: 1462900380000, open: 50.865, high: 50.87, low: 50.84, close: 50.84, volume: 22656, }, { date: 1462900440000, open: 50.8405, high: 50.85, low: 50.82, close: 50.8292, volume: 30074, }, { date: 1462900500000, open: 50.82, high: 50.84, low: 50.81, close: 50.81, volume: 28837, }, { date: 1462900560000, open: 50.81, high: 50.839, low: 50.81, close: 50.835, volume: 25560, }, { date: 1462900620000, open: 50.835, high: 50.8399, low: 50.81, close: 50.82, volume: 20932, }, { date: 1462900680000, open: 50.815, high: 50.83, low: 50.81, close: 50.8125, volume: 21412, }, { date: 1462900740000, open: 50.815, high: 50.8489, low: 50.815, close: 50.83, volume: 33085, }, { date: 1462900800000, open: 50.83, high: 50.8339, low: 50.8188, close: 50.83, volume: 28912, }, { date: 1462900860000, open: 50.83, high: 50.855, low: 50.83, close: 50.855, volume: 17756, }, { date: 1462900920000, open: 50.859, high: 50.86, low: 50.84, close: 50.8599, volume: 66284, }, { date: 1462900980000, open: 50.8505, high: 50.86, low: 50.8401, close: 50.855, volume: 20474, }, { date: 1462901040000, open: 50.8559, high: 50.865, low: 50.84, close: 50.85, volume: 16777, }, { date: 1462901100000, open: 50.8442, high: 50.86, low: 50.842, close: 50.85, volume: 15172, }, { date: 1462901160000, open: 50.8415, high: 50.87, low: 50.8415, close: 50.85, volume: 76020, }, { date: 1462901220000, open: 50.85, high: 50.85, low: 50.83, close: 50.836, volume: 10780, }, { date: 1462901280000, open: 50.8389, high: 50.84, low: 50.82, close: 50.825, volume: 21291, }, { date: 1462901340000, open: 50.8237, high: 50.85, low: 50.81, close: 50.83, volume: 48447, }, { date: 1462901400000, open: 50.8276, high: 50.8495, low: 50.815, close: 50.84, volume: 60230, }, { date: 1462901460000, open: 50.85, high: 50.855, low: 50.83, close: 50.85, volume: 25133, }, { date: 1462901520000, open: 50.8599, high: 50.8599, low: 50.839, close: 50.85, volume: 40590, }, { date: 1462901580000, open: 50.85, high: 50.855, low: 50.8225, close: 50.855, volume: 28259, }, { date: 1462901640000, open: 50.85, high: 50.855, low: 50.82, close: 50.835, volume: 34831, }, { date: 1462901700000, open: 50.835, high: 50.84, low: 50.82, close: 50.8301, volume: 18805, }, { date: 1462901760000, open: 50.831, high: 50.86, low: 50.83, close: 50.845, volume: 22119, }, { date: 1462901820000, open: 50.855, high: 50.86, low: 50.84, close: 50.86, volume: 32313, }, { date: 1462901880000, open: 50.86, high: 50.87, low: 50.85, close: 50.86, volume: 28306, }, { date: 1462901940000, open: 50.85, high: 50.855, low: 50.8325, close: 50.835, volume: 23965, }, { date: 1462902000000, open: 50.835, high: 50.835, low: 50.81, close: 50.82, volume: 27495, }, { date: 1462902060000, open: 50.8221, high: 50.83, low: 50.82, close: 50.82, volume: 40028, }, { date: 1462902120000, open: 50.825, high: 50.8395, low: 50.807, close: 50.82, volume: 35536, }, { date: 1462902180000, open: 50.81, high: 50.82, low: 50.79, close: 50.81, volume: 33905, }, { date: 1462902240000, open: 50.81, high: 50.82, low: 50.8, close: 50.815, volume: 18860, }, { date: 1462902300000, open: 50.81, high: 50.83, low: 50.81, close: 50.83, volume: 25196, }, { date: 1462902360000, open: 50.83, high: 50.85, low: 50.82, close: 50.841, volume: 52631, }, { date: 1462902420000, open: 50.85, high: 50.865, low: 50.84, close: 50.85, volume: 27586, }, { date: 1462902480000, open: 50.8401, high: 50.865, low: 50.84, close: 50.85, volume: 29468, }, { date: 1462902540000, open: 50.85, high: 50.85, low: 50.83, close: 50.839, volume: 12579, }, { date: 1462902600000, open: 50.835, high: 50.8533, low: 50.83, close: 50.85, volume: 14377, }, { date: 1462902660000, open: 50.84, high: 50.8556, low: 50.84, close: 50.85, volume: 27052, }, { date: 1462902720000, open: 50.8401, high: 50.865, low: 50.8401, close: 50.851, volume: 15824, }, { date: 1462902780000, open: 50.85, high: 50.87, low: 50.84, close: 50.86, volume: 30307, }, { date: 1462902840000, open: 50.86, high: 50.87, low: 50.845, close: 50.87, volume: 31888, }, { date: 1462902900000, open: 50.865, high: 50.87, low: 50.855, close: 50.8601, volume: 29610, }, { date: 1462902960000, open: 50.86, high: 50.87, low: 50.84, close: 50.85, volume: 30612, }, { date: 1462903020000, open: 50.85, high: 50.87, low: 50.85, close: 50.87, volume: 8562, }, { date: 1462903080000, open: 50.87, high: 50.89, low: 50.85, close: 50.88, volume: 34287, }, { date: 1462903140000, open: 50.885, high: 50.91, low: 50.88, close: 50.9, volume: 59598, }, { date: 1462903200000, open: 50.89, high: 50.91, low: 50.89, close: 50.895, volume: 72011, }, { date: 1462903260000, open: 50.9, high: 50.905, low: 50.875, close: 50.898, volume: 84612, }, { date: 1462903320000, open: 50.895, high: 50.9, low: 50.89, close: 50.9, volume: 14377, }, { date: 1462903380000, open: 50.8964, high: 50.9, low: 50.89, close: 50.895, volume: 71210, }, { date: 1462903440000, open: 50.9, high: 50.905, low: 50.89, close: 50.9, volume: 53789, }, { date: 1462903500000, open: 50.896, high: 50.9, low: 50.8901, close: 50.9, volume: 13133, }, { date: 1462903560000, open: 50.895, high: 50.9, low: 50.89, close: 50.9, volume: 23583, }, { date: 1462903620000, open: 50.895, high: 50.92, low: 50.895, close: 50.901, volume: 60410, }, { date: 1462903680000, open: 50.901, high: 50.915, low: 50.9, close: 50.915, volume: 41316, }, { date: 1462903740000, open: 50.9199, high: 50.92, low: 50.9, close: 50.91, volume: 33187, }, { date: 1462903800000, open: 50.9, high: 50.91, low: 50.895, close: 50.91, volume: 24469, }, { date: 1462903860000, open: 50.905, high: 50.92, low: 50.89, close: 50.89, volume: 48733, }, { date: 1462903920000, open: 50.9, high: 50.91, low: 50.9, close: 50.91, volume: 20634, }, { date: 1462903980000, open: 50.9037, high: 50.9099, low: 50.86, close: 50.875, volume: 47483, }, { date: 1462904040000, open: 50.875, high: 50.9, low: 50.87, close: 50.895, volume: 44781, }, { date: 1462904100000, open: 50.895, high: 50.92, low: 50.88, close: 50.9, volume: 132513, }, { date: 1462904160000, open: 50.891, high: 50.9289, low: 50.891, close: 50.9236, volume: 67666, }, { date: 1462904220000, open: 50.927, high: 50.95, low: 50.92, close: 50.95, volume: 75568, }, { date: 1462904280000, open: 50.945, high: 50.97, low: 50.94, close: 50.96, volume: 98503, }, { date: 1462904340000, open: 50.9564, high: 50.97, low: 50.9533, close: 50.97, volume: 24317, }, { date: 1462904400000, open: 50.9641, high: 50.97, low: 50.95, close: 50.9559, volume: 49120, }, { date: 1462904460000, open: 50.955, high: 50.97, low: 50.95, close: 50.965, volume: 63632, }, { date: 1462904520000, open: 50.9699, high: 50.97, low: 50.95, close: 50.965, volume: 30268, }, { date: 1462904580000, open: 50.964, high: 50.98, low: 50.96, close: 50.97, volume: 29499, }, { date: 1462904640000, open: 50.9679, high: 50.97, low: 50.96, close: 50.965, volume: 29119, }, { date: 1462904700000, open: 50.965, high: 50.98, low: 50.96, close: 50.979, volume: 40302, }, { date: 1462904760000, open: 50.98, high: 50.98, low: 50.95, close: 50.97, volume: 73366, }, { date: 1462904820000, open: 50.97, high: 50.99, low: 50.97, close: 50.975, volume: 75342, }, { date: 1462904880000, open: 50.98, high: 50.98, low: 50.97, close: 50.9799, volume: 15419, }, { date: 1462904940000, open: 50.98, high: 50.99, low: 50.97, close: 50.979, volume: 25475, }, { date: 1462905000000, open: 50.98, high: 50.98, low: 50.95, close: 50.95, volume: 53201, }, { date: 1462905060000, open: 50.95, high: 50.96, low: 50.94, close: 50.94, volume: 192483, }, { date: 1462905120000, open: 50.94, high: 50.955, low: 50.93, close: 50.935, volume: 45506, }, { date: 1462905180000, open: 50.93, high: 50.93, low: 50.88, close: 50.91, volume: 61259, }, { date: 1462905240000, open: 50.9076, high: 50.9267, low: 50.9076, close: 50.9267, volume: 37550, }, { date: 1462905300000, open: 50.9211, high: 50.95, low: 50.91, close: 50.93, volume: 84916, }, { date: 1462905360000, open: 50.94, high: 50.94, low: 50.915, close: 50.915, volume: 37559, }, { date: 1462905420000, open: 50.92, high: 50.945, low: 50.92, close: 50.94, volume: 41902, }, { date: 1462905480000, open: 50.95, high: 50.95, low: 50.94, close: 50.95, volume: 21432, }, { date: 1462905540000, open: 50.94, high: 50.9465, low: 50.91, close: 50.9199, volume: 59072, }, { date: 1462905600000, open: 50.9171, high: 50.9299, low: 50.91, close: 50.92, volume: 24767, }, { date: 1462905660000, open: 50.92, high: 50.92, low: 50.91, close: 50.915, volume: 13861, }, { date: 1462905720000, open: 50.9135, high: 50.92, low: 50.91, close: 50.915, volume: 39404, }, { date: 1462905780000, open: 50.91, high: 50.935, low: 50.91, close: 50.9225, volume: 61180, }, { date: 1462905840000, open: 50.93, high: 50.93, low: 50.89, close: 50.89, volume: 56506, }, { date: 1462905900000, open: 50.89, high: 50.9, low: 50.87, close: 50.875, volume: 52929, }, { date: 1462905960000, open: 50.88, high: 50.9, low: 50.87, close: 50.879, volume: 41753, }, { date: 1462906020000, open: 50.875, high: 50.88, low: 50.86, close: 50.86, volume: 18711, }, { date: 1462906080000, open: 50.865, high: 50.865, low: 50.84, close: 50.84, volume: 37037, }, { date: 1462906140000, open: 50.8459, high: 50.87, low: 50.845, close: 50.86, volume: 37852, }, { date: 1462906200000, open: 50.865, high: 50.865, low: 50.84, close: 50.84, volume: 42469, }, { date: 1462906260000, open: 50.845, high: 50.85, low: 50.84, close: 50.85, volume: 17821, }, { date: 1462906320000, open: 50.84, high: 50.855, low: 50.84, close: 50.85, volume: 26806, }, { date: 1462906380000, open: 50.85, high: 50.855, low: 50.84, close: 50.845, volume: 14385, }, { date: 1462906440000, open: 50.85, high: 50.86, low: 50.835, close: 50.86, volume: 79757, }, { date: 1462906500000, open: 50.855, high: 50.86, low: 50.85, close: 50.8571, volume: 6077, }, { date: 1462906560000, open: 50.85, high: 50.87, low: 50.84, close: 50.869, volume: 49090, }, { date: 1462906620000, open: 50.865, high: 50.87, low: 50.86, close: 50.86, volume: 21695, }, { date: 1462906680000, open: 50.87, high: 50.88, low: 50.8619, close: 50.875, volume: 25345, }, { date: 1462906740000, open: 50.87, high: 50.885, low: 50.86, close: 50.86, volume: 39670, }, { date: 1462906800000, open: 50.86, high: 50.87, low: 50.84, close: 50.845, volume: 14552, }, { date: 1462906860000, open: 50.84, high: 50.87, low: 50.83, close: 50.87, volume: 40019, }, { date: 1462906920000, open: 50.87, high: 50.87, low: 50.855, close: 50.865, volume: 80032, }, { date: 1462906980000, open: 50.87, high: 50.87, low: 50.85, close: 50.86, volume: 40860, }, { date: 1462907040000, open: 50.86, high: 50.88, low: 50.86, close: 50.875, volume: 34984, }, { date: 1462907100000, open: 50.88, high: 50.88, low: 50.85, close: 50.86, volume: 23476, }, { date: 1462907160000, open: 50.853, high: 50.86, low: 50.85, close: 50.855, volume: 16840, }, { date: 1462907220000, open: 50.855, high: 50.86, low: 50.84, close: 50.845, volume: 30953, }, { date: 1462907280000, open: 50.849, high: 50.855, low: 50.83, close: 50.84, volume: 77744, }, { date: 1462907340000, open: 50.84, high: 50.86, low: 50.835, close: 50.845, volume: 38721, }, { date: 1462907400000, open: 50.845, high: 50.85, low: 50.84, close: 50.845, volume: 7528, }, { date: 1462907460000, open: 50.8499, high: 50.86, low: 50.83, close: 50.8484, volume: 87968, }, { date: 1462907520000, open: 50.85, high: 50.87, low: 50.84, close: 50.8566, volume: 62281, }, { date: 1462907580000, open: 50.86, high: 50.865, low: 50.83, close: 50.83, volume: 36122, }, { date: 1462907640000, open: 50.835, high: 50.85, low: 50.835, close: 50.8415, volume: 21010, }, { date: 1462907700000, open: 50.84, high: 50.845, low: 50.825, close: 50.84, volume: 60640, }, { date: 1462907760000, open: 50.84, high: 50.865, low: 50.84, close: 50.856, volume: 62318, }, { date: 1462907820000, open: 50.853, high: 50.87, low: 50.85, close: 50.87, volume: 21179, }, { date: 1462907880000, open: 50.87, high: 50.895, low: 50.86, close: 50.86, volume: 91825, }, { date: 1462907940000, open: 50.865, high: 50.89, low: 50.86, close: 50.89, volume: 56064, }, { date: 1462908000000, open: 50.8835, high: 50.9, low: 50.88, close: 50.89, volume: 53171, }, { date: 1462908060000, open: 50.885, high: 50.91, low: 50.88, close: 50.91, volume: 33291, }, { date: 1462908120000, open: 50.9, high: 50.92, low: 50.9, close: 50.905, volume: 74919, }, { date: 1462908180000, open: 50.907, high: 50.95, low: 50.907, close: 50.93, volume: 94985, }, { date: 1462908240000, open: 50.928, high: 50.93, low: 50.9, close: 50.9201, volume: 61799, }, { date: 1462908300000, open: 50.93, high: 50.95, low: 50.92, close: 50.935, volume: 39824, }, { date: 1462908360000, open: 50.93, high: 50.95, low: 50.93, close: 50.935, volume: 101162, }, { date: 1462908420000, open: 50.94, high: 50.9479, low: 50.91, close: 50.93, volume: 52622, }, { date: 1462908480000, open: 50.94, high: 50.96, low: 50.93, close: 50.955, volume: 68531, }, { date: 1462908540000, open: 50.95, high: 50.955, low: 50.94, close: 50.955, volume: 42542, }, { date: 1462908600000, open: 50.96, high: 50.97, low: 50.93, close: 50.95, volume: 119586, }, { date: 1462908660000, open: 50.95, high: 50.975, low: 50.945, close: 50.955, volume: 129082, }, { date: 1462908720000, open: 50.95, high: 50.96, low: 50.93, close: 50.95, volume: 51020, }, { date: 1462908780000, open: 50.94, high: 50.95, low: 50.93, close: 50.93, volume: 33721, }, { date: 1462908840000, open: 50.94, high: 50.95, low: 50.93, close: 50.95, volume: 45282, }, { date: 1462908900000, open: 50.95, high: 50.95, low: 50.93, close: 50.935, volume: 34717, }, { date: 1462908960000, open: 50.935, high: 50.96, low: 50.935, close: 50.955, volume: 58450, }, { date: 1462909020000, open: 50.96, high: 50.96, low: 50.95, close: 50.9501, volume: 75347, }, { date: 1462909080000, open: 50.9599, high: 50.9599, low: 50.93, close: 50.93, volume: 70184, }, { date: 1462909140000, open: 50.935, high: 50.96, low: 50.93, close: 50.95, volume: 44750, }, { date: 1462909200000, open: 50.96, high: 50.96, low: 50.93, close: 50.93, volume: 66803, }, { date: 1462909260000, open: 50.94, high: 50.94, low: 50.905, close: 50.935, volume: 67627, }, { date: 1462909320000, open: 50.94, high: 50.99, low: 50.93, close: 50.99, volume: 168463, }, { date: 1462909380000, open: 50.985, high: 51, low: 50.98, close: 51, volume: 90179, }, { date: 1462909440000, open: 50.995, high: 51.02, low: 50.99, close: 50.99, volume: 174470, }, { date: 1462909500000, open: 51, high: 51.01, low: 50.99, close: 51.005, volume: 54513, }, { date: 1462909560000, open: 51, high: 51.005, low: 50.99, close: 50.99, volume: 82285, }, { date: 1462909620000, open: 50.99, high: 51, low: 50.99, close: 51, volume: 92605, }, { date: 1462909680000, open: 51, high: 51.04, low: 51, close: 51.02, volume: 111277, }, { date: 1462909740000, open: 51.015, high: 51.02, low: 50.99, close: 51, volume: 76162, }, { date: 1462909800000, open: 51, high: 51.005, low: 50.98, close: 50.99, volume: 140015, }, { date: 1462909860000, open: 50.99, high: 51, low: 50.98, close: 50.99, volume: 78033, }, { date: 1462909920000, open: 50.99, high: 51.01, low: 50.98, close: 51.005, volume: 132327, }, { date: 1462909980000, open: 51.01, high: 51.02, low: 51, close: 51.02, volume: 94648, }, { date: 1462910040000, open: 51.02, high: 51.06, low: 51.01, close: 51.05, volume: 213948, }, { date: 1462910100000, open: 51.05, high: 51.06, low: 51.04, close: 51.05, volume: 128420, }, { date: 1462910160000, open: 51.05, high: 51.06, low: 51.03, close: 51.04, volume: 177479, }, { date: 1462910220000, open: 51.04, high: 51.05, low: 51.03, close: 51.041, volume: 118095, }, { date: 1462910280000, open: 51.05, high: 51.08, low: 51.04, close: 51.08, volume: 217897, }, { date: 1462910340000, open: 51.075, high: 51.1, low: 51.07, close: 51.085, volume: 332355, }, { date: 1462910400000, open: 51.09, high: 51.09, low: 51.02, close: 51.02, volume: 2141469, }, { date: 1462973400000, open: 51.13, high: 51.14, low: 51.13, close: 51.13, volume: 468332, }, { date: 1462973460000, open: 51.13, high: 51.16, low: 51.02, close: 51.14, volume: 194587, }, { date: 1462973520000, open: 51.13, high: 51.25, low: 51.13, close: 51.21, volume: 110550, }, { date: 1462973580000, open: 51.215, high: 51.33, low: 51.18, close: 51.32, volume: 152522, }, { date: 1462973640000, open: 51.3299, high: 51.3775, low: 51.3, close: 51.36, volume: 115397, }, { date: 1462973700000, open: 51.36, high: 51.36, low: 51.3, close: 51.32, volume: 80400, }, { date: 1462973760000, open: 51.335, high: 51.335, low: 51.2645, close: 51.305, volume: 81939, }, { date: 1462973820000, open: 51.3001, high: 51.31, low: 51.24, close: 51.2401, volume: 99379, }, { date: 1462973880000, open: 51.24, high: 51.37, low: 51.24, close: 51.365, volume: 77814, }, { date: 1462973940000, open: 51.365, high: 51.465, low: 51.35, close: 51.44, volume: 115023, }, { date: 1462974000000, open: 51.45, high: 51.46, low: 51.35, close: 51.361, volume: 91194, }, { date: 1462974060000, open: 51.36, high: 51.42, low: 51.36, close: 51.41, volume: 77590, }, { date: 1462974120000, open: 51.405, high: 51.405, low: 51.32, close: 51.33, volume: 80085, }, { date: 1462974180000, open: 51.328, high: 51.33, low: 51.25, close: 51.263, volume: 106618, }, { date: 1462974240000, open: 51.26, high: 51.27, low: 51.21, close: 51.25, volume: 60057, }, { date: 1462974300000, open: 51.26, high: 51.3023, low: 51.25, close: 51.285, volume: 54900, }, { date: 1462974360000, open: 51.285, high: 51.38, low: 51.28, close: 51.375, volume: 79333, }, { date: 1462974420000, open: 51.38, high: 51.45, low: 51.37, close: 51.4401, volume: 127841, }, { date: 1462974480000, open: 51.4457, high: 51.485, low: 51.43, close: 51.47, volume: 86820, }, { date: 1462974540000, open: 51.46, high: 51.56, low: 51.46, close: 51.525, volume: 235334, }, { date: 1462974600000, open: 51.52, high: 51.53, low: 51.495, close: 51.52, volume: 85616, }, { date: 1462974660000, open: 51.515, high: 51.52, low: 51.5, close: 51.52, volume: 127600, }, { date: 1462974720000, open: 51.52, high: 51.55, low: 51.52, close: 51.5311, volume: 45330, }, { date: 1462974780000, open: 51.54, high: 51.54, low: 51.5, close: 51.5099, volume: 80296, }, { date: 1462974840000, open: 51.51, high: 51.53, low: 51.5, close: 51.505, volume: 35073, }, { date: 1462974900000, open: 51.505, high: 51.5299, low: 51.48, close: 51.5055, volume: 88457, }, { date: 1462974960000, open: 51.51, high: 51.52, low: 51.5, close: 51.51, volume: 81239, }, { date: 1462975020000, open: 51.51, high: 51.52, low: 51.49, close: 51.5, volume: 65610, }, { date: 1462975080000, open: 51.49, high: 51.53, low: 51.49, close: 51.521, volume: 31159, }, { date: 1462975140000, open: 51.52, high: 51.53, low: 51.5, close: 51.529, volume: 25511, }, { date: 1462975200000, open: 51.52, high: 51.53, low: 51.51, close: 51.51, volume: 41698, }, { date: 1462975260000, open: 51.51, high: 51.52, low: 51.5, close: 51.5, volume: 30403, }, { date: 1462975320000, open: 51.5, high: 51.51, low: 51.44, close: 51.45, volume: 121908, }, { date: 1462975380000, open: 51.45, high: 51.47, low: 51.445, close: 51.45, volume: 56579, }, { date: 1462975440000, open: 51.4555, high: 51.47, low: 51.45, close: 51.458, volume: 40029, }, { date: 1462975500000, open: 51.45, high: 51.45, low: 51.42, close: 51.42, volume: 42840, }, { date: 1462975560000, open: 51.42, high: 51.4201, low: 51.36, close: 51.375, volume: 64433, }, { date: 1462975620000, open: 51.38, high: 51.4, low: 51.35, close: 51.3501, volume: 88987, }, { date: 1462975680000, open: 51.355, high: 51.3595, low: 51.34, close: 51.35, volume: 43186, }, { date: 1462975740000, open: 51.3436, high: 51.35, low: 51.31, close: 51.31, volume: 48892, }, { date: 1462975800000, open: 51.32, high: 51.325, low: 51.31, close: 51.3199, volume: 36585, }, { date: 1462975860000, open: 51.31, high: 51.32, low: 51.295, close: 51.31, volume: 69705, }, { date: 1462975920000, open: 51.31, high: 51.31, low: 51.27, close: 51.27, volume: 42421, }, { date: 1462975980000, open: 51.275, high: 51.2755, low: 51.25, close: 51.25, volume: 29552, }, { date: 1462976040000, open: 51.2525, high: 51.365, low: 51.25, close: 51.36, volume: 63286, }, { date: 1462976100000, open: 51.37, high: 51.44, low: 51.3689, close: 51.429, volume: 87200, }, { date: 1462976160000, open: 51.42, high: 51.43, low: 51.39, close: 51.43, volume: 44782, }, { date: 1462976220000, open: 51.44, high: 51.48, low: 51.435, close: 51.45, volume: 60788, }, { date: 1462976280000, open: 51.45, high: 51.49, low: 51.44, close: 51.4799, volume: 57728, }, { date: 1462976340000, open: 51.47, high: 51.49, low: 51.45, close: 51.475, volume: 60338, }, { date: 1462976400000, open: 51.479, high: 51.51, low: 51.46, close: 51.505, volume: 67036, }, { date: 1462976460000, open: 51.5, high: 51.53, low: 51.5, close: 51.525, volume: 64284, }, { date: 1462976520000, open: 51.5244, high: 51.5399, low: 51.49, close: 51.49, volume: 69688, }, { date: 1462976580000, open: 51.499, high: 51.53, low: 51.499, close: 51.52, volume: 38522, }, { date: 1462976640000, open: 51.515, high: 51.54, low: 51.51, close: 51.535, volume: 28644, }, { date: 1462976700000, open: 51.535, high: 51.55, low: 51.53, close: 51.55, volume: 42233, }, { date: 1462976760000, open: 51.55, high: 51.58, low: 51.54, close: 51.5764, volume: 55704, }, { date: 1462976820000, open: 51.58, high: 51.59, low: 51.57, close: 51.57, volume: 40196, }, { date: 1462976880000, open: 51.575, high: 51.585, low: 51.56, close: 51.5765, volume: 60675, }, { date: 1462976940000, open: 51.5736, high: 51.6, low: 51.57, close: 51.58, volume: 53625, }, { date: 1462977000000, open: 51.58, high: 51.65, low: 51.57, close: 51.64, volume: 57778, }, { date: 1462977060000, open: 51.62, high: 51.71, low: 51.61, close: 51.6272, volume: 203530, }, { date: 1462977120000, open: 51.63, high: 51.65, low: 51.6, close: 51.615, volume: 49615, }, { date: 1462977180000, open: 51.62, high: 51.715, low: 51.615, close: 51.699, volume: 103914, }, { date: 1462977240000, open: 51.7, high: 51.71, low: 51.68, close: 51.69, volume: 52145, }, { date: 1462977300000, open: 51.7, high: 51.73, low: 51.69, close: 51.72, volume: 57407, }, { date: 1462977360000, open: 51.725, high: 51.75, low: 51.72, close: 51.745, volume: 139793, }, { date: 1462977420000, open: 51.75, high: 51.77, low: 51.74, close: 51.76, volume: 138813, }, { date: 1462977480000, open: 51.76, high: 51.78, low: 51.76, close: 51.7611, volume: 67750, }, { date: 1462977540000, open: 51.76, high: 51.765, low: 51.655, close: 51.69, volume: 122980, }, { date: 1462977600000, open: 51.69, high: 51.71, low: 51.68, close: 51.7, volume: 75334, }, { date: 1462977660000, open: 51.7, high: 51.71, low: 51.68, close: 51.7, volume: 112178, }, { date: 1462977720000, open: 51.69, high: 51.69, low: 51.58, close: 51.63, volume: 80795, }, { date: 1462977780000, open: 51.63, high: 51.6359, low: 51.6, close: 51.62, volume: 37536, }, { date: 1462977840000, open: 51.62, high: 51.62, low: 51.595, close: 51.605, volume: 53430, }, { date: 1462977900000, open: 51.6, high: 51.64, low: 51.595, close: 51.63, volume: 103689, }, { date: 1462977960000, open: 51.625, high: 51.65, low: 51.62, close: 51.62, volume: 37551, }, { date: 1462978020000, open: 51.63, high: 51.66, low: 51.61, close: 51.659, volume: 39922, }, { date: 1462978080000, open: 51.65, high: 51.69, low: 51.65, close: 51.6699, volume: 60591, }, { date: 1462978140000, open: 51.665, high: 51.7, low: 51.665, close: 51.69, volume: 43626, }, { date: 1462978200000, open: 51.69, high: 51.72, low: 51.68, close: 51.69, volume: 92844, }, { date: 1462978260000, open: 51.68, high: 51.69, low: 51.63, close: 51.65, volume: 111469, }, { date: 1462978320000, open: 51.66, high: 51.68, low: 51.65, close: 51.68, volume: 51618, }, { date: 1462978380000, open: 51.68, high: 51.69, low: 51.67, close: 51.68, volume: 21462, }, { date: 1462978440000, open: 51.68, high: 51.69, low: 51.66, close: 51.675, volume: 62920, }, { date: 1462978500000, open: 51.675, high: 51.69, low: 51.66, close: 51.67, volume: 30463, }, { date: 1462978560000, open: 51.665, high: 51.68, low: 51.65, close: 51.68, volume: 58443, }, { date: 1462978620000, open: 51.68, high: 51.72, low: 51.675, close: 51.715, volume: 36043, }, { date: 1462978680000, open: 51.7199, high: 51.735, low: 51.7, close: 51.73, volume: 44052, }, { date: 1462978740000, open: 51.73, high: 51.75, low: 51.72, close: 51.75, volume: 72401, }, { date: 1462978800000, open: 51.7457, high: 51.76, low: 51.72, close: 51.725, volume: 100138, }, { date: 1462978860000, open: 51.72, high: 51.729, low: 51.6, close: 51.6, volume: 123027, }, { date: 1462978920000, open: 51.5999, high: 51.5999, low: 51.52, close: 51.525, volume: 49063, }, { date: 1462978980000, open: 51.525, high: 51.53, low: 51.51, close: 51.5133, volume: 38657, }, { date: 1462979040000, open: 51.52, high: 51.53, low: 51.47, close: 51.47, volume: 82390, }, { date: 1462979100000, open: 51.46, high: 51.478, low: 51.38, close: 51.39, volume: 107751, }, { date: 1462979160000, open: 51.38, high: 51.42, low: 51.34, close: 51.405, volume: 63422, }, { date: 1462979220000, open: 51.41, high: 51.4499, low: 51.41, close: 51.445, volume: 37125, }, { date: 1462979280000, open: 51.44, high: 51.49, low: 51.435, close: 51.475, volume: 89144, }, { date: 1462979340000, open: 51.475, high: 51.52, low: 51.465, close: 51.51, volume: 41648, }, { date: 1462979400000, open: 51.5176, high: 51.55, low: 51.51, close: 51.54, volume: 31529, }, { date: 1462979460000, open: 51.54, high: 51.56, low: 51.53, close: 51.53, volume: 43517, }, { date: 1462979520000, open: 51.53, high: 51.53, low: 51.47, close: 51.51, volume: 96873, }, { date: 1462979580000, open: 51.51, high: 51.54, low: 51.5, close: 51.535, volume: 85441, }, { date: 1462979640000, open: 51.525, high: 51.555, low: 51.525, close: 51.535, volume: 60706, }, { date: 1462979700000, open: 51.54, high: 51.59, low: 51.54, close: 51.55, volume: 31386, }, { date: 1462979760000, open: 51.5499, high: 51.55, low: 51.51, close: 51.51, volume: 32264, }, { date: 1462979820000, open: 51.51, high: 51.5201, low: 51.48, close: 51.5201, volume: 40569, }, { date: 1462979880000, open: 51.525, high: 51.53, low: 51.51, close: 51.51, volume: 10606, }, { date: 1462979940000, open: 51.515, high: 51.55, low: 51.505, close: 51.545, volume: 52839, }, { date: 1462980000000, open: 51.54, high: 51.55, low: 51.5205, close: 51.54, volume: 13861, }, { date: 1462980060000, open: 51.54, high: 51.545, low: 51.52, close: 51.52, volume: 8934, }, { date: 1462980120000, open: 51.53, high: 51.53, low: 51.495, close: 51.53, volume: 46319, }, { date: 1462980180000, open: 51.53, high: 51.555, low: 51.52, close: 51.52, volume: 22583, }, { date: 1462980240000, open: 51.5272, high: 51.5272, low: 51.49, close: 51.5, volume: 44211, }, { date: 1462980300000, open: 51.5, high: 51.51, low: 51.49, close: 51.51, volume: 16433, }, { date: 1462980360000, open: 51.51, high: 51.57, low: 51.51, close: 51.55, volume: 45077, }, { date: 1462980420000, open: 51.54, high: 51.57, low: 51.52, close: 51.56, volume: 46480, }, { date: 1462980480000, open: 51.569, high: 51.58, low: 51.54, close: 51.575, volume: 101546, }, { date: 1462980540000, open: 51.57, high: 51.58, low: 51.56, close: 51.57, volume: 76642, }, { date: 1462980600000, open: 51.57, high: 51.58, low: 51.56, close: 51.57, volume: 37632, }, { date: 1462980660000, open: 51.57, high: 51.58, low: 51.52, close: 51.52, volume: 168637, }, { date: 1462980720000, open: 51.51, high: 51.55, low: 51.51, close: 51.55, volume: 29574, }, { date: 1462980780000, open: 51.54, high: 51.57, low: 51.54, close: 51.55, volume: 56000, }, { date: 1462980840000, open: 51.55, high: 51.55, low: 51.525, close: 51.535, volume: 49225, }, { date: 1462980900000, open: 51.535, high: 51.54, low: 51.5, close: 51.525, volume: 66536, }, { date: 1462980960000, open: 51.53, high: 51.59, low: 51.53, close: 51.57, volume: 61325, }, { date: 1462981020000, open: 51.58, high: 51.58, low: 51.515, close: 51.525, volume: 34299, }, { date: 1462981080000, open: 51.525, high: 51.535, low: 51.47, close: 51.48, volume: 38247, }, { date: 1462981140000, open: 51.485, high: 51.495, low: 51.47, close: 51.47, volume: 24870, }, { date: 1462981200000, open: 51.4759, high: 51.49, low: 51.47, close: 51.48, volume: 40608, }, { date: 1462981260000, open: 51.4813, high: 51.49, low: 51.45, close: 51.45, volume: 39273, }, { date: 1462981320000, open: 51.455, high: 51.53, low: 51.455, close: 51.5211, volume: 216872, }, { date: 1462981380000, open: 51.53, high: 51.53, low: 51.4901, close: 51.495, volume: 27547, }, { date: 1462981440000, open: 51.5064, high: 51.52, low: 51.4935, close: 51.51, volume: 14571, }, { date: 1462981500000, open: 51.505, high: 51.54, low: 51.505, close: 51.54, volume: 29126, }, { date: 1462981560000, open: 51.53, high: 51.535, low: 51.48, close: 51.48, volume: 25448, }, { date: 1462981620000, open: 51.48, high: 51.48, low: 51.45, close: 51.46, volume: 50662, }, { date: 1462981680000, open: 51.4532, high: 51.4663, low: 51.42, close: 51.43, volume: 39046, }, { date: 1462981740000, open: 51.43, high: 51.45, low: 51.41, close: 51.41, volume: 22613, }, { date: 1462981800000, open: 51.42, high: 51.42, low: 51.4, close: 51.4071, volume: 36439, }, { date: 1462981860000, open: 51.4, high: 51.409, low: 51.39, close: 51.3999, volume: 37396, }, { date: 1462981920000, open: 51.4, high: 51.41, low: 51.39, close: 51.4, volume: 33704, }, { date: 1462981980000, open: 51.4, high: 51.4089, low: 51.39, close: 51.39, volume: 16303, }, { date: 1462982040000, open: 51.4, high: 51.4, low: 51.39, close: 51.4, volume: 13046, }, { date: 1462982100000, open: 51.3901, high: 51.405, low: 51.39, close: 51.4, volume: 34037, }, { date: 1462982160000, open: 51.4, high: 51.405, low: 51.3699, close: 51.37, volume: 49486, }, { date: 1462982220000, open: 51.36, high: 51.38, low: 51.36, close: 51.37, volume: 24303, }, { date: 1462982280000, open: 51.37, high: 51.37, low: 51.35, close: 51.36, volume: 24126, }, { date: 1462982340000, open: 51.355, high: 51.37, low: 51.35, close: 51.36, volume: 10679, }, { date: 1462982400000, open: 51.36, high: 51.37, low: 51.34, close: 51.36, volume: 30698, }, { date: 1462982460000, open: 51.36, high: 51.39, low: 51.349, close: 51.39, volume: 54646, }, { date: 1462982520000, open: 51.4, high: 51.4, low: 51.36, close: 51.36, volume: 34727, }, { date: 1462982580000, open: 51.37, high: 51.375, low: 51.35, close: 51.35, volume: 30200, }, { date: 1462982640000, open: 51.35, high: 51.35, low: 51.305, close: 51.305, volume: 32533, }, { date: 1462982700000, open: 51.31, high: 51.35, low: 51.31, close: 51.34, volume: 17250, }, { date: 1462982760000, open: 51.34, high: 51.3611, low: 51.34, close: 51.34, volume: 54991, }, { date: 1462982820000, open: 51.34, high: 51.36, low: 51.33, close: 51.35, volume: 19785, }, { date: 1462982880000, open: 51.36, high: 51.39, low: 51.36, close: 51.385, volume: 27917, }, { date: 1462982940000, open: 51.385, high: 51.4, low: 51.38, close: 51.4, volume: 6718, }, { date: 1462983000000, open: 51.4, high: 51.4, low: 51.38, close: 51.39, volume: 21907, }, { date: 1462983060000, open: 51.4, high: 51.4099, low: 51.37, close: 51.37, volume: 21640, }, { date: 1462983120000, open: 51.37, high: 51.39, low: 51.36, close: 51.38, volume: 14368, }, { date: 1462983180000, open: 51.38, high: 51.4, low: 51.37, close: 51.39, volume: 14742, }, { date: 1462983240000, open: 51.39, high: 51.42, low: 51.385, close: 51.42, volume: 22384, }, { date: 1462983300000, open: 51.42, high: 51.45, low: 51.42, close: 51.448, volume: 18104, }, { date: 1462983360000, open: 51.449, high: 51.45, low: 51.42, close: 51.43, volume: 40637, }, { date: 1462983420000, open: 51.435, high: 51.47, low: 51.425, close: 51.47, volume: 36148, }, { date: 1462983480000, open: 51.46, high: 51.495, low: 51.45, close: 51.47, volume: 53652, }, { date: 1462983540000, open: 51.46, high: 51.46, low: 51.43, close: 51.44, volume: 17221, }, { date: 1462983600000, open: 51.4438, high: 51.46, low: 51.44, close: 51.46, volume: 14562, }, { date: 1462983660000, open: 51.47, high: 51.48, low: 51.445, close: 51.4495, volume: 16392, }, { date: 1462983720000, open: 51.44, high: 51.4401, low: 51.415, close: 51.43, volume: 39191, }, { date: 1462983780000, open: 51.43, high: 51.44, low: 51.4017, close: 51.4017, volume: 22353, }, { date: 1462983840000, open: 51.4, high: 51.41, low: 51.38, close: 51.38, volume: 19293, }, { date: 1462983900000, open: 51.38, high: 51.385, low: 51.37, close: 51.37, volume: 16224, }, { date: 1462983960000, open: 51.3662, high: 51.38, low: 51.362, close: 51.37, volume: 17840, }, { date: 1462984020000, open: 51.37, high: 51.39, low: 51.37, close: 51.38, volume: 19461, }, { date: 1462984080000, open: 51.39, high: 51.4, low: 51.37, close: 51.399, volume: 13137, }, { date: 1462984140000, open: 51.395, high: 51.4062, low: 51.39, close: 51.3901, volume: 12489, }, { date: 1462984200000, open: 51.395, high: 51.3999, low: 51.365, close: 51.37, volume: 17961, }, { date: 1462984260000, open: 51.37, high: 51.39, low: 51.365, close: 51.385, volume: 11359, }, { date: 1462984320000, open: 51.38, high: 51.395, low: 51.38, close: 51.38, volume: 20385, }, { date: 1462984380000, open: 51.38, high: 51.39, low: 51.36, close: 51.36, volume: 22565, }, { date: 1462984440000, open: 51.36, high: 51.3699, low: 51.36, close: 51.365, volume: 4468, }, { date: 1462984500000, open: 51.36, high: 51.38, low: 51.36, close: 51.375, volume: 26147, }, { date: 1462984560000, open: 51.375, high: 51.38, low: 51.36, close: 51.3685, volume: 18845, }, { date: 1462984620000, open: 51.37, high: 51.37, low: 51.32, close: 51.32, volume: 34966, }, { date: 1462984680000, open: 51.32, high: 51.32, low: 51.3001, close: 51.315, volume: 15210, }, { date: 1462984740000, open: 51.3175, high: 51.375, low: 51.31, close: 51.37, volume: 33836, }, { date: 1462984800000, open: 51.375, high: 51.39, low: 51.37, close: 51.38, volume: 19493, }, { date: 1462984860000, open: 51.3895, high: 51.41, low: 51.38, close: 51.4, volume: 79293, }, { date: 1462984920000, open: 51.4, high: 51.41, low: 51.38, close: 51.38, volume: 38575, }, { date: 1462984980000, open: 51.385, high: 51.39, low: 51.375, close: 51.39, volume: 39952, }, { date: 1462985040000, open: 51.39, high: 51.4, low: 51.385, close: 51.385, volume: 44342, }, { date: 1462985100000, open: 51.385, high: 51.4, low: 51.37, close: 51.37, volume: 18399, }, { date: 1462985160000, open: 51.3732, high: 51.38, low: 51.36, close: 51.37, volume: 17259, }, { date: 1462985220000, open: 51.37, high: 51.38, low: 51.365, close: 51.37, volume: 10226, }, { date: 1462985280000, open: 51.37, high: 51.3701, low: 51.36, close: 51.365, volume: 10921, }, { date: 1462985340000, open: 51.37, high: 51.38, low: 51.36, close: 51.38, volume: 33993, }, { date: 1462985400000, open: 51.38, high: 51.395, low: 51.35, close: 51.36, volume: 40627, }, { date: 1462985460000, open: 51.351, high: 51.351, low: 51.33, close: 51.34, volume: 17600, }, { date: 1462985520000, open: 51.34, high: 51.34, low: 51.32, close: 51.33, volume: 21996, }, { date: 1462985580000, open: 51.33, high: 51.34, low: 51.31, close: 51.31, volume: 18068, }, { date: 1462985640000, open: 51.3064, high: 51.3064, low: 51.27, close: 51.289, volume: 51982, }, { date: 1462985700000, open: 51.29, high: 51.29, low: 51.27, close: 51.2864, volume: 13732, }, { date: 1462985760000, open: 51.2833, high: 51.2833, low: 51.265, close: 51.27, volume: 19089, }, { date: 1462985820000, open: 51.279, high: 51.2899, low: 51.265, close: 51.2899, volume: 22685, }, { date: 1462985880000, open: 51.285, high: 51.286, low: 51.26, close: 51.2671, volume: 18005, }, { date: 1462985940000, open: 51.27, high: 51.275, low: 51.26, close: 51.27, volume: 25623, }, { date: 1462986000000, open: 51.27, high: 51.279, low: 51.26, close: 51.27, volume: 14217, }, { date: 1462986060000, open: 51.27, high: 51.29, low: 51.26, close: 51.26, volume: 32162, }, { date: 1462986120000, open: 51.265, high: 51.27, low: 51.25, close: 51.2599, volume: 32474, }, { date: 1462986180000, open: 51.2574, high: 51.26, low: 51.245, close: 51.252, volume: 52184, }, { date: 1462986240000, open: 51.26, high: 51.29, low: 51.255, close: 51.2899, volume: 24209, }, { date: 1462986300000, open: 51.28, high: 51.3, low: 51.27, close: 51.3, volume: 20710, }, { date: 1462986360000, open: 51.3, high: 51.3299, low: 51.29, close: 51.3299, volume: 16838, }, { date: 1462986420000, open: 51.329, high: 51.33, low: 51.3, close: 51.32, volume: 29893, }, { date: 1462986480000, open: 51.325, high: 51.34, low: 51.32, close: 51.3265, volume: 36644, }, { date: 1462986540000, open: 51.33, high: 51.3428, low: 51.32, close: 51.33, volume: 33329, }, { date: 1462986600000, open: 51.3233, high: 51.3233, low: 51.3, close: 51.3, volume: 21303, }, { date: 1462986660000, open: 51.3, high: 51.325, low: 51.3, close: 51.31, volume: 21187, }, { date: 1462986720000, open: 51.31, high: 51.33, low: 51.309, close: 51.33, volume: 25420, }, { date: 1462986780000, open: 51.33, high: 51.3358, low: 51.29, close: 51.29, volume: 24096, }, { date: 1462986840000, open: 51.29, high: 51.31, low: 51.285, close: 51.31, volume: 25943, }, { date: 1462986900000, open: 51.3063, high: 51.31, low: 51.289, close: 51.289, volume: 17468, }, { date: 1462986960000, open: 51.28, high: 51.3, low: 51.28, close: 51.285, volume: 14950, }, { date: 1462987020000, open: 51.28, high: 51.2868, low: 51.27, close: 51.2766, volume: 4208, }, { date: 1462987080000, open: 51.27, high: 51.3, low: 51.27, close: 51.3, volume: 25872, }, { date: 1462987140000, open: 51.295, high: 51.31, low: 51.29, close: 51.31, volume: 2859, }, { date: 1462987200000, open: 51.305, high: 51.305, low: 51.28, close: 51.2832, volume: 28854, }, { date: 1462987260000, open: 51.28, high: 51.3, low: 51.28, close: 51.28, volume: 20787, }, { date: 1462987320000, open: 51.285, high: 51.285, low: 51.25, close: 51.2699, volume: 39525, }, { date: 1462987380000, open: 51.265, high: 51.265, low: 51.24, close: 51.24, volume: 34890, }, { date: 1462987440000, open: 51.242, high: 51.25, low: 51.21, close: 51.235, volume: 26498, }, { date: 1462987500000, open: 51.23, high: 51.2495, low: 51.23, close: 51.2399, volume: 17674, }, { date: 1462987560000, open: 51.23, high: 51.2399, low: 51.2, close: 51.21, volume: 31259, }, { date: 1462987620000, open: 51.21, high: 51.2199, low: 51.2, close: 51.2, volume: 62556, }, { date: 1462987680000, open: 51.209, high: 51.24, low: 51.2, close: 51.22, volume: 30273, }, { date: 1462987740000, open: 51.22, high: 51.25, low: 51.22, close: 51.25, volume: 17454, }, { date: 1462987800000, open: 51.25, high: 51.2799, low: 51.24, close: 51.2592, volume: 33605, }, { date: 1462987860000, open: 51.255, high: 51.26, low: 51.24, close: 51.26, volume: 24625, }, { date: 1462987920000, open: 51.26, high: 51.26, low: 51.24, close: 51.24, volume: 18064, }, { date: 1462987980000, open: 51.23, high: 51.24, low: 51.22, close: 51.23, volume: 21182, }, { date: 1462988040000, open: 51.23, high: 51.255, low: 51.23, close: 51.25, volume: 18673, }, { date: 1462988100000, open: 51.25, high: 51.25, low: 51.23, close: 51.23, volume: 13845, }, { date: 1462988160000, open: 51.23, high: 51.24, low: 51.2, close: 51.24, volume: 36047, }, { date: 1462988220000, open: 51.24, high: 51.26, low: 51.24, close: 51.26, volume: 15485, }, { date: 1462988280000, open: 51.27, high: 51.28, low: 51.27, close: 51.28, volume: 23272, }, { date: 1462988340000, open: 51.28, high: 51.28, low: 51.24, close: 51.25, volume: 42653, }, { date: 1462988400000, open: 51.2501, high: 51.27, low: 51.25, close: 51.27, volume: 23400, }, { date: 1462988460000, open: 51.27, high: 51.27, low: 51.22, close: 51.2238, volume: 31844, }, { date: 1462988520000, open: 51.225, high: 51.25, low: 51.2101, close: 51.23, volume: 26920, }, { date: 1462988580000, open: 51.23, high: 51.23, low: 51.21, close: 51.221, volume: 21943, }, { date: 1462988640000, open: 51.22, high: 51.23, low: 51.2, close: 51.21, volume: 19760, }, { date: 1462988700000, open: 51.21, high: 51.21, low: 51.1799, close: 51.1899, volume: 33232, }, { date: 1462988760000, open: 51.1847, high: 51.1935, low: 51.18, close: 51.19, volume: 45230, }, { date: 1462988820000, open: 51.19, high: 51.2, low: 51.18, close: 51.19, volume: 62051, }, { date: 1462988880000, open: 51.19, high: 51.198, low: 51.15, close: 51.1501, volume: 28431, }, { date: 1462988940000, open: 51.15, high: 51.17, low: 51.15, close: 51.1601, volume: 12852, }, { date: 1462989000000, open: 51.165, high: 51.2, low: 51.16, close: 51.1999, volume: 19492, }, { date: 1462989060000, open: 51.2, high: 51.2, low: 51.1601, close: 51.18, volume: 30886, }, { date: 1462989120000, open: 51.175, high: 51.1899, low: 51.17, close: 51.17, volume: 10745, }, { date: 1462989180000, open: 51.1793, high: 51.1793, low: 51.155, close: 51.165, volume: 17322, }, { date: 1462989240000, open: 51.163, high: 51.175, low: 51.145, close: 51.145, volume: 31004, }, { date: 1462989300000, open: 51.15, high: 51.155, low: 51.125, close: 51.14, volume: 26048, }, { date: 1462989360000, open: 51.1385, high: 51.16, low: 51.1385, close: 51.15, volume: 28391, }, { date: 1462989420000, open: 51.145, high: 51.15, low: 51.14, close: 51.14, volume: 17541, }, { date: 1462989480000, open: 51.14, high: 51.14, low: 51.1136, close: 51.135, volume: 31995, }, { date: 1462989540000, open: 51.13, high: 51.13, low: 51.11, close: 51.12, volume: 15498, }, { date: 1462989600000, open: 51.12, high: 51.12, low: 51.07, close: 51.08, volume: 48825, }, { date: 1462989660000, open: 51.08, high: 51.08, low: 51.06, close: 51.07, volume: 35146, }, { date: 1462989720000, open: 51.07, high: 51.0798, low: 51.05, close: 51.064, volume: 43814, }, { date: 1462989780000, open: 51.0635, high: 51.0635, low: 51.02, close: 51.02, volume: 28793, }, { date: 1462989840000, open: 51.025, high: 51.05, low: 51.02, close: 51.05, volume: 29817, }, { date: 1462989900000, open: 51.05, high: 51.1, low: 51.045, close: 51.08, volume: 28728, }, { date: 1462989960000, open: 51.08, high: 51.08, low: 51.05, close: 51.05, volume: 50435, }, { date: 1462990020000, open: 51.0599, high: 51.06, low: 51.03, close: 51.03, volume: 34308, }, { date: 1462990080000, open: 51.035, high: 51.06, low: 51.015, close: 51.05, volume: 49308, }, { date: 1462990140000, open: 51.05, high: 51.065, low: 51.05, close: 51.055, volume: 14976, }, { date: 1462990200000, open: 51.05, high: 51.06, low: 51.02, close: 51.025, volume: 39125, }, { date: 1462990260000, open: 51.035, high: 51.04, low: 51.02, close: 51.02, volume: 14875, }, { date: 1462990320000, open: 51.025, high: 51.065, low: 51.025, close: 51.06, volume: 32399, }, { date: 1462990380000, open: 51.065, high: 51.1, low: 51.065, close: 51.1, volume: 13285, }, { date: 1462990440000, open: 51.1, high: 51.1, low: 51.085, close: 51.09, volume: 22783, }, { date: 1462990500000, open: 51.0956, high: 51.1, low: 51.08, close: 51.09, volume: 36233, }, { date: 1462990560000, open: 51.09, high: 51.0901, low: 51.0501, close: 51.0501, volume: 38472, }, { date: 1462990620000, open: 51.05, high: 51.05, low: 51.03, close: 51.04, volume: 24330, }, { date: 1462990680000, open: 51.05, high: 51.07, low: 51.03, close: 51.0613, volume: 30203, }, { date: 1462990740000, open: 51.065, high: 51.09, low: 51.06, close: 51.075, volume: 101522, }, { date: 1462990800000, open: 51.0701, high: 51.0901, low: 51.055, close: 51.09, volume: 26648, }, { date: 1462990860000, open: 51.09, high: 51.115, low: 51.05, close: 51.05, volume: 37532, }, { date: 1462990920000, open: 51.05, high: 51.09, low: 51.05, close: 51.08, volume: 24114, }, { date: 1462990980000, open: 51.085, high: 51.1, low: 51.075, close: 51.1, volume: 23853, }, { date: 1462991040000, open: 51.1, high: 51.13, low: 51.1, close: 51.1288, volume: 14736, }, { date: 1462991100000, open: 51.12, high: 51.14, low: 51.12, close: 51.139, volume: 20308, }, { date: 1462991160000, open: 51.135, high: 51.135, low: 51.12, close: 51.13, volume: 12098, }, { date: 1462991220000, open: 51.129, high: 51.13, low: 51.12, close: 51.13, volume: 18355, }, { date: 1462991280000, open: 51.13, high: 51.14, low: 51.11, close: 51.13, volume: 31717, }, { date: 1462991340000, open: 51.13, high: 51.14, low: 51.11, close: 51.12, volume: 35745, }, { date: 1462991400000, open: 51.12, high: 51.14, low: 51.11, close: 51.13, volume: 18202, }, { date: 1462991460000, open: 51.13, high: 51.14, low: 51.12, close: 51.13, volume: 40009, }, { date: 1462991520000, open: 51.13, high: 51.14, low: 51.11, close: 51.14, volume: 18005, }, { date: 1462991580000, open: 51.13, high: 51.16, low: 51.13, close: 51.16, volume: 38548, }, { date: 1462991640000, open: 51.15, high: 51.15, low: 51.11, close: 51.115, volume: 29839, }, { date: 1462991700000, open: 51.11, high: 51.1199, low: 51.0901, close: 51.0972, volume: 27104, }, { date: 1462991760000, open: 51.1, high: 51.1, low: 51.07, close: 51.09, volume: 33059, }, { date: 1462991820000, open: 51.09, high: 51.11, low: 51.09, close: 51.1099, volume: 19312, }, { date: 1462991880000, open: 51.1, high: 51.11, low: 51.09, close: 51.105, volume: 32468, }, { date: 1462991940000, open: 51.1, high: 51.13, low: 51.1, close: 51.13, volume: 17356, }, { date: 1462992000000, open: 51.13, high: 51.155, low: 51.1201, close: 51.15, volume: 14198, }, { date: 1462992060000, open: 51.14, high: 51.145, low: 51.12, close: 51.13, volume: 58075, }, { date: 1462992120000, open: 51.13, high: 51.13, low: 51.0989, close: 51.105, volume: 27924, }, { date: 1462992180000, open: 51.11, high: 51.1165, low: 51.1, close: 51.1, volume: 30898, }, { date: 1462992240000, open: 51.11, high: 51.11, low: 51.08, close: 51.08, volume: 39719, }, { date: 1462992300000, open: 51.085, high: 51.09, low: 51.035, close: 51.04, volume: 47936, }, { date: 1462992360000, open: 51.03, high: 51.04, low: 51, close: 51.01, volume: 72394, }, { date: 1462992420000, open: 51.01, high: 51.05, low: 51, close: 51.0344, volume: 89099, }, { date: 1462992480000, open: 51.035, high: 51.0401, low: 51, close: 51.0401, volume: 44381, }, { date: 1462992540000, open: 51.04, high: 51.05, low: 51.025, close: 51.025, volume: 23240, }, { date: 1462992600000, open: 51.02, high: 51.07, low: 51.02, close: 51.04, volume: 39944, }, { date: 1462992660000, open: 51.048, high: 51.08, low: 51.0468, close: 51.08, volume: 6404, }, { date: 1462992720000, open: 51.08, high: 51.085, low: 51.05, close: 51.065, volume: 40955, }, { date: 1462992780000, open: 51.07, high: 51.1, low: 51.065, close: 51.1, volume: 21877, }, { date: 1462992840000, open: 51.095, high: 51.1, low: 51.055, close: 51.06, volume: 36251, }, { date: 1462992900000, open: 51.06, high: 51.075, low: 51.06, close: 51.075, volume: 22054, }, { date: 1462992960000, open: 51.08, high: 51.08, low: 51.05, close: 51.0559, volume: 33091, }, { date: 1462993020000, open: 51.05, high: 51.08, low: 51.05, close: 51.08, volume: 16096, }, { date: 1462993080000, open: 51.0728, high: 51.1, low: 51.07, close: 51.095, volume: 18982, }, { date: 1462993140000, open: 51.09, high: 51.09, low: 51.05, close: 51.05, volume: 45821, }, { date: 1462993200000, open: 51.055, high: 51.055, low: 51.01, close: 51.04, volume: 44002, }, { date: 1462993260000, open: 51.035, high: 51.05, low: 51.03, close: 51.0468, volume: 32607, }, { date: 1462993320000, open: 51.04, high: 51.05, low: 51.03, close: 51.03, volume: 12035, }, { date: 1462993380000, open: 51.03, high: 51.04, low: 51, close: 51.015, volume: 61094, }, { date: 1462993440000, open: 51.0101, high: 51.04, low: 51, close: 51.04, volume: 21504, }, { date: 1462993500000, open: 51.04, high: 51.04, low: 51.02, close: 51.0269, volume: 21183, }, { date: 1462993560000, open: 51.025, high: 51.07, low: 51.0201, close: 51.0652, volume: 17983, }, { date: 1462993620000, open: 51.065, high: 51.09, low: 51.06, close: 51.09, volume: 38136, }, { date: 1462993680000, open: 51.0864, high: 51.095, low: 51.065, close: 51.085, volume: 47556, }, { date: 1462993740000, open: 51.085, high: 51.1, low: 51.06, close: 51.079, volume: 27412, }, { date: 1462993800000, open: 51.07, high: 51.09, low: 51.07, close: 51.0801, volume: 39221, }, { date: 1462993860000, open: 51.08, high: 51.085, low: 51.07, close: 51.08, volume: 24630, }, { date: 1462993920000, open: 51.09, high: 51.12, low: 51.08, close: 51.0999, volume: 82176, }, { date: 1462993980000, open: 51.095, high: 51.13, low: 51.09, close: 51.125, volume: 40165, }, { date: 1462994040000, open: 51.12, high: 51.12, low: 51.08, close: 51.09, volume: 40203, }, { date: 1462994100000, open: 51.09, high: 51.09, low: 51.065, close: 51.08, volume: 29588, }, { date: 1462994160000, open: 51.085, high: 51.09, low: 51.07, close: 51.0801, volume: 15777, }, { date: 1462994220000, open: 51.09, high: 51.1, low: 51.0824, close: 51.09, volume: 19421, }, { date: 1462994280000, open: 51.095, high: 51.0963, low: 51.06, close: 51.079, volume: 47311, }, { date: 1462994340000, open: 51.07, high: 51.08, low: 51.05, close: 51.06, volume: 32713, }, { date: 1462994400000, open: 51.06, high: 51.06, low: 51.02, close: 51.02, volume: 44195, }, { date: 1462994460000, open: 51.02, high: 51.02, low: 51, close: 51.005, volume: 54514, }, { date: 1462994520000, open: 51, high: 51.02, low: 51, close: 51.015, volume: 37419, }, { date: 1462994580000, open: 51.019, high: 51.04, low: 51.01, close: 51.035, volume: 33428, }, { date: 1462994640000, open: 51.04, high: 51.04, low: 51.02, close: 51.03, volume: 23917, }, { date: 1462994700000, open: 51.03, high: 51.04, low: 51.02, close: 51.025, volume: 38394, }, { date: 1462994760000, open: 51.025, high: 51.04, low: 51.02, close: 51.04, volume: 14882, }, { date: 1462994820000, open: 51.035, high: 51.075, low: 51.035, close: 51.065, volume: 24579, }, { date: 1462994880000, open: 51.07, high: 51.07, low: 51.05, close: 51.06, volume: 44977, }, { date: 1462994940000, open: 51.055, high: 51.09, low: 51.04, close: 51.0831, volume: 77951, }, { date: 1462995000000, open: 51.087, high: 51.09, low: 51.04, close: 51.051, volume: 54677, }, { date: 1462995060000, open: 51.06, high: 51.08, low: 51.04, close: 51.075, volume: 49941, }, { date: 1462995120000, open: 51.075, high: 51.11, low: 51.075, close: 51.1, volume: 54917, }, { date: 1462995180000, open: 51.09, high: 51.1, low: 51.0701, close: 51.1, volume: 23253, }, { date: 1462995240000, open: 51.1, high: 51.125, low: 51.09, close: 51.1101, volume: 74655, }, { date: 1462995300000, open: 51.11, high: 51.125, low: 51.07, close: 51.12, volume: 53045, }, { date: 1462995360000, open: 51.12, high: 51.13, low: 51.08, close: 51.1, volume: 44525, }, { date: 1462995420000, open: 51.11, high: 51.13, low: 51.1059, close: 51.115, volume: 61674, }, { date: 1462995480000, open: 51.11, high: 51.12, low: 51.1, close: 51.12, volume: 32310, }, { date: 1462995540000, open: 51.115, high: 51.1299, low: 51.08, close: 51.082, volume: 80186, }, { date: 1462995600000, open: 51.08, high: 51.0968, low: 51.07, close: 51.0836, volume: 35773, }, { date: 1462995660000, open: 51.09, high: 51.1, low: 51.05, close: 51.05, volume: 60556, }, { date: 1462995720000, open: 51.05, high: 51.09, low: 51.05, close: 51.0859, volume: 127272, }, { date: 1462995780000, open: 51.0899, high: 51.09, low: 51.05, close: 51.065, volume: 61339, }, { date: 1462995840000, open: 51.06, high: 51.07, low: 51.06, close: 51.065, volume: 16803, }, { date: 1462995900000, open: 51.06, high: 51.08, low: 51.05, close: 51.0699, volume: 76338, }, { date: 1462995960000, open: 51.06, high: 51.1, low: 51.055, close: 51.0966, volume: 92175, }, { date: 1462996020000, open: 51.09, high: 51.11, low: 51.07, close: 51.08, volume: 121628, }, { date: 1462996080000, open: 51.08, high: 51.125, low: 51.07, close: 51.1, volume: 70924, }, { date: 1462996140000, open: 51.105, high: 51.12, low: 51.09, close: 51.115, volume: 90710, }, { date: 1462996200000, open: 51.11, high: 51.11, low: 51.075, close: 51.08, volume: 106028, }, { date: 1462996260000, open: 51.07, high: 51.1, low: 51.07, close: 51.0835, volume: 71504, }, { date: 1462996320000, open: 51.0875, high: 51.0875, low: 51.06, close: 51.065, volume: 38845, }, { date: 1462996380000, open: 51.061, high: 51.07, low: 51.045, close: 51.055, volume: 91169, }, { date: 1462996440000, open: 51.06, high: 51.09, low: 51.05, close: 51.06, volume: 80074, }, { date: 1462996500000, open: 51.05, high: 51.075, low: 51.045, close: 51.065, volume: 71004, }, { date: 1462996560000, open: 51.065, high: 51.095, low: 51.06, close: 51.07, volume: 139869, }, { date: 1462996620000, open: 51.07, high: 51.09, low: 51.05, close: 51.085, volume: 152481, }, { date: 1462996680000, open: 51.08, high: 51.1, low: 51.08, close: 51.08, volume: 85893, }, { date: 1462996740000, open: 51.085, high: 51.09, low: 51.07, close: 51.085, volume: 120608, }, { date: 1462996800000, open: 51.09, high: 51.1, low: 51.05, close: 51.05, volume: 3241880, }, { date: 1463059800000, open: 51.2, high: 51.25, low: 51.2, close: 51.22, volume: 295172, }, { date: 1463059860000, open: 51.22, high: 51.34, low: 51.2111, close: 51.29, volume: 117186, }, { date: 1463059920000, open: 51.3, high: 51.37, low: 51.29, close: 51.3399, volume: 73637, }, { date: 1463059980000, open: 51.33, high: 51.3475, low: 51.29, close: 51.32, volume: 42294, }, { date: 1463060040000, open: 51.32, high: 51.32, low: 51.245, close: 51.26, volume: 42686, }, { date: 1463060100000, open: 51.26, high: 51.31, low: 51.255, close: 51.2602, volume: 46212, }, { date: 1463060160000, open: 51.26, high: 51.31, low: 51.26, close: 51.31, volume: 43928, }, { date: 1463060220000, open: 51.3, high: 51.305, low: 51.245, close: 51.28, volume: 122418, }, { date: 1463060280000, open: 51.28, high: 51.34, low: 51.245, close: 51.335, volume: 81854, }, { date: 1463060340000, open: 51.34, high: 51.345, low: 51.27, close: 51.28, volume: 38010, }, { date: 1463060400000, open: 51.27, high: 51.28, low: 51.24, close: 51.26, volume: 93066, }, { date: 1463060460000, open: 51.26, high: 51.27, low: 51.245, close: 51.265, volume: 83269, }, { date: 1463060520000, open: 51.25, high: 51.28, low: 51.24, close: 51.2575, volume: 69405, }, { date: 1463060580000, open: 51.26, high: 51.31, low: 51.245, close: 51.286, volume: 58118, }, { date: 1463060640000, open: 51.29, high: 51.39, low: 51.29, close: 51.359, volume: 60871, }, { date: 1463060700000, open: 51.355, high: 51.38, low: 51.31, close: 51.37, volume: 43437, }, { date: 1463060760000, open: 51.37, high: 51.38, low: 51.3, close: 51.33, volume: 79468, }, { date: 1463060820000, open: 51.325, high: 51.35, low: 51.3, close: 51.335, volume: 83639, }, { date: 1463060880000, open: 51.335, high: 51.34, low: 51.29, close: 51.31, volume: 93975, }, { date: 1463060940000, open: 51.3005, high: 51.33, low: 51.3, close: 51.31, volume: 51365, }, { date: 1463061000000, open: 51.32, high: 51.32, low: 51.29, close: 51.3, volume: 83927, }, { date: 1463061060000, open: 51.308, high: 51.41, low: 51.305, close: 51.4, volume: 85049, }, { date: 1463061120000, open: 51.4, high: 51.48, low: 51.4, close: 51.45, volume: 69097, }, { date: 1463061180000, open: 51.45, high: 51.48, low: 51.45, close: 51.47, volume: 31652, }, { date: 1463061240000, open: 51.475, high: 51.48, low: 51.39, close: 51.39, volume: 71809, }, { date: 1463061300000, open: 51.39, high: 51.39, low: 51.3001, close: 51.3001, volume: 39548, }, { date: 1463061360000, open: 51.305, high: 51.336, low: 51.3, close: 51.325, volume: 50403, }, { date: 1463061420000, open: 51.33, high: 51.34, low: 51.3, close: 51.315, volume: 77320, }, { date: 1463061480000, open: 51.315, high: 51.355, low: 51.31, close: 51.31, volume: 27040, }, { date: 1463061540000, open: 51.3137, high: 51.37, low: 51.305, close: 51.35, volume: 41556, }, { date: 1463061600000, open: 51.35, high: 51.36, low: 51.32, close: 51.34, volume: 86542, }, { date: 1463061660000, open: 51.34, high: 51.35, low: 51.325, close: 51.33, volume: 71238, }, { date: 1463061720000, open: 51.345, high: 51.38, low: 51.33, close: 51.37, volume: 64301, }, { date: 1463061780000, open: 51.37, high: 51.38, low: 51.345, close: 51.3499, volume: 87500, }, { date: 1463061840000, open: 51.345, high: 51.39, low: 51.345, close: 51.3899, volume: 30380, }, { date: 1463061900000, open: 51.385, high: 51.39, low: 51.37, close: 51.37, volume: 40993, }, { date: 1463061960000, open: 51.37, high: 51.38, low: 51.34, close: 51.38, volume: 66083, }, { date: 1463062020000, open: 51.38, high: 51.39, low: 51.37, close: 51.375, volume: 23049, }, { date: 1463062080000, open: 51.38, high: 51.387, low: 51.345, close: 51.37, volume: 38820, }, { date: 1463062140000, open: 51.37, high: 51.39, low: 51.361, close: 51.39, volume: 28428, }, { date: 1463062200000, open: 51.4, high: 51.41, low: 51.35, close: 51.36, volume: 65899, }, { date: 1463062260000, open: 51.35, high: 51.37, low: 51.35, close: 51.36, volume: 26211, }, { date: 1463062320000, open: 51.365, high: 51.37, low: 51.35, close: 51.36, volume: 30542, }, { date: 1463062380000, open: 51.35, high: 51.44, low: 51.35, close: 51.44, volume: 33843, }, { date: 1463062440000, open: 51.43, high: 51.46, low: 51.425, close: 51.449, volume: 59412, }, { date: 1463062500000, open: 51.445, high: 51.4689, low: 51.43, close: 51.46, volume: 33673, }, { date: 1463062560000, open: 51.4566, high: 51.46, low: 51.41, close: 51.41, volume: 30327, }, { date: 1463062620000, open: 51.41, high: 51.47, low: 51.41, close: 51.455, volume: 63967, }, { date: 1463062680000, open: 51.45, high: 51.46, low: 51.41, close: 51.4201, volume: 29059, }, { date: 1463062740000, open: 51.42, high: 51.44, low: 51.39, close: 51.4, volume: 28439, }, { date: 1463062800000, open: 51.39, high: 51.4, low: 51.3636, close: 51.38, volume: 39717, }, { date: 1463062860000, open: 51.375, high: 51.375, low: 51.33, close: 51.365, volume: 48299, }, { date: 1463062920000, open: 51.365, high: 51.42, low: 51.36, close: 51.42, volume: 62449, }, { date: 1463062980000, open: 51.42, high: 51.4201, low: 51.385, close: 51.39, volume: 55809, }, { date: 1463063040000, open: 51.3903, high: 51.43, low: 51.3903, close: 51.43, volume: 41907, }, { date: 1463063100000, open: 51.43, high: 51.44, low: 51.4, close: 51.415, volume: 25595, }, { date: 1463063160000, open: 51.41, high: 51.4195, low: 51.38, close: 51.409, volume: 36861, }, { date: 1463063220000, open: 51.41, high: 51.43, low: 51.41, close: 51.42, volume: 20941, }, { date: 1463063280000, open: 51.425, high: 51.43, low: 51.4, close: 51.43, volume: 30188, }, { date: 1463063340000, open: 51.425, high: 51.449, low: 51.41, close: 51.415, volume: 63283, }, { date: 1463063400000, open: 51.42, high: 51.44, low: 51.405, close: 51.415, volume: 74406, }, { date: 1463063460000, open: 51.4, high: 51.42, low: 51.38, close: 51.405, volume: 91810, }, { date: 1463063520000, open: 51.405, high: 51.45, low: 51.39, close: 51.39, volume: 140352, }, { date: 1463063580000, open: 51.3925, high: 51.42, low: 51.38, close: 51.41, volume: 107207, }, { date: 1463063640000, open: 51.4, high: 51.415, low: 51.39, close: 51.414, volume: 79840, }, { date: 1463063700000, open: 51.42, high: 51.42, low: 51.4, close: 51.419, volume: 41911, }, { date: 1463063760000, open: 51.41, high: 51.42, low: 51.4, close: 51.4, volume: 40643, }, { date: 1463063820000, open: 51.405, high: 51.42, low: 51.4, close: 51.42, volume: 23866, }, { date: 1463063880000, open: 51.42, high: 51.47, low: 51.415, close: 51.45, volume: 42045, }, { date: 1463063940000, open: 51.45, high: 51.49, low: 51.45, close: 51.49, volume: 52200, }, { date: 1463064000000, open: 51.5, high: 51.518, low: 51.49, close: 51.495, volume: 84912, }, { date: 1463064060000, open: 51.49, high: 51.495, low: 51.44, close: 51.445, volume: 63117, }, { date: 1463064120000, open: 51.445, high: 51.46, low: 51.44, close: 51.445, volume: 38645, }, { date: 1463064180000, open: 51.44, high: 51.445, low: 51.4, close: 51.41, volume: 59268, }, { date: 1463064240000, open: 51.416, high: 51.42, low: 51.37, close: 51.401, volume: 123056, }, { date: 1463064300000, open: 51.4, high: 51.42, low: 51.39, close: 51.39, volume: 58594, }, { date: 1463064360000, open: 51.395, high: 51.415, low: 51.38, close: 51.409, volume: 48475, }, { date: 1463064420000, open: 51.405, high: 51.43, low: 51.4, close: 51.43, volume: 39808, }, { date: 1463064480000, open: 51.425, high: 51.425, low: 51.35, close: 51.37, volume: 70851, }, { date: 1463064540000, open: 51.373, high: 51.39, low: 51.345, close: 51.365, volume: 58725, }, { date: 1463064600000, open: 51.37, high: 51.37, low: 51.34, close: 51.3565, volume: 58788, }, { date: 1463064660000, open: 51.3599, high: 51.42, low: 51.345, close: 51.3901, volume: 64170, }, { date: 1463064720000, open: 51.39, high: 51.41, low: 51.37, close: 51.41, volume: 27451, }, { date: 1463064780000, open: 51.41, high: 51.46, low: 51.41, close: 51.4468, volume: 27297, }, { date: 1463064840000, open: 51.45, high: 51.455, low: 51.425, close: 51.43, volume: 35006, }, { date: 1463064900000, open: 51.435, high: 51.49, low: 51.43, close: 51.48, volume: 51617, }, { date: 1463064960000, open: 51.48, high: 51.49, low: 51.44, close: 51.445, volume: 51931, }, { date: 1463065020000, open: 51.45, high: 51.45, low: 51.43, close: 51.44, volume: 20171, }, { date: 1463065080000, open: 51.45, high: 51.45, low: 51.41, close: 51.41, volume: 21558, }, { date: 1463065140000, open: 51.41, high: 51.43, low: 51.385, close: 51.43, volume: 44532, }, { date: 1463065200000, open: 51.43, high: 51.44, low: 51.41, close: 51.41, volume: 68602, }, { date: 1463065260000, open: 51.4001, high: 51.4299, low: 51.36, close: 51.36, volume: 51430, }, { date: 1463065320000, open: 51.37, high: 51.3768, low: 51.345, close: 51.36, volume: 50302, }, { date: 1463065380000, open: 51.35, high: 51.36, low: 51.3, close: 51.31, volume: 109504, }, { date: 1463065440000, open: 51.31, high: 51.32, low: 51.25, close: 51.25, volume: 69782, }, { date: 1463065500000, open: 51.25, high: 51.305, low: 51.23, close: 51.305, volume: 96224, }, { date: 1463065560000, open: 51.3005, high: 51.32, low: 51.29, close: 51.305, volume: 54682, }, { date: 1463065620000, open: 51.305, high: 51.3165, low: 51.27, close: 51.28, volume: 31859, }, { date: 1463065680000, open: 51.29, high: 51.295, low: 51.27, close: 51.29, volume: 21015, }, { date: 1463065740000, open: 51.285, high: 51.3, low: 51.265, close: 51.28, volume: 27272, }, { date: 1463065800000, open: 51.285, high: 51.285, low: 51.24, close: 51.27, volume: 48942, }, { date: 1463065860000, open: 51.26, high: 51.265, low: 51.24, close: 51.25, volume: 28554, }, { date: 1463065920000, open: 51.245, high: 51.255, low: 51.19, close: 51.25, volume: 146878, }, { date: 1463065980000, open: 51.245, high: 51.27, low: 51.24, close: 51.26, volume: 22029, }, { date: 1463066040000, open: 51.27, high: 51.29, low: 51.26, close: 51.269, volume: 70747, }, { date: 1463066100000, open: 51.27, high: 51.275, low: 51.21, close: 51.22, volume: 46276, }, { date: 1463066160000, open: 51.22, high: 51.2201, low: 51.15, close: 51.1538, volume: 70167, }, { date: 1463066220000, open: 51.15, high: 51.17, low: 51.1, close: 51.105, volume: 96308, }, { date: 1463066280000, open: 51.1, high: 51.13, low: 51.08, close: 51.08, volume: 313718, }, { date: 1463066340000, open: 51.085, high: 51.11, low: 51.08, close: 51.11, volume: 26890, }, { date: 1463066400000, open: 51.11, high: 51.14, low: 51.1, close: 51.13, volume: 38508, }, { date: 1463066460000, open: 51.13, high: 51.155, low: 51.11, close: 51.155, volume: 30701, }, { date: 1463066520000, open: 51.16, high: 51.165, low: 51.15, close: 51.16, volume: 32020, }, { date: 1463066580000, open: 51.15, high: 51.16, low: 51.095, close: 51.0999, volume: 30270, }, { date: 1463066640000, open: 51.1, high: 51.105, low: 51.065, close: 51.1, volume: 45377, }, { date: 1463066700000, open: 51.105, high: 51.11, low: 51.075, close: 51.08, volume: 24542, }, { date: 1463066760000, open: 51.09, high: 51.09, low: 51.05, close: 51.07, volume: 51698, }, { date: 1463066820000, open: 51.07, high: 51.1, low: 51.07, close: 51.07, volume: 28979, }, { date: 1463066880000, open: 51.07, high: 51.1, low: 51.06, close: 51.09, volume: 59701, }, { date: 1463066940000, open: 51.08, high: 51.08, low: 51.06, close: 51.06, volume: 25975, }, { date: 1463067000000, open: 51.07, high: 51.07, low: 51.03, close: 51.06, volume: 40446, }, { date: 1463067060000, open: 51.0501, high: 51.07, low: 51.0489, close: 51.07, volume: 21763, }, { date: 1463067120000, open: 51.08, high: 51.08, low: 51.06, close: 51.07, volume: 39571, }, { date: 1463067180000, open: 51.07, high: 51.09, low: 51.06, close: 51.06, volume: 40453, }, { date: 1463067240000, open: 51.06, high: 51.07, low: 51.02, close: 51.02, volume: 28871, }, { date: 1463067300000, open: 51.025, high: 51.04, low: 51.02, close: 51.036, volume: 32088, }, { date: 1463067360000, open: 51.035, high: 51.0499, low: 51.02, close: 51.035, volume: 41142, }, { date: 1463067420000, open: 51.0399, high: 51.04, low: 51.025, close: 51.0301, volume: 27122, }, { date: 1463067480000, open: 51.035, high: 51.05, low: 51.0238, close: 51.0238, volume: 46035, }, { date: 1463067540000, open: 51.02, high: 51.03, low: 51, close: 51.005, volume: 38308, }, { date: 1463067600000, open: 51.005, high: 51.015, low: 51, close: 51.005, volume: 15105, }, { date: 1463067660000, open: 51.01, high: 51.01, low: 51, close: 51.001, volume: 16505, }, { date: 1463067720000, open: 51.0064, high: 51.03, low: 51, close: 51.009, volume: 35300, }, { date: 1463067780000, open: 51.005, high: 51.01, low: 51, close: 51, volume: 16646, }, { date: 1463067840000, open: 51, high: 51.0305, low: 51, close: 51.028, volume: 65008, }, { date: 1463067900000, open: 51.03, high: 51.05, low: 51.015, close: 51.02, volume: 38601, }, { date: 1463067960000, open: 51.012, high: 51.03, low: 50.99, close: 51.03, volume: 38000, }, { date: 1463068020000, open: 51.03, high: 51.1, low: 51.03, close: 51.06, volume: 24471, }, { date: 1463068080000, open: 51.07, high: 51.0796, low: 51.06, close: 51.0699, volume: 21339, }, { date: 1463068140000, open: 51.07, high: 51.07, low: 51.03, close: 51.035, volume: 33570, }, { date: 1463068200000, open: 51.035, high: 51.08, low: 51.02, close: 51.07, volume: 35255, }, { date: 1463068260000, open: 51.065, high: 51.08, low: 51.055, close: 51.07, volume: 14634, }, { date: 1463068320000, open: 51.08, high: 51.09, low: 51.05, close: 51.06, volume: 32566, }, { date: 1463068380000, open: 51.055, high: 51.06, low: 51.03, close: 51.06, volume: 40120, }, { date: 1463068440000, open: 51.055, high: 51.075, low: 51.035, close: 51.075, volume: 23710, }, { date: 1463068500000, open: 51.07, high: 51.075, low: 51.02, close: 51.0299, volume: 32234, }, { date: 1463068560000, open: 51.02, high: 51.04, low: 51.01, close: 51.04, volume: 25771, }, { date: 1463068620000, open: 51.04, high: 51.055, low: 51.02, close: 51.04, volume: 31916, }, { date: 1463068680000, open: 51.035, high: 51.06, low: 51.02, close: 51.02, volume: 25021, }, { date: 1463068740000, open: 51.02, high: 51.02, low: 50.9901, close: 50.9901, volume: 43172, }, { date: 1463068800000, open: 51, high: 51.02, low: 50.98, close: 51.01, volume: 62734, }, { date: 1463068860000, open: 51.01, high: 51.015, low: 50.95, close: 50.95, volume: 61633, }, { date: 1463068920000, open: 50.95, high: 50.96, low: 50.92, close: 50.935, volume: 23552, }, { date: 1463068980000, open: 50.935, high: 51.01, low: 50.935, close: 51.01, volume: 29912, }, { date: 1463069040000, open: 51.01, high: 51.06, low: 51.005, close: 51.01, volume: 156778, }, { date: 1463069100000, open: 51, high: 51.035, low: 50.99, close: 51.029, volume: 44655, }, { date: 1463069160000, open: 51.021, high: 51.06, low: 51.02, close: 51.055, volume: 15865, }, { date: 1463069220000, open: 51.06, high: 51.07, low: 51.05, close: 51.06, volume: 18389, }, { date: 1463069280000, open: 51.0529, high: 51.07, low: 51.05, close: 51.06, volume: 16952, }, { date: 1463069340000, open: 51.066, high: 51.09, low: 51.05, close: 51.0865, volume: 12088, }, { date: 1463069400000, open: 51.09, high: 51.11, low: 51.08, close: 51.1, volume: 83487, }, { date: 1463069460000, open: 51.1, high: 51.11, low: 51.06, close: 51.06, volume: 58433, }, { date: 1463069520000, open: 51.068, high: 51.09, low: 51.06, close: 51.08, volume: 35712, }, { date: 1463069580000, open: 51.09, high: 51.11, low: 51.08, close: 51.105, volume: 36838, }, { date: 1463069640000, open: 51.1, high: 51.11, low: 51.095, close: 51.1, volume: 44541, }, { date: 1463069700000, open: 51.1, high: 51.11, low: 51.1, close: 51.1089, volume: 15590, }, { date: 1463069760000, open: 51.105, high: 51.11, low: 51.075, close: 51.08, volume: 28688, }, { date: 1463069820000, open: 51.075, high: 51.08, low: 51.03, close: 51.075, volume: 23771, }, { date: 1463069880000, open: 51.08, high: 51.11, low: 51.075, close: 51.11, volume: 51237, }, { date: 1463069940000, open: 51.11, high: 51.11, low: 51.08, close: 51.08, volume: 36660, }, { date: 1463070000000, open: 51.09, high: 51.1, low: 51.06, close: 51.09, volume: 19230, }, { date: 1463070060000, open: 51.09, high: 51.1, low: 51.07, close: 51.1, volume: 16089, }, { date: 1463070120000, open: 51.11, high: 51.11, low: 51.075, close: 51.08, volume: 13913, }, { date: 1463070180000, open: 51.085, high: 51.11, low: 51.085, close: 51.105, volume: 12312, }, { date: 1463070240000, open: 51.11, high: 51.115, low: 51.08, close: 51.09, volume: 22535, }, { date: 1463070300000, open: 51.09, high: 51.09, low: 51.08, close: 51.08, volume: 3149, }, { date: 1463070360000, open: 51.089, high: 51.09, low: 51.065, close: 51.0801, volume: 34357, }, { date: 1463070420000, open: 51.085, high: 51.1, low: 51.0657, close: 51.07, volume: 24537, }, { date: 1463070480000, open: 51.07, high: 51.09, low: 51.065, close: 51.07, volume: 19391, }, { date: 1463070540000, open: 51.07, high: 51.07, low: 51.05, close: 51.07, volume: 13566, }, { date: 1463070600000, open: 51.07, high: 51.08, low: 51.065, close: 51.07, volume: 14083, }, { date: 1463070660000, open: 51.0603, high: 51.0789, low: 51.06, close: 51.07, volume: 24215, }, { date: 1463070720000, open: 51.0665, high: 51.07, low: 51.05, close: 51.0601, volume: 32903, }, { date: 1463070780000, open: 51.0664, high: 51.0765, low: 51.06, close: 51.07, volume: 29092, }, { date: 1463070840000, open: 51.06, high: 51.08, low: 51.059, close: 51.07, volume: 22940, }, { date: 1463070900000, open: 51.07, high: 51.09, low: 51.0632, close: 51.065, volume: 30724, }, { date: 1463070960000, open: 51.07, high: 51.07, low: 51.02, close: 51.04, volume: 35365, }, { date: 1463071020000, open: 51.03, high: 51.0365, low: 50.95, close: 50.97, volume: 41918, }, { date: 1463071080000, open: 50.975, high: 50.99, low: 50.975, close: 50.99, volume: 28614, }, { date: 1463071140000, open: 50.99, high: 51.01, low: 50.98, close: 50.98, volume: 20741, }, { date: 1463071200000, open: 50.98, high: 51, low: 50.975, close: 50.995, volume: 17520, }, { date: 1463071260000, open: 50.995, high: 51, low: 50.97, close: 50.99, volume: 21204, }, { date: 1463071320000, open: 50.99, high: 51.02, low: 50.9838, close: 51.01, volume: 10766, }, { date: 1463071380000, open: 51.011, high: 51.05, low: 51.011, close: 51.05, volume: 24052, }, { date: 1463071440000, open: 51.045, high: 51.08, low: 51.0401, close: 51.06, volume: 13796, }, { date: 1463071500000, open: 51.07, high: 51.075, low: 51.06, close: 51.06, volume: 12212, }, { date: 1463071560000, open: 51.06, high: 51.1, low: 51.06, close: 51.09, volume: 17564, }, { date: 1463071620000, open: 51.08, high: 51.1, low: 51.08, close: 51.1, volume: 3870, }, { date: 1463071680000, open: 51.1, high: 51.15, low: 51.1, close: 51.1399, volume: 44472, }, { date: 1463071740000, open: 51.13, high: 51.14, low: 51.12, close: 51.1399, volume: 15947, }, { date: 1463071800000, open: 51.14, high: 51.15, low: 51.13, close: 51.15, volume: 11892, }, { date: 1463071860000, open: 51.14, high: 51.15, low: 51.12, close: 51.14, volume: 21067, }, { date: 1463071920000, open: 51.1347, high: 51.15, low: 51.1347, close: 51.1499, volume: 9325, }, { date: 1463071980000, open: 51.15, high: 51.17, low: 51.1483, close: 51.15, volume: 44133, }, { date: 1463072040000, open: 51.16, high: 51.17, low: 51.15, close: 51.155, volume: 32032, }, { date: 1463072100000, open: 51.15, high: 51.16, low: 51.1331, close: 51.14, volume: 15216, }, { date: 1463072160000, open: 51.15, high: 51.19, low: 51.15, close: 51.1735, volume: 29640, }, { date: 1463072220000, open: 51.1762, high: 51.19, low: 51.16, close: 51.165, volume: 24613, }, { date: 1463072280000, open: 51.17, high: 51.18, low: 51.16, close: 51.1607, volume: 16663, }, { date: 1463072340000, open: 51.169, high: 51.18, low: 51.16, close: 51.17, volume: 12383, }, { date: 1463072400000, open: 51.18, high: 51.21, low: 51.17, close: 51.2068, volume: 22684, }, { date: 1463072460000, open: 51.2071, high: 51.22, low: 51.19, close: 51.2, volume: 31924, }, { date: 1463072520000, open: 51.21, high: 51.21, low: 51.17, close: 51.17, volume: 19517, }, { date: 1463072580000, open: 51.18, high: 51.18, low: 51.16, close: 51.166, volume: 23302, }, { date: 1463072640000, open: 51.1699, high: 51.19, low: 51.1699, close: 51.19, volume: 24697, }, { date: 1463072700000, open: 51.19, high: 51.19, low: 51.1666, close: 51.175, volume: 14270, }, { date: 1463072760000, open: 51.18, high: 51.19, low: 51.16, close: 51.16, volume: 16043, }, { date: 1463072820000, open: 51.161, high: 51.185, low: 51.161, close: 51.18, volume: 20613, }, { date: 1463072880000, open: 51.185, high: 51.19, low: 51.17, close: 51.18, volume: 8078, }, { date: 1463072940000, open: 51.189, high: 51.2, low: 51.18, close: 51.1983, volume: 18271, }, { date: 1463073000000, open: 51.2, high: 51.2, low: 51.16, close: 51.16, volume: 17096, }, { date: 1463073060000, open: 51.16, high: 51.16, low: 51.1204, close: 51.1204, volume: 36265, }, { date: 1463073120000, open: 51.13, high: 51.145, low: 51.12, close: 51.145, volume: 20300, }, { date: 1463073180000, open: 51.14, high: 51.14, low: 51.11, close: 51.12, volume: 17628, }, { date: 1463073240000, open: 51.12, high: 51.13, low: 51.11, close: 51.1236, volume: 19046, }, { date: 1463073300000, open: 51.13, high: 51.15, low: 51.12, close: 51.13, volume: 22894, }, { date: 1463073360000, open: 51.1399, high: 51.15, low: 51.13, close: 51.14, volume: 13146, }, { date: 1463073420000, open: 51.14, high: 51.15, low: 51.125, close: 51.14, volume: 14327, }, { date: 1463073480000, open: 51.14, high: 51.15, low: 51.13, close: 51.1332, volume: 17921, }, { date: 1463073540000, open: 51.14, high: 51.17, low: 51.14, close: 51.15, volume: 30490, }, { date: 1463073600000, open: 51.155, high: 51.1569, low: 51.13, close: 51.13, volume: 6270, }, { date: 1463073660000, open: 51.135, high: 51.15, low: 51.11, close: 51.1299, volume: 23803, }, { date: 1463073720000, open: 51.1231, high: 51.145, low: 51.12, close: 51.13, volume: 15084, }, { date: 1463073780000, open: 51.13, high: 51.16, low: 51.1201, close: 51.16, volume: 29349, }, { date: 1463073840000, open: 51.16, high: 51.215, low: 51.16, close: 51.21, volume: 95922, }, { date: 1463073900000, open: 51.21, high: 51.22, low: 51.2, close: 51.215, volume: 19646, }, { date: 1463073960000, open: 51.2199, high: 51.22, low: 51.21, close: 51.21, volume: 8615, }, { date: 1463074020000, open: 51.2101, high: 51.22, low: 51.19, close: 51.19, volume: 9416, }, { date: 1463074080000, open: 51.195, high: 51.2, low: 51.18, close: 51.2, volume: 14362, }, { date: 1463074140000, open: 51.2, high: 51.22, low: 51.2, close: 51.2185, volume: 21892, }, { date: 1463074200000, open: 51.21, high: 51.21, low: 51.19, close: 51.21, volume: 32849, }, { date: 1463074260000, open: 51.21, high: 51.21, low: 51.15, close: 51.17, volume: 42924, }, { date: 1463074320000, open: 51.17, high: 51.2, low: 51.168, close: 51.19, volume: 20849, }, { date: 1463074380000, open: 51.1916, high: 51.2, low: 51.16, close: 51.185, volume: 34939, }, { date: 1463074440000, open: 51.18, high: 51.205, low: 51.18, close: 51.2, volume: 43853, }, { date: 1463074500000, open: 51.2, high: 51.22, low: 51.19, close: 51.195, volume: 42210, }, { date: 1463074560000, open: 51.195, high: 51.2, low: 51.1438, close: 51.195, volume: 124744, }, { date: 1463074620000, open: 51.19, high: 51.2, low: 51.16, close: 51.16, volume: 61460, }, { date: 1463074680000, open: 51.16, high: 51.2, low: 51.149, close: 51.19, volume: 51106, }, { date: 1463074740000, open: 51.195, high: 51.21, low: 51.18, close: 51.201, volume: 15831, }, { date: 1463074800000, open: 51.2, high: 51.22, low: 51.183, close: 51.205, volume: 38321, }, { date: 1463074860000, open: 51.206, high: 51.23, low: 51.2, close: 51.22, volume: 23113, }, { date: 1463074920000, open: 51.22, high: 51.25, low: 51.21, close: 51.25, volume: 25281, }, { date: 1463074980000, open: 51.25, high: 51.29, low: 51.2402, close: 51.29, volume: 79699, }, { date: 1463075040000, open: 51.2893, high: 51.29, low: 51.25, close: 51.26, volume: 39541, }, { date: 1463075100000, open: 51.26, high: 51.37, low: 51.26, close: 51.34, volume: 164977, }, { date: 1463075160000, open: 51.335, high: 51.36, low: 51.33, close: 51.36, volume: 13810, }, { date: 1463075220000, open: 51.36, high: 51.37, low: 51.34, close: 51.355, volume: 28895, }, { date: 1463075280000, open: 51.355, high: 51.36, low: 51.34, close: 51.36, volume: 20866, }, { date: 1463075340000, open: 51.355, high: 51.37, low: 51.35, close: 51.36, volume: 40364, }, { date: 1463075400000, open: 51.36, high: 51.39, low: 51.32, close: 51.3366, volume: 47495, }, { date: 1463075460000, open: 51.34, high: 51.35, low: 51.32, close: 51.35, volume: 19293, }, { date: 1463075520000, open: 51.3425, high: 51.37, low: 51.34, close: 51.37, volume: 23958, }, { date: 1463075580000, open: 51.361, high: 51.365, low: 51.34, close: 51.3411, volume: 17280, }, { date: 1463075640000, open: 51.35, high: 51.35, low: 51.32, close: 51.35, volume: 20879, }, { date: 1463075700000, open: 51.35, high: 51.36, low: 51.35, close: 51.36, volume: 9117, }, { date: 1463075760000, open: 51.36, high: 51.38, low: 51.35, close: 51.355, volume: 35144, }, { date: 1463075820000, open: 51.35, high: 51.3882, low: 51.35, close: 51.375, volume: 45925, }, { date: 1463075880000, open: 51.37, high: 51.375, low: 51.34, close: 51.3599, volume: 23132, }, { date: 1463075940000, open: 51.357, high: 51.37, low: 51.35, close: 51.365, volume: 42078, }, { date: 1463076000000, open: 51.37, high: 51.44, low: 51.36, close: 51.44, volume: 58586, }, { date: 1463076060000, open: 51.44, high: 51.47, low: 51.43, close: 51.44, volume: 56434, }, { date: 1463076120000, open: 51.435, high: 51.5, low: 51.435, close: 51.485, volume: 86806, }, { date: 1463076180000, open: 51.4862, high: 51.52, low: 51.48, close: 51.5195, volume: 81304, }, { date: 1463076240000, open: 51.5189, high: 51.53, low: 51.51, close: 51.52, volume: 43465, }, { date: 1463076300000, open: 51.51, high: 51.54, low: 51.51, close: 51.535, volume: 35466, }, { date: 1463076360000, open: 51.535, high: 51.565, low: 51.52, close: 51.56, volume: 45007, }, { date: 1463076420000, open: 51.5601, high: 51.58, low: 51.53, close: 51.56, volume: 86152, }, { date: 1463076480000, open: 51.57, high: 51.57, low: 51.51, close: 51.53, volume: 30425, }, { date: 1463076540000, open: 51.53, high: 51.54, low: 51.53, close: 51.54, volume: 11220, }, { date: 1463076600000, open: 51.535, high: 51.54, low: 51.5, close: 51.505, volume: 28364, }, { date: 1463076660000, open: 51.52, high: 51.555, low: 51.51, close: 51.55, volume: 31650, }, { date: 1463076720000, open: 51.545, high: 51.57, low: 51.54, close: 51.55, volume: 28662, }, { date: 1463076780000, open: 51.55, high: 51.6, low: 51.55, close: 51.59, volume: 62892, }, { date: 1463076840000, open: 51.585, high: 51.65, low: 51.58, close: 51.63, volume: 72097, }, { date: 1463076900000, open: 51.63, high: 51.69, low: 51.63, close: 51.655, volume: 57782, }, { date: 1463076960000, open: 51.65, high: 51.67, low: 51.65, close: 51.6599, volume: 30906, }, { date: 1463077020000, open: 51.65, high: 51.655, low: 51.61, close: 51.62, volume: 20248, }, { date: 1463077080000, open: 51.62, high: 51.66, low: 51.62, close: 51.645, volume: 87353, }, { date: 1463077140000, open: 51.6499, high: 51.67, low: 51.64, close: 51.665, volume: 40046, }, { date: 1463077200000, open: 51.6601, high: 51.675, low: 51.655, close: 51.67, volume: 19689, }, { date: 1463077260000, open: 51.67, high: 51.75, low: 51.67, close: 51.71, volume: 204155, }, { date: 1463077320000, open: 51.7101, high: 51.719, low: 51.66, close: 51.69, volume: 62764, }, { date: 1463077380000, open: 51.69, high: 51.705, low: 51.65, close: 51.66, volume: 47260, }, { date: 1463077440000, open: 51.655, high: 51.655, low: 51.61, close: 51.61, volume: 75177, }, { date: 1463077500000, open: 51.61, high: 51.625, low: 51.59, close: 51.62, volume: 41098, }, { date: 1463077560000, open: 51.62, high: 51.65, low: 51.615, close: 51.64, volume: 50607, }, { date: 1463077620000, open: 51.65, high: 51.675, low: 51.64, close: 51.651, volume: 41383, }, { date: 1463077680000, open: 51.6501, high: 51.68, low: 51.65, close: 51.67, volume: 48182, }, { date: 1463077740000, open: 51.68, high: 51.69, low: 51.665, close: 51.69, volume: 65543, }, { date: 1463077800000, open: 51.69, high: 51.69, low: 51.67, close: 51.685, volume: 47054, }, { date: 1463077860000, open: 51.675, high: 51.68, low: 51.63, close: 51.63, volume: 70881, }, { date: 1463077920000, open: 51.635, high: 51.66, low: 51.63, close: 51.64, volume: 34567, }, { date: 1463077980000, open: 51.63, high: 51.635, low: 51.61, close: 51.6201, volume: 24108, }, { date: 1463078040000, open: 51.62, high: 51.63, low: 51.6, close: 51.61, volume: 36868, }, { date: 1463078100000, open: 51.61, high: 51.64, low: 51.6, close: 51.633, volume: 30557, }, { date: 1463078160000, open: 51.64, high: 51.66, low: 51.63, close: 51.65, volume: 19909, }, { date: 1463078220000, open: 51.645, high: 51.65, low: 51.62, close: 51.62, volume: 21794, }, { date: 1463078280000, open: 51.62, high: 51.65, low: 51.62, close: 51.64, volume: 28334, }, { date: 1463078340000, open: 51.64, high: 51.66, low: 51.62, close: 51.62, volume: 79905, }, { date: 1463078400000, open: 51.625, high: 51.64, low: 51.6, close: 51.605, volume: 33341, }, { date: 1463078460000, open: 51.6, high: 51.61, low: 51.6, close: 51.6, volume: 16938, }, { date: 1463078520000, open: 51.6001, high: 51.62, low: 51.6, close: 51.62, volume: 25713, }, { date: 1463078580000, open: 51.62, high: 51.62, low: 51.6, close: 51.6, volume: 38875, }, { date: 1463078640000, open: 51.6, high: 51.6, low: 51.57, close: 51.6, volume: 29713, }, { date: 1463078700000, open: 51.5999, high: 51.61, low: 51.59, close: 51.6, volume: 12039, }, { date: 1463078760000, open: 51.6, high: 51.64, low: 51.585, close: 51.64, volume: 65286, }, { date: 1463078820000, open: 51.64, high: 51.69, low: 51.64, close: 51.69, volume: 103868, }, { date: 1463078880000, open: 51.69, high: 51.75, low: 51.68, close: 51.74, volume: 101669, }, { date: 1463078940000, open: 51.745, high: 51.805, low: 51.73, close: 51.8, volume: 161312, }, { date: 1463079000000, open: 51.8, high: 51.81, low: 51.78, close: 51.79, volume: 73971, }, { date: 1463079060000, open: 51.7905, high: 51.8, low: 51.7701, close: 51.7732, volume: 32884, }, { date: 1463079120000, open: 51.77, high: 51.8, low: 51.77, close: 51.8, volume: 53162, }, { date: 1463079180000, open: 51.7956, high: 51.8, low: 51.78, close: 51.79, volume: 51491, }, { date: 1463079240000, open: 51.7988, high: 51.8, low: 51.77, close: 51.7731, volume: 38769, }, { date: 1463079300000, open: 51.775, high: 51.79, low: 51.75, close: 51.75, volume: 49482, }, { date: 1463079360000, open: 51.74, high: 51.76, low: 51.73, close: 51.73, volume: 72088, }, { date: 1463079420000, open: 51.7385, high: 51.74, low: 51.71, close: 51.72, volume: 74879, }, { date: 1463079480000, open: 51.72, high: 51.75, low: 51.72, close: 51.729, volume: 56524, }, { date: 1463079540000, open: 51.72, high: 51.725, low: 51.67, close: 51.6985, volume: 70470, }, { date: 1463079600000, open: 51.6954, high: 51.73, low: 51.69, close: 51.695, volume: 31242, }, { date: 1463079660000, open: 51.69, high: 51.7, low: 51.66, close: 51.66, volume: 43999, }, { date: 1463079720000, open: 51.6645, high: 51.7, low: 51.66, close: 51.695, volume: 45219, }, { date: 1463079780000, open: 51.69, high: 51.69, low: 51.65, close: 51.6523, volume: 36893, }, { date: 1463079840000, open: 51.65, high: 51.68, low: 51.64, close: 51.68, volume: 60713, }, { date: 1463079900000, open: 51.67, high: 51.68, low: 51.64, close: 51.655, volume: 44439, }, { date: 1463079960000, open: 51.65, high: 51.66, low: 51.635, close: 51.64, volume: 54861, }, { date: 1463080020000, open: 51.64, high: 51.665, low: 51.625, close: 51.625, volume: 54219, }, { date: 1463080080000, open: 51.625, high: 51.65, low: 51.62, close: 51.63, volume: 54660, }, { date: 1463080140000, open: 51.635, high: 51.67, low: 51.62, close: 51.62, volume: 62518, }, { date: 1463080200000, open: 51.625, high: 51.65, low: 51.62, close: 51.63, volume: 61080, }, { date: 1463080260000, open: 51.63, high: 51.67, low: 51.6235, close: 51.655, volume: 69979, }, { date: 1463080320000, open: 51.655, high: 51.665, low: 51.64, close: 51.65, volume: 43366, }, { date: 1463080380000, open: 51.65, high: 51.67, low: 51.64, close: 51.665, volume: 75827, }, { date: 1463080440000, open: 51.66, high: 51.68, low: 51.66, close: 51.68, volume: 36924, }, { date: 1463080500000, open: 51.68, high: 51.695, low: 51.6628, close: 51.67, volume: 53495, }, { date: 1463080560000, open: 51.68, high: 51.685, low: 51.6628, close: 51.68, volume: 42412, }, { date: 1463080620000, open: 51.68, high: 51.69, low: 51.6601, close: 51.6601, volume: 36471, }, { date: 1463080680000, open: 51.6656, high: 51.68, low: 51.65, close: 51.6799, volume: 46724, }, { date: 1463080740000, open: 51.67, high: 51.675, low: 51.63, close: 51.6585, volume: 44332, }, { date: 1463080800000, open: 51.6538, high: 51.68, low: 51.62, close: 51.64, volume: 40762, }, { date: 1463080860000, open: 51.635, high: 51.635, low: 51.61, close: 51.615, volume: 41004, }, { date: 1463080920000, open: 51.625, high: 51.63, low: 51.595, close: 51.6, volume: 58650, }, { date: 1463080980000, open: 51.595, high: 51.6183, low: 51.59, close: 51.59, volume: 54229, }, { date: 1463081040000, open: 51.595, high: 51.62, low: 51.59, close: 51.595, volume: 54413, }, { date: 1463081100000, open: 51.595, high: 51.6, low: 51.57, close: 51.575, volume: 31251, }, { date: 1463081160000, open: 51.57, high: 51.59, low: 51.57, close: 51.58, volume: 35317, }, { date: 1463081220000, open: 51.585, high: 51.59, low: 51.54, close: 51.562, volume: 103684, }, { date: 1463081280000, open: 51.5662, high: 51.58, low: 51.56, close: 51.56, volume: 44932, }, { date: 1463081340000, open: 51.56, high: 51.63, low: 51.56, close: 51.62, volume: 94396, }, { date: 1463081400000, open: 51.625, high: 51.655, low: 51.62, close: 51.655, volume: 81461, }, { date: 1463081460000, open: 51.65, high: 51.67, low: 51.63, close: 51.645, volume: 99455, }, { date: 1463081520000, open: 51.64, high: 51.66, low: 51.61, close: 51.625, volume: 57301, }, { date: 1463081580000, open: 51.62, high: 51.63, low: 51.57, close: 51.58, volume: 57835, }, { date: 1463081640000, open: 51.585, high: 51.61, low: 51.56, close: 51.5939, volume: 62110, }, { date: 1463081700000, open: 51.599, high: 51.62, low: 51.56, close: 51.58, volume: 58379, }, { date: 1463081760000, open: 51.5799, high: 51.59, low: 51.5401, close: 51.575, volume: 57100, }, { date: 1463081820000, open: 51.575, high: 51.595, low: 51.56, close: 51.595, volume: 48350, }, { date: 1463081880000, open: 51.595, high: 51.63, low: 51.59, close: 51.61, volume: 48797, }, { date: 1463081940000, open: 51.6161, high: 51.63, low: 51.6, close: 51.601, volume: 47082, }, { date: 1463082000000, open: 51.6, high: 51.63, low: 51.595, close: 51.61, volume: 64393, }, { date: 1463082060000, open: 51.605, high: 51.66, low: 51.605, close: 51.635, volume: 86960, }, { date: 1463082120000, open: 51.64, high: 51.68, low: 51.63, close: 51.68, volume: 97364, }, { date: 1463082180000, open: 51.68, high: 51.685, low: 51.65, close: 51.66, volume: 90786, }, { date: 1463082240000, open: 51.66, high: 51.68, low: 51.655, close: 51.665, volume: 60231, }, { date: 1463082300000, open: 51.6656, high: 51.69, low: 51.66, close: 51.67, volume: 79752, }, { date: 1463082360000, open: 51.665, high: 51.69, low: 51.655, close: 51.67, volume: 130331, }, { date: 1463082420000, open: 51.665, high: 51.68, low: 51.63, close: 51.66, volume: 119327, }, { date: 1463082480000, open: 51.655, high: 51.68, low: 51.63, close: 51.639, volume: 112467, }, { date: 1463082540000, open: 51.635, high: 51.65, low: 51.62, close: 51.625, volume: 93474, }, { date: 1463082600000, open: 51.62, high: 51.625, low: 51.55, close: 51.565, volume: 260352, }, { date: 1463082660000, open: 51.565, high: 51.57, low: 51.5, close: 51.5, volume: 346841, }, { date: 1463082720000, open: 51.505, high: 51.51, low: 51.49, close: 51.5008, volume: 180301, }, { date: 1463082780000, open: 51.505, high: 51.51, low: 51.4801, close: 51.495, volume: 85201, }, { date: 1463082840000, open: 51.49, high: 51.5, low: 51.49, close: 51.4975, volume: 57895, }, { date: 1463082900000, open: 51.49, high: 51.49, low: 51.47, close: 51.48, volume: 184418, }, { date: 1463082960000, open: 51.485, high: 51.53, low: 51.48, close: 51.52, volume: 197317, }, { date: 1463083020000, open: 51.515, high: 51.53, low: 51.47, close: 51.475, volume: 159858, }, { date: 1463083080000, open: 51.47, high: 51.54, low: 51.47, close: 51.53, volume: 310679, }, { date: 1463083140000, open: 51.525, high: 51.53, low: 51.49, close: 51.5, volume: 183691, }, { date: 1463083200000, open: 51.495, high: 51.52, low: 51.4425, close: 51.51, volume: 2261125, }, { date: 1463146200000, open: 51.44, high: 51.48, low: 51.39, close: 51.41, volume: 358417, }, { date: 1463146260000, open: 51.44, high: 51.56, low: 51.38, close: 51.48, volume: 173745, }, { date: 1463146320000, open: 51.48, high: 51.56, low: 51.48, close: 51.54, volume: 39855, }, { date: 1463146380000, open: 51.53, high: 51.5956, low: 51.51, close: 51.59, volume: 39587, }, { date: 1463146440000, open: 51.6, high: 51.64, low: 51.53, close: 51.54, volume: 49117, }, { date: 1463146500000, open: 51.55, high: 51.6, low: 51.54, close: 51.54, volume: 30453, }, { date: 1463146560000, open: 51.55, high: 51.57, low: 51.51, close: 51.51, volume: 47651, }, { date: 1463146620000, open: 51.51, high: 51.58, low: 51.49, close: 51.56, volume: 34823, }, { date: 1463146680000, open: 51.56, high: 51.6364, low: 51.55, close: 51.6064, volume: 48134, }, { date: 1463146740000, open: 51.6, high: 51.68, low: 51.595, close: 51.67, volume: 58634, }, { date: 1463146800000, open: 51.67, high: 51.73, low: 51.625, close: 51.69, volume: 84494, }, { date: 1463146860000, open: 51.695, high: 51.74, low: 51.66, close: 51.735, volume: 75406, }, { date: 1463146920000, open: 51.74, high: 51.79, low: 51.72, close: 51.77, volume: 113677, }, { date: 1463146980000, open: 51.76, high: 51.805, low: 51.75, close: 51.8, volume: 86480, }, { date: 1463147040000, open: 51.81, high: 51.82, low: 51.74, close: 51.755, volume: 70666, }, { date: 1463147100000, open: 51.75, high: 51.77, low: 51.71, close: 51.75, volume: 104348, }, { date: 1463147160000, open: 51.75, high: 51.82, low: 51.74, close: 51.79, volume: 48206, }, { date: 1463147220000, open: 51.79, high: 51.83, low: 51.775, close: 51.81, volume: 48751, }, { date: 1463147280000, open: 51.81, high: 51.82, low: 51.77, close: 51.78, volume: 56460, }, { date: 1463147340000, open: 51.78, high: 51.83, low: 51.78, close: 51.7829, volume: 50746, }, { date: 1463147400000, open: 51.79, high: 51.82, low: 51.76, close: 51.82, volume: 54003, }, { date: 1463147460000, open: 51.8211, high: 51.83, low: 51.78, close: 51.81, volume: 42009, }, { date: 1463147520000, open: 51.82, high: 51.825, low: 51.8, close: 51.82, volume: 41302, }, { date: 1463147580000, open: 51.8099, high: 51.81, low: 51.68, close: 51.6864, volume: 59434, }, { date: 1463147640000, open: 51.6899, high: 51.75, low: 51.66, close: 51.75, volume: 57969, }, { date: 1463147700000, open: 51.755, high: 51.8, low: 51.74, close: 51.79, volume: 64674, }, { date: 1463147760000, open: 51.78, high: 51.86, low: 51.775, close: 51.85, volume: 76215, }, { date: 1463147820000, open: 51.8493, high: 51.8493, low: 51.78, close: 51.8099, volume: 51749, }, { date: 1463147880000, open: 51.806, high: 51.84, low: 51.8, close: 51.8101, volume: 47441, }, { date: 1463147940000, open: 51.81, high: 51.81, low: 51.75, close: 51.76, volume: 29929, }, { date: 1463148000000, open: 51.76, high: 51.78, low: 51.71, close: 51.78, volume: 42412, }, { date: 1463148060000, open: 51.78, high: 51.81, low: 51.68, close: 51.73, volume: 60736, }, { date: 1463148120000, open: 51.73, high: 51.79, low: 51.73, close: 51.78, volume: 34461, }, { date: 1463148180000, open: 51.77, high: 51.81, low: 51.72, close: 51.8, volume: 74024, }, { date: 1463148240000, open: 51.8, high: 51.85, low: 51.79, close: 51.85, volume: 42685, }, { date: 1463148300000, open: 51.85, high: 51.89, low: 51.8454, close: 51.86, volume: 89450, }, { date: 1463148360000, open: 51.86, high: 51.895, low: 51.83, close: 51.86, volume: 72387, }, { date: 1463148420000, open: 51.8665, high: 51.87, low: 51.79, close: 51.795, volume: 35115, }, { date: 1463148480000, open: 51.8, high: 51.81, low: 51.74, close: 51.74, volume: 30628, }, { date: 1463148540000, open: 51.735, high: 51.762, low: 51.7, close: 51.762, volume: 90143, }, { date: 1463148600000, open: 51.7699, high: 51.81, low: 51.76, close: 51.78, volume: 52607, }, { date: 1463148660000, open: 51.77, high: 51.78, low: 51.745, close: 51.765, volume: 44851, }, { date: 1463148720000, open: 51.76, high: 51.77, low: 51.74, close: 51.755, volume: 76424, }, { date: 1463148780000, open: 51.76, high: 51.8, low: 51.75, close: 51.8, volume: 42255, }, { date: 1463148840000, open: 51.795, high: 51.805, low: 51.77, close: 51.78, volume: 34416, }, { date: 1463148900000, open: 51.79, high: 51.81, low: 51.785, close: 51.81, volume: 37829, }, { date: 1463148960000, open: 51.8, high: 51.805, low: 51.76, close: 51.78, volume: 52475, }, { date: 1463149020000, open: 51.7732, high: 51.79, low: 51.7601, close: 51.775, volume: 15975, }, { date: 1463149080000, open: 51.774, high: 51.8164, low: 51.76, close: 51.76, volume: 39444, }, { date: 1463149140000, open: 51.7601, high: 51.82, low: 51.7601, close: 51.79, volume: 24990, }, { date: 1463149200000, open: 51.79, high: 51.8, low: 51.77, close: 51.8, volume: 34997, }, { date: 1463149260000, open: 51.79, high: 51.8015, low: 51.73, close: 51.74, volume: 49948, }, { date: 1463149320000, open: 51.74, high: 51.78, low: 51.74, close: 51.7568, volume: 23277, }, { date: 1463149380000, open: 51.75, high: 51.77, low: 51.74, close: 51.75, volume: 24412, }, { date: 1463149440000, open: 51.755, high: 51.7735, low: 51.73, close: 51.7735, volume: 27193, }, { date: 1463149500000, open: 51.775, high: 51.775, low: 51.729, close: 51.735, volume: 32951, }, { date: 1463149560000, open: 51.73, high: 51.8099, low: 51.73, close: 51.782, volume: 58182, }, { date: 1463149620000, open: 51.79, high: 51.79, low: 51.74, close: 51.765, volume: 44163, }, { date: 1463149680000, open: 51.76, high: 51.77, low: 51.75, close: 51.75, volume: 23404, }, { date: 1463149740000, open: 51.755, high: 51.78, low: 51.74, close: 51.7401, volume: 35038, }, { date: 1463149800000, open: 51.745, high: 51.75, low: 51.71, close: 51.715, volume: 35123, }, { date: 1463149860000, open: 51.71, high: 51.71, low: 51.66, close: 51.66, volume: 32449, }, { date: 1463149920000, open: 51.66, high: 51.73, low: 51.65, close: 51.7, volume: 46210, }, { date: 1463149980000, open: 51.7, high: 51.71, low: 51.675, close: 51.71, volume: 31392, }, { date: 1463150040000, open: 51.7, high: 51.71, low: 51.68, close: 51.68, volume: 22908, }, { date: 1463150100000, open: 51.68, high: 51.715, low: 51.68, close: 51.699, volume: 26032, }, { date: 1463150160000, open: 51.7, high: 51.73, low: 51.7, close: 51.71, volume: 33017, }, { date: 1463150220000, open: 51.71, high: 51.719, low: 51.69, close: 51.7019, volume: 21324, }, { date: 1463150280000, open: 51.7, high: 51.72, low: 51.68, close: 51.72, volume: 29504, }, { date: 1463150340000, open: 51.715, high: 51.75, low: 51.715, close: 51.75, volume: 50015, }, { date: 1463150400000, open: 51.745, high: 51.755, low: 51.705, close: 51.725, volume: 55206, }, { date: 1463150460000, open: 51.7238, high: 51.74, low: 51.7, close: 51.715, volume: 28371, }, { date: 1463150520000, open: 51.71, high: 51.74, low: 51.68, close: 51.68, volume: 34354, }, { date: 1463150580000, open: 51.68, high: 51.68, low: 51.63, close: 51.63, volume: 86053, }, { date: 1463150640000, open: 51.63, high: 51.6399, low: 51.6, close: 51.63, volume: 38086, }, { date: 1463150700000, open: 51.62, high: 51.65, low: 51.62, close: 51.65, volume: 24899, }, { date: 1463150760000, open: 51.65, high: 51.67, low: 51.64, close: 51.645, volume: 42468, }, { date: 1463150820000, open: 51.65, high: 51.68, low: 51.65, close: 51.67, volume: 37807, }, { date: 1463150880000, open: 51.679, high: 51.68, low: 51.66, close: 51.68, volume: 37740, }, { date: 1463150940000, open: 51.685, high: 51.69, low: 51.65, close: 51.66, volume: 41243, }, { date: 1463151000000, open: 51.655, high: 51.66, low: 51.63, close: 51.65, volume: 59720, }, { date: 1463151060000, open: 51.65, high: 51.71, low: 51.65, close: 51.7, volume: 28283, }, { date: 1463151120000, open: 51.7, high: 51.75, low: 51.7, close: 51.72, volume: 50587, }, { date: 1463151180000, open: 51.72, high: 51.7219, low: 51.69, close: 51.6965, volume: 43152, }, { date: 1463151240000, open: 51.7, high: 51.7, low: 51.66, close: 51.68, volume: 41484, }, { date: 1463151300000, open: 51.68, high: 51.68, low: 51.63, close: 51.63, volume: 40407, }, { date: 1463151360000, open: 51.6307, high: 51.68, low: 51.63, close: 51.67, volume: 40490, }, { date: 1463151420000, open: 51.67, high: 51.68, low: 51.645, close: 51.66, volume: 25741, }, { date: 1463151480000, open: 51.66, high: 51.66, low: 51.62, close: 51.64, volume: 55328, }, { date: 1463151540000, open: 51.63, high: 51.66, low: 51.63, close: 51.66, volume: 15494, }, { date: 1463151600000, open: 51.655, high: 51.67, low: 51.645, close: 51.66, volume: 74733, }, { date: 1463151660000, open: 51.66, high: 51.72, low: 51.66, close: 51.66, volume: 56689, }, { date: 1463151720000, open: 51.6699, high: 51.68, low: 51.65, close: 51.66, volume: 31009, }, { date: 1463151780000, open: 51.6536, high: 51.685, low: 51.65, close: 51.65, volume: 42935, }, { date: 1463151840000, open: 51.64, high: 51.695, low: 51.64, close: 51.68, volume: 35496, }, { date: 1463151900000, open: 51.67, high: 51.69, low: 51.64, close: 51.675, volume: 55265, }, { date: 1463151960000, open: 51.675, high: 51.74, low: 51.67, close: 51.7399, volume: 46941, }, { date: 1463152020000, open: 51.74, high: 51.7403, low: 51.7, close: 51.7301, volume: 51049, }, { date: 1463152080000, open: 51.74, high: 51.74, low: 51.7, close: 51.73, volume: 55279, }, { date: 1463152140000, open: 51.72, high: 51.75, low: 51.695, close: 51.74, volume: 44600, }, { date: 1463152200000, open: 51.73, high: 51.77, low: 51.71, close: 51.77, volume: 79263, }, { date: 1463152260000, open: 51.76, high: 51.78, low: 51.73, close: 51.78, volume: 95978, }, { date: 1463152320000, open: 51.77, high: 51.8, low: 51.7612, close: 51.77, volume: 46091, }, { date: 1463152380000, open: 51.78, high: 51.81, low: 51.77, close: 51.805, volume: 49870, }, { date: 1463152440000, open: 51.8, high: 51.8, low: 51.74, close: 51.76, volume: 47859, }, { date: 1463152500000, open: 51.76, high: 51.77, low: 51.7, close: 51.71, volume: 34775, }, { date: 1463152560000, open: 51.72, high: 51.764, low: 51.715, close: 51.74, volume: 28008, }, { date: 1463152620000, open: 51.73, high: 51.7536, low: 51.705, close: 51.75, volume: 53310, }, { date: 1463152680000, open: 51.75, high: 51.78, low: 51.75, close: 51.76, volume: 50833, }, { date: 1463152740000, open: 51.77, high: 51.78, low: 51.721, close: 51.75, volume: 47360, }, { date: 1463152800000, open: 51.75, high: 51.75, low: 51.71, close: 51.73, volume: 30566, }, { date: 1463152860000, open: 51.74, high: 51.76, low: 51.71, close: 51.73, volume: 54653, }, { date: 1463152920000, open: 51.725, high: 51.75, low: 51.71, close: 51.73, volume: 61478, }, { date: 1463152980000, open: 51.74, high: 51.755, low: 51.715, close: 51.72, volume: 69451, }, { date: 1463153040000, open: 51.72, high: 51.73, low: 51.68, close: 51.68, volume: 59335, }, { date: 1463153100000, open: 51.68, high: 51.69, low: 51.66, close: 51.689, volume: 44538, }, { date: 1463153160000, open: 51.685, high: 51.69, low: 51.66, close: 51.68, volume: 56503, }, { date: 1463153220000, open: 51.68, high: 51.77, low: 51.68, close: 51.75, volume: 63433, }, { date: 1463153280000, open: 51.75, high: 51.75, low: 51.71, close: 51.745, volume: 49882, }, { date: 1463153340000, open: 51.74, high: 51.75, low: 51.72, close: 51.7399, volume: 35083, }, { date: 1463153400000, open: 51.73, high: 51.77, low: 51.72, close: 51.72, volume: 48223, }, { date: 1463153460000, open: 51.725, high: 51.735, low: 51.69, close: 51.725, volume: 26158, }, { date: 1463153520000, open: 51.73, high: 51.74, low: 51.71, close: 51.72, volume: 38430, }, { date: 1463153580000, open: 51.72, high: 51.74, low: 51.72, close: 51.72, volume: 25362, }, { date: 1463153640000, open: 51.73, high: 51.745, low: 51.693, close: 51.7, volume: 48918, }, { date: 1463153700000, open: 51.7, high: 51.715, low: 51.7, close: 51.7, volume: 21317, }, { date: 1463153760000, open: 51.7, high: 51.7, low: 51.65, close: 51.65, volume: 28565, }, { date: 1463153820000, open: 51.6501, high: 51.67, low: 51.64, close: 51.655, volume: 50273, }, { date: 1463153880000, open: 51.65, high: 51.68, low: 51.64, close: 51.68, volume: 26600, }, { date: 1463153940000, open: 51.6735, high: 51.7, low: 51.67, close: 51.695, volume: 19074, }, { date: 1463154000000, open: 51.7, high: 51.73, low: 51.7, close: 51.71, volume: 43332, }, { date: 1463154060000, open: 51.71, high: 51.72, low: 51.68, close: 51.7, volume: 27675, }, { date: 1463154120000, open: 51.706, high: 51.71, low: 51.67, close: 51.67, volume: 20601, }, { date: 1463154180000, open: 51.677, high: 51.7, low: 51.671, close: 51.68, volume: 23783, }, { date: 1463154240000, open: 51.68, high: 51.685, low: 51.64, close: 51.64, volume: 48189, }, { date: 1463154300000, open: 51.65, high: 51.68, low: 51.64, close: 51.64, volume: 21903, }, { date: 1463154360000, open: 51.64, high: 51.645, low: 51.61, close: 51.61, volume: 25045, }, { date: 1463154420000, open: 51.61, high: 51.64, low: 51.605, close: 51.64, volume: 77331, }, { date: 1463154480000, open: 51.64, high: 51.66, low: 51.63, close: 51.64, volume: 24055, }, { date: 1463154540000, open: 51.64, high: 51.66, low: 51.64, close: 51.66, volume: 18390, }, { date: 1463154600000, open: 51.65, high: 51.66, low: 51.635, close: 51.635, volume: 21571, }, { date: 1463154660000, open: 51.63, high: 51.635, low: 51.575, close: 51.5999, volume: 66563, }, { date: 1463154720000, open: 51.595, high: 51.61, low: 51.59, close: 51.61, volume: 13427, }, { date: 1463154780000, open: 51.61, high: 51.63, low: 51.61, close: 51.6165, volume: 15054, }, { date: 1463154840000, open: 51.62, high: 51.6299, low: 51.595, close: 51.61, volume: 25589, }, { date: 1463154900000, open: 51.6138, high: 51.63, low: 51.59, close: 51.62, volume: 22534, }, { date: 1463154960000, open: 51.61, high: 51.62, low: 51.61, close: 51.61, volume: 26291, }, { date: 1463155020000, open: 51.61, high: 51.63, low: 51.59, close: 51.6266, volume: 25613, }, { date: 1463155080000, open: 51.6201, high: 51.645, low: 51.62, close: 51.625, volume: 27972, }, { date: 1463155140000, open: 51.625, high: 51.63, low: 51.59, close: 51.59, volume: 36545, }, { date: 1463155200000, open: 51.58, high: 51.5856, low: 51.565, close: 51.57, volume: 36990, }, { date: 1463155260000, open: 51.58, high: 51.61, low: 51.57, close: 51.61, volume: 29874, }, { date: 1463155320000, open: 51.6, high: 51.61, low: 51.575, close: 51.5753, volume: 26862, }, { date: 1463155380000, open: 51.58, high: 51.59, low: 51.57, close: 51.575, volume: 18868, }, { date: 1463155440000, open: 51.575, high: 51.61, low: 51.56, close: 51.6, volume: 56415, }, { date: 1463155500000, open: 51.6, high: 51.605, low: 51.587, close: 51.5999, volume: 23160, }, { date: 1463155560000, open: 51.5901, high: 51.6199, low: 51.59, close: 51.61, volume: 16104, }, { date: 1463155620000, open: 51.61, high: 51.64, low: 51.6085, close: 51.625, volume: 47049, }, { date: 1463155680000, open: 51.62, high: 51.6263, low: 51.6, close: 51.62, volume: 31511, }, { date: 1463155740000, open: 51.625, high: 51.63, low: 51.58, close: 51.595, volume: 44969, }, { date: 1463155800000, open: 51.598, high: 51.62, low: 51.59, close: 51.59, volume: 28751, }, { date: 1463155860000, open: 51.5925, high: 51.615, low: 51.585, close: 51.585, volume: 32626, }, { date: 1463155920000, open: 51.59, high: 51.625, low: 51.59, close: 51.61, volume: 25507, }, { date: 1463155980000, open: 51.615, high: 51.63, low: 51.61, close: 51.61, volume: 32025, }, { date: 1463156040000, open: 51.61, high: 51.618, low: 51.59, close: 51.59, volume: 20761, }, { date: 1463156100000, open: 51.6, high: 51.6, low: 51.59, close: 51.5933, volume: 19624, }, { date: 1463156160000, open: 51.59, high: 51.62, low: 51.59, close: 51.61, volume: 22407, }, { date: 1463156220000, open: 51.61, high: 51.6125, low: 51.59, close: 51.59, volume: 20572, }, { date: 1463156280000, open: 51.59, high: 51.63, low: 51.59, close: 51.6225, volume: 28281, }, { date: 1463156340000, open: 51.6299, high: 51.675, low: 51.61, close: 51.665, volume: 81155, }, { date: 1463156400000, open: 51.665, high: 51.69, low: 51.66, close: 51.67, volume: 37528, }, { date: 1463156460000, open: 51.675, high: 51.68, low: 51.66, close: 51.67, volume: 31016, }, { date: 1463156520000, open: 51.675, high: 51.675, low: 51.645, close: 51.65, volume: 17947, }, { date: 1463156580000, open: 51.66, high: 51.6888, low: 51.6599, close: 51.679, volume: 48423, }, { date: 1463156640000, open: 51.67, high: 51.72, low: 51.67, close: 51.675, volume: 55435, }, { date: 1463156700000, open: 51.675, high: 51.7, low: 51.675, close: 51.695, volume: 24570, }, { date: 1463156760000, open: 51.6999, high: 51.7099, low: 51.68, close: 51.69, volume: 51720, }, { date: 1463156820000, open: 51.6835, high: 51.72, low: 51.6835, close: 51.7063, volume: 31168, }, { date: 1463156880000, open: 51.7, high: 51.72, low: 51.67, close: 51.72, volume: 34500, }, { date: 1463156940000, open: 51.71, high: 51.72, low: 51.665, close: 51.68, volume: 46174, }, { date: 1463157000000, open: 51.689, high: 51.695, low: 51.68, close: 51.69, volume: 19511, }, { date: 1463157060000, open: 51.695, high: 51.695, low: 51.68, close: 51.685, volume: 16099, }, { date: 1463157120000, open: 51.69, high: 51.7, low: 51.67, close: 51.68, volume: 39149, }, { date: 1463157180000, open: 51.68, high: 51.72, low: 51.68, close: 51.71, volume: 28658, }, { date: 1463157240000, open: 51.7, high: 51.71, low: 51.685, close: 51.705, volume: 67684, }, { date: 1463157300000, open: 51.7, high: 51.72, low: 51.69, close: 51.705, volume: 47637, }, { date: 1463157360000, open: 51.7065, high: 51.725, low: 51.703, close: 51.7224, volume: 15800, }, { date: 1463157420000, open: 51.721, high: 51.735, low: 51.715, close: 51.72, volume: 26283, }, { date: 1463157480000, open: 51.73, high: 51.73, low: 51.695, close: 51.703, volume: 26448, }, { date: 1463157540000, open: 51.7, high: 51.7, low: 51.66, close: 51.66, volume: 31458, }, { date: 1463157600000, open: 51.665, high: 51.68, low: 51.66, close: 51.67, volume: 26439, }, { date: 1463157660000, open: 51.67, high: 51.67, low: 51.635, close: 51.64, volume: 23281, }, { date: 1463157720000, open: 51.64, high: 51.64, low: 51.6, close: 51.625, volume: 30674, }, { date: 1463157780000, open: 51.625, high: 51.635, low: 51.595, close: 51.6, volume: 53498, }, { date: 1463157840000, open: 51.6001, high: 51.606, low: 51.57, close: 51.5701, volume: 22616, }, { date: 1463157900000, open: 51.57, high: 51.59, low: 51.55, close: 51.585, volume: 61406, }, { date: 1463157960000, open: 51.5837, high: 51.59, low: 51.56, close: 51.59, volume: 28080, }, { date: 1463158020000, open: 51.59, high: 51.62, low: 51.59, close: 51.61, volume: 20069, }, { date: 1463158080000, open: 51.6, high: 51.63, low: 51.59, close: 51.6201, volume: 24029, }, { date: 1463158140000, open: 51.63, high: 51.64, low: 51.62, close: 51.62, volume: 20991, }, { date: 1463158200000, open: 51.625, high: 51.63, low: 51.605, close: 51.605, volume: 17551, }, { date: 1463158260000, open: 51.6035, high: 51.62, low: 51.595, close: 51.6, volume: 22760, }, { date: 1463158320000, open: 51.595, high: 51.61, low: 51.59, close: 51.605, volume: 14853, }, { date: 1463158380000, open: 51.61, high: 51.63, low: 51.61, close: 51.62, volume: 10356, }, { date: 1463158440000, open: 51.62, high: 51.62, low: 51.59, close: 51.597, volume: 14773, }, { date: 1463158500000, open: 51.6, high: 51.61, low: 51.57, close: 51.58, volume: 36265, }, { date: 1463158560000, open: 51.58, high: 51.61, low: 51.58, close: 51.6, volume: 15420, }, { date: 1463158620000, open: 51.608, high: 51.608, low: 51.57, close: 51.59, volume: 38817, }, { date: 1463158680000, open: 51.59, high: 51.59, low: 51.55, close: 51.55, volume: 24836, }, { date: 1463158740000, open: 51.55, high: 51.565, low: 51.54, close: 51.56, volume: 75977, }, { date: 1463158800000, open: 51.55, high: 51.57, low: 51.54, close: 51.57, volume: 20702, }, { date: 1463158860000, open: 51.57, high: 51.59, low: 51.56, close: 51.585, volume: 32744, }, { date: 1463158920000, open: 51.58, high: 51.61, low: 51.56, close: 51.61, volume: 18870, }, { date: 1463158980000, open: 51.6074, high: 51.615, low: 51.59, close: 51.59, volume: 26933, }, { date: 1463159040000, open: 51.59, high: 51.62, low: 51.585, close: 51.62, volume: 20593, }, { date: 1463159100000, open: 51.62, high: 51.65, low: 51.62, close: 51.63, volume: 15449, }, { date: 1463159160000, open: 51.64, high: 51.65, low: 51.63, close: 51.645, volume: 18928, }, { date: 1463159220000, open: 51.645, high: 51.645, low: 51.61, close: 51.625, volume: 27727, }, { date: 1463159280000, open: 51.625, high: 51.635, low: 51.6, close: 51.605, volume: 32192, }, { date: 1463159340000, open: 51.605, high: 51.64, low: 51.6, close: 51.63, volume: 20422, }, { date: 1463159400000, open: 51.64, high: 51.6562, low: 51.635, close: 51.65, volume: 30901, }, { date: 1463159460000, open: 51.649, high: 51.655, low: 51.63, close: 51.64, volume: 36256, }, { date: 1463159520000, open: 51.63, high: 51.665, low: 51.63, close: 51.665, volume: 15738, }, { date: 1463159580000, open: 51.665, high: 51.68, low: 51.64, close: 51.6538, volume: 30352, }, { date: 1463159640000, open: 51.655, high: 51.6599, low: 51.64, close: 51.64, volume: 21890, }, { date: 1463159700000, open: 51.64, high: 51.67, low: 51.63, close: 51.65, volume: 19403, }, { date: 1463159760000, open: 51.65, high: 51.65, low: 51.62, close: 51.62, volume: 24658, }, { date: 1463159820000, open: 51.6295, high: 51.64, low: 51.62, close: 51.63, volume: 17951, }, { date: 1463159880000, open: 51.62, high: 51.635, low: 51.59, close: 51.6, volume: 71240, }, { date: 1463159940000, open: 51.59, high: 51.62, low: 51.59, close: 51.615, volume: 20599, }, { date: 1463160000000, open: 51.62, high: 51.62, low: 51.57, close: 51.57, volume: 53550, }, { date: 1463160060000, open: 51.575, high: 51.58, low: 51.55, close: 51.57, volume: 36443, }, { date: 1463160120000, open: 51.565, high: 51.575, low: 51.56, close: 51.56, volume: 18563, }, { date: 1463160180000, open: 51.56, high: 51.575, low: 51.56, close: 51.56, volume: 13380, }, { date: 1463160240000, open: 51.5625, high: 51.57, low: 51.515, close: 51.52, volume: 126054, }, { date: 1463160300000, open: 51.52, high: 51.53, low: 51.5, close: 51.5, volume: 76471, }, { date: 1463160360000, open: 51.5041, high: 51.535, low: 51.5, close: 51.53, volume: 28500, }, { date: 1463160420000, open: 51.53, high: 51.545, low: 51.51, close: 51.52, volume: 54331, }, { date: 1463160480000, open: 51.51, high: 51.525, low: 51.49, close: 51.5, volume: 52352, }, { date: 1463160540000, open: 51.49, high: 51.52, low: 51.485, close: 51.51, volume: 30779, }, { date: 1463160600000, open: 51.52, high: 51.52, low: 51.47, close: 51.49, volume: 75771, }, { date: 1463160660000, open: 51.49, high: 51.53, low: 51.48, close: 51.525, volume: 36918, }, { date: 1463160720000, open: 51.525, high: 51.53, low: 51.5, close: 51.52, volume: 31331, }, { date: 1463160780000, open: 51.51, high: 51.53, low: 51.5, close: 51.52, volume: 18717, }, { date: 1463160840000, open: 51.51, high: 51.52, low: 51.49, close: 51.51, volume: 25326, }, { date: 1463160900000, open: 51.51, high: 51.51, low: 51.49, close: 51.5, volume: 17491, }, { date: 1463160960000, open: 51.5, high: 51.51, low: 51.47, close: 51.48, volume: 22927, }, { date: 1463161020000, open: 51.49, high: 51.495, low: 51.44, close: 51.44, volume: 97559, }, { date: 1463161080000, open: 51.45, high: 51.46, low: 51.41, close: 51.41, volume: 55122, }, { date: 1463161140000, open: 51.4101, high: 51.44, low: 51.4101, close: 51.43, volume: 25618, }, { date: 1463161200000, open: 51.4399, high: 51.45, low: 51.39, close: 51.4, volume: 60946, }, { date: 1463161260000, open: 51.4, high: 51.42, low: 51.39, close: 51.4001, volume: 70570, }, { date: 1463161320000, open: 51.4, high: 51.44, low: 51.4, close: 51.435, volume: 14992, }, { date: 1463161380000, open: 51.44, high: 51.46, low: 51.43, close: 51.44, volume: 25079, }, { date: 1463161440000, open: 51.44, high: 51.4562, low: 51.43, close: 51.45, volume: 8631, }, { date: 1463161500000, open: 51.455, high: 51.48, low: 51.45, close: 51.48, volume: 21572, }, { date: 1463161560000, open: 51.48, high: 51.4893, low: 51.46, close: 51.485, volume: 85445, }, { date: 1463161620000, open: 51.485, high: 51.5, low: 51.48, close: 51.5, volume: 14569, }, { date: 1463161680000, open: 51.49, high: 51.5, low: 51.48, close: 51.5, volume: 50809, }, { date: 1463161740000, open: 51.49, high: 51.52, low: 51.49, close: 51.51, volume: 15620, }, { date: 1463161800000, open: 51.5, high: 51.51, low: 51.44, close: 51.44, volume: 140844, }, { date: 1463161860000, open: 51.44, high: 51.44, low: 51.4, close: 51.4, volume: 58041, }, { date: 1463161920000, open: 51.4, high: 51.4, low: 51.35, close: 51.359, volume: 105338, }, { date: 1463161980000, open: 51.35, high: 51.36, low: 51.33, close: 51.34, volume: 30750, }, { date: 1463162040000, open: 51.3499, high: 51.36, low: 51.32, close: 51.34, volume: 56699, }, { date: 1463162100000, open: 51.34, high: 51.359, low: 51.325, close: 51.35, volume: 80672, }, { date: 1463162160000, open: 51.35, high: 51.36, low: 51.34, close: 51.349, volume: 21241, }, { date: 1463162220000, open: 51.35, high: 51.37, low: 51.35, close: 51.36, volume: 32765, }, { date: 1463162280000, open: 51.3564, high: 51.375, low: 51.35, close: 51.375, volume: 16939, }, { date: 1463162340000, open: 51.375, high: 51.395, low: 51.36, close: 51.37, volume: 46594, }, { date: 1463162400000, open: 51.37, high: 51.375, low: 51.33, close: 51.33, volume: 53314, }, { date: 1463162460000, open: 51.33, high: 51.335, low: 51.29, close: 51.315, volume: 46822, }, { date: 1463162520000, open: 51.31, high: 51.35, low: 51.31, close: 51.34, volume: 19918, }, { date: 1463162580000, open: 51.34, high: 51.36, low: 51.32, close: 51.36, volume: 47433, }, { date: 1463162640000, open: 51.36, high: 51.37, low: 51.32, close: 51.34, volume: 54647, }, { date: 1463162700000, open: 51.3499, high: 51.36, low: 51.34, close: 51.34, volume: 26058, }, { date: 1463162760000, open: 51.34, high: 51.34, low: 51.32, close: 51.335, volume: 23870, }, { date: 1463162820000, open: 51.3399, high: 51.34, low: 51.32, close: 51.32, volume: 33494, }, { date: 1463162880000, open: 51.32, high: 51.34, low: 51.32, close: 51.3299, volume: 27107, }, { date: 1463162940000, open: 51.32, high: 51.34, low: 51.31, close: 51.325, volume: 53604, }, { date: 1463163000000, open: 51.32, high: 51.35, low: 51.32, close: 51.335, volume: 44433, }, { date: 1463163060000, open: 51.339, high: 51.339, low: 51.27, close: 51.27, volume: 31919, }, { date: 1463163120000, open: 51.27, high: 51.3, low: 51.27, close: 51.3, volume: 20366, }, { date: 1463163180000, open: 51.295, high: 51.33, low: 51.295, close: 51.31, volume: 42701, }, { date: 1463163240000, open: 51.315, high: 51.315, low: 51.28, close: 51.3, volume: 28273, }, { date: 1463163300000, open: 51.3, high: 51.325, low: 51.3, close: 51.3168, volume: 18113, }, { date: 1463163360000, open: 51.31, high: 51.315, low: 51.29, close: 51.305, volume: 29233, }, { date: 1463163420000, open: 51.32, high: 51.33, low: 51.2728, close: 51.29, volume: 60718, }, { date: 1463163480000, open: 51.3, high: 51.3, low: 51.24, close: 51.255, volume: 41200, }, { date: 1463163540000, open: 51.25, high: 51.26, low: 51.22, close: 51.24, volume: 54365, }, { date: 1463163600000, open: 51.235, high: 51.25, low: 51.21, close: 51.23, volume: 56064, }, { date: 1463163660000, open: 51.23, high: 51.23, low: 51.18, close: 51.206, volume: 81303, }, { date: 1463163720000, open: 51.21, high: 51.215, low: 51.18, close: 51.18, volume: 33459, }, { date: 1463163780000, open: 51.1862, high: 51.1862, low: 51.14, close: 51.165, volume: 52897, }, { date: 1463163840000, open: 51.1668, high: 51.215, low: 51.1668, close: 51.195, volume: 32440, }, { date: 1463163900000, open: 51.1917, high: 51.22, low: 51.18, close: 51.22, volume: 36198, }, { date: 1463163960000, open: 51.21, high: 51.245, low: 51.21, close: 51.23, volume: 35111, }, { date: 1463164020000, open: 51.23, high: 51.235, low: 51.205, close: 51.225, volume: 45937, }, { date: 1463164080000, open: 51.215, high: 51.24, low: 51.21, close: 51.23, volume: 18662, }, { date: 1463164140000, open: 51.23, high: 51.26, low: 51.22, close: 51.255, volume: 14158, }, { date: 1463164200000, open: 51.255, high: 51.27, low: 51.24, close: 51.26, volume: 83450, }, { date: 1463164260000, open: 51.26, high: 51.265, low: 51.225, close: 51.25, volume: 39569, }, { date: 1463164320000, open: 51.25, high: 51.255, low: 51.23, close: 51.235, volume: 21851, }, { date: 1463164380000, open: 51.24, high: 51.27, low: 51.21, close: 51.22, volume: 65284, }, { date: 1463164440000, open: 51.21, high: 51.24, low: 51.21, close: 51.21, volume: 95367, }, { date: 1463164500000, open: 51.215, high: 51.225, low: 51.2, close: 51.21, volume: 58359, }, { date: 1463164560000, open: 51.21, high: 51.23, low: 51.205, close: 51.22, volume: 20484, }, { date: 1463164620000, open: 51.2121, high: 51.22, low: 51.19, close: 51.19, volume: 63262, }, { date: 1463164680000, open: 51.19, high: 51.19, low: 51.12, close: 51.12, volume: 67563, }, { date: 1463164740000, open: 51.13, high: 51.15, low: 51.12, close: 51.13, volume: 61272, }, { date: 1463164800000, open: 51.135, high: 51.147, low: 51.12, close: 51.145, volume: 38443, }, { date: 1463164860000, open: 51.145, high: 51.145, low: 51.1, close: 51.1027, volume: 47587, }, { date: 1463164920000, open: 51.1, high: 51.11, low: 51.07, close: 51.1, volume: 73498, }, { date: 1463164980000, open: 51.11, high: 51.12, low: 51.06, close: 51.096, volume: 41813, }, { date: 1463165040000, open: 51.0973, high: 51.12, low: 51.07, close: 51.12, volume: 39628, }, { date: 1463165100000, open: 51.11, high: 51.1572, low: 51.11, close: 51.135, volume: 31752, }, { date: 1463165160000, open: 51.135, high: 51.15, low: 51.12, close: 51.135, volume: 33804, }, { date: 1463165220000, open: 51.14, high: 51.1662, low: 51.14, close: 51.14, volume: 31358, }, { date: 1463165280000, open: 51.146, high: 51.146, low: 51.11, close: 51.115, volume: 70660, }, { date: 1463165340000, open: 51.12, high: 51.1399, low: 51.11, close: 51.1301, volume: 40400, }, { date: 1463165400000, open: 51.13, high: 51.15, low: 51.105, close: 51.12, volume: 59059, }, { date: 1463165460000, open: 51.125, high: 51.13, low: 51.11, close: 51.1256, volume: 42001, }, { date: 1463165520000, open: 51.1225, high: 51.13, low: 51.08, close: 51.1, volume: 70841, }, { date: 1463165580000, open: 51.1, high: 51.11, low: 51.08, close: 51.08, volume: 61518, }, { date: 1463165640000, open: 51.08, high: 51.09, low: 51.05, close: 51.05, volume: 46503, }, { date: 1463165700000, open: 51.05, high: 51.12, low: 51.04, close: 51.1001, volume: 76218, }, { date: 1463165760000, open: 51.105, high: 51.11, low: 51.07, close: 51.1, volume: 45562, }, { date: 1463165820000, open: 51.11, high: 51.13, low: 51.1, close: 51.11, volume: 50828, }, { date: 1463165880000, open: 51.1, high: 51.16, low: 51.1, close: 51.14, volume: 64942, }, { date: 1463165940000, open: 51.145, high: 51.15, low: 51.13, close: 51.13, volume: 23617, }, { date: 1463166000000, open: 51.14, high: 51.16, low: 51.135, close: 51.14, volume: 25215, }, { date: 1463166060000, open: 51.13, high: 51.15, low: 51.1, close: 51.1346, volume: 29848, }, { date: 1463166120000, open: 51.14, high: 51.145, low: 51.13, close: 51.135, volume: 20646, }, { date: 1463166180000, open: 51.14, high: 51.16, low: 51.085, close: 51.0999, volume: 137556, }, { date: 1463166240000, open: 51.1, high: 51.13, low: 51.095, close: 51.13, volume: 24326, }, { date: 1463166300000, open: 51.13, high: 51.16, low: 51.12, close: 51.16, volume: 15489, }, { date: 1463166360000, open: 51.16, high: 51.17, low: 51.15, close: 51.15, volume: 25272, }, { date: 1463166420000, open: 51.155, high: 51.155, low: 51.09, close: 51.1, volume: 72537, }, { date: 1463166480000, open: 51.11, high: 51.13, low: 51.1, close: 51.1, volume: 19033, }, { date: 1463166540000, open: 51.109, high: 51.11, low: 51.06, close: 51.08, volume: 46520, }, { date: 1463166600000, open: 51.0801, high: 51.11, low: 51.08, close: 51.1, volume: 17555, }, { date: 1463166660000, open: 51.11, high: 51.13, low: 51.09, close: 51.125, volume: 26540, }, { date: 1463166720000, open: 51.13, high: 51.145, low: 51.12, close: 51.13, volume: 39867, }, { date: 1463166780000, open: 51.125, high: 51.13, low: 51.12, close: 51.125, volume: 8085, }, { date: 1463166840000, open: 51.13, high: 51.145, low: 51.1, close: 51.1, volume: 51965, }, { date: 1463166900000, open: 51.1, high: 51.15, low: 51.1, close: 51.145, volume: 29142, }, { date: 1463166960000, open: 51.15, high: 51.1783, low: 51.145, close: 51.15, volume: 63409, }, { date: 1463167020000, open: 51.15, high: 51.18, low: 51.14, close: 51.18, volume: 41999, }, { date: 1463167080000, open: 51.185, high: 51.21, low: 51.17, close: 51.21, volume: 32764, }, { date: 1463167140000, open: 51.205, high: 51.215, low: 51.185, close: 51.205, volume: 98693, }, { date: 1463167200000, open: 51.2, high: 51.22, low: 51.18, close: 51.2, volume: 47633, }, { date: 1463167260000, open: 51.2, high: 51.23, low: 51.19, close: 51.2232, volume: 25450, }, { date: 1463167320000, open: 51.22, high: 51.23, low: 51.21, close: 51.215, volume: 20392, }, { date: 1463167380000, open: 51.21, high: 51.249, low: 51.21, close: 51.23, volume: 28352, }, { date: 1463167440000, open: 51.2356, high: 51.2398, low: 51.205, close: 51.22, volume: 44908, }, { date: 1463167500000, open: 51.21, high: 51.215, low: 51.18, close: 51.2036, volume: 57736, }, { date: 1463167560000, open: 51.205, high: 51.21, low: 51.175, close: 51.1999, volume: 54836, }, { date: 1463167620000, open: 51.195, high: 51.195, low: 51.18, close: 51.19, volume: 19611, }, { date: 1463167680000, open: 51.1901, high: 51.1999, low: 51.16, close: 51.17, volume: 33170, }, { date: 1463167740000, open: 51.16, high: 51.18, low: 51.16, close: 51.18, volume: 16394, }, { date: 1463167800000, open: 51.18, high: 51.18, low: 51.14, close: 51.1511, volume: 42308, }, { date: 1463167860000, open: 51.155, high: 51.18, low: 51.15, close: 51.1772, volume: 22821, }, { date: 1463167920000, open: 51.17, high: 51.175, low: 51.14, close: 51.15, volume: 39917, }, { date: 1463167980000, open: 51.145, high: 51.165, low: 51.125, close: 51.1575, volume: 28039, }, { date: 1463168040000, open: 51.1605, high: 51.165, low: 51.12, close: 51.13, volume: 40486, }, { date: 1463168100000, open: 51.124, high: 51.175, low: 51.124, close: 51.17, volume: 47609, }, { date: 1463168160000, open: 51.17, high: 51.205, low: 51.16, close: 51.2, volume: 60646, }, { date: 1463168220000, open: 51.2, high: 51.2, low: 51.18, close: 51.19, volume: 45726, }, { date: 1463168280000, open: 51.19, high: 51.2399, low: 51.18, close: 51.226, volume: 88391, }, { date: 1463168340000, open: 51.22, high: 51.225, low: 51.19, close: 51.215, volume: 68989, }, { date: 1463168400000, open: 51.21, high: 51.23, low: 51.2, close: 51.225, volume: 50055, }, { date: 1463168460000, open: 51.225, high: 51.25, low: 51.22, close: 51.24, volume: 78243, }, { date: 1463168520000, open: 51.2357, high: 51.24, low: 51.21, close: 51.21, volume: 55131, }, { date: 1463168580000, open: 51.21, high: 51.235, low: 51.2, close: 51.235, volume: 44469, }, { date: 1463168640000, open: 51.24, high: 51.26, low: 51.225, close: 51.25, volume: 59309, }, { date: 1463168700000, open: 51.25, high: 51.26, low: 51.195, close: 51.195, volume: 62693, }, { date: 1463168760000, open: 51.19, high: 51.2, low: 51.15, close: 51.18, volume: 86772, }, { date: 1463168820000, open: 51.18, high: 51.18, low: 51.13, close: 51.135, volume: 78645, }, { date: 1463168880000, open: 51.135, high: 51.14, low: 51.11, close: 51.125, volume: 63063, }, { date: 1463168940000, open: 51.121, high: 51.125, low: 51.1, close: 51.12, volume: 64714, }, { date: 1463169000000, open: 51.115, high: 51.16, low: 51.09, close: 51.1, volume: 132864, }, { date: 1463169060000, open: 51.09, high: 51.1298, low: 51.09, close: 51.115, volume: 130985, }, { date: 1463169120000, open: 51.115, high: 51.12, low: 51.085, close: 51.105, volume: 111359, }, { date: 1463169180000, open: 51.11, high: 51.13, low: 51.1, close: 51.115, volume: 104307, }, { date: 1463169240000, open: 51.115, high: 51.13, low: 51.11, close: 51.115, volume: 93090, }, { date: 1463169300000, open: 51.12, high: 51.13, low: 51.085, close: 51.095, volume: 101862, }, { date: 1463169360000, open: 51.09, high: 51.13, low: 51.09, close: 51.115, volume: 140395, }, { date: 1463169420000, open: 51.119, high: 51.13, low: 51.09, close: 51.105, volume: 119025, }, { date: 1463169480000, open: 51.11, high: 51.13, low: 51.1, close: 51.12, volume: 104428, }, { date: 1463169540000, open: 51.12, high: 51.13, low: 51.1, close: 51.105, volume: 227741, }, { date: 1463169600000, open: 51.1, high: 51.1079, low: 51.06, close: 51.08, volume: 2630306, }, ] ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/KlineChart/index.ts ================================================ export * from './KlineChart' export * from './data' ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/ScaleAreaChart.tsx ================================================ // import {WithTranslation, withTranslation} from "react-i18next"; // import styled from "@emotion/styled"; // import { IOrigDataItem, IGetDepthDataParams } from './data' import TrendChart from './TrendChart' import DepthChart from './DepthChart' import { ChartType } from '../' import { IndicatorProps, WrapperedKlineChart } from './KlineChart' import * as sdk from '@loopring-web/loopring-sdk' import { TradingInterval } from '@loopring-web/loopring-sdk' import { useSettings } from '../../../stores' import { getTheme } from '@loopring-web/common-resources' export interface ScaleAreaChartProps { type: ChartType data: any indicator?: IndicatorProps interval?: sdk.TradingInterval handleMove?: (props: any) => void yAxisDomainPercent?: number // defualt 0.1 riseColor?: 'green' | 'red' showTooltip?: boolean showArea?: boolean quoteSymbol?: string showXAxis?: boolean showYAxis?: boolean isHeadTailCompare?: boolean marketPrecision?: number isDailyTrend?: boolean handleMoveOut?: () => void showCartesianGrid?: boolean showClose?: boolean } export const ScaleAreaChart = ({ type, interval, indicator, data, marketPrecision, ...rest }: ScaleAreaChartProps) => { const { themeMode, upColor } = useSettings() switch (type) { case ChartType.Trend: return case ChartType.Depth: return ( ) case ChartType.Kline: // let dateTimeFormat = '%Y %a %d' let dateTimeFormat = '%x' if (interval) { switch (interval) { case TradingInterval.min1: case TradingInterval.min5: case TradingInterval.min15: case TradingInterval.min30: // dateTimeFormat = '%Y %b %d, %I:%M %p' dateTimeFormat = '%c' break case TradingInterval.hr1: case TradingInterval.hr2: case TradingInterval.hr4: case TradingInterval.hr12: // dateTimeFormat = '%Y %b %d, %I %p' dateTimeFormat = '%c' break case TradingInterval.d1: break case TradingInterval.w1: // dateTimeFormat = '%Y %b %d' break default: break } } return ( ) default: return prop "type" is not avaible for current chart } } ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/TrendChart/ScaleAreaChart.stories.tsx ================================================ // also exported from '@storybook/react' if you can deal with breaking changes in 6.1 import { Meta, Story } from '@storybook/react' import { ScaleAreaChart } from '../ScaleAreaChart' import { ChartType } from '../../index' import { withTranslation } from 'react-i18next' import styled from '@emotion/styled' const Styled = styled.div` flex: 1; width: 1000px; height: 500px; ` // @ts-ignore const testTrendData: any = [ { timeStamp: 150, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 160, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 170, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 180, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 190, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 200, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 210, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 220, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 230, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, { timeStamp: 240, // low: Math.random() + 1, // high: Math.random() + 6, // open: Math.random() + 3, close: Math.random() + 4, // volume: (Math.random() + 4) * 1500, }, ] export const Trend = withTranslation()(() => { return ( <> { console.log(props) }} // showTooltip={false} // riseColor="red" // showArea={false} /> ) }) as Story export default { title: 'Charts/Trend', component: Trend, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/TrendChart/index.tsx ================================================ import React, { useCallback, useState } from 'react' import { Area, ComposedChart, Line, ResponsiveContainer, CartesianGrid, Tooltip, XAxis, YAxis, } from '@loopring-web/recharts' import moment from 'moment' import { ScaleAreaChartProps } from '../ScaleAreaChart' import { getRenderData } from '../data' import { Box, Typography } from '@mui/material' import styled from '@emotion/styled' import { useSettings } from '@loopring-web/component-lib/src/stores' import { DAT_STRING_FORMAT, DAT_STRING_FORMAT_S, EmptyValueTag, MINT_STRING_FORMAT, } from '@loopring-web/common-resources' import { useTheme } from '@emotion/react' // import { getValuePrecisionThousand, myLog } from '@loopring-web/common-resources'; const DEFAULT_YAXIS_DOMAIN = 0.05 const TooltipStyled = styled(Box)` background: var(--color-pop-bg); border: 1px solid var(--color-border); border-radius: ${({ theme }) => theme.unit * 0.25}px; padding: ${({ theme }) => theme.unit * 2}px ${({ theme }) => theme.unit * 2}px; > div:last-of-type { color: var(--color-text-secondary); } ` const TrendChart = ({ type, data, yAxisDomainPercent = DEFAULT_YAXIS_DOMAIN, handleMove, showTooltip = true, showArea = true, quoteSymbol, showXAxis = true, showYAxis = true, isHeadTailCompare = false, isDailyTrend = false, handleMoveOut = undefined, showCartesianGrid = false, showClose = true, }: ScaleAreaChartProps) => { const theme = useTheme() const userSettings = useSettings() const UP_COLOR = theme.colorBase.success const DOWN_COLOR = theme.colorBase.error const DAILY_TREND_COLOR = theme.colorBase.primary const upColor = userSettings ? userSettings.upColor : 'green' const renderData = getRenderData(type, data) const [priceTrend, setPriceTrend] = useState<'up' | 'down'>( renderData[renderData.length - 1]?.sign === 1 ? 'up' : 'down', ) const [currentIndex, setCurrentIndex] = useState(-1) const trendColor = upColor === 'green' ? priceTrend === 'up' ? UP_COLOR : DOWN_COLOR : priceTrend === 'up' ? DOWN_COLOR : UP_COLOR const hasData = data && Array.isArray(data) && !!data.length const handleMousemove = useCallback( (props: any) => { if (!hasData) return const { activeTooltipIndex } = props // avoid duplicated event const isUpdate = activeTooltipIndex !== currentIndex if (Number.isFinite(activeTooltipIndex) && isUpdate) { setCurrentIndex(activeTooltipIndex) if (renderData[activeTooltipIndex] && renderData[activeTooltipIndex].sign) { setPriceTrend(renderData[activeTooltipIndex].sign === 1 ? 'up' : 'down') } if (handleMove) { handleMove(renderData[activeTooltipIndex]) } } }, [renderData, handleMove, currentIndex, hasData], ) const renderTooltipContent = useCallback( (props: any) => { if (!hasData) return if (!props.payload || !props.payload.length || !props.payload[0].payload.timeStamp) return const { timeStamp, close, sign: inputSign, change: inputChange } = props.payload[0].payload let change if (inputChange) { change = inputChange } else { const index = data.findIndex((o: any) => o.timeStamp === timeStamp) change = index === 0 ? EmptyValueTag : (((close - data[index - 1].close) / data[index - 1].close) * 100).toFixed(2) } const sign = inputChange ? (Number(inputChange || 0) >= 0 ? 1 : -1) : inputSign if (isDailyTrend) { return ( {moment.unix(timeStamp / 1000).format(DAT_STRING_FORMAT)} Change:  {Number(change || 0) > 0 ? `+${change}` : change} % ) } else { return ( {quoteSymbol && ( {`${close} ${quoteSymbol}`}  {Number(change || 0) > 0 ? `+${change}` : change} % )} {moment.unix(timeStamp / 1000).format(MINT_STRING_FORMAT)} ) } }, [hasData, isDailyTrend, data, upColor, quoteSymbol], ) const handleMouseLeave = useCallback(() => { if (handleMoveOut) { handleMoveOut() } setPriceTrend(renderData[renderData.length - 1]?.sign === 1 ? 'up' : 'down') }, [renderData, handleMoveOut]) React.useEffect(() => { if (!!renderData.length) { setPriceTrend(renderData[renderData.length - 1].sign === 1 ? 'up' : 'down') } if (isHeadTailCompare) { const isUp = (renderData[0]?.close || 0) < (renderData[renderData.length - 1]?.close || 0) setPriceTrend(isUp ? 'up' : 'down') } }, [renderData[0]?.close]) const customTick = ({ x, y, payload }: any) => { if (!renderData || !renderData.length) { return } return ( {moment(payload.value).format(DAT_STRING_FORMAT_S)} ) } const getDynamicYAxisDomain = useCallback(() => { const valueList = renderData.map((o) => o.close) const min = Math.min(...valueList) const max = Math.max(...valueList) if (min / max < 0.1) { return [(dataMin: number) => dataMin * 0.1, (dataMax: number) => dataMax * 2.5] } if (min / max < 0.5) { return [(dataMin: number) => dataMin * 0.5, (dataMax: number) => dataMax * 1.2] } return [ (dataMin: number) => dataMin * (1 - yAxisDomainPercent), (dataMax: number) => dataMax * (1 + yAxisDomainPercent), ] }, [renderData, yAxisDomainPercent]) // const customYAxisTick = (value: any) => { // const formattedValue = getValuePrecisionThousand((Number.isFinite(value) ? value : Number(value || 0)).toFixed(2), undefined, undefined, 2) // return formattedValue ?? '0.00' // } return ( {showCartesianGrid && ( )} { // const valueList = renderData.map((o) => o.close) // const min = Math.min(...valueList) // const max = Math.max(...valueList) return index == 0 || index >= 5 ? '' : value }} tick={{ fill: theme.colorBase.textThird, fontSize: 12, textAnchor: 'start', width: 34, transform: 'translate(-68, 0)', }} /* tickFormatter={convertValue} */ stroke={'var(--color-text-secondary)'} /> {hasData && showTooltip && ( } position={{ y: 50 }} content={(props) => renderTooltipContent(props)} /> )} {showArea && ( )} ) } export default TrendChart ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/data.tsx ================================================ import { ChartType } from '../' export interface IOrigDataItem { timeStamp: number low: number high: number open: number close: number volume: number } export interface IDataItem { timeStamp: number low: number high: number open: number close: number volume: number sign: 1 | -1 } export interface GetSignParams { type: keyof typeof ChartType data: any dataIndex: number open: number close: number closeDimIdx: number } const getSign = ({ data, dataIndex, close }: GetSignParams): IDataItem['sign'] => { let sign const closeLastDay = dataIndex !== 0 && data[dataIndex - 1].close sign = dataIndex > 0 && closeLastDay ? (closeLastDay < close ? 1 : -1) : 1 return sign as IDataItem['sign'] } export const getRenderData = ( type: keyof typeof ChartType, data?: IOrigDataItem[], ): IDataItem[] => { if (!data || !Array.isArray(data)) return [] const closeDimIdx = 3 return data.map((o, index) => { if ((o as any).change) { return { ...o, sign: ((o as any).change ?? 0) >= 0 ? 1 : -1 } } else { return { ...o, sign: getSign({ type, data: data, dataIndex: index, open: o.open, close: o.close, closeDimIdx, }), } } }) } export const getAprRenderData = ( type: keyof typeof ChartType, data?: { apy: string; createdAt: number }[], ): { apy: string; createdAt: number }[] => { if (!data || !Array.isArray(data)) return [] return data.map((o, _index) => ({ ...o, sign: { ...o, }, })) } export interface IGetDepthDataParams { bidsPrices: number[] bidsAmtTotals: number[] asksPrices: number[] asksAmtTotals: number[] } export const getDepthData = (data?: IGetDepthDataParams | any) => { if (!data || Array.isArray(data)) return undefined const { bidsPrices, asksPrices, bidsAmtTotals, asksAmtTotals } = data const formattedBidsPrices = bidsPrices.map((price: number) => ({ type: 'bids', price, })) const formattedAsksPrices = asksPrices.map((price: number) => ({ type: 'asks', price, })) const jointPrices = formattedBidsPrices.concat(formattedAsksPrices) const jointAmtTotals = bidsAmtTotals.concat(asksAmtTotals) const fakeData = [ { price: formattedBidsPrices[formattedBidsPrices.length - 1]?.price, bids: 0, }, { price: formattedAsksPrices[0]?.price, asks: 0, }, ] const rawAmtTotals = jointAmtTotals.map((amount: number, index: number) => jointPrices[index].type === 'bids' ? { price: jointPrices[index].price, bids: amount, } : { price: jointPrices[index].price, asks: amount, }, ) const filteredBidsList = rawAmtTotals.filter((o: any) => o.bids) const filteredAsksList = rawAmtTotals.filter((o: any) => o.asks) const newData = [...filteredBidsList, ...fakeData, ...filteredAsksList] return newData } ================================================ FILE: packages/component-lib/src/components/charts/scaleAreaChart/index.ts ================================================ export * from './ScaleAreaChart' export * from './data' export * from './KlineChart' export * from './APRChart' ================================================ FILE: packages/component-lib/src/components/datetimerangepicker/index.tsx ================================================ import { ConvertToIcon } from '@loopring-web/common-resources' import { MobileDateTimePicker } from '@mui/lab' import { TextField, Box } from '@mui/material' import styled from '@emotion/styled' import moment from 'moment' import { useTranslation } from 'react-i18next' const StyledDateTimeRangePicker = styled(Box)` background-color: var(--field-opacity); display: flex; align-items: center; justify-content: space-between; .MuiOutlinedInput-root { background-color: transparent !important; height: var(--input-height-large); } ` type DateTimeRangePickerProps = { startValue: moment.Moment | null startMinDateTime?: moment.Moment startMaxDateTime?: moment.Moment onStartChange?: (m: moment.Moment | null) => void onStartOpen?: () => void endValue: moment.Moment | null endMinDateTime?: moment.Moment endMaxDateTime?: moment.Moment onEndChange?: (m: moment.Moment | null) => void onEndOpen?: () => void customeEndInputPlaceHolder?: string } export const DateTimeRangePicker = (props: DateTimeRangePickerProps) => { const { startValue, startMinDateTime, startMaxDateTime, onStartChange, onStartOpen, endValue, endMinDateTime, endMaxDateTime, onEndChange, onEndOpen, customeEndInputPlaceHolder, } = props const { t } = useTranslation() return ( {})} minDateTime={startMinDateTime} maxDateTime={startMaxDateTime} renderInput={(params) => ( )} /> {})} minDateTime={endMinDateTime} maxDateTime={endMaxDateTime} renderInput={(params) => ( )} /> ) } ================================================ FILE: packages/component-lib/src/components/footer/index.tsx ================================================ import styled from '@emotion/styled' import { Box, Container, Link, List, Typography } from '@mui/material' import React from 'react' import { DiscordIcon, FooterInterface, LoopringIcon, MediumIcon, TwitterIcon, YoutubeIcon, } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import { useTheme } from '@emotion/react' import { useSettings } from '../../stores' import * as sdk from '@loopring-web/loopring-sdk' const LinkStyle = styled(Link)` color: var(--color-text-secondary); line-height: 20px; font-size: 12px; &:hover { color: var(--color-text-hover); } ` as typeof Link const FooterDiv = styled(Box)` background: var(--color-global-bg); ` export const Footer = withTranslation(['layout'])( ({ t, linkListMap, mediaList, isLandingPage, isBeta = false, }: { isLandingPage: boolean linkListMap: { [key: string]: FooterInterface[] } mediaList: FooterInterface[] isBeta: boolean } & WithTranslation) => { const { mode } = useTheme() const { isMobile } = useSettings() React.useLayoutEffect(() => { function updateSize() {} window.addEventListener('resize', updateSize) updateSize() return () => window.removeEventListener('resize', updateSize) }, []) const linkListMapRender = React.useMemo(() => { return Reflect.ownKeys(linkListMap).map((key) => { return ( {t('labelFooter' + key.toString())} {linkListMap[key.toString()].map((item: any) => { return ( {t('label' + 'key' + item.linkName)} ) })} ) }) }, [linkListMap]) const medias = React.useMemo(() => { const renderIcon = (name: string) => { switch (name) { case 'Discord': return case 'Twitter': return case 'Youtube': return case 'Medium': return } } return ( {mediaList.map((o, index) => ( {renderIcon(o.linkName)} ))} ) }, [mediaList]) const { isDevToggle, setIsDevToggle, defaultNetwork } = useSettings() return ( {!!(isLandingPage && !isMobile) ? ( <> {linkListMapRender} Follow us {medias} {t('labelCopyRight', { year: new Date().getFullYear() })} ) : ( { if (isBeta && defaultNetwork !== sdk.ChainId.MAINNET) { setIsDevToggle(!isDevToggle) window.location.reload() } }} paddingTop={isMobile ? 2 : 0} > {isBeta ? t('labelCopyRightBeta') : t('labelCopyRight', { year: new Date().getFullYear() })} {medias} )} ) }, ) ================================================ FILE: packages/component-lib/src/components/header/Header.tsx ================================================ import styled from '@emotion/styled' import { AppBar, Box, ClickAwayListener, Container, Divider, IconButton, Slide, SwipeableDrawer, Toolbar, Typography, useScrollTrigger, } from '@mui/material' import { Link as RouterLink, useHistory, useLocation, useRouteMatch } from 'react-router-dom' import { useTranslation, WithTranslation, withTranslation } from 'react-i18next' import { HeaderMenuSub, HeadMenuItem, Layer2Item, PopoverPure, TabItemPlus } from '../basic-lib' import { HeaderProps, HeaderToolBarInterface } from './Interface' import { ButtonComponentsMap, CloseIcon, HeaderMenuItemInterface, headerMenuLandingData, HeaderMenuTabStatus, hexToRGB, L1L2_NAME_DEFINED, LoopringLogoIcon, MapChainId, MenuIcon, RouterMainKey, SoursURL, subMenuLayer2, } from '@loopring-web/common-resources' import { BtnDownload, BtnNotification, BtnSetting, BtnSettingMobile, ColorSwitch, ProfileMenu, WalletConnectBtn, WalletConnectL1Btn, } from './toolbar' import React, { useState } from 'react' import { bindPopper } from 'material-ui-popup-state/es' import { bindTrigger, usePopupState } from 'material-ui-popup-state/hooks' import { useTheme } from '@emotion/react' import _ from 'lodash' import { useSettings } from '../../stores' import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' const logoSVG = SoursURL + 'svg/logo.svg' const ToolBarStyled = styled(Toolbar)` && { display: flex; justify-content: space-between; align-items: stretch; padding: 0; } ` const HeaderStyled = styled(AppBar)` & { z-index: 400; box-shadow: none; height: var(--header-height); margin: 0 auto; background-color: var(--color-box); backdrop-filter: blur(4px); box-sizing: border-box; ${({ theme }) => theme.border.borderConfig({ d_W: 1, c_key: 'blur' })}; border-radius: 0; border: 0; &.item-scrolled.MuiAppBar-root.MuiAppBar-positionFixed { } } &.scrollable { background-color: initial; } ` const LogoStyle = styled(Typography)` display: flex; align-items: center; position: relative; &:after { content: 'beta'; position: absolute; color: var(--color-logo); display: block; font-size: 1rem; line-height: 0.8rem; right: -24px; top: 14px; font-weight: 200; opacity: 0; box-shadow: 0 0 1px 0 var(--color-logo); //border: 1px solid ; border-radius: 2px; padding: 3px 2px; } a.MuiButtonBase-root { min-width: auto; border-radius: 0; text-indent: -999999em; background: var(--color-primary); background: var(--color-logo); mask: url(${logoSVG}) space; mask-size: contain; mask-position: center; width: 105px; height: 40px; margin-top: -10px; color: transparent; &:hover { background-color: inherit; background: var(--color-logo); } } ` as typeof Typography export const LoopringLogo = React.memo(() => { return ( { window.open('https://loopring.io/#', '_blank') }} replace={true} color={'inherit'} > Loopring 路印 loopring protocol 3.6 The first Layer2 Decentralized trading Platform ) }) const ToolBarItem = ({ buttonComponent, notification, account, chainId, isLayer1Only = false, ...props }: any) => { const match = useRouteMatch('/:l1/:l2?') const { isMobile } = useSettings() const render = React.useMemo(() => { switch (buttonComponent) { case ButtonComponentsMap.ProfileMenu: // @ts-ignore return case ButtonComponentsMap.Notification: return ( ) case ButtonComponentsMap.Setting: return isMobile ? : case ButtonComponentsMap.Download: return case ButtonComponentsMap.ColorSwitch: return case ButtonComponentsMap.WalletConnect: return isLayer1Only ? ( ) : ( ) default: return undefined } }, [buttonComponent, match?.params, props, notification, account]) return {render} } export const HideOnScroll = React.forwardRef(({ children, window, ...rest }: any, ref) => { const trigger = useScrollTrigger({ target: window ? window() : undefined, }) return ( {children} ) }) const NodeMenuItem = React.memo( ({ label, router, layer, child, handleListKeyDown, ...rest }: HeaderMenuItemInterface & any) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( <> {layer >= 1 ? ( ) : ( {rest.t(label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, })} )} ) }, ) export const LAYERMAP = { '1': 'l1', '2': 'l2', } interface MobileDrawerProps { linkList: { title: string des: string link: string logo?: string onClick: () => void imgMarginRight?: number }[] open: boolean onClose: () => void showSetting: boolean showColorSwitch: boolean } const mobileDrawerBGColorList = ['#E1E8FF', '#D5FFF0', '#FFE9D9', '#E5E8EE', '#E5E8EE'] const MobileDrawer = (props: MobileDrawerProps) => { const { linkList, showColorSwitch, showSetting } = props const { t } = useTranslation() const theme = useTheme() return ( {}} open={props.open} anchor='top' onClose={() => { props.onClose() }}> {linkList.map((item, index) => { return ( {item.title} {item.des} ) })} {/* */} {showSetting && } {showColorSwitch && } ) } interface NestedMobileDrawerProps { linkList: { title: string // des: string, link: string // logo: string, onClick?: () => void id: string subLinkList?: { title: string des: string link: string LogoElement: any onClick: () => void highlighted: boolean }[] highlighted: boolean }[] open: boolean onClose: () => void showSetting: boolean showColorSwitch: boolean } const NestedMobileDrawer = (props: NestedMobileDrawerProps) => { const { linkList, showColorSwitch, showSetting} = props const { t } = useTranslation() const [selectedItem, setSelectedItem] = useState(undefined) return ( {}} open={props.open} anchor='top' onClose={props.onClose}> {linkList.map((item) => { const selected = selectedItem === item.id return ( { if (selectedItem === item.id) { setSelectedItem(undefined) } else { setSelectedItem(item.id) } }} > {item.title} {item.subLinkList && (selected ? ( ) : ( ))} {item.subLinkList && selected && ( {item.subLinkList.map((subItem) => { const LogoElement = subItem.LogoElement return ( {/* */} {subItem.title} {subItem.des} ) })} )} ) })} {showSetting && ( <> )} {showColorSwitch && } ) } export const Header = withTranslation(['layout', 'landPage', 'common'], { withRef: true })( React.forwardRef( ( { headerMenuData, headerToolBarData, notification, allowTrade, selected, account, chainId, isWrap = true, landBtn, isLandPage = false, isMobile = false, toolBarMap = ButtonComponentsMap, i18n, t, transparent, application, ...rest }: HeaderProps & WithTranslation, ref: React.ForwardedRef, ) => { const history = useHistory() const theme = useTheme() const location = useLocation() const match = useRouteMatch('/:l1/:l2?/:pair?') const getMenuButtons = React.useMemo(() => { return Reflect.ownKeys(headerToolBarData ?? {}).map((item, index) => { return ( ) }) }, [account, isMobile, notification, headerToolBarData]) const getDrawerChoices: any = React.useCallback( ({ menuList, layer = 0, // onClose, handleListKeyDown, ...rest }: { menuList: HeaderMenuItemInterface[] layer?: number // onClose?: () => void; handleListKeyDown?: any } & WithTranslation) => { let _obj = {} if (menuList instanceof Array) { _obj[0] = menuList } else { _obj = menuList } return Reflect.ownKeys(_obj).map((key, index) => { return ( 0 ? 'column' : 'row'} > {!!_obj[key].length && _obj[key].reduce( (prev: JSX.Element[], props: HeaderMenuItemInterface, l2Index: number) => { const { label, child, status } = props if (status === HeaderMenuTabStatus.hidden) { return prev } else { if (child) { return [ ...prev, memoized({ ...props, layer, divider: index + 1 !== _obj[key].length, handleListKeyDown, ...rest, }), ] } let selectedFlag: boolean | undefined = undefined if (application === 'web-earn') { if (label.id === 'portal') { selectedFlag = match?.params['l1'] === 'portal' } else if (label.id === 'L2Assets') { selectedFlag = match?.params['l1'] === 'l2assets' } else if (label.id === 'dual') { selectedFlag = match?.params['l1'] === 'invest' && match?.params['l2'] === 'dual' } else if (label.id === 'btrade') { selectedFlag = match?.params['l1'] === 'trade' && match?.params['l2'] === 'btrade' } else if (label.id === 'taikoFarming') { selectedFlag = match?.params['l1'] === 'taiko-farming' } else { selectedFlag = false } } else { selectedFlag = match?.params[LAYERMAP[layer + 1]] && new RegExp(label.id?.toLowerCase(), 'ig').test( match?.params[LAYERMAP[layer + 1]], ) } return [ ...prev, ), style: { textDecoration: 'none' }, key: key.toString() + '-' + layer + l2Index, }} />, ] } }, [] as JSX.Element[], )} {Reflect.ownKeys(_obj).length !== index + 1 && } ) }) }, [isMobile, selected, account?.readyState, allowTrade], ) const memoized: any = React.useCallback( ({ label, router, child, layer, ref, handleListKeyDown: _handleListKeyDown, ...rest }: any) => ( any }) => { return getDrawerChoices({ menuList: child, layer: layer + 1, handleListKeyDown: () => { if (_handleListKeyDown) { _handleListKeyDown() } if (handleListKeyDown) { handleListKeyDown({ ...rest }) } }, ...rest, }) }, }} /> ), [allowTrade, getDrawerChoices, isMobile, match?.params], ) // const handleThemeClick = React.useCallback(() => { // setTheme(themeMode === "light" ? ThemeType.dark : ThemeType.light); // }, [themeMode, setTheme]); const isMaintaining = false const displayDesktop = React.useMemo(() => { return ( {!isLandPage && getDrawerChoices({ menuList: headerMenuData, i18n, t, ...rest, })} {isLandPage && ( {getDrawerChoices({ menuList: headerMenuLandingData, i18n, t, ...rest, })} )} {getMenuButtons} {!!isLandPage && landBtn ? landBtn : <>} ) }, [ isLandPage, getDrawerChoices, headerMenuData, i18n, t, rest, getMenuButtons, isMaintaining, history, ]) const popupState = usePopupState({ variant: 'popover', popupId: 'mobile', }) const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const displayMobile = React.useMemo(() => { const _headerMenuData: HeaderMenuItemInterface[] = (headerMenuData ?? []).reduce( (prev, _item) => { const item = _.cloneDeep(_item) if (item.label.id === RouterMainKey.l2assets) { item.child = { ...subMenuLayer2 } } return [...prev, item] }, [] as HeaderMenuItemInterface[], ) // @ts-ignore const pair = match?.params?.pair ?? 'LRC-ETH' return ( window.open('https://loopring.io/#', '_blank')} display={'inline-flex'} alignItems={'center'}> {isLandPage && headerMenuLandingData[0] ? ( <> {getMenuButtons} {landBtn ? landBtn : <>} { popupState.close() }} > { setMobileMenuOpen(true) }} > { return { title: t(item.label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, }), des: item.label.description ? t(item.label.description) : '', link: item.router?.path ?? '', logo: theme.mode === 'dark' ? item.logo?.dark : item.logo?.light, onClick: () => { window.open(item.router?.path ?? '', '_blank') }, } })} open={mobileMenuOpen} onClose={() => { setMobileMenuOpen(false) }} showColorSwitch /> ) : ( <> {getMenuButtons} { popupState.close() }} > { setMobileMenuOpen(true) }} > {application === 'webapp' ? ( { const childList = Array.isArray(item.child) ? item.child : _.values(item.child).flat() const hasChildren = childList && childList.length > 0 const highlightedMap = new Map([ [ '/pro', '/pro'], [ '/l2assets/assets/Tokens', '/l2assets'], [ '/markets', '/markets'], ]) const highlighted = !hasChildren && highlightedMap.get(history.location.pathname) === item.router?.path return { title: t(item.label.i18nKey, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, }), id: item.label.id, des: item.label.description ? t(item.label.description) : '', link: item.router?.path ?? '', logo: theme.mode === 'dark' ? item.logo?.dark : item.logo?.light, onClick: () => { if (!hasChildren) { history.push(item.router?.path ?? '') setMobileMenuOpen(false) } }, highlighted, subLinkList: hasChildren ? childList.map((subItem) => { if ( (history.location.pathname.startsWith('/trade/lite') && subItem.label.id === 'lite') || (history.location.pathname.startsWith('/trade/pro') && subItem.label.id === 'pro') || (history.location.pathname.startsWith('/trade/btrade') && subItem.label.id === 'btrade') || (history.location.pathname.startsWith('/invest/dual') && subItem.label.id === 'dual') ) { var highlighted = true } else { highlighted = history.location.pathname === subItem.router?.path } return { title: t(subItem.label.i18nKey), des: subItem.label.description ? t(subItem.label.description) : '', link: subItem.router?.path ?? '', LogoElement: subItem.label.icon, onClick: () => { history.push(subItem.router?.path ?? '') setMobileMenuOpen(false) }, highlighted } }) : undefined, } })} open={mobileMenuOpen} onClose={() => { setMobileMenuOpen(false) }} showSetting={true} showColorSwitch={false} /> ) : ( { return { title: t(item.label.i18nKey), des: item.label.description ? t(item.label.description) : '', link: item.router?.path ?? '', logo: theme.mode === 'dark' ? item.logo?.dark : item.logo?.light, onClick: () => { history.push(item.router?.path ?? '') setMobileMenuOpen(false) }, imgMarginRight: item.label.id === 'dual' ? -2 : 0, } })} open={mobileMenuOpen} onClose={() => { setMobileMenuOpen(false) }} showSetting={true} showColorSwitch={false} /> )} )} ) }, [ headerMenuData, match?.params, isLandPage, location.pathname, rest, t, getMenuButtons, headerToolBarData, i18n, isMaintaining, popupState, mobileMenuOpen, getDrawerChoices, history, ]) const paddingStyle = { paddingTop: 0, paddingRight: isLandPage ? theme.unit : theme.unit * 3, paddingBottom: 0, paddingLeft: isLandPage ? theme.unit : theme.unit * 3, } return ( {isWrap ? ( {isMobile ? displayMobile : displayDesktop} ) : ( {isMobile ? displayMobile : displayDesktop} )} ) }, ), ) ================================================ FILE: packages/component-lib/src/components/header/Interface.ts ================================================ import { Account, ButtonComponentsMap, HeaderMenuItemInterface, NOTIFICATIONHEADER, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' export interface HeaderToolBarInterface { buttonComponent: number args?: any } export interface HeaderProps { headerToolBarData: { [key in ButtonComponentsMap]: R } headerMenuData: HeaderMenuItemInterface[] notification?: NOTIFICATIONHEADER account?: Account chainId: sdk.ChainId allowTrade: { register: { enable: boolean; reason?: string } order: { enable: boolean; reason?: string } joinAmm: { enable: boolean; reason?: string } dAppTrade: { enable: boolean; reason?: string } raw_data: { enable: boolean; reason?: string } } isMobile: boolean isWrap?: boolean selected: string className?: string isLandPage?: boolean toolBarMap?: typeof ButtonComponentsMap transparent?: boolean landBtn?: JSX.Element application: string } ================================================ FILE: packages/component-lib/src/components/header/index.ts ================================================ export * from './Header' export * from './Interface' export * from './toolbar' ================================================ FILE: packages/component-lib/src/components/header/toolbar/Interface.ts ================================================ import React from 'react' import { AccountFull } from '@loopring-web/common-resources' export enum WalletNotificationStatus { none = 'none', error = 'error', pending = 'pending', success = 'success', } export type WalletNotificationInterface = { // status: keyof typeof WalletNotificationStatus message: string handleClick?: (event: React.MouseEvent) => void } export type WalletConnectBtnProps = { NetWorkItems: JSX.Element | (() => JSX.Element) handleClick: (_e: React.MouseEvent) => void handleClickUnlock: (_e: React.MouseEvent) => void handleClickSignIn: (_e: React.MouseEvent) => void accountState: AccountFull isLayer1Only?: boolean isShowOnUnConnect: boolean } ================================================ FILE: packages/component-lib/src/components/header/toolbar/WalletConnect.tsx ================================================ import { WalletConnectBtnProps } from './Interface' import { useTranslation } from 'react-i18next' import React from 'react' import { AccountStatus, ChainTests, CircleIcon, gatewayList, getShortAddr, LoadingIcon, LockIcon, myLog, UnConnectIcon, UnlockedIcon, } from '@loopring-web/common-resources' import { Typography, Box } from '@mui/material' import { Button, ButtonProps } from '../../basic-lib' import { bindHover, usePopupState } from 'material-ui-popup-state/hooks' import styled from '@emotion/styled' import { useSettings } from '../../../stores' import * as sdk from '@loopring-web/loopring-sdk' import { useSystem } from '@loopring-web/core' import { useLocation } from 'react-router' // type ChainId = sdk.ChainId | ChainIdExtends; const WalletConnectBtnStyled = styled(Button)` text-transform: none; min-width: 120px; padding-left: 2px; padding-right: 2px; &.wallet-btn { justify-content: center; } i { padding-right: ${({ theme }) => theme.unit / 2}px; display: flex; justify-content: center; align-content: space-between; svg { height: auto; font-size: ${({ theme }) => theme.fontDefault.h5}; } } &.wrong-network { background: var(--color-error); color: var(--color-text-primary); } ` const ProviderBox = styled(Box)` display: none; background-image: none; height: 100%; width: 48px; background-size: contain; background-repeat: no-repeat; background-position: center; ${({ account }) => { if (account && account.connectName) { const item = gatewayList.find(({ key }) => key === account.connectName) // connectName: keyof typeof ConnectProviders; return item?.imgSrc ? ` display: flex; background-image:url(${item.imgSrc}); ` : '' } }}; ` as (props: ButtonProps & { account: any }) => JSX.Element export const WalletConnectBtn = ({ accountState, handleClick, NetWorkItems, handleClickUnlock, handleClickSignIn }: WalletConnectBtnProps) => { const { t, i18n } = useTranslation(['layout', 'common']) const { isMobile, defaultNetwork } = useSettings() const { exchangeInfo } = useSystem() const location = useLocation() const isTaikoFarmingButNotTaikoNet = location.pathname === '/taiko-farming' && ![sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const {label, btnClassname, icon, isLocked} = React.useMemo(() => { const account = accountState?.account var label: string | undefined var btnClassname: string | undefined var icon: JSX.Element | undefined const isLocked = account.readyState === AccountStatus.LOCKED if (account && isTaikoFarmingButNotTaikoNet) { return { btnClassname: 'wrong-network', label: 'labelWrongNetwork', icon: , isLocked } } else if (account) { const addressShort = account.accAddress ? getShortAddr(account?.accAddress) : undefined if (addressShort) { label = addressShort } switch (account.readyState) { case AccountStatus.UN_CONNECT: btnClassname = 'un-connect' label = 'labelConnectWallet' break case AccountStatus.LOCKED: btnClassname = 'locked' icon = isMobile ? : break case AccountStatus.ACTIVATED: btnClassname = 'unlocked' icon = break case AccountStatus.NO_ACCOUNT: btnClassname = 'no-account' icon = break case AccountStatus.DEPOSITING: btnClassname = 'depositing' icon = break case AccountStatus.NOT_ACTIVE: btnClassname = 'not-active' icon = break case AccountStatus.ERROR_NETWORK: btnClassname = 'wrong-network' label = 'labelWrongNetwork' icon = break default: } } else { label = 'labelConnectWallet' } return { label: label ?? '', btnClassname, icon, isLocked } }, [accountState?.account?.readyState, i18n, isMobile]) const popupState = usePopupState({ variant: 'popover', popupId: `popupId: 'wallet-connect-notification'`, }) return ( <> {NetWorkItems} { ((isLocked || accountState?.account.readyState === AccountStatus.NOT_ACTIVE) && !isMobile) ? ( {t(label)} {exchangeInfo && } ) : ( btnClassname === ele) !== -1 ? 'contained' : 'outlined' } size={ ['un-connect', 'wrong-network'].findIndex((ele) => btnClassname === ele) !== -1 ? 'small' : 'medium' } color={'primary'} className={`wallet-btn ${btnClassname}`} onClick={handleClick} {...bindHover(popupState)} > {icon ? ( {icon} ) : ( <> )} {t(label)} {isLocked && ( { e.stopPropagation() handleClickUnlock(e) }} > )} )} ) } export const WalletConnectL1Btn = ({ accountState, handleClick, NetWorkItems, isShowOnUnConnect, }: // isShowOnUnConnect, WalletConnectBtnProps) => { const { t } = useTranslation(['layout', 'common']) // const { isMobile } = useSettings(); const [label, setLabel] = React.useState(t('labelConnectWallet')) const [btnClassname, setBtnClassname] = React.useState('') const [icon, setIcon] = React.useState() React.useEffect(() => { const account = accountState?.account if (account) { const addressShort = account.accAddress ? getShortAddr(account?.accAddress) : undefined if (addressShort) { setLabel(addressShort) } setIcon(undefined) myLog('wallet connect account.readyState:', account.readyState) switch (account.readyState) { case AccountStatus.UN_CONNECT: setBtnClassname('un-connect') setLabel('labelConnectWallet') break case AccountStatus.LOCKED: case AccountStatus.ACTIVATED: case AccountStatus.NO_ACCOUNT: case AccountStatus.DEPOSITING: case AccountStatus.NOT_ACTIVE: setBtnClassname('unlocked') const chainId = account._chainId as any switch (chainId) { case sdk.ChainId.MAINNET: setIcon( L1 , // ) break // case sdk.ChainId.GOERLI: // case ChainIdExtends[]: default: if (ChainTests.includes(Number(chainId))) { setIcon( Test , // ) } break // setIcon( // {ChainIdExtends[account._chainId]} // // // ); } break case AccountStatus.ERROR_NETWORK: setBtnClassname('wrong-network') setLabel('labelWrongNetwork') setIcon() break default: } } else { setLabel('labelConnectWallet') } }, [accountState?.account?.readyState]) const _handleClick = (event: React.MouseEvent) => { // debounceCount(event) if (handleClick) { handleClick(event) } } const popupState = usePopupState({ variant: 'popover', popupId: `popupId: 'wallet-connect-notification'`, }) return ( <> {NetWorkItems} {(!isShowOnUnConnect || accountState?.account?.readyState !== AccountStatus.UN_CONNECT) && ( btnClassname === ele) !== -1 ? 'contained' : 'outlined' } size={ ['un-connect', 'wrong-network'].findIndex((ele) => btnClassname === ele) !== -1 ? 'small' : 'medium' } color={'primary'} className={`wallet-btn ${btnClassname}`} onClick={_handleClick} {...bindHover(popupState)} > {icon ? ( {icon} ) : ( <> )} {t(label)} )} ) } ================================================ FILE: packages/component-lib/src/components/header/toolbar/index.tsx ================================================ import { Badge, Box, IconButton } from '@mui/material' import { Account, AccountStatus, CircleIcon, DownloadIcon, NotificationIcon, ProfileIcon, SettingIcon, NOTIFICATIONHEADER, ThemeType, DarkIcon, LightIcon, } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import { bindHover, usePopupState } from 'material-ui-popup-state/hooks' import { bindPopper } from 'material-ui-popup-state/es' import { PopoverPure, SubMenu, SubMenuList } from '../../basic-lib' import { SettingPanel } from '../../block/SettingPanel' import { NotificationPanel } from '../../block/NotificationPanel' import React, { useRef, useState } from 'react' import { DownloadPanel } from '../../block/DownloadPanel' import * as sdk from '@loopring-web/loopring-sdk' import { useSettings } from '../../../stores' export const BtnDownload = ({ t, url, i18nTitle, }: { i18nTitle: string i18nDescription: string url: string } & WithTranslation) => { const popupState = usePopupState({ variant: 'popover', popupId: 'downloadPop', }) return ( ) } export const BtnNotification = ({ notification, //:{notifyMap,myNotifyMap}, account, chainId, onClickExclusiveredPacket, showExclusiveRedpacket, exclusiveRedpacketCount, }: { notification: NOTIFICATIONHEADER account: Account chainId: sdk.ChainId onClickExclusiveredPacket: () => void showExclusiveRedpacket: boolean exclusiveRedpacketCount: number }) => { const popupState = usePopupState({ variant: 'popover', popupId: 'notificationPop', }) const popupStateEle = bindPopper(popupState) // bindHover(popupState) return ( {!notification?.myNotifyMap?.total && notification?.notifyMap?.notifications?.length ? ( ) : ( <> )} ) } export const BtnSetting = ({ t, label }: any) => { const popupState = usePopupState({ variant: 'popover', popupId: 'settingPop', }) return ( ) } export const BtnSettingMobile = ({ t, label }: any) => { const [popupMobileOpen, setPopupMobileOpen] = useState(false) const btn = useRef(null) return ( setPopupMobileOpen((prev) => !prev)} > setPopupMobileOpen(false)} > ) } export const ProfileMenu = ({ t, label, readyState, router, subMenu }: any) => { const popupState = usePopupState({ variant: 'popover', popupId: 'settingPop', }) return readyState == AccountStatus.ACTIVATED ? ( ) : ( <> ) } export const ColorSwitch = () => { const { setTheme, themeMode } = useSettings() const handleThemeClick = React.useCallback( (_e: any) => { setTheme(themeMode === ThemeType.dark ? ThemeType.light : ThemeType.dark) }, [themeMode], ) return ( {themeMode === ThemeType.dark ? : } ) } export * from './Interface' export * from './WalletConnect' ================================================ FILE: packages/component-lib/src/components/index.tsx ================================================ export * from './basic-lib' export * from './tradePanel' export * from './tableList' export * from './header' export * from './footer' export * from './basic-lib/popover' export * from './modal' export * from './block' export * from './styled' export * from './charts' export * from './toast' export * from './bottomRule' export * from './provider' export * from './share' export * from './carousel' ================================================ FILE: packages/component-lib/src/components/layout/AmmDetail.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { Box, Breadcrumbs, Container, GlobalStyles, Grid, Hidden, Link, Toolbar, Typography, } from '@mui/material' import { css, Theme, useTheme } from '@emotion/react' import { AmmDetailExtendProps, AmmInData, AmmPanelType, CoinInfo, EmptyValueTag, FloatTag, globalCss, headerMenuData, headerToolBarData, LinkedIcon, MyAmmLP, PriceTag, YEAR_DAY_FORMAT, } from '@loopring-web/common-resources' import { account, ammCalcData, coinMap, tradeCalcData } from '../../static' import { withTranslation } from 'react-i18next' import { useSettings } from '../../stores' import moment from 'moment' import { TradeTitle } from '../block' import { Header } from '../header' import React from 'react' import { AmmPanel, AmmProps } from '../tradePanel' import { Currency } from '@loopring-web/loopring-sdk' const Style = styled.div`` const tradeData: any = { coinA: { belong: 'ETH', balance: 0.3, tradeValue: 0 }, coinB: { belong: 'LRC', balance: 1000, tradeValue: 0 }, } const titleInfo: AmmDetailExtendProps, any> = { // tradeCalcData:, ammCalcData: ammCalcData, activity: { ruleType: 'SWAP_VOLUME_RANKING', market: 'LRC-ETH', status: 'NotStarted' as any, totalRewards: 1232141, myRewards: 122, rewardToken: coinMap['USDT'] as CoinInfo, duration: { from: new Date('2021-1-1'), to: new Date(), }, }, coinAInfo: coinMap[ammCalcData.myCoinA.belong] as CoinInfo, coinBInfo: coinMap[ammCalcData.myCoinB.belong] as CoinInfo, tradeFloat: { change: 1000, timeUnit: '24h', priceU: 1.23123, floatTag: FloatTag.increase, // tagNew: false, }, amountU: 197764.89, totalLPToken: 12132131, totalA: 0.002, totalB: 12344, totalAU: 0.002, totalBU: 12344, rewardToken: 'LRC', rewardA: 13, feeA: 121, feeB: 1232, isNew: true, isActivity: false, APR: 56, } const myAmm: MyAmmLP = { feeA: 122, feeB: 21, feeU: 0.0012, reward: 123, rewardToken: coinMap.DPR as CoinInfo, balanceA: 12131, balanceB: 0.0012, balanceU: 232, } const AmmDetailWrap = withTranslation('common')(({ t, ...rest }: any) => { const ammProps: AmmProps = { ammCalcDataDeposit: ammCalcData, ammCalcDataWithDraw: ammCalcData, refreshRef: React.createRef(), ammDepositData: tradeData, ammWithdrawData: tradeData, // tradeCalcData, handleAmmAddChangeEvent: (data, type) => { console.log('handleAmmAddChangeEvent', data, type) }, handleAmmRemoveChangeEvent: (data) => { console.log('handleAmmRemoveChangeEvent', data) }, onAmmRemoveClick: (data) => { console.log('onAmmRemoveClick', data) }, onAmmAddClick: (data) => { console.log('onAmmAddClick', data) }, } const WrapAmmPanel = (rest: any) => { return } const BoxStyled = styled(Box)` ${({ theme }) => theme.border.defaultFrame({ c_key: '' })}; background-color: var(--color-box); ` const { currency } = useSettings() return ( <>
    {/*style={{height: '100%' }}*/} {t('labelAmmList')} {tradeData.coinA.belong} {tradeData.coinB.belong} {t('labelBack')} {/**/} {/* */} {/**/} {t('labelLiquidity')} {t('labelAPR')} <> {' '} {typeof titleInfo.amountU === 'undefined' ? EmptyValueTag : currency === Currency.usd ? PriceTag.Dollar + titleInfo.amountU : 0} {' '} {titleInfo.APR ? titleInfo.APR : EmptyValueTag}% {t('labelLPTotal')} {t('labelLPTokens')} {titleInfo.totalLPToken} {t('labelLPTotal')} {titleInfo.ammCalcData?.myCoinA.belong} {titleInfo.totalA} {t('labelLPTotal')} {titleInfo.ammCalcData?.myCoinB.belong} {titleInfo.totalB} {titleInfo.tradeFloat.timeUnit} {t('labelVolume')} {t('labelFee')} {/* ' : '*/} {PriceTag.Dollar + titleInfo.tradeFloat.priceU} {titleInfo.ammCalcData?.myCoinA.belong} {titleInfo.feeA} {' + '} {titleInfo.ammCalcData?.myCoinB.belong} {titleInfo.feeB} {' '} {t('labelMyPoolShare')} {' '} {myAmm.balanceA ? myAmm.balanceA : EmptyValueTag}{' '} {titleInfo.ammCalcData?.myCoinA.belong}{' '} {' '} --{' '} {' '} {myAmm.balanceB ? myAmm.balanceA : EmptyValueTag}{' '} {titleInfo.ammCalcData?.myCoinB.belong} {' '} --{' '} {t('labelReward')} {typeof titleInfo.activity === 'undefined' ? ( EmptyValueTag ) : ( <> {titleInfo.activity.totalRewards}{' '} {titleInfo.activity.rewardToken.simpleName} )} {t('labelMyReward')} {typeof titleInfo.activity === 'undefined' ? ( EmptyValueTag ) : ( <> {myAmm.reward} {titleInfo.activity.rewardToken.simpleName} )} {t('labelDate')}: {typeof titleInfo.activity === 'undefined' ? EmptyValueTag : moment(titleInfo.activity.duration.from).format(YEAR_DAY_FORMAT) + ' - ' + moment(titleInfo.activity.duration.to).format(YEAR_DAY_FORMAT)} {/*
    */} ) }) const Template: Story = () => { const theme: Theme = useTheme() return ( <> {' '} ) } export default { title: 'components/Layout/AmmDetail', component: Template, argTypes: {}, } as Meta export const AmmDetailStory = Template.bind({}) // SwitchPanel.args = {} ================================================ FILE: packages/component-lib/src/components/layout/Error.stories.tsx ================================================ import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { Box, Container, GlobalStyles } from '@mui/material' import { css, Theme, useTheme } from '@emotion/react' import { globalCss } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import styled from '@emotion/styled' const StyleBox = styled(Box)` background-image: url('https://static.loopring.io/assets/images/error_bg.png'); background-repeat: no-repeat; background-attachment: fixed; background-position: bottom; ` as typeof Box const ErrorWrap = () => { const { messageKey }: { id?: string; messageKey: string } = { messageKey: 'errorMessageTokenMapIsEmpty', } const { t } = useTranslation('common') return ( <> {/*style={{height: '100%' }}*/} {t(messageKey)} {/*
    */} ) } const Template: Story = () => { const theme: Theme = useTheme() return ( <> ) } export default { title: 'components/Layout/Error', component: ErrorWrap, argTypes: {}, } as Meta export const ErrorStory = Template.bind({}) // SwitchPanel.args = {} ================================================ FILE: packages/component-lib/src/components/layout/layer2.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { Box, Collapse, Container, CssBaseline, GlobalStyles, Grid, Paper, Toolbar, Typography, } from '@mui/material' import { Header, HideOnScroll } from '../header' import { css, Theme, useTheme } from '@emotion/react' import { Button, SubMenu, SubMenuList as BasicSubMenuList } from '../basic-lib' import { globalCss, headerMenuData, headerToolBarData, PriceTag, subMenuLayer2, } from '@loopring-web/common-resources' import { withTranslation } from 'react-i18next' import { OrderHistoryTable as OrderHistoryTableUI } from '../tableList/orderHistoryTable' import { AssetTitle, AssetTitleProps } from '../block' import { AccountBasePanel, AccountBaseProps } from '../' import React from 'react' const Style = styled.div`` const SubMenuList = withTranslation('layout', { withRef: true })(BasicSubMenuList) const OrderHistoryTable = withTranslation('common', { withRef: true })(OrderHistoryTableUI) const AssetTitleWrap = (rest: any) => { const assetTitleProps: AssetTitleProps = { onShowReceive: () => {}, onShowSend: () => {}, accountId: 0, setHideL2Assets: () => undefined, hideL2Assets: false, assetInfo: { totalAsset: 123456.789, priceTag: PriceTag.Dollar, }, } return ( <> ) } const Layer2Wrap = withTranslation('common')(({ t, ...rest }: any) => { const selected = 'assets' const StylePaper = styled(Box)` width: 100%; height: 100%; flex: 1; background: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; padding: ${({ theme }) => theme.unit * 3}px; .tableWrapper { ${({ theme }) => theme.border.defaultFrame({ c_key: 'default', d_R: 1 })}; // margin-top: ${({ theme }) => theme.unit * 3}px; // border: 1px solid #252842; // border-radius: ${({ theme }) => theme.unit}px; // padding: 26px; } ` as typeof Paper const accountInfoProps: AccountBaseProps = { __timer__: -1, frozen: false, keySeed: '', qrCodeUrl: '', accAddress: '0x123567243o24o242423098784', accountId: 0, apiKey: '', connectName: 'unknown' as any, eddsaKey: undefined, etherscanUrl: 'https://material-ui.com/components/material-icons/', keyNonce: undefined, nonce: undefined, publicKey: undefined, readyState: 'unknown', level: 'VIP 1', mainBtn: ( ), } const hasAccount = true const [showAccountInfo, _setShowAccountInfo] = React.useState(hasAccount) // const handleClick = (_event: React.MouseEvent) => { // if (showAccountInfo) { // setShowAccountInfo(false); // } else { // setShowAccountInfo(true); // } // _event.stopPropagation(); // }; // headerMenuData["as"].extender = hasAccount ? ( // // {showAccountInfo ? : } // // ) : undefined; return ( <>
    {hasAccount ? ( ) : undefined} {/*style={{height: '100%' }}*/} Orders {}} /> {/*
    */} ) }) const Template: Story = () => { const theme: Theme = useTheme() console.log(theme.mode) return ( <> ) } export default { title: 'components/Layout/Layer2', component: Layer2Wrap, argTypes: {}, } as Meta export const Layer2Story = Template.bind({}) // SwitchPanel.args = {} ================================================ FILE: packages/component-lib/src/components/layout/trade.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { Box, Container, GlobalStyles, Toolbar } from '@mui/material' import { css, Theme, useTheme } from '@emotion/react' import { Header } from '../header' import { globalCss, headerMenuData, headerToolBarData } from '@loopring-web/common-resources' import { account, tradeCalcData } from '../../static' import { SwapPanel, SwapProps } from '../tradePanel' import React from 'react' const Style = styled.div`` const TradeWrap = () => { let tradeData: any = { sell: { belong: undefined }, buy: { belong: undefined }, } const WrapSwapPanel = () => { let swapProps: SwapProps = { campaignTagConfig: { SWAP: [], ORDERBOOK: [], MARKET: [], AMM: [], FIAT: [], } as any, refreshRef: React.createRef(), tradeData, // swapTradeData: tradeData, tradeCalcData, onSwapClick: () => { console.log('Swap button click') }, handleSwapPanelEvent: async (data: any, switchType: any) => { console.log(data, switchType) }, } return {...swapProps}> } return ( <>
    {/*style={{height: '100%' }}*/} {/*
    */} ) } const Template: Story = () => { const theme: Theme = useTheme() return ( <> {' '} ) } export default { title: 'components/Layout/Trade', component: TradeWrap, argTypes: {}, } as Meta export const TradeStory = Template.bind({}) // SwitchPanel.args = {} ================================================ FILE: packages/component-lib/src/components/modal/ClosureAnnouncementModal.tsx ================================================ import React from 'react' import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography, Box, // styled, // useTheme, } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { AlertIcon, SoursURL } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { useTheme } from '@emotion/react' // styled const StyledDialog = styled(Dialog)(({ theme }) => ({ '& .MuiDialog-paper': { borderRadius: theme.unit * 2, maxWidth: '480px', width: '90vw', maxHeight: '90vh', overflow: 'hidden', }, })) const StyledDialogTitle = styled(DialogTitle)(({ theme }) => ({ background: `linear-gradient(135deg, ${theme.colorBase.primary} 0%, ${theme.colorBase.warning} 100%)`, color: theme.colorBase.textPrimary, padding: theme.unit * 2, display: 'flex', alignItems: 'center', gap: theme.unit, fontSize: '1.25rem', fontWeight: 600, })) const StyledDialogContent = styled(Box)(({ theme }) => ({ paddingTop: theme.unit * 5, paddingBottom: theme.unit * 2, paddingLeft: theme.unit * 4, paddingRight: theme.unit * 4, maxHeight: '85vh', overflow: 'auto', '&::-webkit-scrollbar': { width: '6px', }, '&::-webkit-scrollbar-track': { backgroundColor: theme.colorBase.divide, borderRadius: '3px', }, '&::-webkit-scrollbar-thumb': { backgroundColor: theme.colorBase.textSecondary, borderRadius: '3px', }, })) const StyledDialogActions = styled(DialogActions)(({ theme }) => ({ padding: theme.unit * 2, borderTop: `1px solid ${theme.colorBase.divide}`, })) const HighlightDate = styled('span')(({ theme }) => ({ backgroundColor: theme.colorBase.warning, color: theme.colorBase.black, padding: '2px 8px', borderRadius: '4px', fontSize: '1.4rem', })) const SectionTitle = styled(Typography)(({ theme }) => ({ fontSize: '1.8rem', fontWeight: 600, color: theme.colorBase.textPrimary, marginBottom: theme.unit, display: 'flex', alignItems: 'center', gap: theme.unit, })) const BulletList = styled('ul')(({ theme }) => ({ paddingLeft: '8px', margin: `${theme.unit}px 0`, listStyle: 'none', '& li': { marginBottom: theme.unit * 0.5, color: theme.colorBase.textSecondary, lineHeight: 1.5, position: 'relative', paddingLeft: '8px', fontSize: '1.3rem', }, '& li::before': { content: '"•"', color: theme.colorBase.textSecondary, position: 'absolute', left: 0, }, })) const NetworkSection = styled(Box)(({ theme }) => ({ backgroundColor: theme.colorBase.box, borderRadius: theme.unit, marginBottom: theme.unit * 2, })) export const ClosureAnnouncementModal = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: () => void }) => { const theme = useTheme() return ( {}} // Prevent closing on outside click disableEscapeKeyDown // Prevent closing with Escape key aria-describedby='closure-announcement-dialog' > Loopring DeFi{' '} Closure {' '} Announcement {/* */} Loopring DeFi business will be shut down after July 31st, 2025, which allows the team to allocate all resources to focus on improving the Loopring Layer2 network. The impacted usages include: Dual Investment Portal Block Trade ETH Staking 📅 After the Sunset Date (July 31, 2025) 🔷 For Existing Positions: 1. Dual Investment:
  • No new subscriptions will be allowed after the sunset date.
  • All existing positions will be settled before July 31st.
  • 2. Portal:
  • All active positions will be force-closed after the sunset date.
  • 3. ETH Staking:
  • No new subscriptions will be accepted.
  • Users holding wstETH, rETH, or CiETH can still:
  • Redeem directly on Loopring Layer 2, or
  • Withdraw to Ethereum Layer 1 and interact directly with Lido, Rocket Pool, or the CIAN protocol for redemption.
  • 🌐 For Users on Taiko and Base Networks: After July 31st, Loopring will cease all services on both the Taiko and Base networks.
  • Users who do not withdraw their assets from their Loopring DeFi (Layer 3) account before the sunset date will have their funds automatically force-withdrawn to the upper layer (Taiko or Base).
  • Note: Accounts with dust balances (i.e., less than $0.10 USD) will not trigger a force-withdrawal operation.
    🌐 For Ethereum Users: Loopring Layer 2 will continue to operate as usual. All core features—including the fully decentralized exchange—will remain functional.
  • All assets, including tokens and NFTs, remain safe and intact on Layer 2.
  • However, all Loopring DeFi features will be permanently disabled after the sunset date.
  • Thank you to all our users and community members who have supported Loopring DeFi. While this chapter is closing, we remain committed to building and supporting decentralized infrastructure that empowers users and developers alike. If you have any questions, please reach out to our support channels.
    ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/ModalPanelBase.tsx ================================================ import { Box, Typography } from '@mui/material' import { Trans, WithTranslation } from 'react-i18next' import { DoneIcon, FailedIcon, RefuseIcon, SoursURL } from '@loopring-web/common-resources' import React from 'react' import { useTheme } from '@emotion/react' import { Button } from '../basic-lib' import { ConnectProviders } from '@loopring-web/web3-provider' export const InProgressBasic = ({ label, providerName, describe, }: { describe: JSX.Element label: string providerName: string } & WithTranslation) => { const providerDescribe = React.useMemo(() => { switch (providerName) { case ConnectProviders.MetaMask: return ( {/*Please adding MetaMask to your browser,*/} Please click approve button on MetaMask popup window. When MetaMask dialog is dismiss, please manually click MetaMask on your browser toolbar. ) case ConnectProviders.WalletConnect: case ConnectProviders.Coinbase: return ( Please click approve on your device. ) } }, [providerName]) return ( {label} {'loading'} {describe} {providerDescribe} ) } export const CompletedBasic = ({ t, label, describe, onClose, }: WithTranslation & { label: string describe: JSX.Element onClose: (event: any) => void }) => { const theme = useTheme() return ( {label} {describe} ) } export const FailedBasic = ({ onRetry, describe, label, t, }: { describe: JSX.Element onRetry: (event: any) => void label: string } & WithTranslation) => { return ( {label} {describe} ) } export const WarningBasic = ({ callback, describe, label, t, }: { describe: JSX.Element callback: (event: any) => void label: string } & WithTranslation) => { return ( {label} {describe} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/AccountBase.tsx ================================================ import React from 'react' import { Box, Button, Typography } from '@mui/material' import { CopyIcon, ExitIcon, getShortAddr, LinkIcon, SoursURL, } from '@loopring-web/common-resources' import { TFunction, Trans } from 'react-i18next' import styled from '@emotion/styled' import { AccountBaseProps } from './Interface' import { ConnectProviders } from '@loopring-web/web3-provider' const BoxStyled = styled(Box)` & .MuiButton-root { color: var(--color-text-secondary); &:hover { color: var(--color-text-primary); } } & .active { } & .unlock { svg { color: var(--color-error); } } & .lock { svg { color: var(--color-success); } } ` as typeof Box export const AccountBasePanel = ({ onDisconnect, accAddress, level, connectName, etherscanUrl, onCopy, hideVIPlevel, t, }: AccountBaseProps & { t: TFunction }) => { const addressShort = getShortAddr(accAddress) const etherscanLink = etherscanUrl + 'address/' + accAddress const connectBy = connectName === ConnectProviders.Unknown ? t('labelWrongNetwork') : connectName const getImagePath = React.useMemo(() => { const path = SoursURL + `images/vips/${level.toUpperCase().replace('_', '')}` return ( VIP ) }, [level]) return ( Connected with{' '} {connectName} . {addressShort} {!hideVIPlevel && level && getImagePath} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/AddAsset.tsx ================================================ import { Box, Typography } from '@mui/material' import { MenuBtnStyled } from '../../styled' import { AddAssetProps } from './Interface' import { useTranslation } from 'react-i18next' import { AddAssetList, AnotherIcon, BackIcon, CardIcon, ExchangeAIcon, IncomingIcon, L1L2_NAME_DEFINED, L1l2Icon, L2l2Icon, MapChainId, OutputIcon, } from '@loopring-web/common-resources' import { useSettings, useToggle } from '../../../stores' const IconItem = ({ svgIcon }: { svgIcon: string }) => { switch (svgIcon) { case 'IncomingIcon': return case 'CardIcon': return case 'L2l2Icon': return case 'L1l2Icon': return case 'ExchangeAIcon': return case 'OutputIcon': return case 'AnotherIcon': return } } export const AddAsset = ({ symbol, addAssetList, allowTrade, isNewAccount = false, disbaleList }: AddAssetProps) => { const { t } = useTranslation('common') const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { toggle: { receive }, } = useToggle() const isLp: boolean = symbol?.startsWith('LP-') ?? false const lpDisaList = [AddAssetList.BuyWithCard.key, AddAssetList.FromExchange.key] return ( {isNewAccount ? t('labelAddAssetTitleActive', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) : t('labelAddAssetTitle', { symbol, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {t('labelAddAssetHowto', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {addAssetList.reduce((prev, item) => { if ( !symbol || (item.key == 'FromAnotherNet' && receive['orbiter']?.includes(symbol)) || (item.key == 'FromExchange' && receive['layerSwap']?.includes(symbol)) || !['FromAnotherNet', 'FromExchange'].includes(item.key) ) { prev.push( } onClick={(e) => { item.handleSelect(e) }} > <>{IconItem({ svgIcon: item.svgIcon })} {t('label' + item.key, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} , ) } return prev }, [] as JSX.Element[])} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/BasicPanel.tsx ================================================ import { Box, Link, Typography, Divider } from '@mui/material' import { TFunction, Trans, withTranslation } from 'react-i18next' import { Account, DoneIcon, FailedIcon, L1L2_NAME_DEFINED, LinkIcon, LoadingIcon, MapChainId, RefuseIcon, SoursURL, SubmitIcon, } from '@loopring-web/common-resources' import React from 'react' import { Button, TextareaAutosizeStyled } from '../../basic-lib' import { RESULT_INFO } from '@loopring-web/loopring-sdk' import { ConnectProviders } from '@loopring-web/web3-provider' import { DropdownIconStyled } from '../../tradePanel' import { useSettings } from '../../../stores' import { sanitize } from 'dompurify' import styled from '@emotion/styled' import { TransErrorHelp } from '../../block' export enum IconType { LoadingIcon, DoneIcon, FailedIcon, RefuseIcon, SubmitIcon, PendingIcon, } export interface PanelProps { title?: string iconType?: IconType value?: number | string symbol?: string hash?: string info?: any describe1?: any describe2?: any chainInfos?: any txCheck?: { route: string callback: (e?: any) => void } to?: string btnInfo?: { btnTxt?: any param?: { [key: string]: string } callback?: (e?: any) => void } providerName?: ConnectProviders | 'unknown' | undefined link?: { name: string url: string } t: TFunction account?: Account etherscanBaseUrl?: string patch?: any error?: RESULT_INFO errorOptions?: any updateDepositHash?: any className?: string legacyTitleStyle?: boolean [key: string]: any } const BoxStyle = styled(Box)` &.btrade-panel { .status-icon { margin-top: ${({ theme }) => theme.unit * 4}px; } .content-main { align-self: stretch; & > div { align-self: stretch; } } } & { height: inherit; .content-main { overflow: auto; align-self: stretch; & > div { align-self: stretch; } } } ` export const BasicPanel = withTranslation('common', { withRef: true })( ({ t, title, iconType, describe1, describe2, txCheck, btnInfo, providerName, error, errorOptions, className, link, legacyTitleStyle = true, }: PanelProps) => { const isLoading = iconType === IconType.LoadingIcon const size = isLoading ? 60 : 60 // const marginTopIcon = isLoading ? 0 : 8; const marginTopDescribe1 = isLoading ? 2 : 3 // const marginProvider = 9; // const marginTopBtn = link ? 8 : 11; const marginToplink = 2 const iconDiv = React.useMemo(() => { switch (iconType) { case IconType.LoadingIcon: return ( {'loading'} ) case IconType.FailedIcon: return case IconType.SubmitIcon: return case IconType.RefuseIcon: return ( ) case IconType.DoneIcon: return ( ) case IconType.PendingIcon: return } }, [iconType, size]) const providerDescribe = React.useMemo(() => { if (providerName) { switch (providerName) { case ConnectProviders.MetaMask: case ConnectProviders.Coinbase: return ( Please click approve button on {providerName} popup window. When {providerName} dialog is dismiss, please manually click MetaMask on your browser toolbar. ) case ConnectProviders.WalletConnect: return ( Please click approve on your device. ) default: break } } return <> }, [providerName]) const [dropdownStatus, setDropdownStatus] = React.useState<'up' | 'down'>('down') const { defaultNetwork, isMobile } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( <> {legacyTitleStyle ? ( {t(title as string, { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, })} ) : ( <> {typeof title !== 'string' ? ( title ) : ( {t(title, { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, })} )} )} {iconDiv} {describe1 && ( {iconType === IconType.FailedIcon ? ( {error ? ( setDropdownStatus((prev) => (prev === 'up' ? 'down' : 'up')) } > ) : typeof describe1 === 'string' ? ( setDropdownStatus((prev) => (prev === 'up' ? 'down' : 'up')) } dangerouslySetInnerHTML={{ __html: sanitize(describe1), }} /> ) : ( <>{describe1} )} {dropdownStatus === 'up' && ( )} ) : typeof describe1 === 'string' ? ( ) : ( {describe1} )} {txCheck && ( )} )} {!!describe2 && <>{describe2}} {providerName && ( {providerDescribe} )} {btnInfo && ( {link && ( {link.name} {['Txn Hash', 'Banxa Status'].includes(link.name) && ( )} )} )} ) }, ) export const ConnectBase = (props: PanelProps) => { const propsPatch = { title: 'labelConnectWallet', } return } export const CreateAccountBase = (props: PanelProps) => { const propsPatch = { title: 'labelCreateAccount', } return } export const RetrieveAccountBase = (props: PanelProps) => { const propsPatch = { title: 'labelRetrieveAccount', } return } export const UnlockAccountBase = (props: PanelProps) => { const propsPatch = { ...props, title: 'labelUpdateAccount', } return } export const UpdateAccountBase = (props: PanelProps) => { const propsPatch = { title: props.patch?.isReset ? 'labelResetAccount' : 'labelUpdateAccount', } return } export const ExportAccountBase = (props: PanelProps) => { const propsPatch = { title: 'labelExportAccount', } return } export const DepositBase = (props: PanelProps) => { return } export const MintBase = (props: PanelProps) => { const propsPatch = { title: 'labelNFTMint', } return } export const DeployBase = (props: PanelProps) => { const propsPatch = { title: 'labelNFTDeployTitle', } return } export const ForceWithdrawBase = (props: PanelProps) => { const propsPatch = { title: 'labelForceWithdrawTitle', } return } export const ClaimWithdrawBase = (props: PanelProps) => { const propsPatch = { title: 'labelClaimWithdrawTitle', } return } export const TransferBase = (props: PanelProps) => { const propsPatch = { title: 'labelL2toL2Title', } return } export const WithdrawBase = (props: PanelProps) => { const propsPatch = { title: 'labelL2ToL1Title', } return } export const AmmBase = (props: PanelProps) => { return } export const DualBase = (props: PanelProps & { showTitle: boolean }) => { const { showTitle } = props return } export const VaultJoinBase = ( props: PanelProps & { title: string }, ) => { // const { showTitle, title } = props return ( ) } export const VaultExitBase = (props: PanelProps & { showTitle: boolean }) => { const { showTitle } = props return ( ) } export const VaultBorrowBase = ( props: PanelProps & { showTitle: boolean }, ) => { const { showTitle } = props return ( ) } export const VaultRepayBase = ( props: PanelProps & { showTitle: boolean }, ) => { const { showTitle } = props return ( ) } export const VaultTradeBase = (props: PanelProps & { showTitle: boolean }) => { const { showTitle } = props return ( ) } export const VaultDustCollectorBase = ( props: PanelProps & { showTitle: boolean }, ) => { const { showTitle } = props return ( ) } export const RedPacketBase = (props: PanelProps) => { const propsPatch = { title: 'labelSendRedPacketTitle', } return } export const RedPacketOpenBase = (props: PanelProps) => { const propsPatch = { title: 'labelRedPacketOpen', } return } export const BtradeBase = (props: PanelProps) => { const propsPatch = { title: 'labelBtradeTitle', } return } export const TaikoFarmingMintBase = (props: PanelProps) => { return } export const TaikoFarmingStakeBase = (props: PanelProps) => { return } export const TaikoFarmingRedeemBase = (props: PanelProps) => { return } export const TransferToTaikoBase = (props: PanelProps) => { return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/BridgePanel.tsx ================================================ import { withTranslation, WithTranslation } from 'react-i18next' import { AnotherIcon, MapChainId } from '@loopring-web/common-resources' import React from 'react' import { useSettings } from '../../../stores' import { Box, Typography } from '@mui/material' import { ChevronRight } from '@mui/icons-material' import { SpaceBetweenBox } from '../../../components/basic-lib' export interface BridgeProps { onClickEthereum: () => void } export const BridgePanel = withTranslation('common')( ({ onClickEthereum, }: BridgeProps) => { return ( Bridge To other networks onClickEthereum()} height={'64px'} border={'1px solid var(--color-border)'} borderRadius={'4px'} alignItems={'center'} justifyContent={'space-between'} sx={{ cursor: 'pointer', px: 2, mt: 2 }} leftNode={ To Ethereum } rightNode={} /> ) } ) ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/CheckActiveStatus.tsx ================================================ import { EmptyValueTag, L1L2_NAME_DEFINED, MapChainId, RowConfig, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import { Box, Button, Typography } from '@mui/material' import { useSettings } from '../../../stores' import { CheckActiveStatusProps } from './Interface' import { useTheme } from '@emotion/react' import { DepositRecorder } from './DepositRecorder' import styled from '@emotion/styled' const BoxStyle = styled(Box)` .modalContent { .depositRecord { width: 100%; padding: 0 ${({ theme }) => theme.unit}px; background: initial; } } ` export const CheckActiveStatus = ({ account, goSend, goDisconnect, isFeeNotEnough, // isDepositing = false, walletMap, knowDisable, know, onIKnowClick, chainInfos, accAddress, clearDepositHash, chargeFeeTokenList = [], ...props }: CheckActiveStatusProps) => { const { t } = useTranslation('common') const theme = useTheme() const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( {!know ? ( <> {t('labelActiveAccountTitle', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} <> {chainInfos && accAddress && chainInfos?.depositHashes && chainInfos?.depositHashes[accAddress] && chainInfos?.depositHashes[accAddress].length && clearDepositHash ? ( <> {t('labelDepositWaiting')} ) : ( <> {t('labelBenefitL2', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} )} ) : ( <> {t('labelActiveAccountTitle', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} {account.isContract ? ( <> {t('labelActivatedAccountNotSupport', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {t('labelActivatedAccountNotSupportDes', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) : ( <> {isFeeNotEnough.isOnLoading ? ( {t('labelFeeCalculating')} ) : isFeeNotEnough.isFeeNotEnough ? ( <> ) : ( // // {t("labelNotBalancePayForActive")} // {t('labelEnoughBalancePayForActive', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} )} {t('labelActivatedAccountChargeFeeList', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} {t('labelToken')} {t('labelMinRequirement')} {t('labelAvailability')} {chargeFeeTokenList?.map((item, index) => ( {item.belong} {item.fee} {walletMap && walletMap[item.belong] ? walletMap[item.belong].count : EmptyValueTag} ))} {isFeeNotEnough.isOnLoading ? ( {t('labelFeeCalculating')} ) : ( isFeeNotEnough.isFeeNotEnough && ( {t('labelHaveInProcessingL1toL2', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) )} )} )} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/CheckImportCollection.tsx ================================================ import { BackIcon, DropDownIcon, getShortAddr } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import { Box, ListItemText, Typography } from '@mui/material' import { useSettings } from '../../../stores' import { CheckImportCollectionProps } from './Interface' import React from 'react' import { Button, MenuItem, TextField } from '../../basic-lib' export const CheckImportCollection = ({ // account, value, onChange, loading = false, contractList = [], disabled = false, onClick, }: CheckImportCollectionProps) => { const { t } = useTranslation('common') const { isMobile } = useSettings() // const disabled = () => { // return gDisabled || !tradeData.tokenAddress || !tradeData.collectionMeta; // }; return ( {t('labelCheckImportCollectionTitle')} ) => { onChange(event.target?.value) }} inputProps={{ IconComponent: DropDownIcon }} fullWidth={true} > {contractList.map((item) => { return ( {getShortAddr(item)} } /> ) })} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/ClaimWithdraw.tsx ================================================ import { ClaimWithdrawBase, IconType, PanelProps } from './BasicPanel' import { L1L2_NAME_DEFINED, MapChainId, NFTWholeINFO } from '@loopring-web/common-resources' import { Typography } from '@mui/material' import { useSettings } from '../../../stores' export const ClaimWithdraw_WaitForAuth = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelClaimWithdrawWaitForAuth', { symbol: props.symbol, value: props.value, }), } return } export const ClaimWithdraw_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelClaimWithdrawDenied', { symbol: props.symbol, value: props.value, }), } return } export const ClaimWithdraw_First_Method_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied', { symbol: props.symbol, value: props.value, }), } return } export const ClaimWithdraw_In_Progress = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelClaimWithdrawInProgress', { symbol: props.symbol, value: props.value, }), } return } export const ClaimWithdraw_Failed = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelClaimWithdrawFailed', { symbol: props.symbol, value: props.value, }), } return } export const ClaimWithdraw_Submit = (props: PanelProps & Partial) => { const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.SubmitIcon, describe1: props.t('labelClaimWithdrawSubmit', { symbol: props.symbol, value: props.value, }), describe2: ( {props.t('labelTransferDelayConfirm', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/ClaimWithdrawPanel.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { FeeInfo, IBData, L1L2_NAME_DEFINED, MapChainId, TOAST_TIME, TradeBtnStatus, } from '@loopring-web/common-resources' import React from 'react' import { ClaimProps } from '../../tradePanel' import { Box, Grid, Typography } from '@mui/material' import { Button } from '../../basic-lib' import { useSettings } from '../../../stores' import { Toast, ToastType } from '../../toast' import { useTheme } from '@emotion/react' import { FeeSelect } from './FeeSelect' export const ClaimWithdrawPanel = withTranslation(['common', 'error'], { withRef: true, })( & { tradeValueView: string }, I, Fee extends FeeInfo>({ t, tradeData, feeInfo, btnInfo, btnStatus, isFeeNotEnough, handleFeeChange, chargeFeeTokenList, disabled, claimType, onClaimClick, isNFT, nftIMGURL, }: ClaimProps & WithTranslation & { assetsData: any[] }) => { const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [open, setOpen] = React.useState(false) const [showFeeModal, setShowFeeModal] = React.useState(false) const handleToggleChange = (value: Fee) => { if (handleFeeChange) { handleFeeChange(value) } } const getDisabled = React.useMemo(() => { return disabled || btnStatus === TradeBtnStatus.DISABLED }, [disabled, btnStatus]) const theme = useTheme() return ( {t('labelRedPacketClaimTitle', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {isNFT ? ( {tradeData?.tradeValueView + ' NFTs'} ) : ( {tradeData?.tradeValueView + ' ' + tradeData?.belong} )} {t('labelRedPacketFrom')} {t(`labelClaim${claimType}`, { symbol: tradeData?.belong })} {t('labelRedPacketTo', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {t('labelToMyL2', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as Fee) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} { setOpen(false) }} severity={ToastType.error} /> ) }, ) as (props: ClaimProps & React.RefAttributes) => JSX.Element ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/CoinbaseSmartWalletModal.tsx ================================================ import { Box, Button, FormControl, FormHelperText, IconButton, Input, InputAdornment, Modal, OutlinedInput, Typography, } from '@mui/material' import styled from '@emotion/styled' import { ModalCloseButton } from '../../basic-lib' import { withTranslation } from 'react-i18next' import { AccountStep } from './Interface' import { useOpenModals, useSettings } from '../../../stores' import React from 'react' import { CheckIcon, CloseIcon, ErrorIcon, FailedIcon, hexToRGB, LoadingIcon, LoadingIcon2, SoursURL } from '@loopring-web/common-resources' import { keyframes, useTheme } from '@emotion/react' import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew' import Visibility from '@mui/icons-material/Visibility' import VisibilityOff from '@mui/icons-material/VisibilityOff' import { range } from 'lodash' // 创建旋转动画 const spin = keyframes` from { transform: rotate(0deg); } to { transform: rotate(360deg); } ` // 旋转的LoadingIcon2组件 const RotatingLoadingIcon = styled(LoadingIcon2)` animation: ${spin} 1.5s linear infinite; transform-origin: center; ` // 步骤指示器组件定义 enum StepStatus { INIT = 'init', PROCESSING = 'processing', COMPLETED = 'completed', } interface Step { rightElement: React.ReactNode } interface VerticalStepperProps { steps: Step[] currentStep: number } const StepperContainer = styled(Box)` display: flex; flex-direction: column; align-items: center; gap: 20px; width: 100%; ` const StepCircle = styled(Box)<{ status: StepStatus }>` width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background-color: ${({ status, theme }) => status === StepStatus.COMPLETED ? hexToRGB(theme.colorBase.success, 0.1) : hexToRGB(theme.colorBase.boxSecondary, 1)}; color: var(--color-text-button); svg { height: 24px; width: 24px; } ` const ConnectingLine = styled(Box)<{ isActive: boolean }>` width: 2px; position: absolute; background: ${({ isActive }) => isActive ? `repeating-linear-gradient( to bottom, var(--color-success), var(--color-success) 4px, transparent 4px, transparent 8px )` : `repeating-linear-gradient( to bottom, var(--field-opacity), var(--field-opacity) 4px, transparent 4px, transparent 8px )` }; ` const VerticalStepper: React.FC = ({ steps, currentStep, }) => { const circleRefs = React.useRef<(HTMLDivElement | null)[]>([]); circleRefs.current = circleRefs.current.slice(0, steps.length); const positions = (() => { const positions = circleRefs.current.map((ref) => { if (!ref) return null; const containerRect = ref.parentElement!.getBoundingClientRect() const rect = ref.getBoundingClientRect(); return { x: rect.left - containerRect.left, y: rect.top - containerRect.top, }; }); return positions; })(); return ( {steps.map((step, index) => { const status = currentStep > index ? StepStatus.COMPLETED : currentStep === index ? StepStatus.PROCESSING : StepStatus.INIT return ( { circleRefs.current[index] = el }} key={index} width={'100%'}> {status === StepStatus.COMPLETED ? ( ) : status === StepStatus.PROCESSING ? ( ) : ( <> )} {step.rightElement} )})} {range(0, steps.length - 1).map((index) => ( {index < steps.length - 1 && positions && positions[index] && positions[index + 1] && ( index} sx={{ left: `${23 + (positions[index]?.x || 0)}px`, top: `${(positions[index]?.y || 0) + 52}px`, height: `${ (positions[index + 1]?.y || 0) - (positions[index]?.y || 0) - 56 }px`, zIndex: 0, }} /> )} ))} ) } const Coinbase_Smart_Wallet_Password_Intro = withTranslation('common')( ({ t, onClickConfirm }: { t: any; onClickConfirm: () => void }) => { return ( {/* */} Sign-In Password Required for
    Coinbase Smart Wallet
    Coinbase Smart Wallet detected.
    To use Loopring DeFi securely, you’ll need to set a password to locally encrypt your EDDSA signature.
    When you close the Loopring dApp and return later, you’ll be required to sign in with your password to continue.
    ) }, ) const isValidPassword = (password: string): boolean => { const hasValidLength = password.length >= 6 && password.length <= 20; const hasLetters = /[a-zA-Z]/.test(password); const hasNumbers = /[0-9]/.test(password); const onlyLetterOrNumber = /^[a-zA-Z0-9]+$/.test(password); return hasValidLength && hasLetters && hasNumbers && onlyLetterOrNumber; } const Coinbase_Smart_Wallet_Password_Set = withTranslation('common')( ({ t, onClickConfirm, onClickBack }: { t: any onClickConfirm: (pwd: string) => void onClickBack: () => void }) => { const [password, setPassword] = React.useState('') const [confirmPassword, setConfirmPassword] = React.useState('') const [showPassword, setShowPassword] = React.useState(false) const error = password && !isValidPassword(password) ? 'format' : confirmPassword && password !== confirmPassword ? 'notMatch' : undefined const handleToggleShowPassword = () => { setShowPassword(!showPassword) } const handleConfirm = () => { onClickConfirm(password) } return ( Set Password Set Password setPassword(e.target.value)} endAdornment={ showPassword ? ( ) : ( ) } sx={{ backgroundColor: 'transparent', border: '1px solid var(--color-border)', borderRadius: '8px', height: '48px', fontSize: '20px', pl: 1.5, }} disableUnderline /> *6–20 chars, letters + numbers Confirm Password setConfirmPassword(e.target.value)} sx={{ backgroundColor: 'transparent', borderRadius: '8px', border: '1px solid var(--color-border)', height: '48px', fontSize: '20px', pl: 1.5, }} /> Not match ) }, ) const Coinbase_Smart_Wallet_Password_Set_Confirm = withTranslation('common')(({onClickProceed}: {onClickProceed: () => void}) => { return ( The following steps require multiple interactions with your Coinbase Smart Wallet. If the wallet extension does not automatically prompt you for action, please open it manually to proceed. ) }) const Coinbase_Smart_Wallet_Password_Input = withTranslation('common')( ({ t, onClickConfirm, onClickForgetPassword, inputDisabled, showPasswordMismatchError, onInputPassword }: { t: any onClickConfirm: (pwd: string) => void onClickForgetPassword: () => void inputDisabled?: boolean showPasswordMismatchError: boolean onInputPassword: () => void }) => { const [password, setPassword] = React.useState('') const [showPassword, setShowPassword] = React.useState(false) const handleToggleShowPassword = () => { setShowPassword(!showPassword) } const handleConfirm = () => { onClickConfirm(password) } return ( {t('labelSignIn')} {!inputDisabled && ( <> Input Password { setPassword(e.target.value) onInputPassword() }} endAdornment={ showPassword ? ( ) : ( ) } sx={{ backgroundColor: 'transparent', border: '1px solid var(--color-border)', borderRadius: '8px', height: '48px', fontSize: '16px', pl: 2, }} disableUnderline /> {showPasswordMismatchError && ( Authentication failed: password mismatch )} )} {inputDisabled && ( No corresponding password found, please reset password )} Forget Password? ) }, ) const Coinbase_Smart_Wallet_Password_Forget_Password = withTranslation('common')( ({ t, onClickConfirm }: { t: any onClickConfirm: () => void }) => { const theme = useTheme() return ( Reset Loopring DeFi Account & Password Please note: this operation will not affect your assets - only your EDDSA key will be replaced with a new one. This process requires confirmation via your wallet. Please approve the signature request in your wallet to proceed. ) }, ) const Coinbase_Smart_Wallet_Password_Forget_Password_Confirm = withTranslation('common')( ({ t, onClickConfirm, onClickBack }: { t: any onClickConfirm: () => void onClickBack: () => void }) => { const [isAcknowledged, setIsAcknowledged] = React.useState(false) const theme = useTheme() return ( Forget Password You will need to reset your Loopring DeFi account. Please note:
    · Any active positions (such as trades in the Loopring Portal) will be automatically closed upon reset.
    · If you have pending Dual Investment positions, you must wait for them to settle before you can reset your account.

    For assistance during the reset process, please contact: support@loopring.io
    setIsAcknowledged(!isAcknowledged)} > {isAcknowledged && ( )} I acknowledge and would like to proceed.
    ) }, ) const Coinbase_Smart_Wallet_Password_Set_Processing = withTranslation('common')( ({ step, showResumeUpdateAccount }: { step: 'keyGenerating' | 'blockConfirming' | 'updatingAccount' | 'completed'; showResumeUpdateAccount: boolean }) => { const { isMobile } = useSettings() return ( Loopring DeFi Account Creation Sign In & Generate EDDSA Key ), }, { rightElement: ( Submit EDDSA Key & Wait for Block Confirmation It requires a certain number of block confirmations. The dApp will automatically monitor the progress and proceed to Step 3 once confirmations are complete. ), }, { rightElement: ( Update Account ), }, ]} currentStep={step === 'keyGenerating' ? 0 : step === 'blockConfirming' ? 1 : step === 'updatingAccount' ? 2 : 3} /> {showResumeUpdateAccount && It appears you’ve already generated your EDDSA key, but did not complete the final “Update Account” step in your previous session. } ) }, ) const Coinbase_Smart_Wallet_Password_Set_Error = withTranslation('common')( ({ onClickConfirm }: { onClickConfirm: () => void }) => { return ( We're currently unable to create your Loopring DeFi account, which may be due to network conditions. Please try again later. If the issue persists, feel free to contact us at support@loopring.io for assistance. ) }, ) const Coinbase_Smart_Wallet_Password_Get_Error = withTranslation('common')( ({ onClickConfirm }: { onClickConfirm: () => void }) => { return ( We're currently unable to retrieve your EDDSA key from the Loopring server, possibly due to network conditions. Please try again later. If the issue persists, don't hesitate to contact us at support@loopring.io for further assistance. ) }, ) export { Coinbase_Smart_Wallet_Password_Intro, Coinbase_Smart_Wallet_Password_Set, Coinbase_Smart_Wallet_Password_Set_Confirm, Coinbase_Smart_Wallet_Password_Input, Coinbase_Smart_Wallet_Password_Forget_Password_Confirm, Coinbase_Smart_Wallet_Password_Forget_Password, Coinbase_Smart_Wallet_Password_Set_Processing, Coinbase_Smart_Wallet_Password_Set_Error, Coinbase_Smart_Wallet_Password_Get_Error } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/Connect.tsx ================================================ import { ConnectProviders } from '@loopring-web/web3-provider' import { ConnectBase, IconType, PanelProps } from './BasicPanel' import { useSettings } from '../../../stores' import { Box, Typography } from '@mui/material' // value symbol export const CommonConnectInProgress = (props: PanelProps) => { const { isMobile } = useSettings() const providerName = props.providerName const propsPatch = { providerName, iconType: IconType.LoadingIcon, describe1: props.t('labelProviderProcessing', { name: isMobile ? 'DApp, network:' + props?.network : providerName ? providerName + ', network:' + props?.network : props.t('labelUnknown') + ', network:' + props?.network, }), } return } export const WalletConnectConnectInProgress = (props: PanelProps) => { const propsPatch = { providerName: ConnectProviders.WalletConnect, iconType: IconType.LoadingIcon, describe1: props.t('labelProviderProcessing', { name: ConnectProviders.WalletConnect, }), } return } // value symbol export const ConnectSuccess = (props: PanelProps) => { const propsPatch = { providerName: undefined, iconType: IconType.DoneIcon, describe1: props.t('labelSuccessConnect', { providerName: props.providerName, }), describe2: ( {props.t('labelSuccessConnectDescribe')} ), } return } // value symbol export const ConnectFailed = ({ NetWorkItems, providerName, ...props }: PanelProps) => { const propsPatch = { providerName: undefined, iconType: IconType.FailedIcon, describe1: props.t('labelFailedConnect'), describe2: NetWorkItems ? <>{NetWorkItems} : '', } return } export const ConnectReject = ({ NetWorkItems, providerName, ...props }: PanelProps) => { const propsPatch = { providerName: undefined, iconType: IconType.RefuseIcon, describe1: props.t('labelRejectConnect'), describe2: NetWorkItems ? <>{NetWorkItems} : '', } return } export const ConnectRejectSwitchNetwork = ({ NetWorkItems, providerName, ...props }: PanelProps) => { const propsPatch = { title: 'labelRejectSwitchNetwork', // providerName, iconType: IconType.RefuseIcon, describe1: NetWorkItems ? <>{NetWorkItems} : '', } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/CreateAccount.tsx ================================================ import { CreateAccountBase, IconType, PanelProps } from './BasicPanel' import { AlertIcon2, L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { Button, Checkbox, Modal, Box, Typography } from '@mui/material' import { useState } from 'react' import { Trans } from 'react-i18next' import { useTheme } from '@emotion/react' import { CheckBoxIcon, CheckedIcon } from '@loopring-web/common-resources' // symbol export const CreateAccount_Approve_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelTokenAccess', { symbol: props.symbol }), } return } // symbol export const CreateAccount_Approve_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFailedTokenAccess', { symbol: props.symbol }), } return } // symbol export const CreateAccount_Approve_Submit = (props: PanelProps) => { const propsPatch = { iconType: IconType.SubmitIcon, describe1: props.t('labelSuccessTokenAccess', { symbol: props.symbol }), } return } // value symbol export const CreateAccount_WaitForAuth = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelL1toL2WaitForAuth', { symbol: props.symbol, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, }), } return } // value symbol export const CreateAccount_Denied = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelCreateAccountDepositDenied', { layer2: L1L2_NAME_DEFINED[network].layer2, symbol: props.symbol, }), } return } // value symbol export const CreateAccount_Failed = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelCreateAccountFailed', { layer2: L1L2_NAME_DEFINED[network].layer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, value: props.value, symbol: props.symbol, }), } return } // value symbol export const CreateAccount_Submit = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.SubmitIcon, describe1: props.t('labelCreateAccountSubmit', { layer2: L1L2_NAME_DEFINED[network].layer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, value: props.value, symbol: props.symbol, count: 30, }), } return } export const CreateAccount_EOA_Only_Alert = (props: { onClose: () => void onConfirm: () => void }) => { const [acknowledged, setAcknowledged] = useState(false) const theme = useTheme() return ( Please ensure that you are activating your Loopring account using an EOA (Externally Owned Account) wallet. Currently, only EOA wallets are supported on the Loopring network. Smart contract wallets are not supported.
    Depositing assets to a smart wallet on the Loopring network may result in permanent loss of those assets. Please exercise caution.
    } icon={} color='default' sx={{ height: 16, width: 16, mr: 1 }} onChange={(_, checked) => { setAcknowledged(checked) }} /> I acknowledge the risk and would like to proceed
    ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/CreateRedPacketPanel.tsx ================================================ import { useTranslation } from 'react-i18next' import { SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { CreateRedPacketProps, RedPacketStep, TargetRedPacketStep } from '../../tradePanel' import { FeeInfo, LuckyRedPacketList, NFTWholeINFO, RedPacketOrderData, RedPacketOrderType, } from '@loopring-web/common-resources' import { HorizontalLabelPositionBelowStepper, TradeMenuList, useBasicTrade, } from '../../tradePanel/components' import React from 'react' import { cloneDeep } from 'lodash' import { CreateRedPacketScope, CreateRedPacketStepTokenType, CreateRedPacketStepType, CreateRedPacketStepWrap, TargetRedpacktInputAddressStep, TargetRedpacktSelectStep, } from '../../tradePanel/components/CreateRedPacketWrap' import { Box, styled } from '@mui/material' import { LuckyTokenAmountType, LuckyTokenClaimType, LuckyTokenViewType, } from '@loopring-web/loopring-sdk' import moment from 'moment' const BoxStyle = styled(Box)` &.createRedPacket { .container { align-items: center; display: flex; } } ` export const CreateRedPacketPanel = < T extends Partial>, I extends any, C = FeeInfo, NFT = NFTWholeINFO, >({ tradeType, tradeData, disabled, handleOnDataChange, walletMap = {}, coinMap = {}, tokenMap = {}, assetsData, myNFTPanel, onSendTargetRedpacketClick, targetRedPackets, popRedPacket, popRedPacketAmountStr, onClickViewTargetDetail, onCloseRedpacketPop, contacts, isWhiteListed, showExclusiveOption, ...rest }: CreateRedPacketProps & { assetsData: any[] }) => { const { t, i18n, ready: tReady } = useTranslation(['common', 'error']) const { redPacketConfig } = rest const { onChangeEvent, index, switchData } = useBasicTrade({ ...rest, coinMap, type: tradeType, // index, walletMap, } as any) const [panelIndex, setPanelIndex] = React.useState( tradeType === RedPacketOrderType.FromNFT ? RedPacketStep.ChooseType : RedPacketStep.TradeType, ) let steps: string[] if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { if (tradeType === RedPacketOrderType.FromNFT) { steps = [ 'labelRedPacketChoose', //Prepare NFT metadata 'labelRedPacketMain', //labelADMint2 'labelRedPacketRecipientList', //labelADMint2 ] } else { steps = [ 'labelRedPacketChooseTarget', //labelADMint2 'labelRedPacketTypeTokens', //labelADMint2 'labelRedPacketChoose', //Prepare NFT metadata 'labelRedPacketMain', //labelADMint2 'labelRedPacketRecipientList', //labelADMint2 ] } } else { if (tradeType === RedPacketOrderType.FromNFT) { steps = [ 'labelRedPacketChoose', //Prepare NFT metadata 'labelRedPacketMain', //labelADMint2 ] } else { steps = [ 'labelRedPacketTypeTokens', //labelADMint2 'labelRedPacketChoose', //Prepare NFT metadata 'labelRedPacketMain', //labelADMint2 ] } } React.useEffect(() => { if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { if (tradeData.target?.redpacketHash) { setActiveStep(TargetRedPacketStep.TargetSend) } else if (!isToken && tradeData.nftData && panelIndex === TargetRedPacketStep.NFTList) { setActiveStep(TargetRedPacketStep.Main) } } else { if (!isToken && tradeData.nftData && panelIndex === RedPacketStep.NFTList) { setActiveStep(RedPacketStep.Main) } } }, [tradeData?.nftData, panelIndex, tradeType, tradeData.target?.redpacketHash]) React.useEffect(() => { if (tradeData.tradeType !== RedPacketOrderType.FromNFT) { handleOnDataChange({ type: { ...tradeData.type, scope: LuckyTokenViewType.PRIVATE, mode: LuckyTokenClaimType.BLIND_BOX, partition: LuckyTokenAmountType.RANDOM, }, tradeType: RedPacketOrderType.BlindBox, } as any) } else { handleOnDataChange({ type: { ...tradeData.type, scope: LuckyTokenViewType.PRIVATE, mode: LuckyTokenClaimType.BLIND_BOX, partition: LuckyTokenAmountType.RANDOM, }, } as any) } }, []) const setActiveStep = React.useCallback( (index: RedPacketStep | TargetRedPacketStep) => { if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { switch (index) { case TargetRedPacketStep.TargetChosse: setPanelIndex(0) break case TargetRedPacketStep.TradeType: if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(0) } else { setPanelIndex(1) } break case TargetRedPacketStep.ChooseType: if (tradeType !== RedPacketOrderType.FromNFT) { handleOnDataChange({ collectionInfo: undefined, tokenId: undefined, tradeValue: undefined, balance: undefined, nftData: undefined, belong: undefined, tokenAddress: undefined, image: undefined, } as any) } if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(0) } else { setPanelIndex(2) } break case TargetRedPacketStep.Main: handleOnDataChange({ validSince: Date.now(), } as any) if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(1) } else { setPanelIndex(3) } break case TargetRedPacketStep.NFTList: if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(2) } else { setPanelIndex(4) } break case TargetRedPacketStep.TargetSend: if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(3) } else { setPanelIndex(5) } break } } else { switch (index) { case RedPacketStep.TradeType: setPanelIndex(0) break case RedPacketStep.ChooseType: if (tradeType !== RedPacketOrderType.FromNFT) { handleOnDataChange({ collectionInfo: undefined, tokenId: undefined, tradeValue: undefined, balance: undefined, nftData: undefined, belong: undefined, tokenAddress: undefined, image: undefined, } as any) } if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(0) } else { setPanelIndex(1) } break case RedPacketStep.Main: handleOnDataChange({ validSince: Date.now(), } as any) if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(1) } else { setPanelIndex(2) } break case RedPacketStep.NFTList: if (tradeType === RedPacketOrderType.FromNFT) { setPanelIndex(2) } else { setPanelIndex(3) } break } } }, [tradeData.type?.scope], ) React.useEffect(() => { setPanelIndex((state) => { if (state > 1) { if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { return index + 3 } else { return index + 2 } } else { return state } }) }, [index, tradeData.type?.scope]) // LP token should not exist in withdraw panel for now const getWalletMapWithoutLP = React.useCallback(() => { const clonedWalletMap = cloneDeep(walletMap ?? {}) const keyList = Object.keys(clonedWalletMap) keyList.forEach((key) => { const [first] = key.split('-') if (first === 'LP') { delete clonedWalletMap[key] } }) return clonedWalletMap }, [walletMap]) const [selectedType, setSelectType] = React.useState( tradeData.tradeType === RedPacketOrderType.NFT ? LuckyRedPacketList.find((config) => config.tags?.includes('defaultForNFT')) : tradeData.tradeType === RedPacketOrderType.BlindBox ? LuckyRedPacketList.find((config) => config.tags?.includes('defaultForBlindBox') ) : tradeData.tradeType === RedPacketOrderType.FromNFT ? LuckyRedPacketList.find((config) => config.tags?.includes('defaultForFromNFT')) : LuckyRedPacketList.find((config) => config.tags?.includes('defaultForERC20')), ) const [privateChecked, setPrivateChecked] = React.useState(false) const isToken = tradeType === RedPacketOrderType.TOKEN || (tradeType === RedPacketOrderType.BlindBox && !tradeData.isNFT) const [showScope, setShowScope] = React.useState(true) const backToScope = () => setShowScope(true) const props: SwitchPanelProps = React.useMemo(() => { const isTarget = tradeData.type?.scope === LuckyTokenViewType.TARGET let showNFT = isTarget ? redPacketConfig.showNFT : redPacketConfig.showNFT && tradeData.type?.scope !== LuckyTokenViewType.PUBLIC const tradeMenuListPanel = { key: 'tradeMenuList', element: ( ), toolBarItem: undefined, } const nftListPanel = { key: 'nftList', element: myNFTPanel, toolBarItem: undefined, } const selectTypePanel = { key: 'selectType', element: ( { if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { setActiveStep(TargetRedPacketStep.Main) } else { setActiveStep(RedPacketStep.Main) } }, } as any)} privateChecked={privateChecked} onChangePrivateChecked={() => { handleOnDataChange({ type: { ...tradeData?.type, scope: !privateChecked ? 1 : 0, }, } as any) setPrivateChecked(!privateChecked) }} backToScope={backToScope} showNFT={showNFT} onSelecteValue={(item) => { setSelectType(item) if (tradeType === RedPacketOrderType.BlindBox) { handleOnDataChange({ isNFT: item.isBlindboxNFT ? true : false, type: { ...tradeData?.type, partition: item.value.partition, mode: item.value.mode, }, } as any) } else { handleOnDataChange({ type: { ...tradeData?.type, partition: item.value.partition, mode: item.value.mode, }, } as any) } }} /> ), toolBarItem: undefined, } const tradePanel = { key: 'trade', element: ( // @ts-ignore ), toolBarItem: undefined, } const selectTokenTypePanel = { key: 'selectTokenType', element: ( { if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { setActiveStep(TargetRedPacketStep.TargetChosse) } else { backToScope() } }, onClickNext: () => { if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { setActiveStep(TargetRedPacketStep.ChooseType) } else { setActiveStep(RedPacketStep.ChooseType) } }, showNFT, onChangeTradeType: (tradeType) => { const found = tradeType === RedPacketOrderType.NFT ? LuckyRedPacketList.find((config) => config.tags?.includes('defaultForNFT')) : tradeType === RedPacketOrderType.BlindBox ? LuckyRedPacketList.find((config) => config.tags?.includes('defaultForBlindBox'), ) : tradeType === RedPacketOrderType.FromNFT ? LuckyRedPacketList.find((config) => config.tags?.includes('defaultForFromNFT')) : LuckyRedPacketList.find((config) => config.tags?.includes('defaultForERC20')) setSelectType(found) handleOnDataChange({ type: { ...tradeData?.type, partition: found!.value.partition, mode: found!.value.mode, }, isNFT: tradeType === RedPacketOrderType.NFT || tradeType === RedPacketOrderType.FromNFT || (tradeType === RedPacketOrderType.BlindBox && !redPacketConfig?.showERC20Blindbox) ? true : false, tradeType, } as any) }, } as any)} /> ), toolBarItem: undefined, } const targetSelectPanel = { key: 'targetSelect', element: ( { handleOnDataChange({ target: { ...tradeData.target, redpacketHash: info.hash, maxSendCount: info.remainCount, }, } as any) setActiveStep(TargetRedPacketStep.TargetSend) }} targetRedPackets={targetRedPackets} onClickCreateNew={() => { window.scrollTo(0, 0) setActiveStep(TargetRedPacketStep.TradeType) }} onClickViewDetail={(hash) => { onClickViewTargetDetail(hash) }} {...{ ...rest }} /> ), toolBarItem: undefined, } const maximumTargetsLength = tradeData.target?.maxSendCount ?? 0 const targetInputAddressPanel = { key: 'targetInputAddress', element: ( { handleOnDataChange({ target: { ...tradeData.target, popupChecked, }, } as any) }} onFileInput={(input) => { handleOnDataChange({ target: { ...tradeData.target, addressListString: input .split(';') .map((value) => value.trim()) .slice(0, maximumTargetsLength) .join(';\n'), }, } as any) }} onManualEditInput={(input) => { handleOnDataChange({ target: { ...tradeData.target, addressListString: input, }, } as any) }} onClickSend={() => { onSendTargetRedpacketClick().then(() => { handleOnDataChange({ target: undefined, } as any) setActiveStep(TargetRedPacketStep.TargetChosse) }) }} onConfirm={(addressList) => { handleOnDataChange({ target: { ...tradeData.target, addressListString: [ ...(tradeData.target?.addressListString ? tradeData.target.addressListString.split(';\n') : []), ...addressList ].join(';\n'), }, } as any) }} popUpOptionDisabled={isWhiteListed ? false : true} onClickBack={() => { handleOnDataChange({ target: undefined, } as any) setActiveStep(TargetRedPacketStep.TargetChosse) }} sentAddresses={tradeData.target?.sentAddresses} clearInput={() => { handleOnDataChange({ numbers: undefined, tradeValue: undefined, validSince: Date.now(), validUntil: moment().add('days', 1).toDate().getTime(), giftNumbers: undefined, memo: '', } as any) }} /> ), toolBarItem: undefined, } const tokenNFTSelectionPanel = isToken ? tradeMenuListPanel : myNFTPanel ? nftListPanel : undefined return { index: panelIndex, _width: '100%', panelList: [ ...(isTarget && tradeData.tradeType !== RedPacketOrderType.FromNFT ? [targetSelectPanel] : []), ...(tradeData.tradeType !== RedPacketOrderType.FromNFT ? [selectTokenTypePanel] : []), selectTypePanel, tradePanel, tokenNFTSelectionPanel as any, ...(isTarget ? [targetInputAddressPanel] : []), ], scrollDisabled: tradeData.type?.scope === LuckyTokenViewType.TARGET && panelIndex !== TargetRedPacketStep.TargetSend, } }, [ tradeData, rest, tradeType, switchData, coinMap, assetsData, rest, walletMap, onChangeEvent, getWalletMapWithoutLP, panelIndex, disabled, tradeData, isToken, tradeData.type?.scope, ]) let activeStep if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { activeStep = panelIndex === 3 || panelIndex === 4 ? 3 : panelIndex } else { activeStep = panelIndex === 2 || panelIndex === 3 ? 2 : panelIndex } return ( {showScope ? ( { if (tradeData.type?.scope === LuckyTokenViewType.TARGET) { setActiveStep(TargetRedPacketStep.TargetChosse) } else { setActiveStep(RedPacketStep.TradeType) } setShowScope(false) }} onSelecteScope={(scope) => { if (tradeData.tradeType === RedPacketOrderType.FromNFT) { handleOnDataChange({ type: { ...tradeData?.type, scope: scope, mode: LuckyTokenClaimType.BLIND_BOX, partition: LuckyTokenAmountType.RANDOM, }, } as any) setSelectType(LuckyRedPacketList.find((config) => config.tags?.includes('defaultForFromNFT'))) } else { const found = LuckyRedPacketList.find((config) => config.tags?.includes('defaultForBlindBox') ) setSelectType(found) handleOnDataChange({ type: { ...tradeData?.type, scope: scope, mode: LuckyTokenClaimType.BLIND_BOX, partition: LuckyTokenAmountType.RANDOM, }, isNFT: false, tradeType: RedPacketOrderType.BlindBox, } as any) } }} selectedScope={tradeData.type!.scope!} showBackBtn={tradeData.tradeType === RedPacketOrderType.FromNFT} showExclusiveOption={showExclusiveOption ? true : false} /> ) : ( <> )} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/DeployNFT.tsx ================================================ import { DeployBase, IconType, PanelProps } from './BasicPanel' import { NFTWholeINFO } from '@loopring-web/common-resources' import { sanitize } from 'dompurify' export const NFTDeploy_WaitForAuth = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelNFTTokenDeployWaitForAuth', { symbol: props.symbol, value: props.value, }), } return } export const NFTDeploy_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelDeployDenied', { symbol: props.symbol, value: props.value, }), } return } export const NFTDeploy_First_Method_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied', { symbol: sanitize(props.symbol ?? 'NFT'), value: props.value, }), } return } export const NFTDeploy_In_Progress = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelDeployInProgress', { symbol: sanitize(props.symbol ?? 'NFT'), value: props.value, }), } return } export const NFTDeploy_Failed = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelDeployFailed', { symbol: sanitize(props.symbol ?? 'NFT'), value: props.value, }), } return } export const NFTDeploy_Submit = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.SubmitIcon, describe1: props.t('labelDeploySubmit', { symbol: sanitize(props.symbol ?? 'NFT'), value: props.value, }), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/Deposit.tsx ================================================ import { Trans, useTranslation } from 'react-i18next' import { DepositBase, IconType, PanelProps } from './BasicPanel' import { Box, Link, Typography } from '@mui/material' import { getShortAddr, L1L2_NAME_DEFINED, LinkIcon, MapChainId, SPECIAL_ACTIVATION_NETWORKS, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { ChainId } from '@loopring-web/loopring-sdk' import { useSystem } from '@loopring-web/core' export const Deposit_Sign_WaitForRefer = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitingRefer'), } return } export const Deposit_Approve_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelTokenAccess', { symbol: props.symbol }), } return } export const Deposit_Approve_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFailedTokenAccess', { symbol: props.symbol, value: props.value, }), } return } export const Deposit_WaitForAuth = (props: PanelProps) => { const { defaultNetwork } = useSettings() const { app } = useSystem() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const isSpecialActivation = app === 'earn' && SPECIAL_ACTIVATION_NETWORKS.includes(defaultNetwork) const propsPatch = { iconType: IconType.LoadingIcon, describe1: ( {isSpecialActivation ? props.t('labelL1toL2WaitForAuthTaikoEarn', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, symbol: props.symbol, value: props.value, to: props.to ?? '', }) : props.t('labelL1toL2WaitForAuth', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, symbol: props.symbol, value: props.value, to: props.to ?? '', })} ), } return } export const Deposit_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelL1toL2Denied', { symbol: props.symbol, value: props.value, }), } return } export const Deposit_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelL1toL2Failed', { symbol: props.symbol, value: props.value, }), } return } export const Deposit_Submit = (props: PanelProps) => { const { defaultNetwork, isMobile } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { t } = useTranslation() const propsPatch = { iconType: IconType.SubmitIcon, describe1: ( Add asset submitted. ), describe2: ( {t('labelDepositWaiting')} {props.t('labelL1toL2TokenAmount')} {props.value + ' ' + props.symbol} {props.t('labelL1toL2From')} {'L1: ' + getShortAddr(props.account?.accAddress ?? '')} {props.t('labelL1toL2TO', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {props.to ? `${L1L2_NAME_DEFINED[network].l2Symbol}: ` + getShortAddr(props.to) : t('labelToMyL2', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/DepositNFT.tsx ================================================ import { DepositBase, IconType, PanelProps } from './BasicPanel' import { getShortAddr, L1L2_NAME_DEFINED, LinkIcon, MapChainId, NFTWholeINFO, } from '@loopring-web/common-resources' import { sanitize } from 'dompurify' import { Box, Link, Typography } from '@mui/material' import { Trans, useTranslation } from 'react-i18next' import { useSettings } from '../../../stores' export const NFTDeposit_Approve_WaitForAuth = ( props: PanelProps & Partial & Partial, ) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelNFTAccess', { symbol: props?.symbol ?? 'NFT', l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTDeposit_Approve_Denied = ( props: PanelProps & Partial & Partial, ) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelNFTTokenFailedAccess', { symbol: props?.symbol ?? 'NFT', value: props.value, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTDeposit_WaitForAuth = (props: PanelProps & Partial) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelNFTTokenDepositWaitForAuth', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, symbol: props?.symbol ?? 'NFT', value: props.value, }), } return } export const NFTDeposit_Denied = (props: PanelProps & Partial) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelL1toL2Denied', { symbol: props?.symbol ?? 'NFT', value: props.value, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTDeposit_Failed = (props: PanelProps & Partial) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelL1toL2Failed', { symbol: sanitize(props.symbol ?? 'NFT').toString(), value: props.value, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTDeposit_Submit = (props: PanelProps & Partial) => { const { defaultNetwork, isMobile } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { t } = useTranslation() const propsPatch = { iconType: IconType.SubmitIcon, describe1: ( Add asset submitted. ), describe2: ( {t('labelDepositWaiting')} {props.t('labelL1toL2NFTAmount', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {props.value + ' ' + props.symbol} {props.t('labelL1toL2From', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {'L1: ' + getShortAddr(props.account?.accAddress ?? '')} {props.t('labelL1toL2TO', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {props.to ? `${L1L2_NAME_DEFINED[network].l2Symbol}: ` + getShortAddr(props.to) : t('labelToMyL2', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/DepositPanel.tsx ================================================ import { DepositProps } from '../../tradePanel/Interface' import { Trans, withTranslation, WithTranslation } from 'react-i18next' import { CoinInfo, CoinMap, IBData, L1L2_NAME_DEFINED, MapChainId, SoursURL, TRADE_TYPE, } from '@loopring-web/common-resources' import { ModalBackButton, SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { DepositWrap, TradeMenuList, useBasicTrade } from '../../tradePanel/components' import React from 'react' import { cloneDeep } from 'lodash' import { DepositConfirm } from '../../tradePanel/components/DepositConfirm' import { useSettings } from '../../../stores' import { Box, Typography } from '@mui/material' export const DepositPanel = withTranslation('common', { withRef: true })( < T extends { toAddress?: string addressError?: { error: boolean; message: string } } & IBData, I, >({ type = TRADE_TYPE.TOKEN, onDepositClick, depositBtnStatus, walletMap = {}, coinMap = {}, accountReady, addressDefault, allowTrade, isShow, isLoopringSmartWallet, onBack, t, ...rest }: DepositProps & WithTranslation) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { onChangeEvent, index, switchData } = useBasicTrade({ ...rest, type, walletMap, coinMap, } as any) const [panelIndex, setPanelIndex] = React.useState(index + 1) const handleConfirm = (index: number) => { setPanelIndex(index) } // const hanleConfirm = () => {}; React.useEffect(() => { setPanelIndex(index + 1) }, [index]) const getFilteredWalletMap = React.useCallback(() => { if (walletMap) { const clonedWalletMap = cloneDeep(walletMap) Object.values(clonedWalletMap).forEach((o: any) => { if (o.belong && o.count && Number(o.count) === 0) { delete clonedWalletMap[o.belong] } }) return clonedWalletMap } return {} }, [walletMap]) const getFilteredCoinMap: any = React.useCallback(() => { if (coinMap && getFilteredWalletMap()) { const clonedCoinMap = cloneDeep(coinMap) const remainList = {} Object.keys(getFilteredWalletMap()).forEach((token) => { if (clonedCoinMap[token]) { remainList[token] = clonedCoinMap[token] } }) return remainList } return {} }, [coinMap, getFilteredWalletMap]) const props: SwitchPanelProps<'tradeMenuList' | 'trade' | 'confirm'> = { index: panelIndex, // show default show panelList: [ { key: 'confirm', element: React.useMemo( () => ( // @ts-ignore My L2 (address) ), tradeData: switchData.tradeData, onDepositClick, handleConfirm, }} /> ), [rest, handleConfirm, onDepositClick, type, switchData.tradeData], ), toolBarItem: ( { setPanelIndex(1) }} {...rest} /> ), }, { key: 'trade', // onBack, element: React.useMemo( () => ( // @ts-ignore key={'trade'} {...{ ...rest, t, type, tradeData: switchData.tradeData, onChangeEvent, disabled: !!rest.disabled, handleConfirm, onDepositClick, depositBtnStatus, walletMap, coinMap, addressDefault, allowTrade, accountReady, isLoopringSmartWallet }} /> ), [ rest, switchData.tradeData, onChangeEvent, // onDepositClick, depositBtnStatus, walletMap, coinMap, addressDefault, allowTrade, isLoopringSmartWallet ], ), toolBarItem: React.useMemo( () => ( <> {onBack ? ( { onBack() }} {...rest} /> ) : ( <> )} ), [onBack], ), }, { key: 'tradeMenuList', element: React.useMemo( () => ( // @ts-ignore >, //oinMap }} /> ), [rest, onChangeEvent, switchData.tradeData, getFilteredWalletMap, getFilteredCoinMap], ), toolBarItem: undefined, // toolBarItem: toolBarItemBack }, ], } return !switchData.tradeData?.belong || isLoopringSmartWallet === undefined ? ( {'loading'} ) : ( ) }, ) as (props: DepositProps & React.RefAttributes) => JSX.Element // export const TransferModal = withTranslation() ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/DepositRecorder.tsx ================================================ import { Box, Link, Typography } from '@mui/material' import styled from '@emotion/styled' import React from 'react' import { TFunction } from 'react-i18next' import { AccountHashInfo, CompleteIcon, getFormattedHash, L1L2_NAME_DEFINED, LinkIcon, MapChainId, WaitingIcon, WarningIcon, } from '@loopring-web/common-resources' import { useTheme } from '@emotion/react' import { useSettings } from '../../../stores' const BoxStyled = styled(Box)` background: var(--color-box-hover); position: relative; overflow-y: scroll; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* Internet Explorer 10+ */ &::-webkit-scrollbar { /* WebKit */ width: 0; } &:before { content: ''; position: absolute; display: block; top: 0; left: 0; right: 0; width: 100%; height: 1px; box-shadow: ${({ theme }) => theme.colorBase.shadowHeader}; } border-bottom-left-radius: ${({ theme }) => theme.unit}px; border-bottom-right-radius: ${({ theme }) => theme.unit}px; ` as typeof Box export const DepositRecorder = ({ t, accAddress, chainInfos, etherscanUrl, clear, }: // updateDepositHash { t: TFunction } & { accAddress: string etherscanUrl: string chainInfos: AccountHashInfo clear?: () => void // updateDepositHash: (depositHash: string, accountAddress: string, status?: 'success' | 'failed') => void }) => { const theme = useTheme() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const depositView = React.useMemo(() => { return ( <> {chainInfos && chainInfos?.depositHashes && chainInfos?.depositHashes[accAddress] && chainInfos?.depositHashes[accAddress].length ? ( <> {t('labelL1toL2Hash', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {clear && ( { clear() }} > {t('labelClearAll')} )} {chainInfos?.depositHashes[accAddress].map((txInfo) => { return ( {txInfo.symbol ? ( {t('labelL1toL2Record', { symbol: txInfo.symbol, value: txInfo.value, })} ) : ( getFormattedHash(txInfo.hash) )} {txInfo.status === 'pending' ? ( ) : txInfo.status === 'success' ? ( ) : ( )} ) })} ) : ( {t('labelL1toL2HashEmpty', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} )} ) }, [network, chainInfos?.depositHashes[accAddress]]) return ( {depositView} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/Dual.tsx ================================================ import { AmmBase, BtradeBase, DualBase, IconType, PanelProps, TaikoFarmingMintBase, TaikoFarmingRedeemBase, TaikoFarmingStakeBase } from './BasicPanel' import { Box, ListItemIcon, ListItemText, Tooltip, Typography } from '@mui/material' import { useSettings } from '../../../stores' import { ConvertToIcon, EmptyValueTag, Info2Icon, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import moment from 'moment' import { CoinIcon } from '../../basic-lib' // value symbol export const Dual_Success = (props: PanelProps) => { const { isMobile } = useSettings() const propsPatch = { iconType: IconType.PendingIcon, describe1: ( {props.t('labelDualProcessing', { symbol: props.symbol, value: props.value, })} ), describe2: ( {props.t('labelDualProcessingDes')} ), } return } // value symbol export const Dual_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelDualFailed', { symbol: props.symbol, value: props.value, }), } return } export const Staking_Success = (props: PanelProps) => { const { isMobile } = useSettings() const { info } = props const propsPatch = { iconType: IconType.DoneIcon, describe1: ( {props.t('labelStakingSuccess', { symbol: info.symbol, })} ), describe2: ( {props.t('labelDeFiSideAmount')} {info.amount + ' ' + info.symbol} {props.t('labelDeFiSideProduct')} {info?.productId ?? EmptyValueTag} {props.t('labelDeFiSideSubscribeTime')} {moment(new Date(info.stakeAt)) // .utc() // .startOf("days") .format(YEAR_DAY_MINUTE_FORMAT)} {props.t('labelDeFiSideLockDuration')} {info.daysDuration ? '≥ ' + info.daysDuration + ' ' + props.t('labelDay') : EmptyValueTag} ), } return } export const Staking_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelStakingFailed', { symbol: props.info?.symbol, }), } return } export const Staking_Redeem_Success = (props: PanelProps) => { const { isMobile } = useSettings() const { info } = props const propsPatch = { iconType: IconType.DoneIcon, describe1: ( {props.t('labelStakingRedeemSuccess', { symbol: info?.symbol, })} ), describe2: ( {props.t('labelDefiStakingRedeem')} {info.amount + ' ' + info.symbol} {props.t('labelStakingRedeemRemaining')} {info.remainAmount && info.remainAmount != '0' ? info.remainAmount + ' ' + info.symbol : EmptyValueTag} {props.t('labelDeFiSideProduct')} {info?.productId ?? EmptyValueTag} {props.t('labelStakingRedeemDate')} {moment(new Date()) // .utc() // .startOf("days") .format(YEAR_DAY_MINUTE_FORMAT)} ), } return } export const Staking_Redeem_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelStakingFailed', { symbol: props.info?.symbol, }), } return } export const BtradeDetail = (props: any) => { const { isMobile } = useSettings() const { info } = props const { t } = props return info?.buyToken && info?.sellToken ? ( {t("labelBtradePlacedAmount")} {t("labelBtradePlacedAmountTip")}}> {info?.placedAmount} {t("labelBtradeExecutedAmount")} {t("labelBtradeExecutedAmountTip")}}> {info?.executedAmount} {t("labelBtradeExecutedRate")} {info?.executedRate} {t("labelBtradeConvertedAmount")} {t("labelBtradeConvertedAmountTip")}}> {info?.convertedAmount} {t("labelBtradeSettledAmount")} {t("labelBtradeSettledAmountTip")}}> {info?.settledAmount} {props.t('labelPrice')} {info?.convertStr} {props.t('labelFee')} {info?.feeStr ? info?.feeStr + ' ' + info.buyToken.symbol : EmptyValueTag} {info?.time && ( {props.t('labelBtradeTime')} {moment(new Date(info.time)).format(YEAR_DAY_MINUTE_FORMAT)} )} ) : ( <> ) } export const BtradeSwap_Delivering = (props: PanelProps) => { const { t } = props const { isMobile } = useSettings() const propsPatch = { iconType: IconType.SubmitIcon, describe1: ( {t('labelBtradeSwapDelivering')} {t('labelBtradeSwapDeliverDes')} ), describe2: , } return } export const BtradeSwap_Pending = (props: PanelProps) => { const { t } = props const propsPatch = { iconType: IconType.PendingIcon, describe1: ( {t('labelBtradeSwapPending')} ), describe2: , } return } export const BtradeSwap_Settled = (props: PanelProps) => { const { t } = props const propsPatch = { iconType: IconType.DoneIcon, describe1: ( {t('labelBtradeSwapSettled')} ), describe2: , } return } export const BtradeSwap_Failed = (props: PanelProps) => { const { t } = props const propsPatch = { iconType: IconType.FailedIcon, describe1: ( {t('labelBtradeSwapFailed')} ), describe2: , } return } export const AMM_Pending = (props: PanelProps) => { const propsPatch = { iconType: IconType.PendingIcon, // describe1: ( // // {t("labelAMMPending")} // // ), } return } export const Taiko_Farming_Lock_Success = (props: PanelProps) => { const { isMobile } = useSettings() const { info } = props const propsPatch = { iconType: IconType.DoneIcon, title: props.t('labelTaikoFarming'), describe1: ( {props.t('labelTaikoFarmingLockSuccess')} ), describe2: ( {props.t('labelDeFiSideAmount')} {info?.amount + ' ' + info?.symbol} {props.t('labelDeFiSideSubscribeTime')} {info?.stakeAt && moment(new Date(info.stakeAt)) // .utc() // .startOf("days") .format(YEAR_DAY_MINUTE_FORMAT)} ), } return } export const Taiko_Farming_Lock_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelStakingFailed', { symbol: props.info?.symbol, }), } return } export const Taiko_Farming_Redeem_Success = (props: PanelProps) => { const { isMobile } = useSettings() const { info } = props const propsPatch = { iconType: IconType.DoneIcon, describe2: ( TAIKO Amount {info?.amount} Time {moment(info?.redeemAt).format(YEAR_DAY_MINUTE_FORMAT)} ), } return } export const Taiko_Farming_Redeem_In_Progress = (props: PanelProps) => { const { isMobile } = useSettings() const { info } = props const propsPatch = { iconType: IconType.LoadingIcon, describe2: ( TAIKO Amount {info?.amount} {/* {props.t("labelTime")} {info?.mintAt && moment(new Date(info.mintAt)).format(YEAR_DAY_MINUTE_FORMAT)} */} ), } return } export const Taiko_Farming_Redeem_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: 'Redeem TAIKO Failed', } return } export const Taiko_Farming_Mint_Success = (props: PanelProps) => { const { isMobile } = useSettings() const { info } = props const propsPatch = { iconType: IconType.DoneIcon, title: props.t('Mint lrTAIKO'), describe1: ( {props.t('labelTaikoFarmingMintSuccess')} ), describe2: ( {props.t("labelAmount")} {info?.amount + ' ' + info?.symbol} {props.t("labelTime")} {info?.mintAt && moment(new Date(info.mintAt)).format(YEAR_DAY_MINUTE_FORMAT)} {props.t('labelTaikoFarmingMintInfoDes')} window.open('/#/portal/portalHome', '_blank')} sx={{ cursor: 'pointer' }} color={'var(--color-primary)'} mt={1} >{props.t('labelTaikoFarmingMintVisitPortal')} ), } return } export const Taiko_Farming_Mint_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('Mint Failed'), } return } export const Taiko_Farming_Mint_In_Progress = (props: PanelProps) => { const { isMobile } = useSettings() const { info } = props const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelVaultRepayInProgress', { symbol: props.symbol, value: props.value, }), describe2: ( {props.t("labelAmount")} {info?.amount + ' ' + info?.symbol} {props.t("labelTime")} {info?.mintAt && moment(new Date(info.mintAt)).format(YEAR_DAY_MINUTE_FORMAT)} ), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/EditContact.tsx ================================================ import React from 'react' import { useTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import { AddressError, AlertIcon, CloseIcon, hexToRGB, L1L2_NAME_DEFINED, LoadingIcon, MapChainId, TradeBtnStatus, } from '@loopring-web/common-resources' import { Box, Divider, IconButton, InputAdornment, Typography } from '@mui/material' import { Button, ModalBackButton, TextField } from '../../basic-lib' import { FullAddressType } from '../../tradePanel' import styled from '@emotion/styled' import { useTheme } from '@emotion/react' const BoxStyle = styled(Box)` & { height: inherit; .content-main { overflow: scroll; align-self: stretch; & > div { align-self: stretch; } } } ` export const EditContact = ({ selectedAddressType, detectedWalletType, addressDefault, isAddressCheckLoading, addName, onChangeName, realAddr, isContactExit, isEdit, handleOnAddressChange, allowToClickIsSure, onChangeAddressType, btnStatus, addrStatus, submitContact, isENSWrong, btnLabel, onBack, }) => { const { t } = useTranslation(['common']) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const label = React.useMemo(() => { if (btnLabel) { const key = btnLabel.split('|') return t( key[0], key && key[1] ? { arg: key[1].toString(), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(isEdit ? `labelContactsEditContactBtn` : `labelContactsAddContactBtn`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [btnLabel, isEdit]) const getDisabled = React.useMemo(() => { return btnStatus === TradeBtnStatus.DISABLED }, [btnStatus]) const theme = useTheme() const isInvalidAddressOrENS = !isAddressCheckLoading && addressDefault && addrStatus === AddressError.InvalidAddr return ( <> {onBack ? ( ) : ( <> )} {isEdit ? t('labelContactsEditContact') : t('labelContactsAddContact')} {isEdit && isENSWrong && ( {t('labelContactENSAlert')} )} {t('labelContactsAddressTitle')} <> handleOnAddressChange(event?.target?.value)} fullWidth={true} InputProps={{ style: { paddingRight: '0', }, endAdornment: ( {addressDefault !== '' ? ( isAddressCheckLoading ? ( ) : ( !isEdit && ( handleOnAddressChange('')} > ) ) ) : ( '' )} ), }} /> {isInvalidAddressOrENS ? ( {t('labelInvalidAddress')} ) : isContactExit ? ( {t('labelContactsContactExisted')} ) : ( addressDefault && realAddr && !isAddressCheckLoading && ( {realAddr.toLowerCase() === addressDefault.toLowerCase() ? '' : realAddr} ) )} {t('labelContactsNameTitle')} { onChangeName(e.target.value) }} fullWidth={true} InputProps={{ style: { paddingRight: '0', }, endAdornment: ( onChangeName('')} > ), }} /> ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/ExportAccount.tsx ================================================ import { ExportAccountBase, IconType, PanelProps } from './BasicPanel' // symbol export const ExportAccount_Approve_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const ExportAccount_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } // symbol export const ExportAccount_Success = (props: PanelProps) => { const describe1 = props.t('labelExportAccountSuccess') const propsPatch = { iconType: IconType.DoneIcon, describe1, } return } // value symbol export const ExportAccount_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelExportAccountFailed'), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/FeeSelect.tsx ================================================ import { Box, BoxProps, Divider, Typography } from '@mui/material' import styled from '@emotion/styled' import { useTranslation } from 'react-i18next' import { BackIcon, EmptyValueTag, LoadingIcon, RoundCheckIcon, RoundCircleIcon, myLog, } from '@loopring-web/common-resources' import { FeeSelectProps, Modal, FeeSelectModalProps } from '../../../components/tradePanel' import { CoinIcon } from '../../../components/basic-lib' import { OffchainFeeReqType, toBig } from '@loopring-web/loopring-sdk' const OptionStyled = styled(Box)<{ checked?: boolean; disabled?: boolean }>` border: 1px solid; border-color: ${({ checked }) => (checked ? 'var(--color-primary)' : 'var(--color-border)')}; width: 100%; display: flex; justify-content: space-between; border-radius: ${({ theme }) => theme.unit}px; padding: ${({ theme }) => theme.unit * 1.5}px ${({ theme }) => theme.unit * 2}px; min-height: ${({ theme }) => theme.unit * 8}px; align-items: center; cursor: pointer; opacity: ${({ disabled }) => (disabled ? '0.5' : '1')}; ` type OptionType = { checked?: boolean; disabled?: boolean } & BoxProps const Option = (props: OptionType) => { const { checked, disabled, children, ...otherProps } = props return ( {children} {checked ? ( ) : ( )} ) } const FeeTypeStyled = styled(Box)<{ checked?: boolean }>` border: 1px solid; border-color: ${({ checked }) => (checked ? 'var(--color-primary)' : 'var(--color-border)')}; display: flex; justify-content: space-between; border-radius: ${({ theme }) => theme.unit}px; padding: ${({ theme }) => theme.unit * 0.5}px ${({ theme }) => theme.unit * 1.5}px; align-items: center; cursor: pointer; margin-right: ${({ theme }) => 2 * theme.unit}px; ` const BackIconStyled = styled(BackIcon)` transform: rotate(180deg); ` export const FeeSelectModal = (props: FeeSelectModalProps) => { const { t } = useTranslation() const { open, onClose, chargeFeeTokenList, feeInfo: selectedFeeInfo, handleToggleChange, disableNoToken, withdrawInfos, } = props return ( { onClose() }} content={ {t('labelFee')} {withdrawInfos && ( {Object.keys(withdrawInfos.types).map((key) => { const type = Number(key) return ( { withdrawInfos.onChangeType(Number(key)) }} checked={withdrawInfos.type === type} key={key} > {t('withdrawTypeLabel' + withdrawInfos.types[key])} ) })} )} {chargeFeeTokenList?.map((feeInfo, index) => { const inefficient = !feeInfo.count || toBig(feeInfo.count).isZero() || toBig(feeInfo.count).lt(feeInfo.fee ?? '0') const disabled = (disableNoToken && !feeInfo.hasToken) || inefficient myLog('inefficient', feeInfo.belong, inefficient) return ( ) })} } /> ) } export const FeeSelect = (props: FeeSelectProps) => { const { open, chargeFeeTokenList, disableNoToken, feeInfo: selectedFeeInfo, handleToggleChange, onClose, withdrawInfos, onClickFee, isFeeNotEnough, feeLoading, isFastWithdrawAmountLimit, floatLeft, middleContent, feeNotEnoughContent, networkFeeElement } = props const { t } = useTranslation() const defaultNetworkFeeElement = {t('labelL2toL2Fee')}: return ( <> {networkFeeElement ? networkFeeElement : defaultNetworkFeeElement} onClickFee()} > {withdrawInfos && withdrawInfos.types && (withdrawInfos.type === OffchainFeeReqType.OFFCHAIN_WITHDRAWAL ? t('labelL2toL1Standard') : withdrawInfos.type === OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL ? t('labelL2toL1Fast') : '')} {feeLoading ? ( ) : isFastWithdrawAmountLimit ? ( {t('labelL2toL2FeeFastNotAllowEnough')} ) : selectedFeeInfo && selectedFeeInfo.belong && selectedFeeInfo.fee ? ( selectedFeeInfo.fee + ' ' + selectedFeeInfo.belong ) : ( EmptyValueTag + ' ' + selectedFeeInfo?.belong ?? EmptyValueTag )} insufficient balance {middleContent} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/ForceWithdraw.tsx ================================================ import { ForceWithdrawBase, IconType, PanelProps } from './BasicPanel' import { L1L2_NAME_DEFINED, MapChainId, NFTWholeINFO } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' export const ForceWithdraw_WaitForAuth = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelForceWithdrawWaitForAuth', { symbol: props.symbol, value: props.value, }), } return } export const ForceWithdraw_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelForceWithdrawDenied', { symbol: props.symbol, value: props.value, }), } return } export const ForceWithdraw_First_Method_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied', { symbol: props.symbol, value: props.value, }), } return } export const ForceWithdraw_In_Progress = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelForceWithdrawInProgress', { symbol: props.symbol, value: props.value, }), } return } export const ForceWithdraw_Failed = (props: PanelProps & Partial) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelForceWithdrawFailed', { symbol: props.symbol, value: props.value, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const ForceWithdraw_Submit = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.SubmitIcon, describe1: props.t('labelForceWithdrawSubmit', { symbol: props.symbol, value: props.value, }), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/ForceWithdrawPanel.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { ModalBackButton, SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { ForceWithdrawProps } from '../../tradePanel' import { IBData, TRADE_TYPE } from '@loopring-web/common-resources' import { TradeMenuList, useBasicTrade } from '../../tradePanel/components' import React from 'react' import { ForceWithdrawWrap } from '../../tradePanel/components/ForceWithdrawWrap' export const ForceWithdrawPanel = withTranslation(['common', 'error'], { withRef: true, })( , I>({ type = TRADE_TYPE.TOKEN, chargeFeeTokenList, onWithdrawClick, withdrawBtnStatus, assetsData, walletMap = {}, coinMap = {}, onBack, ...rest }: ForceWithdrawProps & WithTranslation & { assetsData: any[] }) => { const { onChangeEvent, index, switchData } = useBasicTrade({ ...rest, coinMap, type, walletMap, }) const [panelIndex, setPanelIndex] = React.useState(index + 1) React.useEffect(() => { setPanelIndex(index + 1) }, [index]) // LP token should not exist in withdraw panel for now const props: SwitchPanelProps = { index: panelIndex, // show default show panelList: [ { key: 'confirm', element: React.useMemo( () => ( // @ts-ignore // <> ), [onWithdrawClick, rest, switchData.tradeData, type], ), toolBarItem: ( <> { setPanelIndex(1) }} {...rest} /> ), }, { key: 'trade', element: React.useMemo( () => ( // @ts-ignore ), [ rest, type, chargeFeeTokenList, switchData.tradeData, onChangeEvent, coinMap, onWithdrawClick, withdrawBtnStatus, assetsData, walletMap, ], ), toolBarItem: undefined, }, ].concat( walletMap && Object.keys(walletMap)?.length ? ([ { key: 'tradeMenuList', element: ( { if (coinMap[item]) { prev[item] = coinMap[item] } return prev }, {} as any), selected: switchData.tradeData.belong, tradeData: switchData.tradeData, walletMap, //oinMap }} /> ), toolBarItem: undefined, }, ] as any) : [], ), } return }, ) as (props: ForceWithdrawProps & React.RefAttributes) => JSX.Element ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/HadAccount.tsx ================================================ import { AccountBaseProps } from './Interface' import { AccountBasePanel } from './AccountBase' import { Box } from '@mui/material' import { WithTranslation, withTranslation } from 'react-i18next' import { DepositRecorder } from './DepositRecorder' import { AccountHashInfo } from '@loopring-web/common-resources' import { useTheme } from '@emotion/react' export const HadAccount = withTranslation('common')( ({ noButton, mainBtn, className, onClose, t, ...props }: WithTranslation & AccountBaseProps & { noButton?: boolean onClose: (e?: any) => void className?: string clearDepositHash: () => void chainInfos: AccountHashInfo }) => { const theme = useTheme() return ( <> {noButton ? ( ) : ( // // // <> {mainBtn as any} )} ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/Interface.ts ================================================ import { ButtonProps } from '../../basic-lib' import { Account, AccountHashInfo, CAMPAIGNTAGCONFIG, Contact, FeeInfo, NFTWholeINFO, TradeTypes, VendorItem, VendorProviders, WalletMap, } from '@loopring-web/common-resources' import React from 'react' import {NFTBurn_User_Denied} from "./Transfer"; export type AccountBaseProps = { level?: string mainBtn?: ((props: ButtonProps) => JSX.Element) | JSX.Element etherscanUrl: string onDisconnect?: any onSwitch?: any onCopy?: any onViewQRCode?: any hideVIPlevel?: boolean } & Account export enum AccountStep { //l1 should be at top NoAccount, QRCode, HadAccount, Deposit_Sign_WaitForRefer, Deposit_Approve_WaitForAuth, Deposit_Approve_Denied, Deposit_WaitForAuth, Deposit_Denied, Deposit_Failed, Deposit_Submit, //L2 General_Failed, ContinuousBanxaOrder, CheckingActive, AddAssetGateway, SendAssetGateway, SendAssetFromContact, SendNFTGateway, PayWithCard, QRCodeScanner, ThirdPanelReturn, // new // Deposit, NFTDeposit_Approve_WaitForAuth, NFTDeposit_Approve_Denied, NFTDeposit_WaitForAuth, NFTDeposit_Denied, NFTDeposit_Failed, NFTDeposit_Submit, NFTMint_WaitForAuth, NFTMint_First_Method_Denied, NFTMint_In_Progress, NFTMint_Denied, NFTMint_Failed, NFTMint_Success, NFTDeploy_WaitForAuth, NFTDeploy_First_Method_Denied, NFTDeploy_In_Progress, NFTDeploy_Denied, NFTDeploy_Failed, NFTDeploy_Submit, RedPacketSend_WaitForAuth, RedPacketSend_First_Method_Denied, RedPacketSend_In_Progress, RedPacketSend_User_Denied, RedPacketSend_Failed, RedPacketSend_Success, RedPacketOpen_In_Progress, RedPacketOpen_Failed, RedPacketOpen_Claim_In_Progress, RedPacketSend_Claim_Success, RedPacketOpen_Claim_Failed, ForceWithdraw_WaitForAuth, ForceWithdraw_First_Method_Denied, ForceWithdraw_In_Progress, ForceWithdraw_Denied, ForceWithdraw_Failed, ForceWithdraw_Submit, ClaimWithdraw_WaitForAuth, ClaimWithdraw_Denied, ClaimWithdraw_First_Method_Denied, ClaimWithdraw_In_Progress, ClaimWithdraw_Failed, ClaimWithdraw_Submit, Transfer_WaitForAuth, Transfer_First_Method_Denied, Transfer_User_Denied, Transfer_In_Progress, Transfer_Success, Transfer_Failed, Transfer_RAMP_WaitForAuth, Transfer_RAMP_First_Method_Denied, Transfer_RAMP_User_Denied, Transfer_RAMP_In_Progress, Transfer_RAMP_Success, Transfer_RAMP_Failed, Transfer_BANXA_WaitForAuth, Transfer_BANXA_First_Method_Denied, Transfer_BANXA_User_Denied, Transfer_BANXA_In_Progress, Transfer_BANXA_Success, Transfer_BANXA_Confirm, Transfer_BANXA_Failed, Withdraw_WaitForAuth, Withdraw_First_Method_Denied, Withdraw_User_Denied, Withdraw_In_Progress, Withdraw_Success, Withdraw_Failed, NFTTransfer_WaitForAuth, NFTTransfer_First_Method_Denied, NFTTransfer_User_Denied, NFTTransfer_In_Progress, NFTTransfer_Success, NFTTransfer_Failed, NFTBurn_WaitForAuth, NFTBurn_First_Method_Denied, NFTBurn_User_Denied, NFTBurn_In_Progress, NFTBurn_Success, NFTBurn_Failed, NFTWithdraw_WaitForAuth, NFTWithdraw_First_Method_Denied, NFTWithdraw_User_Denied, NFTWithdraw_In_Progress, NFTWithdraw_Success, NFTWithdraw_Failed, CreateAccount_EOA_Only_Alert, CreateAccount_Approve_WaitForAuth, CreateAccount_Approve_Denied, CreateAccount_Approve_Submit, CreateAccount_WaitForAuth, CreateAccount_Denied, CreateAccount_Failed, CreateAccount_Submit, UpdateAccount, UpdateAccount_Approve_WaitForAuth, UpdateAccount_First_Method_Denied, UpdateAccount_User_Denied, UpdateAccount_Success, UpdateAccount_Failed, UpdateAccount_SmartWallet_NotSupported_Alert, // UnlockAccount, UnlockAccount_WaitForAuth, UnlockAccount_User_Denied, UnlockAccount_Success, UnlockAccount_Failed, UnlockAccount_Reset_Key_Confirm, ResetAccount_Approve_WaitForAuth, ResetAccount_First_Method_Denied, ResetAccount_User_Denied, ResetAccount_Success, ResetAccount_Failed, ExportAccount_Approve_WaitForAuth, ExportAccount_User_Denied, ExportAccount_Success, ExportAccount_Failed, Dual_Success, Dual_Failed, Staking_Success, Staking_Failed, Staking_Redeem_Success, Staking_Redeem_Failed, BtradeSwap_Pending, BtradeSwap_Delivering, BtradeSwap_Settled, BtradeSwap_Failed, AMM_Pending, VaultTrade_Success, VaultTrade_Failed, VaultTrade_In_Progress, VaultJoin_Success, VaultJoin_Failed, VaultJoin_In_Progress, VaultRedeem_Success, VaultRedeem_Failed, VaultRedeem_In_Progress, VaultBorrow_Success, VaultBorrow_Failed, VaultBorrow_In_Progress, VaultRepay_Success, VaultRepay_Failed, VaultRepay_In_Progress, VaultDustCollector_Success, VaultDustCollector_Failed, VaultDustCollector_In_Progress, Taiko_Farming_Lock_Success, Taiko_Farming_Lock_Failed, Taiko_Farming_Redeem_In_Progress, Taiko_Farming_Redeem_Success, Taiko_Farming_Redeem_Failed, Taiko_Farming_Mint_Success, Taiko_Farming_Mint_In_Progress, Taiko_Farming_Mint_Failed, Transfer_To_Taiko_User_Denied, Transfer_To_Taiko_In_Progress, Transfer_To_Taiko_Success, Transfer_To_Taiko_Failed, Coinbase_Smart_Wallet_Password_Intro, Coinbase_Smart_Wallet_Password_Set, Coinbase_Smart_Wallet_Password_Set_Confirm, Coinbase_Smart_Wallet_Password_Set_Processing, Coinbase_Smart_Wallet_Password_Set_Error, Coinbase_Smart_Wallet_Password_Get_Error, Coinbase_Smart_Wallet_Password_Input, Coinbase_Smart_Wallet_Password_Forget_Password_Confirm, Coinbase_Smart_Wallet_Password_Forget_Password, } /** * @param handleSelect default handleSelect, if item have no private handleSelect function */ export interface VendorMenuProps { // termUrl: string; type?: TradeTypes banxaRef?: React.Ref vendorList: VendorItem[] handleSelect?: (event: React.MouseEvent, key: string) => void vendorForce: VendorProviders | undefined campaignTagConfig?: CAMPAIGNTAGCONFIG callback?: () => void } interface InferfaceAssetItem { key: string svgIcon: string enableKey?: string | null handleSelect: (event?: React.MouseEvent) => void type: 'sameLayer' | 'crossLayer' | 'crossChain' cornerTag?: 'Loopring' | '3rd party' description?: string } export interface AddAssetItem extends InferfaceAssetItem {} export interface SendAssetItem extends InferfaceAssetItem {} export interface AddAssetProps { symbol?: string addAssetList: AddAssetItem[] isNewAccount?: boolean allowTrade: { [key: string]: { enable?: boolean; reason?: string; show?: boolean } } disbaleList?: string[] } export interface SendAssetProps { isToL1?: boolean symbol?: string sameLayerAssetList: AddAssetItem[] crossLayerAssetList: AddAssetItem[] crossChainAssetList: AddAssetItem[] allowTrade: { [key: string]: { enable?: boolean; reason?: string; show?: boolean } } toL1Title: string } export interface SendNFTAssetProps { nftData: Partial sendAssetList: AddAssetItem[] isNotAllowToL1?: boolean allowTrade: { [key: string]: { enable?: boolean; reason?: string; show?: boolean } } } export interface CheckActiveStatusProps { account: Account & { isContract: boolean | undefined } chargeFeeTokenList: C[] goDisconnect: () => void goSend: () => void isDepositing: boolean walletMap?: WalletMap isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } onIKnowClick: () => void knowDisable: boolean know: boolean clearDepositHash?: () => void chainInfos?: AccountHashInfo accAddress?: string } export interface CheckImportCollectionProps { account: Account value: string onChange: (item: string) => void contractList: string[] disabled?: boolean loading?: boolean onClick: (item: string) => void } export interface SendDialogProps { // onCloseSend: () => void sendInfo: { open: boolean selected: Contact | undefined } } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/MintNFT.tsx ================================================ import { IconType, MintBase, PanelProps } from './BasicPanel' import { L1L2_NAME_DEFINED, MapChainId, NFTWholeINFO } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' export const NFTMint_WaitForAuth = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelNFTTokenMintWaitForAuth', { symbol: props.symbol && props.symbol?.length > 10 ? props.symbol?.slice(0, 10) + '...' : props.symbol ?? '', value: props.value, }), } return } export const NFTMint_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelMintDenied', { symbol: props.symbol, value: props.value, }), } return } export const NFTMint_First_Method_Denied = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied', { symbol: props.symbol, value: props.value, }), } return } export const NFTMint_In_Progress = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelMintInProgress', { symbol: props.symbol, value: props.value, }), } return } export const NFTMint_Failed = (props: PanelProps & Partial) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelMintFailed', { symbol: props.symbol ?? 'NFT', value: props.value, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTMint_Success = (props: PanelProps & Partial) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelMintSuccess', { symbol: props.symbol ?? 'NFT', value: props.value, }), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/ModalAccount.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { Box, Modal } from '@mui/material' import { ModalAccountProps, ModalBackButton, ModalCloseButton, ModelPanelStyle, QRButtonStyle, } from '../../../index' export const ModalAccount = withTranslation('common', { withRef: true })( ({ open, onClose, step, isLayer2Only = false, onBack, style, noClose, etherscanBaseUrl, onQRClick, panelList, isWebEarn, ...rest }: ModalAccountProps & WithTranslation) => { // const { w, h } = style ? style : { w: undefined, h: undefined }; return ( {noClose ? <> : } {onBack ? : <>} {onQRClick ? : <>} {panelList.map((panel, index) => { return ( {panel.view} ) })} ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/NoAccount.tsx ================================================ import { AccountBasePanel } from './AccountBase' import { AccountBaseProps } from './Interface' import { Box, Typography } from '@mui/material' import { AnimationArrow, Button, useSettings } from '../../../index' import { WithTranslation, withTranslation } from 'react-i18next' import { AccountHashInfo, L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' import { DepositRecorder } from './DepositRecorder' import { useTheme } from '@emotion/react' export const NoAccount = withTranslation('common')( ({ goActiveAccount, className, noButton = false, t, onClose, ...props }: WithTranslation & AccountBaseProps & { noButton?: boolean className?: string goActiveAccount: () => void onClose: (e?: any) => void chainInfos: AccountHashInfo clearDepositHash: () => void }) => { const theme = useTheme() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( {noButton ? ( ) : ( <> {t('labelActivatedAccountDeposit', { layer2: L1L2_NAME_DEFINED[network].layer2, })} )} ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/QRAddressPanel.tsx ================================================ import { Box, Link, Typography } from '@mui/material' import { WithTranslation, withTranslation } from 'react-i18next' import { Account, CopyIcon, copyToClipBoard, L1L2_NAME_DEFINED, MapChainId, TOAST_TIME, } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { useSettings } from '../../../stores' import { Button } from '../../basic-lib' import React from 'react' import { Toast, ToastType } from '../../toast' import { QRCode } from '../QRCode' const BoxStyle = styled(Box)` ${({ theme }) => theme.border.defaultFrame({ c_key: 'blur', d_R: 1 / 2, d_W: 0 })}; background: var(--provider-agree); ` as typeof Box export const QRAddressPanel = withTranslation('common')( ({ isForL2Send, accAddress, isNewAccount, t, btnInfo, }: // etherscanUrl, WithTranslation & { btnInfo: { btnTxt: string callback: () => void } etherscanUrl: string isForL2Send: boolean isNewAccount: boolean } & Account) => { const { feeChargeOrder } = useSettings() const [copyToastOpen, setCopyToastOpen] = React.useState(false) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] // const etherscanLink = etherscanUrl + 'address/' + accAddress; return ( {t('labelReceiveAddress')} {!!isForL2Send && ( {isNewAccount ? t('labelReceiveAddressGuide', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, symbol: feeChargeOrder?.join(', '), }) : t('labelReceiveAddressGuide', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, symbol: t('labelAssets', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, }), })} )} { e.stopPropagation() copyToClipBoard(accAddress) setCopyToastOpen(true) }} > {accAddress} {t(isNewAccount ? 'labelReceiveAddressDesActive' : 'labelReceiveAddressDes', { loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} { setCopyToastOpen(false) }} severity={ToastType.success} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/RedPacket.tsx ================================================ import { IconType, PanelProps, RedPacketBase, RedPacketOpenBase } from './BasicPanel' import { sanitize } from 'dompurify' import * as sdk from '@loopring-web/loopring-sdk' // value symbol export const RedPacketSend_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } // value symbol export const RedPacketSend_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), } return } // value symbol export const RedPacketSend_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } // value symbol export const RedPacketSend_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelRedPacketSendInProgress'), } return } // value symbol export const RedPacketSend_Success = (props: PanelProps) => { let propsPatch: any = { iconType: IconType.DoneIcon, } if (props.info.scope === sdk.LuckyTokenViewType.TARGET) { propsPatch = { ...propsPatch, title: 'labelSendRedPacketTitleExclusive', describe1: props.t('labelRedPacketSendSuccess', { symbol: props.symbol, value: props.value, }), } } else { propsPatch = { ...propsPatch, title: 'labelSendRedPacketTitlePublic', describe1: props.t('labelRedPacketSendSuccess', { symbol: props.symbol, value: props.value, }), btnInfo: props?.info?.shared ? { btnTxt: 'labelShareQRCode', callback: props.info.shared, } : props.btnInfo, } } return } // value symbol export const RedPacketSend_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelRedPacketSendFailed', { symbol: props.symbol, value: props.value, }), } return } export const NFTRedPacketSend_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const NFTRedPacketSend_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), } return } export const NFTRedPacketSend_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } export const NFTRedPacketSend_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelRedPacketSendInProgress'), } return } export const NFTRedPacketSend_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelRedPacketSendSuccess', { symbol: sanitize(props.symbol ?? 'NFT').toString(), value: props.value, }), } return } export const NFTRedPacketSend_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelRedPacketSendFailed', { symbol: sanitize(props.symbol ?? 'NFT').toString(), value: props.value, }), } return } export const RedPacketOpen_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, btnInfo: undefined, title: '', } return } export const RedPacketOpen_Failed = (props: PanelProps) => { const propsPatch = { title: '', iconType: IconType.FailedIcon, describe1: props.t('labelRedPacketOpenFailed'), } return } export const RedPacketOpen_Claim_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, btnInfo: undefined, title: '', } return } export const RedPacketSend_Claim_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelRedPacketClaimSuccess'), } return } export const RedPacketOpen_Claim_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelRedPacketOpenFailed'), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/SendAsset.tsx ================================================ import { Box, CardContent, Divider, Tab, Tabs, Tooltip, Typography } from '@mui/material' import styled from '@emotion/styled' import { MenuBtnStyled } from '../../styled' import { AccountStep, AddAssetItem, SendAssetProps } from './Interface' import { useTranslation } from 'react-i18next' import { AlertIcon, AnotherIcon, BackIcon, Contact, ExchangeAIcon, fontDefault, hexToRGB, IncomingIcon, Info2Icon, L1L2_NAME_DEFINED, L1l2Icon, L2l2Icon, MapChainId, OutputIcon, SagaStatus, SendAssetList, } from '@loopring-web/common-resources' import { useOpenModals, useSettings, useToggle } from '../../../stores' import { Button, CoinIcon, TickCardStyleItem } from '../../basic-lib' import React from 'react' import { useTheme } from '@emotion/react' import { store, useContacts } from '@loopring-web/core' const BoxStyled = styled(Box)`` as typeof Box const IconItem = ({ svgIcon }: { svgIcon: string }) => { switch (svgIcon) { case 'IncomingIcon': return case 'L2l2Icon': return case 'L1l2Icon': return case 'ExchangeAIcon': return case 'OutputIcon': return case 'AnotherIcon': return } } export const SendAsset = ({ sameLayerAssetList, crossChainAssetList, crossLayerAssetList, allowTrade, symbol, isToL1, toL1Title }: SendAssetProps) => { const { t } = useTranslation('common') const { defaultNetwork, isMobile } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { toggle: { send }, } = useToggle() const filterAndMap = (list: AddAssetItem[]) => list .filter( (item) => !symbol || (item.key === 'SendAssetToAnotherNet' && send['orbiter']?.includes(symbol)) || !['SendAssetToAnotherNet'].includes(item.key), ) .map((item, index) => ( } onClick={(e) => { item.handleSelect(e) }} sx={{ overflow: 'hidden', '& .corner-tag': { background: '#B8BCC7', }, '& .title, .description': { color: 'var(--color-text-secondary)', }, ':hover': { '& .corner-tag': { background: 'var(--color-primary)', }, '& .title, .description': { color: 'var(--color-text-primary)', } }, borderTopLeftRadius: index === 0 ? '4px' : '0px', borderTopRightRadius: index === 0 ? '4px' : '0px', borderBottomLeftRadius: index === list.length - 1 ? '4px' : '0px', borderBottomRightRadius: index === list.length - 1 ? '4px' : '0px', '&&&&:hover' : { borderColor: 'var(--color-border)' }, height: isMobile ? '48px' : '64px', }} > {IconItem({ svgIcon: item.svgIcon })} {t('label' + item.key, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {item.description && ( {item.description} )} )) return ( {t('labelSendAssetTitle', { symbol, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} {t('labelSendAssetHowto')}}> {filterAndMap(sameLayerAssetList)} {toL1Title} {filterAndMap(crossLayerAssetList)} {filterAndMap(crossChainAssetList).length > 0 && To other networks} {filterAndMap(crossChainAssetList)} ) { /**/ } } const BoxStyle = styled(Box)` & { height: inherit; .content-main { overflow: scroll; align-self: stretch; & > div { align-self: stretch; } } .MuiTab-root.sendType { padding: ${({ theme }) => 3 * theme.unit}px ${({ theme }) => 3 * theme.unit}px; &.Mui-selected, &:hover { padding: ${({ theme }) => 3 * theme.unit}px ${({ theme }) => 3 * theme.unit}px; box-sizing: border-box; } &:after { display: none; } } } ` export const SendFromContact = ( props: { isENSWrong: boolean, selected: string } & Contact, ) => { const [selected, setSelected] = React.useState(SendAssetList.SendAssetToOtherL1.key) const { defaultNetwork } = useSettings() const { setShowTransfer, setShowWithdraw, setShowEditContact, setShowAccount } = useOpenModals() const { status: contactStatus } = useContacts() const [contact, setContact] = React.useState({ ...props, }) const network = MapChainId[defaultNetwork] ?? MapChainId[1] const theme = useTheme() const submit = React.useCallback(() => { // : Contact, network: 'SendToOtherL1'| 'SendTOL2', onClose: () => void switch (selected) { case SendAssetList.SendAssetToOtherL1.key: setShowWithdraw({ isShow: true, address: contact.contactAddress, name: contact.contactName, addressType: contact.addressType, }) setShowAccount({ isShow: false }) break case SendAssetList.SendAssetToL2.key: setShowTransfer({ isShow: true, address: contact.contactAddress, name: contact.contactName, addressType: contact.addressType, }) setShowAccount({ isShow: false }) break } }, [selected, contact]) const geUpdateContact = (_e) => { setShowEditContact({ isShow: true, info: { ...contact, from: AccountStep.SendAssetFromContact, }, }) setShowAccount({ isShow: false }) } const { t } = useTranslation() React.useEffect(() => { if (contactStatus == SagaStatus.UNSET && contact.isENSWrong) { const { contacts } = store.getState().contacts setContact((state) => { const _contact = contacts?.find( (contact) => contact.contactAddress?.toLowerCase() === state?.contactAddress?.toLowerCase(), ) if (contact.isENSWrong && !_contact?.ens) { return { ...state, isENSWrong: false, } } return state }) } }, [contactStatus]) return ( <> {t('labelContactsNetworkChoose', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, })} {t('labelSendToContact')} {contact?.contactName} {contact?.contactAddress} {contact?.ens} {contact.isENSWrong && ( )} setSelected(value)} aria-label='l2-history-tabs' sx={{ display: 'flex', flexWrap: 'nowrap', }} > {L1L2_NAME_DEFINED[network].l1ChainName + '/' + L1L2_NAME_DEFINED[network].l1Symbol} } /> {L1L2_NAME_DEFINED[network].loopringL2 + '/' + L1L2_NAME_DEFINED[network].l2Symbol} } /> ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/SendNFTAsset.tsx ================================================ import { Box, Typography } from '@mui/material' import styled from '@emotion/styled' import { MenuBtnStyled } from '../../styled' import { SendNFTAssetProps } from './Interface' import { useTranslation } from 'react-i18next' import { AnotherIcon, BackIcon, ExchangeAIcon, IncomingIcon, L1L2_NAME_DEFINED, L1l2Icon, L2l2Icon, MapChainId, OutputIcon, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' const BoxStyled = styled(Box)`` as typeof Box const IconItem = ({ svgIcon }: { svgIcon: string }) => { switch (svgIcon) { case 'IncomingIcon': return case 'L2l2Icon': return case 'L1l2Icon': return case 'ExchangeAIcon': return case 'OutputIcon': return case 'AnotherIcon': return } } export const SendNFTAsset = ({ sendAssetList, allowTrade, // nftData, isNotAllowToL1 = false, }: SendNFTAssetProps) => { const { t } = useTranslation('common') const { defaultNetwork, isMobile } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( {t('labelSendAssetTitle', { symbol: 'NFT', loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {t('labelSendAssetHowto')} {sendAssetList.map((item) => { return ( } onClick={(e) => { item.handleSelect(e) }} > <>{IconItem({ svgIcon: item.svgIcon })} {t('label' + item.key, { loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) })} ) { /**/ } } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/ThirdPanelReturn.tsx ================================================ import { Box, BoxProps, Link, Typography } from '@mui/material' import { useSettings } from '../../../stores' import { Button } from '../../basic-lib' import { BackIcon, BANXA_URLS, SoursURL } from '@loopring-web/common-resources' import { MenuBtnStyled } from '../../styled' import styled from '@emotion/styled' import { Trans, useTranslation } from 'react-i18next' import * as sdk from '@loopring-web/loopring-sdk' export const ThirdPanelReturn = ({ title, description, btnInfo, }: { title: string | JSX.Element description: string | JSX.Element btnInfo: { btnTxt: string callback: () => void } } & any) => { const { isMobile } = useSettings() return ( <> {title} {'loading-gif'} {description} ) } const BoxStyle = styled(Box)` .way-content > div:first-of-type { position: relative; font-size: ${({ theme }) => theme.fontDefault.body1}; ${({ theme }) => ` padding-bottom: ${10 * theme.unit}px; &:before { display: block; content: " "; position: absolute; bottom: 0px; left: 0; right: 0; bottom: ${6 * theme.unit}px; margin: 0 ${2 * theme.unit}px; color: var(--color-text-third); border: 1px solid var(--color-text-third); opacity: 0.4; } &:after { display: block; content: "OR"; position: absolute; width: 32px; height: 32px; line-height: 32px; text-align: center; left: 50%; margin-left: -16px; bottom: ${2 * theme.unit}px; background: ${theme.colorBase.box}; color: var(--color-text-third); bottom: 32px; } `} padding-left: 0; padding-right: 0; ` as (props: BoxProps & { ismobile: boolean | undefined }) => JSX.Element export const ContinuousBanxaOrder = ({ // _title, // description, chainId, btnInfo, btnInfo2, orderId, }: { title: string | JSX.Element chainId: sdk.ChainId orderId: string // description: string | JSX.Element; btnInfo?: { btnTxt: string callback: () => void isLoading?: boolean } btnInfo2?: { btnTxt: string callback: () => void isLoading?: boolean } } & any) => { const { isMobile } = useSettings() const { t } = useTranslation() return ( <> {t('labelBanxaTitleCreateAgain')} You already have an awaiting payment order continue , or you can create a new order instead. {'loading'} } onClick={(_e) => { btnInfo.callback() }} > {btnInfo.btnTxt} {t('labelHaveAnBanxaCancel')} } onClick={(_e) => { btnInfo2.callback() }} > {btnInfo2.btnTxt} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/Transfer.tsx ================================================ import { BasicPanel, IconType, PanelProps, TransferBase } from './BasicPanel' import { sanitize } from 'dompurify' import { useSettings } from '../../../stores' import { L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' import { Typography } from '@mui/material' // value symbol export const Transfer_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } // value symbol export const Transfer_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), } return } // value symbol export const Transfer_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } // value symbol export const Transfer_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelL2toL2InProgress'), } return } // value symbol export const Transfer_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelL2toL2Success', { symbol: props.symbol, value: props.value, }), } return } export const Transfer_banxa_confirm = (props: PanelProps) => { const propsPatch = { iconType: IconType.SubmitIcon, describe1: props.t('labelBanxaConfirmSubmit', { order: props.info.id }), } return } // value symbol export const Transfer_Failed = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelL2toL2Failed', { symbol: props.symbol, value: props.value, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTTransfer_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const NFTTransfer_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), } return } export const NFTTransfer_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } export const NFTTransfer_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelL2toL2InProgress'), } return } export const NFTTransfer_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelL2toL2Success', { symbol: sanitize(props.symbol ?? 'NFT').toString(), value: props.value, }), } return } export const NFTTransfer_Failed = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelL2toL2Failed', { symbol: sanitize(props.symbol ?? 'NFT').toString(), value: props.value, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTBurn_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), title: 'labelL2NFTBurnTitle', } return } export const NFTBurn_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), title: 'labelL2NFTBurnTitle', } return } export const NFTBurn_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), title: 'labelL2NFTBurnTitle', } return } export const NFTBurn_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelL2toL2InProgress'), title: 'labelL2NFTBurnTitle', } return } export const NFTBurn_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelL2NFTBurSuccess', { symbol: sanitize(props.symbol ?? 'NFT').toString(), value: props.value, }), title: 'labelL2NFTBurnTitle', } return } export const NFTBurn_Failed = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelL2toL2Failed', { symbol: sanitize(props.symbol ?? 'NFT').toString(), value: props.value, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), title: 'labelL2NFTBurnTitle', } return } // value symbol export const General_Failed = (props: PanelProps) => { return ( {props.errorMessage} } {...props} /> ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/TransferPanel.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { ModalBackButton, SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { IBData, SoursURL, TRADE_TYPE } from '@loopring-web/common-resources' import React from 'react' import { TransferProps } from '../../tradePanel' import { TradeMenuList, TransferWrap, useBasicTrade } from '../../tradePanel/components' import { TransferConfirm } from '../../tradePanel/components/TransferConfirm' import { ContactSelection } from '../../tradePanel/components/ContactSelection' import { Box } from '@mui/material'; export const TransferPanel = withTranslation(['common', 'error'], { withRef: true, })( , I>({ walletMap = {}, coinMap = {}, isThumb = true, type = TRADE_TYPE.TOKEN, chargeFeeTokenList, onTransferClick, transferBtnStatus, assetsData, addrStatus, isAddressCheckLoading, onBack, isFromContact, contact, contacts, ...rest }: TransferProps & WithTranslation & { assetsData: any[] }) => { const { onChangeEvent, index, switchData } = useBasicTrade({ ...rest, walletMap, coinMap, type, }) const [panelIndex, setPanelIndex] = React.useState(index + 1) const handleConfirm = (index: number) => { setPanelIndex(index) } // const hanleConfirm = () => {}; React.useEffect(() => { setPanelIndex(index + 1) }, [index]) const confirmPanel = { key: 'confirm', element: React.useMemo( () => ( ), [rest, onTransferClick, type, switchData.tradeData, isThumb], ), toolBarItem: ( { setPanelIndex(1) }} {...rest} /> ), } const tradePanel = { key: 'trade', element: React.useMemo( () => ( // @ts-ignore { setPanelIndex(3) }, }} /> ), [ rest, type, chargeFeeTokenList, switchData.tradeData, onChangeEvent, isThumb, onTransferClick, transferBtnStatus, assetsData, addrStatus, isAddressCheckLoading, ], ), toolBarItem: React.useMemo( () => ( <> {onBack ? ( { onBack() }} {...rest} /> ) : ( <> )} ), [onBack], ), } const tokenSelectionPanel = { key: 'tradeMenuList', element: React.useMemo( () => ( ), [switchData, rest, onChangeEvent], ), // toolBarItem: undefined, toolBarItem: undefined, } const contactSelectionPanel = { key: 'contactSelection', element: React.useMemo( () => ( { setPanelIndex(1) rest.handleOnAddressChange(address, true) }} scrollHeight={'380px'} /> ), [contacts], ), toolBarItem: React.useMemo( () => ( { setPanelIndex(1) }} {...rest} /> ), [onBack], ), } const props: SwitchPanelProps = { index: panelIndex, panelList: [confirmPanel, tradePanel, tokenSelectionPanel, contactSelectionPanel], } return (type == TRADE_TYPE.TOKEN && !switchData.tradeData?.belong) ? {'loading'} : }, ) as (props: TransferProps & React.RefAttributes) => JSX.Element ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/TransferToTaikoAccount.tsx ================================================ import { IconType, PanelProps, TransferToTaikoBase } from './BasicPanel' import { useSettings } from '../../../stores' import { L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' export const Transfer_To_Taiko_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const Transfer_To_Taiko_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), } return } export const Transfer_To_Taiko_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } export const Transfer_To_Taiko_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelL2toL2InProgress'), } return } export const Transfer_To_Taiko_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelL2toL2Success', { symbol: props.symbol, value: props.value, }), } return } export const Transfer_To_Taiko_banxa_confirm = (props: PanelProps) => { const propsPatch = { iconType: IconType.SubmitIcon, describe1: props.t('labelBanxaConfirmSubmit', { order: props.info.id }), } return } export const Transfer_To_Taiko_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: 'Send to Taiko Failed' } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/TransferToTaikoAccountPanel.tsx ================================================ import { AlertIcon, BackIcon, CloseIcon, ContactIcon, hexToRGB, Info2Icon, SearchIcon, TokenType, } from '@loopring-web/common-resources' import { Box, Button, Input, Tooltip, Typography, Modal } from '@mui/material' import { ModalCloseButton, SpaceBetweenBox } from '../../basic-lib' import { TransferToTaikoAccountProps } from '../../tradePanel' import { CoinIcons } from '../../tableList' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import { FeeSelectModal } from './FeeSelect' import { ContactSelection } from '../../tradePanel/components/ContactSelection' import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import { useTheme } from '@emotion/react' const formatTokenList = (tokens: string[]):string => { if (!tokens || tokens.length === 0) return ''; if (tokens.length === 1) return tokens[0]; if (tokens.length === 2) return `${tokens[0]} and ${tokens[1]}`; const lastToken = tokens[tokens.length - 1]; const otherTokens = tokens.slice(0, -1); return `${otherTokens.join(', ')}, and ${lastToken}`; } export const TransferToTaikoAccountModal = (props: TransferToTaikoAccountProps) => { const { onClickContact, balance, fee, onClickFee, onInputAmount, onInputAddress, token, onClickToken, onClickBalance, feeSelect, contacts, panel, tokenSelection, receiptInput, amountInput, onClickBack, onClickClose, open, supportedTokens, sendBtn, maxAlert, receiptError, receiptClear, showReceiptWarning, onClickConfirm, title, hideContactBtn } = props const theme = useTheme(); const mainPanel = ( <> {title} Select Token } rightNode={ Available: {balance} } /> {token.symbol} } inputProps={{ sx: { textAlign: 'right' } }} onInput={(e: any) => onInputAmount(e.target.value)} value={amountInput} /> {maxAlert.message || '--'} Recipient {receiptClear.show && ( )} {!hideContactBtn && } } onInput={(e: any) => onInputAddress(e.target.value)} value={receiptInput} /> {receiptError.show && ( {receiptError.message} )} {showReceiptWarning && ( Please ensure that the recipient's address is capable of properly receiving the assets. )}
    Trust Mode: Operated by Loopring's team to maintain liquidity.
    Supported Assets: {formatTokenList(supportedTokens)}.
    Transaction Fee } rightNode={ {feeSelect.feeLoading ? ( calculating ) : ( fee )} {fee !== '--' && ( )} } /> insufficient balance
    ) const confirmPanel = ( {title} Token Amount {amountInput} {token.symbol} Recipient {receiptInput} Fee {fee} ) const tokenSelectPanel = ( tokenSelection.onChangeFilter(e.target.value)} endAdornment={ tokenSelection.filter ? ( x ) : ( <> ) } placeholder='Search' startAdornment={} sx={{ width: '94%', px: 1, bgcolor: 'var(--field-opacity)' }} disableUnderline /> Cancel {tokenSelection.tokens.map((token) => { return ( tokenSelection.onClickToken(token.symbol)} leftNode={ {token.symbol} } rightNode={ {token.amount} } /> ) })} ) return ( {panel === 'main' ? ( mainPanel ) : panel === 'tokenSelection' ? ( tokenSelectPanel ) : panel === 'confirm' ? ( confirmPanel ) : ( )} ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/UnlockAccount.tsx ================================================ import { Trans } from 'react-i18next' import { IconType, PanelProps, UnlockAccountBase } from './BasicPanel' import { Box, Button, Link, Typography } from '@mui/material' import { WalletType } from '@loopring-web/loopring-sdk' import { FEED_BACK_LINK } from '@loopring-web/common-resources' import { TextareaAutosizeStyled } from '../../basic-lib' import React from 'react' import { DropdownIconStyled } from '../../tradePanel' import { TransErrorHelp } from '../../block' import { Checkbox } from '@mui/material' import { CheckBoxIcon, CheckedIcon } from '@loopring-web/common-resources' // symbol export const UnlockAccount_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const UnlockAccount_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: , } return } // symbol export const UnlockAccount_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: , } return } export const UnlockAccount_Failed = ({ error, errorOptions, t, onClickReset, ...props }: PanelProps & { walletType?: WalletType; onClickReset: () => void }) => { const isContractOrInCounterFactual = props.walletType && (props.walletType.isContract || props.walletType.isInCounterFactualStatus) const showDropdown = error?.code === 500000 || isContractOrInCounterFactual const [dropdownStatus, setDropdownStatus] = React.useState<'up' | 'down'>('down') const propsPatch = { ...props, iconType: IconType.FailedIcon, describe1: ( <> {error ? ( setDropdownStatus((prev) => (prev === 'up' ? 'down' : 'up'))} justifyContent={'center'} alignItems={'center'} > {showDropdown && } {isContractOrInCounterFactual && ( Please contact us . )} ) : ( )} {showDropdown && dropdownStatus === 'up' && (isContractOrInCounterFactual ? ( ) : ( {t('labelUnlockErrorLine1Part1')} {t('labelUnlockErrorLine1Part2')} {t('labelUnlockErrorLine1Part3')} {t('labelUnlockErrorLine2Part1')} {t('labelUnlockErrorLine2Part2')} ))} ), } return } export const UnlockAccount_Reset_Key_Confirm = ({ t, resetAccount }: PanelProps & { walletType?: WalletType; resetAccount: () => void }) => { const [checked, setChecked] = React.useState(false) return ( {t('labelUpdateAccount')} Please read the following caveats carefully before resetting your EDDSA key:

    • If you have any unsettled Dual Investment portfolios, your account will be locked until they are settled.
    • If you have an existing Portal position, you will not be able to reset your L2 on your own.
    • If you are unsure about the sequence of this operation, please contact us at support@loopring.io and provide your wallet details for assistance.
    • If you have any pending limit orders, they will be canceled because they are associated with the old L2 keypair.
    } icon={} color='default' sx={{ height: 16, width: 16, mr: 0.5 }} onChange={() => { setChecked(!checked) }} /> I have read the caveats and still want to reset the EDDSA key on my own.
    ) } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/UpdateAccount.tsx ================================================ import { TFunction } from 'react-i18next' import { BasicPanel, IconType, PanelProps, UpdateAccountBase } from './BasicPanel' import { Box, Typography } from '@mui/material' import { AnimationArrow, Button, useSettings } from '../../../index' import { AccountBasePanel, AccountBaseProps } from './index' import { DepositRecorder } from './DepositRecorder' import { AccountHashInfo, L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' export const UpdateAccount = ({ t, goUpdateAccount, ...props }: { t: TFunction } & AccountBaseProps & { goUpdateAccount?: () => void clearDepositHash: () => void chainInfos: AccountHashInfo }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( {t('labelActivatedAccountDeposit', { layer2: L1L2_NAME_DEFINED[network].layer2, })} ) } // symbol export const UpdateAccount_Approve_WaitForAuth = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const UpdateAccount_First_Method_Denied = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied', { layer2: L1L2_NAME_DEFINED[network].layer2, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const UpdateAccount_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } // symbol export const UpdateAccount_Success = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const describe1 = props.t( props.patch?.isReset ? 'labelResetAccountSuccess' : 'labelUpdateAccountSuccess', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) const describe2 = ( {props.t( props.patch?.isReset ? 'labelResetAccountSuccess2' : 'labelUpdateAccountSuccess2', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, )} ) const propsPatch = { iconType: IconType.DoneIcon, describe1, describe2, } return } // value symbol export const UpdateAccount_Failed = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelUpdateAccountFailed', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, }), } return } export const UpdateAccount_SmartWallet_NotSupported_Alert = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: Currently, only EOA wallets are supported.
    Loopring Smart Wallet support is coming soon.
    Stay tuned for updates!
    , } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/Vault.tsx ================================================ import { IconType, PanelProps, VaultBorrowBase, VaultDustCollectorBase, VaultExitBase, VaultJoinBase, VaultRepayBase, VaultTradeBase, } from './BasicPanel' import { Box, Tooltip, Typography } from '@mui/material' import { AlertIcon, EmptyValueTag, ErrorIcon, hexToRGB, Info2Icon, mapSpecialTokenName, TokenType, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import moment from 'moment/moment' import React from 'react' import { useSettings } from '../../../stores' import { useTheme } from '@emotion/react' import { CoinIcons } from '../../tableList' import { useTranslation } from 'react-i18next' import { SpaceBetweenBox } from '../../../components/basic-lib' const TradeDes2 = (props: PanelProps) => { const { isMobile, coinJson } = useSettings() const theme = useTheme() const { percentage, status, feeStr: amountFee, buyStr: amountBuy, sellStr: amountSell, sellToken: sellSymbol, buyToken: buySymbol, price, time, placedAmount, executedAmount, executedRate, convertedAmount, fromSymbol, toSymbol, } = props?.info ?? {} const {t} = useTranslation() return ( <> {props.t('labelVaultType')} {props.t('labelVaultSwap')} {props.t('labelVaultStatus')} {props.t('labelVaultTradeStatus', { status, percentage: percentage ? `(${percentage}%)` : '', })} {t('labelVaultPlacedAmount')} {t('labelVaultPlacedAmountTip')}}> {' '} {placedAmount} {t('labelVaultExecutedAmount')} {t('labelVaultExecutedAmountTip')}}> {executedAmount !== EmptyValueTag && ( )}{' '} {executedAmount} {t('labelVaultExecutedRate')} {executedRate} {t('labelVaultConvertedAmount')} {t('labelVaultConvertedAmountTip')}}> {convertedAmount !== EmptyValueTag && ( )}{' '} {convertedAmount} {props.t('labelVaultPrice')} {price} {props.t('labelVaultFee')} {amountFee ? ( <> {amountFee + ' ' + buySymbol} ) : ( EmptyValueTag )} {props.t('labelVaultTime')} {moment(time).format(YEAR_DAY_MINUTE_FORMAT)} <> {props.isPending && ( {props.t('labelVaultPendingDes')} )} ) } export const VaultTrade_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelVaultTradeSuccess', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const VaultTrade_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelVaultTradeFailed', { symbol: props.symbol, value: props.value, }), describe2: ( <> {props.info && } {props?.error && props.error?.message && ( {props.error?.message} )} ), } return } export const VaultTrade_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelVaultTradeInProgress', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } const JoinDes2 = (props: PanelProps) => { const { isMobile, coinJson } = useSettings() const theme = useTheme() const { symbol, vSymbol, sum, status, type, time } = props?.info ?? {} return ( <> {props.t('labelVaultType')} {type} {props.t('labelVaultStatus')} {props.t(`labelVaultJoinStatus`, { status, percentage: '', })} {props.t('labelVaultAmount')} {sum + ' ' + mapSpecialTokenName(symbol)} {/**/} {/* */} {/* {props.t('labelVaultReceive')}*/} {/* */} {/* */} {/* */} {/* {sum + ' ' + symbol}*/} {/* */} {/**/} {props.t('labelVaultTime')} {moment(time).format(YEAR_DAY_MINUTE_FORMAT)} <> {props.isPending && ( {props.t('labelVaultPendingDes')} )} ) } export const VaultJoin_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelVaultJoinSuccess', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const VaultJoin_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelVaultJoinFailed', { symbol: props.symbol, value: props.value, }), describe2: ( <> {props.info && } {props?.error && props.error?.message && ( {props.error?.message} )} ), } return } export const VaultJoin_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelVaultJoinInProgress', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const RedeemDes2 = ( props: PanelProps & { isPending?: boolean isNoWrap?: boolean isForcedLiqudation: boolean }, ) => { const { isMobile, coinJson } = useSettings() const theme = useTheme() const { usdValue, usdDebt, usdEquity, profitPercent, profit, time, status, isForcedLiqudation } = props?.info ?? {} const detail = React.useMemo(() => { return ( <> {props.t('labelVaultExitType')} {isForcedLiqudation ? props.t('labelVaultExitTypeForcedLiquidation') : props.t('labelVaultExitTypeClose')} {props.t('labelVaultExitStatusLabel')} {props.t('labelVaultExitStatus', { status })} {props.t('labelVaultExitTotalBalance')} {usdValue} {props.t('labelVaultExitTotalDebt')} {usdDebt} {props.t('labelVaultExitTotalEquity')} {usdEquity} {props.t('labelVaultExitProfit')} {profit} {props.t('labelVaultExitProfitPercentage')} {profitPercent} {props.t('labelVaultTime')} {moment(time).format(YEAR_DAY_MINUTE_FORMAT)} ) }, [usdValue, usdDebt, usdEquity, time, isMobile]) return props.isNoWrap ? ( <>{detail} ) : ( <> {detail} {props.isPending && ( {props.t('labelVaultPendingDes')} )} ) } export const VaultRedeem_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelVaultRedeemSuccess', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const VaultRedeem_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelVaultRedeemFailed', { symbol: props.symbol, value: props.value, }), describe2: ( <> {props.info && } {props?.error && props.error?.message && ( {props.error?.message} )} ), } return } export const VaultRedeem_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelVaultRedeemInProgress', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const BorrowDes2 = ( props: PanelProps & { isPending?: boolean }, ) => { const { isMobile, coinJson } = useSettings() const theme = useTheme() const { sum, status, symbol, time } = props?.info ?? {} return ( <> {props.t('labelVaultBorrowTypeLabel')} {props.t('labelVaultBorrow')} {props.t('labelVaultBorrowStatusLabel')} {props.t('labelVaultBorrowStatus', { status })} {props.t('labelVaultBorrowAmountLabel')} {sum + ' ' + symbol} {props.t('labelVaultTime')} {moment(time ? time : new Date()).format(YEAR_DAY_MINUTE_FORMAT)} <> {props.isPending && ( {props.t('labelVaultPendingDes')} )} ) } export const VaultBorrow_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelVaultBorrowSuccess', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const VaultBorrow_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelVaultBorrowFailed', { symbol: props.symbol, value: props.value, }), describe2: ( <> {props.info && }{' '} {props?.error && props.error?.message && ( {props.error?.message} )} ), } return } export const VaultBorrow_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelVaultBorrowInProgress', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const RepayDes2 = ( props: PanelProps & { isPending?: boolean }, ) => { const { isMobile, coinJson } = useSettings() const theme = useTheme() const { status, sum, vSymbol, symbol, time } = props // const { usdValue, usdDebt, usdEquity, forexMap } = props?.info ?? {} return ( <> {props.t('labelVaultRepayTypeLabel')} {props.t('labelVaultRepayType')} {props.t('labelVaultRepayStatusLabel')} {props.t('labelVaultRepayStatus', { status })} {props.t('labelVaultRepayTotalBalance')} {sum + ' ' + symbol} {props.t('labelVaultTime')} {moment(time).format(YEAR_DAY_MINUTE_FORMAT)} {props.isPending && ( {props.t('labelVaultPendingDes')} )} ) } export const VaultRepay_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelVaultRepaySuccess', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const VaultRepay_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelVaultRepayFailed', { symbol: props.symbol, value: props.value, }), describe2: ( <> {props.info && } {props?.error && props.error?.message && ( {props.error?.message} )} ), } return } export const VaultRepay_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelVaultRepayInProgress', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } const DustCollectorDes = ( props: PanelProps & { isPending?: boolean }, ) => { const theme = useTheme() const { status, totalValueInCurrency, convertedInUSDT, repaymentInUSDT, time, dusts } = props const { t } = useTranslation() return ( <> {repaymentInUSDT ? (repaymentInUSDT + ' USDT') : '--'} {totalValueInCurrency} {t('labelVaultRepayment')}} rightNode={{repaymentInUSDT ? (repaymentInUSDT + ' USDT') : '--'} } marginBottom={2} /> {t('labelVaultTime')}} rightNode={{moment(time).format(YEAR_DAY_MINUTE_FORMAT)}} /> {dusts && dusts.map((dust) => { return ( {dust.symbol} } rightNode={ {dust.amount} {dust.valueInCurrency} } /> ) })} {status === 'failed' && ( {t('labelVaultErrorOccurred')} )} ) } export const VaultDustCollector_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelVaultRepaySuccess', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } export const VaultDustCollector_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelVaultRepayFailed', { symbol: props.symbol, value: props.value, }), describe2: ( <> {props.info && } {props?.error && props.error?.message && ( {props.error?.message} )} ), } return } export const VaultDustCollector_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelVaultRepayInProgress', { symbol: props.symbol, value: props.value, }), describe2: props.info && , } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/Withdraw.tsx ================================================ import { IconType, PanelProps, WithdrawBase } from './BasicPanel' import { sanitize } from 'dompurify' import { useSettings } from '../../../stores' import { L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' // value symbol export const Withdraw_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } // value symbol export const Withdraw_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), } return } // value symbol export const Withdraw_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } // value symbol export const Withdraw_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelL2toL1InProgress'), } return } // value symbol export const Withdraw_Success = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelL2toL1Success', { symbol: props.symbol, value: props.value, layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } // value symbol export const Withdraw_Failed = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelL2toL1Failed', { symbol: props.symbol, value: props.value, layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTWithdraw_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const NFTWithdraw_First_Method_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelFirstSignDenied'), } return } export const NFTWithdraw_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: props.t('labelSignDenied'), } return } export const NFTWithdraw_In_Progress = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelL2toL1InProgress'), } return } export const NFTWithdraw_Success = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.DoneIcon, describe1: props.t('labelL2toL1Success', { symbol: sanitize(props.symbol ?? 'NFT'), value: props.value, layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } export const NFTWithdraw_Failed = (props: PanelProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const propsPatch = { iconType: IconType.FailedIcon, describe1: props.t('labelL2toL1Failed', { symbol: sanitize(props.symbol ?? 'NFT'), value: props.value, layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), } return } ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/WithdrawPanel.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { ModalBackButton, SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { WithdrawProps } from '../../tradePanel' import { IBData, SoursURL, TRADE_TYPE } from '@loopring-web/common-resources' import { TradeMenuList, useBasicTrade, WithdrawWrap } from '../../tradePanel/components' import React from 'react' import { cloneDeep } from 'lodash' import { WithdrawConfirm } from '../../tradePanel/components/WithdrawConfirm' import { ContactSelection } from '../../tradePanel/components/ContactSelection' import { Box } from '@mui/material' // import { getAllContacts } from "./TransferPanel"; export const WithdrawPanel = withTranslation(['common', 'error'], { withRef: true, })( , I>({ type = TRADE_TYPE.TOKEN, chargeFeeTokenList, onWithdrawClick, withdrawBtnStatus, assetsData, walletMap = {}, coinMap = {}, onBack, isFromContact, contact, contacts, ...rest }: WithdrawProps & WithTranslation & { assetsData: any[] }) => { const { onChangeEvent, index, switchData } = useBasicTrade({ ...rest, coinMap, type, walletMap, }) // const _onChangeEvent = _.debounce((_index: 0 | 1, { to, tradeData }: SwitchData) => { // onChangeEvent(_index, { to, tradeData }) // }, globalSetup.wait) const [panelIndex, setPanelIndex] = React.useState(index + 1) const handleConfirm = (index: number) => { setPanelIndex(index) } // React.useEffect(() => { // return () => { // _onChangeEvent.cancel() // } // }, []) // const hanleConfirm = () => {}; React.useEffect(() => { setPanelIndex(index + 1) }, [index]) // LP token should not exist in withdraw panel for now const getWalletMapWithoutLP = React.useCallback(() => { const clonedWalletMap = cloneDeep(walletMap ?? {}) const keyList = Object.keys(clonedWalletMap) keyList.forEach((key) => { const [first] = key.split('-') if (first === 'LP') { delete clonedWalletMap[key] } }) return clonedWalletMap }, [walletMap]) const confirmPanel = { key: 'confirm', element: React.useMemo( () => ( ), [onWithdrawClick, rest, switchData.tradeData, type], ), toolBarItem: ( { setPanelIndex(1) }} {...rest} /> ), } const tradePanel = { key: 'withdraw', element: React.useMemo( () => ( // @ts-ignore { setPanelIndex(3) }, }} /> ), [ rest, type, chargeFeeTokenList, switchData.tradeData, onChangeEvent, coinMap, onWithdrawClick, withdrawBtnStatus, assetsData, walletMap, ], ), toolBarItem: React.useMemo( () => ( <> {onBack ? ( { onBack() }} {...rest} /> ) : ( <> )} ), [onBack], ), } const tokenSelectionPanel = { key: 'tradeMenuList', element: React.useMemo( () => ( ), [switchData, rest, onChangeEvent, getWalletMapWithoutLP], ), toolBarItem: undefined, } const contactSelectionPanel = { key: 'contactSelection', element: React.useMemo( () => ( { setPanelIndex(1) rest.handleOnAddressChange(address, true) }} scrollHeight={rest.isToMyself ? '280px' : '480px'} /> ), [contacts], ), toolBarItem: React.useMemo( () => ( { setPanelIndex(1) }} {...rest} /> ), [onBack], ), } const props: SwitchPanelProps = { index: panelIndex, // show default show panelList: [confirmPanel, tradePanel, tokenSelectionPanel, contactSelectionPanel], } return type == TRADE_TYPE.TOKEN && !switchData.tradeData?.belong ? ( {'loading'} ) : ( ) }, ) as (props: WithdrawProps & React.RefAttributes) => JSX.Element ================================================ FILE: packages/component-lib/src/components/modal/ModalPanels/index.ts ================================================ export * from './Interface' export * from './AccountBase' export * from './CreateAccount' export * from './Deposit' export * from './DepositNFT' export * from './Transfer' export * from './Withdraw' export * from './Dual' export {BridgePanel} from './BridgePanel' export * from './UpdateAccount' export * from './UnlockAccount' export * from './ExportAccount' export * from './QRAddressPanel' export * from './NoAccount' export * from './HadAccount' export * from './ModalAccount' export * from './Connect' export * from '../../tradePanel/Deposit/DepositGroup' export * from './TransferPanel' export * from './WithdrawPanel' export * from './DepositPanel' export * from './MintNFT' export * from './DeployNFT' export * from './AddAsset' export * from './SendAsset' export * from './SendNFTAsset' export * from './CheckActiveStatus' export * from './CheckImportCollection' export * from './ForceWithdraw' export * from './ForceWithdrawPanel' export * from './ThirdPanelReturn' export * from './CreateRedPacketPanel' export * from './ClaimWithdraw' export * from './RedPacket' export * from './FeeSelect' export * from './EditContact' export { IconType, BasicPanel } from './BasicPanel' export * from './Vault' export {TransferToTaikoAccountModal} from './TransferToTaikoAccountPanel' export { Transfer_To_Taiko_WaitForAuth, Transfer_To_Taiko_First_Method_Denied, Transfer_To_Taiko_User_Denied, Transfer_To_Taiko_In_Progress, Transfer_To_Taiko_Success, Transfer_To_Taiko_banxa_confirm, Transfer_To_Taiko_Failed } from './TransferToTaikoAccount' export { Coinbase_Smart_Wallet_Password_Intro, Coinbase_Smart_Wallet_Password_Set, Coinbase_Smart_Wallet_Password_Set_Confirm, Coinbase_Smart_Wallet_Password_Input, Coinbase_Smart_Wallet_Password_Forget_Password_Confirm, Coinbase_Smart_Wallet_Password_Forget_Password } from './CoinbaseSmartWalletModal' ================================================ FILE: packages/component-lib/src/components/modal/QRCode/Interface.ts ================================================ import { ReactNode } from 'react' export interface GatewayItemQRCode { key: string imgSrc: string handleSelect?: (event: React.MouseEvent) => void } /** * @param handleSelect default hanldeSelect, if item have no private handleSelect function */ export interface QRCodePanelProps { title?: string | JSX.Element description?: string | JSX.Element } export type ModalQRCodeProps = QRCodePanelProps & { open: boolean className?: string onClose: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] } export type GuardianModalProps = { open: boolean onClose: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] title: ReactNode body: ReactNode onBack?: () => void showBackButton?: boolean } ================================================ FILE: packages/component-lib/src/components/modal/QRCode/QRCode.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { Box, BoxProps, Modal, Typography } from '@mui/material' import { ModalQRCodeProps, QRCodePanelProps } from './Interface' import { ModalCloseButton } from '../../basic-lib' import QRCodeStyling, { Options } from 'qr-code-styling' import { SoursURL } from '@loopring-web/common-resources' import React from 'react' const ModalContentStyled = styled(Box)` & > div { background: var(--color-pop-bg); position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: var(--modal-width); } &.guardianPop .content { padding-top: ${({ theme }) => 5 * theme.unit}px; border-radius: ${({ theme }) => theme.unit}px; } ` export type QCodeProps = { url: string size?: number fgColor1?: string fgColor2?: string bgColor?: string imageInfo?: { imageSrc?: string; size?: number } } const qrCode = new QRCodeStyling() export const QRCode = ({ size = 160, fgColor1 = 'var(--color-primary)', fgColor2 = '#000', bgColor = '#fff', url = 'https://exchange.loopring.io/', imageInfo = { imageSrc: `${SoursURL + 'svg/loopring.svg'}`, size: 40, }, }: QCodeProps & QRCodePanelProps) => { const ref = React.useRef() React.useEffect(() => { if (url) { qrCode.update({ type: 'svg', data: url, width: size, height: size, image: imageInfo.imageSrc, dotsOptions: { gradient: { rotation: 45, type: 'linear', colorStops: [ { offset: 0, color: fgColor1, }, { offset: 1, color: fgColor2, }, ], }, type: 'dots', }, backgroundOptions: { color: bgColor, }, imageOptions: { crossOrigin: 'anonymous', margin: 8, }, cornersSquareOptions: { type: 'extra-rounded', }, cornersDotOptions: { type: 'square', }, }) if (ref.current) { const boxRef = ref.current as any while (boxRef.hasChildNodes()) { boxRef.removeChild(boxRef.lastChild) } qrCode.append(ref.current) } } }, [url]) return } export const QRCodeV2 = ({ options, ...rest }: { options: Options } & BoxProps) => { const ref = React.useRef(null) React.useEffect(() => { if (ref.current) { if (ref.current.children[0]) { ref.current.removeChild(ref.current.children[0]) } const qrCode = new QRCodeStyling(options) qrCode.append(ref.current) } }, [options, ref]) return } export const QRCodePanel = ({ title, description, ...rest }: // imageSettings = { // height: 80, // width: 80, // src: `${SoursURL + "svg/loopring.svg"}`, // }, // handleClick QCodeProps & QRCodePanelProps) => { if (rest.url === undefined) { rest.url = '' } return ( {title && ( {title} )} {description && ( {description} )} ) } export const ModalQRCode = withTranslation('common')( ({ onClose, open, t, ...rest }: QCodeProps & ModalQRCodeProps & WithTranslation) => { return ( ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/QRCode/index.ts ================================================ export * from './QRCode' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/modal/RedPacketPanels/Interface.ts ================================================ export enum RedPacketViewStep { QRCodePanel, OpenPanel, DetailPanel, TimeOutPanel, PreparePanel, OpenedPanel, RedPacketClock, BlindBoxDetail, Loading, } ================================================ FILE: packages/component-lib/src/components/modal/RedPacketPanels/RedPacketModal.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { Box, Modal } from '@mui/material' import { ModalBackButton, ModalCloseButton, ModalRedPacketProps, RedPacketViewStep, } from '../../../index' import SwipeableViews from 'react-swipeable-views' import { CloseRedPacketIcon } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { useTheme } from '@emotion/react' import { isEmpty } from 'lodash' const BoxStyle = styled(Box)` .redPacketClose { svg { height: var(--btn-icon-size); width: var(--btn-icon-size); } //transform: translateY(-50%) translateX(-50%); //left: 50%; top: ${({ theme }) => 3.5 * theme.unit}px; right: ${({ theme }) => 16 * theme.unit}px; } ` export const ModalRedPacket = withTranslation('common', { withRef: true })( ({ // t, open, onClose, step, onBack, panelList, style, ...rest }: ModalRedPacketProps & WithTranslation) => { const theme = useTheme() return ( {/* hide close button if view redpacket view not rendered */} {step !== RedPacketViewStep.Loading && !isEmpty(panelList[step].view.props) && ( } onClose={onClose} className={'redPacketClose'} {...rest} /> )} {onBack ? : <>} {panelList.map((panel, index) => { return ( {panel.view} ) })} ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/RedPacketPanels/index.ts ================================================ export * from './RedPacketModal' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/modal/Vault/VaultBorrowPanel.tsx ================================================ import { IBData, SoursURL, TokenType, TRADE_TYPE, VaultBorrowData, } from '@loopring-web/common-resources' import { CountDownIcon, SwitchPanel, SwitchPanelProps, VaultBorrowProps, } from '@loopring-web/component-lib' import React from 'react' import { Box, Typography } from '@mui/material' import { TradeMenuList, useBasicTrade, VaultBorrowWrap } from '../../tradePanel/components' import { useTranslation } from 'react-i18next' export const VaultBorrowPanel = , V extends VaultBorrowData, I>({ // onVaultBorrowClick, walletMap = {}, coinMap = {}, _width, type = TRADE_TYPE.TOKEN, onRefreshData, ...rest }: VaultBorrowProps) => { const { t, i18n } = useTranslation() const [panelIndex, setPanelIndex] = React.useState(0) const onChangeEvent = (index, data) => { if (index !== panelIndex) { if (data.tradeData.belong !== rest.tradeData.belong) { rest.handlePanelEvent && rest.handlePanelEvent(data, "Tobutton") } setPanelIndex(index) } else { rest.handlePanelEvent && rest.handlePanelEvent(data, "Tobutton") } } const props: SwitchPanelProps<'tradeMenuList' | 'trade' | 'confirm'> = { index: panelIndex, // show default show panelList: [ { key: 'trade', element: React.useMemo( () => ( // @ts-ignore ), [rest, onChangeEvent, walletMap, coinMap], ), toolBarItem: React.useMemo( () => ( <> ), [], ), }, { key: 'tradeMenuList', element: React.useMemo( () => ( // @ts-ignore ), [rest, walletMap, coinMap], ), toolBarItem: undefined, }, ], } return !rest.tradeData?.belong ? ( {'loading'} ) : ( ) } ================================================ FILE: packages/component-lib/src/components/modal/Vault/VaultExitPanel.tsx ================================================ import { useSettings } from '../../../stores' import { L1L2_NAME_DEFINED, MapChainId, TradeBtnStatus } from '@loopring-web/common-resources' import { Box, Divider, Typography, IconButton } from '@mui/material' import { Trans, useTranslation } from 'react-i18next' import { Button } from '../../basic-lib' import React from 'react' import { VaultExitBaseProps } from '../../tradePanel' import CloseIcon from '@mui/icons-material/Close' export const VaultExitPanel = ({ onSubmitClick, onClose, btnStatus, disabled, confirmLabel = 'labelVaultConfirm', cancelLabel = 'labelVaultCancel', }: VaultExitBaseProps) => { const { t } = useTranslation() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const getDisabled = React.useMemo(() => { return disabled || btnStatus === TradeBtnStatus.DISABLED }, [btnStatus, disabled]) const label = React.useMemo(() => { if (confirmLabel) { const key = confirmLabel.split('|') return t( key[0], key && key[1] ? { arg: key[1].toString(), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(`labelVaultConfirm`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [confirmLabel]) return ( <> Settle You can only settle your account after all existing positions have been closed. · If there is a loss (due to an unprofitable trade or interest payments), a portion of your collateral may be used to cover the deficit. In this case, only the remaining collateral will be available for withdrawal from Portal. · If your trades are profitable, your full collateral will be available for withdrawal, and any profits will be credited to your Loopring DeFi account accordingly. ) } ================================================ FILE: packages/component-lib/src/components/modal/Vault/VaultJoinPanel.tsx ================================================ import { IBData, SoursURL, TRADE_TYPE, VaultJoinData, VaultLoanType } from '@loopring-web/common-resources' import { CountDownIcon, Modal, SwitchPanel, SwitchPanelProps, useSettings, VaultJoinProps, } from '@loopring-web/component-lib' import React from 'react' import { Box, Typography, Divider, Tabs, Tab } from '@mui/material' import { TradeMenuList, VaultJoinWrap } from '../../tradePanel/components' import { useTranslation } from 'react-i18next' import { useTheme } from '@emotion/react' export const VaultJoinPanelModal = , V extends VaultJoinData, I>({ onSubmitClick, btnStatus, isActiveAccount, walletMap = {}, coinMap = {}, onRefreshData, _width, onToggleAddRedeem, isAddOrRedeem, panelIndex, handleConfirm, basicTrade: {onChangeEvent, switchData}, modalOpen, onCloseModal, ...rest }: VaultJoinProps) => { const isMobile = useSettings().isMobile const { t, i18n } = useTranslation() const theme = useTheme() const props: SwitchPanelProps<'tradeMenuList' | 'trade' | 'confirm'> = { index: panelIndex, // show default show panelList: [ { key: 'trade', // onBack, element: React.useMemo( () => ( // @ts-ignore ), [ rest, switchData.tradeData, onChangeEvent, onSubmitClick, // onDepositClick, btnStatus, walletMap, coinMap, isAddOrRedeem, ], ), toolBarItem: React.useMemo( () => ( <> {isActiveAccount ? ( Supply Collateral ) : ( { onToggleAddRedeem(value) }} sx={{marginLeft: 1.5}} > )} ), [isAddOrRedeem, isActiveAccount], ), }, { key: 'tradeMenuList', element: React.useMemo( () => ( // @ts-ignore ), [rest, onChangeEvent, switchData.tradeData, walletMap, coinMap], ), toolBarItem: undefined, }, ], } return ( {'loading'} ) : isMobile ? ( ) : ( ) } /> ) } ================================================ FILE: packages/component-lib/src/components/modal/Vault/VaultLoanPanel.tsx ================================================ import { Box, BoxProps, Divider, Tab, Tabs, Toolbar, Typography } from '@mui/material' import styled from '@emotion/styled' import { boxLiner, toolBarPanel } from '../../styled' import { useTranslation, WithTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import { IBData, VaultBorrowData, VaultLoanType } from '@loopring-web/common-resources' import { VaultBorrowPanel } from './VaultBorrowPanel' import { VaultRepayPanel } from './VaultRepayPanel' import { useSystem } from '@loopring-web/core' import { VaultBorrowProps, VaultRepayWrapProps } from '../../tradePanel' export type VaultLoanProps = { vaultLoanType: VaultLoanType handleTabChange: (index: VaultLoanType) => void vaultRepayProps: VaultRepayWrapProps vaultBorrowProps: VaultBorrowProps } const TabPanelBtn = ({ t, value, handleChange }: WithTranslation & any) => { return ( ) } const WrapStyle = styled(Box)< BoxProps & { _height?: number | string _width?: number | string isMobile: boolean } >` ${({ _width, isMobile }) => isMobile ? `width:100%;height:auto;` : ` width: ${ typeof _width === 'string' ? _width : typeof _width === 'number' ? _width + 'px' : `var(--swap-box-width)` }; height: auto;`} ${({ theme }) => boxLiner({ theme })} ${({ theme }) => toolBarPanel({ theme })} border-radius: ${({ theme }) => theme.unit}px; .trade-panel .menu-panel > div { padding: 8px 0px; } .trade-panel .coinInput-wrap { background: var(--field-opacity); } .MuiToolbar-root { justify-content: space-between; } .vaultRepay { .MuiListItem-root { .MuiListItemText-root { align-items: center; } width: auto; padding: ${({ theme }) => theme.unit}px ${({ theme }) => theme.unit / 2}px ${({ theme }) => theme.unit}px ${({ theme }) => 2 * theme.unit}px; margin: ${({ theme }) => theme.unit}px ${({ theme }) => 0 * theme.unit}px; ${({ theme }) => theme.border.defaultFrame({ d_W: 1, c_key: 'var(--color-border)', })}; &:hover { ${({ theme }) => theme.border.defaultFrame({ d_W: 1, c_key: 'var(--color-border-select)', })}; .MuiSvgIcon-root { fill: var(--color-primary); } } } } ` as ( props: BoxProps & { _height?: number | string _width?: number | string isMobile: boolean }, ) => JSX.Element export const VaultLoanPanel = , V extends VaultBorrowData, I>({ handleTabChange, vaultLoanType, vaultRepayProps, vaultBorrowProps, }: VaultLoanProps) => { const { t } = useTranslation() const { forexMap } = useSystem() const { // defaultNetwork, isMobile, } = useSettings() return ( Repay {vaultLoanType === VaultLoanType.Borrow && ( )} {vaultLoanType === VaultLoanType.Repay && ( )} ) } ================================================ FILE: packages/component-lib/src/components/modal/Vault/VaultRepayPanel.tsx ================================================ import { BackIcon, CurrencyToTag, EmptyValueTag, getValuePrecisionThousand, IBData, PriceTag, TokenType, TRADE_TYPE, VaultRepayData, } from '@loopring-web/common-resources' import { EmptyDefault, SwitchPanel, SwitchPanelProps, useSettings, VaultRepayProps, } from '@loopring-web/component-lib' import React, { useEffect } from 'react' import { Box, Typography } from '@mui/material' import { TradeMenuList, useBasicTrade, VaultRepayWrap } from '../../tradePanel/components' import { useTranslation } from 'react-i18next' export const VaultRepayPanel = , V extends VaultRepayData, I>({ walletMap = {}, coinMap = {}, _width, type = TRADE_TYPE.TOKEN, forexMap, initialSymbol, ...rest }: VaultRepayProps) => { const { currency } = useSettings() const { t, i18n } = useTranslation() const { tokenInfo } = rest const { onChangeEvent, index, switchData } = useBasicTrade({ ...rest, type, walletMap, coinMap, } as any) useEffect(() => { if (initialSymbol) { onChangeEvent(1, { to: 'button', tradeData: { ...switchData.tradeData, belong: initialSymbol as any, }, }) } }, [initialSymbol]) const props: SwitchPanelProps<'tradeMenuList' | 'trade' | 'confirm'> = { index: index, // show default show panelList: [ { key: 'tradeMenuList', element: React.useMemo( () => ( // @ts-ignore { // @ts-ignore onChangeEvent(1 ^ _index, data) }, hasCancel: false, selected: switchData.tradeData.belong, tradeData: switchData.tradeData, walletMap, coinMap, tokenType: TokenType.vault, contentEle: ({ ele }: { ele: any }) => { return ( { // ele?.count || ele.count !== '0' // ? getValuePrecisionThousand( // ele.count, // tokenInfo?.precision ?? 6, // tokenInfo?.precision ?? 6, // ) // : EmptyValueTag} // / } {getValuePrecisionThousand( ele.borrowed, tokenInfo?.precision ?? 6, tokenInfo?.precision ?? 6, )} {PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (ele?.usd ?? 0) * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: false, isAbbreviate: true, abbreviate: 6, }, )} ) }, }} filterWithBorrowed={true} /> ), [rest, onChangeEvent, switchData.tradeData, walletMap, coinMap], ), toolBarItem: undefined, }, { key: 'trade', element: React.useMemo( () => ( { // @ts-ignore onChangeEvent(1 ^ _index, data) }, disabled: !!rest.disabled, walletMap, coinMap, }} /> ), [rest, switchData.tradeData, onChangeEvent, walletMap, coinMap], ), toolBarItem: React.useMemo(() => <>, []), }, ], } return Reflect.ownKeys(walletMap)?.length ? ( ) : ( ( {t('labelNoContent')} )} /> ) } ================================================ FILE: packages/component-lib/src/components/modal/Vault/index.ts ================================================ export * from './VaultJoinPanel' export * from './VaultExitPanel' export * from './VaultLoanPanel' ================================================ FILE: packages/component-lib/src/components/modal/WalletConnect/Interface.ts ================================================ import { GatewayItem } from '@loopring-web/common-resources' /** * @param handleSelect default hanldeSelect, if item have no private handleSelect function */ export interface ProviderMenuProps { termUrl: string NetWorkItems?: JSX.Element gatewayList: GatewayItem[] handleSelect?: (event: React.MouseEvent, key: string) => void providerName?: string status?: 'processing' } export enum WalletConnectStep { Provider, CommonProcessing, WalletConnectProcessing, WalletConnectQRCode, SuccessConnect, FailedConnect, RejectConnect, RejectSwitchNetwork, } ================================================ FILE: packages/component-lib/src/components/modal/WalletConnect/ModalWalletConnect.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { Box, Modal } from '@mui/material' import { ModalBackButton, ModalCloseButton, ModalWalletConnectProps, ModelPanelStyle, } from '../../../index' export const ModalWalletConnect = withTranslation('common', { withRef: true })( ({ // t, open, onClose, step, onBack, panelList, style, ...rest }: ModalWalletConnectProps & WithTranslation) => { return ( {onBack ? : <>} {panelList.map((panel, index) => { return ( {panel.view} ) })} ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/WalletConnect/ProviderMenu.tsx ================================================ import { Box, Checkbox, FormControlLabel as MuiFormControlLabel, Link, Typography, } from '@mui/material' import { Trans, WithTranslation } from 'react-i18next' import styled from '@emotion/styled' import { ProviderMenuProps } from './Interface' import { CheckBoxIcon, CheckedIcon, ConvertToIcon, GatewayItem, LOOPRING_DOCUMENT, myLog, } from '@loopring-web/common-resources' import React from 'react' import { MenuBtnStyled, shake } from '../../styled' import * as loopringProvider from '@loopring-web/web3-provider' import { useSettings } from '../../../stores' const CheckboxStyled = styled(Checkbox)` &.shake { animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; } ${shake} ` as typeof Checkbox const BoxContent = styled(Box)` .modalContent { padding-right: ${({ theme }) => theme.unit * 5}px; padding-left: ${({ theme }) => theme.unit * 5}px; } @media only screen and (max-width: 768px) { .modalContent { padding-right: ${({ theme }) => theme.unit * 4}px; padding-left: ${({ theme }) => theme.unit * 4}px; } } ` const BoxStyle = styled(Box)` ${({ theme }) => theme.border.defaultFrame({ c_key: 'blur', d_R: 1 / 2, d_W: 0 })}; //background: var(--provider-agree); .MuiFormControlLabel-root { font-size: ${({ theme }) => theme.fontDefault.h6}; align-items: flex-start; margin-right: 0; .MuiTypography-root { padding: ${({ theme }) => theme.unit}px 0; } } ` as typeof Box export const ProviderMenu = ({ t, gatewayList, termUrl, handleSelect, NetWorkItems, status, providerName = loopringProvider.ConnectProviders.Unknown, }: ProviderMenuProps & WithTranslation) => { const { isMobile, defaultNetwork } = useSettings() const [checkboxValue, setCheckboxValue] = React.useState(false) const [isShake, setIsShake] = React.useState(false) React.useEffect(() => { const isAgreed = localStorage.getItem('userTermsAgreed') setCheckboxValue(isAgreed === 'true') setIsShake(false) }, []) const handleCheckboxChange = React.useCallback((_event: any, state: boolean) => { setCheckboxValue(state) localStorage.setItem('userTermsAgreed', String(state)) }, []) const _handleSelect = React.useCallback( (event: any, key: string, handleSelect?: (event: React.MouseEvent, key: string) => void) => { if (handleSelect && checkboxValue) { handleSelect(event, key) setIsShake(false) } else if (!checkboxValue) { setIsShake(true) setTimeout(() => { if (isShake) { setIsShake(false) } }, 80) } }, [checkboxValue, isShake], ) const isProvider = React.useCallback( (key: string) => { if ( key === 'WalletConnectV1' && loopringProvider.connectProvides?.usedWeb3 && loopringProvider.connectProvides?.usedProvide ) { myLog('connectProvides', 'usedWeb3', '') } return providerName === key }, [providerName], ) const loadingGif = './static/loading-1.gif' // const !== loopringProvider.ConnectProviders.Unknown return ( {t('labelConnectWallet')} {NetWorkItems && ( {NetWorkItems} )} {gatewayList.map((item: GatewayItem) => ( { _handleSelect(e, item.key, item.handleSelect ? item.handleSelect : handleSelect) }} endIcon={ status === 'processing' && isProvider(item.key) ? ( {t('labelConnecting')} ) : ( ) } > {item.key} {t(item.keyi18n)} ))} } icon={} color='default' /> } label={ I have read, understand, and agree to the { window.open(`${LOOPRING_DOCUMENT}terms_en.md`, '_blank') window.opener = null }} > Terms of Service . } /> ) { /**/ } } ================================================ FILE: packages/component-lib/src/components/modal/WalletConnect/WalletConnectQRCode.tsx ================================================ import { Box, Link, Typography } from '@mui/material' import { WithTranslation } from 'react-i18next' import { SoursURL } from '@loopring-web/common-resources' import { QRCode } from '../QRCode' export const WalletConnectQRCode = ({ url, onCopy, t, }: { url: string; onCopy: () => void } & WithTranslation) => { return ( {'walletConnect'} WalletConnect {t('labelWalletConnectQRCode')} {t('labelCopyClipBoard')} ) } ================================================ FILE: packages/component-lib/src/components/modal/WalletConnect/index.ts ================================================ export * from './ModalWalletConnect' export * from './Interface' export * from './WalletConnectQRCode' export * from './ProviderMenu' ================================================ FILE: packages/component-lib/src/components/modal/WalletPanels/Interface.ts ================================================ export enum GuardianStep { LockAccount_WaitForAuth, LockAccount_User_Denied, LockAccount_Success, LockAccount_Failed, Approve_User_Denied, Approve_WaitForAuth, Approve_Success, Approve_Failed, Reject_User_Denied, Reject_WaitForAuth, Reject_Success, Reject_Failed, } ================================================ FILE: packages/component-lib/src/components/modal/WalletPanels/LockWallet.tsx ================================================ import { Trans, useTranslation } from 'react-i18next' import { BasicPanel, IconType, PanelProps } from '../ModalPanels/BasicPanel' import { Box, Typography } from '@mui/material' export const LockWallet = (props: PanelProps) => { const propsPatch = { title: 'labelLockWallet', } return } // symbol export const LockAccount_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const LockAccount_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: , } return } // symbol export const LockAccount_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: , } return } // value symbol export const LockAccount_Failed = (props: PanelProps & any) => { const { t } = useTranslation('common') const propsPatch = { iconType: IconType.FailedIcon, describe1: ( {t('labelLockAccountFailed')} {props?.error} ), } return } ================================================ FILE: packages/component-lib/src/components/modal/WalletPanels/ModalWallet.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { Box, Modal } from '@mui/material' import { useTheme } from '@emotion/react' import { ModalBackButton, ModalCloseButton, ModalGuardianProps, QRButtonStyle, SwipeableViewsStyled, SwitchPanelStyled, } from '../../../index' export const ModalWallet = withTranslation('common', { withRef: true })( ({ open, onClose, step, onBack, style, noClose, onQRClick, panelList, ...rest }: ModalGuardianProps & WithTranslation) => { const theme = useTheme() const { w, h } = style ? style : { w: undefined, h: undefined } return ( {noClose ? <> : } {onBack ? : <>} {onQRClick ? : <>} {panelList.map((panel, index) => { return ( {panel.view} ) })} ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/WalletPanels/WalletApprove.tsx ================================================ import { Trans } from 'react-i18next' import { BasicPanel, IconType, PanelProps } from '../ModalPanels/BasicPanel' export const WalletApprove = (props: PanelProps) => { const propsPatch = { title: 'labelWalletApprove', } return } // symbol export const Approve_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const Approve_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: , } return } // symbol export const Approve_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: , } return } // value symbol export const Approve_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: , } return } export const HebaoReject = (props: PanelProps) => { const propsPatch = { title: 'labelWalletReject', } return } // symbol export const Reject_WaitForAuth = (props: PanelProps) => { const propsPatch = { iconType: IconType.LoadingIcon, describe1: props.t('labelWaitForAuth'), } return } export const Reject_User_Denied = (props: PanelProps) => { const propsPatch = { iconType: IconType.RefuseIcon, describe1: , } return } // symbol export const Reject_Success = (props: PanelProps) => { const propsPatch = { iconType: IconType.DoneIcon, describe1: , } return } // value symbol export const Reject_Failed = (props: PanelProps) => { const propsPatch = { iconType: IconType.FailedIcon, describe1: , } return } ================================================ FILE: packages/component-lib/src/components/modal/WalletPanels/index.ts ================================================ export * from './Interface' export * from './LockWallet' export * from './ModalWallet' export * from './WalletApprove' ================================================ FILE: packages/component-lib/src/components/modal/accountList_new.stories.tsx ================================================ import styled from '@emotion/styled' import React from 'react' import { Meta, Story } from '@storybook/react' import { WithTranslation, withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import { Box, Button, Grid, Typography } from '@mui/material' import { ModalWalletConnect } from './WalletConnect' import { AccountBaseProps, AccountStep as AccountStep, CreateAccount_Approve_Denied, CreateAccount_Approve_Submit, CreateAccount_Approve_WaitForAuth, CreateAccount_Denied, CreateAccount_Failed, CreateAccount_Submit, CreateAccount_WaitForAuth, Deposit_Approve_Denied, Deposit_Approve_WaitForAuth, Deposit_Denied, Deposit_Failed, Deposit_Submit, Deposit_WaitForAuth, ExportAccount_Approve_WaitForAuth, ExportAccount_Failed, ExportAccount_Success, ExportAccount_User_Denied, General_Failed, HadAccount, NoAccount, Transfer_Failed, Transfer_First_Method_Denied, Transfer_In_Progress, Transfer_Success, Transfer_User_Denied, Transfer_WaitForAuth, UpdateAccount, UpdateAccount_Approve_WaitForAuth, UpdateAccount_Failed, UpdateAccount_First_Method_Denied, UpdateAccount_Success, UpdateAccount_User_Denied, Withdraw_Failed, Withdraw_First_Method_Denied, Withdraw_In_Progress, Withdraw_Success, Withdraw_User_Denied, Withdraw_WaitForAuth, } from './ModalPanels' import { account } from '../../static' import { ConnectProviders } from '@loopring-web/web3-provider' import { gatewayList } from '@loopring-web/common-resources' const Style = styled.div` flex: 1; height: 100%; ` const Template: Story = withTranslation()((rest: WithTranslation) => { gatewayList[0] = { ...gatewayList[0], handleSelect: () => console.log('metaMask 11'), } const mainBtn = React.useMemo(() => { return ( ) }, []) const accountInfoProps: AccountBaseProps = { ...account, level: 'VIP 1', etherscanUrl: 'https://material-ui.com/components/material-icons/', } // const accAddress = '0xcEd11e039a5C50927a17a8D4632616DFa8F72BF6' const retryBtn = React.useMemo(() => { return { btnTxt: 'retry', callback: () => {}, } }, []) const closeBtn = React.useMemo(() => { return { btnTxt: 'close', callback: () => {}, } }, []) const { nameList0, accountList0 } = React.useMemo(() => { const accountMap = { [AccountStep.NoAccount]: { view: ( {}, }} /> ), }, [AccountStep.UpdateAccount]: { view: ( undefined} /> ), }, [AccountStep.HadAccount]: { view: ( ), }, } return { nameList0: Object.keys(accountMap), accountList0: Object.values(accountMap), } }, []) const { nameList, accountList } = React.useMemo(() => { const accountMap = { [AccountStep.Deposit_Sign_WaitForRefer]: { view: ( ), }, [AccountStep.Deposit_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.Deposit_Approve_Denied]: { view: ( ), }, [AccountStep.Deposit_WaitForAuth]: { view: ( ), }, [AccountStep.Deposit_Denied]: { view: ( ), }, [AccountStep.Deposit_Failed]: { view: ( ), }, [AccountStep.Deposit_Submit]: { view: ( {}, }} providerName={account.connectName as ConnectProviders} {...{ ...rest, symbol: 'LRC', value: '1.2121', }} /> ), }, } return { nameList: Object.keys(accountMap), accountList: Object.values(accountMap), } }, []) const { nameList2, accountList2 } = React.useMemo(() => { const accountMap = { [AccountStep.General_Failed]: { view: ( ), }, [AccountStep.Transfer_WaitForAuth]: { view: ( ), }, [AccountStep.Transfer_First_Method_Denied]: { view: ( ), }, [AccountStep.Transfer_User_Denied]: { view: ( ), }, [AccountStep.Transfer_In_Progress]: { view: ( ), }, [AccountStep.Transfer_Success]: { view: ( ), }, [AccountStep.Transfer_Failed]: { view: ( ), }, } return { nameList2: Object.keys(accountMap), accountList2: Object.values(accountMap), } }, []) const { nameList3, accountList3 } = React.useMemo(() => { const accountMap = { [AccountStep.Withdraw_WaitForAuth]: { view: ( ), }, [AccountStep.Withdraw_First_Method_Denied]: { view: ( ), }, [AccountStep.Withdraw_User_Denied]: { view: ( ), }, [AccountStep.Withdraw_In_Progress]: { view: ( ), }, [AccountStep.Withdraw_Success]: { view: ( ), }, [AccountStep.Withdraw_Failed]: { view: ( ), }, } return { nameList3: Object.keys(accountMap), accountList3: Object.values(accountMap), } }, []) const { nameList4, accountList4 } = React.useMemo(() => { const accountMap = { [AccountStep.CreateAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.CreateAccount_Approve_Denied]: { view: ( ), }, [AccountStep.CreateAccount_Approve_Submit]: { view: ( ), }, [AccountStep.CreateAccount_WaitForAuth]: { view: ( ), }, [AccountStep.CreateAccount_Denied]: { view: ( ), }, [AccountStep.CreateAccount_Failed]: { view: ( ), }, [AccountStep.CreateAccount_Submit]: { view: ( ), }, } return { nameList4: Object.keys(accountMap), accountList4: Object.values(accountMap), } }, []) const { nameList5, accountList5 } = React.useMemo(() => { const accountMap = { [AccountStep.UpdateAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.UpdateAccount_First_Method_Denied]: { view: ( ), }, [AccountStep.UpdateAccount_User_Denied]: { view: ( ), }, [AccountStep.UpdateAccount_Success]: { view: ( ), }, [AccountStep.UpdateAccount_Success]: { view: ( ), }, [AccountStep.UpdateAccount_Failed]: { view: ( ), }, [AccountStep.ResetAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.ResetAccount_First_Method_Denied]: { view: ( ), }, [AccountStep.ResetAccount_User_Denied]: { view: ( ), }, [AccountStep.ResetAccount_Success]: { view: ( ), }, [AccountStep.ExportAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.ExportAccount_User_Denied]: { view: ( ), }, [AccountStep.ExportAccount_Success]: { view: ( ), }, [AccountStep.ExportAccount_Failed]: { view: ( ), }, } return { nameList5: Object.keys(accountMap), accountList5: Object.values(accountMap), } }, []) const fontSize = '30px' const color = 'white' const width = 400 const w = 540 const h = 600 return ( <> ) }) as Story // @ts-ignore export const ModalListStory = Template.bind({}) export default { title: 'components/account_list_new', component: ModalWalletConnect, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/modal/index.ts ================================================ export * from './QRCode' export * from './WalletConnect' export * from './ModalPanelBase' export * from './ModalPanels' export * from './WalletPanels' export * from './RedPacketPanels' export * from './setting' export * from './Vault' export * from './ClosureAnnouncementModal' export type ModalBasicProps = { open: boolean onClose: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] onBack?: () => void isLayer2Only?: boolean step: number noClose?: boolean style?: any //{w,h} onQRClick?: () => void etherscanBaseUrl: string panelList: Array<{ view: JSX.Element onBack?: undefined | (() => void) onClose?: undefined | (() => void) height?: any width?: any }> isWebEarn?: boolean } export type ModalWalletConnectProps = ModalBasicProps export type ModalAccountProps = ModalWalletConnectProps export type ModalGuardianProps = ModalWalletConnectProps export type ModalRedPacketProps = ModalBasicProps ================================================ FILE: packages/component-lib/src/components/modal/setting/Interface.ts ================================================ export type ModalSettingFeeProps = { open: boolean onClose: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] noClose?: boolean style?: any } ================================================ FILE: packages/component-lib/src/components/modal/setting/SettingFee.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { Button, CoinIcon, ModalCloseButton, ModalSettingFeeProps, useOpenModals, useSettings, } from '../../../index' import { Box, Link, ListItemIcon, ListItemText, Modal, Typography } from '@mui/material' import { SwitchPanelStyled } from '../../styled' import { DragListIcon, FeeChargeOrderDefaultMap, L1L2_NAME_DEFINED, MapChainId, myLog, } from '@loopring-web/common-resources' import { DragDropContext, Draggable, Droppable, DropResult, ResponderProvided, } from 'react-beautiful-dnd' import React from 'react' import { useTheme } from '@emotion/react' export const PanelStyled = {} export const ModalSettingFee = withTranslation('common', { withRef: true })( ({ t, open, onClose, style, ...rest }: ModalSettingFeeProps & WithTranslation) => { const theme = useTheme() const { feeChargeOrder, setFeeChargeOrder, defaultNetwork } = useSettings() const { setShowFeeSetting } = useOpenModals() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [feeChargeValue, setFeeChargeValue] = React.useState( feeChargeOrder ?? FeeChargeOrderDefaultMap.get(defaultNetwork)!, ) const onDragEnd = React.useCallback((dropResult: DropResult, provider: ResponderProvided) => { myLog('draggableDone', dropResult, provider) if (dropResult.destination) { const { index: startIndex } = dropResult.source const { index: endIndex } = dropResult.destination setFeeChargeValue((state) => { const result = [].slice.call(state) const [removed] = result.splice(startIndex, 1) result.splice(endIndex, 0, removed) return result }) // const result = [].concat(feeChargeValue); // const [removed] = result.splice(startIndex, 1); // result.splice(endIndex, 0, removed); } // result: DropResult, provided: ResponderProvided }, []) const getItemStyle = (isDragging: any, draggableStyle: any, index: number) => { return { userSelect: 'none', background: isDragging ? 'var(--color-box-hover)' : '', ...draggableStyle, position: draggableStyle.position === 'fixed' ? 'absolute' : '', //--list-coin-item = 44 + margin = 16 top: draggableStyle.position === 'fixed' ? `calc((var(--list-coin-item) + ${theme.unit * 2}px) * ${index})` : '', left: 0, } } return ( {t('labelSettingChargeFeeOrder')} {t('labelDesSettingChargeFeeOrder', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {(provided) => ( {feeChargeValue.map((item, index) => ( {(draggableProvided, draggableSnapshot) => ( {item} )} ))} {provided.placeholder} )} { setFeeChargeValue(FeeChargeOrderDefaultMap.get(defaultNetwork)!) }} > {t('labelReset')} ) }, ) ================================================ FILE: packages/component-lib/src/components/modal/setting/index.ts ================================================ export * from './SettingFee' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/modal/walletList.stories.tsx ================================================ import styled from '@emotion/styled' import React from 'react' import { Meta, Story } from '@storybook/react' import { withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import { Box, Button, Grid } from '@mui/material' import { AccountFull, AccountStatus, gatewayList, SagaStatus } from '@loopring-web/common-resources' import { ModalWalletConnect, ProviderMenu, WalletConnectQRCode, WalletConnectStep, } from './WalletConnect' import { ModalQRCode, QRCodePanel } from './QRCode' import { account } from '../../static' import { WalletConnectBtn } from '../header' import { CommonConnectInProgress, ConnectFailed, ConnectReject, ConnectSuccess, WalletConnectConnectInProgress, } from '../index' const Style = styled.div` flex: 1; height: 100%; ` const accountState: AccountFull = { account: { ...account, _chainId: 1, }, status: SagaStatus.DONE, resetAccount: () => undefined, updateAccount: () => undefined, } const ConnectButtonWrap = withTranslation('common')((_rest: any) => { return ( <> undefined} NetWorkItems={<>} isShowOnUnConnect /> undefined} NetWorkItems={<>} /> undefined} NetWorkItems={<>} /> undefined} NetWorkItems={<>} /> undefined} NetWorkItems={<>} /> undefined} NetWorkItems={<>} /> undefined} NetWorkItems={<>} /> undefined} NetWorkItems={<>} /> ) }) const Template: Story = withTranslation()(({ ...rest }: any) => { const [openWallet, setOpenWallet] = React.useState(false) const [openQRCode, setOpenQRCode] = React.useState(false) gatewayList[0] = { ...gatewayList[0], handleSelect: () => console.log('metaMask 11'), } const url: string = 'xxxxxx' const walletList = React.useMemo(() => { return Object.values({ [WalletConnectStep.Provider]: { view: , }, [WalletConnectStep.CommonProcessing]: { view: , }, [WalletConnectStep.WalletConnectProcessing]: { view: , }, [WalletConnectStep.WalletConnectQRCode]: { view: , }, [WalletConnectStep.SuccessConnect]: { view: , }, [WalletConnectStep.FailedConnect]: { view: {}} />, }, [WalletConnectStep.RejectConnect]: { view: {}} />, }, }) }, [rest]) // const accountList = React.useMemo(() => { // return Object.values({ // [ AccountStep.NoAccount ]: { // } // }}/>, // [ AccountStep.Deposit ]: , // [ AccountStep.Depositing ]: , // [ AccountStep.FailedDeposit ]: undefined} // etherscanLink={accountInfoProps.etherscanUrl}/>, // [ AccountStep.SignAccount ]: undefined}/>, // [ AccountStep.ProcessUnlock ]: , // [ AccountStep.SuccessUnlock ]: , // [ AccountStep.FailedUnlock ]: undefined}/>, // [ AccountStep.HadAccount ]: , // [ AccountStep.TokenAccessProcess ]: , // [ AccountStep.DepositApproveProcess ]: , // [ AccountStep.ActiveAccountProcess ]: , // [ AccountStep.FailedTokenAccess ]: , // }) // // }, []) return ( <> ) }) as Story // @ts-ignore export const ModalListStory = Template.bind({}) export default { title: 'components/wallet_list', component: ModalWalletConnect, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/panel/FilterMarketsPanel.tsx ================================================ import React from 'react' import { Box, Tab, Tabs, Typography } from '@mui/material' import { WithTranslation, withTranslation } from 'react-i18next' import SwipeableViews from 'react-swipeable-views' import { useTheme } from '@emotion/react' interface TabPanelProps { children?: React.ReactNode index: any value: any } function TabPanel(props: TabPanelProps) { const { children, value, index, ...other } = props return ( ) } export const FilterMarketPanel = withTranslation('common')(({ t }: WithTranslation) => { const [value, setValue] = React.useState(1) const theme = useTheme() const handleChange = (_event: any, newValue: any) => { setValue(newValue) } const handleChangeIndex = (index: number) => { setValue(index) } return ( <> ) }) ================================================ FILE: packages/component-lib/src/components/panel/Panel.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { Box, Grid } from '@mui/material' import { FilterMarketPanel } from './FilterMarketsPanel' const Style = styled.div` background: var(--color-global-bg); height: 100%; flex: 1; ` const FilterMarketPanelWrap = () => { return } const Template: Story = () => { return ( ) } export default { title: 'components/Panel', component: FilterMarketPanel, argTypes: {}, } as Meta export const PanelsStory = Template.bind({}) ================================================ FILE: packages/component-lib/src/components/panel/index.ts ================================================ export * from './FilterMarketsPanel' ================================================ FILE: packages/component-lib/src/components/provider.tsx ================================================ import type { Provider as TProvider } from 'react' export const provider = (Provider: TProvider, props: any = {}) => [Provider, props] /** * @param providers inner -> outer * @param children * @constructor * example * * * */ export const ProviderComposer = ({ providers, children, }: { providers: Array<[TProvider, any]> children: any }) => { // @ts-ignore return providers.reduce((children, [Provider, props]: [TProvider, any]) => { // @ts-ignore return {children} }, children) // return children; } ================================================ FILE: packages/component-lib/src/components/share/Interface.ts ================================================ import { SOCIAL_NAME_KEYS } from '@loopring-web/common-resources'; export type SocialButtonProps = { /** Social Network Enum */ socialEnum: SOCIAL_NAME_KEYS size?: number sendShareEvent: (key: SOCIAL_NAME_KEYS) => void } export type ShareProps = { social: { [key in Partial]: SocialButtonProps } size: number loading: boolean imageUrl: string direction?: 'row' | 'column' } ================================================ FILE: packages/component-lib/src/components/share/components/SocialButton.tsx ================================================ import { SOCIAL_COMPONENT_MAP, // SOCIAL_NAME_KEYS, // SOCIAL_WITH_TITLE, } from '@loopring-web/common-resources' import { SocialButtonProps } from '../Interface'; import { Avatar } from '@mui/material'; // // function getExtraSocialProps({ // message, // socialEnum, // }: { // message: string // socialEnum: SOCIAL_NAME_KEYS // }) { // if (!message) return {} // // if (SOCIAL_WITH_TITLE.has(socialEnum)) { // return { title: message } // } // // if (socialEnum === SOCIAL_NAME_KEYS.Facebook) { // return { quote: message } // } // // return { body: message } // } export const SocialButton = ({size, socialEnum, sendShareEvent}: SocialButtonProps) => { const {SocialNetworkName, SocialIcon} = SOCIAL_COMPONENT_MAP[ socialEnum ] return ( sendShareEvent(socialEnum)}> ) } ================================================ FILE: packages/component-lib/src/components/share/index.tsx ================================================ import styled from '@emotion/styled' import { ShareProps } from './Interface' import { Box, IconButton, Modal, Typography } from '@mui/material' import { DownloadIcon, ExchangeIO, shareDownload, shareOnFacebook, shareOnTwitter, shareViaEmail, SOCIAL_NAME_KEYS, SoursURL, } from '@loopring-web/common-resources' import { SocialButton } from './components/SocialButton' import { WithTranslation, withTranslation } from 'react-i18next' import { ModalCloseButton } from '../basic-lib' import { Carousel, CarouselItem } from '../carousel' import React from 'react' const StyleBox = styled(Box)` .shareContainer { } .shareLoader { } .shareLabel { } .shareButtons { } .shareSocialButton { background-color: initial; cursor: pointer; } .shareSocialIcon { } ` export const Share = ({ social, loading, size, direction = 'row' }: ShareProps) => { return ( {loading ? ( {'loading'} ) : ( {Object.keys(social).map( (socialNetwork, index) => social[socialNetwork] && ( ), )} )} ) } const BoxStyle = styled(Box)` background: var(--color-global-bg); & { background: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; //border: 1px solid var(--color-box); //cursor: pointer; } .btn-close { svg { height: var(--btn-icon-size); width: var(--btn-icon-size); } margin-top: 0; //transform: translateY(-50%) translateX(-50%); //left: 50%; top: ${({ theme }) => 2 * theme.unit}px; right: ${({ theme }) => 1 * theme.unit}px; } .shareSocialButton { margin: 0 ${({ theme }) => (1 / 2) * theme.unit}px; } .btn { margin: 0 ${({ theme }) => (1 / 2) * theme.unit}px; background: var(--color-primary); &:hover { background: var(--color-primary); } } ` export const ShareModal = withTranslation('common')( ({ t, open, onClose, loading, imageList, className = '', onClick, message = '', ipfsProvides, link = ExchangeIO, ...rest }: WithTranslation & { open: boolean onClose: (event: any) => void onClick: (event: any) => void className?: string loading: boolean imageList: CarouselItem[] message: string ipfsProvides: any link }) => { const [selected, setSelected] = React.useState(0) const sendShareEvent = (SOCIAL_NAME: SOCIAL_NAME_KEYS | 'download') => { if (imageList[selected]) { switch (SOCIAL_NAME) { case SOCIAL_NAME_KEYS.Facebook: shareOnFacebook(message, imageList[selected].imageUrl, ipfsProvides.ipfs, link) break case SOCIAL_NAME_KEYS.Twitter: shareOnTwitter(message, imageList[selected].imageUrl, ipfsProvides.ipfs, link) break case SOCIAL_NAME_KEYS.Email: shareViaEmail( message, imageList[selected].imageUrl, ipfsProvides.ipfs, imageList[selected].width, imageList[selected].height, ) break case 'download': shareDownload( imageList[selected].name ?? 'LoopringReferral.png', imageList[selected].imageUrl, ) break } } } return ( {t('labelShareReferralCode')} { sendShareEvent('download') }} > ) }, ) ================================================ FILE: packages/component-lib/src/components/styled.ts ================================================ import styled from '@emotion/styled' import { Box, BoxProps, Grid, Typography } from '@mui/material' import { css, Theme, useTheme } from '@emotion/react' import { UpColor } from '@loopring-web/common-resources' import { Button, ButtonProps } from './basic-lib' import { useSettings } from '../stores' // @ts-ignore export const boxLiner = (_props: { theme: Theme }) => css` background: var(--color-box-linear); border: 0.5px solid var(--color-border); border-radius: ${_props.theme.unit}px; textarea, .coinInput-wrap, .btnInput-wrap, .MuiOutlinedInput-root { background: var(--field-opacity); border-color: var(--opacity); :hover { border-color: var(--color-border-hover); } } .MuiToolbar-root .MuiButtonBase-root.outlined { background-color: var(--field-opacity); } ` export const TypographyStrong = styled(Typography)` color: var(--color-secoundary); ` as typeof Typography export const TypographyGood = styled(Typography)` color: var(--color-success); ` as typeof Typography export const TablePaddingX = (_props: { pLeft: number; pRight: number }) => { const { unit } = useTheme() const { isMobile } = useSettings() return css` .rdg-row, .rdg-header-row { .rdg-cell:first-of-type { padding-left: ${unit * (isMobile ? 1 : 3)}px; } .rdg-cell:last-of-type { padding-right: ${unit * (isMobile ? 1 : 3)}px; } } ` } export const VipStyled = styled(Typography)` margin-left: ${({ theme }) => theme.unit}px; padding: ${({ theme }) => theme.unit / 4}px ${({ theme }) => theme.unit}px; ${({ theme }) => theme.border.defaultFrame({ c_key: 'rgba(0,0,0,0)', d_R: 0.25 })}; background-color: var(--vip-bg); height: 2rem; color: var(--vip-text); ` as typeof Typography export const floatTag = ({ theme, custom }: any) => css` .float-group { font-weight: lighter; .float-tag.float-increase { color: ${custom.chg === UpColor.green ? theme.colorBase.success : theme.colorBase.error}; } .float-tag.float-decrease { color: ${custom.chg === UpColor.green ? theme.colorBase.error : theme.colorBase.success}; } .float-tag.float-none { color: ${theme.colorBase.textPrimary}; } } ` export const AvatarIconPair = ({ theme }: any) => css` .icon-next { margin-left: -${theme.unit}px; } ` export const baseTitleCss = ({ theme, custom }: any) => css` height: 72px; ${AvatarIconPair({ theme })} h3.MuiTypography-root { font-size: 1.6rem; margin-left: ${theme.unit}px; color: ${theme.colorBase.textSecondary}; .MuiTypography-root { margin: 0 ${theme.unit / 4}px; } .next-coin { color: ${theme.colorBase.textPrimary}; } } ${floatTag({ theme, custom })}; .float-chart { margin-left: ${theme.unit}px; .chart-change { color: ${theme.colorBase.textSecondary}; } } ` export const ButtonListRightStyled = styled(Grid)` .MuiButton-root:not(:last-child) { margin-right: ${({ theme }) => theme.unit}px; } ` as typeof Grid export const modalContentBaseStyle = ({ theme }: any) => css` &:focus-visible { outline: 0; } display: flex; flex-direction: column; justify-content: space-between; align-items: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); padding-top: var(--toolbar-row-padding); border: 0; border-radius: ${theme.unit}px; ` export const ModelPanelStyle = styled(Box)` ${({ theme }) => modalContentBaseStyle({ theme: theme })}; background: var(--color-pop-bg); ` as typeof Box export const SwitchPanelStyled: any = styled(Box)< { _height?: number | string; _width?: number | string } & BoxProps >` .MuiModal-root & { .react-swipeable-view-container > div { background: var(--opacity); } .container { padding-bottom: 0; } } .trade-panel { .react-swipeable-view-container { & > div { padding: 0 ${({ theme }) => (theme.unit * 5) / 2}px ${({ theme }) => theme.unit * 5}px; .container { height: 100%; padding-top: 0; } } } } && { ${({ theme }) => modalContentBaseStyle({ theme: theme })} ${({ _height, _width, theme }) => ` background: ${theme.colorBase.box}; .react-swipeable-view-container { height: ${ _height && Number.isNaN(_height) ? _height + 'px' : _height ? _height : '100%' } ; width: ${_width && Number.isNaN(_width) ? _width + 'px' : _width ? _width : '100%'}; & > div{ height:initial; overflow-x: hidden; overflow-y: scroll !important; background: initial; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* Internet Explorer 10+ */ &::-webkit-scrollbar { /* WebKit */ width: 0; } } } .trade-panel{ .react-swipeable-view-container { & > div{ height: calc(100% - var(--toolbar-row-padding)); padding-bottom:0; overflow-x: hidden; overflow-y: scroll !important; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* Internet Explorer 10+ */ &::-webkit-scrollbar { /* WebKit */ width: 0; } } } } `} } &.collectionSelect { background: var(--color-global-bg); align-items: stretch; } .MuiModal-root & { .coin-menu { flex: 1; height: 100%; } } ` as (props: { _height?: number | string; _width?: number | string } & BoxProps) => JSX.Element // height:${ // _height // ? typeof _height === "number" // ? ` calc(${_height + "px"} - ${ // theme.unit * 4 // }px - 2 * var(--toolbar-row-padding) ) ` // : ` calc(${_height} - ${ // theme.unit * 4 // }px - 2 * var(--toolbar-row-padding) )` // : "410px" // } !important; export const toolBarPanel = ({ theme }: any) => css` .MuiToolbar-root { align-content: stretch; justify-content: flex-end; box-sizing: border-box; height: var(--toolbar-row-padding-minus); padding: 0 ${(theme.unit * 5) / 2}px; margin-top: var(--toolbar-row-padding-minus); .MuiIconButton-root { height: var(--btn-icon-size); width: var(--btn-icon-size); min-width: var(--btn-icon-size); margin: 0; display: flex; padding: 0; justify-content: center; justify-items: center; align-items: center; font-size: ${theme.fontDefault.h4}; } } ` export const TableFilterStyled = styled(Box)` margin: 0 ${({ theme }) => theme.unit * 3}px ${({ theme }) => theme.unit * 2}px; ` as typeof Box export const AnimationArrow = styled(Box)` &.arrowCta { transform-origin: center; display: block; height: 12px; width: 12px; border: 9px solid transparent; transform: rotate(45deg) scale(0.5); position: relative; margin: ${({ theme }) => theme.unit * 2}px; } &.arrowCta:after, &.arrowCta:before { content: ''; display: block; height: inherit; width: inherit; position: absolute; top: 0; left: 0; } &.arrowCta:after { border-bottom: 3px solid var(--color-text-primary); border-right: 3px solid var(--color-text-primary); top: 0; left: 0; opacity: 1; animation: bottom-arrow 1.65s infinite; } @keyframes bottom-arrow { 0% { opacity: 1; transform: translate(0, 0); } 45% { opacity: 0; transform: translate(12px, 12px); } 46% { opacity: 0; transform: translate(-16px, -16px); } 90% { opacity: 1; transform: translate(-6px, -6px); } 100% { opacity: 1; transform: translate(-6px, -6px); } } &.arrowCta:before { top: 0; left: 0; border-bottom: 3px solid var(--color-text-primary); border-right: 3px solid var(--color-text-primary); animation: top-arrow 1.65s infinite; } @keyframes top-arrow { 0% { transform: translate(-6px, -6px); } 35% { transform: translate(0, 0); } 90% { opacity: 1; transform: translate(0, 0); } 100% { opacity: 1; transform: translate(0, 0); } } ` as typeof Box export const shake = css` @keyframes shake { 10%, 90% { transform: translate3d(-1px, 0, 0); } 20%, 80% { transform: translate3d(2px, 0, 0); } 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } 40%, 60% { transform: translate3d(4px, 0, 0); } } ` export const MenuBtnStyled = styled(Button)` font-size: ${({ theme }) => theme.fontDefault.body1}; background: var(--opacity); color: var(--color-text-secondary); display: flex; padding: 0 ${({ theme }) => theme.unit * 3}px; text-indent: 0.5em; position: relative; &.addAsset, &.sendAsset { white-space: pre; font-size: ${({ theme }) => theme.fontDefault.h5}; justify-content: space-between; flex-direction: row; height: 64px; &.isMobile { font-size: 14px; height: 48px; } } &.banxaEnter { justify-content: space-between; } &.vaultBtn { border: 0; font-size: ${({ theme }) => theme.fontDefault.h5}; color: var(--color-text-primary); justify-content: space-between; .MuiButton-endIcon { color: var(--color-text-Secondary); } &:hover { .MuiButton-endIcon { color: var(--color-primary); } } } &.redPacketType { display: flex; flex-direction: column; align-items: flex-start; height: ${({ theme }) => theme.unit * 18}px; text-indent: 0em; text-align: left; padding: ${({ theme }) => theme.unit * 1.5}px ${({ theme }) => theme.unit * 2}px; justify-content: flex-start; .mainTitlte { } } &.provider { justify-content: space-between; flex-direction: row; white-space: pre; color: var(--color-text-primary); height: var(--provider-btn-height); &:hover { border: 1px solid var(--color-border-select); } &.Mui-disabled { .MuiButton-endIcon { color: var(--color-text-disable); } } &.selected { border: 1px solid var(--color-border-select); &:after { display: none; } } &.MuiButton-root { ${ // @ts-ignore ({ loading, loadingbg, t }) => { return loading === 'true' ? ` pointer-events: none; ` : '' } } } .loading { font-size: ${({ theme }) => theme.fontDefault.body2}; } } &.vendor { justify-content: center; flex-direction: column; & > .vendorName { text-indent: -999em; justify-content: center; } } &:hover { background: var(--provider-hover); border-color: var(--color-border-select); color: var(--color-text-button-select); } &.selected { &.redPacketType { border: 1px solid var(--color-border-select); &:after { display: none; } } position: relative; background: var(--provider-hover); border-color: var(--opacity); color: var(--color-text-button-select); &:after { position: absolute; content: '\u25CF'; text-indent: 0em; color: var(--color-success); display: flex; left: 0; padding-left: ${({ theme }) => (theme.unit * 3) / 2}px; align-items: center; font-size: ${({ theme }) => theme.fontDefault.h5}; } } ` as (props: ButtonProps) => JSX.Element export const StyledPaperBg = styled(Box)` background: var(--color-box-third); border-radius: ${({ theme }) => theme.unit}px; ` as any export const MediaLabelStyled = styled(Box)` border-radius: 0 0 ${({ theme }) => theme.unit}px 0; padding: ${({ theme }) => theme.unit / 2}px ${({ theme }) => theme.unit}px; color: var(--color-box); font-size: 1.4rem; background: ${({ colorbg }) => (colorbg ? colorbg : 'var(--color-tag)')}; cursor: help; ` as (props: BoxProps & { colorbg?: string }) => JSX.Element export const BoxBannerStyle = styled(Box)< BoxProps & { backGroundUrl?: string | number; direction?: 'left' | 'right' } >` background-color: var(--color-box); .bg:after { display: block; content: ''; float: ${({ direction }) => direction}; width: 35%; background-repeat: no-repeat; background-position: center; background-size: contain; background-image: url('${({ backGroundUrl }) => backGroundUrl}'); } &.mobile .bg { position: relative; display: flex; flex-direction: column; &:after { opacity: 0.08; z-index: 1; position: absolute; top: 0; width: 100%; height: 100%; pointer-events: none; } } ` as ( props: BoxProps & { backGroundUrl?: string | number direction?: 'left' | 'right' }, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/tableList/QuoteTable/MarketDetail.tsx ================================================ import { ChartType, ScaleAreaChart } from '../../charts' import React from 'react' import { Box, Button, Grid, Link, Typography } from '@mui/material' import { AmmHistoryItem, CurrencyToTag, EmptyValueTag, ForexMap, getValuePrecisionThousand, PriceTag, SoursURL, SvgSize, TokenType, VaultSwapStep, } from '@loopring-web/common-resources' import { Trans, useTranslation } from 'react-i18next' import { CoinIcons } from '../assetsTable' import * as sdk from '@loopring-web/loopring-sdk' import { useSettings } from '../../../stores' import { QuoteTableChangedCell } from './QuoteTable' import { ToggleButtonGroup } from '../../basic-lib' import styled from '@emotion/styled' const BgButton = styled(Button)<{ customBg: string }>` background-color: ${({ customBg }) => customBg}; transition: all 0.2s ease-in-out; &:hover { background-color: ${({ customBg }) => customBg}; opacity: 0.8; } &:disabled { background-color: var(--color-button-disabled); } ` enum TradingInterval { hr1 = 'hr1', d1 = 'd1', w1 = 'w1', m1 = 'm1', } const TimeMarketIntervalData = [ { id: TradingInterval.hr1, i18nKey: 'labelProTime1h', }, { id: TradingInterval.d1, i18nKey: 'labelProTime1d', }, { id: TradingInterval.w1, i18nKey: 'labelProTime1w', }, { id: TradingInterval.m1, i18nKey: 'labelProTime1m', }, ] enum TimeMarketIntervalDataIndex { hr1, d1, w1, m1, } export const MarketDetail = ({ tokenInfo, trends, isShow, forexMap, etherscanBaseUrl, isLoading, timeIntervalData = TimeMarketIntervalData, showBtns, onClickBuy, onClickSell }: { tokenInfo isLoading?: boolean trends: any isShow: boolean forexMap: ForexMap etherscanBaseUrl: string timeIntervalData: typeof TimeMarketIntervalData showBtns?: boolean onClickBuy?: () => void onClickSell?: () => void }) => { const { t } = useTranslation() const { coinJson, currency, upColor } = useSettings() // const [data, setData] = React.useState([]) const [timeInterval, setTimeInterval] = React.useState(TradingInterval.hr1) const [trend, setTrend] = React.useState([]) const handleTimeIntervalChange = React.useCallback( (timeInterval: TradingInterval) => { setTimeInterval(timeInterval) if (trends?.length) { const _trends = trends[TimeMarketIntervalDataIndex[timeInterval]] setTrend(_trends.map(trend => { return { ...trend, change: sdk.toBig(trend.close).minus(_trends[_trends.length - 1].close).div(_trends[_trends.length - 1].close).multipliedBy(100).toFixed(2), } })) } }, [trends], ) const priceCall = React.useCallback( (price: any) => { const priceStr = sdk.toBig(price ?? 0).times(forexMap[currency]) return getValuePrecisionThousand(priceStr, 5, 4, 2, false, { isFait: true, floor: true }) }, [forexMap, currency], ) React.useEffect(() => { if (isShow && !isLoading) { handleTimeIntervalChange(TradingInterval.hr1) } }, [isShow, isLoading]) return tokenInfo?.symbol ? ( <> {/* eslint-disable-next-line react/jsx-no-undef */} {tokenInfo.type == TokenType.vault ? tokenInfo?.erc20Symbol : tokenInfo.symbol} {tokenInfo.name} {PriceTag[CurrencyToTag[currency]] + priceCall(tokenInfo.price)} {typeof tokenInfo.percentChange24H !== 'undefined' ? (sdk.toBig(tokenInfo.percentChange24H).gt(0) ? '+' : '') + getValuePrecisionThousand(tokenInfo.percentChange24H, 2, 2, 2, true) + '%' : EmptyValueTag} {!trend?.length ? ( {'loading'} ) : ( )} { handleTimeIntervalChange(value) }, data: timeIntervalData.map((item) => { return { value: item.id, key: item.i18nKey, } }), }} /> {t('labelStats')} {t('label24Volume')} {tokenInfo.volume24H ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand(tokenInfo.volume24H, 2, 2, 2, false, { isAbbreviate: false, abbreviate: 6, }) : EmptyValueTag} {t('label24PriceChange')} {typeof tokenInfo.percentChange24H !== 'undefined' ? (sdk.toBig(tokenInfo.percentChange24H).gt(0) ? '+' : '') + getValuePrecisionThousand(tokenInfo.percentChange24H, 2, 2, 2, true) + '%' : EmptyValueTag} {t('label7dPriceChange')} {typeof tokenInfo.percentChange7D !== 'undefined' ? (sdk.toBig(tokenInfo.percentChange7D).gt(0) ? '+' : '') + getValuePrecisionThousand(tokenInfo.percentChange7D, 2, 2, 2, true) + '%' : EmptyValueTag} {t('labelTokenInfo')} {t('labelMarketCap')} {tokenInfo.marketCap ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand(tokenInfo.marketCap, 2, 2, 2, false, { isAbbreviate: false, abbreviate: 6, }) : EmptyValueTag} {t('labelTokenSupply')} {tokenInfo.totalSupply !== 'undefined' ? getValuePrecisionThousand(tokenInfo.totalSupply, 2, 2, 2, false, { isAbbreviate: false, abbreviate: 6, }) : EmptyValueTag} {t('labelTokenContractAddress')} {tokenInfo.tokenAddress ? tokenInfo.tokenAddress : EmptyValueTag} {t('labelTokenWebsite')} {tokenInfo.website} {t('labelTokenIntroduce')} {tokenInfo.type == TokenType.vault ? ( {tokenInfo.symbol} is a token backed 1:1 with {tokenInfo.erc20Symbol}, bringing greater liquidity to Loopring DEX. ) : ( <> )} {showBtns && Buy / Long Sell / Short } ) : ( <> ) } ================================================ FILE: packages/component-lib/src/components/tableList/QuoteTable/MarketTable.tsx ================================================ import styled from '@emotion/styled' import { Box, IconButton, Typography } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { RouteComponentProps, withRouter } from 'react-router-dom' import { Account, CAMPAIGNTAGCONFIG, CurrencyToTag, EmptyValueTag, ForexMap, getValuePrecisionThousand, PriceTag, RowConfig, RowConfigType, SCENARIO, StarHollowIcon, StarSolidIcon, TickerNew, TokenType, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { Button, Column, Table } from '../../basic-lib' import { TablePaddingX } from '../../styled' import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' import { TagIconList } from '../../block' import { QuoteTableChangedCell } from './QuoteTable' import { CoinIcons } from '../assetsTable' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; --template-columns: ${({ ispro, isMobile }: any) => ispro || isMobile ? '35% 44% auto' : '30% 30% 30% 10%'} !important; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignLeft { text-align: left; .rdg-header-sort-cell { justify-content: flex-start; } } .textAlignCenter { text-align: center; } ` as any export interface MarketTableProps { rawData: R[] rowConfig: RowConfigType onItemClick?: (item: R) => void onRowClick?: (item: R) => void campaignTagConfig: CAMPAIGNTAGCONFIG hiddenFav?: boolean actionEle?: JSX.Element // headerRowHeight?: number onVisibleRowsChange?: (startIndex: number) => void account: Account favoriteMarket: string[] handleStartClick: (pair: string, index?: number) => void // addFavoriteMarket: (pair: string) => void // removeFavoriteMarket: (pair: string) => void currentheight?: number showLoading?: boolean isPro?: boolean forexMap: ForexMap } export const MarketTable = withTranslation('tables')( withRouter( ({ t, currentheight = 350, rowConfig = RowConfig, onVisibleRowsChange, campaignTagConfig, rawData, history, onRowClick, favoriteMarket, handleStartClick, hiddenFav = false, actionEle = (row) => { return ( ) }, // addFavoriteMarket, // removeFavoriteMarket, showLoading, account, forexMap, isPro = false, onItemClick, ...rest }: MarketTableProps & WithTranslation & RouteComponentProps) => { const { currency, isMobile, coinJson, upColor } = useSettings() // const handleStartClick = ( // event: React.MouseEvent, // isFavourite: boolean, // pair: string, // ): void => { // event.stopPropagation() // if (isFavourite) { // dispatch(removeFavoriteMarket(pair)) // } else { // dispatch(addFavoriteMarket(pair)) // } // } const priceCall = React.useCallback( (price: any) => { const priceStr = sdk.toBig(price ?? 0).times(forexMap[currency]) return getValuePrecisionThousand(priceStr, 5, 4, 2, false, { isFait: true, floor: true, }) }, [forexMap, currency], ) const getColumnMode = React.useCallback((): Column[] => { const basicRender = [ { key: 'pair', name: t('labelTxToken'), sortable: true, formatter: ({ row, rowIdx }: any) => { // const isFavourite = favoriteMarket?.includes(row.symbol) const symbol = row?.type === TokenType.vault ? row.erc20Symbol : row.symbol let tokenIcon: [any, any] = [coinJson[symbol], undefined] return ( {!hiddenFav ? ( { e.stopPropagation() handleStartClick(row.symbol, rowIdx) }} > {row.isFavorite ? ( ) : ( )} ) : ( <> )} {symbol}   {campaignTagConfig && ( )} ) }, }, { key: 'price', name: t('labelQuotaLastPrice'), headerCellClass: 'textAlignLeft', cellClass: 'textAlignLeft', sortable: true, formatter: ({ row }: any) => { return ( {PriceTag[CurrencyToTag[currency]] + priceCall(row.price)} ) }, }, { key: 'change', name: t(isMobile ? 'labelQuota24hChangeLit' : 'labelQuota24hChange'), sortable: true, headerCellClass: isMobile ? 'textAlignRight' : 'textAlignLeft', cellClass: isMobile ? 'textAlignRight' : 'textAlignLeft', formatter: ({ row }: any) => { const value = row.percentChange24H return (
    {typeof value !== 'undefined' ? (sdk.toBig(value).gt(0) ? '+' : '') + getValuePrecisionThousand(value, 2, 2, 2, true) + '%' : EmptyValueTag}
    ) }, }, ] const extraRender = [ { key: 'volume', name: t('labelQuota24hAmount'), headerCellClass: 'textAlignRight', // resizable: true, sortable: true, formatter: ({ row }: any) => { return (
    {row.volume24H ? getValuePrecisionThousand( row.volume24H, row.precision, row.precision, row.precision, false, { isAbbreviate: true, abbreviate: 6 }, ) : EmptyValueTag}
    ) }, }, ] // const isMobile = []; if (isMobile) { return [...basicRender] } if (isPro) { return [...basicRender] } return [...basicRender, ...extraRender] }, [ campaignTagConfig, currency, favoriteMarket, forexMap, handleStartClick, history, isMobile, isPro, t, upColor, ]) const defaultArgs: any = { rawData: [], columnMode: getColumnMode(), generateRows: (rawData: any) => rawData, onRowClick: onRowClick, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], sortMethod: (sortedRows, sortColumn) => { switch (sortColumn) { case 'pair': sortedRows = sortedRows.sort((a, b) => { return a.symbol?.localeCompare(b.symbol) }) break case 'price': sortedRows = sortedRows.sort((a, b) => { const [valueA, valueB] = [a.price, b.price] if (a.price && b.price) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break case 'change': sortedRows = sortedRows.sort((a, b) => { const [valueA, valueB] = [a.percentChange24H, b.percentChange24H] if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break case 'volume': sortedRows = sortedRows.sort((a, b) => { // const valueA = a['volume24H'] // const valueB = b['volume'] const [valueA, valueB] = [a.volume24H, b.volume24H] if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break default: return sortedRows } return sortedRows }, sortDefaultKey: 'change', } return ( ) }, ), ) ================================================ FILE: packages/component-lib/src/components/tableList/QuoteTable/QuoteTable.tsx ================================================ import styled from '@emotion/styled' import { Box, Button, IconButton, Typography } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { RouteComponentProps, withRouter } from 'react-router-dom' import { Account, CAMPAIGNTAGCONFIG, CurrencyToTag, EmptyValueTag, FloatTag, ForexMap, getValuePrecisionThousand, PriceTag, RowConfig, SCENARIO, StarHollowIcon, StarSolidIcon, Ticker, } from '@loopring-web/common-resources' import { Column, Table } from '../../basic-lib' import { TablePaddingX } from '../../styled' import { useSettings } from '@loopring-web/component-lib/src/stores' import { useDispatch } from 'react-redux' import { Currency } from '@loopring-web/loopring-sdk' import React from 'react' import { TagIconList } from '../../block' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; --template-columns: ${({ ispro, isMobile }: any) => ispro || isMobile ? '35% 44% auto' : '240px 220px 100px auto auto auto 132px'} !important; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export type QuoteTableRawDataItem = Ticker & { pair: { coinA: string coinB: string } floatTag: keyof typeof FloatTag coinApriceU: number precision?: number reward?: number rewardToken?: string timeUnit?: '24h' } export const QuoteTableChangedCell: any = styled.span` color: ${({ theme: { colorBase }, upColor, value }: any) => { const isUpColorGreen = upColor === 'green' return value > 0 ? isUpColorGreen ? colorBase.success : colorBase.error : value < 0 ? isUpColorGreen ? colorBase.error : colorBase.success : colorBase.textPrimary }}; ` export interface QuoteTableProps { rawData: QuoteTableRawDataItem[] rowHeight?: number campaignTagConfig: CAMPAIGNTAGCONFIG headerRowHeight?: number onVisibleRowsChange?: (startIndex: number) => void onRowClick?: (row: QuoteTableRawDataItem, column: any) => void account: Account favoriteMarket: string[] addFavoriteMarket: (pair: string) => void removeFavoriteMarket: (pair: string) => void currentheight?: number showLoading?: boolean isPro?: boolean forexMap: ForexMap } export const QuoteTable = withTranslation('tables')( withRouter( ({ t, currentheight = 350, rowHeight = RowConfig.rowHeight, headerRowHeight = RowConfig.rowHeaderHeight, onVisibleRowsChange, campaignTagConfig, rawData, history, onRowClick, favoriteMarket, addFavoriteMarket, removeFavoriteMarket, showLoading, account, forexMap, isPro = false, ...rest }: QuoteTableProps & WithTranslation & RouteComponentProps) => { let userSettings = useSettings() const upColor = userSettings?.upColor const { currency, isMobile } = userSettings const handleStartClick = ( event: React.MouseEvent, isFavourite: boolean, pair: string, ): void => { event.stopPropagation() if (isFavourite) { dispatch(removeFavoriteMarket(pair)) } else { dispatch(addFavoriteMarket(pair)) } } const getColumnMode = React.useCallback((): Column[] => { const basicRender = [ { key: 'pair', name: t('labelQuotaPair'), sortable: true, formatter: ({ row }: any) => { const { coinA, coinB } = row['pair'] const pair = `${coinA}-${coinB}` const isFavourite = favoriteMarket?.includes(pair) return ( handleStartClick(e, isFavourite, pair)} > {isFavourite ? ( ) : ( )} {coinA} /{coinB}   {campaignTagConfig && ( )} ) }, }, { key: 'close', name: t('labelQuotaLastPrice'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', sortable: true, formatter: ({ row }: any) => { const value = row.close const precision = row['precision'] || 6 const price = Number.isFinite(value) ? getValuePrecisionThousand(value, undefined, undefined, precision, true, { isPrice: true, }) : EmptyValueTag const faitPrice = Number.isFinite(value) ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( row.coinApriceU * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, }, ) : EmptyValueTag return ( {price} {'/'} {faitPrice} ) }, }, { key: 'change', name: t(isMobile ? 'labelQuota24hChangeLit' : 'labelQuota24hChange'), sortable: true, headerCellClass: 'textAlignCenter', formatter: ({ row }: any) => { const value = row.change return (
    {typeof value !== 'undefined' ? (row.floatTag === FloatTag.increase ? '+' : '') + getValuePrecisionThousand(value, 2, 2, 2, true) + '%' : EmptyValueTag}
    ) }, }, ] const extraRender = [ { key: 'high', name: t('labelQuota24hHigh'), headerCellClass: 'textAlignRight', formatter: ({ row, column }: any) => { const value = row[column.key] const precision = row.precision || 6 const price = Number.isFinite(value) ? getValuePrecisionThousand(value, undefined, undefined, precision, true, { isPrice: true, }) : EmptyValueTag return (
    {price}
    ) }, }, { key: 'low', name: t('labelQuota24hLow'), headerCellClass: 'textAlignRight', formatter: ({ row, column }: any) => { const value = row[column.key] const precision = row.precision || 6 const price = Number.isFinite(value) ? getValuePrecisionThousand(value, undefined, undefined, precision, true, { isPrice: true, }) : EmptyValueTag return (
    {price}
    ) }, }, { key: 'volume', name: t('labelQuota24hAmount'), headerCellClass: 'textAlignRight', // resizable: true, sortable: true, formatter: ({ row }: any) => { const value = row.volume const precision = row.volume || 6 const price = value && value !== '0' ? getValuePrecisionThousand(value, precision, undefined, undefined, true, { isTrade: true, }) : EmptyValueTag return (
    {price}
    ) }, }, { key: 'actions', headerCellClass: 'textAlignCenter', name: t('labelQuoteAction'), formatter: ({ row }: any) => { const { coinA, coinB } = row['pair'] const tradePair = `${coinA}-${coinB}` return (
    ) }, }, ] // const isMobile = []; if (isMobile) { return [...basicRender] } if (isPro) { return [...basicRender] } return [...basicRender, ...extraRender] }, [ campaignTagConfig, currency, favoriteMarket, forexMap, handleStartClick, history, isMobile, isPro, t, upColor, ]) const dispatch = useDispatch() const defaultArgs: any = { rawData: [], columnMode: getColumnMode(), generateRows: (rawData: any) => rawData, onRowClick: onRowClick, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], sortMethod: (sortedRows: QuoteTableRawDataItem[], sortColumn: string) => { switch (sortColumn) { case 'pair': sortedRows = sortedRows.sort((a, b) => { const valueA = a.pair.coinA const valueB = b.pair.coinA return valueB.localeCompare(valueA) }) break case 'close': sortedRows = sortedRows.sort((a, b) => { const valueA = a['close'] const valueB = b['close'] if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break case 'change': sortedRows = sortedRows.sort((a, b) => { const valueA = a['change'] const valueB = b['change'] if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break case 'high': sortedRows = sortedRows.sort((a, b) => { const valueA = a['high'] const valueB = b['high'] if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break case 'low': sortedRows = sortedRows.sort((a, b) => { const valueA = a['low'] const valueB = b['low'] if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break case 'volume': sortedRows = sortedRows.sort((a, b) => { const valueA = a['volume'] const valueB = b['volume'] if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break default: return sortedRows } return sortedRows }, sortDefaultKey: 'change', } return ( ) }, ), ) ================================================ FILE: packages/component-lib/src/components/tableList/QuoteTable/index.ts ================================================ export * from './MarketTable' export * from './QuoteTable' export * from './MarketDetail' ================================================ FILE: packages/component-lib/src/components/tableList/ammRecordTable/AmmRecordTable.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { AmmRecordRow as Row, AmmRecordTable, AmmTradeType } from './index' import { coinMap } from '../../../static' import { CoinInfo } from '@loopring-web/common-resources' import moment from 'moment' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` const rawData: Row[] = [ { type: AmmTradeType.add, coinA: coinMap['ETH'] as CoinInfo, coinB: coinMap['LRC'] as CoinInfo, totalDollar: 12, amountA: 122, amountB: 231, time: moment().add(-1, 'days').toDate().getTime(), }, { type: AmmTradeType.remove, coinA: coinMap['ETH'] as CoinInfo, coinB: coinMap['LRC'] as CoinInfo, totalDollar: 12, amountA: 122, amountB: 231, time: moment().add(-100, 'days').toDate().getTime(), }, { type: AmmTradeType.swap, coinA: coinMap['ETH'] as CoinInfo, coinB: coinMap['LRC'] as CoinInfo, totalDollar: 12, amountA: 122, amountB: 231, time: moment().add(-15, 'days').toDate().getTime(), }, { type: AmmTradeType.swap, coinA: coinMap['ETH'] as CoinInfo, coinB: coinMap['LRC'] as CoinInfo, totalDollar: 12, amountA: 122, amountB: 231, time: moment().add(-3, 'hours').toDate().getTime(), }, { type: AmmTradeType.swap, coinA: coinMap['ETH'] as CoinInfo, coinB: coinMap['LRC'] as CoinInfo, totalDollar: 12, amountA: 122, amountB: 231, time: moment().add(-75, 'second').toDate().getTime(), }, ] export const Template: Story = (args: any) => { return ( <> ) } Template.bind({}) Template.args = { rawData: rawData, pagination: { pageSize: 5, }, } export default { title: 'components/TableList/AmmRecordTable', component: AmmRecordTable, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/ammRecordTable/AmmRecordTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Typography } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import moment from 'moment' import { Column, Table, TablePagination, TableProps } from '../../basic-lib' import { CurrencyToTag, EmptyValueTag, ForexMap, getValuePrecisionThousand, globalSetup, PriceTag, RowConfig, } from '@loopring-web/common-resources' import { AmmRecordRow as Row, AmmRecordTableProps, AmmTradeType } from './Interface' import { FormatterProps } from 'react-data-grid' import styled from '@emotion/styled' import { TablePaddingX } from '../../styled' import { Currency } from '@loopring-web/loopring-sdk' import { useSettings } from '../../../stores' import { TFunction } from 'i18next' // height: ${(props: any) => { // if (props.currentheight && props.currentheight > 350) { // return props.currentheight + "px"; // } else { // return "100%"; // } // }}; const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 280px 240px auto auto !important;` : `--template-columns: 90% 10% !important;`} .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { currentheight?: number; isMobile?: boolean } & BoxProps) => JSX.Element const columnMode = ( { t }: { t: TFunction }, currency: Currency, forexMap: ForexMap, ): Column, unknown>[] => [ { key: 'style', sortable: false, width: 'auto', minWidth: 240, name: t('labelAmmTableType'), formatter: ({ row }: FormatterProps, unknown>) => { const { type, coinA, coinB, amountA, amountB } = row const isAdd = type === AmmTradeType.add const side = isAdd ? t('labelAmmJoin') : t('labelAmmExit') return ( {side}    {`${getValuePrecisionThousand(amountA, undefined, undefined, undefined, false, { isTrade: true, })} ${coinA.simpleName}`}   +   {`${getValuePrecisionThousand(amountB, undefined, undefined, undefined, false, { isTrade: true, })} ${coinB.simpleName}`} ) }, }, { key: 'totalValue', sortable: false, width: 'auto', headerCellClass: 'textAlignCenter', cellClass: 'textAlignCenter', name: t('labelAmmTotalValue'), formatter: ({ row }: FormatterProps, unknown>) => { const { totalDollar } = row return ( {typeof totalDollar === 'undefined' ? EmptyValueTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (totalDollar || 0) * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true }, )} ) }, }, { key: 'time', sortable: false, width: 'auto', headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', name: t('labelAmmTime'), formatter: ({ row }: FormatterProps, unknown>) => { const { time } = row let timeString if (typeof time === 'undefined') { timeString = EmptyValueTag } else { timeString = moment(new Date(time), 'YYYYMMDDHHMM').fromNow() } return ( {timeString} ) }, }, ] const columnModeMobile = ( { t }: { t: TFunction }, currency: Currency, forexMap: ForexMap, ): Column, unknown>[] => [ { key: 'style', sortable: false, width: 'auto', minWidth: 240, name: t('labelAmmTableType'), formatter: ({ row }: FormatterProps, unknown>) => { const { type, coinA, coinB, amountA, amountB } = row const isAdd = type === AmmTradeType.add const side = isAdd ? t('labelAmmJoin') : t('labelAmmExit') return ( {side}    {`${getValuePrecisionThousand(amountA, undefined, undefined, undefined, false, { isTrade: true, })} ${coinA.simpleName}`}   +   {`${getValuePrecisionThousand(amountB, undefined, undefined, undefined, false, { isTrade: true, })} ${coinB.simpleName}`} ) }, }, { key: 'totalValue', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelAmmTotalValue') + '/' + t('labelAmmTime'), formatter: ({ row }: FormatterProps, unknown>) => { const { totalDollar } = row const time = moment(new Date(row.time), 'YYYYMMDDHHMM').fromNow() return ( {typeof totalDollar === 'undefined' ? EmptyValueTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (totalDollar || 0) * (forexMap[currency] ?? 0), undefined, undefined, undefined, true, { isFait: true }, )} {time} ) }, }, ] export const AmmRecordTable = withTranslation('tables')( ({ t, i18n, tReady, handlePageChange, pagination, currentheight, rowHeight = RowConfig.rowHeight, headerRowHeight = RowConfig.rowHeaderHeight, showFilter = true, rawData, scroll = false, wait = globalSetup.wait, currency = Currency.usd, forexMap, ...rest }: AmmRecordTableProps & WithTranslation) => { const [page, setPage] = React.useState(1) const { isMobile } = useSettings() const defaultArgs: TableProps = { rawData, columnMode: isMobile ? columnModeMobile({ t }, currency, forexMap) : columnMode({ t }, currency, forexMap), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }) => columnsRaw as Column, unknown>[], } const pageSize = pagination ? pagination.pageSize : 10 const _handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) // updateData({actionType: ActionType.page, currPage: page}) if (handlePageChange) { handlePageChange({ limit: pageSize, offset: (currPage - 1) * pageSize, }) } }, [handlePageChange, page, pageSize], ) const height = (currentheight || 0) + (!!rawData.length ? 0 : RowConfig.rowHeaderHeight) return ( {pagination && !!rawData.length && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/ammRecordTable/Interface.ts ================================================ import { CoinInfo, ForexMap } from '@loopring-web/common-resources' import { Currency } from '@loopring-web/loopring-sdk' export enum AmmTradeType { add = 'add', swap = 'swap', remove = 'remove', } enum TxStatus { processing = 'processing', processed = 'processed', received = 'received', failed = 'failed', } export interface AmmRecordRow { totalDollar: number amountA: number amountB: number time: number type: keyof typeof AmmTradeType coinA: CoinInfo coinB: CoinInfo status?: keyof typeof TxStatus } export type AmmRecordTableProps> = { rawData: R[] pagination?: { pageSize: number total: number } scroll?: boolean page?: number handlePageChange?: (props: any) => void showFilter?: boolean wait?: number showloading?: boolean currentheight?: number rowHeight?: number headerRowHeight?: number currency?: Currency forexMap: ForexMap } // rowHeight={RowConfig.rowHeight} // headerRowHeight={RowConfig.headerRowHeight} // currentheight={tableHeight} ================================================ FILE: packages/component-lib/src/components/tableList/ammRecordTable/index.ts ================================================ export * from './AmmRecordTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/ammTable/AmmTable.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import { AmmTable, RawDataAmmItem } from './index' import { AmmSideTypes } from './interface' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` const rawData: RawDataAmmItem[] = [ { side: AmmSideTypes.Join, amount: { from: { key: 'LRC', value: '2333', }, to: { key: 'ETH', value: '1.05', }, }, lpTokenAmount: '1785.65', fee: { key: 'LRC', value: '2.55', }, time: 0, }, { side: AmmSideTypes.Exit, amount: { from: { key: 'LRC', value: '12333', }, to: { key: 'ETH', value: '1.25', }, }, lpTokenAmount: '1745.23', fee: { key: 'LRC', value: '21.55', }, time: 0, }, ] const Template: Story = withTranslation()((args: any) => { return ( <> ) }) as Story export const Amm = Template.bind({}) Amm.args = { rawData: rawData, pagination: { pageSize: 5, }, showFilter: true, } export default { title: 'components/TableList/Amm', component: AmmTable, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/ammTable/AmmTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Link, Typography } from '@mui/material' import styled from '@emotion/styled' import { TFunction, withTranslation, WithTranslation } from 'react-i18next' import moment from 'moment' import { Column, Table, TablePagination } from '../../basic-lib' import { TableFilterStyled, TablePaddingX } from '../../styled' import { Filter, FilterTradeTypes } from './components/Filter' import { getValuePrecisionThousand, globalSetup, myLog, UNIX_TIMESTAMP_FORMAT, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { AmmSideTypes } from './interface' import { Currency } from '@loopring-web/loopring-sdk' import { DateRange } from '@mui/lab' import _ from 'lodash' import { useLocation } from 'react-router-dom' export type RawDataAmmItem = { side: AmmSideTypes amount: { from: { key: string value?: string } to: { key: string value?: string } } lpTokenAmount?: string fee: { key: string value?: string } time: number } export type AmmTableProps = { getAmmpoolList: (props: any) => void rawData: RawDataAmmItem[] filterPairs: string[] showloading: boolean pagination?: { pageSize: number total: number } showFilter?: boolean } // enum TableType { // filter = 'filter', // page = 'page' // } const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 300px auto auto auto !important;` : `--template-columns: 78% 22% !important;`} .rdg-row .rdg-cell:first-of-type { display: flex; align-items: center; } .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element const StyledSideCell: any = styled(Typography)` color: ${(props: any) => { const { value, theme: { colorBase }, } = props return value === AmmSideTypes.Join ? colorBase.success : colorBase.error }}; ` const getColumnModeAssets = ( t: TFunction, _currency: Currency, ): Column[] => [ { key: 'side', name: t('labelAmmSide'), formatter: ({ row }) => { const tradeType = row['side'] === AmmSideTypes.Join ? t('labelAmmJoin') : t('labelAmmExit') const { from, to } = row['amount'] const renderFromValue = getValuePrecisionThousand( from.value, undefined, undefined, undefined, false, { isTrade: true }, ) const renderToValue = getValuePrecisionThousand( to.value, undefined, undefined, undefined, false, { isTrade: true }, ) return ( <> {tradeType} {`${renderFromValue} ${from.key} + ${renderToValue} ${to.key}`} ) }, }, { key: 'lpTokenAmount', name: t('labelAmmLpTokenAmount'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const amount = row['lpTokenAmount'] const renderValue = row['side'] === AmmSideTypes.Join ? `+${getValuePrecisionThousand(amount, undefined, undefined, undefined, false, { isTrade: true, })}` : `-${getValuePrecisionThousand(amount, undefined, undefined, undefined, false, { isTrade: true, })}` return {renderValue} }, }, { key: 'fee', name: t('labelTxNetworkFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { key, value } = row['fee'] return ( {`${getValuePrecisionThousand(value, undefined, undefined, undefined, false, { isTrade: true, floor: false, })} ${key}`} ) }, }, { key: 'time', name: t('labelAmmRecordTime'), headerCellClass: 'textAlignRight', // minWidth: 400, formatter: ({ row }) => { const time = moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() return {time} }, }, ] const getColumnModeMobileAssets = ( t: TFunction, _currency: Currency, ): Column[] => [ { key: 'side', name: ( {t('labelAmmSide')} {t('labelAmmLpTokenAmount') + '/' + t('labelTxNetworkFee')} ), formatter: ({ row }) => { const tradeType = row['side'] === AmmSideTypes.Join ? t('labelAmmJoin') : t('labelAmmExit') const { from, to } = row['amount'] const renderFromValue = getValuePrecisionThousand( from.value, undefined, undefined, undefined, false, { isTrade: true }, ) const renderToValue = getValuePrecisionThousand( to.value, undefined, undefined, undefined, false, { isTrade: true }, ) const { key, value } = row.fee return ( {tradeType} {`${renderFromValue} ${from.key} + ${renderToValue} ${to.key}`} {`Fee: ${getValuePrecisionThousand(value, undefined, undefined, undefined, false, { isTrade: true, floor: false, })} ${key}`} ) }, }, { key: 'lpTokenAmmTime', name: t('labelAmmTime'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { // const amount = row["lpTokenAmount"]; // const renderValue = // row["side"] === AmmSideTypes.Join // ? `+${getValuePrecisionThousand( // amount, // undefined, // undefined, // undefined, // false, // { isTrade: true } // )}` // : `-${getValuePrecisionThousand( // amount, // undefined, // undefined, // undefined, // false, // { isTrade: true } // )}`; const time = moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() return ( {time} ) }, }, ] export const AmmTable = withTranslation('tables')((props: WithTranslation & AmmTableProps) => { const { search } = useLocation() const searchParams = new URLSearchParams(search) const { t, pagination, showFilter, rawData, filterPairs, getAmmpoolList } = props const [isDropDown, setIsDropDown] = React.useState(true) const [page, setPage] = React.useState(1) // const [filterPair, setFilterPair] = React.useState("all"); const [filterItems, setFilterItems] = React.useState<{ filterType: FilterTradeTypes filterDate: DateRange filterPair: string }>({ filterType: FilterTradeTypes.allTypes, filterDate: [null, null], filterPair: 'all', }) const { currency, isMobile } = useSettings() const defaultArgs: any = { columnMode: isMobile ? getColumnModeMobileAssets(t, currency) : getColumnModeAssets(t, currency), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw, style: { backgroundColor: ({ colorBase }: any) => `${colorBase.box}`, }, } const updateData = _.debounce( async ({ page = 1, type = FilterTradeTypes.allTypes, date = [null, null], pair }: any) => { const start = date ? date[0] && Number(moment(date[0]).format(UNIX_TIMESTAMP_FORMAT)) : undefined const end = date ? date[1] && Number(moment(date[1]).format(UNIX_TIMESTAMP_FORMAT)) : undefined await getAmmpoolList({ tokenSymbol: pair, txTypes: type !== FilterTradeTypes.allTypes ? type : '', start, end, offset: (page - 1) * (pagination?.pageSize ?? 10), limit: pagination?.pageSize ?? 10, }) }, globalSetup.wait, ) const handleFilterChange = React.useCallback( ({ type, date, pair }) => { let filters = { filterType: type ? type : filterItems.filterType, filterDate: date ? date : filterItems.filterDate, filterPair: pair ? pair : filterItems.filterPair, } setFilterItems(filters) updateData({ type: filters.filterType, date: filters.filterDate, pair: filters.filterPair, page: 1, }) }, [filterItems.filterDate, filterItems.filterPair, filterItems.filterType, updateData], ) const handlePageChange = React.useCallback( ({ page = 1, type, date, pair }: any) => { setPage(page) myLog('AmmTable page,', page) updateData({ page, type, date, pair }) }, [updateData], ) const handleReset = React.useCallback(() => { setFilterItems({ filterType: FilterTradeTypes.allTypes, filterDate: [null, null], filterPair: '', }) updateData({ page: 1, type: FilterTradeTypes.allTypes, date: [null, null], pair: '', }) }, [updateData]) React.useEffect(() => { let filters: any = {} updateData.cancel() if (searchParams.get('pair')) { filters.pair = searchParams.get('pair') } handleFilterChange(filters) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( {showFilter && (isMobile && isDropDown ? ( setIsDropDown(false)} > {t('labelShowFilter')} ) : ( ))}
    {!!(pagination && pagination.total) && ( handlePageChange({ page })} /> )} ) }) ================================================ FILE: packages/component-lib/src/components/tableList/ammTable/components/Filter.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Grid, MenuItem } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { DateRangePicker, TextField } from '../../../basic-lib/form' import { Button } from '../../../basic-lib/btns' import { DropDownIcon } from '@loopring-web/common-resources' import { DateRange } from '@mui/lab' import { useSettings } from '../../../../stores' export interface FilterProps { filterPairs: string[] filterType: string filterPair: string filterDate: DateRange handleFilterChange: ({ filterType, filterDate, filterToken }: any) => void handleReset: () => void } const StyledTextFiled = styled(TextField)` &.MuiTextField-root { max-width: initial; } .MuiInputBase-root { width: initial; max-width: initial; } ` export enum FilterTradeTypes { join = 'Add', exit = 'Remove', allTypes = 'all', } export const Filter = withTranslation('tables', { withRef: true })( ({ t, filterPairs, filterType, filterDate, filterPair, handleReset, handleFilterChange, }: FilterProps & WithTranslation) => { const { isMobile } = useSettings() const FilterTradeTypeList = [ { label: t('labelAmmFilterTypes'), value: FilterTradeTypes.allTypes, }, { label: t('labelAmmJoin'), value: FilterTradeTypes.join, }, { label: t('labelAmmExit'), value: FilterTradeTypes.exit, }, ] const rawPairList = [].slice .call(filterPairs) .sort((a: string, b: string) => { return a.localeCompare(b) }) .map((pair: string) => ({ label: pair, value: pair, })) const formattedRawPairList = [ { label: t('labelFilterAllPairs'), value: 'all', }, ...rawPairList, ] return ( { handleFilterChange({ date: date }) }} /> ) => { handleFilterChange({ type: event.target.value }) }} inputProps={{ IconComponent: DropDownIcon }} > {FilterTradeTypeList.map((o) => ( {o.label} ))} ) => { handleFilterChange({ pair: event.target.value }) }} inputProps={{ IconComponent: DropDownIcon }} > {formattedRawPairList.map((o) => ( {o.label} ))} {/* */} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/ammTable/index.ts ================================================ export * from './AmmTable' export * from './interface' ================================================ FILE: packages/component-lib/src/components/tableList/ammTable/interface.ts ================================================ export enum AmmSideTypes { Exit = 'Exit', Join = 'Join', } ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/AssetsDefiTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Typography, Tooltip } from '@mui/material' import styled from '@emotion/styled' import { TFunction, withTranslation, WithTranslation, Trans } from 'react-i18next' import { Column, Table, Button } from '../../basic-lib' import { TablePaddingX } from '../../styled' import { BackIcon, EmptyValueTag, getValuePrecisionThousand, HiddenTag, Info2Icon, RowConfig, UpColor, } from '@loopring-web/common-resources' import { useOpenModals, useSettings } from '../../../stores' import { CoinIcons } from './components/CoinIcons' import ActionMemo from './components/ActionMemo' import { AssetsTableProps, RawDataAssetsItem } from './AssetsTable' import * as sdk from '@loopring-web/loopring-sdk' export type RawDefiAssetsItem = RawDataAssetsItem & { apr: string average: string defiInfo: sdk.DefiMarketInfo baseToken?: string } const TableWrap = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { //.rdg-header-row { // .rdg-cell { // display: inline-flex; // align-items: center; // } //} flex: 1; --template-columns: 16% auto auto 10% 16% !important; .rdg-cell:first-of-type { display: flex; align-items: center; margin-top: ${({ theme }) => theme.unit / 8}px; } .rdg-cell:last-of-type { } .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export const AssetsDefiTable = withTranslation('tables')( (props: WithTranslation & Partial>) => { const { t, rawData = [], allowTrade, showFilter, getMarketArrayListCallback, disableWithdrawList, hideInvestToken, hideSmallBalances, isLoading = false, setHideSmallBalances, onSend, onReceive, forexMap, rowConfig = RowConfig, hideAssets, onTokenLockHold, tokenLockDetail, searchValue, isWebEarn, ...rest } = props const gridRef = React.useRef(null) const { setShowETHStakingApr } = useOpenModals() const { isMobile, coinJson } = useSettings() const { upColor } = useSettings() const colorRight = upColor === UpColor.green ? ['var(--color-success)', 'var(--color-error)'] : ['var(--color-error)', 'var(--color-success)'] const getColumnModeAssets = (t: TFunction, allowTrade?: any): Column[] => [ { key: 'token', name: t('labelToken'), formatter: ({ row, column }) => { const token = row[column.key] let tokenIcon: [any, any] = [undefined, undefined] const [head, middle, tail] = token.value.split('-') if (token.type === 'lp' && middle && tail) { tokenIcon = coinJson[middle] && coinJson[tail] ? [coinJson[middle], coinJson[tail]] : [undefined, undefined] } if (token.type !== 'lp' && head && head !== 'lp') { tokenIcon = coinJson[head] ? [coinJson[head], undefined] : [undefined, undefined] } return ( <> {token.value} ) }, }, { key: 'amount', name: t('labelAmount'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row.amount const precision = row.precision return ( {hideAssets ? HiddenTag : getValuePrecisionThousand(value, precision, precision, undefined, false, { floor: true, })} ) }, }, { key: 'averagePositionCost', name: ( Average ), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const precision = row.precision return ( {hideAssets ? HiddenTag : row.average ? getValuePrecisionThousand(row.average, precision, precision, undefined, false, { floor: true, }) + ` ${row?.baseToken}` : EmptyValueTag} ) }, }, { key: 'apr', name: ( APR ), headerCellClass: 'textAlignCenter', formatter: ({ row }) => { return ( ) }, }, { key: 'actions', name: t('labelActions'), headerCellClass: 'textAlignRight', // minWidth: 280, formatter: ({ row }) => { const token = row.token const tokenValue = token.value return ( ) }, }, ] return (
    0 ? rowConfig.rowHeaderHeight + rawData.length * rowConfig.rowHeight : 350, }} rowHeight={rowConfig.rowHeight} headerRowHeight={rowConfig.rowHeaderHeight} rawData={rawData} generateRows={(rowData: any) => rowData} generateColumns={({ columnsRaw }: any) => columnsRaw as Column[]} showloading={isLoading} columnMode={getColumnModeAssets(t, allowTrade) as any} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/AssetsTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Modal, Typography } from '@mui/material' import styled from '@emotion/styled' import { TFunction, withTranslation, WithTranslation } from 'react-i18next' import { Column, Table } from '../../basic-lib' import { Filter } from './components/Filter' import { TablePaddingX } from '../../styled' import { CurrencyToTag, ForexMap, getValuePrecisionThousand, HiddenTag, mapSpecialTokenName, MarketType, PriceTag, RowConfig, TokenType, } from '@loopring-web/common-resources' import { useOpenModals, useSettings } from '../../../stores' import { CoinIcons } from './components/CoinIcons' import ActionMemo, { LockedMemo } from './components/ActionMemo' import * as sdk from '@loopring-web/loopring-sdk' import { XOR } from '../../../types/lib' import { LockDetailPanel } from './components/modal' import _ from 'lodash' const TableWrap = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { flex: 1; ${({ isMobile, isWebEarn }) => isWebEarn ? isMobile ? `--template-columns: 28% 68% 4% !important;` : `--template-columns: 200px 150px auto auto 220px !important;` : isMobile ? `--template-columns: 54% 40% 6% !important;` : `--template-columns: 200px 150px auto auto 184px !important;`} .rdg-cell:first-of-type { display: flex; align-items: center; margin-top: ${({ theme }) => theme.unit / 8}px; padding-left: ${({ isWebEarn }) => isWebEarn && 0}; } .rdg-cell:last-of-type { padding-right: ${({ isWebEarn }) => isWebEarn && 0}; } .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } } .investAsset.rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 200px 150px auto auto 205px !important;` : `--template-columns: 54% 40% 6% !important;`} } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean; lan: string; isWebEarn?: boolean } & BoxProps) => JSX.Element export type TradePairItem = { first: string last: string } export type RawDataAssetsItem = { token: { type: TokenType value: string } amount: string available: string locked: string tradePairList?: TradePairItem[] smallBalance: boolean tokenValueDollar: number precision: number hideDepositButton?: boolean hideWithdrawButton?: boolean } export type AssetsTableProps = { rawData: R[] searchValue?: string isInvest?: boolean pagination?: { pageSize: number } allowTrade?: any tableHeight?: number onVisibleRowsChange?: (props: any) => void showFilter?: boolean onSend: (token: string, isToL1: boolean) => void onReceive: (token: string) => void isLoading?: boolean getMarketArrayListCallback: (token: string) => string[] rowConfig?: typeof RowConfig disableWithdrawList: string[] forexMap: ForexMap onTokenLockHold?: (item: R) => void tokenLockDetail?: | undefined | { list: any[] row: any } hideAssets?: boolean isLeverageETH?: boolean isWebEarn?: boolean } & XOR< { hideInvestToken: boolean hideSmallBalances: boolean setHideLpToken: (value: boolean) => void setHideSmallBalances: (value: boolean) => void }, {} > export const AssetsTable = withTranslation('tables')( (props: WithTranslation & AssetsTableProps) => { const { t, isInvest = false, rawData, allowTrade, showFilter, onReceive, onSend, getMarketArrayListCallback, disableWithdrawList, hideInvestToken, hideSmallBalances, setHideLpToken, isLoading = false, setHideSmallBalances, forexMap, rowConfig = RowConfig, hideAssets, onTokenLockHold, tokenLockDetail, searchValue, isWebEarn, ...rest } = props const gridRef = React.useRef(null) const prevScrollTop = React.useRef(0) // const container = React.useRef(null) const [filter, setFilter] = React.useState({ searchValue: searchValue ?? '', }) const [pageSize, setPageSize] = React.useState(8) const [{ total, hasMore }, setTotal] = React.useState({ total: 0, hasMore: false }) const [page, setPage] = React.useState(1) const [viewData, setViewData] = React.useState([]) const { language, isMobile, coinJson, currency } = useSettings() const [modalState, setModalState] = React.useState(false) React.useEffect(() => { // let height = gridRef?.current?.offsetHeight // @ts-ignore let height = gridRef?.current?.element?.parentElement?.offsetHeight if (height) { const size = Math.floor((height - RowConfig.rowHeaderHeight) / RowConfig.rowHeight) setPageSize((size >= 8 ? size : 8) * 2) } else { setPageSize(16) } }, [gridRef?.current]) const handleScroll = _.debounce(() => { // const currentScrollTop = gridRef?.current?.scrollTop; const currentScrollTop = window.scrollY if (currentScrollTop > prevScrollTop.current) { setPage((prevPage) => prevPage + 1) } }, 200) const updateData = React.useCallback( (page) => { if (isWebEarn) { setViewData( (rawData && rawData.length > 0 ? rawData : []).filter((o) => { return o.amount !== '--' }), ) return } let resultData = rawData && !!rawData.length ? [...rawData] : [] if (hideSmallBalances) { resultData = resultData.filter((o) => !o.smallBalance) } // if (filter.hideLpToken) { if (hideInvestToken) { resultData = resultData.filter((o) => o.token.type === TokenType.single) } if (filter.searchValue) { resultData = resultData.filter((o) => o.token.value.toLowerCase().includes(filter.searchValue.toLowerCase()), ) } if (pageSize * page >= resultData.length) { setTotal({ total: resultData.length, hasMore: false }) } else { setTotal({ total: pageSize * (page + 1 / 2), hasMore: true }) } setViewData(resultData.slice(0, pageSize * page)) // resetTableData(resultData) }, [rawData, filter, hideSmallBalances, hideInvestToken, pageSize], ) React.useEffect(() => { updateData(page) }, [rawData, page]) React.useEffect(() => { updateData(1) return () => { handleScroll.cancel() } }, [filter, hideInvestToken, hideSmallBalances]) React.useEffect(() => { window.addEventListener('scroll', handleScroll) return () => { window.removeEventListener('scroll', handleScroll) } }, []) const handleFilterChange = React.useCallback( (filter: any) => { setFilter(filter) }, [setFilter], ) const getColumnModeAssets = ( t: TFunction, allowTrade?: any, ): Column[] => [ { key: 'token', name: t('labelToken'), formatter: ({ row, column }) => { const token = row[column.key] let tokenIcon: [any, any] = [undefined, undefined] const [head, middle, tail] = token.value.split('-') if (token.type === 'lp' && middle && tail) { tokenIcon = coinJson[middle] && coinJson[tail] ? [coinJson[middle], coinJson[tail]] : [undefined, undefined] } if (token.type !== 'lp' && head && head !== 'lp') { tokenIcon = coinJson[head] ? [coinJson[head], undefined] : [undefined, undefined] } return ( <> {isWebEarn ? ( ) : ( )} {mapSpecialTokenName(token.value)} ) }, }, { key: 'amount', name: t('labelAmount'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row['amount'] const precision = row['precision'] return ( {hideAssets ? HiddenTag : getValuePrecisionThousand(value, precision, precision, undefined, false, { floor: true, })} ) }, }, { key: 'locked', name: isWebEarn ? t('labelLocked2') : t('labelLocked'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( { if (row) { setModalState(true) onTokenLockHold && onTokenLockHold(row) } }, tokenLockDetail, }} /> ) }, }, { key: 'value', name: t('labelAssetsTableValue'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( {hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (row?.tokenValueDollar || 0) * (forexMap[currency] ?? 0), undefined, undefined, undefined, true, { isFait: true, floor: true }, )} ) }, }, { key: 'actions', name: t('labelActions'), headerCellClass: 'textAlignRight', // minWidth: 280, formatter: ({ row }) => { const token = row['token'] const isLp = token.type === TokenType.lp const tokenValue = token.value const isDefi = token.type === TokenType.defi || ['CIETH', 'WSTETH', 'RETH'].includes(tokenValue) const isToL1 = token.type !== TokenType.lp const lpPairList = tokenValue.split('-') lpPairList.splice(0, 1) const lpPair = lpPairList.join('-') const renderMarket: MarketType = (isLp ? lpPair : tokenValue) as MarketType return ( ) }, }, ] const getColumnMobileAssets = ( t: TFunction, allowTrade?: any, ): Column[] => [ { key: 'token', name: '', formatter: ({ row, column }) => { const token = row[column.key] const value = row['amount'] const precision = row['precision'] let tokenIcon: [any, any] = [undefined, undefined] const [head, middle, tail] = token.value.split('-') if (token.type === 'lp' && middle && tail) { tokenIcon = coinJson[middle] && coinJson[tail] ? [coinJson[middle], coinJson[tail]] : [undefined, undefined] } if (token.type !== 'lp' && head && head !== 'lp') { tokenIcon = coinJson[head] ? [coinJson[head], undefined] : [undefined, undefined] } return ( {token.value} ) }, }, { key: 'locked', name: '', headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { const token = row[column.key] const value = row['amount'] const precision = row['precision'] let tokenIcon: [any, any] = [undefined, undefined] // const [head, middle, tail] = token.value.split('-') // if (token.type === 'lp' && middle && tail) { // tokenIcon = // coinJson[middle] && coinJson[tail] // ? [coinJson[middle], coinJson[tail]] // : [undefined, undefined] // } // if (token.type !== 'lp' && head && head !== 'lp') { // tokenIcon = coinJson[head] ? [coinJson[head], undefined] : [undefined, undefined] // } return ( {hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (row?.tokenValueDollar || 0) * (forexMap[currency] ?? 0), undefined, undefined, undefined, true, { isFait: true, floor: true }, )} {t('labelTotalLocked')}:{' '} {hideAssets ? HiddenTag : getValuePrecisionThousand(value, precision, precision, undefined, false, { floor: true, })} / {hideAssets ? HiddenTag : getValuePrecisionThousand( row['locked'], precision, precision, undefined, false, { floor: true, }, )} ) }, }, { key: 'actions', name: '', headerCellClass: 'textAlignRight', // minWidth: 280, formatter: ({ row }) => { const token = row['token'] const isLp = token.type === TokenType.lp const tokenValue = token.value const isDefi = token.type === TokenType.defi || ['CIETH', 'WSTETH', 'RETH'].includes(tokenValue) const lpPairList = tokenValue.split('-') lpPairList.splice(0, 1) const lpPair = lpPairList.join('-') const renderMarket: MarketType = (isLp ? lpPair : tokenValue) as MarketType return ( ) }, }, ] return ( {!isWebEarn && showFilter && ( )} setModalState(false)}> <>
    0 ? rowConfig.rowHeaderHeight + viewData.length * rowConfig.rowHeight : 350, minHeight: 0 }} rowHeight={rowConfig.rowHeight} headerRowHeight={isMobile ? 0 : rowConfig.rowHeaderHeight} rawData={viewData} generateRows={(rowData: any) => rowData} generateColumns={({ columnsRaw }: any) => columnsRaw as Column[]} showloading={isLoading} // onScroll={handleScroll} columnMode={ (isMobile ? getColumnMobileAssets(t, allowTrade) : getColumnModeAssets(t, allowTrade)) as any } /> {hasMore && ( {'loading'} {t('labelLoadingMore')} )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/VaultAssetsTable.tsx ================================================ import React from 'react' import {Box, BoxProps, Typography} from '@mui/material' import styled from '@emotion/styled' import {TFunction, withTranslation, WithTranslation} from 'react-i18next' import {Column, Table} from '../../basic-lib' import {Filter, VaultAssetFilter} from './components/Filter' import {TablePaddingX} from '../../styled' import { BrushIcon, CurrencyToTag, EmptyValueTag, ForexMap, getValuePrecisionThousand, HiddenTag, PriceTag, RowConfig, TokenType, } from '@loopring-web/common-resources' import {useSettings} from '../../../stores' import {CoinIcons} from './components/CoinIcons' import * as sdk from '@loopring-web/loopring-sdk' import {XOR} from '../../../types/lib' import _ from 'lodash' import { Button } from '@mui/material' import Decimal from 'decimal.js' const BgButton = styled(Button)<{ customBg: string }>` background-color: ${({ customBg }) => customBg}; color: var(--color-text); transition: all 0.2s ease-in-out; &:hover { background-color: ${({ customBg }) => customBg}; opacity: 0.8; } &:disabled { background-color: var(--color-button-disabled); } ` const TableWrap = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { flex: 1; ${({ hideActions }) => hideActions ? `--template-columns: 230px 180px auto !important;` : `--template-columns: 200px 150px auto auto !important;`} .rdg-cell:first-of-type { display: flex; align-items: center; margin-top: ${({ theme }) => theme.unit / 8}px; } .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } } .investAsset.rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 200px 150px auto auto 205px !important;` : `--template-columns: 54% 40% 6% !important;`} } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean; lan: string } & BoxProps) => JSX.Element export type TradePairItem = { first: string last: string } export type VaultDataAssetsItem = { token: { type: TokenType value: string belongAlice?: string } amount: string available: string locked: string tradePairList?: TradePairItem[] smallBalance: boolean tokenValueDollar: number erc20Symbol: string precision: number equity: string debt: string repayDisabled: boolean } export type VaultAssetsTableProps = { rawData: R[] searchValue?: string pagination?: { pageSize: number } onRowClick?: (index: number, row: R) => void allowTrade?: any tableHeight?: number onVisibleRowsChange?: (props: any) => void showFilter?: boolean isLoading?: boolean rowConfig?: typeof RowConfig forexMap: ForexMap hideAssets?: boolean actionRow: (props: { row }) => JSX.Element onClickDustCollector: () => void hideActions?: boolean noMinHeight?: boolean hideDustCollector?: boolean onRowClickTrade: ({ row }) => void onRowClickRepay: ({ row }) => void } & XOR< { setHideSmallBalances: (status: any) => void hideSmallBalances: boolean }, {} > export const VaultAssetsTable = withTranslation('tables')( (props: WithTranslation & VaultAssetsTableProps) => { const { t, rawData, allowTrade, showFilter, onRowClick, actionRow, hideSmallBalances, isLoading = false, setHideSmallBalances, forexMap, rowConfig = RowConfig, hideAssets, searchValue, onClickDustCollector, hideActions, noMinHeight, hideDustCollector, onRowClickTrade, onRowClickRepay, ...rest } = props const gridRef = React.useRef(null) const prevScrollTop = React.useRef(0) const [filter, setFilter] = React.useState({ searchValue: searchValue ?? '', }) const [pageSize, setPageSize] = React.useState(8) const [{ total, hasMore }, setTotal] = React.useState({ total: 0, hasMore: false }) const [page, setPage] = React.useState(1) const [viewData, setViewData] = React.useState([]) const { language, isMobile, coinJson, currency } = useSettings() React.useEffect(() => { // @ts-ignore - gridRef.current.element is accessible at runtime let height = gridRef?.current?.element?.parentElement?.offsetHeight if (height) { const size = Math.floor((height - RowConfig.rowHeaderHeight) / RowConfig.rowHeight) setPageSize((size >= 8 ? size : 8) * 2) } else { setPageSize(16) } }, [gridRef?.current]) const handleScroll = _.debounce(() => { const currentScrollTop = window.scrollY if (currentScrollTop > prevScrollTop.current) { setPage((prevPage) => prevPage + 1) } }, 200) const updateData = React.useCallback( (page) => { let resultData = rawData && !!rawData.length ? [...rawData] : [] if (hideSmallBalances) { const list = ['ETH', 'LRC', 'USDT'] resultData = resultData.filter((o) => list.includes(o.erc20Symbol) || !o.smallBalance) } if (filter.searchValue) { resultData = resultData.filter((o) => o.token.value.toLowerCase().includes(filter.searchValue.toLowerCase()), ) } if (pageSize * page >= resultData.length) { setTotal({ total: resultData.length, hasMore: false }) } else { setTotal({ total: pageSize * (page + 1 / 2), hasMore: true }) } setViewData(resultData.slice(0, pageSize * page)) }, [rawData, hideSmallBalances, pageSize, filter.searchValue, setTotal, setViewData], ) React.useEffect(() => { updateData(page) }, [rawData, page]) React.useEffect(() => { updateData(1) return () => { handleScroll.cancel() } }, [filter, hideSmallBalances]) React.useEffect(() => { window.addEventListener('scroll', handleScroll) return () => { window.removeEventListener('scroll', handleScroll) } }, []) const handleFilterChange = React.useCallback( (filter: any) => { setFilter(filter) }, [setFilter], ) const getColumnModeAssets = (t: TFunction): Column[] => [ { key: 'token', name: t('labelToken'), formatter: ({ row }) => { const symbol = row.erc20Symbol let tokenIcon: [any, any] = [coinJson[symbol], undefined] return ( <> {row.token.belongAlice ?? row.token.vaule} ) }, }, { key: 'holding', name: 'Holding', headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { amount, precision } = row return ( {hideAssets ? HiddenTag : amount && Number(amount) > 0 ? getValuePrecisionThousand(amount, precision, precision, undefined, false, { floor: true, }) : EmptyValueTag} ) }, }, { key: 'debt', name: 'Debt', headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( {hideAssets ? HiddenTag : new Decimal(row.debt).isZero() ? EmptyValueTag : row.debt} ) }, }, { key: 'equity', name: 'Equity', headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( {hideAssets ? HiddenTag : new Decimal(row.equity).isZero() ? EmptyValueTag : row.equity} ) }, }, ...(!hideActions ? [ { key: 'actions', name: t('labelActions'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { return ( ) }, }, ] : []), ] const getColumnMobileAssets = (t: TFunction): Column[] => [ { key: 'token', name: t('labelToken'), formatter: ({ row }) => { const symbol = row.erc20Symbol let tokenIcon: [any, any] = [coinJson[symbol], undefined] return ( <> {row.token.belongAlice ?? row.token.vaule} ) }, }, { key: 'holding', name: 'Holding', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { const { amount, precision } = row return ( {hideAssets ? HiddenTag : amount && Number(amount) > 0 ? getValuePrecisionThousand(amount, precision, precision, undefined, false, { floor: true, }) : EmptyValueTag} ) }, }, { key: 'actions', name: t('labelActions'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( ) }, }, ] const MobileCardView = () => { return ( {viewData.map((row, index) => { const symbol = row.erc20Symbol; let tokenIcon: [any, any] = [coinJson[symbol], undefined]; return ( {row.token.belongAlice ?? row.token.value} {hideAssets ? HiddenTag : row.amount && Number(row.amount) > 0 ? getValuePrecisionThousand( row.amount, row.precision, row.precision, undefined, false, { floor: true, }, ) : EmptyValueTag} Debt {hideAssets ? HiddenTag : new Decimal(row.debt).isZero() ? EmptyValueTag : row.debt} Equity {hideAssets ? HiddenTag : new Decimal(row.equity).isZero() ? EmptyValueTag : row.equity} {!hideActions && ( onRowClickTrade({ row })} > {t('labelTrade')} { if (!row.repayDisabled) { onRowClickRepay({ row }) } }} > Repay )} ) })} ); }; return ( {showFilter && ( {!hideDustCollector && {' '} {t('labelVaultDustCollector')} } )} {isMobile ? ( ) : ( <>
    0 ? rowConfig.rowHeaderHeight + total * rowConfig.rowHeight : 350, minHeight: noMinHeight ? 0 : undefined }} onRowClick={onRowClick as any} rowHeight={rowConfig.rowHeight} headerRowHeight={rowConfig.rowHeaderHeight} rawData={viewData} generateRows={(rowData: any) => rowData} generateColumns={({ columnsRaw }: any) => columnsRaw as Column[]} showloading={isLoading} columnMode={getColumnModeAssets(t) as any} /> {hasMore && ( {'loading'} {t('labelLoadingMore')} )} )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/VaultPositionsTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Typography } from '@mui/material' import styled from '@emotion/styled' import { TFunction, withTranslation, WithTranslation } from 'react-i18next' import { Column, Table } from '../../basic-lib' import { VaultAssetFilter } from './components/Filter' import { TablePaddingX } from '../../styled' import { BrushIcon, hexToRGB, HiddenTag, MarginLevelIcon, RowConfig, TokenType } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { CoinIcons, CoinIconsNew } from './components/CoinIcons' import _ from 'lodash' import { Button } from '@mui/material' import { useTheme } from '@emotion/react' import { marginLevelType } from '@loopring-web/core/src/hooks/useractions/vault/utils' const TableWrap = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { flex: 1; ${({ isMobile }) => isMobile ? `--template-columns: 30% auto 50% !important;` : `--template-columns: 250px auto auto 290px !important;`} .rdg-cell:first-of-type { display: flex; align-items: center; margin-top: ${({ theme }) => theme.unit / 8}px; } .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean; lan: string } & BoxProps) => JSX.Element export type PositionItem = { tokenPair: { coinJson: any pair: string leverage: string marginLevel: string } direction: 'long' | 'short' holding: string onClickTrade: () => void onClickClose: () => void } export type VaultPositionsTableProps = { rawData: PositionItem[] onRowClick?: (index: number, row: PositionItem) => void isLoading?: boolean rowConfig?: typeof RowConfig hideAssets?: boolean showFilter?: boolean hideSmallBalances?: boolean setHideSmallBalances?: (status: boolean) => void hideDustCollector?: boolean onClickDustCollector?: () => void } export const VaultPositionsTable = withTranslation('tables')( (props: WithTranslation & VaultPositionsTableProps) => { const { t, rawData, onRowClick, rowConfig = RowConfig, hideAssets, showFilter, hideSmallBalances = false, setHideSmallBalances, hideDustCollector = false, onClickDustCollector, isLoading, ...rest } = props const total = rawData.length const { language, isMobile } = useSettings() const theme = useTheme() const [filter, setFilter] = React.useState({ searchValue: '', }) const handleFilterChange = React.useCallback( (props: { searchValue: string }) => { setFilter(props) }, [setFilter] ) const getColumnModeAssets = (t: TFunction): Column[] => [ { key: 'token', name: t('labelToken'), formatter: ({ row }) => { return ( {row.tokenPair.pair} ) }, }, { key: 'direction', name: 'Direction', headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( {row.direction === 'long' ? 'Long' : 'Short'} ) }, }, { key: 'holding', name: 'Holding', headerCellClass: 'textAlignRight', formatter: ({ row }) => { return {hideAssets ? HiddenTag : row.holding} }, }, { key: 'actions', name: t('labelActions'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { return ( ) }, }, ] const getColumnMobileAssets = (t: TFunction): Column[] => [ { key: 'token', name: t('labelToken'), formatter: ({ row }) => { return ( {row.tokenPair.pair} ) }, }, { key: 'holding', name: 'Holding', headerCellClass: 'textAlignRight', formatter: ({ row }) => { return {row.direction === 'long' ? 'Long' : 'Short'} {hideAssets ? HiddenTag : row.holding} }, }, { key: 'actions', name: t('labelActions'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { return ( ) }, }, ] return ( {/* {showFilter && ( {!hideDustCollector && {' '} {t('labelVaultDustCollector')} } )} */}
    0 ? rowConfig.rowHeaderHeight + total * rowConfig.rowHeight : 200, minHeight: 0, }} onRowClick={onRowClick as any} rowHeight={rowConfig.rowHeight} headerRowHeight={rowConfig.rowHeaderHeight} rawData={rawData} generateRows={(rowData: any) => rowData} generateColumns={({ columnsRaw }: any) => columnsRaw as Column[]} showloading={isLoading} columnMode={(isMobile ? getColumnMobileAssets(t) : getColumnModeAssets(t)) as any} t={t} EmptyRowsRenderer={ No positions } /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/assetsTable.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import { AssetsTable, RawDataAssetsItem } from './index' import { TokenType } from '@loopring-web/common-resources' const Style = styled.div` flex: 1; height: 100%; ` const rawData: RawDataAssetsItem[] = [ { token: { type: TokenType.single, value: 'LRC', }, tokenValueDollar: 112, amount: '25987.09324', available: '25987.01234', locked: '5.9873', tradePairList: [ { first: 'LRC', last: 'ETH', }, { first: 'LRC', last: 'BTC', }, { first: 'LRC', last: 'LTC', }, ], smallBalance: false, }, { token: { type: TokenType.lp, value: 'LP-LRC-USDT', }, tokenValueDollar: 112, amount: '987.09324', available: '887.01234', locked: '115.9873', tradePairList: undefined, smallBalance: true, }, { token: { type: TokenType.lp, value: 'LP-ETH-USDT', }, tokenValueDollar: 112, amount: '15987.09324', available: '15687.01234', locked: '312.9073', tradePairList: undefined, smallBalance: false, }, { token: { type: TokenType.single, value: 'LRC', }, tokenValueDollar: 112, amount: '25987.09324', available: '25987.01234', locked: '5.9873', tradePairList: [ { first: 'LRC', last: 'ETH', }, { first: 'LRC', last: 'BTC', }, { first: 'LRC', last: 'LTC', }, ], smallBalance: false, }, { token: { type: TokenType.lp, value: 'LP-LRC-USDT', }, tokenValueDollar: 112, amount: '987.09324', available: '887.01234', locked: '115.9873', tradePairList: undefined, smallBalance: true, }, { token: { type: TokenType.lp, value: 'LP-ETH-USDT', }, tokenValueDollar: 112, amount: '15987.09324', available: '15687.01234', locked: '312.9073', tradePairList: undefined, smallBalance: false, }, { token: { type: TokenType.single, value: 'LRC', }, tokenValueDollar: 112, amount: '25987.09324', available: '25987.01234', locked: '5.9873', tradePairList: [ { first: 'LRC', last: 'ETH', }, { first: 'LRC', last: 'BTC', }, { first: 'LRC', last: 'LTC', }, ], smallBalance: false, }, { token: { type: TokenType.lp, value: 'LP-LRC-USDT', }, tokenValueDollar: 112, amount: '987.09324', available: '887.01234', locked: '115.9873', tradePairList: undefined, smallBalance: true, }, { token: { type: TokenType.lp, value: 'LP-ETH-USDT', }, tokenValueDollar: 112, amount: '15987.09324', available: '15687.01234', locked: '312.9073', tradePairList: undefined, smallBalance: false, }, { token: { type: TokenType.single, value: 'LRC', }, tokenValueDollar: 112, amount: '25987.09324', available: '25987.01234', locked: '5.9873', tradePairList: [ { first: 'LRC', last: 'ETH', }, { first: 'LRC', last: 'BTC', }, { first: 'LRC', last: 'LTC', }, ], smallBalance: false, }, { token: { type: TokenType.lp, value: 'LP-LRC-USDT', }, tokenValueDollar: 112, amount: '987.09324', available: '887.01234', locked: '115.9873', tradePairList: undefined, smallBalance: true, }, { token: { type: TokenType.lp, value: 'LP-ETH-USDT', }, tokenValueDollar: 112, amount: '15987.09324', available: '15687.01234', locked: '312.9073', tradePairList: undefined, smallBalance: false, }, ] const Template: Story = withTranslation()((args: any) => { return ( <> ) }) as Story //@ts-ignore export const Assets = Template.bind({}) Assets.args = { rawData: rawData, // pagination: { // pageSize: 5 // } onVisibleRowsChange: (data: any) => { console.log(data) }, } export default { title: 'components/TableList/Assets', component: AssetsTable, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/components/ActionMemo.tsx ================================================ import React from 'react' import { Box, Grid, ListItemText, MenuItem, Typography } from '@mui/material' import styled from '@emotion/styled' import { Button, Popover, PopoverType, PopoverWrapProps } from '../../../basic-lib' import { EmptyValueTag, getValuePrecisionThousand, HiddenTag, MapChainId, MoreIcon, LEVERAGE_ETH_CONFIG, AmmPanelType, InvestAssetRouter, RouterPath, } from '@loopring-web/common-resources' import { useHistory } from 'react-router-dom' import { useOpenModals, useSettings, useToggle } from '../../../../stores' import { RawDataAssetsItem } from '../AssetsTable' import { useTranslation } from 'react-i18next' const GridStyled = styled(Grid)` .MuiGrid-item { padding: ${({ theme }) => theme.unit / 4}px 0 0; } ` export type ActionProps = { tokenValue: any allowTrade?: any market?: `${string}-${string}` isLp: boolean isDefi: boolean isInvest: boolean onSend: (token: string, isToL1: boolean) => void onReceive: (token: string) => void getMarketArrayListCallback?: (token: string) => string[] isLeverageETH: boolean isWebEarn?: boolean hideDepositButton?: boolean hideWithdrawButton?: boolean } const ActionPopContent = React.memo( ({ market, isLp, isDefi, allowTrade, onSend, onReceive, // onShowDeposit, tokenValue, isInvest, // onShowTransfer, // onShowWithdraw, getMarketArrayListCallback, isLeverageETH, isWebEarn, }: ActionProps) => { const history = useHistory() const { t } = useTranslation(['tables', 'common']) const { setShowAmm } = useOpenModals() const { toggle } = useToggle() const _allowTrade = { ...toggle, allowTrade, } const { isMobile } = useSettings() const tradeList = [ ...[ onReceive(tokenValue)}> {isWebEarn ? t('labelDeposit') : t('labelReceive')} , onSend(tokenValue, isLp)}> {isWebEarn ? t('labelWithdraw') : t('labelSend')} , ], // ...(isToL1 // ? [ // onShowWithdraw(tokenValue)}> // {t("labelL2toL1Action")} // , // ] // : []), ] const marketList = (isLp || isWebEarn) ? [] : getMarketArrayListCallback && market && getMarketArrayListCallback(market).filter((pair) => { const [first, last] = pair.split('-') if (['USDT', 'USDC'].includes(first) || ['USDT', 'USDC'].includes(last)) { return true } return first === market }) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const coins = LEVERAGE_ETH_CONFIG.coins[network] return ( {isMobile && tradeList.map((item) => <>{item})} {isLp ? ( <> { setShowAmm({ isShow: true, type: AmmPanelType.Join, symbol: market, }) }} > {t('labelPoolTableAddLiquidity')} { setShowAmm({ isShow: true, type: AmmPanelType.Exit, symbol: market, }) }} > {t('labelPoolTableRemoveLiquidity')} ) : isDefi || isLeverageETH ? ( isInvest && !isMobile ? ( <> {tradeList.map((item) => ( <>{item} ))} ) : ( <> {!['CIETH', 'WSTETH', 'RETH'].includes(tokenValue) && ( { if (coins.includes(tokenValue)) { history.push(`${RouterPath.invest}/${InvestAssetRouter.LEVERAGEETH}`) } else { history.push( `${RouterPath.invest}/${InvestAssetRouter.STAKE}/${tokenValue}-null/invest`, ) } }} > {t('labelDefiInvest')} )} { if (coins.includes(tokenValue)) { history.push(`${RouterPath.invest}/${InvestAssetRouter.LEVERAGEETH}/redeem`) } else { history.push( `${RouterPath.invest}/${InvestAssetRouter.STAKE}/${tokenValue}-null/redeem`, ) } }} > {t('labelDefiRedeem')} ) ) : ( marketList?.map((pair) => { const formattedPair = pair.replace('-', ' / ') return ( history.push({ pathname: `/trade/lite/${pair}`, }) } > {formattedPair} ) }) )} ) }, ) const ActionMemo = React.memo((props: ActionProps) => { const { isMobile } = useSettings() const history = useHistory() const { t } = useTranslation('tables') const { allowTrade, tokenValue, onSend, onReceive, isLp, isInvest = false, isDefi, // onShowDeposit, // onShowTransfer, // onShowWithdraw, isLeverageETH, isWebEarn, hideDepositButton, hideWithdrawButton } = props const popoverProps: PopoverWrapProps = { type: PopoverType.click, popupId: 'testPopup', className: 'arrow-none', children: , popoverContent: , anchorOrigin: { vertical: 'bottom', horizontal: 'right', }, transformOrigin: { vertical: 'top', horizontal: 'right', }, } as PopoverWrapProps const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const coins = LEVERAGE_ETH_CONFIG.coins[network] return ( {isMobile ? ( <> {((!isLp && allowTrade?.order?.enable) || isLp || isDefi || isLeverageETH) && ( )} ) : ( <> {isInvest ? ( <> {!['CIETH', 'WSTETH', 'RETH'].includes(tokenValue) && ( )} ) : ( <> {!hideDepositButton && } {!hideWithdrawButton && } )} {!isWebEarn && !isLp && !isInvest && allowTrade?.order?.enable && ( )} {(isLp || isInvest || isLeverageETH) && ( )} )} ) }) export default ActionMemo export const LockedMemo = React.memo( ( props: Omit & { hideAssets?: boolean onTokenLockHold?: (item: any) => void tokenLockDetail?: | undefined | { list: any[] row: any } }, ) => { const { onTokenLockHold, tokenLockDetail, ...row } = props const value = row['locked'] const precision = row['precision'] // myLog(tokenLockDetail); if (!Number(value)) { return {EmptyValueTag} } else { return ( { if (onTokenLockHold) { onTokenLockHold(row) } }} > {props.hideAssets ? HiddenTag : getValuePrecisionThousand(value, precision, precision, undefined, false, { floor: true, })} ) } }, ) ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/components/CoinIcons.tsx ================================================ import React from 'react' import { Avatar, Box, BoxProps, styled, Typography } from '@mui/material' import { CoinInfo, CoinSource, SoursURL, TokenType } from '@loopring-web/common-resources' import { AvatarCoin, VaultTag } from '../../../basic-lib' import { useSettings } from '../../../../stores' const BoxStyle = styled(Box)` ${({ size }) => { return ` .logo-icon.dual:last-child { transform: scale(0.6) translate(${size / 6}px, ${size / 6}px); } .logo-icon.vault:last-child { transform: bottom; } ` }} ` export const CoinIcons = React.memo( ({ tokenIcon, size: _size, type = TokenType.single, }: { tokenIcon: [CoinSource, CoinSource?] size?: number | 'middle' | 'small' | 'large' type?: TokenType }) => { const size = React.useMemo(() => { if (!_size) { return 24 } else if (typeof _size === 'string') { switch (_size) { case 'middle': return 24 case 'small': return 20 case 'large': return 36 } } else { return _size } }, [_size]) const [coinAInfo, coinBInfo] = tokenIcon return ( {coinAInfo ? ( ' } /> ) : ( )} {[TokenType.vault].includes(type) && ( )} {coinBInfo || [TokenType.dual, TokenType.lp].includes(type) ? ( {coinBInfo ? ( ' } /> ) : ( )} ) : ( <> )} ) }, ) export const CoinIconsNew = ({ tokenIcon, size: _size, secondLogoType, ...rest }: { tokenIcon: [CoinSource, CoinSource?] size?: number | 'middle' | 'small' | 'large' secondLogoType?: 'normal' | 'subscript' } & BoxProps) => { const size = React.useMemo(() => { if (!_size) { return 24 } else if (typeof _size === 'string') { switch (_size) { case 'middle': return 24 case 'small': return 20 case 'large': return 36 } } else { return _size } }, [_size]) const [coinAInfo, coinBInfo] = tokenIcon return ( {coinAInfo ? ( ' } /> ) : ( )} {secondLogoType === 'subscript' && coinBInfo && ( ' } /> {/* */} )} {secondLogoType !== 'subscript' && coinBInfo && ( {coinBInfo ? ( ' } /> ) : ( )} )} ) } export const ColumnCoinDeep = React.memo( ({ token: { type = TokenType.single, ...token }, isNotRequiredName = false, }: { token: CoinInfo & { type?: TokenType } } & { isNotRequiredName?: boolean }) => { let tokenIcon: [any, any] = [undefined, undefined] const [head, middle, tail] = token?.simpleName?.split('-') const { coinJson } = useSettings() if (type === 'lp' && middle && tail) { tokenIcon = coinJson[middle] && coinJson[tail] ? [coinJson[middle], coinJson[tail]] : [undefined, undefined] } if (type !== 'lp' && head && head !== 'lp') { tokenIcon = coinJson[head] ? [coinJson[head], undefined] : [undefined, undefined] } return ( {token?.simpleName} {!isNotRequiredName && ( {token?.name} )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/components/Filter.tsx ================================================ import { Box, Checkbox, Grid } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { FormControlLabel, InputSearch } from '../../../' import { CheckBoxIcon, CheckedIcon, TokenType } from '@loopring-web/common-resources' export type TokenTypeCol = { type: TokenType value: string } export interface FilterProps { hideInvestToken?: boolean hideSmallBalances?: boolean setHideLpToken?: (value: boolean) => void setHideSmallBalances?: (value: boolean) => void filter: { searchValue: string } handleFilterChange: (props: { searchValue: string }) => void noHideInvestToken?: boolean } export enum CheckboxType { smallBalance = 'smallBalance', invest = 'invest', } export const Filter = withTranslation('tables', { withRef: true })( ({ t, handleFilterChange, filter, hideInvestToken, hideSmallBalances, setHideLpToken, setHideSmallBalances, noHideInvestToken }: FilterProps & WithTranslation) => { return ( { handleFilterChange({ searchValue: value }) }} /> {!noHideInvestToken && } icon={} color='default' onChange={(event) => { if (setHideLpToken) { setHideLpToken(event.target.checked) } }} /> } label={t('labelHideInvestToken')} />} } icon={} color='default' onChange={(event) => { if (setHideSmallBalances) { setHideSmallBalances(event.target.checked) } }} /> } label={t('labelHideSmallBalances')} /> ) }, ) export const VaultAssetFilter = withTranslation('tables', { withRef: true })( ({ t, hideSmallBalances, setHideSmallBalances, }: FilterProps & WithTranslation) => { return ( } icon={} color='default' onChange={(event) => { if (setHideSmallBalances) { setHideSmallBalances(event.target.checked) } }} /> } label={t('labelHideSmallBalances')} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/components/modal.tsx ================================================ import { useTranslation } from 'react-i18next' import styled from '@emotion/styled' import { Box, Link, Typography } from '@mui/material' import { useSettings } from '../../../../stores' import { LoadingBlock } from '../../../block' import { CoinIcons } from './CoinIcons' import { L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' const ContentWrapperStyled = styled(Box)` position: absolute; top: 45%; left: 50%; transform: translate(-50%, -50%); // min-width: ${({ theme }) => theme.unit * 87.5}px; // height: 60%; background-color: var(--color-pop-bg); box-shadow: 0px ${({ theme }) => theme.unit / 2}px ${({ theme }) => theme.unit / 2}px rgba(0, 0, 0, 0.25); padding: 0 ${({ theme }) => theme.unit * 1}px; border-radius: ${({ theme }) => theme.unit / 2}px; ` const HeaderStyled = styled(Box)` display: flex; align-items: center; width: 100%; margin-top: ${({ theme }) => theme.unit * 2}px; margin-bottom: ${({ theme }) => theme.unit * 2}px; padding: 0 ${({ theme }) => theme.unit * 3}px; ` export const LockDetailPanel = ({ tokenLockDetail, }: { tokenLockDetail?: | undefined | { list: any[] row: any } }) => { const { t } = useTranslation() const { isMobile, coinJson, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const token = tokenLockDetail?.row?.token let tokenIcon: [any, any] = [undefined, undefined] if (token) { const [head, middle, tail] = token?.value?.split('-') if (token.type === 'lp' && middle && tail) { tokenIcon = coinJson[middle] && coinJson[tail] ? [coinJson[middle], coinJson[tail]] : [undefined, undefined] } if (token.type !== 'lp' && head && head !== 'lp') { tokenIcon = coinJson[head] ? [coinJson[head], undefined] : [undefined, undefined] } } return ( {token && } {t('labelLocketInfo', { symbol: token?.value, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {tokenLockDetail && tokenLockDetail.list?.length ? ( tokenLockDetail.list.map((item) => { return ( {t(item.key, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {item.value} ) }) ) : ( )} ) } ================================================ FILE: packages/component-lib/src/components/tableList/assetsTable/index.ts ================================================ export * from './AssetsTable' export {VaultAssetsTable} from './VaultAssetsTable' export * from './AssetsDefiTable' export * from './components/Filter' export * from './components/CoinIcons' export { VaultPositionsTable } from './VaultPositionsTable' ================================================ FILE: packages/component-lib/src/components/tableList/btradeSwapTable/BtradeSwapTable.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Tooltip, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { BtradeSwapsType, RawDataBtradeSwapsItem } from './Interface' import { EmptyValueTag, globalSetup, Info2Icon, RowInvestConfig, } from '@loopring-web/common-resources' import { useHistory } from 'react-router-dom' import moment from 'moment/moment' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })}; & .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 33% 20% auto auto auto !important;` : `--template-columns: 33% 32% auto !important;`} } ` const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export interface BtradeSwapsTableProps { rawData: R[] showloading: boolean onItemClick: (item: R) => void pagination: { pageSize: number total: number } getBtradeOrderList: (props: Omit) => Promise } export const BtradeSwapTable = withTranslation(['tables', 'common'])( (props: BtradeSwapsTableProps & WithTranslation) => { const { rawData, showloading, onItemClick, pagination, getBtradeOrderList, t } = props const [page, setPage] = React.useState(0) // const [_pageSize, setPageSize] = React.useState(pagination?.pageSize); const { isMobile, upColor } = useSettings() const history = useHistory() const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Type', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelBtradeSwapType'), formatter: ({ row }: FormatterProps) => { const colorMap = [ [BtradeSwapsType.Settled, 'var(--color-success)'], [BtradeSwapsType.Delivering, 'var(--color-warning)'], [BtradeSwapsType.Failed, 'var(--color-error)'], [BtradeSwapsType.Cancelled, 'var(--color-error)'], [BtradeSwapsType.Pending, 'var(--color-warning)'], ] const found = colorMap.find((x) => x[0] === row?.type) return ( {row?.type === BtradeSwapsType.Delivering ? ( {t('labelBtradeDeliveringDes').toString()} } > {t( 'labelBtrade' + // @ts-ignore (row?.type == BtradeSwapsType.Failed || // @ts-ignore row?.type == BtradeSwapsType.Cancelled ? BtradeSwapsType.Failed.toString() : row?.type), )} ) : ( {t('labelBtrade' + row?.type)} )} {row ? `${row.fromAmountDisplay} ${row.fromSymbol} -> ${row.toAmountDisplay} ${row.toSymbol}` : EmptyValueTag} ) }, }, { key: 'Filled', name: t('labelBtradeSwapFailed'), formatter: ({ row }: FormatterProps) => { return <>{row.filledPercent ? row.filledPercent + '%' : EmptyValueTag} }, }, { key: 'Price', name: t('labelBtradeSwapPrice'), formatter: ({ row }: FormatterProps) => { return <> {row.price?.value + ' ' + row.price?.key} }, }, { key: 'Fee', name: t('labelBtradeSwapFee'), formatter: ({ row }: FormatterProps) => { return ( <> {row.feeAmount !== undefined ? row.feeAmount + ' ' + row.feeSymbol : EmptyValueTag} ) }, }, { key: 'Time', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelBtradeSwapTime'), formatter: ({ row }: FormatterProps) => { return <>{moment(new Date(row.time)).fromNow()} }, }, ], [history, upColor, t], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'Type', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelBtradeSwapType'), formatter: ({ row }: FormatterProps) => { const colorMap = [ [BtradeSwapsType.Settled, 'var(--color-success)'], [BtradeSwapsType.Delivering, 'var(--color-warning)'], [BtradeSwapsType.Failed, 'var(--color-error)'], [BtradeSwapsType.Cancelled, 'var(--color-error)'], [BtradeSwapsType.Pending, 'var(--color-warning)'], ] const found = colorMap.find((x) => x[0] === row?.type) return ( {row?.type === BtradeSwapsType.Delivering ? ( {t( 'labelBtrade' + // @ts-ignore (row?.type == BtradeSwapsType.Failed || // @ts-ignore row?.type == BtradeSwapsType.Cancelled ? BtradeSwapsType.Failed.toString() : row?.type), )} ) : ( {t('labelBtrade' + row?.type)} )} {row ? `${row.fromAmountDisplay} ${row.fromSymbol} -> ${row.toAmountDisplay} ${row.toSymbol}` : EmptyValueTag} ) }, }, { key: 'Price', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelBtradeSwapPrice') + '/' + t('labelBtradeSwapFailed'), formatter: ({ row }: FormatterProps) => { return ( {row.price?.value + ' ' + row.price?.key} {row.filledPercent ? row.filledPercent + '%' : EmptyValueTag} ) }, }, { key: 'Fee', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelBtradeSwapFee') + '/' + t('labelBtradeSwapTime'), formatter: ({ row }: FormatterProps) => { return ( {row.feeAmount !== undefined ? row.feeAmount + ' ' + row.feeSymbol : EmptyValueTag} {moment(new Date(row.time)).fromNow()} ) }, }, ], [history, upColor, t], ) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination.pageSize, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getBtradeOrderList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( async (currPage: number) => { // if (currPage === page) return; setPage(currPage) updateData({ currPage: currPage }) }, [updateData], ) const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } // React.useEffect(() => { // updateData.cancel(); // handlePageChange(1); // return () => { // updateData.cancel(); // }; // }, [pagination?.pageSize]); React.useEffect(() => { // setPageSize(pagination?.pageSize); updateData.cancel() handlePageChange(1) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( { onItemClick(row) }} // sortMethod={sortMethod} {...{ ...defaultArgs, ...props, rawData, showloading, }} /> {pagination && !!rawData.length && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/btradeSwapTable/Interface.ts ================================================ export enum BtradeSwapsType { Settled = 'Settled', Delivering = 'Delivering', Failed = 'Failed', Pending = 'Pending', Cancelled = 'Cancelled', } export type RawDataBtradeSwapsItem = { type: BtradeSwapsType fromAmount: string fromSymbol: string toAmount: string toSymbol: string fromFAmount: string toFAmount: string price: { key: string value: string from: string } feeAmount: string feeSymbol: string time: number filledPercent: string settledFromAmount: string settledToAmount: string fromAmountDisplay: string toAmountDisplay: string } ================================================ FILE: packages/component-lib/src/components/tableList/btradeSwapTable/index.ts ================================================ export * from './BtradeSwapTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/defiTxsTable/DefiStakingTable.tsx ================================================ import _ from 'lodash' import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { EmptyValueTag, getValuePrecisionThousand, globalSetup, HiddenTag, RowInvestConfig, } from '@loopring-web/common-resources' import { Column, ModalCloseButton, Table, TablePagination } from '../../basic-lib' import * as sdk from '@loopring-web/loopring-sdk' import { Box, BoxProps, Modal, Typography } from '@mui/material' import { SwitchPanelStyled, TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { DefiSideStakingTableProps, RawDataDefiSideStakingItem } from './Interface' import { ConfirmStackingRedeem, DeFiSideDetail } from '../../tradePanel' import ActionMemo from './components/ActionMemo' import moment from 'moment' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 14% auto 16% 16% 11% 11% 160px !important;` : `--template-columns: 24% auto auto 8% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })}; ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight) { return props.currentheight + 'px' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export const DefiStakingTable = withTranslation(['tables', 'common'])( (props: DefiSideStakingTableProps & WithTranslation) => { const { rawData, idIndex, pagination, tokenMap, geDefiSideStakingList, showloading, // onDetailClick, redeemItemClick: _redeemItemClick, hideAssets, t, } = props const [openDetail, setOpenDetail] = React.useState(false) const [openAlert, setOpenAlert] = React.useState(false) const [detail, setDetail] = React.useState(undefined) const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const redeemItemClick = (item: R) => { setDetail(item) const requiredHoldDay = (item.claimableTime - item.stakeAt) / 86400000 const holdDay = moment(Date.now()).diff( moment(new Date(item.stakeAt ?? '')) .utc() .startOf('days'), 'days', false, ) if (requiredHoldDay > holdDay) { setOpenAlert(true) } else { setOpenDetail(false) _redeemItemClick(item) } } const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { geDefiSideStakingList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Product', sortable: false, width: 'auto', name: t('labelDefiStakingProduct'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { return ( {row.productId} ) }, }, { key: 'Frozen', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDefiStakingFrozen'), formatter: ({ row }: FormatterProps) => { const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.remainAmount && row.remainAmount !== '0' ? hideAssets ? HiddenTag : getValuePrecisionThousand( sdk.toBig(row.remainAmount).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + tokenInfo.symbol : EmptyValueTag return <> {amountStr} }, }, { key: 'Earn', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDefiStakingEarn'), formatter: ({ row }) => { const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.totalRewards && row.totalRewards !== '0' ? hideAssets ? HiddenTag : getValuePrecisionThousand( sdk.toBig(row.totalRewards).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + tokenInfo.symbol : EmptyValueTag return <> {amountStr} }, }, { key: 'PreviousEarn', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDefiStakingPreviousEarn'), formatter: ({ row }) => { const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.lastDayPendingRewards && row.lastDayPendingRewards !== '0' ? hideAssets ? HiddenTag : getValuePrecisionThousand( sdk.toBig(row.lastDayPendingRewards).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + tokenInfo.symbol : EmptyValueTag return <> {amountStr} }, }, { key: 'Duration', sortable: false, width: 'auto', headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', name: t('labelDefiStakingDuration'), formatter: ({ row }) => { const diff = moment(Date.now()).diff( moment(new Date(row.stakeAt ?? '')) .utc() .startOf('days'), 'days', false, ) return <>{diff ? diff + ' ' + t('labelDays') : '< 1' + ' ' + t('labelDays')} }, }, { key: 'APR', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDefiStakingARR'), formatter: ({ row }: FormatterProps) => { return <>{row.apr && row.apr !== '0.00' ? row.apr + '%' : EmptyValueTag} }, }, { key: 'StakingAction', sortable: false, cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDefiStakingAction'), formatter: ({ row }) => { return ( { setDetail(item) setOpenDetail(item as any) }, }} /> ) }, }, ], [t, tokenMap, idIndex, hideAssets], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'Product', sortable: false, width: 'auto', name: t('labelDefiStakingProduct') + '/' + t('labelDefiStakingFrozen'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.remainAmount && row.remainAmount !== '0' ? (hideAssets ? HiddenTag : getValuePrecisionThousand( sdk.toBig(row.remainAmount).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, )) + ' ' + tokenInfo.symbol : EmptyValueTag return ( {amountStr} {row.productId} ) }, }, { key: 'Earn', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDefiStakingAndPreviousEarn'), formatter: ({ row }) => { const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.lastDayPendingRewards && row.lastDayPendingRewards !== '0' ? hideAssets ? HiddenTag : getValuePrecisionThousand( sdk.toBig(row.lastDayPendingRewards).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) : EmptyValueTag const amountPreviousEarnStr = row.totalRewards && row.totalRewards != '0' ? (hideAssets ? HiddenTag : getValuePrecisionThousand( sdk.toBig(row.totalRewards).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, )) + ' ' + tokenInfo.symbol : EmptyValueTag return <>{amountStr + '/' + amountPreviousEarnStr} }, }, { key: 'APR', sortable: false, width: 'auto', headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', name: t('labelDefiStakingDuration') + '/' + t('labelDefiStakingARR'), formatter: ({ row }) => { const diff = moment(Date.now()).diff( moment(new Date(row.stakeAt ?? '')) .utc() .startOf('days'), 'days', false, ) return ( {diff ? diff + ' ' + t('labelDays') : '< 1' + ' ' + t('labelDays') + '/'} {row.apr && row.apr !== '0.00' && Number(row.apr) !== 0 ? row.apr + '%' : EmptyValueTag} ) }, }, { key: 'StakingAction', sortable: false, cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDefiStakingAction'), formatter: ({ row }: FormatterProps) => { //onDetailClick return ( { setDetail(item) setOpenDetail(item as any) }, }} /> ) }, }, ], [t, tokenMap, idIndex], ) // const [isDropDown, setIsDropDown] = React.useState(true); const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return ( 0 ? RowInvestConfig.rowHeaderHeight + rawData.length * RowInvestConfig.rowHeight : 350 } rowHeight={RowInvestConfig.rowHeight} headerRowHeight={RowInvestConfig.rowHeaderHeight} {...{ ...defaultArgs, // rowRenderer: RowRenderer, ...props, rawData, showloading, }} /> {!!(pagination && pagination.total > pagination.pageSize) && ( )} setOpenDetail(false)} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description' > setOpenDetail(false)} t={t} /> {detail && ( {t('labelDeFiSideInvestmentDetails', { ns: 'common', symbol: 'LRC', })} )} { setOpenAlert(false) if (isAgree) { _redeemItemClick(detail as any) } }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/defiTxsTable/DefiStakingTxTable.tsx ================================================ import _ from 'lodash' import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { EmptyValueTag, getShortAddr, getValuePrecisionThousand, globalSetup, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { DefiSideStakingTxTableProps, RawDataDefiSideStakingTxItem } from './Interface' import * as sdk from '@loopring-web/loopring-sdk' import moment from 'moment' const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: auto 20% 180px auto !important;` : `--template-columns: 24% auto 20% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export const DefiStakingTxTable = withTranslation(['tables', 'common'])( ( props: DefiSideStakingTxTableProps & WithTranslation, ) => { const { rawData, idIndex, pagination, tokenMap, getSideStakingTxList, showloading, t } = props const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getSideStakingTxList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) // { // "accountId": 10023, // "tokenId": 1, // "amount": "10000000000000000000000", // "productId": "LRC-20221221", // "hash": "0x2dc2ee00c80a33e00259cefa72b4a5b48e5d1187c43b101468e1dde77b43f2fa", // // "type": "subscribe", // // "createdAt": 1662086517521, // // "updatedAt": 1662086517521 // }, const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Type', sortable: false, width: 'auto', name: t('labelDefiStakingTxType'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { let side = { color: 'var(--color-text-primary)', // @ts-ignore type: `labelStakeTransactionType${row.stakingType}`, } const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.amount ? getValuePrecisionThousand( sdk.toBig(row.amount).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + tokenInfo.symbol : EmptyValueTag // @ts-ignore switch (row.stakingType) { case sdk.StakeTransactionType.subscribe: side = { ...side, color: 'var(--color-success)', } break case sdk.StakeTransactionType.redeem: side = { ...side, color: 'var(--color-error)', } break case sdk.StakeTransactionType.claim: side = { ...side, color: 'var(--color-warning)', } break } return ( {t(side.type)} {amountStr} ) }, }, { key: 'Product', sortable: false, width: 'auto', name: t('labelDefiStakingTxProduct'), cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', formatter: ({ row }) => { return <>{row.productId ? row.productId : EmptyValueTag} }, }, { key: 'HashID', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDefiStakingTxHashId'), formatter: ({ row }) => { return <>{row.hash ? getShortAddr(row.hash) : EmptyValueTag} }, }, { key: 'SubscribeTime', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDefiStakingTxRewardsDate'), formatter: ({ row }: FormatterProps) => { return ( {typeof row.createdAt === 'undefined' ? EmptyValueTag : moment(new Date(row.createdAt), 'YYYYMMDDHHMM').fromNow()} ) }, }, ], [t, tokenMap, idIndex], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'Type', sortable: false, width: 'auto', name: t('labelDefiStakingTxType'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { let side = { color: 'var(--color-text-primary)', // @ts-ignore type: `labelStakeTransactionType${row.stakingType}`, } // @ts-ignore switch (row.stakingType) { case sdk.StakeTransactionType.subscribe: side = { ...side, color: 'var(--color-success)', } break case sdk.StakeTransactionType.redeem: side = { ...side, color: 'var(--color-error)', } break case sdk.StakeTransactionType.claim: side = { ...side, color: 'var(--color-warning)', } break } return ( {t(side.type)} ) }, }, { key: 'Product', sortable: false, width: 'auto', name: t('labelDefiStakingTxAmount') + '/' + t('labelDefiStakingTxProduct'), cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', formatter: ({ row }: FormatterProps) => { const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.amount && row.amount != '0' ? getValuePrecisionThousand( sdk.toBig(row.amount).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + tokenInfo.symbol : EmptyValueTag return ( {amountStr} {row.productId} ) }, }, { key: 'SubscribeTime', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDefiStakingTxRewardsMobileDate'), formatter: ({ row }: FormatterProps) => { return ( {typeof row.createdAt === 'undefined' ? EmptyValueTag : moment(new Date(row.createdAt), 'YYYYMMDDHHMM').fromNow()} ) }, }, ], [t, tokenMap, idIndex], ) // const [isDropDown, setIsDropDown] = React.useState(true); const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { // let filters: any = {}; updateData.cancel() updateData({ currPage: 1 }) return () => { updateData.cancel() } }, [pagination?.pageSize]) return (
    {pagination && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/defiTxsTable/DefiTxsTable.tsx ================================================ import _ from 'lodash' import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { DirectionTag, EmptyValueTag, getValuePrecisionThousand, globalSetup, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Typography } from '@mui/material' import moment from 'moment' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import * as sdk from '@loopring-web/loopring-sdk' import { DefiTxsTableProps, RawDataDefiTxsItem } from './Interface' const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: auto 20% 180px !important;` : `--template-columns: 100% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export const DefiTxsTable = withTranslation(['tables', 'common'])( (props: DefiTxsTableProps & WithTranslation) => { const { rawData, idIndex, pagination, tokenMap, getDefiTxList, showloading, t } = props const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getDefiTxList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'style', sortable: false, width: 'auto', minWidth: 240, name: t('labelDefiType') + ' ' + t('labelDefiAmount'), formatter: ({ row }: FormatterProps) => { const { action, sellToken, buyToken } = row const isJoin = !new RegExp(sdk.DefiAction.Withdraw, 'ig').test(action ?? ' ') const sellTokenInfo = sellToken?.tokenId !== undefined && tokenMap[idIndex[sellToken?.tokenId]] const sellVolume = sdk.toBig(sellToken?.volume ?? 0).div('1e' + sellTokenInfo.decimals) const buyTokenInfo = buyToken?.tokenId !== undefined && tokenMap[idIndex[buyToken?.tokenId]] const buyVolume = sdk.toBig(buyToken?.volume ?? 0).div('1e' + buyTokenInfo.decimals) const side = isJoin ? t('labelDefiJoin') : t('labelDefiExit') return ( {side}    {`${getValuePrecisionThousand( sellVolume, sellTokenInfo?.precision, sellTokenInfo?.precision, sellTokenInfo?.precision, false, { isTrade: true, floor: false }, )} ${sellTokenInfo.symbol}`}  {DirectionTag}   {`${getValuePrecisionThousand( buyVolume, buyTokenInfo?.precision, buyTokenInfo?.precision, buyTokenInfo?.precision, false, { isTrade: true, floor: false }, )} ${buyTokenInfo.symbol}`} ) }, }, { key: 'fee', name: t('labelTxTradingFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { fee } = row const feeTokenInfo = tokenMap[idIndex[fee?.tokenId ?? '']] const renderValue = fee?.volume == '0' || fee?.volume === undefined ? EmptyValueTag : `${getValuePrecisionThousand( sdk.toBig(fee?.volume).div('1e' + feeTokenInfo.decimals), feeTokenInfo?.precision, feeTokenInfo?.precision, feeTokenInfo?.precision, false, { isTrade: false, floor: false }, )} ${feeTokenInfo.symbol}` return {renderValue} }, }, { key: 'time', sortable: false, width: 'auto', headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', name: t('labelDefiTime'), formatter: ({ row }) => { const { updatedAt: time } = row let timeString if (typeof time === 'undefined') { timeString = EmptyValueTag } else { timeString = moment(new Date(time), 'YYYYMMDDHHMM').fromNow() } return ( {timeString} ) }, }, ], [t, tokenMap, idIndex], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'side', name: ( {t('labelDefiType')} {t('labelDefiTime') + '/' + t('labelTxTradingFee')} ), headerCellClass: 'textAlignRight', formatter: ({ row }: FormatterProps) => { const { action, sellToken, buyToken } = row const isJoin = !new RegExp(sdk.DefiAction.Withdraw, 'ig').test(action ?? ' ') const sellTokenInfo = sellToken?.tokenId !== undefined && tokenMap[idIndex[sellToken?.tokenId]] const sellVolume = sdk.toBig(sellToken?.volume ?? 0).div('1e' + sellTokenInfo.decimals) const buyTokenInfo = buyToken?.tokenId !== undefined && tokenMap[idIndex[buyToken?.tokenId]] const buyVolume = sdk.toBig(buyToken?.volume ?? 0).div('1e' + buyTokenInfo.decimals) const side = isJoin ? t('labelDefiJoin') : t('labelDefiExit') const { fee } = row const feeTokenInfo = tokenMap[idIndex[fee?.tokenId ?? '']] const feeVolume = sdk .toBig(fee?.volume ?? 0) .div('1e' + feeTokenInfo.decimals) .toNumber() const renderFee = feeVolume === 0 || feeVolume === undefined ? EmptyValueTag : `${getValuePrecisionThousand( feeVolume, feeTokenInfo?.precision, feeTokenInfo?.precision, feeTokenInfo?.precision, false, { isTrade: false, floor: false }, )} ${feeTokenInfo.symbol}` const { updatedAt: time } = row let timeString if (typeof time === 'undefined') { timeString = EmptyValueTag } else { timeString = moment(new Date(time), 'YYYYMMDDHHMM').fromNow() } return ( {side}   {`${getValuePrecisionThousand( sellVolume, sellTokenInfo?.precision, sellTokenInfo?.precision, sellTokenInfo?.precision, false, { isTrade: false, floor: false }, )} ${sellTokenInfo.symbol}`}  {DirectionTag}   {`${getValuePrecisionThousand( buyVolume, buyTokenInfo?.precision, buyTokenInfo?.precision, buyTokenInfo?.precision, false, { isTrade: false, floor: false }, )} ${buyTokenInfo.symbol}`} {`Fee: ${renderFee}`} {timeString} ) }, }, ], [t, tokenMap, idIndex], ) // const [isDropDown, setIsDropDown] = React.useState(true); const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { // let filters: any = {}; updateData.cancel() updateData({ currPage: 1 }) // handlePageChange(1); // if (searchParams.get("types")) { // filters.type = searchParams.get("types"); // } // handleFilterChange(filters); return () => { updateData.cancel() } }, [pagination?.pageSize]) return (
    {pagination && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/defiTxsTable/Interface.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' export type RawDataDefiTxsItem = Partial export interface DefiTxsTableProps { // etherscanBaseUrl?: string; rawData: R[] pagination?: { pageSize: number total: number } idIndex: { [key: string]: string } tokenMap: { [key: string]: any } getDefiTxList: (props: any) => Promise // filterTokens: string[]; // showFilter?: boolean; showloading: boolean // accAddress: string; // accountId: number; } export interface RawDataDefiSideStakingItem extends sdk.StakeInfoOrigin, Omit { status_product: number } export interface DefiSideStakingTableProps { // etherscanBaseUrl?: string; rawData: R[] pagination?: { pageSize: number total: number } idIndex: { [key: string]: string } tokenMap: { [key: string]: any } geDefiSideStakingList: (props: any) => Promise showloading: boolean redeemItemClick: (item: R) => void hideAssets?: boolean } export type RawDataDefiSideStakingTxItem = sdk.STACKING_TRANSACTIONS & { stakingType: string } export interface DefiSideStakingTxTableProps { // etherscanBaseUrl?: string; rawData: R[] pagination?: { pageSize: number total: number } idIndex: { [key: string]: string } tokenMap: { [key: string]: any } getSideStakingTxList: (props: any) => Promise showloading: boolean } ================================================ FILE: packages/component-lib/src/components/tableList/defiTxsTable/components/ActionMemo.tsx ================================================ import React from 'react' import { Box, Grid, ListItemText, MenuItem } from '@mui/material' import styled from '@emotion/styled' import { Button, Popover, PopoverType, PopoverWrapProps } from '../../../basic-lib' import { MoreIcon } from '@loopring-web/common-resources' import { useSettings } from '../../../../stores' import { RawDataDefiSideStakingItem } from '../Interface' import { useTranslation } from 'react-i18next' const GridStyled = styled(Grid)` .MuiGrid-item { padding: ${({ theme }) => theme.unit / 4}px 0 0; } ` export type ActionProps = { item: R redeemItemClick: (item: R) => void onDetailClick: (item: R) => void } const ActionPopContent = React.memo( ({ redeemItemClick, onDetailClick, item, }: ActionProps) => { const { t } = useTranslation(['tables', 'common']) const { isMobile } = useSettings() const tradeList = [ onDetailClick(item)}> {t('labelDefiStakingDetail', { ns: 'tables' })} , redeemItemClick(item)}> {t('labelDefiStakingRedeem', { ns: 'tables' })} , ] return ( {isMobile && tradeList.map((item, index) => {item})} ) }, ) as (props: ActionProps) => JSX.Element const ActionMemo = React.memo((props: ActionProps) => { const { isMobile } = useSettings() const { t } = useTranslation(['tables', 'common']) const { redeemItemClick, onDetailClick, item } = props const popoverProps: PopoverWrapProps = { type: PopoverType.click, popupId: 'testPopup', className: 'arrow-none', children: , popoverContent: , anchorOrigin: { vertical: 'bottom', horizontal: 'right', }, transformOrigin: { vertical: 'top', horizontal: 'right', }, } as PopoverWrapProps return ( {isMobile ? ( <> ) : ( <> )} ) }) as (props: ActionProps) => JSX.Element export default ActionMemo ================================================ FILE: packages/component-lib/src/components/tableList/defiTxsTable/index.ts ================================================ export * from './DefiTxsTable' export * from './Interface' export * from './DefiStakingTable' export * from './DefiStakingTxTable' ================================================ FILE: packages/component-lib/src/components/tableList/dualTable/DualTable.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { Button, Column, Table } from '../../basic-lib' import { Box, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { RawDataDualsItem } from './Interface' import { EmptyValueTag, ForexMap, getValuePrecisionThousand, RowDualInvestConfig, UpColor, UpIcon, YEAR_DAY_FORMAT, } from '@loopring-web/common-resources' import { useHistory } from 'react-router-dom' import moment from 'moment/moment' import * as sdk from '@loopring-web/loopring-sdk' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` const TableStyled = styled(Table)<{ isMobile: boolean }>` &.rdg { --template-columns: ${({ isMobile }: any) => isMobile ? '20% 44% auto !important' : '18% auto 18% 18% 20% !important'}; height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight) { return props.currentheight + 'px' } else { return '100%' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export interface DualsTableProps { rawData: R[] showloading: boolean forexMap: ForexMap onItemClick: (item: R) => void } const ButtonStyled = styled(Button)` & { border-color: var(--color-primary); color: var(--color-primary); font-size: 16px; height: ${({ theme }) => 5 * theme.unit}px; padding-left: ${({ theme }) => 2.5 * theme.unit}px; padding-right: ${({ theme }) => 2.5 * theme.unit}px; } ` export const DualTable = withTranslation(['tables', 'common'])( (props: DualsTableProps & WithTranslation) => { const { rawData, showloading, onItemClick, t } = props const { isMobile, upColor } = useSettings() const history = useHistory() const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Apy', sortable: true, cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelDualApy'), formatter: ({ row }: FormatterProps) => { return {row?.apy ?? EmptyValueTag} }, }, { key: 'targetPrice', sortable: true, name: t('labelDualPrice'), formatter: ({ row }: FormatterProps) => { const [_upColor, _downColor] = upColor == UpColor.green ? ['var(--color-success)', 'var(--color-error)'] : ['var(--color-error)', 'var(--color-success)'] return ( {row.strike + ' ' + (row.currentPrice?.quoteUnit ?? row?.currentPrice?.quote)} {row.settleRatio ? getValuePrecisionThousand( sdk .toBig(row.strike ?? 0) .minus(row.currentPrice?.currentPrice ?? 0) .div(row.currentPrice?.currentPrice ?? 1) .times(100) .abs(), 2, 2, 2, true, ) + '%' : EmptyValueTag} ) }, }, { key: 'Term', sortable: true, name: t('labelDualTerm'), formatter: ({ row }: FormatterProps) => { return {row.term} }, }, { key: 'Settlement', sortable: true, name: t('labelDualSettlement'), formatter: ({ row }: FormatterProps) => { return ( {moment(new Date(row.expireTime)).format(YEAR_DAY_FORMAT)} ) }, }, { key: 'Action', sortable: false, cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDualAction'), formatter: ({ row }: FormatterProps) => { return ( { onItemClick(row) }} > {t('labelInvestBtn', { ns: 'common' })} ) }, }, ], [history, upColor, t], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'Apy', sortable: true, cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelDualApy'), formatter: ({ row }: FormatterProps) => { return {row?.apy ?? EmptyValueTag} }, }, { key: 'targetPrice', sortable: true, name: t('labelDualPrice'), formatter: ({ row }: FormatterProps) => { const [_upColor, _downColor] = upColor == UpColor.green ? ['var(--color-success)', 'var(--color-error)'] : ['var(--color-error)', 'var(--color-success)'] return ( {row.strike + ' ' + (row.currentPrice?.quoteUnit ?? row?.currentPrice?.quote)} {row.settleRatio ? getValuePrecisionThousand( sdk .toBig(row.strike ?? 0) .minus(row.currentPrice?.currentPrice ?? 0) .div(row.currentPrice?.currentPrice ?? 1) .times(100) .abs(), 2, 2, 2, true, ) + '%' : EmptyValueTag} ) }, }, { key: 'Settlement', sortable: true, cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDualSettlement'), formatter: ({ row }: FormatterProps) => { return <>{moment(new Date(row.expireTime)).format(YEAR_DAY_FORMAT)} }, }, ], [t], ) const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } const sortMethod = React.useCallback( (_sortedRows, sortColumn) => { let _rawData: R[] = [] switch (sortColumn) { case 'Apy': _rawData = rawData.sort((a, b) => { const replaced = new RegExp(`[\\${sdk.SEP},%]`, 'ig') const valueA = a.apy?.replace(replaced, '') ?? 0 const valueB = b.apy?.replace(replaced, '') ?? 0 return Number(valueB) - Number(valueA) //.localeCompare(valueA); }) // default; break case 'targetPrice': _rawData = rawData.sort((a, b) => { const replaced = new RegExp(`\\${sdk.SEP}`, 'ig') const valueA = a.strike?.replace(replaced, '') ?? 0 const valueB = b.strike?.replace(replaced, '') ?? 0 return Number(valueB) - Number(valueA) //.loc }) break case 'Settlement': case 'Term': _rawData = rawData.sort((a, b) => { return b.expireTime - a.expireTime }) break default: _rawData = rawData } // resetTableData(_rawData) return _rawData }, [rawData], ) return ( 0 ? RowDualInvestConfig.rowHeaderHeight + rawData.length * RowDualInvestConfig.rowHeight : RowDualInvestConfig.minHeight } rowHeight={RowDualInvestConfig.rowHeight} headerRowHeight={RowDualInvestConfig.rowHeaderHeight} onRowClick={(_index: number, row: R) => { onItemClick(row) }} sortMethod={sortMethod} {...{ ...defaultArgs, // rowRenderer: RowRenderer, ...props, rawData, showloading, }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/dualTable/Interface.ts ================================================ import { DualViewInfo } from '@loopring-web/common-resources' export type RawDataDualsItem = DualViewInfo ================================================ FILE: packages/component-lib/src/components/tableList/dualTable/index.ts ================================================ export * from './DualTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/dualTxsTable/DualAssetTable.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { AlertIcon, ClockIcon, EmptyValueTag, getValuePrecisionThousand, globalSetup, HiddenTag, MoreIcon, TokenType, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Link, Tooltip, Typography } from '@mui/material' import moment from 'moment' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { DualAssetTableProps, RawDataDualAssetItem } from './Interface' import { CoinIcons } from '../assetsTable' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import { DUAL_TYPE } from '@loopring-web/loopring-sdk' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 16% 15% 20% 105px 14% 80px 110px 90px!important` : `--template-columns: 16% 30% 44% 10% !important;`} } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (prosp: BoxProps & { isMobile: boolean }) => JSX.Element const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { return props.currentheight + 'px' }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .logo-icon.dual:last-child { transform: scale(0.6) translate(0, 4px); } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export const DualAssetTable = withTranslation(['tables', 'common'])( (props: DualAssetTableProps & WithTranslation) => { const { rawData, pagination, getDualAssetList, getDetail, dualMarketMap, showloading, showDetail, refresh, cancelReInvest, hideAssets, rowConfig, noMinHeight, t, } = props const { isMobile, coinJson } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getDualAssetList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnMode = React.useCallback( (): Column[] => [ { key: 'Product', sortable: false, width: 'auto', name: t('labelDualAssetProduct'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }: FormatterProps) => { const { sellSymbol, buySymbol, __raw__: { order: { dualType, // investmentStatus, settlementStatus, dualReinvestInfo, }, }, } = row const [base, quote] = dualType === DUAL_TYPE.DUAL_BASE ? [sellSymbol, buySymbol] : [buySymbol, sellSymbol] const showClock = dualReinvestInfo?.isRecursive && settlementStatus?.toUpperCase() == sdk.SETTLEMENT_STATUS.PAID && dualReinvestInfo?.retryStatus?.toUpperCase() === sdk.DUAL_RETRY_STATUS.RETRYING return ( {showClock ? ( {`${base}/${quote}`} ) : ( {`${base}/${quote}`} )} ) }, }, { key: 'Frozen', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDualAssetFrozen'), formatter: ({ row }: FormatterProps) => { if (hideAssets) { return <>{HiddenTag} } else if (!row?.amount) { return <>{'-- ' + row.sellSymbol} } else { return <>{row?.amount + ' ' + row.sellSymbol} } }, }, { key: 'Return', sortable: false, width: 'auto', name: t('labelDualAssetReturn'), cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', formatter: ({ row }: FormatterProps) => { const { currentPrice, lessEarnView, greaterEarnView } = getDetail(row) const { base, quote } = currentPrice return ( <> {hideAssets ? HiddenTag : (lessEarnView === '0' ? EmptyValueTag : lessEarnView) + ' ' + base + '/' + (greaterEarnView === '0' ? EmptyValueTag : greaterEarnView) + ' ' + quote} ) }, }, { key: 'Price', sortable: false, width: 'auto', name: t('labelDualAssetPrice'), cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', formatter: ({ row }: FormatterProps) => { return <>{row?.strike} }, }, { key: 'Settlement_Date', sortable: false, width: 'auto', name: t('labelDualAssetSettlement_Date'), cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', formatter: ({ row }: FormatterProps) => { return <>{moment(new Date(row.expireTime)).format(YEAR_DAY_MINUTE_FORMAT)} }, }, { key: 'APR', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDualAssetAPR'), formatter: ({ row }: FormatterProps) => { return <>{row?.apy} }, }, { key: 'Auto', sortable: false, name: t('labelDualAutoReinvest'), formatter: ({ row }: FormatterProps) => { return row?.__raw__.order?.dualReinvestInfo?.isRecursive ? ( { cancelReInvest(row) }} > {t('labelDualAssetReInvestEnable')} ) : ( <>{t('labelDualAssetReInvestDisable')} ) }, }, { key: 'Action', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDualAssetAction'), formatter: ({ row }: FormatterProps) => { const investmentStatus = row.__raw__.order.investmentStatus const showRefresh = investmentStatus === sdk.LABEL_INVESTMENT_STATUS.PROCESSING const dualType = row.__raw__.order?.dualType const isRecursive = row.__raw__.order?.dualReinvestInfo?.isRecursive const newStrike = row.__raw__.order?.dualReinvestInfo?.newStrike const { currentPrice, precisionForPrice, base, currency } = row.__raw__?.currentPrice const currentView = currentPrice ? getValuePrecisionThousand( currentPrice, precisionForPrice ?? 6, precisionForPrice ?? 6, undefined, false, ) : EmptyValueTag let showAlert = false if (isRecursive && dualType == DUAL_TYPE.DUAL_BASE) { showAlert = sdk .toBig(currentPrice ?? 0) .div(newStrike) .minus(1) .lte(-0.05) } else { showAlert = sdk .toBig(currentPrice ?? 0) .div(newStrike) .minus(1) .gte(0.05) } return showRefresh ? ( { refresh(row) }} > {t('labelDualAssetRefresh')} ) : showAlert ? ( showDetail(row)}> {t('labelDualAssetDetail')} ) : ( showDetail(row)}>{t('labelDualAssetDetail')} ) }, }, ], [coinJson, t, hideAssets], ) const getColumnMobile = React.useCallback( (): Column[] => [ { key: 'Product', sortable: false, width: 'auto', name: t('labelDualAssetProduct'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }: FormatterProps) => { const { __raw__: { order: { dualType, investmentStatus, settlementStatus, dualReinvestInfo }, }, } = row // const inAuto = investmentStatus === sdk.LABEL_INVESTMENT_STATUS.PROCESSING const showClock = dualReinvestInfo?.isRecursive && settlementStatus?.toUpperCase() == sdk.SETTLEMENT_STATUS.PAID && dualReinvestInfo?.retryStatus?.toUpperCase() === sdk.DUAL_RETRY_STATUS.RETRYING return ( {showClock && ( )} ) }, }, { key: 'Price', sortable: false, width: 'auto', name: t('labelDualAssetPrice') + '/' + t('labelDualAssetSettlement_Date'), cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', formatter: ({ row }: FormatterProps) => { const dualType = row.__raw__.order?.dualType const newStrike = row.__raw__.order?.dualReinvestInfo?.newStrike const isRecursive = row.__raw__.order?.dualReinvestInfo?.isRecursive const { currentPrice, precisionForPrice, base, currency } = row.__raw__?.currentPrice ?? {} const currentView = currentPrice ? getValuePrecisionThousand( currentPrice, precisionForPrice ?? 6, precisionForPrice ?? 6, undefined, false, ) : EmptyValueTag let showAlert = false if (isRecursive && dualType == DUAL_TYPE.DUAL_BASE) { showAlert = sdk.toBig(currentPrice).div(newStrike).minus(1).lte(-0.05) } else { showAlert = sdk.toBig(currentPrice).div(newStrike).minus(1).gte(0.05) } return ( {showAlert ? ( {row?.strike} ) : ( {row?.strike} )} {moment(new Date(row.expireTime)).format(YEAR_DAY_MINUTE_FORMAT)} ) }, }, { key: 'APR', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelDualAssetAPR') + '/' + t('labelDualAssetFrozen'), formatter: ({ row }: FormatterProps) => { return ( <> {row?.apy} {hideAssets ? HiddenTag : row?.amount + ' ' + row.sellSymbol} ) }, }, { key: 'Action', sortable: false, cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: 'Action', formatter: ({ row }: FormatterProps) => { return showDetail(row)} cursor={'pointer'} /> }, }, ], [coinJson, t, hideAssets], ) const sortMethod = React.useCallback( (_sortedRows, sortColumn) => { let _dualList: R[] = [] switch (sortColumn) { case 'Settlement_Date': _dualList = rawData.sort((a, b) => { return b.expireTime - a.expireTime }) break case 'APR': _dualList = rawData.sort((a, b) => { const replaced = new RegExp(`[\\${sdk.SEP},%]`, 'ig') const valueA = a.apy?.replace(replaced, '') ?? 0 const valueB = b.apy?.replace(replaced, '') ?? 0 return Number(valueB) - Number(valueA) }) break default: _dualList = rawData } // resetTableData(_dualList) return _dualList }, [rawData], ) const defaultArgs: any = { columnMode: isMobile ? getColumnMobile() : getColumnMode(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { if (dualMarketMap) { updateData.cancel() updateData({ currPage: 1 }) } // let filters: any = {}; // handlePageChange(1); // if (searchParams.get("types")) { // filters.type = searchParams.get("types"); // } // handleFilterChange(filters); return () => { updateData.cancel() } }, [pagination?.pageSize, dualMarketMap]) return ( {pagination && pagination.total > pagination.pageSize && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/dualTxsTable/DualTxsTable.tsx ================================================ import _ from 'lodash' import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { CompleteIcon, WarningIcon, WaitingIcon, DAY_FORMAT, DirectionTag, EmptyValueTag, getValuePrecisionThousand, globalSetup, MINUTE_FORMAT, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Tooltip, Typography } from '@mui/material' import moment from 'moment' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { DualTxsTableProps, LABEL_INVESTMENT_STATUS_MAP, RawDataDualTxsItem } from './Interface' import * as sdk from '@loopring-web/loopring-sdk' import { DUAL_TYPE, LABEL_INVESTMENT_STATUS } from '@loopring-web/loopring-sdk' const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 30% auto 76px 120px 120px 160px 120px !important;` : `--template-columns: 100% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export const DualTxsTable = withTranslation(['tables', 'common'])( (props: DualTxsTableProps & WithTranslation) => { const { rawData, idIndex, pagination, tokenMap, getDualTxList, showloading, dualMarketMap, t } = props const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getDualTxList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { sortable: false, width: 'auto', key: 'DualTxsSide', name: t('labelDualTxsSide'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }: FormatterProps) => { const { __raw__: { order: { settlementStatus, tokenInfoOrigin: { amountIn, tokenOut, amountOut }, timeOrigin: { expireTime }, investmentStatus, }, }, // expireTime, sellSymbol, } = row const sellAmount = sdk .toBig(amountIn ? amountIn : 0) .div('1e' + tokenMap[sellSymbol].decimals) const amount = getValuePrecisionThousand( sellAmount, tokenMap[sellSymbol].precision, tokenMap[sellSymbol].precision, tokenMap[sellSymbol].precision, false, ) const side = settlementStatus === sdk.SETTLEMENT_STATUS.PAID ? t(LABEL_INVESTMENT_STATUS_MAP.INVESTMENT_RECEIVED) : Date.now() - expireTime >= 0 && investmentStatus !== LABEL_INVESTMENT_STATUS.CANCELLED && investmentStatus !== LABEL_INVESTMENT_STATUS.FAILED ? t(LABEL_INVESTMENT_STATUS_MAP.DELIVERING) : t(LABEL_INVESTMENT_STATUS_MAP.INVESTMENT_SUBSCRIBE) const statusColor = settlementStatus === sdk.SETTLEMENT_STATUS.PAID ? 'var(--color-tag)' : Date.now() - expireTime >= 0 && investmentStatus !== LABEL_INVESTMENT_STATUS.CANCELLED && investmentStatus !== LABEL_INVESTMENT_STATUS.FAILED ? 'var(--color-warning)' : 'var(--color-success)' let buySymbol, buyAmount if (tokenOut !== undefined) { buySymbol = tokenMap[idIndex[tokenOut]].symbol buyAmount = getValuePrecisionThousand( sdk.toBig(amountOut ? amountOut : 0).div('1e' + tokenMap[buySymbol].decimals), tokenMap[buySymbol].precision, tokenMap[buySymbol].precision, tokenMap[buySymbol].precision, false, ) } const sentence = settlementStatus === sdk.SETTLEMENT_STATUS.PAID ? `${amount} ${sellSymbol} ${DirectionTag} ${buyAmount} ${buySymbol} ` : Date.now() - expireTime >= 0 ? `${amount} ${sellSymbol}` : `${amount} ${sellSymbol}` const pending = ( {t('labelDualPending')} ) const failed = ( {t('labelDualFailed')} ) return ( {investmentStatus === sdk.LABEL_INVESTMENT_STATUS.FAILED || investmentStatus === sdk.LABEL_INVESTMENT_STATUS.CANCELLED ? ( failed ) : investmentStatus === sdk.LABEL_INVESTMENT_STATUS.PROCESSING ? ( pending ) : ( {side} )}    {sentence} ) }, }, { sortable: false, width: 'auto', key: 'Product', name: t('labelDualTxsProduct'), cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', formatter: ({ row }: FormatterProps) => { const { sellSymbol, buySymbol, __raw__: { order: { dualType }, }, } = row const [base, quote] = dualType === DUAL_TYPE.DUAL_BASE ? [sellSymbol, buySymbol] : [buySymbol, sellSymbol] return <>{base + '/' + quote} }, }, { sortable: false, width: 'auto', key: 'APR', name: t('labelDualTxAPR'), cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', formatter: ({ row }: FormatterProps) => { return <>{row?.apy} }, }, { sortable: false, width: 'auto', key: 'TargetPrice', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDualTxsTargetPrice'), formatter: ({ row }: FormatterProps) => { return <>{row?.strike} }, }, { sortable: false, width: 'auto', key: 'Price', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDualTxsSettlementPrice'), formatter: ({ row }: FormatterProps) => { const { __raw__: { order: { deliveryPrice, tokenInfoOrigin: { quote }, }, }, } = row return ( <> {deliveryPrice ? getValuePrecisionThousand( deliveryPrice, tokenMap[quote]?.precision, tokenMap[quote]?.precision, tokenMap[quote]?.precision, true, { isFait: true }, ) : EmptyValueTag} ) }, }, { sortable: false, width: 'auto', key: 'Settlement_Date', name: t('labelDualTxsSettlement_Date'), formatter: ({ row }: FormatterProps) => { return ( {moment(new Date(row.__raw__.order.timeOrigin.expireTime)).format( YEAR_DAY_MINUTE_FORMAT, )} ) }, }, { key: 'Auto', sortable: true, name: t('labelDualAutoReinvest'), formatter: ({ row }: FormatterProps) => { let icon: any = undefined, status = '' let content = '' const { __raw__: { order: { dualReinvestInfo, tokenInfoOrigin, settlementStatus }, }, } = row switch (dualReinvestInfo?.retryStatus) { case sdk.DUAL_RETRY_STATUS.RETRY_SUCCESS: icon = status = 'labelDualRetryStatusSuccess' content = 'labelDualRetrySuccess' break case sdk.DUAL_RETRY_STATUS.RETRY_FAILED: icon = status = 'labelDualRetryStatusError' content = 'labelDualRetryFailed' break case sdk.DUAL_RETRY_STATUS.NO_RETRY: if (dualReinvestInfo?.isRecursive) { content = 'labelDualAssetReInvestEnable' } else if ( dualReinvestInfo.onceRecursive && settlementStatus === sdk.SETTLEMENT_STATUS.PAID && tokenInfoOrigin.tokenOut !== tokenInfoOrigin.tokenIn ) { icon = status = 'labelDualRetryStatusTerminated' content = 'labelDualRetryTerminated' } else { content = 'labelDualAssetReInvestDisable' } break case sdk.DUAL_RETRY_STATUS.RETRYING: icon = status = 'labelDualRetryStatusRetrying' content = 'labelDualRetryPending' break default: content = dualReinvestInfo.isRecursive ? 'labelDualAssetReInvestEnable' : 'labelDualAssetReInvestDisable' } return icon ? ( <>{t(content)} <>{icon} ) : ( <>{t(content)} ) }, }, { key: 'time', name: t('labelDualTxsTime'), headerCellClass: 'textAlignRight', title: t('labelDualAutoInvestTip'), formatter: ({ row }) => { return ( {moment(new Date(row.__raw__.order?.createdAt), 'YYYYMMDDHHMM').fromNow()} ) }, }, ], [t, tokenMap, idIndex], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { sortable: false, width: 'auto', key: 'DualTxsSide', name: t('labelDualTxsSide'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }: FormatterProps) => { let icon: any = undefined, status = '', content const { sellSymbol, apy, buySymbol: _marketBuy, __raw__: { order: { settlementStatus, dualType, deliveryPrice, investmentStatus, tokenInfoOrigin: { amountIn, tokenOut, amountOut, tokenIn }, timeOrigin: { expireTime }, dualReinvestInfo, }, }, } = row const sellAmount = sdk .toBig(amountIn ? amountIn : 0) .div('1e' + tokenMap[sellSymbol].decimals) const amount = getValuePrecisionThousand( sellAmount, tokenMap[sellSymbol].precision, tokenMap[sellSymbol].precision, tokenMap[sellSymbol].precision, false, ) const side = settlementStatus === sdk.SETTLEMENT_STATUS.PAID ? t(LABEL_INVESTMENT_STATUS_MAP.INVESTMENT_RECEIVED) : Date.now() - expireTime >= 0 && investmentStatus !== LABEL_INVESTMENT_STATUS.CANCELLED && investmentStatus !== LABEL_INVESTMENT_STATUS.FAILED ? t(LABEL_INVESTMENT_STATUS_MAP.DELIVERING) : t(LABEL_INVESTMENT_STATUS_MAP.INVESTMENT_SUBSCRIBE) const statusColor = settlementStatus === sdk.SETTLEMENT_STATUS.PAID ? 'var(--color-tag)' : Date.now() - expireTime >= 0 && investmentStatus !== LABEL_INVESTMENT_STATUS.CANCELLED && investmentStatus !== LABEL_INVESTMENT_STATUS.FAILED ? 'var(--color-warning)' : 'var(--color-success)' let buySymbol, buyAmount if (tokenOut !== undefined) { buySymbol = tokenMap[idIndex[tokenOut]].symbol buyAmount = getValuePrecisionThousand( sdk.toBig(amountOut ? amountOut : 0).div('1e' + tokenMap[buySymbol].decimals), tokenMap[buySymbol].precision, tokenMap[buySymbol].precision, tokenMap[buySymbol].precision, false, ) } const sentence = settlementStatus === sdk.SETTLEMENT_STATUS.PAID ? `${amount} ${sellSymbol} ${DirectionTag} ${buyAmount} ${buySymbol} ` : Date.now() - expireTime >= 0 ? `${amount} ${sellSymbol}` : `${amount} ${sellSymbol}` const [base, quote] = dualType === DUAL_TYPE.DUAL_BASE ? [sellSymbol, _marketBuy] : [_marketBuy, sellSymbol] switch (dualReinvestInfo?.retryStatus) { case sdk.DUAL_RETRY_STATUS.RETRY_SUCCESS: icon = status = 'labelDualRetryStatusSuccess' content = 'labelDualRetrySuccess' break case sdk.DUAL_RETRY_STATUS.RETRY_FAILED: icon = status = 'labelDualRetryStatusError' content = 'labelDualRetryFailed' break case sdk.DUAL_RETRY_STATUS.NO_RETRY: if (dualReinvestInfo?.isRecursive) { content = 'labelDualAssetReInvestEnable' } else if ( dualReinvestInfo.onceRecursive && settlementStatus === sdk.SETTLEMENT_STATUS.PAID && tokenIn !== tokenOut ) { icon = status = 'labelDualRetryStatusTerminated' content = 'labelDualRetryTerminated' } else { content = 'labelDualAssetReInvestDisable' } break case sdk.DUAL_RETRY_STATUS.RETRYING: icon = status = 'labelDualRetryStatusRetrying' content = 'labelDualRetryPending' break default: content = dualReinvestInfo.isRecursive ? 'labelDualAssetReInvestEnable' : 'labelDualAssetReInvestDisable' } const recursiveStatus = icon ? ( <>{content} <>{icon} ) : ( <> ) const pending = ( {t('labelDualPending')} ) const failed = ( {t('labelDualFailed')} ) return ( {investmentStatus === sdk.LABEL_INVESTMENT_STATUS.FAILED || investmentStatus === sdk.LABEL_INVESTMENT_STATUS.CANCELLED ? ( failed ) : investmentStatus === sdk.LABEL_INVESTMENT_STATUS.PROCESSING ? ( pending ) : ( {side} )}   {sentence} APY: {apy} {row?.__raw__.order?.dualReinvestInfo?.isRecursive || row?.__raw__.order?.dualReinvestInfo?.retryStatus == sdk.DUAL_RETRY_STATUS.RETRY_SUCCESS ? t('labelDualAssetReInvestYes') : t('labelDualAssetReInvestNo')} {recursiveStatus} {base + '/' + quote} {` ${t('labelDualTxsSettlement')}: ${ deliveryPrice ? getValuePrecisionThousand( deliveryPrice, tokenMap[quote]?.precision, tokenMap[quote]?.precision, tokenMap[quote]?.precision, true, { isFait: true }, ) : EmptyValueTag } (${t('labelDualTPrice')} : ${row?.strike}) ${moment(new Date(row.__raw__.order.timeOrigin.expireTime)).format( `${DAY_FORMAT} ${MINUTE_FORMAT}`, )} `} ) }, }, ], [t, tokenMap, idIndex], ) // const [isDropDown, setIsDropDown] = React.useState(true); const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { if (dualMarketMap) { updateData.cancel() updateData({ currPage: 1 }) } return () => { updateData.cancel() } }, [pagination?.pageSize, dualMarketMap]) return (
    {pagination && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/dualTxsTable/Interface.ts ================================================ import { DualCurrentPrice, DualViewOrder, RowConfig, RowConfigType, } from '@loopring-web/common-resources' import { DualDetailType } from '../../tradePanel' export type RawDataDualTxsItem = DualViewOrder & { amount: string } export type RawDataDualAssetItem = DualViewOrder & { amount: string currentPrice: DualCurrentPrice } export interface DualAssetTableProps { rawData: R[] getDetail: (item: R) => DualDetailType dualMarketMap: any idIndex: { [key: string]: string } tokenMap: { [key: string]: any } showloading: boolean getDualAssetList: (props: any) => Promise showDetail: (item: R) => void cancelReInvest: (item: R) => void refresh: (item: R) => void hideAssets?: boolean pagination?: { pageSize: number total: number } getProduct: (item: R) => void rowConfig?: typeof RowConfig noMinHeight?: boolean } export interface DualTxsTableProps { rawData: R[] pagination?: { pageSize: number total: number } idIndex: { [key: string]: string } tokenMap: { [key: string]: any } dualMarketMap: any getDualTxList: (props: any) => Promise showloading: boolean } export enum LABEL_INVESTMENT_STATUS_MAP { INVESTMENT_RECEIVED = 'labelInvestmentStatusSettled', DELIVERING = 'labelInvestmentStatusDelivering', INVESTMENT_SUBSCRIBE = 'labelInvestmentStatusSubscribe', } ================================================ FILE: packages/component-lib/src/components/tableList/dualTxsTable/index.ts ================================================ export * from './DualTxsTable' export * from './DualAssetTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/index.ts ================================================ export * from './QuoteTable' export * from './orderHistoryTable' export * from './transactionsTable' export * from './transactionsTable/Interface' export * from './assetsTable' export * from './tradeTable' export * from './tradeRroTable' export * from './poolsTable' export * from './ammRecordTable' export * from './myPoolTable' export * from './ammTable' export * from './tradeRaceTable' export * from './nftTable' export * from './investOverviewTable' export * from './defiTxsTable' export * from './tradeNFTTable' export * from './dualTxsTable' export * from './dualTable' export * from './tradeRaceTable' export * from './redPacketTable' export * from './btradeSwapTable' export * from './rewardTable' export * from './vaultTable' export { TaikoTarmingTxRecordsTable } from './taikoFarmingTable' ================================================ FILE: packages/component-lib/src/components/tableList/investOverviewTable/Interface.ts ================================================ import { CoinInfo, InvestItem, RowConfig } from '@loopring-web/common-resources' import { TokenInfo, XOR } from '@loopring-web/loopring-sdk' export type DepartmentRow = Required }> export type RowInvest = DepartmentRow & { isExpanded?: boolean children?: InvestItem[] } export enum SubRowAction { ToggleSubRow = 'toggleSubRow', UpdateRaw = 'updateRaw', SortRow = 'sortRow', } export interface InvestRowAction { type: SubRowAction symbol?: string sortColumn?: string _des?: 'DESC' | 'ASC' | undefined rows?: R[] } type FilterExtend = { showFilter: boolean filterValue: string getFilteredData: (filterValue: string) => void } export type InvestOverviewTableProps = { rawData: R[] wait?: number coinJson: any showLoading?: boolean rowConfig?: typeof RowConfig } & XOR export enum ColumnKey { TYPE = 'TYPE', APR = 'APR', DURATION = 'DURATION', ACTION = 'ACTION', } ================================================ FILE: packages/component-lib/src/components/tableList/investOverviewTable/InvestOverviewTable.tsx ================================================ import React from 'react' import { useTranslation } from 'react-i18next' import { Button, CoinIcon, Column, Table } from '../../basic-lib' import { EmptyValueTag, getValuePrecisionThousand, InvestAssetRouter, InvestDuration, InvestMapType, RouterPath, RowConfig, TokenType, } from '@loopring-web/common-resources' import { Box, BoxProps, Link, Typography } from '@mui/material' import { ColumnKey, InvestOverviewTableProps, RowInvest, SubRowAction } from './Interface' import styled from '@emotion/styled' import { useHistory } from 'react-router-dom' import { TokenInfo } from '@loopring-web/loopring-sdk' import { TableFilterStyled, TablePaddingX } from '../../styled' import { investRowReducer } from './components/expends' import { Filter } from './components/Filter' import { DropdownIconStyled } from '../../tradePanel' import { useSettings } from '../../../stores' import { InvestColumnKey } from './index' import { ColumnCoinDeep } from '../assetsTable' const TableStyled = styled(Box)<{ isMobile?: boolean; hasContent?: boolean } & BoxProps>` & .rdg.rdg { ${({ hasContent }) => (hasContent ? `min-height:initial;` : ``)} // border-radius: ${({ theme }) => theme.unit}px; ${({ isMobile }) => !isMobile ? `--template-columns: 320px auto auto 124px !important;` : ` --template-columns: 46% auto auto !important; `} .rdg-cell.action { display: flex; justify-content: flex-end; align-items: center; } & > .rdg-row { border-top: 1px solid var(--color-box-hover); } & > .rdg-row.child_row { border-top: none; .rdg-cell:first-of-type { margin-left: ${({ theme }) => 2 * theme.unit}px; } &:hover { background-color: transparent; } } & > .rdg-row.child_row:first-of-type { border-top: 0px solid var(--color-box-hover); } .rdg-row.expends { background-color: var(--color-pop-bg); } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean; hasContent?: boolean } & BoxProps) => JSX.Element export const InvestOverviewTable = ({ rawData, wait, coinJson, filterValue, getFilteredData, showLoading, showFilter, rowConfig = RowConfig, ...rest }: InvestOverviewTableProps) => { const { t, i18n } = useTranslation(['tables', 'common']) const [rows, dispatch] = React.useReducer(investRowReducer, rawData) const history = useHistory() React.useEffect(() => { if (rawData.length) { dispatch({ rows: rawData, type: SubRowAction.UpdateRaw, }) } }, [rawData]) // myLog("Overview", rows); const handleFilterChange = React.useCallback( ({ searchValue }: any) => { if (getFilteredData) { getFilteredData(searchValue) } }, [getFilteredData], ) const tableHeight = React.useMemo(() => { return rows.length > 0 ? (rows.length + 1) * rowConfig.rowHeight : 350 }, [rows.length, rowConfig]) const [isDropDown, setIsDropDown] = React.useState(true) const getColumnMode = (): Column[] => [ { key: ColumnKey.TYPE, sortable: false, name: t('labelToken'), formatter: ({ row }) => { switch (row.type) { case InvestMapType.Token: const token = row.coinInfo return default: return ( {t(row.i18nKey, { ns: 'common' })} ) } }, }, { key: ColumnKey.APR, sortable: false, name: t('labelAPR'), width: 'auto', maxWidth: 80, cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeftSortable', formatter: ({ row }) => { const [start, end] = row.apr // myLog("end", end); return ( {end === 0 && start === 0 ? EmptyValueTag : start === end ? getValuePrecisionThousand(end, 2, 2, 2, true) + '%' : end === 0 || start === 0 ? getValuePrecisionThousand(end ? end : start, 2, 2, 2, true) + '%' : getValuePrecisionThousand(start, 2, 2, 2, true) + '% - ' + getValuePrecisionThousand(end, 2, 2, 2, true) + '%'} ) }, }, { key: ColumnKey.DURATION, sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDuration'), formatter: ({ row }) => { const label = (row.duration ?? '').split('|') return ( {row.durationType === InvestDuration.Duration ? `${t(label[0], { ns: 'common', arg: label[1] })}` : t('labelInvest' + row.durationType, { ns: 'common' })} ) // ${t("labelDay", { // ns: "common", // })} }, }, { key: ColumnKey.ACTION, name: t('labelActions'), headerCellClass: 'textAlignRight', cellClass: 'action', formatter: ({ row }) => { switch (row.type) { case InvestMapType.Token: return ( ) default: return ( ) } }, }, ] const getColumnMobileMode = (): Column[] => [ { key: ColumnKey.TYPE, sortable: false, name: t('labelToken'), formatter: ({ row }) => { switch (row.type) { case InvestMapType.Token: const token: TokenInfo = row.token return ( {token?.symbol && } {token?.symbol} {token?.name} ) default: return ( {t(row.i18nKey, { ns: 'common' })} ) } }, }, { key: ColumnKey.APR, sortable: false, name: t('labelAPR'), width: 'auto', maxWidth: 80, cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeftSortable', formatter: ({ row }) => { const [start, end] = row.apr // myLog("end", end); return ( {end === 0 && start === 0 ? EmptyValueTag : start === end ? getValuePrecisionThousand(end, 2, 2, 2, true) + '%' : end === 0 || start === 0 ? getValuePrecisionThousand(end ? end : start, 2, 2, 2, true) + '%' : getValuePrecisionThousand(start, 2, 2, 2, true) + '% - ' + getValuePrecisionThousand(end, 2, 2, 2, true) + '%'} ) }, }, { key: ColumnKey.DURATION, sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelDuration'), formatter: ({ row }) => { const label = (row.duration ?? '').split('|') return ( {row.durationType === InvestDuration.Duration ? `${t(label[0], { ns: 'common', arg: label[1] })}` : t('labelInvest' + row.durationType, { ns: 'common' })} ) // ${t("labelDay", { // ns: "common", // })} }, }, ] const { isMobile } = useSettings() return ( 0 ? true : false}> {showFilter && (isMobile && isDropDown ? ( setIsDropDown(false)} > {t('labelShowFilter')} ) : ( {t('labelTitleOverviewAllPrd', { ns: 'common' })} ))}
    { if ( row.type !== InvestMapType.Token // row.type === InvestMapType.STAKE || // row.type === InvestMapType.AMM || row.type === InvestMapType.AMM ) { return 'child_row' } if (row.isExpanded) { return 'expends' } return '' }} onRowClick={(_, row) => { if (row.children) { dispatch({ symbol: row.token.symbol, type: SubRowAction.ToggleSubRow, }) } }} style={{ height: tableHeight }} rowHeight={rowConfig.rowHeight} headerRowHeight={rowConfig.rowHeaderHeight} rawData={rows} // sortMethod={sortMethod} generateRows={(rowData: any) => rowData} generateColumns={({ columnsRaw }: any) => columnsRaw as Column[]} sortDefaultKey={InvestColumnKey.APR} columnMode={isMobile ? getColumnMobileMode() : getColumnMode()} /> ) } ================================================ FILE: packages/component-lib/src/components/tableList/investOverviewTable/components/Filter.tsx ================================================ import { Grid } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { InputSearch } from '../../../' export interface FilterProps { // hideInvestToken: boolean; // hideSmallBalances: boolean; // setHideLpToken: (value: boolean) => void; // setHideSmallBalances: (value: boolean) => void; searchValue: string handleFilterChange: (props: { searchValue: string }) => void } export enum CheckboxType { smallBalance = 'smallBalance', invest = 'invest', } export const Filter = withTranslation('tables', { withRef: true })( ({ // t, handleFilterChange, searchValue, }: // hideInvestToken, // hideSmallBalances, // // setHideLpToken, // setHideSmallBalances, FilterProps & WithTranslation) => { // myLog(searchValue, "searchValue"); return ( { handleFilterChange({ searchValue: value }) }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/investOverviewTable/components/expends.tsx ================================================ import { DepartmentRow, InvestRowAction, RowInvest, SubRowAction } from '../Interface' import { InvestColumnKey } from '../index' function toggleSubRow(rows: RowInvest[], symbol?: string): RowInvest[] { if (rows.length) { let rowIndex = rows.findIndex((r) => r?.token?.symbol === symbol) if (rowIndex === -1) { rowIndex = 0 } const row = rows[rowIndex] const { children } = row if (!children) return rows const newRows = [...rows] newRows[rowIndex] = { ...row, isExpanded: !row.isExpanded } if (!row.isExpanded) { // @ts-ignore newRows.splice(rowIndex + 1, 0, ...children) } else { newRows.splice(rowIndex + 1, children.length) } return newRows } else { return [] } } function updateRow(_oldRows: RowInvest[], rows: RowInvest[]): RowInvest[] { return [...rows] } export const sortMethod = ( sortedRows: any[], sortColumn: string, _des: 'DESC' | 'ASC' | undefined, ) => { let _rawData = [...sortedRows] switch (sortColumn) { case InvestColumnKey.TYPE: _rawData = sortedRows.sort((a, b) => { const valueA = a.token.symbol const valueB = b.token.symbol return valueB.localeCompare(valueA) }) break case InvestColumnKey.APR: _rawData = sortedRows.sort((a, b) => { return Number(b.apr[1] ?? 0) - Number(a.apr[1] ?? 0) // myLog("a.apr[1]", a.apr[1]); }) break default: break } // resetTableData(_rawData) return _rawData } export function investRowReducer( _rows: DepartmentRow[], { type, symbol, rows, sortColumn, _des }: InvestRowAction | any, ): DepartmentRow[] { switch (type) { case SubRowAction.ToggleSubRow: return toggleSubRow(_rows, symbol) case SubRowAction.UpdateRaw: return updateRow(_rows, rows ?? []) case SubRowAction.SortRow: return sortMethod(_rows, sortColumn, _des) // case "deleteSubRow": // return deleteSubRow(rows, id); default: return _rows } } ================================================ FILE: packages/component-lib/src/components/tableList/investOverviewTable/index.ts ================================================ import { ColumnKey as InvestColumnKey, DepartmentRow, InvestOverviewTableProps, RowInvest, } from './Interface' export * from './InvestOverviewTable' export type { InvestOverviewTableProps, RowInvest, DepartmentRow } export { InvestColumnKey } ================================================ FILE: packages/component-lib/src/components/tableList/myPoolTable/Interface.ts ================================================ import { Account, AmmDetail, CoinKey, ForexMap, MyAmmLP, RowConfig, } from '@loopring-web/common-resources' import { Currency, LoopringMap, TokenInfo } from '@loopring-web/loopring-sdk' import { EarningsDetail } from '../rewardTable' export type MyPoolRow = MyAmmLP & { ammDetail: AmmDetail } export type Method = { handleWithdraw: (row: R) => void handleDeposit: (row: R) => void allowTrade?: any } export type MyPoolTableProps = { rawData: R[] totalAMMClaims?: { detail: EarningsDetail[] totalDollar: string } account: Account title: string | (() => JSX.Element) | JSX.Element totalDollar?: string | number | undefined pagination?: { pageSize: number } filter: { searchValue: string } handleFilterChange: (props: { searchValue: string }) => void forexMap: ForexMap idIndex: { [key: string]: string } tokenMap: LoopringMap> }> tokenPrices: { [key in keyof R]: number } allowTrade?: any tableHeight?: number showFilter?: boolean hideSmallBalances?: boolean wait?: number showloading?: boolean currency?: Currency rowConfig?: typeof RowConfig setHideSmallBalances?: (value: boolean) => void hideAssets?: boolean totalAMMClaims?: { detail: EarningsDetail[] totalDollar: string } rewardsAPIError?: any getUserRewards?: () => void } & Method ================================================ FILE: packages/component-lib/src/components/tableList/myPoolTable/MyPoolTable.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { MyPoolRow as Row, MyPoolTable } from './index' import { coinMap } from '../../../static' import { CoinInfo } from '@loopring-web/common-resources' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` // @ts-ignore const rawData: Row[] = [ { feeA: 122, feeB: 21, feeU: 0.0012, reward: 123, rewardToken: coinMap['USDT'] as CoinInfo, feeA24: 122, feeB24: 122, feeU24: 0.0012, reward24: 123, reward224: 123, rewardU24: 123, extraU24: 123, extraRewards24: [ { tokenSymbol: 'LRC', amount: 100, }, ], balanceA: 12131, balanceB: 0.0012, balanceU: 232, balanceAStr: '', balanceBStr: '', ammDetail: { coinA: 'ETH', coinB: 'LRC', address: '', market: 'ETH-LRC', coinAInfo: coinMap['ETH'] as CoinInfo, coinBInfo: coinMap['LRC'] as CoinInfo, amountU: 12, totalLPToken: 12132131, totalA: 0.002, totalB: 12344, APR: 56, isNew: true, isActivity: false, }, }, { feeA: 122, feeB: 21, feeU: 0.0012, reward: 123, rewardToken: coinMap['USDT'] as CoinInfo, feeA24: 122, feeB24: 122, feeU24: 0.0012, reward24: 123, reward224: 123, rewardU24: 123, extraU24: 123, extraRewards24: [ { tokenSymbol: 'LRC', amount: 100, }, ], balanceA: 12131, balanceB: 0.0012, balanceU: 232, balanceAStr: '', balanceBStr: '', ammDetail: { coinA: 'ETH', coinB: 'LRC', address: '', market: 'ETH-LRC', coinAInfo: coinMap['ETH'] as CoinInfo, coinBInfo: coinMap['LRC'] as CoinInfo, totalLPToken: 12132131, totalA: 0.002, totalB: 12344, APR: 56, isNew: true, isActivity: false, }, }, { feeA: 122, feeB: 21, feeU: 0.0012, reward: 123, rewardToken: coinMap['USDT'] as CoinInfo, feeA24: 122, feeB24: 122, feeU24: 0.0012, reward24: 123, reward224: 123, rewardU24: 123, extraU24: 123, extraRewards24: [ { tokenSymbol: 'LRC', amount: 100, }, ], balanceA: 12131, balanceB: 0.0012, balanceU: 232, balanceAStr: '', balanceBStr: '', ammDetail: { coinA: 'ETH', coinB: 'LRC', address: '', market: 'ETH-LRC', coinAInfo: coinMap['ETH'] as CoinInfo, coinBInfo: coinMap['LRC'] as CoinInfo, // amountU: 12, totalLPToken: 12132131, totalA: 0.002, totalB: 12344, APR: 56, isNew: true, isActivity: false, }, }, ] export const Template: Story = (args: any) => { return ( <> ) } Template.bind({}) Template.args = { rawData: rawData, pagination: { pageSize: 5, }, } export default { title: 'components/TableList/MyPoolTable', component: MyPoolTable, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/myPoolTable/MyPoolTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Grid, Tooltip, Typography } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { Button, Popover, PopoverPure, PopoverType, PopoverWrapProps } from '../../basic-lib' import { Column, Table } from '../../basic-lib/' import { AssetTabIndex, CurrencyToTag, EmptyValueTag, getValuePrecisionThousand, HiddenTag, MoreIcon, PriceTag, RowConfig, TokenType, RouterPath, } from '@loopring-web/common-resources' import { MyPoolRow, MyPoolTableProps } from './Interface' import styled from '@emotion/styled' import { TableFilterStyled, TablePaddingX } from '../../styled' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { bindHover } from 'material-ui-popup-state/es' import { useSettings } from '../../../stores' import * as sdk from '@loopring-web/loopring-sdk' import { Filter } from './components/Filter' import { AmmAPRDetail, AmmPairDetail, AmmRewardsDetail } from '../../block' import { ActionPopContent, DetailRewardPanel } from './components/ActionPop' import { CoinIcons } from '../assetsTable' import { useHistory } from 'react-router-dom' const TableStyled = styled(Box)` .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 200px 80px auto auto 200px !important;` : `--template-columns: 16% 60% auto 8% !important;`} height: calc(86px * 5 + var(--header-row-height)); .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; } .textAlignRightSortable { display: flex; justify-content: flex-end; } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} .assetWrap & { .titleSummary.mobile { margin-top: ${({ theme }) => theme.unit * 5}px; flex-direction: row; } } .titleSummary.mobile { flex-direction: row; } ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element const PoolStyle = styled(Box)` &.MuiTypography-body1 { font-size: ${({ theme }) => theme.fontDefault.body1}; } .MuiButton-root:not(:first-of-type) { margin-left: ${({ theme }) => theme.unit}px; } ` as typeof Box export const MyPoolTable = withTranslation('tables')( >({ t, i18n, tReady, allowTrade, handleFilterChange, filter, totalDollar, showFilter = true, rawData, title, totalAMMClaims, handleWithdraw, handleDeposit, hideSmallBalances = false, setHideSmallBalances, currency = CurrencyToTag.USD, showloading, tableHeight, tokenMap, forexMap, rowConfig = RowConfig, hideAssets, rewardsAPIError, getUserRewards, }: MyPoolTableProps & WithTranslation) => { const { isMobile, coinJson } = useSettings() const history = useHistory() const getPopoverState = React.useCallback((label: string) => { return usePopupState({ variant: 'popover', popupId: `popup-poolsLiq-${label}`, }) }, []) const getPopoverRewardState = React.useCallback((label: string) => { return usePopupState({ variant: 'popover', popupId: `popup-poolsReward-${label}`, }) }, []) const getPopoverAprState = React.useCallback((label: string) => { return usePopupState({ variant: 'popover', popupId: `popup-poolsApr-${label}`, }) }, []) const columnMode = (): Column[] => { return [ { key: 'pools', sortable: false, width: 'auto', minWidth: 240, name: t('labelPool'), formatter: ({ row }) => { return ( {row.ammDetail?.coinAInfo?.simpleName} / {row.ammDetail?.coinBInfo?.simpleName} ) }, }, { key: 'APR', sortable: true, name: t('labelAPR'), width: 'auto', maxWidth: 80, headerCellClass: 'textAlignRightSortable', formatter: ({ row, rowIdx }) => { const APR = typeof row?.ammDetail?.APR !== undefined && row?.ammDetail?.APR ? row.ammDetail.APR : EmptyValueTag const popoverState = getPopoverAprState(rowIdx.toString()) return ( {APR === 0 || typeof APR === 'undefined' || APR == EmptyValueTag ? EmptyValueTag : getValuePrecisionThousand(APR, 2, 2, 2, true) + '%'} {!(APR === 0 || typeof APR === 'undefined' || APR == EmptyValueTag) && ( )} ) }, }, { key: 'liquidity', sortable: true, width: 'auto', headerCellClass: 'textAlignRightSortable', name: t('labelMyLiquidity'), formatter: ({ row, rowIdx }) => { const popState = getPopoverState(rowIdx.toString()) if (!row || !row.ammDetail) { return } const { // totalAmmValueDollar, balanceU, balanceA, balanceB, ammDetail: { coinAInfo, coinBInfo }, } = row as any // const coinAIcon: any = coinJson[coinAInfo.simpleName]; // const coinBIcon: any = coinJson[coinBInfo.simpleName]; return ( {typeof balanceU === 'undefined' ? EmptyValueTag : hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (balanceU || 0) * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: true }, )} ) }, }, { key: 'rewards', name: t('label24Rewards'), headerCellClass: 'textAlignRight', formatter: ({ row, rowIdx }) => { const { rewardToken, rewardToken2, reward24, reward224, extraRewards24, ammDetail: { coinAInfo, coinBInfo }, } = row let dollarReward24 = 0 const popState = getPopoverRewardState(rowIdx.toString()) if (!(typeof row.rewardU24 === 'undefined' || row.rewardU24 === 0)) { dollarReward24 += row.rewardU24 } if (!(typeof row.feeU24 === 'undefined' || row.feeU24 === 0)) { dollarReward24 += row.feeU24 } if (!(typeof row.extraU24 === 'undefined' || row.extraU24 === 0)) { dollarReward24 += row.extraU24 // dollarReward24 += row.feeU24; } return ( {dollarReward24 === 0 ? EmptyValueTag : hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (dollarReward24 || 0) * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: true }, )} {coinAInfo && ( )} ) }, }, { key: 'action', name: t('labelActions'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( {handleDeposit && ( )} {handleWithdraw && ( )} ) }, }, ] } const columnModeMobile = (): Column[] => { return [ { key: 'pools', sortable: false, width: 'auto', name: t('labelPool'), formatter: ({ row }) => { return ( {row.ammDetail?.coinAInfo?.simpleName} / {row.ammDetail?.coinBInfo?.simpleName} ) }, }, { key: 'liquidity', sortable: true, headerCellClass: 'textAlignRightSortable', name: t('labelMyLiquidity'), //+ "/" + t("labelFeeEarned") formatter: ({ row }) => { if (!row || !row.ammDetail) { return } const { balanceU, balanceA, balanceB, ammDetail: { coinAInfo, coinBInfo }, } = row as any return ( {typeof balanceU === 'undefined' ? EmptyValueTag : hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (balanceU || 0) * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: true }, )} {hideAssets ? HiddenTag + ` + ` + HiddenTag : getValuePrecisionThousand(balanceA, undefined, 2, 2, true, { isAbbreviate: true, abbreviate: 3, }) + ' ' + coinAInfo?.simpleName + ` + ` + getValuePrecisionThousand(balanceB, undefined, 2, 2, true, { isAbbreviate: true, abbreviate: 3, }) + ' ' + coinBInfo?.simpleName} ) }, }, { key: 'APR', sortable: true, name: t('labelAPR'), width: 'auto', maxWidth: 80, headerCellClass: 'textAlignRightSortable', formatter: ({ row }) => { const APR = typeof row?.ammDetail?.APR !== undefined && row.ammDetail.APR ? row.ammDetail.APR : EmptyValueTag return ( {APR === EmptyValueTag || typeof APR === 'undefined' ? EmptyValueTag : getValuePrecisionThousand(APR, 2, 2, 2, true) + '%'} ) }, }, { key: 'action', name: '', headerCellClass: 'textAlignRight', formatter: ({ row }) => { const popoverProps: PopoverWrapProps = { type: PopoverType.click, popupId: 'testPopup', className: 'arrow-none', children: , popoverContent: ( ), anchorOrigin: { vertical: 'bottom', horizontal: 'right', }, transformOrigin: { vertical: 'top', horizontal: 'right', }, } as PopoverWrapProps return ( ) }, }, ] } const nanToEmptyTag = (value: any, prefix: string) => { return value === 'NaN' ? EmptyValueTag : prefix + value } return ( 0 ? 'min-height' : ''}`}> { <>{title} {showFilter && ( )} } {totalDollar !== undefined ? ( {totalDollar ? hideAssets ? HiddenTag : nanToEmptyTag( getValuePrecisionThousand( sdk.toBig(totalDollar).times(forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: true }, ), PriceTag[CurrencyToTag[currency]], ) : EmptyValueTag} ) : ( '' )} {totalAMMClaims ? ( rewardsAPIError && getUserRewards ? ( ) : ( {t('labelAMMClaimableEarnings', { ns: 'common' })} {totalAMMClaims.totalDollar !== '0' ? ( <> } > {hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(totalAMMClaims.totalDollar).times(forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: true }, )} ) : ( {EmptyValueTag} )} ) ) : ( <> )}
    rawData} generateColumns={({ columnsRaw }) => columnsRaw as Column[]} sortMethod={(sortedRows: R[], sortColumn: string) => { switch (sortColumn) { case 'liquidity': sortedRows = sortedRows.sort((a, b) => { const valueA = a.balanceU const valueB = b.balanceU if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break case 'APR': sortedRows = sortedRows.sort((a, b) => { const valueA = a.ammDetail.APR || 0 const valueB = b.ammDetail.APR || 0 if (valueA && valueB) { return valueB - valueA } if (valueA && !valueB) { return -1 } if (!valueA && valueB) { return 1 } return 0 }) break default: return sortedRows } return sortedRows }} {...{ t, i18n, tReady, }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/myPoolTable/components/ActionPop.tsx ================================================ import React from 'react' import { Box, ListItemText, MenuItem, Typography } from '@mui/material' import { useTranslation } from 'react-i18next' import { EmptyValueTag, getValuePrecisionThousand, HiddenTag, myLog, TokenType, } from '@loopring-web/common-resources' import { EarningsDetail } from '../../rewardTable' import styled from '@emotion/styled' import { CoinIcons } from '../../assetsTable' import { useSettings } from '../../../../stores' export const ActionPopContent = React.memo( ({ row, allowTrade, handleWithdraw, handleDeposit, t }: any) => { return ( {allowTrade?.joinAmm?.enable && ( handleDeposit(row)}> {t('labelPoolTableAddLiquidity', { ns: 'tables' })} )} handleWithdraw(row)}> {t('labelPoolTableRemoveLiquidity', { ns: 'tables' })} ) }, ) const ContentWrapperStyled = styled(Box)` padding: 0 ${({ theme }) => theme.unit * 1}px; border-radius: ${({ theme }) => theme.unit / 2}px; ` export const DetailRewardPanel = ({ detailList, hideAssets = false, }: { detailList?: EarningsDetail[] hideAssets?: boolean }) => { const { t } = useTranslation() myLog('detailLis', detailList) const { coinJson } = useSettings() return ( {t(`labelClaimTypePROTOCOL_FEE`)} {detailList?.map((item) => { if (item.amount === '0') { return } else { return ( {item.token} {item.amount == '0' ? EmptyValueTag : hideAssets ? HiddenTag : getValuePrecisionThousand( item.amountStr, item.precision, item.precision, undefined, false, { floor: true, }, ) + ' ' + item.token} ) } })} ) } ================================================ FILE: packages/component-lib/src/components/tableList/myPoolTable/components/Filter.tsx ================================================ import { Box, Checkbox, FormControlLabel } from '@mui/material' import { InputSearch } from '../../../basic-lib' import { CheckBoxIcon, CheckedIcon } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../../stores' import { useTheme } from '@emotion/react' export interface FilterProps { hideSmallBalances: boolean setHideSmallBalances?: (value: boolean) => void filter: { searchValue: string } handleFilterChange: (props: { searchValue: string }) => void } export const Filter = withTranslation('tables', { withRef: true })( ({ t, handleFilterChange, filter, hideSmallBalances, setHideSmallBalances, }: FilterProps & WithTranslation) => { const { isMobile } = useSettings() const theme = useTheme() return ( {setHideSmallBalances && ( } icon={} color='default' onChange={(event) => { setHideSmallBalances(event.target.checked) }} /> } label={t('labelHideSmallBalances')} /> )} { handleFilterChange({ searchValue: value }) }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/myPoolTable/index.ts ================================================ export * from './MyPoolTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/nftTable/Interface.ts ================================================ // export enum TsNFTTradeTypes { // deposit = "DEPOSIT", // withdraw = "WITHDRAW", // transfer = "TRANSFER", // } import { UserNFTTxsHistory, UserNFTTxTypes } from '@loopring-web/loopring-sdk' import { DateRange } from '@mui/lab' export enum TsTradeStatus { processing = 'processing', processed = 'processed', received = 'received', failed = 'failed', } export enum TxnDetailStatus { processed = 'PROCESSED', processing = 'PROCESSING', received = 'RECEIVED', failed = 'FAILED', } export type TxnDetailProps = UserNFTTxsHistory & { metadata?: any } & { time: string fee: { unit: string value: number } storageInfo: { accountId: number storageId: number tokenId: number } blockId: number indexInBlock: number memo?: string createdAt: string nftData: string etherscanBaseUrl?: string } export type NFTTableFilter = { txType?: UserNFTTxTypes limit?: number duration?: DateRange page: number } export type NFTTableProps = NFTTableFilter & { etherscanBaseUrl?: string rawData: Row[] showFilter?: boolean pagination?: { pageSize: number total: number } getTxnList: (filter: NFTTableFilter) => Promise // showFilter?: boolean; showloading: boolean accAddress: string accountId: number // accAddress?: string; } ================================================ FILE: packages/component-lib/src/components/tableList/nftTable/TsNFTTable.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Box, BoxProps, Link, Typography } from '@mui/material' import { Trans, WithTranslation, withTranslation } from 'react-i18next' import moment from 'moment' import { BoxNFT, Column, NftImage, Table, TablePagination } from '../../basic-lib' import { CompleteIcon, DepositIcon, DirectionTag, EmptyValueTag, EXPLORE_TYPE, Explorer, getFormattedHash, getShortAddr, getValuePrecisionThousand, L1L2_NAME_DEFINED, MapChainId, MintIcon, RedPacketIcon, RowConfig, TransferIcon, WaitingIcon, WarningIcon, WithdrawIcon, } from '@loopring-web/common-resources' import { TableFilterStyled, TablePaddingX } from '../../styled' import { NFTTableFilter, NFTTableProps, TsTradeStatus, TxnDetailProps } from './Interface' import { Filter } from './components/Filter' import { ChainId, NFT_IMAGE_SIZES, TxNFTType } from '@loopring-web/loopring-sdk' import { useSettings } from '../../../stores' import { sanitize } from 'dompurify' const TYPE_COLOR_MAPPING = [ { type: TsTradeStatus.processed, color: 'success' }, { type: TsTradeStatus.processing, color: 'warning' }, { type: TsTradeStatus.received, color: 'warning' }, { type: TsTradeStatus.failed, color: 'error' }, ] const CellStatus = ({ row: { status } }: any) => { const RenderValue = styled.div` display: flex; align-items: center; color: ${({ theme }) => theme.colorBase[`${TYPE_COLOR_MAPPING.find((o) => o.type === status)?.color}`]}; & svg { width: 24px; height: 24px; } ` const svg = status === 'processed' ? ( ) : status === 'processing' || status === 'received' ? ( ) : ( ) const RenderValueWrapper = {svg} return RenderValueWrapper } const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => isMobile ? `--template-columns: 60% 40% !important;` : `--template-columns: 20% 15% 20% 15% 20% 10% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export const TsNFTTable = withTranslation(['tables', 'common'])( ({ accAddress, showFilter = true, rawData, page, pagination, txType, getTxnList, duration, showloading, etherscanBaseUrl, accountId, t, ...props }: NFTTableProps & WithTranslation) => { const [isDropDown, setIsDropDown] = React.useState(true) const { isMobile, defaultNetwork } = useSettings() const isTaiko = [ChainId.TAIKO, ChainId.TAIKOHEKLA].includes(defaultNetwork) const network = MapChainId[defaultNetwork] ?? MapChainId[1] const handleFilterChange = (filter: Partial) => { getTxnList({ page: filter.page ?? page, txType: filter.txType !== undefined ? // @ts-ignore filter.txType == 0 ? undefined : filter.txType : txType, duration: filter.duration ?? duration, }) } const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'side', name: t('labelTxSide'), formatter: ({ row }) => { return ( {row.metadata?.imageSize ? ( {row.metadata?.imageSize && ( undefined} src={row.metadata?.imageSize[NFT_IMAGE_SIZES.small]} /> )} ) : ( )} {t(`labelNFTType${TxNFTType[row.nftTxType]}`)} ) }, }, { key: 'amount', name: t('labelTxAmount'), headerCellClass: 'textAlignLeft', formatter: ({ row }: { row: Row }) => { const hasSymbol = row.nftTxType === TxNFTType[TxNFTType.TRANSFER] ? row?.receiverAddress?.toLowerCase().trim() === accAddress?.toLowerCase().trim() ? '+' : '-' : row.nftTxType === TxNFTType[TxNFTType.DEPOSIT] || row.nftTxType === TxNFTType[TxNFTType.MINT] ? '+' : row.nftTxType === TxNFTType[TxNFTType.WITHDRAW] ? '-' : // @ts-ignore row.nftTxType === TxNFTType[TxNFTType.SEND_LUCKY_TOKEN] ? '-' : // @ts-ignore row.nftTxType === TxNFTType[TxNFTType.SEND_BACK_LUCKY_TOKEN] ? '+' : // @ts-ignore row.nftTxType === TxNFTType[TxNFTType.WITHDRAW_LUCKY_TOKEN] ? '+' : '' return ( <> {hasSymbol} {row.amount ?? EmptyValueTag} {getFormattedHash(row.nftData)} ) }, }, { key: 'from', name: t('labelTxFrom'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { const receiverAddress = row.nftTxType === TxNFTType[TxNFTType.WITHDRAW] ? getShortAddr(row.withdrawalInfo.recipient, isMobile) : getShortAddr(row.receiverAddress, isMobile) const senderAddress = getShortAddr(row.senderAddress) const [from, to] = row.nftTxType === TxNFTType[TxNFTType.TRANSFER] ? row.receiverAddress?.toLowerCase().trim() === accAddress?.toLowerCase().trim() ? [senderAddress, `${L1L2_NAME_DEFINED[network].l2Symbol}`] : [`${L1L2_NAME_DEFINED[network].l2Symbol}`, receiverAddress] : row.nftTxType === TxNFTType[TxNFTType.DEPOSIT] ? [ `${L1L2_NAME_DEFINED[network].l1Symbol}`, `${L1L2_NAME_DEFINED[network].l2Symbol}`, ] : row.nftTxType === TxNFTType[TxNFTType.MINT] ? [ `${L1L2_NAME_DEFINED[network].l2Symbol} Mint`, `${L1L2_NAME_DEFINED[network].l2Symbol}`, ] : row.nftTxType === TxNFTType[TxNFTType.WITHDRAW] ? [`${L1L2_NAME_DEFINED[network].l2Symbol}`, receiverAddress] : // @ts-ignore row.nftTxType === TxNFTType[TxNFTType.SEND_BACK_LUCKY_TOKEN] ? [ `${L1L2_NAME_DEFINED[network].l2Symbol} Red Packet`, `${L1L2_NAME_DEFINED[network].l2Symbol}`, ] : // @ts-ignore row.nftTxType === TxNFTType[TxNFTType.SEND_LUCKY_TOKEN] ? [ `${L1L2_NAME_DEFINED[network].l2Symbol}`, `${L1L2_NAME_DEFINED[network].l2Symbol} Red Packet`, ] : // @ts-ignore row.nftTxType === TxNFTType[TxNFTType.WITHDRAW_LUCKY_TOKEN] ? [ `${L1L2_NAME_DEFINED[network].l2Symbol} Red Packet`, `${L1L2_NAME_DEFINED[network].l2Symbol}`, ] : ['', ''] const hash = row.txHash !== '' ? row.txHash : row.hash let path = row.txHash !== '' ? etherscanBaseUrl + `/tx/${row.txHash}` : Explorer + `tx/${row.hash}-${EXPLORE_TYPE['NFT' + row.nftTxType.toUpperCase()]}-${ row.storageInfo.accountId }-${row.storageInfo.tokenId}-${row.storageInfo.storageId}` return ( {from && to ? from + ` ${DirectionTag} ` + to : ''} ) }, }, { key: 'fee', name: t('labelTxNetworkFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const fee = row['fee'] ?? {} const renderValue = `${getValuePrecisionThousand( fee.value, undefined, undefined, undefined, false, { floor: false, isTrade: true, }, )} ${fee.unit}` return {renderValue} }, }, { key: 'memo', name: t('labelTxMemo'), headerCellClass: 'textAlignCenter', formatter: ({ row }) => ( ), }, { key: 'time', name: t('labelTxTime'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row.updatedAt const hasValue = Number.isFinite(value) const renderValue = hasValue ? moment(new Date(value), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return {renderValue} }, }, ], [etherscanBaseUrl], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'amount', name: ( {t('labelTransactions')} {t('labelTxAmount') + ' / ' + t('labelTxNetworkFee')} ), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { // const hasValue = Number.isFinite(row.amount); let side, hasSymbol, sideIcon switch (row.nftTxType) { case TxNFTType[TxNFTType.DEPOSIT]: side = t('labelReceive') hasSymbol = '+' sideIcon = break case TxNFTType[TxNFTType.TRANSFER]: side = t('labelSendL2') hasSymbol = row.receiverAddress?.toLowerCase() === accAddress?.toLowerCase() ? '+' : '-' sideIcon = break case TxNFTType[TxNFTType.MINT]: side = t('labelMint') sideIcon = hasSymbol = '+' break // @ts-ignore case TxNFTType[TxNFTType.SEND_LUCKY_TOKEN]: side = t('labelNFTTypeSEND_LUCKY_TOKEN') sideIcon = hasSymbol = '-' break // @ts-ignore case TxNFTType[TxNFTType.SEND_BACK_LUCKY_TOKEN]: side = t('labelNFTTypeSEND_BACK_LUCKY_TOKEN') sideIcon = hasSymbol = '+' break // @ts-ignore case TxNFTType[TxNFTType.WITHDRAW_LUCKY_TOKEN]: side = t('labelNFTTypeWITHDRAW_LUCKY_TOKEN') sideIcon = hasSymbol = '+' break case TxNFTType[TxNFTType.WITHDRAW]: default: hasSymbol = '-' sideIcon = side = t('labelSendL1') } // const renderValue = hasValue ? row.amount : EmptyValueTag; const renderFee = `Fee: ${getValuePrecisionThousand( row.fee.value, undefined, undefined, undefined, false, { floor: false, isTrade: true, }, )} ${row.fee.unit}` return ( {row.metadata?.imageSize ? ( ) : ( sideIcon )} {side} {hasSymbol} {row.amount ?? EmptyValueTag} {renderFee} ) }, }, { key: 'from', name: t('labelTxFrom') + ' / ' + t('labelTxTime'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { const receiverAddress = row.nftTxType === TxNFTType[TxNFTType.WITHDRAW] ? getShortAddr(row.withdrawalInfo.recipient, isMobile) : getShortAddr(row.receiverAddress, isMobile) const senderAddress = getShortAddr(row.senderAddress, isMobile) const [from, to] = row.nftTxType === TxNFTType[TxNFTType.TRANSFER] ? row.receiverAddress?.toLowerCase() === accAddress?.toLowerCase() ? [senderAddress, `${L1L2_NAME_DEFINED[network].l2Symbol}`] : [`${L1L2_NAME_DEFINED[network].l2Symbol}`, receiverAddress] : row.nftTxType === TxNFTType[TxNFTType.DEPOSIT] ? [ `${L1L2_NAME_DEFINED[network].l1Symbol}`, `${L1L2_NAME_DEFINED[network].l2Symbol}`, ] : row.nftTxType === TxNFTType[TxNFTType.MINT] ? ['Mint', `${L1L2_NAME_DEFINED[network].l2Symbol}`] : row.nftTxType === TxNFTType[TxNFTType.WITHDRAW] ? [`${L1L2_NAME_DEFINED[network].l2Symbol}`, receiverAddress] : ['', ''] const hash = row.txHash !== '' ? row.txHash : row.hash const path = row.txHash !== '' ? etherscanBaseUrl + `/tx/${row.txHash}` : Explorer + `tx/${row.hash}-${EXPLORE_TYPE[row.nftTxType.toUpperCase()]}` const hasValue = Number.isFinite(row.updatedAt) const renderTime = hasValue ? moment(new Date(row.updatedAt), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return ( { window.open(path, '_blank') window.opener = null }} > {from + ` ${DirectionTag} ` + to} {renderTime} ) }, }, ], [etherscanBaseUrl, isMobile, t], ) const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return ( {showFilter && (isMobile && isDropDown ? ( setIsDropDown(false)} > {t('labelShowFilter')} ) : ( ))}
    {accountId && showFilter && !isTaiko && ( View transactions on block explorer )} {!!(pagination && pagination.total) && ( { handleFilterChange({ page }) }} /> )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/nftTable/components/Filter.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Grid, MenuItem } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { Button, DateRangePicker, TextField } from '../../../basic-lib' import { DropDownIcon } from '@loopring-web/common-resources' import { DateRange } from '@mui/lab' import { TxNFTType, UserNFTTxTypes } from '@loopring-web/loopring-sdk' import { NFTTableFilter } from '../Interface' import { useSettings } from '../../../../stores' export interface FilterProps { handleFilterChange: (filter: Partial) => void filterDate?: DateRange filterType?: UserNFTTxTypes // handleReset: () => void; marketArray?: string[] } // export enum FilterOrderTypes { // allTypes = "All Types", // buy = "Buy", // sell = "Sell", // } const StyledTextFiled = styled(TextField)` &.MuiTextField-root { max-width: initial; } .MuiInputBase-root { width: initial; max-width: initial; } ` export const Filter = withTranslation('tables', { withRef: true })( ({ t, filterType, filterDate, handleFilterChange }: FilterProps & WithTranslation) => { const { isMobile } = useSettings() const transactionTypeList = [ { label: t(`labelTxNFTFilter${TxNFTType.ALL}`), value: 0, }, { label: t(`labelTxNFTFilter${TxNFTType.DEPOSIT}`), value: UserNFTTxTypes[TxNFTType.DEPOSIT], }, { label: t(`labelTxNFTFilter${TxNFTType.WITHDRAW}`), value: UserNFTTxTypes[TxNFTType.WITHDRAW], }, { label: t(`labelTxNFTFilter${TxNFTType.TRANSFER}`), value: UserNFTTxTypes[TxNFTType.TRANSFER], }, { label: t(`labelTxNFTFilter${TxNFTType.MINT}`), value: UserNFTTxTypes[TxNFTType.MINT], }, ] return ( { handleFilterChange({ duration: date }) }} /> ) => { let txType: any = event.target.value as UserNFTTxTypes handleFilterChange({ txType, }) }} inputProps={{ IconComponent: DropDownIcon }} > {transactionTypeList.map((o) => ( {o.label} ))} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/nftTable/components/modal.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { Box, Grid, Typography } from '@mui/material' import moment from 'moment' import { EmptyValueTag, L1L2_NAME_DEFINED, MapChainId, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { TxType } from '@loopring-web/loopring-sdk' import React from 'react' import { TxnDetailProps } from '../Interface' import { useSettings } from '../../../../stores' // import { getValuePrecisionThousand } from '@loopring-web/common-resources'; const ContentWrapperStyled = styled(Box)` top: 45%; left: 50%; transform: translate(-50%, -50%); width: 70%; min-width: ${({ theme }) => theme.unit * 87.5}px; height: 75%; background-color: var(--color-box); box-shadow: 0px ${({ theme }) => theme.unit / 2}px ${({ theme }) => theme.unit / 2}px rgba(0, 0, 0, 0.25); border-radius: ${({ theme }) => theme.unit}px; position: absolute; display: flex; justify-content: center; align-items: center; ` const HeaderStyled = styled(Box)` position: absolute; top: 0; z-index: 22; width: 100%; height: ${({ theme }) => theme.unit * 7.5}px; box-shadow: 0px ${({ theme }) => theme.unit / 4}px ${({ theme }) => theme.unit}px rgba(0, 0, 0, 0.25); border-radius: ${({ theme }) => theme.unit}px ${({ theme }) => theme.unit}px 0px 0px; display: flex; align-items: center; ` const GridContainerStyled = styled(Grid)` margin-top: ${({ theme }) => theme.unit * 7.5}px; flex-direction: column; width: auto; ` const GridItemStyled = styled(Grid)` display: flex; align-items: baseline; margin-bottom: ${({ theme }) => theme.unit * 3}px; ` const EthHshStyled = styled(Typography)` cursor: pointer; text-decoration: underline; ` const TypographyStyled = styled(Typography)` color: var(--color-text-secondary); width: ${({ theme }) => theme.unit * 20}px; ` const InfoValueStyled = styled(Box)` // max-width: ${({ theme }) => theme.unit * 32}px; word-break: break-all; font-size: 1.4rem; color: ${(props: any) => (props.hash ? 'var(--color-secondary)' : 'var(--color-text-primary)')}; ` as any const StatusStyled = styled(Typography)` color: ${({ theme, status }) => status === 'processed' ? theme.colorBase.success : status === 'processing' ? theme.colorBase.warning : status === 'failed' ? theme.colorBase.error : theme.colorBase.secondaryHover}; ` export const TxnDetailPanel = withTranslation('common', { withRef: true })( React.forwardRef( ( { t, txType, hash, txHash, status, time, from, to, amount, fee, memo, etherscanBaseUrl, }: TxnDetailProps & WithTranslation, ref: React.ForwardedRef, ) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const headerLabel = txType === TxType.DEPOSIT ? 'labelDTxnDetailHeader' : txType === TxType.OFFCHAIN_WITHDRAWAL ? 'labelWTxnDetailHeader' : 'labelTTxnDetailHeader' const renderStatus = status.toUpperCase() === 'PROCESSED' ? t('labelTxnDetailProcessed') : status.toUpperCase() === 'PROCESSING' || status.toUpperCase() === 'RECEIVED' ? t('labelTxnDetailProcessing') : t('labelTxnDetailFailed') return ( {t(headerLabel)} {t('labelTxnDetailHash', { layer2: L1L2_NAME_DEFINED[network].layer2, })} {hash} {txHash && ( {t('labelTxnDetailHashLv1', { layer2: L1L2_NAME_DEFINED[network].layer2, })} { window.open(`${etherscanBaseUrl}tx/${txHash}`) window.opener = null }} > {txHash} {/* {txHash} */} )} {t('labelTxnDetailStatus')} {/* {status.toUpperCase()} */} {renderStatus} {t('labelTxnDetailTime')} {moment(time).format(YEAR_DAY_MINUTE_FORMAT)} {t('labelTxnDetailFrom')} {from || EmptyValueTag} {t('labelTxnDetailTo')} {to || EmptyValueTag} {t('labelTxnDetailAmount')} {amount} {t('labelTxnDetailFee')} {fee} {t('labelTxnDetailMemo')} {memo || EmptyValueTag} ) }, ), ) ================================================ FILE: packages/component-lib/src/components/tableList/nftTable/index.ts ================================================ export * from './TsNFTTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/orderHistoryTable/OrderHistoryTable.tsx ================================================ import React from 'react' import { Button, CancelAllOrdersAlert, CancelOneOrdersAlert, PopoverPure, QuoteTableRawDataItem, } from '../../index' import styled from '@emotion/styled' import { Box, BoxProps, ClickAwayListener, Grid, Link, Modal, Tooltip, Typography, } from '@mui/material' import { DateRange } from '@mui/lab' import { WithTranslation, withTranslation } from 'react-i18next' import moment from 'moment' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { CompleteIcon, DirectionTag, DropDownIcon, EmptyValueTag, getValuePrecisionThousand, globalSetup, RowConfig, TableType, TradeStatus, TradeTypes, UNIX_TIMESTAMP_FORMAT, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Filter, FilterOrderTypes } from './components/Filter' import { OrderDetailPanel } from './components/modal' import { TableFilterStyled, TablePaddingX } from '../../styled' import * as sdk from '@loopring-web/loopring-sdk' import { useSettings } from '../../../stores' import _ from 'lodash' import { useLocation } from 'react-router-dom' const CancelColHeaderStyled = styled(Typography)` display: flex; align-items: center; color: ${({ empty }: any) => empty === 'true' ? 'var(--color-text-third)' : 'var(--color-primary)'}; cursor: ${({ empty }: any) => (empty === 'true' ? 'not-allowed' : 'pointer')}; ` as any export type OrderPair = { from: { key: string value: number } to: { key: string value: number } } export interface OrderHistoryRow { side: keyof typeof TradeTypes orderType: sdk.OrderType amount: OrderPair average: number filledAmount: OrderPair time: number hash: string status: keyof typeof TradeStatus sortColumn: string filterColumn: string actionsStatus: object tradeChannel: string extraOrderInfo: { extraOrderType: string isTriggerd: boolean stopPrice: string stopSide: string } __raw__: any } export enum DetailRole { maker = 'maker', taker = 'taker', } export type OrderDetailItem = { market: string amount: number filledPrice: string time: number fee: { key: string value: string } volumeToken: string } export type OrderHistoryTableDetailItem = { amount: OrderPair filledPrice: { value: string | number precision?: number } fee: { key: string value: string | number precision?: number } role: string time: number volume?: number orderId: string } export type OrderHistoryRawDataItem = { market: string side: TradeTypes amount: OrderPair average: number | string price: { key: string value: number } time: number status: TradeStatus hash: string orderId: string extraOrderInfo?: { extraOrderType: string isTriggerd: boolean stopPrice: string stopSide: string } __raw__: any } const TableStyled = styled(Box)< BoxProps & { isMobile?: boolean isopen?: string ispro?: string isStopLimit?: boolean } >` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile, isopen, ispro, isStopLimit }) => !isMobile ? `--template-columns: ${ isopen === 'open' ? ispro === 'pro' ? `auto auto ${isStopLimit ? 234 : 250}px auto auto ${ isStopLimit ? '110px' : '' } auto auto` : `auto auto ${isStopLimit ? 200 : 230}px auto auto ${ isStopLimit ? 'auto' : '' } 130px 140px` : ispro === 'pro' ? `auto auto ${isStopLimit ? 234 : 250}px auto ${ isStopLimit ? '110px' : '' } auto auto` : `auto auto ${isStopLimit ? 200 : 230}px auto 130px ${ isStopLimit ? 'auto' : '' } 130px 130px` } !important;` : `--template-columns: 14% 62% 24% !important;`} .rdg-cell:last-of-type { display: flex; justify-content: flex-end; } } .textAlignRight { text-align: right; } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as ( props: { isMobile?: boolean isopen?: string ispro?: string isStopLimit?: boolean } & BoxProps, ) => JSX.Element export interface OrderHistoryTableProps { rawData: OrderHistoryRawDataItem[] pagination?: { pageSize: number total: number } showFilter?: boolean getOrderList: (props: Omit) => Promise userOrderDetailList?: any[] getUserOrderDetailTradeList?: ( props?: Omit, ) => Promise showLoading?: boolean marketArray?: string[] showDetailLoading?: boolean isOpenOrder?: boolean cancelOrder: ({ orderHash, clientOrderId }: any) => Promise cancelOrderByHashList?: (orderHashList: string) => Promise isScroll?: boolean isPro?: boolean handleScroll?: (event: React.UIEvent, isOpen?: boolean) => Promise clearOrderDetail?: () => void onRowClick?: (rowIdx: number, row: QuoteTableRawDataItem, column: any) => void isStopLimit?: boolean } export const OrderHistoryTable = withTranslation('tables')( (props: OrderHistoryTableProps & WithTranslation) => { const { search } = useLocation() const searchParams = new URLSearchParams(search) const { t, rawData, pagination, showFilter, getOrderList, showLoading, marketArray, showDetailLoading, /* getOrderDetail, */ /* orderDetailList, */ cancelOrder, isOpenOrder = false, isScroll, handleScroll, isPro = false, clearOrderDetail, cancelOrderByHashList, userOrderDetailList, getUserOrderDetailTradeList, onRowClick, isStopLimit, } = props const { isMobile } = useSettings() const actionColumns = ['status'] const [filterType, setFilterType] = React.useState(FilterOrderTypes.allTypes) const [filterDate, setFilterDate] = React.useState>([null, null]) const [filterToken, setFilterToken] = React.useState('all') const [page, setPage] = React.useState(1) const [modalState, setModalState] = React.useState(false) const [currOrderId, setCurrOrderId] = React.useState('') const [showCancelAllAlert, setShowCancelAllAlert] = React.useState(false) const [showCancelOneAlert, setShowCancelOndAlert] = React.useState<{ open: boolean row?: OrderHistoryRawDataItem }>({ open: false, row: undefined, }) const updateData = _.debounce( async ({ isOpen = isOpenOrder, actionType, currFilterType = filterType, currFilterDate = filterDate, currFilterToken = filterToken, currPage = page, }) => { if (actionType === TableType.filter) { currPage = 1 setPage(1) } const types = currFilterType === FilterOrderTypes.buy ? 'BUY' : currFilterType === FilterOrderTypes.sell ? 'SELL' : '' const start = Number(moment(currFilterDate[0]).format(UNIX_TIMESTAMP_FORMAT)) const end = Number(moment(currFilterDate[1]).format(UNIX_TIMESTAMP_FORMAT)) await getOrderList({ limit: pagination?.pageSize ?? 10, offset: (currPage - 1) * (pagination?.pageSize ?? 10), side: [types] as sdk.Side[], market: currFilterToken === 'all' ? '' : currFilterToken, start: Number.isNaN(start) ? -1 : start, end: Number.isNaN(end) ? -1 : end, status: isOpen ? ['processing'] : ['processed', 'failed', 'cancelled', 'cancelling', 'expired'], }) }, globalSetup.wait, ) const handleFilterChange = React.useCallback( async ({ isOpen = isOpenOrder, type = filterType, date = filterDate, token = filterToken, currPage = page, }) => { setFilterType(type) setFilterDate(date) setFilterToken(token) await updateData({ isOpen: isOpen, actionType: TableType.filter, currFilterType: type, currFilterDate: date, currFilterToken: token, currPage: currPage, }) }, [updateData, filterDate, filterType, filterToken, page], ) const handlePageChange = React.useCallback( async (page: number) => { setPage(page) await updateData({ actionType: TableType.page, currPage: page }) }, [updateData], ) const handleReset = React.useCallback(async () => { setFilterType(FilterOrderTypes.allTypes) setFilterDate([null, null]) setFilterToken('all') await updateData({ TableType: TableType.filter, currFilterType: FilterOrderTypes.allTypes, currFilterDate: [null, null], currFilterToken: 'all', }) }, [updateData]) const handleOrderClick = React.useCallback( async (row: OrderHistoryRawDataItem) => { if (clearOrderDetail) { clearOrderDetail() } setCurrOrderId(row.orderId) setModalState(true) if (getUserOrderDetailTradeList) { await getUserOrderDetailTradeList({ orderHash: row.hash, }) } }, [clearOrderDetail, getUserOrderDetailTradeList], ) React.useEffect(() => { let filters: any = {} updateData.cancel() if (searchParams.get('market')) { filters.token = searchParams.get('market') } filters.isOpen = isOpenOrder handleFilterChange(filters) return () => { updateData.cancel() } }, [pagination?.pageSize, isOpenOrder]) const stopLimitColumn = React.useCallback((): Column[] => { if (isStopLimit) { return [ { key: 'Condition', name: t('labelStopLimitStopPrice'), headerCellClass: 'textAlignRight', formatter: ({ row }: any) => { return ( {row?.extraOrderInfo?.isTriggered ? t('labelStopLimitTriggered', { time: row.extraOrderInfo?.triggeredTime ? moment(new Date(row.extraOrderInfo?.triggeredTime)).format( YEAR_DAY_MINUTE_FORMAT, ) : '', interpolation: { escapeValue: false, }, }) : t('labelStopLimitWaitingTrigger')} } > {row.extraOrderInfo?.stopSide == sdk.STOP_SIDE.LESS_THAN_AND_EQUAL ? '≤' : '≥'} {row.extraOrderInfo?.stopPrice} {row?.extraOrderInfo?.isTriggered && } ) }, }, ] } else { return [] } }, [isStopLimit]) const CellStatus = React.useCallback( ({ row, rowIdx }: any) => { const value = row.status const popupId = `status-orderTable-${rowIdx}` const rightState = usePopupState({ variant: 'popover', popupId: popupId, }) const RenderValue: any = styled(Typography)` position: relative; display: flex; justify-content: flex-end; align-items: center; color: ${({ theme }) => { const { colorBase } = theme return value === TradeStatus.Processed ? colorBase.success : value === TradeStatus.Expired ? colorBase.textSecondary : colorBase.textPrimary }}; height: 100%; & svg { font-size: 14px; transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transform: ${() => (rightState.isOpen ? 'rotate(180deg)' : '')}; } ` let actualValue = '' switch (value) { case TradeStatus.Processing: actualValue = t('labelOrderProcessing') break case TradeStatus.Processed: actualValue = t('labelOrderProcessed') break case TradeStatus.Cancelling: actualValue = t('labelOrderCancelling') break case TradeStatus.Cancelled: actualValue = t('labelOrderCancelled') break case TradeStatus.Expired: actualValue = t('labelOrderExpired') break case TradeStatus.Waiting: actualValue = t('labelOrderWaiting') break default: break } return ( <> {isMobile ? ( {actualValue} ) : ( handleOrderClick(row)} > {actualValue} )} ) }, [handleOrderClick, isMobile, t], ) const getPopoverState = React.useCallback((label: number) => { return usePopupState({ variant: 'popover', popupId: `popup-cancel-order-${label}`, }) }, []) const getColumnModeOrderHistory = (): Column[] => [ { key: 'types', name: t('labelOrderTypes'), formatter: ({ row }) => { let renderValue = '' if (row.extraOrderInfo?.extraOrderType == 'STOP_LIMIT') { renderValue = t('labelOrderStopLimitOrder') } else { switch (row.orderType) { case 'AMM': renderValue = t('labelOrderMarketOrder') break case 'LIMIT_ORDER': renderValue = t('labelOrderLimitOrder') break case 'MAKER_ONLY': renderValue = t('labelOrderLimitOrder') break case 'TAKER_ONLY': renderValue = t('labelOrderLimitOrder') break default: break } } return
    {renderValue}
    }, }, { key: 'channels', name: t('labelOrderChannels'), formatter: ({ row }) => { const value = row['tradeChannel'] let renderChannel = '' switch (value) { case 'MIXED': renderChannel = t('labelOrderChannelsMixed') break case 'AMM_POOL': renderChannel = t('labelOrderChannelsSwap') break case 'ORDER_BOOK': renderChannel = t('labelOrderChannelsOrderBook') break default: break } return
    {renderChannel}
    }, }, { key: 'amount', name: t('labelOrderAmount'), formatter: ({ row, column }) => { const { from, to } = row[column.key] const precisionFrom = row.amount.from?.['precision'] const precisionTo = row.amount.to?.['precision'] const { key: keyFrom, value: valueFrom } = from const { key: keyTo, value: valueTo } = to const renderValue = `${getValuePrecisionThousand( valueFrom, precisionFrom, precisionFrom, )} ${keyFrom} ${DirectionTag} ${getValuePrecisionThousand( valueTo, precisionTo, precisionTo, )} ${keyTo}` return (
    {renderValue}
    ) }, }, { key: 'average', name: t('labelOrderAverage'), headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { const value = row[column.key] const precisionMarket = row['precisionMarket'] const renderValue = value ? getValuePrecisionThousand(value, undefined, undefined, precisionMarket, true, { isPrice: true, }) : EmptyValueTag return
    {renderValue}
    }, }, { key: 'price', name: t('labelOrderPrice'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row['price'].value const precisionMarket = row['precisionMarket'] const hasValue = Number.isFinite(value) const renderValue = hasValue ? getValuePrecisionThousand(value, undefined, undefined, precisionMarket, true, { isPrice: true, }) : EmptyValueTag return (
    {renderValue}
    ) }, }, ...[].concat(stopLimitColumn() as never[]), { key: 'time', name: t('labelOrderTime'), headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { const value = row[column.key] const renderValue = Number.isFinite(value) ? moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return (
    {renderValue}
    ) }, }, { key: 'status', headerCellClass: 'textAlignRight', name: t('labelOrderStatus'), formatter: ({ row, column, rowIdx }) => ( <> ), }, ] const getColumnModeOpenHistory = (isEmpty: boolean): Column[] => [ { key: 'types', name: t('labelOrderTypes'), formatter: ({ row }) => { let renderValue = '' if (row.extraOrderInfo?.extraOrderType == 'STOP_LIMIT') { renderValue = t('labelOrderStopLimitOrder') } else { switch (row.orderType) { case 'AMM': renderValue = t('labelOrderAmm') break case 'LIMIT_ORDER': renderValue = t('labelOrderLimitOrder') break case 'MAKER_ONLY': renderValue = t('labelOrderMaker') break case 'TAKER_ONLY': renderValue = t('labelOrderTaker') break default: break } } return
    {renderValue}
    }, }, { key: 'channels', name: t('labelOrderChannels'), formatter: ({ row }) => { const value = row['tradeChannel'] let renderChannel = '' switch (value) { case 'MIXED': renderChannel = t('labelOrderChannelsMixed') break case 'AMM_POOL': renderChannel = t('labelOrderChannelsAMM') break case 'ORDER_BOOK': renderChannel = t('labelOrderChannelsOrderBook') break default: break } return
    {renderChannel}
    }, }, { key: 'amount', name: t('labelOrderAmount'), formatter: ({ row, column }) => { const { from, to } = row[column.key] const { key: keyFrom, value: valueFrom } = from const { key: keyTo, value: valueTo } = to const precisionFrom = row.amount.from?.['precision'] const precisionTo = row.amount.to?.['precision'] const renderValue = `${getValuePrecisionThousand( valueFrom, precisionFrom, precisionFrom, )} ${keyFrom} ${DirectionTag} ${getValuePrecisionThousand( valueTo, precisionTo, precisionTo, )} ${keyTo}` return (
    {renderValue}
    ) }, }, { key: 'price', name: t('labelOrderPrice'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row['price'].value const precisionMarket = row['precisionMarket'] const hasValue = Number.isFinite(value) const renderValue = hasValue ? getValuePrecisionThousand( value, precisionMarket, precisionMarket, precisionMarket, true, { isPrice: true }, ) : EmptyValueTag return (
    {renderValue}
    ) }, }, { key: 'completion', name: t('labelOrderCompletion'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const rawValue = row['completion'] const renderValue = `${(rawValue * 100).toFixed(2)}%` return
    {renderValue}
    }, }, ...[].concat(stopLimitColumn() as never[]), { key: 'time', name: t('labelOrderTime'), headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { const value = row[column.key] const renderValue = Number.isFinite(value) ? moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return (
    {renderValue}
    ) }, }, { key: 'cancel', headerCellClass: 'textAlignRight', name: ( setShowCancelAllAlert(true)} > {t('labelOrderCancelAll')} ), formatter: ({ row }: any) => { return ( <> { setShowCancelOndAlert({ open: true, row }) }} style={{ cursor: 'pointer' }} className='rdg-cell-value textAlignRight' > {t('labelOrderCancelOrder')} ) }, }, ] const getColumnModeMobileOrderHistory = (): Column[] => [ { key: 'types', name: t('labelOrderTypes') + '/' + t('labelOrderChannels'), formatter: ({ row }) => { let renderChannel = '', renderValue = '' switch (row.tradeChannel) { case 'MIXED': renderChannel = t('labelOrderChannelsMixed') break case 'AMM_POOL': renderChannel = t('labelOrderChannelsSwap') break case 'ORDER_BOOK': renderChannel = t('labelOrderChannelsOrderBook') break default: break } if (row?.extraOrderInfo?.extraOrderType) { renderValue = t('labelOrderStopLimitOrder') } else { switch (row.orderType) { case 'AMM': renderValue = t('labelOrderMarketOrder') break case 'LIMIT_ORDER': renderValue = t('labelOrderLimitOrder') break case 'MAKER_ONLY': renderValue = t('labelOrderLimitOrder') break case 'TAKER_ONLY': renderValue = t('labelOrderLimitOrder') break default: break } } return ( {renderValue} {renderChannel} ) }, }, { key: 'amount', name: t('labelOrderAmount') + '/' + t('labelOrderAverage'), headerCellClass: 'textAlignRight', formatter: ({ row, column, rowIdx }) => { const { from, to } = row[column.key] const precisionFrom = row.amount.from?.['precision'] const precisionTo = row.amount.to?.['precision'] const { key: keyFrom, value: valueFrom } = from const { key: keyTo, value: valueTo } = to const renderValue = `${getValuePrecisionThousand( valueFrom, precisionFrom, precisionFrom, )} ${keyFrom} ${DirectionTag} ${getValuePrecisionThousand( valueTo, precisionTo, precisionTo, )} ${keyTo}` const average = row.average ? getValuePrecisionThousand( row.average, undefined, undefined, row['precisionMarket'], true, { isPrice: true }, ) : EmptyValueTag return ( {renderValue} {average} ) }, }, { key: 'price', name: t('labelOrderPrice') + '/' + t('labelOrderTime'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row['price'].value const precisionMarket = row['precisionMarket'] const hasValue = Number.isFinite(value) const time = Number.isFinite(value) ? moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag const renderValue = hasValue ? getValuePrecisionThousand(value, undefined, undefined, precisionMarket, true, { isPrice: true, }) : EmptyValueTag return ( {renderValue} {time} ) }, }, ] const getColumnModeMobileOpenHistory = ( isEmpty: boolean, ): Column[] => [ { key: 'types', name: t('labelOrderTypes') + '/' + t('labelOrderChannels'), formatter: ({ row }) => { let renderChannel = '', renderValue = '' switch (row.tradeChannel) { case 'MIXED': renderChannel = t('labelOrderChannelsMixed') break case 'AMM_POOL': renderChannel = t('labelOrderChannelsSwap') break case 'ORDER_BOOK': renderChannel = t('labelOrderChannelsOrderBook') break default: break } if (row.extraOrderInfo?.extraOrderType == 'STOP_LIMIT') { renderValue = t('labelOrderStopLimitOrder') } else { switch (row.orderType) { case 'AMM': renderValue = t('labelOrderMarketOrder') break case 'LIMIT_ORDER': renderValue = t('labelOrderLimitOrder') break case 'MAKER_ONLY': renderValue = t('labelOrderLimitOrder') break case 'TAKER_ONLY': renderValue = t('labelOrderLimitOrder') break default: break } } return ( {renderValue} {renderChannel} ) }, }, { key: 'amount', headerCellClass: 'textAlignRight', name: t('labelOrderAmount') + '/' + t('labelOrderPrice'), formatter: ({ row, column }) => { const { from, to } = row[column.key] const precisionFrom = row.amount.from?.['precision'] const precisionTo = row.amount.to?.['precision'] const { key: keyFrom, value: valueFrom } = from const { key: keyTo, value: valueTo } = to const renderValue = `${getValuePrecisionThousand( valueFrom, precisionFrom, precisionFrom, )} ${keyFrom} ${DirectionTag} ${getValuePrecisionThousand( valueTo, precisionTo, precisionTo, )} ${keyTo}` //@ts-ignore const hasValue = Number.isFinite(row['price']?.value) const price = hasValue ? getValuePrecisionThousand( //@ts-ignore row['price']?.value, row['precisionMarket'], row['precisionMarket'], row['precisionMarket'], true, { isPrice: true }, ) : EmptyValueTag const completion = `${(row['completion'] * 100).toFixed(2)}%` return ( {renderValue} {completion} {price} ) }, }, { key: 'time', name: ( setShowCancelAllAlert(true)} > {t('labelOrderCancelAll')} ), headerCellClass: 'textAlignRight', formatter: ({ row, rowIdx }) => { const time = Number.isFinite(row.time) ? moment(new Date(row.time), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag const orderHash = row.hash const clientOrderId = row['orderId'] const popState = getPopoverState(rowIdx) const handleClose = () => { popState.setOpen(false) } // @ts-ignore const handleRequestCancel = async (e: MouseEvent) => { e.preventDefault() await cancelOrder({ orderHash, clientOrderId }) handleClose() } return ( <> { setShowCancelOndAlert({ open: true, row: row as any }) }} style={{ cursor: 'pointer' }} className='rdg-cell-value textAlignRight' > {t('labelOrderCancelOrder')} {time} popState.setOpen(false)}> {t('labelOrderCancelConfirm')} ) }, }, ] const actualColumns = isOpenOrder ? isMobile ? getColumnModeMobileOpenHistory(rawData.length === 0) : getColumnModeOpenHistory(rawData.length === 0) : isMobile ? getColumnModeMobileOrderHistory() : getColumnModeOrderHistory() const defaultArgs: any = { columnMode: actualColumns, generateRows: (rawData: any) => rawData, onRowClick: isPro ? onRowClick : null, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], actionColumns, } const handleCancelAll = React.useCallback(async () => { const openOrdresList = rawData .filter((o) => o.status === 'processing') .map((o) => o.hash) .join(',') if (cancelOrderByHashList) { await cancelOrderByHashList(openOrdresList) } }, [rawData, cancelOrderByHashList]) const handleCancelOne = React.useCallback(async () => { if (showCancelOneAlert?.row) { const orderHash = showCancelOneAlert?.row?.hash const clientOrderId = showCancelOneAlert?.row?.orderId await cancelOrder({ orderHash, clientOrderId }) } }, [showCancelOneAlert]) const [isDropDown, setIsDropDown] = React.useState(true) return ( {showFilter && (isMobile && isDropDown ? ( setIsDropDown(false)} > {t('labelShowFilter')} ) : ( ))}
    handleOrderClick(row as any)} onScroll={handleScroll ? (e) => handleScroll(e, isOpenOrder) : undefined} style={{ height: isOpenOrder && !isScroll ? RowConfig.rowHeaderHeight + rawData.length * RowConfig.rowHeight : 'initial', }} {...{ ...defaultArgs, ...props, rawData, showloading: showLoading }} /> setShowCancelAllAlert(false)} /> setShowCancelOndAlert({ open: false, row: undefined })} /> setModalState(false)}> {pagination && !!rawData.length && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/orderHistoryTable/SingleOrderHistoryTable.tsx ================================================ import { Box, BoxProps, Typography } from '@mui/material' import { Column, generateColumns, Table } from '../../basic-lib' import { OrderDetailItem } from './OrderHistoryTable' import { withTranslation, WithTranslation } from 'react-i18next' import { EmptyValueTag, getValuePrecisionThousand, myLog } from '@loopring-web/common-resources' import styled from '@emotion/styled' import moment from 'moment' import { TablePaddingX } from '../../styled' import { useSettings } from '../../../stores' interface Row { amount: number tradingPrice: number filledPrice: string time: number total: { key: string value: number } fee: { value: number; key: string } sortColumn: string filterColumn: string actionsStatus: object } const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: auto; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: auto auto auto auto 180px !important;` : `--template-columns: 40% 40% 20% !important;`} } .textAlignRight { text-align: right; } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export interface SingleOrderHistoryTableProps { rawData: OrderDetailItem[] showloading?: boolean } export const SingleOrderHistoryTable = withTranslation('tables')( (props: SingleOrderHistoryTableProps & WithTranslation) => { const { t } = props const getColumnModeSingleHistory = (): Column[] => { return [ { key: 'amount', name: props.t('labelOrderAmount'), formatter: ({ row }) => { const value = row['amount'] const renderValue = `${getValuePrecisionThousand(value, undefined, undefined, 6)}` return
    {renderValue}
    }, }, { key: 'filledPrice', name: props.t('labelOrderFilledPrice'), headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { const value = row[column.key] const renderValue = value ? getValuePrecisionThousand(value, undefined, undefined, undefined, true, { isPrice: true, }) : EmptyValueTag return
    {renderValue}
    }, }, { key: 'fee', name: props.t('labelOrderFee'), headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { myLog(666, row['fee']) const value = row[column.key].value const token = row[column.key].key const renderValue = value ? `${getValuePrecisionThousand(value, undefined, undefined, undefined, false, { floor: false, })} ${token}` : EmptyValueTag return
    {renderValue}
    }, }, { key: 'role', name: props.t('labelOrderRole'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const renderValue = row['fee'].value ? props.t('labelTaker') : props.t('labelMaker') return
    {renderValue}
    }, }, { key: 'time', name: props.t('labelOrderTime'), headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { const value = row[column.key] const renderValue = Number.isFinite(value) ? moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return (
    {renderValue}
    ) }, }, ] } const getColumnModeMobileSingleHistory = (): Column[] => { return [ { key: 'amount', name: t('labelOrderAmount') + '/' + t('labelOrderFilledPrice'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const renderValue = `${getValuePrecisionThousand(row.amount, undefined, undefined, 6)}` const filledPrice = row.filledPrice ? `${getValuePrecisionThousand( Number(row.filledPrice), undefined, undefined, undefined, true, { isPrice: true }, )} ` : EmptyValueTag return ( {renderValue} {filledPrice} ) }, }, { key: 'fee', name: props.t('labelOrderFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row.fee.value const token = row.fee.key const renderValue = value ? `${getValuePrecisionThousand(value, undefined, undefined, undefined, false, { floor: false, })} ${token}` : EmptyValueTag return
    {renderValue}
    }, }, { key: 'role', name: t('labelOrderRole') + '/' + t('labelOrderTime'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const renderValue = row['fee'].value ? props.t('labelTaker') : props.t('labelMaker') const time = Number.isFinite(row.time) ? moment(new Date(row.time), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return ( {renderValue} {time} ) }, }, ] } const { isMobile } = useSettings() const defaultArgs: any = { rawData: [], columnMode: isMobile ? getColumnModeMobileSingleHistory() : getColumnModeSingleHistory(), generateRows: (rawData: any) => rawData, generateColumns, } return (
    ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/orderHistoryTable/components/Filter.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Grid, MenuItem } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { DateRangePicker, TextField } from '../../../' import { Button } from '../../../basic-lib/btns' import { DropDownIcon } from '@loopring-web/common-resources' import { DateRange } from '@mui/lab' export interface FilterProps { handleFilterChange: ({ filterType, filterDate, filterToken }: any) => void filterDate: DateRange filterType: FilterOrderTypes filterToken: string handleReset: () => void marketArray?: string[] } export enum FilterOrderTypes { allTypes = 'all', buy = 'Buy', sell = 'Sell', } const StyledTextFiled = styled(TextField)` &.MuiTextField-root { max-width: initial; } .MuiInputBase-root { width: initial; max-width: initial; } ` export const Filter = withTranslation('tables', { withRef: true })( ({ t, filterDate, filterToken, handleFilterChange, handleReset, marketArray = [], }: FilterProps & WithTranslation) => { const getTokenTypeList = React.useCallback(() => { return [ { label: t('labelOrderFilterAllPairs'), value: 'all', }, ...Array.from(new Set(marketArray)).map((token) => ({ label: token, value: token, })), ] }, [t, marketArray]) return ( { handleFilterChange({ date: date }) }} /> ) => { handleFilterChange({ token: event.target.value }) }} inputProps={{ IconComponent: DropDownIcon }} > {getTokenTypeList().map((o) => ( {o.label} ))} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/orderHistoryTable/components/modal.tsx ================================================ import { withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { Box, Typography } from '@mui/material' import { SingleOrderHistoryTable } from '../SingleOrderHistoryTable' import { TFunction } from 'i18next' import { OrderDetailItem } from '../OrderHistoryTable' import { useSettings } from '../../../../stores' export enum TxnDetailStatus { processed = 'PROCESSED', processing = 'PROCESSING', received = 'RECEIVED', failed = 'FAILED', } const ContentWrapperStyled = styled(Box)` position: absolute; top: 45%; left: 50%; transform: translate(-50%, -50%); min-width: ${({ theme }) => theme.unit * 87.5}px; background-color: var(--color-box); box-shadow: 0px ${({ theme }) => theme.unit / 2}px ${({ theme }) => theme.unit / 2}px rgba(0, 0, 0, 0.25); padding: 0 ${({ theme }) => theme.unit * 1}px; border-radius: ${({ theme }) => theme.unit / 2}px; ` const HeaderStyled = styled(Box)` display: flex; justify-content: space-between; align-items: center; width: 100%; margin-top: ${({ theme }) => theme.unit * 2}px; margin-bottom: ${({ theme }) => theme.unit * 2}px; padding: 0 ${({ theme }) => theme.unit * 3}px; ` export const OrderDetailPanel = withTranslation('tables', { withRef: true })( ({ rawData, t, showLoading, orderId, }: { rawData: OrderDetailItem[] showLoading?: boolean t: TFunction orderId: string }) => { const { isMobile } = useSettings() const volume = rawData.map((o) => o.amount).reduce((prev, curr) => (prev || 0) + (curr || 0), 0) const volumeToken = rawData[0]?.volumeToken return ( {t('labelOrderDetailTradingVolume')} :  {volume} {volumeToken} {t('labelOrderDetailOrderId')} :   {isMobile ? `${orderId.substring(0, 10)}...${orderId.substring(orderId.length - 10)}` : orderId} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/orderHistoryTable/index.ts ================================================ export * from './OrderHistoryTable' ================================================ FILE: packages/component-lib/src/components/tableList/poolsTable/Interface.ts ================================================ import { Account, AmmDetail, CAMPAIGNTAGCONFIG, // ForexMap, RowConfig, } from '@loopring-web/common-resources' // import { Currency } from "@loopring-web/loopring-sdk"; export type PoolRow = AmmDetail type FilterExtend = { showFilter?: boolean filterValue: string getFilteredData: (filterValue: string) => void } export type PoolTableProps, T = any> = { rawData: R[] handleWithdraw: (row: R) => void handleDeposit: (row: R) => void campaignTagConfig?: CAMPAIGNTAGCONFIG wait?: number tableHeight?: number showLoading?: boolean rowConfig?: typeof RowConfig forexValue?: number } & FilterExtend export type IconColumnProps = { row: R account: Account size?: number campaignTagConfig?: CAMPAIGNTAGCONFIG } ================================================ FILE: packages/component-lib/src/components/tableList/poolsTable/PoolsTable.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { PoolRow, PoolsTable } from './index' import { coinMap } from '../../../static' import { CoinInfo, FloatTag } from '@loopring-web/common-resources' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` const rawData: PoolRow[] = [ { coinA: 'ETH', coinB: 'LRC', address: '', market: 'ETH-LRC', coinAInfo: coinMap['ETH'] as CoinInfo, coinBInfo: coinMap['LRC'] as CoinInfo, amountU: 12, totalLPToken: 12132131, totalA: 0.002, totalB: 12344, tradeFloat: { change: 1000, timeUnit: '24h', priceU: 1.23123, floatTag: FloatTag.increase, reward: 12312, }, APR: 56, isNew: true, isActivity: false, }, { coinA: 'ETH', coinB: 'LRC', address: '', market: 'ETH-LRC', coinAInfo: coinMap['ETH'] as CoinInfo, coinBInfo: coinMap['LRC'] as CoinInfo, amountU: 12, totalLPToken: 12132131, totalA: 0.002, totalB: 12344, tradeFloat: { change: 1000, timeUnit: '24h', priceU: 1.23123, floatTag: FloatTag.increase, reward: 12312, }, APR: 56, isNew: true, isActivity: false, }, ] export const PoolTable: Story = (args: any) => { return ( <> ) } PoolTable.bind({}) PoolTable.args = { rawData: rawData, pagination: { pageSize: 5, }, } export default { title: 'components/TableList', component: PoolsTable, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/poolsTable/PoolsTable.tsx ================================================ import React from 'react' import { WithTranslation, withTranslation } from 'react-i18next' import { bindPopper, bindHover } from 'material-ui-popup-state' import * as sdk from '@loopring-web/loopring-sdk' import { Button, Column, InputSearch, NewTagIcon, Popover, PopoverPure, PopoverType, PopoverWrapProps, Table, TableProps, } from '../../basic-lib' import { CurrencyToTag, EmptyValueTag, getValuePrecisionThousand, globalSetup, MoreIcon, PriceTag, RowConfig, RowInvestConfig, SCENARIO, TokenType, } from '@loopring-web/common-resources' import { Box, BoxProps, Grid, Typography } from '@mui/material' import { PoolRow, PoolTableProps } from './Interface' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { useSettings, useToggle } from '../../../stores' import { TablePaddingX } from '../../styled' import { AmmAPRDetail, AmmPairDetail, TagIconList } from '../../block' import { ActionPopContent } from '../myPoolTable/components/ActionPop' import { CoinIcons } from '../assetsTable' import _ from 'lodash' import { useHistory, useLocation } from 'react-router-dom' import { usePopupState } from 'material-ui-popup-state/hooks' const TableStyled = styled(Box)<{ isMobile?: boolean } & BoxProps>` .rdg { border-radius: ${({ theme }) => theme.unit}px; ${({ isMobile }) => !isMobile ? `--template-columns: 240px auto auto auto 200px !important;` : ` --template-columns: 16% 50% auto 8% !important; `} .rdg-cell.action { display: flex; justify-content: flex-end; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export const PoolsTable = withTranslation(['tables', 'common'])( >({ t, i18n, tReady, campaignTagConfig, showFilter = true, rawData, filterValue: _filterValue, getFilteredData, wait = globalSetup.wait, showLoading, handleWithdraw, handleDeposit, rowConfig = RowConfig, forexValue = 1, ...rest }: WithTranslation & PoolTableProps) => { const { currency, isMobile, coinJson } = useSettings() const { search, pathname } = useLocation() const { toggle } = useToggle() const searchParams = new URLSearchParams(search) const history = useHistory() const [filterValue, setFilterValue] = React.useState(_filterValue ?? '') const updateData = _.debounce(({ searchValue = '' }: { searchValue: string }) => { getFilteredData(searchValue) }, wait) const handleFilterChange = React.useCallback(async ({ searchValue }: any) => { setFilterValue(searchValue) searchParams.set('search', searchValue ?? '') history.push({ pathname, search: searchParams.toString(), }) updateData({ searchValue }) }, []) const sortMethod = React.useCallback( (_sortedRows, sortColumn) => { let _rawData: T[] = [] switch (sortColumn) { case 'pools': _rawData = rawData.sort((a, b) => { const valueA = a.coinAInfo.simpleName const valueB = b.coinAInfo.simpleName return valueB.localeCompare(valueA) }) break case 'liquidity': _rawData = rawData.sort((a, b) => { return sdk .toBig(b.amountU?.replaceAll(sdk.SEP, '') ?? 0) .minus(a.amountU?.replaceAll(sdk.SEP, '') ?? 0) .toNumber() }) break case 'volume24': _rawData = rawData.sort((a, b) => { return sdk .toBig(b?.tradeFloat?.priceU ?? 0) .minus(a?.tradeFloat?.priceU ?? 0) .toNumber() }) break case 'APR': _rawData = rawData.sort((a, b) => { return sdk .toBig(b.APR ?? 0) .minus(a.APR ?? 0) .toNumber() }) break default: _rawData = rawData } return _rawData }, [rawData], ) const getPopoverState = React.useCallback((label: string) => { return usePopupState({ variant: 'popover', popupId: `popup-poolsTable-${label}`, }) }, []) const columnMode = (): Column[] => [ { key: 'pools', sortable: true, width: 'auto', minWidth: 240, name: t('labelPool'), formatter: ({ row }: FormatterProps) => { return ( {row.coinAInfo?.simpleName} / {row.coinBInfo?.simpleName} {campaignTagConfig && ( )} {row.isNew && } ) }, }, { key: 'liquidity', sortable: true, width: 'auto', headerCellClass: 'textAlignRight', name: t('labelLiquidity'), formatter: ({ row, rowIdx }) => { const { coinA, coinB, totalAStr, totalBStr, amountU } = row const popoverState = getPopoverState(rowIdx.toString()) return ( {typeof amountU === 'undefined' || !Number(amountU) ? EmptyValueTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(amountU).times(forexValue), undefined, undefined, 2, true, { isFait: true }, )} ) }, }, { key: 'volume24', sortable: true, width: 'auto', minWidth: 156, headerCellClass: 'textAlignRight', name: t('label24TradeVolume'), formatter: ({ row }) => { const { priceU } = row?.tradeFloat ?? {} return ( {priceU ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(priceU).times(forexValue), undefined, undefined, 2, true, { isFait: true }, ) : EmptyValueTag} ) }, }, { key: 'APR', sortable: true, name: t('labelAPR'), width: 'auto', maxWidth: 68, headerCellClass: 'textAlignRight', formatter: ({ row, rowIdx }) => { const APR = typeof row.APR !== undefined && row.APR ? row?.APR : EmptyValueTag const popoverState = getPopoverState(rowIdx.toString()) return ( {APR === 0 || typeof APR === 'undefined' || APR == EmptyValueTag ? EmptyValueTag : getValuePrecisionThousand(APR, 2, 2, 2, true) + '%'} {!(APR === 0 || typeof APR === 'undefined' || APR == EmptyValueTag) && ( )} ) }, }, { key: 'action', name: t('labelAction'), // maxWidth: 120, width: 'auto', headerCellClass: `textAlignRight`, cellClass: () => `action`, formatter: ({ row }) => { return ( ) }, }, ] const columnModeMobile = (): Column[] => [ { key: 'pools', sortable: true, width: 'auto', name: t('labelPool'), formatter: ({ row }: FormatterProps) => { return ( {/**/} {/* */} {/* */} {/* {row.coinAInfo?.simpleName}*/} {/* */} {/* */} {/* /*/} {/* */} {/* */} {/* {row.coinBInfo?.simpleName}*/} {/* */} {/* */} {/* {campaignTagConfig && (*/} {/* */} {/* )}*/} {/* {row.isNew && }*/} {/**/} ) }, }, { key: 'liquidity', sortable: true, headerCellClass: 'textAlignRight', name: t('labelLiquidity'), formatter: ({ row }) => { const { coinA, coinB, totalA, totalB, amountU } = row as any return ( {typeof amountU === 'undefined' || !Number(amountU) ? EmptyValueTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(amountU).times(forexValue), undefined, undefined, 2, true, { isFait: true }, )} {getValuePrecisionThousand(totalA, undefined, 2, 2, true, { isAbbreviate: true, abbreviate: 3, }) + ' ' + coinA + ` + ` + getValuePrecisionThousand(totalB, undefined, 2, 2, true, { isAbbreviate: true, abbreviate: 3, }) + ' ' + coinB} ) }, }, { key: 'volume24', sortable: true, width: 'auto', headerCellClass: 'textAlignRight', name: t('label24VolumeSimple', { ns: 'common' }) + '/' + t('labelAPR'), formatter: ({ row }) => { const { priceU } = row?.tradeFloat ?? {} const APR = typeof row.APR !== undefined && row.APR ? row?.APR : EmptyValueTag return ( {priceU ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(priceU).times(forexValue), undefined, undefined, 2, true, { isFait: true }, ) : EmptyValueTag} APR: {APR === EmptyValueTag || typeof APR === 'undefined' ? EmptyValueTag : getValuePrecisionThousand(APR, 2, 2, 2, true) + '%'} ) }, }, { key: 'action', name: '', headerCellClass: 'textAlignRight', formatter: ({ row }) => { const popoverProps: PopoverWrapProps = { type: PopoverType.click, popupId: 'testPopup', className: 'arrow-none', children: , popoverContent: , anchorOrigin: { vertical: 'bottom', horizontal: 'right', }, transformOrigin: { vertical: 'top', horizontal: 'right', }, } as PopoverWrapProps return ( ) }, }, ] const defaultArgs: TableProps = { rawData, columnMode: isMobile ? columnModeMobile() : columnMode(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }) => columnsRaw as Column[], } return ( {t('labelTitleOverviewAllPrd', { ns: 'common' })} {showFilter && ( handleFilterChange({ searchValue: value })} /> )}
    ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/poolsTable/index.ts ================================================ export * from './PoolsTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/redPacketTable/Interface.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import { ClaimToken, CoinInfo, ForexMap, TokenType } from '@loopring-web/common-resources' import { XOR } from '../../../types/lib' export type RawDataRedPacketRecordsItem = { token: (CoinInfo | sdk.UserNFTBalanceInfo) & { type: TokenType } type: sdk.LuckyTokenType status: sdk.LuckyTokenItemStatus validSince: number validUntil: number totalCount: number remainCount: number totalAmount: string remainAmount: string createdAt: number rawData: sdk.LuckyTokenItemForReceive } export type RawDataRedPacketReceivesItem = { token: (CoinInfo | sdk.UserNFTBalanceInfo) & { type: TokenType } amount: string type: sdk.LuckyTokenType status: sdk.LuckyTokenItemStatus claimAt: number sender: string rawData: any } export type RawDataRedPacketBlindBoxReceivesItem = { token: (CoinInfo | sdk.UserNFTBalanceInfo) & { type: TokenType } amount: string type: sdk.LuckyTokenType status: sdk.LuckyTokenItemStatus claimAt: number sender: string rawData: any } export type RawDataRedPacketClaimItem = { token: CoinInfo & { type: TokenType } amountStr: string volume: number rawData: any } export type RawDataNFTRedPacketClaimItem = Omit & { token: sdk.UserNFTBalanceInfo & { type: TokenType.nft } } export type RawDataRedPacketDetailItem = { accountStr: string isSelf: boolean amountStr: string createdAt: number rawData: any isMax: boolean helper?: string } export type RedPacketClaimTableProps = { rawData: R[] showloading: boolean forexMap: ForexMap onItemClick: (item: ClaimToken) => void onViewMoreClick?: (type: 'NFTs' | 'blindbox') => void etherscanBaseUrl: string isNFT?: boolean getClaimRedPacket: (props: any) => void totalLuckyTokenNFTBalance?: number hideAssets?: boolean blindBoxBalance?: number } & XOR< { pagination?: { pageSize: number total: number } page?: number }, {} > export interface RedPacketRecordsTableProps { rawData: R[] tokenType: TokenType showloading: boolean forexMap: ForexMap onItemClick: (item: sdk.LuckyTokenItemForReceive) => void etherscanBaseUrl: string pagination?: { pageSize: number total: number } getMyRedPacketRecordTxList: (props: any) => void tableType: 'token' | 'NFT' | 'blindbox' } export interface RedPacketReceiveTableProps { rawData: R[] tokenType: TokenType showloading: boolean forexMap: ForexMap etherscanBaseUrl: string pagination?: { pageSize: number total: number } onItemClick: (item: sdk.LuckTokenHistory) => void onClaimItem: (item: sdk.LuckTokenHistory) => void getRedPacketReceiveList: (props: any) => void showActionableRecords: boolean isUncliamedNFT?: boolean page: number setPage: (item: number) => void } export interface RedPacketBlindBoxReceiveTableProps { rawData: R[] showloading: boolean forexMap: ForexMap etherscanBaseUrl: string pagination?: { pageSize: number total: number } onItemClick: ( item: sdk.LuckyTokenBlindBoxItemReceive, pageInfo?: { offset: number; limit: number; filter: any }, ) => any onItemClickOpen: ( item: sdk.LuckyTokenBlindBoxItemReceive, pageInfo?: { offset: number; limit: number; filter: any }, ) => any getRedPacketReceiveList: (props: any) => void showActionableRecords: boolean isUnclaimed?: boolean page: number setPage: (item: number) => void } export enum LuckyTokenItemStatusMap { SUBMITTING = 0, NOT_EFFECTIVE = 1, PENDING = 2, COMPLETED = 3, OVER_DUE = 4, FAILED = 5, } export interface aRedPacketDetailTableProps { rawData: R[] showloading: boolean forexMap: ForexMap onItemClick: (item: R) => void etherscanBaseUrl: string getClaimRedPacket: (props: any) => void } ================================================ FILE: packages/component-lib/src/components/tableList/redPacketTable/RedPacketBlindBoxReceiveTable.tsx ================================================ import styled from '@emotion/styled' import { Box, Tooltip, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import { BoxNFT, Button, Column, NftImageStyle, Table, TablePagination } from '../../basic-lib' import { CoinInfo, EmptyValueTag, getValuePrecisionThousand, globalSetup, hexToRGB, myLog, RowConfig, TokenType, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import { RawDataRedPacketBlindBoxReceivesItem, RedPacketBlindBoxReceiveTableProps, } from './Interface' import React from 'react' import { FormatterProps } from 'react-data-grid' import _ from 'lodash' import moment from 'moment' import * as sdk from '@loopring-web/loopring-sdk' import { ColumnCoinDeep } from '../assetsTable' import TextTooltip from './textTooltip' import { useTheme } from '@emotion/react' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` const TableStyled = styled(Table)<{ isUnclaimed: boolean }>` &.rdg { --template-columns: 25% 25% 25% 25% !important; height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any const RowHeight = 55 export const RedPacketBlindBoxReceiveTable = withTranslation(['tables', 'common'])( ( props: RedPacketBlindBoxReceiveTableProps & WithTranslation, ) => { const { getRedPacketReceiveList, pagination, rawData, showloading, t, onItemClick, showActionableRecords, isUnclaimed, page, setPage, onItemClickOpen } = props const updateData = _.debounce(async ({ page = 1, filter = {} }: any) => { await getRedPacketReceiveList({ offset: (page - 1) * (pagination?.pageSize ?? 10), limit: pagination?.pageSize ?? 10, filter: { ...filter, statuses: showActionableRecords ? [0] // 0 is for sdk.BlindBoxStatus.NOT_OPENED : undefined, }, }) }, globalSetup.wait) const theme = useTheme() const handlePageChange = React.useCallback( ({ page = 1 }: any) => { setPage(page) myLog('RedPacket Receive page,', page) updateData({ page, }) }, [updateData], ) React.useEffect(() => { updateData.cancel() handlePageChange({ page: 1 }) return () => { updateData.cancel() } }, [showActionableRecords]) const exclusiveTag = ( {t('labelRedPacketExclusiveTag', { ns: 'common' })} ) const columnModeTransaction = [ { key: 'Token', name: t('labelToken'), formatter: ({ row }: FormatterProps) => { const isTarget = row.rawData.luckyToken.type.scope === sdk.LuckyTokenViewType.TARGET if (row.rawData.luckyToken.isNft) { const metadata = row.rawData.luckyToken.nftTokenInfo?.metadata return ( {metadata?.imageSize ? ( {metadata?.imageSize && ( )} ) : ( )} {metadata?.base?.name ?? 'NFT'} {isTarget && exclusiveTag} ) } else { const _token = row.token as CoinInfo & { type: TokenType } return ( {isTarget && exclusiveTag} ) } }, }, { key: 'Amount', name: t('labelAmount'), formatter: ({ row }: FormatterProps) => { const { token } = row if (token && token.type === TokenType.single) { const { decimals, precision } = token as unknown as { decimals: number precision: number } return ( <> {row.rawData.claim.amount ? getValuePrecisionThousand( sdk.toBig(row.rawData.claim.amount).div('1e' + decimals), precision, precision, precision, false, ) : EmptyValueTag} ) } else { return <>{row.rawData.claim.amount ? row.rawData.claim.amount : EmptyValueTag} } }, }, { key: 'ReceiveTime', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelReceiveTime'), formatter: ({ row }: FormatterProps) => { return <>{moment(new Date(row.claimAt), 'YYYYMMDDHHMM').fromNow()} }, }, { key: 'Status', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelRecordStatus'), formatter: ({ row }: FormatterProps) => { if (row.rawData.luckyToken.validUntil > Date.now()) { return ( {t('labelRedpacketCantOpen', { time: moment(row.rawData.luckyToken.validUntil).format( YEAR_DAY_MINUTE_FORMAT, ), interpolation: { escapeValue: false, }, })} } > {t('labelRedPacketOpen', { ns: 'common' })} ) } else if (row.rawData.claim.status === sdk.BlindBoxStatus.OPENED) { return <>{t('labelBlindBoxOpend')} } else if (row.rawData.claim.status === sdk.BlindBoxStatus.EXPIRED) { return <>{t('labelBlindBoxExpired')} } else if (row.rawData.claim.status === sdk.BlindBoxStatus.NOT_OPENED) { return ( {t('labelBlindBoxExpiredTime', { time: moment(row.rawData.luckyToken.nftExpireTime).format( YEAR_DAY_MINUTE_FORMAT, ), interpolation: { escapeValue: false, }, })} ) } }, }, ] as Column[] const columnModeTransactionUnClaimed = [ { key: 'Token', name: t('labelToken'), formatter: ({ row }: FormatterProps) => { if (row.rawData.luckyToken.isNft) { const metadata = row.rawData.luckyToken.nftTokenInfo?.metadata return ( {metadata?.imageSize ? ( {metadata?.imageSize && ( )} ) : ( )} {metadata?.base?.name ?? 'NFT'} ) } else { const _token = row.token as CoinInfo & { type: TokenType } return ( ) } }, }, { key: 'RevealTime', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: ( ), formatter: ({ row }: FormatterProps) => { return ( <> {moment(new Date(row.rawData.luckyToken.validUntil)).format(YEAR_DAY_MINUTE_FORMAT)} ) }, }, { key: 'ExpiredTime', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: ( ), formatter: ({ row }: FormatterProps) => { return ( <>{moment(new Date(row.rawData.claim.expireTime)).format(YEAR_DAY_MINUTE_FORMAT)} ) }, }, { key: 'Status', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelAction'), formatter: ({ row }: FormatterProps) => { if (row.rawData.luckyToken.validUntil > Date.now()) { return ( {t('labelRedpacketCantOpen', { time: moment(row.rawData.luckyToken.validUntil).format( YEAR_DAY_MINUTE_FORMAT, ), interpolation: { escapeValue: false, }, })} } > {t('labelRedPacketOpen', { ns: 'common' })} ) } else if (row.rawData.claim.status === sdk.BlindBoxStatus.OPENED) { return <>{t('labelBlindBoxOpend')} } else if (row.rawData.claim.status === sdk.BlindBoxStatus.EXPIRED) { return <>{t('labelBlindBoxExpired')} } else if (row.rawData.claim.status === sdk.BlindBoxStatus.NOT_OPENED) { return ( ) } }, }, ] as Column[] const defaultArgs: any = { columnMode: isUnclaimed ? columnModeTransactionUnClaimed : columnModeTransaction, generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return ( { onItemClick(row.rawData, { offset: (page - 1) * (pagination?.pageSize ?? 10), limit: pagination?.pageSize ?? 10, filter: { statuses: showActionableRecords ? [0] // 0 is for sdk.BlindBoxStatus.NOT_OPENED : undefined, }, }) }} sortMethod={React.useCallback( (_sortedRows, sortColumn) => { let resultRows: R[] = [] switch (sortColumn) { case 'Token': resultRows = rawData.sort((a: R, b: R) => { if (a.token.type == TokenType.nft) { return (a.token as any)?.metadata?.base?.name?.localeCompare( (b.token as any)?.metadata?.base?.name, ) } else { return (a.token as any)?.simpleName.localeCompare( (b.token as any)?.simpleName, ) } }) break case 'Amount': resultRows = rawData.sort((a: R, b: R) => { return a.amount.localeCompare(b.amount) }) break case 'Time': resultRows = rawData.sort((a: R, b: R) => { return b.claimAt - a.claimAt }) break default: } return resultRows }, [rawData], )} headerRowHeight={RowConfig.rowHeaderHeight} {...{ ...defaultArgs, // rowRenderer: RowRenderer, ...props, rawData, showloading, }} /> {!!(pagination && pagination.total) && ( handlePageChange({ page })} /> )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/redPacketTable/RedPacketClaimTable.tsx ================================================ import styled from '@emotion/styled' import { Box, Link, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import { Column, NftImage, Table, TablePagination } from '../../basic-lib' import { WithTranslation, withTranslation } from 'react-i18next' import { RawDataNFTRedPacketClaimItem, RawDataRedPacketClaimItem, RedPacketClaimTableProps, } from './Interface' import { useHistory } from 'react-router-dom' import { useSettings } from '../../../stores' import React from 'react' import { FormatterProps } from 'react-data-grid' import { CurrencyToTag, EmptyValueTag, getValuePrecisionThousand, globalSetup, HiddenTag, myLog, PriceTag, RowConfig, TokenType, } from '@loopring-web/common-resources' import { ColumnCoinDeep } from '../assetsTable' import _ from 'lodash' import { useTheme } from '@emotion/react' import { SoursURL } from '@loopring-web/loopring-sdk' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 99%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export const RedPacketClaimTable = withTranslation(['tables', 'common'])( ( props: RedPacketClaimTableProps & WithTranslation, ) => { const { rawData, forexMap, showloading, onItemClick, onViewMoreClick, getClaimRedPacket, pagination, page, isNFT = false, totalLuckyTokenNFTBalance, hideAssets, blindBoxBalance, t, } = props const history = useHistory() const { currency } = useSettings() const updateData = _.debounce(async ({ page = 1 }: any) => { await getClaimRedPacket({ offset: (page - 1) * (pagination?.pageSize ?? 10), limit: pagination?.pageSize ?? 10, }) }, globalSetup.wait) const handlePageChange = React.useCallback( ({ page = 1 }: any) => { myLog('RedPacket Receive page,', page) updateData({ page, }) }, [updateData], ) React.useEffect(() => { updateData.cancel() handlePageChange({ page: 1 }) // updateData({}); return () => { updateData.cancel() } }, []) const theme = useTheme() const getColumnModeTransaction = React.useCallback((): Column[] => { return [ { key: 'Token', sortable: true, cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelToken'), formatter: ({ row: { token } }: FormatterProps) => { if (token.type !== TokenType.nft) { if ((token.icon && token.simpleName === 'NFTs') || token.simpleName === 'Blind Box') { return ( undefined} src={token.icon} width={28} height={28} /> {token.simpleName} ) } else { return } } else { return <> } }, }, { key: 'Amount', sortable: true, name: t('labelAmount'), formatter: ({ row }: FormatterProps) => { return {hideAssets ? HiddenTag : row.amountStr} }, }, { key: 'Value', sortable: true, name: t('labelValue'), formatter: ({ row }: FormatterProps) => { return ( {row.volume !== undefined ? hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (row.volume || 0) * (forexMap[currency] ?? 0), 2, 2, 2, true, { isFait: true }, ) : EmptyValueTag} ) }, }, { key: 'Actions', name: t('labelActions'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { if (row.token.type === TokenType.single && row.token.name === 'NFTs') { return onViewMoreClick!('NFTs')}>View More } else if (row.token.type === TokenType.single && row.token.name === 'Blind Box') { return onViewMoreClick!('blindbox')}>View More } else { return onItemClick(row.rawData)}>{t('labelClaim')} } }, }, ] }, [history, t, hideAssets, currency]) const NFTrow = { token: { icon: theme.mode === 'dark' ? SoursURL + 'images/nft_dark.png' : SoursURL + 'images/nft_light.png', name: 'NFTs', simpleName: 'NFTs', description: '', company: '', type: TokenType.single, }, amountStr: totalLuckyTokenNFTBalance ? totalLuckyTokenNFTBalance.toString() : EmptyValueTag, volume: undefined, } const blindboxRow = { token: { icon: theme.mode === 'dark' ? SoursURL + 'images/blindbox_dark.png' : SoursURL + 'images/blindbox_light.png', name: 'Blind Box', simpleName: 'Blind Box', description: '', company: '', type: TokenType.single, }, amountStr: blindBoxBalance ? blindBoxBalance.toString() : EmptyValueTag, volume: undefined, } const defaultArgs: any = { columnMode: getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } const sortMethod = React.useCallback( (_sortedRows, sortColumn) => { let resultRows: R[] = [] switch (sortColumn) { case 'Token': resultRows = rawData.sort((a: R, b: R) => { if (a.token.type == TokenType.nft) { return (a.token as any)?.metadata?.base?.name?.localeCompare( (b.token as any)?.metadata?.base?.name, ) } else { return (a.token as any)?.simpleName.localeCompare((b.token as any)?.simpleName) } }) break case 'Amount': resultRows = rawData.sort((a: R, b: R) => { return a.amountStr.localeCompare(b.amountStr) }) break case 'Value': resultRows = rawData.sort((a: R, b: R) => { return b.volume - a.volume }) break default: } return resultRows }, [rawData], ) return ( { const isNFTsOrBlindbox = row.token.type === TokenType.single && (row.token.name === 'NFTs' || row.token.name === 'Blind Box') if (!isNFTsOrBlindbox) { onItemClick(row.rawData) } }} sortMethod={sortMethod} {...{ ...defaultArgs, // rowRenderer: RowRenderer, ...props, rawData: [ ...(isNFT ? [] : [NFTrow, blindboxRow]), // if isNFT then not show nft row, else shows. ...rawData, ], showloading, }} /> {!!(pagination && pagination.total && page !== undefined) && ( handlePageChange({ page })} /> )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/redPacketTable/RedPacketReceiveTable.tsx ================================================ import styled from '@emotion/styled' import { Box, Tooltip, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import { BoxNFT, Button, Column, NftImageStyle, Table, TablePagination } from '../../basic-lib' import { CoinInfo, globalSetup, hexToRGB, myLog, RowConfig, TokenType, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import { RawDataRedPacketReceivesItem, RedPacketReceiveTableProps } from './Interface' import { useHistory } from 'react-router-dom' import React from 'react' import { FormatterProps } from 'react-data-grid' import _ from 'lodash' import moment from 'moment' import { ColumnCoinDeep } from '../assetsTable' import * as sdk from '@loopring-web/loopring-sdk' import TextTooltip from './textTooltip' import { useTheme } from '@emotion/react' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` const TableStyled = styled(Table)<{ isUnClaimedNFT: boolean; isNFT: boolean }>` &.rdg { --template-columns: ${({ isUnClaimedNFT, isNFT }) => isUnClaimedNFT ? '25% 25% 25% 25% !important' : isNFT ? '25% 25% 25% 25% !important' : '33% 33% 33% !important'}; height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export const RedPacketReceiveTable = withTranslation(['tables', 'common'])( ( props: RedPacketReceiveTableProps & WithTranslation, ) => { const { tokenType, getRedPacketReceiveList, pagination, rawData, showloading, t, onItemClick, onClaimItem, showActionableRecords, isUncliamedNFT, setPage, page, } = props // const { isMobile, upColor } = useSettings(); const history = useHistory() const updateData = _.debounce(async ({ page = 1, filter = {} }: any) => { await getRedPacketReceiveList({ offset: (page - 1) * (pagination?.pageSize ?? 12), limit: pagination?.pageSize ?? 12, filter: { ...filter, statuses: tokenType === TokenType.nft && showActionableRecords ? [0] : undefined, }, }) }, globalSetup.wait) const handlePageChange = React.useCallback( ({ page = 1 }: any) => { setPage(page) myLog('RedPacket Receive page,', page) updateData({ page, filter: { isNft: tokenType === TokenType.nft }, }) }, [updateData, tokenType], ) React.useEffect(() => { updateData.cancel() handlePageChange({ page: 1 }) return () => { updateData.cancel() } }, [tokenType, showActionableRecords]) const theme = useTheme() const fromBlindboxTag = ( {t('labelRedpacketFromBlindbox')}}> ) const exclusiveTag = ( {t('labelRedPacketExclusiveTag', { ns: 'common' })} ) const getColumnModeTransactionUnclaimedNFT = React.useCallback( (): Column[] => [ { key: 'Token', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelToken'), formatter: ({ row }: FormatterProps) => { const { token } = row const { metadata } = token as sdk.UserNFTBalanceInfo return ( {metadata?.imageSize ? ( {metadata?.imageSize && ( )} ) : ( )} {metadata?.base?.name ?? 'NFT'} {row.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && fromBlindboxTag} {row.type.scope === sdk.LuckyTokenViewType.TARGET && exclusiveTag} ) }, }, { key: 'Amount', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelAmount'), formatter: ({ row }: FormatterProps) => { return <>{`${row.amount}`} }, }, { key: 'ExpiredTime', name: ( ), formatter: ({ row }: FormatterProps) => { return ( <>{moment(new Date(row.rawData.claim.expireTime)).format(YEAR_DAY_MINUTE_FORMAT)} ) }, }, { key: 'Action', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: 'Action', formatter: ({ row }: FormatterProps) => { if (row.rawData.claim.status === sdk.ClaimRecordStatus.WAITING_CLAIM) { return ( ) } else if (row.rawData.claim.status === sdk.ClaimRecordStatus.EXPIRED) { return {t('labelBlindBoxExpired')} } else if (row.rawData.claim.status === sdk.ClaimRecordStatus.CLAIMED) { return {t('labelBlindBoxClaimed')} } else if (row.rawData.claim.status === sdk.ClaimRecordStatus.CLAIMING) { return {t('labelRedPacketClaiming')} } return <> }, }, ], [history, t, tokenType], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Token', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelToken'), formatter: ({ row }: FormatterProps) => { const { token } = row if (token.type === TokenType.single) { const _token = token as CoinInfo & { type: TokenType } return ( {row.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && fromBlindboxTag} {row.type.scope === sdk.LuckyTokenViewType.TARGET && exclusiveTag} ) } else { const { metadata } = token as sdk.UserNFTBalanceInfo return ( {metadata?.imageSize ? ( {metadata?.imageSize && ( )} ) : ( )} {metadata?.base?.name ?? 'NFT'} {row.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && fromBlindboxTag} {row.type.scope === sdk.LuckyTokenViewType.TARGET && exclusiveTag} ) } }, }, { key: 'Amount', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelAmount'), formatter: ({ row }: FormatterProps) => { return <>{`${row.amount}`} }, }, ...(tokenType === TokenType.nft ? [ { key: 'Action', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelRecordStatus'), formatter: ({ row }: FormatterProps) => { if (row.rawData.claim.status === sdk.ClaimRecordStatus.WAITING_CLAIM) { return ( ) } else if (row.rawData.claim.status === sdk.ClaimRecordStatus.EXPIRED) { return {t('labelBlindBoxExpired')} } else if (row.rawData.claim.status === sdk.ClaimRecordStatus.CLAIMED) { return {t('labelBlindBoxClaimed')} } else if (row.rawData.claim.status === sdk.ClaimRecordStatus.CLAIMING) { return {t('labelRedPacketClaiming')} } else { return <> } }, }, ] : []), { key: 'Time', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelReceiveTime'), formatter: ({ row }: FormatterProps) => { return <>{moment(new Date(row.claimAt), 'YYYYMMDDHHMM').fromNow()} }, }, ], [history, t, tokenType], ) const defaultArgs: any = { columnMode: isUncliamedNFT ? getColumnModeTransactionUnclaimedNFT() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return ( { onItemClick(row.rawData) }} sortMethod={React.useCallback( (_sortedRows, sortColumn) => { let resultRows: R[] = [] switch (sortColumn) { case 'Token': resultRows = rawData.sort((a: R, b: R) => { if (a.token.type == TokenType.nft) { return (a.token as any)?.metadata?.base?.name?.localeCompare( (b.token as any)?.metadata?.base?.name, ) } else { return (a.token as any)?.simpleName.localeCompare( (b.token as any)?.simpleName, ) } }) break case 'Amount': resultRows = rawData.sort((a: R, b: R) => { return a.amount.localeCompare(b.amount) }) break case 'Time': resultRows = rawData.sort((a: R, b: R) => { return b.claimAt - a.claimAt }) break default: } return resultRows }, [rawData], )} headerRowHeight={RowConfig.rowHeaderHeight} {...{ ...defaultArgs, // rowRenderer: RowRenderer, ...props, rawData, showloading, }} /> {!!(pagination && pagination.total) && ( handlePageChange({ page })} /> )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/redPacketTable/RedPacketRecodTable.tsx ================================================ import styled from '@emotion/styled' import { Box, Tooltip, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import { BoxNFT, Column, NftImageStyle, Table, TablePagination } from '../../basic-lib' import { CoinInfo, globalSetup, hexToRGB, myLog, RowConfig, SoursURL, TokenType } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import * as sdk from '@loopring-web/loopring-sdk' import { LuckyTokenItemStatusMap, RawDataRedPacketRecordsItem, RedPacketRecordsTableProps, } from './Interface' import { useHistory } from 'react-router-dom' import React from 'react' import { FormatterProps } from 'react-data-grid' import _ from 'lodash' import moment from 'moment' import { ColumnCoinDeep } from '../assetsTable' import { useTheme } from '@emotion/react' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` const TableStyled = styled(Table)<{ isBlindbox: boolean }>` &.rdg { --template-columns: ${({ isBlindbox }) => isBlindbox ? '30% 25% 25% 20% !important' : '22% 12% 24% auto auto auto !important'}; height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export const RedPacketRecordTable = withTranslation(['tables', 'common'])( ( props: RedPacketRecordsTableProps & WithTranslation, ) => { const { getMyRedPacketRecordTxList, pagination, rawData, showloading, onItemClick, tokenType, tableType, t, } = props const history = useHistory() const [page, setPage] = React.useState(1) const updateData = _.debounce(async ({ page = 1, filter = {} }: any) => { await getMyRedPacketRecordTxList({ offset: (page - 1) * (pagination?.pageSize ?? 10), limit: pagination?.pageSize ?? 10, filter, }) }, globalSetup.wait) const handlePageChange = React.useCallback( ({ page = 1 }: any) => { setPage(page) myLog('AmmTable page,', page) updateData({ page, filter: { isNft: tableType === 'NFT' ? true : tableType === 'token' ? false : undefined, modes: tableType === 'blindbox' ? 2 : [0, 1], }, }) }, [updateData, tableType], ) const theme = useTheme() const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Token', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelRecordToken'), formatter: ({ row }: FormatterProps) => { const {token} = row const blindBoxTag = ( {t('labelRedpacketFromBlindbox')}}> ) const exclusiveTag = Exclusive if (token.type === TokenType.single) { const _token = token as CoinInfo & { type: TokenType } return ( {row.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && tableType !== 'blindbox' && blindBoxTag} {row.type.scope === sdk.LuckyTokenViewType.TARGET && exclusiveTag} ) } else { const { metadata } = token as sdk.UserNFTBalanceInfo return ( {metadata?.imageSize ? ( {metadata?.imageSize && ( )} ) : ( )} {metadata?.base?.name ?? 'NFT'} {row.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && tableType !== 'blindbox' && blindBoxTag} {row.type.scope === sdk.LuckyTokenViewType.TARGET && exclusiveTag} ) } }, }, { key: 'Amount', name: t('labelRecordAmount'), formatter: ({ row }: FormatterProps) => { return <>{row.totalAmount} }, }, ...(tableType !== 'blindbox' ? [ { key: 'Type', name: t('labelRecordType'), formatter: ({ row }: FormatterProps) => { return ( <> {t( row.type.mode === sdk.LuckyTokenClaimType.RELAY ? 'labelLuckyRelayToken' : row.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? 'labelLuckyBlindBox' : row.type.partition === sdk.LuckyTokenAmountType.AVERAGE ? 'labelRedPacketSendAverageTitle' : 'labelRedPacketSenRandomTitle', { ns: 'common' }, ) + ' — ' + t(`labelRedPacketViewType${row?.type?.scope ?? 0}`, { ns: 'common', })} ) }, }, ] : []), { key: 'Status', name: t('labelRecordStatus'), formatter: ({ row }: FormatterProps) => { const statusMap = [ [0, t(`labelRedPacketStatusNotStarted`, { ns: 'common' })], [1, t(`labelRedPacketStatusNotStarted`, { ns: 'common' })], [2, t(`labelRedPacketStatusStarted`, { ns: 'common' })], [3, t(`labelRedPacketStatusEnded`, { ns: 'common' })], [4, t(`labelRedPacketStatusEnded`, { ns: 'common' })], [5, t(`labelRedPacketStatusEnded`, { ns: 'common' })], ] as [number, string][] const found = statusMap.find((x) => x[0] === LuckyTokenItemStatusMap[row.status]) return <>{found ? found[1] : ''} }, }, ...(tableType !== 'blindbox' ? [ { key: 'Number', sortable: true, cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelRecordNumber'), formatter: ({ row }: FormatterProps) => { return <>{`${row.totalCount - row.remainCount}/${row.totalCount}`} }, }, ] : []), { key: 'Time', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelRecordTime'), formatter: ({ row }: FormatterProps) => { return <>{moment(new Date(row.createdAt), 'YYYYMMDDHHMM').fromNow()} }, }, ], [history, onItemClick, t], ) React.useEffect(() => { updateData.cancel() handlePageChange({ page: 1 }) // updateData({}); return () => { updateData.cancel() } }, [tokenType, tableType]) const defaultArgs: any = { columnMode: getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return ( { onItemClick(row.rawData) }} rowHeight={RowConfig.rowHeight} headerRowHeight={RowConfig.rowHeaderHeight} sortMethod={React.useCallback( (_sortedRows, sortColumn) => { let resultRows: R[] = [] switch (sortColumn) { case 'Token': resultRows = rawData.sort((a: R, b: R) => { if (a.token.type == TokenType.nft) { return (a.token as any)?.metadata?.base?.name?.localeCompare( (b.token as any)?.metadata?.base?.name, ) } else { return (a.token as any)?.simpleName.localeCompare( (b.token as any)?.simpleName, ) } }) break case 'Amount': resultRows = rawData.sort((a: R, b: R) => { return a.totalAmount.localeCompare(b.totalAmount) }) break case 'Number': resultRows = rawData.sort((a: R, b: R) => { return b.totalCount - a.totalCount }) break case 'Time': resultRows = rawData.sort((a: R, b: R) => { return b.createdAt - a.createdAt }) break default: } return resultRows }, [rawData], )} {...{ ...defaultArgs, ...props, rawData, showloading, }} /> {!!(pagination && pagination.total) && ( handlePageChange({ page })} /> )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/redPacketTable/index.ts ================================================ export * from './RedPacketRecodTable' export * from './RedPacketClaimTable' export * from './RedPacketReceiveTable' export * from './Interface' export * from './RedPacketBlindBoxReceiveTable' ================================================ FILE: packages/component-lib/src/components/tableList/redPacketTable/textTooltip.tsx ================================================ import { Tooltip } from '@mui/material' type TextTooltipProps = { tooltipTitle: string text: string } const TextTooltip = ({ tooltipTitle, text }: TextTooltipProps) => { return ( {text} ) } export default TextTooltip ================================================ FILE: packages/component-lib/src/components/tableList/rewardTable/ReferralsTable.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { EmptyValueTag, getShortAddr, globalSetup, RowConfig, YEAR_DAY_FORMAT, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Typography } from '@mui/material' import moment from 'moment' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' export type ReferralsRow = sdk.ReferDownsides & { amount: { unit: string; value: string } } const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (prosp: BoxProps & { isMobile: boolean }) => JSX.Element const TableStyled = styled(Table)` &.rdg { min-height: 240px; height: ${(props: any) => { return props.currentheight + 'px' }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .logo-icon.dual:last-child { transform: scale(0.6) translate(0, 4px); } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export const ReferralsTable = withTranslation(['tables', 'common'])( ( props: { rawData: R[] pagination: { pageSize: number total: number } getList: (props: { limit: number; offset: number }) => void showloading: boolean } & WithTranslation, ) => { const { rawData, pagination, getList, showloading, t } = props const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnMode = React.useCallback( (): Column[] => [ { key: 'time', name: t('labelReferralsTableTime'), headerCellClass: 'textAlignLeft', cellClass: 'textAlignLeft', formatter: ({ row }) => { return ( <> {Number.isFinite(row.startAt) ? moment(new Date(row.startAt), 'YYYYMMDDHHMM').format(YEAR_DAY_FORMAT) : EmptyValueTag} ) }, }, { key: 'referee', name: t('labelReferralsTableReferee'), headerCellClass: 'textAlignCenter', cellClass: 'textAlignCenter', formatter: ({ row }) => { return ( {getShortAddr(row.address)} ) }, }, { key: 'Rewards', name: t('labelReferralsTableAmount'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { return ( <> {row.amount?.value} {row.amount?.unit} ) }, }, ], [], ) const defaultArgs: any = { columnMode: getColumnMode(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { updateData.cancel() updateData({ currPage: 1 }) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( {pagination && pagination.total > pagination.pageSize && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/rewardTable/RefundTable.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { EmptyValueTag, globalSetup, RowConfig, YEAR_DAY_FORMAT, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps } from '@mui/material' import moment from 'moment' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' export type RefundRow = sdk.ReferSelf & { amount: { unit: string; value: string } } const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (prosp: BoxProps & { isMobile: boolean }) => JSX.Element const TableStyled = styled(Table)` &.rdg { min-height: 240px; height: ${(props: any) => { return props.currentheight + 'px' }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .logo-icon.dual:last-child { transform: scale(0.6) translate(0, 4px); } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export const RefundTable = withTranslation(['tables', 'common'])( ( props: { rawData: R[] pagination: { pageSize: number total: number } getList: (props: { limit: number; offset: number }) => void showloading: boolean } & WithTranslation, ) => { const { rawData, pagination, getList, showloading, t } = props const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnMode = React.useCallback( (): Column[] => [ { key: 'time', name: t('labelRefundTableTime'), headerCellClass: 'textAlignLeft', cellClass: 'textAlignLeft', formatter: ({ row }) => { return ( <> {Number.isFinite(row.startAt) ? moment(new Date(row.startAt), 'YYYYMMDDHHMM').format(YEAR_DAY_FORMAT) : EmptyValueTag} ) }, }, { key: 'Rewards', name: t('labelRefundTableAmount'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { return ( <> {row.amount?.value} {row.amount?.unit} ) }, }, ], [], ) const defaultArgs: any = { columnMode: getColumnMode(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { updateData.cancel() updateData({ currPage: 1 }) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( {pagination && pagination.total > pagination.pageSize && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/rewardTable/RewardTable.tsx ================================================ import styled from '@emotion/styled' import { Box } from '@mui/material' import { TablePaddingX } from '../../styled' import { TFunction, WithTranslation, withTranslation } from 'react-i18next' import { Column, Table } from '../../basic-lib' import moment from 'moment' import { EmptyValueTag } from '@loopring-web/common-resources' interface Row { amount: string time: number } const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: auto; .rdg { --template-columns: auto auto !important; min-height: ${({ theme }) => theme.unit * 30}px !important; } .textAlignRight { text-align: right; } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as typeof Box const getColumnModeRewardTable = ( t: TFunction, chosenCardInfo?: string, ): Column[] => { return [ { key: 'amount', name: t('labelRewardTableAmount'), formatter: ({ row, column }) => { const value = row[column.key] const renderValue = `${value} ${chosenCardInfo}` // const renderValue = `${getValuePrecisionThousand(valueFrom, undefined, undefined, precisionFrom)} ${keyFrom} \u2192 ${getValuePrecisionThousand(valueTo, precisionTo, precisionTo, precisionTo)} ${keyTo}` return
    {renderValue}
    }, }, { key: 'time', name: t('labelRewardTableTime'), headerCellClass: 'textAlignRight', formatter: ({ row, column }) => { const value = row[column.key] const renderValue = Number.isFinite(value) ? moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return (
    {renderValue}
    ) }, }, ] } export interface RewardTableProps { rawData: Row[] chosenCardInfo?: string } export const RewardTable = withTranslation('tables')( ({ chosenCardInfo, rawData, t }: RewardTableProps & WithTranslation) => { const defaultArgs: any = { rawData: [], columnMode: getColumnModeRewardTable(t, chosenCardInfo), // generateRows, generateRows: (rawData: any) => rawData, // generateColumns, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return (
    ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/rewardTable/RewardsTable.tsx ================================================ import { useTranslation, WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { CurrencyToTag, EmptyValueTag, ForexMap, getValuePrecisionThousand, HiddenTag, myLog, PriceTag, RowConfig, TokenType, } from '@loopring-web/common-resources' import { Column, Table } from '../../basic-lib' import { Box, BoxProps, Link, Tooltip, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { CoinIcons } from '../assetsTable' import * as sdk from '@loopring-web/loopring-sdk' export type EarningsDetail = { amountStr: string amount: string name?: string claimType: sdk.CLAIM_TYPE tokenValueDollar: string token: string precision: number } export type EarningsRow = { token: { type: TokenType value: string } detail: Array precision: number amountStr: string amount: string tokenValueDollar: number rawData: any } const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (prosp: BoxProps & { isMobile: boolean }) => JSX.Element const TableStyled = styled(Table)` &.rdg { min-height: 240px; height: ${(props: any) => { return props.currentheight + 'px' }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .logo-icon.dual:last-child { transform: scale(0.6) translate(0, 4px); } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any const ContentWrapperStyled = styled(Box)` padding: 0 ${({ theme }) => theme.unit * 1}px; border-radius: ${({ theme }) => theme.unit / 2}px; ` export const DetailRewardPanel = ({ detailList, hideAssets = false, }: { detailList?: EarningsDetail[] hideAssets?: boolean }) => { const { t } = useTranslation() myLog('detailLis', detailList) return ( {detailList?.map((item, index) => { if (item.amount === '0') { return } else { return ( {Reflect.ownKeys(sdk.CLAIM_TYPE)?.includes(item.claimType?.toUpperCase() ?? '') ? t(`labelClaimType${item.claimType}`) : item?.name ? item?.name : t('labelClaimOtherRewards')} {item.amount == '0' ? EmptyValueTag : hideAssets ? HiddenTag : getValuePrecisionThousand( item.amountStr, item.precision, item.precision, undefined, false, { floor: true, }, ) + ' ' + item.token} ) } })} ) } export const RewardsTable = withTranslation(['tables', 'common'])( ( props: { hideAssets?: boolean forexMap: ForexMap rawData: R[] onItemClick: (item: any) => void onDetail: (item: any) => void showloading: boolean } & WithTranslation, ) => { const { forexMap, hideAssets, rawData, onItemClick, showloading, t } = props const { currency, isMobile, coinJson } = useSettings() const getColumnMode = React.useCallback( (): Column[] => [ { key: 'token', name: t('labelToken'), formatter: ({ row, column }) => { const token = row[column.key] let tokenIcon: [any, any] = [undefined, undefined] const [head, middle, tail] = token.value.split('-') if (token.type === 'lp' && middle && tail) { tokenIcon = coinJson[middle] && coinJson[tail] ? [coinJson[middle], coinJson[tail]] : [undefined, undefined] } if (token.type !== 'lp' && head && head !== 'lp') { tokenIcon = coinJson[head] ? [coinJson[head], undefined] : [undefined, undefined] } return ( {token.value} ) }, }, { key: 'amount', name: t('labelAmount'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row.amountStr const precision = row.precision return ( { // onDetail(row) // }} > {row.amount === '0' ? ( EmptyValueTag ) : ( } > {hideAssets ? HiddenTag : getValuePrecisionThousand(value, precision, precision, undefined, false, { floor: true, })} )} ) }, }, { key: 'value', name: t('labelAssetsTableValue'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { return ( {row.amount === '0' ? EmptyValueTag : hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (row?.tokenValueDollar || 0) * (forexMap[currency] ?? 0), undefined, undefined, undefined, true, { isFait: true, floor: true }, )} ) }, }, { key: 'Actions', name: t('labelActions'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { return ( {row.amount === '0' ? ( EmptyValueTag ) : ( onItemClick(row)}>{t('labelClaim')} )} ) }, }, ], [hideAssets, forexMap, currency], ) const defaultArgs: any = { columnMode: getColumnMode(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return ( ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/rewardTable/index.ts ================================================ export * from './ReferralsTable' export * from './RewardTable' export * from './RefundTable' export * from './RewardsTable' ================================================ FILE: packages/component-lib/src/components/tableList/tableList.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import { QuoteTable, QuoteTableRawDataItem } from './QuoteTable/QuoteTable' import { OrderHistoryTable } from './orderHistoryTable' import { RawDataTransactionItem, TransactionTable } from './transactionsTable' import { OrderHistoryRawDataItem } from './orderHistoryTable/OrderHistoryTable' import { TradeStatus, TradeTypes } from '@loopring-web/common-resources' import { TradeRaceTable } from './index' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` // type RawDataItem = (string | number | number[] | string[])[] | {} const rawDatacoinBPrice: QuoteTableRawDataItem[] = [ { pair: { coinA: 'LRC', coinB: 'BUSD', }, close: 12.4, floatTag: 'decrease', change: 8.12, high: 123.34, low: 23.41, volume: 21759000, changeU: 21759000, closeU: 21759000, coinApriceU: 21759000, precision: 3, priceU: 3, reward: 3, rewardToken: '3', timeUnit: '24h', open: 100, __rawTicker__: {} as any, }, ] const rawDataOrderHistory: OrderHistoryRawDataItem[] = [ { market: 'ETH-LRC', side: TradeTypes.Sell, amount: { from: { key: 'ETH', value: 1231 }, to: { key: 'ETH', value: 1231 }, }, average: '12', // filledAmount: OrderPair; price: { key: 'ETH', value: 1231, }, time: 1223, status: TradeStatus.Expired, hash: 'xxxxxxxxxxx', orderId: 'xxxxxx', __raw__: {} as any, }, { market: 'ETH-LRC', side: TradeTypes.Sell, amount: { from: { key: 'ETH', value: 1231 }, to: { key: 'ETH', value: 1231 }, }, average: '12', // filledAmount: OrderPair; price: { key: 'ETH', value: 1231, }, time: 1223, status: TradeStatus.Expired, hash: 'xxxxxxxxxxx', orderId: 'xxxxxx', __raw__: {} as any, }, { market: 'ETH-LRC', side: TradeTypes.Sell, amount: { from: { key: 'ETH', value: 1231 }, to: { key: 'ETH', value: 1231 }, }, average: '12', // filledAmount: OrderPair; price: { key: 'ETH', value: 1231, }, time: 1223, status: TradeStatus.Expired, hash: 'xxxxxxxxxxx', orderId: 'xxxxxx', __raw__: {} as any, }, { market: 'ETH-LRC', side: TradeTypes.Sell, amount: { from: { key: 'ETH', value: 1231 }, to: { key: 'ETH', value: 1231 }, }, average: '12', // filledAmount: OrderPair; price: { key: 'ETH', value: 1231, }, time: 1223, status: TradeStatus.Expired, hash: 'xxxxxxxxxxx', orderId: 'xxxxxx', __raw__: {} as any, }, ] const rawDataTransaction: RawDataTransactionItem[] = [] const rawRank: any[] = [ { rank: 1, address: '0x...1', xxx: 'xxx', }, { rank: 2, address: '0x...1', xxx: 'xxx', }, { rank: 3, address: '0x...1', xxx: 'xxx', }, { rank: 4, address: '0x...1', xxx: 'xxx', }, ] const Template: Story = withTranslation()((args: any) => { const { type } = args return ( <> ) }) as Story // @ts-ignore export const OrderHistory = Template.bind({}) // @ts-ignore export const Quote = Template.bind({}) // @ts-ignore export const Transaction = Template.bind({}) // @ts-ignore export const TradeRace = Template.bind({}) Quote.args = { rawData: rawDatacoinBPrice, type: 'coinBPrice', onVisibleRowsChange: (data: any) => { console.log(data) }, } OrderHistory.args = { rawData: rawDataOrderHistory, type: 'orderHistory', showFilter: true, // pagination: { // pageSize: 5 // } } Transaction.args = { rawData: rawDataTransaction, type: 'transaction', pagination: { pageSize: 5, }, showFilter: true, } TradeRace.args = { rawData: rawRank, type: 'rank', column: [ { key: 'rank', label: 'rank', }, { key: 'address', label: 'address', }, { key: 'xxx', label: 'xxx', }, ], } export default { title: 'components/TableList', component: QuoteTable, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/taikoFarmingTable/Interface.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' export type RawDataDefiTxsItem = Partial export interface DefiTxsTableProps { rawData: R[] pagination?: { pageSize: number total: number } idIndex: { [key: string]: string } tokenMap: { [key: string]: any } getDefiTxList: (props: any) => Promise showloading: boolean } export interface RawDataDefiSideStakingItem extends sdk.StakeInfoOrigin, Omit { status_product: number } export interface DefiSideStakingTableProps { rawData: R[] pagination?: { pageSize: number total: number } idIndex: { [key: string]: string } tokenMap: { [key: string]: any } geDefiSideStakingList: (props: any) => Promise showloading: boolean redeemItemClick: (item: R) => void hideAssets?: boolean noMinHeight?: boolean } export type RawDataTaikoFarmingTxItem = sdk.STACKING_TRANSACTIONS & { stakingType: string } export interface TaikoFarmingTxTableProps { rawData: R[] pagination?: { pageSize: number total: number } idIndex: { [key: string]: string } tokenMap: { [key: string]: any } getSideStakingTxList: (props: any) => Promise showloading: boolean } ================================================ FILE: packages/component-lib/src/components/tableList/taikoFarmingTable/TaikoFarmingPortfolioTable.tsx ================================================ import _ from 'lodash' import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { globalSetup, RowInvestConfig, TokenType } from '@loopring-web/common-resources' import { Column, ModalCloseButton, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Modal, Typography } from '@mui/material' import { SwitchPanelStyled, TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { DefiSideStakingTableProps } from './Interface' import { ConfirmStackingRedeem } from '../../tradePanel' import { CoinIcons } from '../assetsTable' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 33% 34% 33% !important;` : `--template-columns: 33% 34% 33% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })}; ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight) { return props.currentheight + 'px' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any interface Item { coinJSON: any tokenSymbol: string amount: string value: string } export const TaikoFarmingPortfolioTable = withTranslation(['tables', 'common'])( (props: DefiSideStakingTableProps & WithTranslation) => { const { rawData, idIndex, pagination, tokenMap, geDefiSideStakingList, showloading, // onDetailClick, redeemItemClick: _redeemItemClick, hideAssets, noMinHeight, t, } = props const [openDetail, setOpenDetail] = React.useState(false) const [openAlert, setOpenAlert] = React.useState(false) const [detail, setDetail] = React.useState(undefined) const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { geDefiSideStakingList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'token', sortable: false, width: 'auto', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelToken'), formatter: ({ row }) => { return ( {row.tokenSymbol} ) }, }, { key: 'Amount', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelAmount'), formatter: ({ row }) => { return <> {row.amount} }, }, { key: 'Value', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelValue'), formatter: ({ row }) => { return <> {row.value} }, }, ], [t, tokenMap, idIndex, hideAssets], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'token', sortable: false, width: 'auto', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelToken'), formatter: ({ row }) => { return ( <> {row.tokenSymbol} ) }, }, { key: 'Amount', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: t('labelAmount'), formatter: ({ row }) => { return <> {row.amount} }, }, { key: 'Value', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelValue'), formatter: ({ row }) => { return <> {row.value} }, }, ], [t, tokenMap, idIndex], ) const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return ( 0 ? RowInvestConfig.rowHeaderHeight + rawData.length * RowInvestConfig.rowHeight : 350 } rowHeight={RowInvestConfig.rowHeight} headerRowHeight={RowInvestConfig.rowHeaderHeight} style={{ minHeight:noMinHeight ? 0 : undefined }} {...{ ...defaultArgs, ...props, rawData, showloading, }} /> {!!(pagination && pagination.total > pagination.pageSize) && ( )} setOpenDetail(false)} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description' > setOpenDetail(false)} t={t} /> {detail && ( {t('labelDeFiSideInvestmentDetails', { ns: 'common', symbol: 'LRC', })} )} { setOpenAlert(false) if (isAgree) { _redeemItemClick(detail as any) } }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/taikoFarmingTable/TaikoTarmingTxRecordsTable.tsx ================================================ import _ from 'lodash' import { WithTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { EmptyValueTag, getShortAddr, getValuePrecisionThousand, globalSetup, Info2Icon, } from '@loopring-web/common-resources' import { Column, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Tooltip, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { RawDataTaikoFarmingTxItem, TaikoFarmingTxTableProps } from './Interface' import * as sdk from '@loopring-web/loopring-sdk' import moment from 'moment' const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 30% 40% 30% !important;` : `--template-columns: 20% 45% 35% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export const TaikoTarmingTxRecordsTable = withTranslation(['tables', 'common'])( ( props: TaikoFarmingTxTableProps & WithTranslation, ) => { const { rawData, idIndex, pagination, tokenMap, getSideStakingTxList, showloading, t } = props const { isMobile } = useSettings() const [page, setPage] = React.useState(1) const updateData = _.debounce( ({ // tableType, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { // tableType: TableType; currPage?: number pageSize?: number }) => { getSideStakingTxList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ currPage: currPage }) }, [updateData, page], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Type', sortable: false, width: 'auto', name: t('labelDefiStakingTxType'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { let side = { color: 'var(--color-text-primary)', // @ts-ignore type: `labelStakeTransactionType${row.stakingType}`, } const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.amount ? getValuePrecisionThousand( sdk.toBig(row.amount).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + (row.stakingType === sdk.StakeTransactionType.claim ? 'lrTAIKO' : tokenInfo.symbol) : EmptyValueTag switch (row.stakingType) { case sdk.StakeTransactionType.subscribe: side = { color: 'var(--color-success)', type: 'labelLock', } break case sdk.StakeTransactionType.redeem: side = { ...side, color: 'var(--color-success)', } break case sdk.StakeTransactionType.claim: side = { ...side, color: 'var(--color-success)', } break } return ( {row.stakingType === sdk.StakeTransactionType.redeem ? 'Settlement' : t(side.type)} {row.stakingType === sdk.StakeTransactionType.redeem && ( )} {amountStr} ) }, }, { key: 'HashID', sortable: false, width: 'auto', cellClass: 'textAlignCenter', headerCellClass: 'textAlignCenter', name: 'Hash', formatter: ({ row }) => { return <>{row.hash ? getShortAddr(row.hash) : EmptyValueTag} }, }, { key: 'SubscribeTime', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelLockTime'), formatter: ({ row }: FormatterProps) => { return ( {typeof row.createdAt === 'undefined' ? EmptyValueTag : moment(new Date(row.createdAt), 'YYYYMMDDHHMM').fromNow()} ) }, }, ], [t, tokenMap, idIndex], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'Type', sortable: false, width: 'auto', name: t('labelDefiStakingTxType'), cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { let side = { color: 'var(--color-text-primary)', // @ts-ignore type: `labelStakeTransactionType${row.stakingType}`, } // @ts-ignore switch (row.stakingType) { case sdk.StakeTransactionType.subscribe: side = { ...side, color: 'var(--color-success)', } break case sdk.StakeTransactionType.redeem: side = { ...side, color: 'var(--color-error)', } break case sdk.StakeTransactionType.claim: side = { ...side, color: 'var(--color-warning)', } break } return ( {t(side.type)} ) }, }, { key: 'Product', sortable: false, width: 'auto', name: t('labelDefiStakingTxAmount'), cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', formatter: ({ row }: FormatterProps) => { const tokenInfo = tokenMap[idIndex[row.tokenId ?? '']] const amountStr = row.amount && row.amount != '0' ? getValuePrecisionThousand( sdk.toBig(row.amount).div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + tokenInfo.symbol : EmptyValueTag return ( {amountStr} ) }, }, { key: 'SubscribeTime', sortable: false, width: 'auto', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelLockTime'), formatter: ({ row }: FormatterProps) => { return ( {typeof row.createdAt === 'undefined' ? EmptyValueTag : moment(new Date(row.createdAt), 'YYYYMMDDHHMM').fromNow()} ) }, }, ], [t, tokenMap, idIndex], ) const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { updateData.cancel() updateData({ currPage: 1 }) return () => { updateData.cancel() } }, [pagination?.pageSize]) return (
    {pagination && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/taikoFarmingTable/index.ts ================================================ export * from './Interface' export { TaikoTarmingTxRecordsTable } from './TaikoTarmingTxRecordsTable' export { TaikoFarmingPortfolioTable } from './TaikoFarmingPortfolioTable' ================================================ FILE: packages/component-lib/src/components/tableList/tradeNFTTable/Interface.ts ================================================ export type NFTTradeFilter = { side: undefined | 'SELL' | 'BUY' limit?: number offset?: number start?: number end?: number // pageSize:number, // duration?: DateRange; page?: number } export enum FilterTradeNFTTypes { // maker = "Maker", // taker = "Taker", allTypes = 'all', sell = 'sell', buy = 'buy', self = 'self', } export type NFTTradeProps = NFTTradeFilter & { etherscanBaseUrl?: string rawData: Row[] pagination?: { pageSize: number total: number page: number } idIndex: { [key: string]: string } tokenMap: any getTradeList: (filter: NFTTradeFilter) => Promise showFilter?: boolean showLoading: boolean accAddress: string accountId: number currentHeight: number rowHeight?: number headerRowHeight?: number // accAddress?: string; } ================================================ FILE: packages/component-lib/src/components/tableList/tradeNFTTable/TradeNFTTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Link, Typography } from '@mui/material' import styled from '@emotion/styled' import { TFunction, Trans, withTranslation, WithTranslation } from 'react-i18next' import moment from 'moment' import { BoxNFT, Column, NftImage, Table, TablePagination } from '../../basic-lib' import { TableFilterStyled, TablePaddingX } from '../../styled' import { Filter, FilterTradeTypes } from './components/Filter' import { EmptyValueTag, Explorer, getShortAddr, getValuePrecisionThousand, globalSetup, RowConfig, TableType, UNIX_TIMESTAMP_FORMAT, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import * as sdk from '@loopring-web/loopring-sdk' import { Currency, NFT_IMAGE_SIZES } from '@loopring-web/loopring-sdk' import _ from 'lodash' import { FilterTradeNFTTypes, NFTTradeProps } from './Interface' import { DateRange } from '@mui/lab' const StyledSideCell: any = styled(Typography)` color: ${(props: any) => { const { value, theme: { colorBase }, } = props return value === FilterTradeNFTTypes.sell ? colorBase.success : colorBase.error }}; ` const TableStyled = styled(Box)< BoxProps & { isMobile?: boolean currentheight?: number } >` display: flex; flex-direction: column; flex: 1; .rdg { height: ${(props: any) => props.currentheight}px; ${({ isMobile }) => !isMobile ? `--template-columns: 50% auto 20% auto !important; ` : ` --template-columns: 70% 30% !important;`} .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .rdg-header-row { } .textAlignRight { text-align: right; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as ( props: { isMobile?: boolean currentheight?: number } & BoxProps, ) => JSX.Element const getColumnModeAssets = ( t: TFunction, _currency: Currency, tokenMap: any, accountId: number, filterType: FilterTradeNFTTypes, ): Column[] => { return [ { key: 'side', name: t('labelTradeNFTSide'), formatter: ({ row }) => { let { nftAmount, metadata, bInfo, sInfo } = row metadata = { ...metadata, ...metadata?.base, } let tradeType, fromAddr if (filterType === FilterTradeNFTTypes.buy && bInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.buy fromAddr = sInfo.address } else if (filterType === FilterTradeNFTTypes.sell && sInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.sell fromAddr = bInfo.address } else if (sInfo.accountId === accountId && bInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.self fromAddr = '' } else if (bInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.buy fromAddr = sInfo.address } else { tradeType = FilterTradeNFTTypes.sell fromAddr = bInfo.address } // const amount = Number(nftAmount ?? 0);// getValuePrecisionThousand( Number(nftAmount??0) , 0, 0, 0); return ( {metadata?.imageSize ? ( {metadata?.imageSize && ( undefined} src={metadata?.imageSize[NFT_IMAGE_SIZES.small]} /> )} ) : ( )} {tradeType === FilterTradeNFTTypes.buy ? t('labelFilterTradeNFTBuy') : tradeType === FilterTradeNFTTypes.self ? t('labelFilterTradeNFTSelf') : t('labelFilterTradeNFTSell')} {`${Number(nftAmount)} * ${ metadata?.base?.name ? metadata?.base?.name : t('labelUnknown', { ns: 'common' }) } ${ tradeType === FilterTradeNFTTypes.buy ? t('labelFrom') : tradeType === FilterTradeNFTTypes.sell ? t('labelTo') : '' } ${fromAddr && getShortAddr(fromAddr)}`} ) }, }, { key: 'price', name: t('labelTradeNFTUnitPrice'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { // const {value} = row[ "price" ]; let { price, feeTokenSymbol } = row let erc20Info = tokenMap[feeTokenSymbol] // const precision = row[ "precision" ] || 6; const renderValue = price ? getValuePrecisionThousand( price, undefined, undefined, erc20Info?.precision ?? undefined, true, { isPrice: true }, ) + ' ' + erc20Info.symbol : EmptyValueTag return {renderValue} }, }, { key: 'fee', name: t('labelTradeFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { let { sInfo, bInfo, feeTokenSymbol } = row let feeAmount if (filterType == FilterTradeNFTTypes.sell && sInfo.accountId === accountId) { feeAmount = sInfo.feeAmount } else if (filterType == FilterTradeNFTTypes.buy && bInfo.accountId === accountId) { feeAmount = bInfo.feeAmount } else if (bInfo.accountId === accountId && sInfo.accountId === accountId) { feeAmount = sdk.toBig(bInfo.feeAmount).plus(sInfo.feeAmount) } else if (bInfo.accountId === accountId) { feeAmount = bInfo.feeAmount } else { feeAmount = sInfo.feeAmount } let feeTokenInfo = tokenMap[feeTokenSymbol] const fee = feeAmount ? sdk .toBig(feeAmount ?? 0) .div('1e' + feeTokenInfo.decimals) .toString() : undefined const renderValue = fee ? getValuePrecisionThousand( fee, feeTokenInfo?.precision ?? undefined, feeTokenInfo?.precision ?? undefined, feeTokenInfo?.precision ?? undefined, true, { isPrice: true }, ) + ' ' + feeTokenInfo.symbol : EmptyValueTag // return ( // const {key, value} = row[ "fee" ]; // myLog({value}) return
    {`${renderValue}`}
    }, }, { key: 'time', name: t('labelTradeTime'), headerCellClass: 'textAlignRight', // minWidth: 400, formatter: ({ row }) => { const time = moment(new Date(row.createdAt), 'YYYYMMDDHHMM').fromNow() return
    {time}
    }, }, ] } const getColumnModeMobileAssets = ( t: TFunction, _currency: Currency, tokenMap: any, accountId: number, filterType: FilterTradeNFTTypes, ): Column[] => { return [ { key: 'side', name: t('labelTradeNFTSide') + '/' + t('labelTradeNFTUnitPrice'), formatter: ({ row }) => { let { nftAmount, metadata, price, feeTokenSymbol, bInfo, sInfo } = row metadata = { ...metadata, ...metadata?.base, } let tradeType, fromAddr if (filterType === FilterTradeNFTTypes.buy && bInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.buy fromAddr = sInfo.address } else if (filterType === FilterTradeNFTTypes.sell && sInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.sell fromAddr = bInfo.address } else if (sInfo.accountId === accountId && bInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.self fromAddr = '' } else if (bInfo.accountId === accountId) { tradeType = FilterTradeNFTTypes.buy fromAddr = sInfo.address } else { tradeType = FilterTradeNFTTypes.sell fromAddr = bInfo.address } let erc20Info = tokenMap[feeTokenSymbol] const renderValue = price ? getValuePrecisionThousand( price, undefined, undefined, erc20Info?.precision ?? undefined, true, { isPrice: true }, ) + ' ' + erc20Info.symbol : EmptyValueTag return ( {metadata?.imageSize ? ( {metadata?.imageSize && ( undefined} src={metadata?.imageSize[NFT_IMAGE_SIZES.small]} /> )} ) : ( )} {tradeType === FilterTradeNFTTypes.buy ? t('labelFilterTradeNFTBuy') : t('labelFilterTradeNFTSell')} {`${Number(nftAmount)} ${ metadata?.base?.name ? metadata?.base?.name : t('labelUnknown', { ns: 'common' }) } ${ tradeType === FilterTradeNFTTypes.buy ? t('labelFrom') : tradeType === FilterTradeNFTTypes.sell ? t('labelTo') : '' } ${fromAddr && getShortAddr(fromAddr)}`} {t('labelUPrice') + renderValue} ) }, }, { key: 'fee', name: t('labelTradeFee'), cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', formatter: ({ row }) => { let { sInfo, bInfo, feeTokenSymbol } = row const time = moment(new Date(row.createdAt), 'YYYYMMDDHHMM').fromNow() let feeAmount if (filterType == FilterTradeNFTTypes.sell && sInfo.accountId === accountId) { feeAmount = sInfo.feeAmount } else if (filterType == FilterTradeNFTTypes.buy && bInfo.accountId === accountId) { feeAmount = bInfo.feeAmount } else if (bInfo.accountId === accountId && sInfo.accountId === accountId) { feeAmount = sdk.toBig(bInfo.feeAmount).plus(sInfo.feeAmount) } else if (bInfo.accountId === accountId) { feeAmount = bInfo.feeAmount } else { feeAmount = sInfo.feeAmount } let feeTokenInfo = tokenMap[feeTokenSymbol] const fee = sdk .toBig(feeAmount) .div('1e' + feeTokenInfo.decimals) .toString() const renderValue = fee ? getValuePrecisionThousand( fee, undefined, undefined, feeTokenInfo?.precision ?? undefined, true, { isPrice: true }, ) + ' ' + feeTokenInfo.symbol : EmptyValueTag // return ( // const {key, value} = row[ "fee" ]; // myLog({value}) return ( {`${renderValue} ${feeTokenSymbol}`} {time} ) }, }, ] } export const TradeNFTTable = withTranslation('tables')( ({ t, pagination, showFilter, // idIndex, tokenMap, // filterPairs = [], rawData, currentHeight, rowHeight = RowConfig.rowHeight, getTradeList, headerRowHeight = RowConfig.rowHeaderHeight, showLoading = false, accAddress, accountId, ...rest }: WithTranslation & NFTTradeProps) => { // const {search} = useLocation(); // const searchParams = new URLSearchParams(search); const [filterType, setFilterType] = React.useState(FilterTradeNFTTypes.allTypes) const [filterDate, setFilterDate] = React.useState>([null, null]) const { currency, isMobile, defaultNetwork } = useSettings() const isTaiko = [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const defaultArgs: any = { columnMode: isMobile ? getColumnModeMobileAssets(t, currency, tokenMap, accountId, filterType) : getColumnModeAssets(t, currency, tokenMap, accountId, filterType), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], style: { backgroundColor: ({ colorBase }: any) => `${colorBase.box}`, }, } // myLog('TradeNFTTable', rawData, pagination) const pageSize = pagination ? pagination.pageSize : 10 const updateData = _.debounce( ({ // isSell, // page, tableType, // tableType, // currFilterPair = filterPair, currFilterDate = filterDate, currPage = pagination?.page || 1, currFilterType = filterType, }) => { if (tableType === TableType.filter) { currPage = 1 } const start = currFilterDate[0] ? Number(moment(currFilterDate[0]).format(UNIX_TIMESTAMP_FORMAT)) : undefined const end = currFilterDate[1] ? Number(moment(currFilterDate[1]).format(UNIX_TIMESTAMP_FORMAT)) : undefined // const market = // currFilterPair === "all" ? "" : currFilterPair.replace(/\s+/g, ""); if (getTradeList) { getTradeList({ start: start, end: end, limit: pagination?.pageSize ?? 10, offset: (currPage - 1) * (pagination?.pageSize ?? 10), page: currPage, side: currFilterType === FilterTradeNFTTypes.allTypes ? undefined : currFilterType.toUpperCase(), }) } }, globalSetup.wait, //[filterPair, filterType, pageSize, getUserTradeList, pagination] ) const handleFilterChange = React.useCallback( ({ type = filterType, date = [null, null] }) => { setFilterType(type) setFilterDate(date as any) updateData({ tableType: TableType.filter, currFilterType: type, currFilterData: date, }) }, [updateData, filterType], ) const handlePageChange = React.useCallback( (page: number) => { updateData({ tableType: TableType.page, currPage: page }) }, [updateData], ) const handleReset = () => { setFilterType(FilterTradeNFTTypes.allTypes) setFilterDate([null, null]) // setFilterPair("all"); // setFilterDate(date); updateData({ tableType: 'filter', currFilterType: FilterTradeTypes.allTypes, currFilterDate: [null, null], currPage: 1, }) } // const tradeposition = isL2Trade === true ? "layer2" : "swap"; const [isDropDown, setIsDropDown] = React.useState(true) React.useEffect(() => { let filters: any = {} updateData.cancel() handleFilterChange(filters) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( {showFilter && (isMobile && isDropDown ? ( setIsDropDown(false)} > {t('labelShowFilter')} ) : ( ))}
    {accountId && showFilter && !isTaiko && ( View transactions on block explorer )} {pagination && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/tradeNFTTable/components/Filter.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Grid, MenuItem } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { Button, DateRangePicker, TextField } from '../../../basic-lib' import { DropDownIcon } from '@loopring-web/common-resources' import { FilterTradeNFTTypes } from '../Interface' import { useSettings } from '../../../../stores' import { DateRange } from '@mui/lab' export interface FilterProps { filterDate?: DateRange filterType: FilterTradeNFTTypes handleReset: () => void handleFilterChange: ({ type, date }: any) => void } const StyledTextFiled = styled(TextField)` &.MuiTextField-root { max-width: initial; } .MuiInputBase-root { width: initial; max-width: initial; } ` export enum FilterTradeTypes { maker = 'Maker', taker = 'Taker', allTypes = 'all', } export const Filter = withTranslation('tables', { withRef: true })( ({ t, filterDate = [null, null], filterType, // filterPairs = [], // filterPair, handleReset, handleFilterChange, }: // marketMap, FilterProps & WithTranslation) => { const FilterTradeTypeList = [ { label: t('labelFilterTradeNFTAll'), value: FilterTradeNFTTypes.allTypes, }, { label: t('labelFilterTradeNFTSell'), value: FilterTradeNFTTypes.sell, }, { label: t('labelFilterTradeNFTBuy'), value: FilterTradeNFTTypes.buy, }, ] const { isMobile } = useSettings() return ( ) => { handleFilterChange({ type: event.target.value }) }} inputProps={{ IconComponent: DropDownIcon }} > {FilterTradeTypeList.map((o) => ( {o.label} ))} { handleFilterChange({ date: date }) }} /> ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/tradeNFTTable/index.ts ================================================ export * from './TradeNFTTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/tradeRaceTable/TradeRaceTable.tsx ================================================ import { PlaceComponent, Table } from '../../basic-lib' import { withTranslation } from 'react-i18next' import { getShortAddr } from '@loopring-web/common-resources' import { Box } from '@mui/material' export const TradeRaceTable = withTranslation('tables')( ({ rawData, column, showloading, scrollable, }: { rawData: R column: { key: string; label: string }[] showloading: boolean scrollable: boolean }) => { const defaultArgs: any = { columnMode: column.length ? column.map((item, index) => ({ key: item.key, name: item.label, width: 'auto', headerCellClass: index == 0 ? 'textAlignLeft' : column.length == index + 1 ? 'textAlignRight' : `textAlignCenter`, cellClass: index == 0 ? 'textAlignLeft' : column.length == index + 1 ? 'rdg-cell-value textAlignRight' : 'rdg-cell-value textAlignCenter', formatter: ({ row }: any) => { if (/address/gi.test(item.key.toLowerCase())) { return getShortAddr(row[item.key]) } else if (/rank/gi.test(item.key.toLowerCase())) { return ( ) } else { return row[item.key] ?? '' } }, })) : [], generateRows: (rawData: R) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw, } return (
    ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/tradeRaceTable/TradeRaceTableConfig.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Box } from '@mui/material' import { TablePaddingX } from '../../styled' import { Column, PlaceComponent, Table } from '../../basic-lib' import { withTranslation } from 'react-i18next' import { getShortAddr, RowConfig } from '@loopring-web/common-resources' const TableStyled = styled(Box)<{ height: number | undefined | string }>` display: flex; flex-direction: column; flex: 1; .rdg { height: ${({ height }) => height}px; --template-columns: 10% auto auto !important; height: auto; .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as typeof Box export const TradeRaceTableConfig = withTranslation('tables')( ({ rawData, column, showloading, scrollable, t, ...props }: any) => { const getColumnMode = React.useCallback( (): Column[] => column.length ? column.map((item: any, index: number) => ({ key: item.key, name: item.label, width: 'auto', headerCellClass: index == 0 ? 'textAlignLeft' : column.length == index + 1 ? 'textAlignRight' : `textAlignCenter`, cellClass: index == 0 ? 'textAlignLeft' : column.length == index + 1 ? 'rdg-cell-value textAlignRight' : 'rdg-cell-value textAlignCenter', formatter: ({ row }: any) => { if (/address/gi.test(item.key.toLowerCase())) { return getShortAddr(row[item.key]) } if (/rank/gi.test(item.key.toLowerCase())) { const value = row[item.key] return ( ) } return row[item.key] ?? '' }, })) : [], [], ) const defaultArgs: any = { columnMode: getColumnMode(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } return (
    ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/tradeRaceTable/index.ts ================================================ export { TradeRaceTable } from './TradeRaceTable' // export { TradeRaceTableConfig } from "./TradeRaceTableConfig"; ================================================ FILE: packages/component-lib/src/components/tableList/tradeRroTable/TradePro.tsx ================================================ import { Box, BoxProps, Typography } from '@mui/material' import { TFunction, withTranslation, WithTranslation } from 'react-i18next' import moment from 'moment' import { Column, Table } from '../../basic-lib' import { EmptyValueTag, getValuePrecisionThousand, MarketRowHeight, SECOND_FORMAT, TradeTypes, } from '@loopring-web/common-resources' import { RawDataTradeItem } from '../tradeTable' import { useSettings } from '../../../stores' import styled from '@emotion/styled' import { TablePaddingX } from '../../styled' import { Currency, MarketInfo } from '@loopring-web/loopring-sdk' export type TradeProTableProps = { rawData: RawDataTradeItem[] precision: number marketInfo: MarketInfo currentheight?: number rowHeight?: number headerRowHeight?: number } const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { height: ${(props: any) => props.currentheight}px; //--template-columns: 300px 120px auto auto !important; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .rdg-header-row { font-size: ${({theme}) => theme.fontDefault.body2}; color: var(--color-text-third); // background-color: inherit !important; } .text-align-right { text-align: right; } } ${({theme}) => TablePaddingX({pLeft: theme.unit * 2, pRight: theme.unit * 2})} ` as (props: { currentheight?: number } & BoxProps) => JSX.Element export const TradePro = withTranslation('tables')( ({ t, rawData, marketInfo, // quotePrecision, currentheight, rowHeight = MarketRowHeight, headerRowHeight = MarketRowHeight, // tokenMap, precision, ...rest }: WithTranslation & TradeProTableProps) => { const {currency, isMobile} = useSettings() // @ts-ignore const [, baseSymbol, quoteSymbol] = marketInfo?.market?.match(/(\w+)-(\w+)/i) const getColumnModeAssets = ( t: TFunction, _currency: Currency, // quotePrecision, baseSymbol: string, quoteSymbol: string, precision: number, ): Column, unknown>[] => [ ...[ { key: 'price', name: t('labelTradeProPrice', {symbol: quoteSymbol}), // @ts-ignore formatter: ({row}) => { const color = row?.side === TradeTypes.Buy ? 'var(--color-error)' : 'var(--color-success)' const {value} = row?.price ?? {} // const precision = row['precision'] || 6 const renderValue = value ? getValuePrecisionThousand(value, undefined, undefined, precision, true) : EmptyValueTag return ( {renderValue} ) }, }, { key: 'amount', name: t('labelTradeProAmount', {symbol: baseSymbol}), headerCellClass: 'text-align-right', // @ts-ignore formatter: ({row}) => { const {volume} = row['amount'] // getValuePrecisionThousand(volume, precision, precision, precision, true) // const value = return ( {volume ? volume : EmptyValueTag} ) }, }, ], ...(isMobile ? [] : [ { key: 'time', name: t('labelTradeTime'), headerCellClass: 'text-align-right', // @ts-ignore formatter: ({row}) => { const time = moment(new Date(row['time'])).format(SECOND_FORMAT) //,M-DD return ( {time} ) }, }, ]), ] const defaultArgs: any = { rawData: rawData, columnMode: getColumnModeAssets( t, currency, // quotePrecision, baseSymbol, quoteSymbol, precision, ).filter((o) => !o.hidden), generateRows: (rawData: any) => rawData, generateColumns: ({columnsRaw}: any) => columnsRaw as Column[], style: { // backgroundColor: ({colorBase}: any) => `${colorBase.box}` }, } return (
    ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/tradeRroTable/index.ts ================================================ export * from './TradePro' ================================================ FILE: packages/component-lib/src/components/tableList/tradeRroTable/tradeTable.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import { TradePro } from './index' import { RawDataTradeItem } from '../tradeTable' import * as sdk from '@loopring-web/loopring-sdk' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` const rawData: RawDataTradeItem[] = [ { role: sdk.OrderMakerType.maker, amount: { from: { key: 'LRC', value: 2333, }, to: { key: 'ETH', value: 1.05, }, volume: 1111, }, price: { key: 'ETH', value: 1785.65, }, fee: { key: 'LRC', value: 2.55, }, time: 0, __raw__: {}, }, { role: sdk.OrderMakerType.maker, amount: { from: { key: 'LRC', value: 2333, }, to: { key: 'ETH', value: 1.05, }, volume: 1111, }, price: { key: 'ETH', value: 1785.65, }, fee: { key: 'LRC', value: 2.55, }, time: 0, __raw__: {}, }, { role: sdk.OrderMakerType.maker, amount: { from: { key: 'LRC', value: 2333, }, to: { key: 'ETH', value: 1.05, }, volume: 1111, }, price: { key: 'ETH', value: 1785.65, }, fee: { key: 'LRC', value: 2.55, }, time: 0, __raw__: {}, }, { role: sdk.OrderMakerType.maker, amount: { from: { key: 'LRC', value: 2333, }, to: { key: 'ETH', value: 1.05, }, volume: 1111, }, price: { key: 'ETH', value: 1785.65, }, fee: { key: 'LRC', value: 2.55, }, time: 0, __raw__: {}, }, { role: sdk.OrderMakerType.maker, amount: { from: { key: 'LRC', value: 2333, }, to: { key: 'ETH', value: 1.05, }, volume: 1111, }, price: { key: 'ETH', value: 1785.65, }, fee: { key: 'LRC', value: 2.55, }, time: 0, __raw__: {}, }, ] const Template: Story = withTranslation()((args: any) => { return ( <> ) }) as Story // @ts-ignore export const Trade = Template.bind({}) Trade.args = { rawData: rawData, pagination: { pageSize: 5, }, showFilter: true, } export default { title: 'components/TableList/TradePro', component: TradePro, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/tradeTable/TradeTable.tsx ================================================ import React from 'react' import { Box, BoxProps, Link, Typography } from '@mui/material' import styled from '@emotion/styled' import { TFunction, Trans, withTranslation, WithTranslation } from 'react-i18next' import moment from 'moment' import { Column, Table, TablePagination } from '../../basic-lib' import { TableFilterStyled, TablePaddingX } from '../../styled' import { Filter, FilterTradeTypes } from './components/Filter' import { DirectionTag, EmptyValueTag, Explorer, getValuePrecisionThousand, globalSetup, RowConfig, TableType, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { DateRange } from '@mui/lab' import { Currency, MarketTradeInfo } from '@loopring-web/loopring-sdk' import { XOR } from '../../../types/lib' import { useLocation } from 'react-router-dom' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' export enum TradeItemCounterparty { orderbook = 'Orderbook', pool = 'Pool', } export type RawDataTradeItem = { // side: keyof typeof TradeTypes; role: sdk.OrderMakerType amount: { from: { key: string value: number | undefined } to: { key: string value: number | undefined } volume?: number } counterParty?: TradeItemCounterparty price: { key: string value: number | undefined } fee: { key: string value: number | undefined } time: number __raw__: Partial } export type TradeTableProps = { rawData: RawDataTradeItem[] // getUserTradeList?: (param: Omit) => void; getUserTradeList?: (param: { market?: string page: number total?: number pageSize: number // offset: (page - 1) * pageSize, // limit: pageSize, orderHash?: any fillTypes?: any fromId?: any }) => void pagination?: { page: number pageSize: number total: number } currentheight?: number rowHeight?: number headerRowHeight?: number isL2Trade?: boolean marketMap?: any showLoading?: boolean accAddress?: string accountId?: number } & XOR<{ showFilter: true; filterPairs: string[] }, { showFilter?: false }> const TableStyled = styled(Box)< BoxProps & { isMobile?: boolean currentheight?: number tradeposition: string } >` display: flex; flex-direction: column; flex: 1; .rdg { height: ${(props: any) => props.currentheight}px; ${({ isMobile, tradeposition }) => !isMobile ? `--template-columns: ${ tradeposition === 'swap' ? '300px 120px auto auto !important;' : '100px 340px auto 120px auto auto !important;' }` : ` --template-columns: 40% 40% 20% !important;`} .rdg-cell.action { display: flex; justify-content: center; align-items: center; } .rdg-header-row { // background-color: inherit !important; } .textAlignRight { text-align: right; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as ( props: { isMobile?: boolean currentheight?: number tradeposition: string } & BoxProps, ) => JSX.Element const getColumnModeAssets = ( t: TFunction, _currency: Currency, tokenMap: any, isL2Trade: boolean, ): Column[] => { if (isL2Trade) { return [ { key: 'role', name: t('labelTradeRole'), formatter: ({ row }) => { const value = row['role'] const renderValue = value === sdk.OrderMakerType.maker ? t('labelTradeRoleMaker') : t('labelTradeRoleTaker') return {renderValue} }, }, { key: 'side', name: t('labelTradeSide'), formatter: ({ row }) => { // const tradeType = row[ 'side' ] === TradeTypes.Buy ? t('labelBuy') : t('labelSell') const { from, to } = row['amount'] const precisionFrom = tokenMap ? tokenMap[from.key]?.precision : undefined const precisionTo = tokenMap ? tokenMap[to.key]?.precision : undefined const fromValue = from.value ? getValuePrecisionThousand(from.value, precisionFrom, precisionFrom) : EmptyValueTag const toValue = to.value ? getValuePrecisionThousand(to.value, precisionTo, precisionTo) : EmptyValueTag return ( {`${fromValue} ${from.key} ${DirectionTag} ${toValue} ${to.key}`} ) }, }, { key: 'counterparty', name: t('labelTradeConterparty'), formatter: ({ row }) => { const value = row['counterParty'] const renderValue = value === TradeItemCounterparty.orderbook ? t('labelTradeCounterpartyOrderbook') : t('labelTradeCounterpartyPool') return {renderValue} }, }, { key: 'price', name: t('labelTradePrice'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { value } = row['price'] const precision = row['precision'] || 6 const renderValue = value ? getValuePrecisionThousand(value, undefined, undefined, precision, true, { isPrice: true, }) : EmptyValueTag return {renderValue} }, }, { key: 'fee', name: t('labelTxTradingFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { key, value } = row['fee'] // myLog({value}) return
    {`${value} ${key}`}
    }, }, { key: 'time', name: t('labelTradeTime'), headerCellClass: 'textAlignRight', // minWidth: 400, formatter: ({ row }) => { const time = moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() return
    {time}
    }, }, ] } else { return [ { key: 'side', name: t('labelTradeSide'), formatter: ({ row }) => { // const tradeType = row[ 'side' ] === TradeTypes.Buy ? t('labelBuy') : t('labelSell') const { from, to } = row['amount'] const precisionFrom = tokenMap ? tokenMap[from.key]?.precision : undefined const precisionTo = tokenMap ? tokenMap[to.key]?.precision : undefined const fromValue = from.value ? getValuePrecisionThousand(from.value, precisionFrom, precisionFrom) : EmptyValueTag const toValue = to.value ? getValuePrecisionThousand(to.value, precisionTo, precisionTo) : EmptyValueTag return ( {`${fromValue} ${from.key} ${DirectionTag} ${toValue} ${to.key}`} ) }, }, { key: 'price', name: t('labelTradePrice'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { value } = row['price'] const precision = row['precision'] || 6 const renderValue = value ? getValuePrecisionThousand(value, undefined, undefined, precision, true, { isPrice: true, }) : EmptyValueTag return {renderValue} }, }, { key: 'fee', name: t('labelTxTradingFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { key, value } = row['fee'] // myLog({value}) return
    {`${value} ${key}`}
    }, }, { key: 'time', name: t('labelTradeTime'), headerCellClass: 'textAlignRight', // minWidth: 400, formatter: ({ row }) => { const time = moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() return
    {time}
    }, }, ] } } const getColumnModeMobileAssets = ( t: TFunction, _currency: Currency, tokenMap: any, isL2Trade: boolean, ): Column[] => { return [ ...(isL2Trade ? [ { key: 'role', name: t('labelTradeRole') + '/' + t('labelTradeConterparty'), // @ts-ignore formatter: ({ row }) => { const value = row['role'] const renderValue = value === sdk.OrderMakerType.maker ? t('labelTradeRoleMaker') : t('labelTradeRoleTaker') const counterParty = row.counterParty === TradeItemCounterparty.orderbook ? t('labelTradeCounterpartyOrderbook') : t('labelTradeCounterpartyPool') return ( {renderValue} {counterParty} ) }, }, ] : []), { key: 'side', name: t('labelTradeSide') + '/' + t('labelTxTradingFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { // const tradeType = row[ 'side' ] === TradeTypes.Buy ? t('labelBuy') : t('labelSell') const { from, to } = row['amount'] const precisionFrom = tokenMap ? tokenMap[from.key]?.precision : undefined const precisionTo = tokenMap ? tokenMap[to.key]?.precision : undefined const fromValue = from.value ? getValuePrecisionThousand(from.value, precisionFrom, precisionFrom) : EmptyValueTag const toValue = to.value ? getValuePrecisionThousand(to.value, precisionTo, precisionTo) : EmptyValueTag const { key, value } = row['fee'] return ( {`${fromValue} ${from.key} ${DirectionTag} ${toValue} ${to.key}`} {t('labelFee', { ns: 'common' }) + `: ${value} ${key}`} ) }, }, { key: 'price', name: t('labelTradePrice') + '/' + t('labelTradeTime'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const precision = row['precision'] || 6 const time = moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() const renderValue = row.price ? getValuePrecisionThousand( row.price.value, undefined, undefined, precision, true, // { isPrice: true } ) : EmptyValueTag return ( {renderValue} {time} ) }, }, ] } export const TradeTable = withTranslation('tables')( ({ t, pagination, showFilter, filterPairs = [], rawData, currentheight, rowHeight = RowConfig.rowHeight, headerRowHeight = RowConfig.rowHeaderHeight, tokenMap = undefined, isL2Trade = false, getUserTradeList, showLoading = false, accAddress, accountId, ...rest }: WithTranslation & TradeTableProps & { tokenMap?: any }) => { const { search } = useLocation() const searchParams = new URLSearchParams(search) const [filterType, setFilterType] = React.useState(FilterTradeTypes.allTypes) const [filterDate, setFilterDate] = React.useState>([null, null]) const [filterPair, setFilterPair] = React.useState('all') // const [page, setPage] = React.useState(1); // const [totalData, setTotalData] = React.useState(rawData) const { currency, isMobile, defaultNetwork } = useSettings() const isTaiko = [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const defaultArgs: any = { columnMode: isMobile ? getColumnModeMobileAssets(t, currency, tokenMap, isL2Trade) : getColumnModeAssets(t, currency, tokenMap, isL2Trade), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], style: { backgroundColor: ({ colorBase }: any) => `${colorBase.box}`, }, } const pageSize = pagination ? pagination.pageSize : 10 const updateData = _.debounce( ({ tableType, currFilterPair = filterPair, currPage = pagination?.page || 1, currFilterType = filterType, }) => { if (tableType === 'filter') { currPage = 1 } const market = currFilterPair === 'all' ? '' : currFilterPair.replace(/\s+/g, '') if (getUserTradeList) { getUserTradeList({ ...pagination, pageSize, market, page: currPage, fillTypes: currFilterType !== 'all' ? currFilterType : '', }) } }, globalSetup.wait, //[filterPair, filterType, pageSize, getUserTradeList, pagination] ) const handleFilterChange = React.useCallback( ({ type = filterType, date = filterDate, pair = filterPair }) => { setFilterType(type) setFilterDate(date) setFilterPair(pair) updateData({ tableType: TableType.filter, currFilterType: type, currFilterDate: date, currFilterPair: pair, }) }, [updateData, filterDate, filterType, filterPair], ) const handlePageChange = React.useCallback( (page: number) => { updateData({ tableType: TableType.page, currPage: page }) }, [updateData], ) const handleReset = () => { setFilterType(FilterTradeTypes.allTypes) setFilterDate([null, null]) setFilterPair('all') updateData({ tableType: 'filter', currFilterType: FilterTradeTypes.allTypes, currFilterDate: [null, null], currFilterPair: 'all', currPage: 1, }) } const tradeposition = isL2Trade === true ? 'layer2' : 'swap' const [isDropDown, setIsDropDown] = React.useState(true) React.useEffect(() => { let filters: any = {} updateData.cancel() if (searchParams.get('market')) { filters.pair = searchParams.get('market') } handleFilterChange(filters) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( {showFilter && (isMobile && isDropDown ? ( setIsDropDown(false)} > {t('labelShowFilter')} ) : ( ))}
    {accountId && showFilter && !isTaiko && ( View transactions on block explorer )} {pagination && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/tradeTable/components/Filter.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Grid, MenuItem } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { TextField } from '../../../basic-lib/form' import { Button } from '../../../basic-lib/btns' import { DropDownIcon } from '@loopring-web/common-resources' import { DateRange } from '@mui/lab' export interface FilterProps { // rawData: RawDataTradeItem[]; filterDate: DateRange filterType: FilterTradeTypes filterPair: string handleReset: () => void handleFilterChange: ({ type, date }: any) => void marketMap?: any filterPairs: string[] } const StyledTextFiled = styled(TextField)` &.MuiTextField-root { max-width: initial; } .MuiInputBase-root { width: initial; max-width: initial; } ` export enum FilterTradeTypes { maker = 'Maker', taker = 'Taker', allTypes = 'all', } export const Filter = withTranslation('tables', { withRef: true })( ({ t, // filterDate, // filterType, filterPairs = [], filterPair, handleReset, handleFilterChange, }: // marketMap, FilterProps & WithTranslation) => { const rawPairList = [].slice .call(filterPairs) // .map((item: string) => item.replace("-", " - ")) .sort((a: string, b: string) => { return a.localeCompare(b) }) const formattedRawPairList = [ { label: t('labelFilterAllPairs'), value: 'all', }, ...Array.from(new Set(rawPairList)).map((pair: string) => ({ label: pair, //.replace("-", " - "), value: pair, })), ] return ( ) => { handleFilterChange({ pair: event.target.value }) }} inputProps={{ IconComponent: DropDownIcon }} > {formattedRawPairList.map((o) => ( {o.label} ))} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/tradeTable/index.ts ================================================ export * from './TradeTable' ================================================ FILE: packages/component-lib/src/components/tableList/tradeTable/tradeTable.stories.tsx ================================================ import styled from '@emotion/styled' import { Meta, Story } from '@storybook/react' import { withTranslation } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' import * as sdk from '@loopring-web/loopring-sdk' import { RawDataTradeItem, TradeItemCounterparty, TradeTable } from './index' const Style = styled.div` flex: 1; height: 100%; flex: 1; ` const rawData: RawDataTradeItem[] = [ { role: sdk.OrderMakerType.maker, amount: { from: { key: 'eth', value: 1234, }, to: { key: 'let', value: 5678, }, volume: 1234, }, counterParty: TradeItemCounterparty.orderbook, price: { key: 'ETH', value: 1200, }, fee: { key: 'LRC', value: 0.1, }, time: Date.now(), __raw__: {} as any, }, { role: sdk.OrderMakerType.maker, amount: { from: { key: 'eth', value: 1234, }, to: { key: 'let', value: 5678, }, volume: 1234, }, counterParty: TradeItemCounterparty.orderbook, price: { key: 'ETH', value: 1200, }, fee: { key: 'LRC', value: 0.1, }, time: Date.now(), __raw__: {} as any, }, { role: sdk.OrderMakerType.maker, amount: { from: { key: 'eth', value: 1234, }, to: { key: 'let', value: 5678, }, volume: 1234, }, counterParty: TradeItemCounterparty.orderbook, price: { key: 'ETH', value: 1200, }, fee: { key: 'LRC', value: 0.1, }, time: Date.now(), __raw__: {} as any, }, { role: sdk.OrderMakerType.maker, amount: { from: { key: 'eth', value: 1234, }, to: { key: 'let', value: 5678, }, volume: 1234, }, counterParty: TradeItemCounterparty.orderbook, price: { key: 'ETH', value: 1200, }, fee: { key: 'LRC', value: 0.1, }, time: Date.now(), __raw__: {} as any, }, ] const Template: Story = withTranslation()((args: any) => { return ( <> ) }) as Story // @ts-ignore export const Trade = Template.bind({}) Trade.args = { rawData: rawData, pagination: { pageSize: 5, }, showFilter: true, } export default { title: 'components/TableList/Trade', component: TradeTable, argTypes: {}, } as Meta ================================================ FILE: packages/component-lib/src/components/tableList/transactionsTable/Interface.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' // export type TransactionSide = { // address: string; // env: string; // } export enum TransactionStatus { processing = 'processing', processed = 'processed', received = 'received', failed = 'failed', } export const TransactionTradeTypes = { allTypes: `${sdk.UserTxTypes.DEPOSIT},${sdk.UserTxTypes.TRANSFER},${sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW},${sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL},` + `${sdk.UserTxTypes.FORCE_WITHDRAWAL},` + `${sdk.UserTxTypes.WITHDRAW_LUCKY_TOKEN},${sdk.UserTxTypes.SEND_LUCKY_TOKEN},${sdk.UserTxTypes.SEND_BACK_LUCKY_TOKEN},` + `${sdk.UserTxTypes.UNIFIED_CLAIM},${sdk.UserTxTypes.L2_STAKING},` + `${sdk.UserTxTypes.DUAL_INVESTMENT},${'change_password'}`, receive: `${sdk.UserTxTypes.DEPOSIT}`, send: `${sdk.UserTxTypes.TRANSFER},${sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL},${sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL}`, forceWithdraw: `${sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW}`, redPacket: `${sdk.UserTxTypes.WITHDRAW_LUCKY_TOKEN},${sdk.UserTxTypes.SEND_LUCKY_TOKEN},${sdk.UserTxTypes.SEND_BACK_LUCKY_TOKEN}`, } export enum TransactionTradeViews { allTypes = 'ALL', receive = 'RECEIVE', send = 'SEND', forceWithdraw = 'FORCE_WITHDRAWAL', redPacket = 'RED_PACKET', } export type RawDataTransactionItem = { side: sdk.UserTxTypes // token?: string, // tradeType: TransactionTradeTypes, // from: string; // to: string; amount: { unit: string value: number } fee: { unit: string value: number } memo?: string time: number txnHash: string status: TransactionStatus path?: string } & Partial ================================================ FILE: packages/component-lib/src/components/tableList/transactionsTable/TransactionTable.tsx ================================================ import styled from '@emotion/styled' import { Box, BoxProps, Link, Typography } from '@mui/material' import { Trans, WithTranslation, withTranslation } from 'react-i18next' import moment from 'moment' import { Column, Table, TablePagination } from '../../basic-lib' import { CompleteIcon, DepositIcon, DirectionTag, EmptyValueTag, EXPLORE_TYPE, Explorer, getShortAddr, getValuePrecisionThousand, globalSetup, L1L2_NAME_DEFINED, MapChainId, mapSpecialTokenName, RedPacketIcon, RewardIcon, TableType, TransferIcon, UNIX_TIMESTAMP_FORMAT, WaitingIcon, WarningIcon, WithdrawIcon, } from '@loopring-web/common-resources' import { Filter } from './components/Filter' import { TableFilterStyled, TablePaddingX } from '../../styled' import { RawDataTransactionItem, TransactionStatus, TransactionTradeTypes, TransactionTradeViews, } from './Interface' import { DateRange } from '@mui/lab' import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' import { useSettings } from '../../../stores' import { useLocation } from 'react-router-dom' import _ from 'lodash' export type TxsFilterProps = { tokenSymbol?: string start?: number end?: number offset?: number limit?: number types?: sdk.UserTxTypes[] | string } const TYPE_COLOR_MAPPING = [ { type: TransactionStatus.processed, color: 'success' }, { type: TransactionStatus.processing, color: 'warning' }, { type: TransactionStatus.received, color: 'warning' }, { type: TransactionStatus.failed, color: 'error' }, ] const CellStatus = ({ row }: any) => { const status = row['status'] const RenderValue = styled.div` display: flex; align-items: center; color: ${({ theme }) => theme.colorBase[`${TYPE_COLOR_MAPPING.find((o) => o.type === status)?.color}`]}; & svg { width: 24px; height: 24px; } ` const svg = status === 'processed' ? ( ) : status === 'processing' || status === 'received' ? ( ) : ( ) return {svg} } const MemoCellStyled = styled(Box)` max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; ` const TableStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 192px auto auto auto 120px 120px !important;` : `--template-columns: 60% 40% !important;`} .rdgCellCenter { height: 100%; justify-content: center; align-items: center; } .textAlignRight { text-align: right; } .textAlignCenter { text-align: center; } .textAlignLeft { text-align: left; } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })} ` as (props: { isMobile?: boolean } & BoxProps) => JSX.Element export interface TransactionTableProps { etherscanBaseUrl?: string rawData: RawDataTransactionItem[] pagination?: { pageSize: number total: number } getTxnList: ({ tokenSymbol, start, end, limit, offset, types }: TxsFilterProps) => Promise filterTokens: string[] showFilter?: boolean showloading: boolean accAddress: string accountId: number } export const TransactionTable = withTranslation(['tables', 'common'])( (props: TransactionTableProps & WithTranslation) => { const { rawData, pagination, showFilter, getTxnList, filterTokens, showloading, etherscanBaseUrl, accAddress, accountId, t, } = props const { isMobile, defaultNetwork } = useSettings() const isTaiko = [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { search } = useLocation() const searchParams = new URLSearchParams(search) const [page, setPage] = React.useState(1) const [filterType, setFilterType] = React.useState( TransactionTradeViews.allTypes, ) const [filterDate, setFilterDate] = React.useState>(['', '']) const [filterToken, setFilterToken] = React.useState('all') const updateData = _.debounce( ({ tableType, currFilterType = filterType, currFilterDate = filterDate, currFilterToken = filterToken, currPage = page, pageSize = pagination?.pageSize ?? 10, }: { tableType: TableType currFilterType?: TransactionTradeViews currFilterDate?: DateRange currFilterToken?: string currPage?: number pageSize?: number }) => { if (tableType === 'filter') { currPage = 1 setPage(1) } const tokenSymbol = currFilterToken === 'all' ? '' : currFilterToken const formattedType = currFilterType.toUpperCase() const types = formattedType === TransactionTradeViews.receive ? TransactionTradeTypes.receive //UserTxTypes.DEPOSIT : formattedType === TransactionTradeViews.send ? TransactionTradeTypes.send : formattedType.toUpperCase() === TransactionTradeViews.forceWithdraw ? TransactionTradeTypes.forceWithdraw : TransactionTradeTypes.allTypes const start = Number(moment(currFilterDate[0]).format(UNIX_TIMESTAMP_FORMAT)) const end = Number(moment(currFilterDate[1]).format(UNIX_TIMESTAMP_FORMAT)) getTxnList({ limit: pageSize, offset: (currPage - 1) * pageSize, types, tokenSymbol: tokenSymbol, start: Number.isNaN(start) ? -1 : start, end: Number.isNaN(end) ? -1 : end, }) }, globalSetup.wait, ) const handleFilterChange = React.useCallback( ({ type = filterType, date = filterDate, token = filterToken }) => { setFilterType(type) setFilterDate(date) setFilterToken(token) updateData({ tableType: TableType.filter, currFilterType: type, currFilterDate: date, currFilterToken: token, }) }, [updateData, filterDate, filterType, filterToken], ) const handleReset = React.useCallback(() => { setFilterType(TransactionTradeViews.allTypes) setFilterDate([null, null]) setFilterToken('all') updateData({ tableType: TableType.filter, currFilterType: TransactionTradeViews.allTypes, currFilterDate: [null, null], currFilterToken: 'all', }) }, [updateData]) const handlePageChange = React.useCallback( (currPage: number) => { if (currPage === page) return setPage(currPage) updateData({ tableType: TableType.page, currPage: currPage }) }, [updateData, page], ) const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'side', name: t('labelTxSide'), formatter: ({ row }) => { const value = row.side const renderValue = value.toLowerCase() === sdk.UserTxTypes.TRANSFER && row.receiverAddress?.toUpperCase() === accAddress?.toUpperCase() ? t(`labelTypeReceive`) : t(`labelType${value?.toUpperCase()}`) return ( {renderValue} ) }, }, { key: 'amount', name: t('labelTxAmount'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const { unit, value } = row['amount'] const hasValue = Number.isFinite(value) && value > 0 const hasSymbol = row.side.toLowerCase() === sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW ? '' : row.side.toLowerCase() === sdk.UserTxTypes.TRANSFER // TransactionTradeTypes.transfer ? row.receiverAddress?.toUpperCase() === accAddress?.toUpperCase() ? '+' : '-' : row.side.toLowerCase() === sdk.UserTxTypes.DEPOSIT //TransactionTradeTypes.deposit ? '+' : /chain_withdrawal/i.test(row.side.toLowerCase()) //TransactionTradeTypes.withdraw ? '-' : row.side.toLowerCase() === sdk.UserTxTypes.SEND_LUCKY_TOKEN ? '-' : row.side.toLowerCase() === sdk.UserTxTypes.SEND_BACK_LUCKY_TOKEN ? '+' : row.side.toLowerCase() === sdk.UserTxTypes.WITHDRAW_LUCKY_TOKEN ? '+' : row.side.toLowerCase() === sdk.UserTxTypes.UNIFIED_CLAIM ? '+' : '' const renderValue = hasValue ? `${getValuePrecisionThousand(value, undefined, undefined, undefined, false, { isTrade: true, })}` : EmptyValueTag return ( {hasSymbol} {row.side !== sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW && hasValue ? `${renderValue} ${mapSpecialTokenName(unit)}` : renderValue} ) }, }, { key: 'fee', name: t('labelTxNetworkFee'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const fee = row.fee const renderValue = fee.value === 0 || fee.value === undefined ? EmptyValueTag : `${getValuePrecisionThousand(fee.value, undefined, undefined, undefined, false, { floor: false, isTrade: true, })} ${fee.unit}` return {renderValue} }, }, { key: 'from', name: t('labelTxFrom'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { const receiverAddress = /chain_withdrawal/i.test(row.side.toLowerCase()) ? // row.side.toLowerCase() === sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL row.withdrawalInfo ? getShortAddr(row.withdrawalInfo.recipient, isMobile) : '' : getShortAddr(row.receiverAddress, isMobile) const senderAddress = getShortAddr(row.senderAddress) // myLog("receiverAddress", row.receiverAddress); // if (/chain_withdrawal/i.test(row.side.toLowerCase())) { // myLog("receiverAddress", row.receiverAddress); // } const [from, to] = row.side.toLowerCase() == sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW ? [ t('labelForceWithdrawDes', { address: getShortAddr(row.withdrawalInfo?.recipient), layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), '', ] : [ sdk.UserTxTypes.L2_STAKING, sdk.UserTxTypes.UNIFIED_CLAIM, sdk.UserTxTypes.DUAL_INVESTMENT, sdk.UserTxTypes.SEND_LUCKY_TOKEN, sdk.UserTxTypes.TRANSFER, sdk.UserTxTypes.WITHDRAW_LUCKY_TOKEN, ].includes(row.side.toLowerCase()) ? row.receiverAddress?.toUpperCase() === accAddress?.toUpperCase() ? [senderAddress, `${L1L2_NAME_DEFINED[network].l2Symbol}`] : [`${L1L2_NAME_DEFINED[network].l2Symbol}`, receiverAddress] : row.side.toLowerCase() === sdk.UserTxTypes.DEPOSIT ? [ row.senderAddress.toLowerCase() !== accAddress ? `${L1L2_NAME_DEFINED[network].l1Symbol} ` + senderAddress : `${L1L2_NAME_DEFINED[network].l1Symbol}`, `${L1L2_NAME_DEFINED[network].l2Symbol}`, ] : /chain_withdrawal/i.test(row.side.toLowerCase()) //sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL ? [ `${L1L2_NAME_DEFINED[network].l2Symbol}`, row.withdrawalInfo?.recipient?.toUpperCase() === accAddress.toUpperCase() ? `${L1L2_NAME_DEFINED[network].l1Symbol}` : `${L1L2_NAME_DEFINED[network].l1Symbol} ` + receiverAddress, ] : ['', ''] const hash = row.txHash !== '' ? row.txHash : row.hash let path: string | undefined if ( [ sdk.UserTxTypes.L2_STAKING, sdk.UserTxTypes.UNIFIED_CLAIM, sdk.UserTxTypes.DUAL_INVESTMENT, sdk.UserTxTypes.SEND_LUCKY_TOKEN, sdk.UserTxTypes.WITHDRAW_LUCKY_TOKEN, 'change_pwd', ].includes(row.side.toLowerCase()) ) { path = row.txHash !== '' ? etherscanBaseUrl + `/tx/${row.txHash.slice(0, 6)}-${row.txHash.slice(row.txHash - 4)}` : [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) ? undefined : Explorer + `tx/${row.hash}-transfer-${row.storageInfo.accountId}-${row.storageInfo.tokenId}-${row.storageInfo.storageId}` } else { path = row.txHash !== '' ? etherscanBaseUrl + `/tx/${row.txHash}` : [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) ? undefined : Explorer + `tx/${row.hash}-${EXPLORE_TYPE[row.txType.toUpperCase()]}` } return ( {row.side.toLowerCase() === 'change_pwd' ? `${hash.slice(0, 6)}...${hash.slice(hash.length - 4)}` : from && to ? from + ` ${DirectionTag} ` + to : from + to} ) }, }, { key: 'status', name: t('labelTxMemo'), headerCellClass: 'textAlignCenter', formatter: ({ row }) => ( {row['memo'] || EmptyValueTag} ), }, { key: 'time', name: t('labelTxTime'), headerCellClass: 'textAlignRight', formatter: ({ row }) => { const value = row['time'] const hasValue = Number.isFinite(value) const renderValue = hasValue ? moment(new Date(row['time']), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return {renderValue} }, }, ], [t, accAddress, isMobile, etherscanBaseUrl], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'amount', name: ( {t('labelTransactions')} {t('labelTxAmount') + ' / ' + t('labelTxNetworkFee')} ), cellClass: 'textAlignRight', headerCellClass: 'textAlignLeft', formatter: ({ row }) => { const { unit, value } = row['amount'] const hasValue = Number.isFinite(value) const side = row.side.toLowerCase() === sdk.UserTxTypes.TRANSFER && row.receiverAddress?.toUpperCase() === accAddress?.toUpperCase() ? t(`labelTypeReceive`) : t(`labelType${row.side?.toUpperCase()}`) const hasSymbol = row.side.toLowerCase() === sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW ? '' : // t("labelForceWithdrawTotalDes", { // address: getShortAddr(row.withdrawalInfo?.recipient), // symbol: row.symbol, // }) row.side.toLowerCase() === sdk.UserTxTypes.TRANSFER ? row['receiverAddress']?.toUpperCase() === accAddress?.toUpperCase() ? '+' : '-' : row.side.toLowerCase() === sdk.UserTxTypes.DEPOSIT ? '+' : row.side.toLowerCase() === sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW ? '-' : row.side.toLowerCase() === sdk.UserTxTypes.SEND_LUCKY_TOKEN ? '-' : row.side.toLowerCase() === sdk.UserTxTypes.SEND_BACK_LUCKY_TOKEN ? '+' : row.side.toLowerCase() === sdk.UserTxTypes.WITHDRAW_LUCKY_TOKEN ? '+' : row.side.toLowerCase() === sdk.UserTxTypes.UNIFIED_CLAIM ? '+' : '' const sideIcon = row.side.toLowerCase() === sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW ? ( ) : row.side.toLowerCase() === sdk.UserTxTypes.DEPOSIT ? ( ) : row.side.toLowerCase() === sdk.UserTxTypes.TRANSFER ? ( ) : row.side.toLowerCase() === sdk.UserTxTypes.SEND_LUCKY_TOKEN ? ( ) : row.side.toLowerCase() === sdk.UserTxTypes.WITHDRAW_LUCKY_TOKEN ? ( ) : row.side.toLowerCase() === sdk.UserTxTypes.UNIFIED_CLAIM ? ( ) : ( ) const renderValue = hasValue ? `${getValuePrecisionThousand(value, undefined, undefined, undefined, false, { isTrade: true, })}` : EmptyValueTag const renderFee = `Fee: ${getValuePrecisionThousand( row.fee.value, undefined, undefined, undefined, false, { floor: false, isTrade: true, }, )} ${row.fee.unit}` return ( {sideIcon} {side} {hasSymbol + renderValue + ' ' + unit} {renderFee} ) }, }, { key: 'from', name: t('labelTxFrom') + ' / ' + t('labelTxTime'), headerCellClass: 'textAlignRight', cellClass: 'textAlignRight', formatter: ({ row }) => { // row.side.toLowerCase() === sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL const receiverAddress = /chain_withdrawal/i.test(row.side.toLowerCase()) ? getShortAddr(row.withdrawalInfo.recipient, isMobile) : getShortAddr(row.receiverAddress, isMobile) const senderAddress = getShortAddr(row.senderAddress, isMobile) const [from, to] = row.side.toLowerCase() === sdk.UserTxTypes.DELEGATED_FORCE_WITHDRAW ? [ t('labelForceWithdrawDes', { address: getShortAddr(row.withdrawalInfo?.recipient), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), '', ] : row.side.toLowerCase() === sdk.UserTxTypes.TRANSFER ? row['receiverAddress']?.toUpperCase() === accAddress?.toUpperCase() ? [senderAddress, L1L2_NAME_DEFINED[network].l2Symbol] : [L1L2_NAME_DEFINED[network].l2Symbol, receiverAddress] : row.side.toLowerCase() === sdk.UserTxTypes.DEPOSIT ? [ row.senderAddress.toLowerCase() !== accAddress ? `${L1L2_NAME_DEFINED[network].l1Symbol} ` + senderAddress : `${L1L2_NAME_DEFINED[network].l1Symbol}`, L1L2_NAME_DEFINED[network].l2Symbol, ] : /chain_withdrawal/i.test(row.side.toLowerCase()) //sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL ? [ L1L2_NAME_DEFINED[network].l2Symbol, row.withdrawalInfo?.recipient?.toUpperCase() === accAddress.toUpperCase() ? `${L1L2_NAME_DEFINED[network].l1Symbol}` : `${L1L2_NAME_DEFINED[network].l1Symbol} ` + receiverAddress, ] : ['', ''] const hash = row.txHash !== '' ? row.txHash : row.hash const path = row.txHash !== '' ? etherscanBaseUrl + `/tx/${row.txHash}` : Explorer + `tx/${row.hash}-${EXPLORE_TYPE[row.txType.toUpperCase()]}` const hasValue = Number.isFinite(row.time) const renderTime = hasValue ? moment(new Date(row.time), 'YYYYMMDDHHMM').fromNow() : EmptyValueTag return ( { window.open(path, '_blank') window.opener = null }} > {from && to ? from + ` ${DirectionTag} ` + to : from + to} {renderTime} ) }, }, ], [t, accAddress, isMobile, etherscanBaseUrl], ) const [isDropDown, setIsDropDown] = React.useState(true) const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { let filters: any = {} updateData.cancel() if (searchParams.get('types')) { filters.type = searchParams.get('types') } handleFilterChange(filters) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( {showFilter && (isMobile && isDropDown ? ( setIsDropDown(false)} > {t('labelShowFilter')} ) : ( ))}
    {accountId && showFilter && !isTaiko && ( View transactions on block explorer )} {pagination && ( )} ) }, ) ================================================ FILE: packages/component-lib/src/components/tableList/transactionsTable/components/Filter.tsx ================================================ import React from 'react' import styled from '@emotion/styled' import { Box, Grid, MenuItem } from '@mui/material' import { withTranslation, WithTranslation } from 'react-i18next' import { DateRangePicker, TextField } from '../../../basic-lib/form' import { Button } from '../../../basic-lib/btns' import { DropDownIcon } from '@loopring-web/common-resources' import { DateRange } from '@mui/lab' import { useSettings } from '../../../../stores' import { TransactionTradeViews } from '../Interface' export interface FilterProps { filterTokens: string[] filterDate: DateRange filterType: TransactionTradeViews filterToken: string handleFilterChange: ({ type, date }: any) => void handleReset: () => void } const StyledTextFiled = styled(TextField)` &.MuiTextField-root { max-width: initial; } .MuiInputBase-root { width: initial; max-width: initial; } ` const StyledBtnBox = styled(Box)` display: flex; margin-left: 40%; button:first-of-type { margin-right: 8px; } ` export const Filter = withTranslation('tables', { withRef: true })( ({ t, filterTokens = [], filterDate, filterType, filterToken, handleFilterChange, handleReset, }: FilterProps & WithTranslation) => { const { isMobile } = useSettings() const transactionTypeList = [ { label: t('labelTxFilterALL'), value: TransactionTradeViews.allTypes, }, { label: t('labelTxFilterRECEIVE'), value: TransactionTradeViews.receive, }, { label: t('labelTxFilterSEND'), value: TransactionTradeViews.send, }, { label: t('labelTxFilterFORCEWITHDRAW'), value: TransactionTradeViews.forceWithdraw, }, // { // label: t("labelTxFilterDEPOSIT"), // value: TransactionTradeTypes.deposit, // }, // // { // label: t("labelTxFilterTRANSFER"), // value: TransactionTradeTypes.transfer, // }, // { // label: t("labelTxFilterWITHDRAW"), // value: TransactionTradeTypes.withdraw, // }, // { // label: t("labelTxFilterFORCEWITHDRAW"), // value: TransactionTradeTypes.forceWithdraw, // }, ] const tokenTypeList: { label: string; value: string }[] = [ { label: t('labelTxFilterAllTokens'), value: 'all', }, ...Array.from(filterTokens) .sort((a: string, b: string) => { return a.localeCompare(b) }) .map((token: string) => ({ label: token, value: token, })), ] return ( { handleFilterChange({ date: date }) }} /> ) => { handleFilterChange({ type: event.target.value }) }} inputProps={{ IconComponent: DropDownIcon }} > {transactionTypeList.map((o) => ( {o.label} ))} ) => { handleFilterChange({ token: event.target.value }) }} inputProps={{ IconComponent: DropDownIcon }} > {tokenTypeList.map((o) => ( {o.label} ))} ) }, ) export const Filter2 = Filter ================================================ FILE: packages/component-lib/src/components/tableList/transactionsTable/components/modal.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import styled from '@emotion/styled' import { Box, Grid, Typography } from '@mui/material' import moment from 'moment' import { EmptyValueTag, YEAR_DAY_SECOND_FORMAT } from '@loopring-web/common-resources' import { TxType } from '@loopring-web/loopring-sdk' import React from 'react' // import { getValuePrecisionThousand } from '@loopring-web/common-resources'; export enum TxnDetailStatus { processed = 'PROCESSED', processing = 'PROCESSING', received = 'RECEIVED', failed = 'FAILED', } export type TxnDetailProps = { txType?: TxType hash: string txHash: string status: keyof typeof TxnDetailStatus time: string from: string to: string amount: string fee: string memo?: string etherscanBaseUrl?: string } const ContentWrapperStyled = styled(Box)` top: 45%; left: 50%; transform: translate(-50%, -50%); width: 70%; min-width: ${({ theme }) => theme.unit * 87.5}px; height: 75%; background-color: var(--color-box); box-shadow: 0px ${({ theme }) => theme.unit / 2}px ${({ theme }) => theme.unit / 2}px rgba(0, 0, 0, 0.25); border-radius: ${({ theme }) => theme.unit}px; position: absolute; display: flex; justify-content: center; align-items: center; ` const HeaderStyled = styled(Box)` position: absolute; top: 0; z-index: 22; width: 100%; height: ${({ theme }) => theme.unit * 7.5}px; box-shadow: 0px ${({ theme }) => theme.unit / 4}px ${({ theme }) => theme.unit}px rgba(0, 0, 0, 0.25); border-radius: ${({ theme }) => theme.unit}px ${({ theme }) => theme.unit}px 0px 0px; display: flex; align-items: center; ` const GridContainerStyled = styled(Grid)` margin-top: ${({ theme }) => theme.unit * 7.5}px; flex-direction: column; width: auto; ` const GridItemStyled = styled(Grid)` display: flex; align-items: baseline; margin-bottom: ${({ theme }) => theme.unit * 3}px; ` const EthHshStyled = styled(Typography)` cursor: pointer; text-decoration: underline; ` const TypographyStyled = styled(Typography)` color: var(--color-text-secondary); width: ${({ theme }) => theme.unit * 20}px; ` const InfoValueStyled = styled(Box)` // max-width: ${({ theme }) => theme.unit * 32}px; word-break: break-all; font-size: 1.4rem; color: ${(props: any) => (props.hash ? 'var(--color-secondary)' : 'var(--color-text-primary)')}; ` as any const StatusStyled = styled(Typography)` color: ${({ theme, status }) => status === 'processed' ? theme.colorBase.success : status === 'processing' ? theme.colorBase.warning : status === 'failed' ? theme.colorBase.error : theme.colorBase.secondaryHover}; ` export const TxnDetailPanel = withTranslation('common', { withRef: true })( React.forwardRef( ( { t, txType, hash, txHash, status, time, from, to, amount, fee, memo, etherscanBaseUrl, }: TxnDetailProps & WithTranslation, ref: React.ForwardedRef, ) => { const headerLabel = txType === TxType.DEPOSIT ? 'labelDTxnDetailHeader' : txType === TxType.OFFCHAIN_WITHDRAWAL ? 'labelWTxnDetailHeader' : 'labelTTxnDetailHeader' const renderStatus = status.toUpperCase() === 'PROCESSED' ? t('labelTxnDetailProcessed') : status.toUpperCase() === 'PROCESSING' || status.toUpperCase() === 'RECEIVED' ? t('labelTxnDetailProcessing') : t('labelTxnDetailFailed') return ( {t(headerLabel)} {t('labelTxnDetailHash')} {hash} {txHash && ( {t('labelTxnDetailHashLv1')} { window.open(`${etherscanBaseUrl}tx/${txHash}`) window.opener = null }} > {txHash} )} {t('labelTxnDetailStatus')} {renderStatus} {t('labelTxnDetailTime')} {moment(time).format(YEAR_DAY_SECOND_FORMAT)} {t('labelTxnDetailFrom')} {from || EmptyValueTag} {t('labelTxnDetailTo')} {to || EmptyValueTag} {t('labelTxnDetailAmount')} {amount} {t('labelTxnDetailFee')} {fee} {t('labelTxnDetailMemo')} {memo || EmptyValueTag} ) }, ), ) ================================================ FILE: packages/component-lib/src/components/tableList/transactionsTable/index.ts ================================================ export * from './TransactionTable' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tableList/vaultTable/Interface.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' export enum VaultRecordType { borrow = 'borrow', open = 'open', closeout = 'closeout', margin = 'margin', repay = 'repay', trade = 'trade', convert = 'convert', redeem = 'redeem', closeShort = 'closeShort', } export type RawDataVaultTxItem = { type: VaultRecordType status: sdk.VaultOperationStatus vSymbol: string vTokenB: string erc20SymbolB: string erc20Symbol: string mainContentRender: string | JSX.Element fillAmount: string percentage: string feeStr: string feeTokenSymbol: string feeErc20Symbol: string raw_data: { operation: sdk.VaultOperation; order: sdk.VaultOrder } // fromAmount: string // fromSymbol: string // toAmount: string // toSymbol: string // fromFAmount: string // toFAmount: string // price: { // key: string // value: string // from: string // } // feeAmount: string // feeSymbol: string // time: number // filledPercent: string // settledFromAmount: string // settledToAmount: string } ================================================ FILE: packages/component-lib/src/components/tableList/vaultTable/VaultTxTable.tsx ================================================ import { WithTranslation, useTranslation, withTranslation } from 'react-i18next' import { useSettings } from '../../../stores' import React from 'react' import { Column, EmptyDefault, SpaceBetweenBox, Table, TablePagination } from '../../basic-lib' import { Box, BoxProps, Button, Tooltip, Typography } from '@mui/material' import { TablePaddingX } from '../../styled' import styled from '@emotion/styled' import { FormatterProps } from 'react-data-grid' import { RawDataVaultTxItem, VaultRecordType } from './Interface' import { DoneIcon, EmptyValueTag, FailedIcon, globalSetup, Info2Icon, LoadingIcon, RowInvestConfig, TokenType, YEAR_DAY_MINUTE_FORMAT, hexToRGB, ErrorIcon, mapSpecialTokenName } from '@loopring-web/common-resources' import { useHistory } from 'react-router-dom' import moment from 'moment' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import { RedeemDes2 } from '../../modal' import { CoinIcons } from '../assetsTable' import { CoinIcon, } from '@loopring-web/component-lib' import { useTheme } from '@mui/material' import Decimal from 'decimal.js' const TableWrapperStyled = styled(Box)` display: flex; flex-direction: column; flex: 1; height: 100%; ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })}; & .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 16% 38% auto auto auto !important;` : `--template-columns: 16% 40% auto !important;`} } ` const TableStyled = styled(Table)` &.rdg { height: ${(props: any) => { if (props.ispro === 'pro') { return '620px' } if (props.currentheight && props.currentheight > 350) { return props.currentheight + 'px' } else { return '100%' } }}; .rdg-cell.action { display: flex; justify-content: center; align-items: center; } } .textAlignRight { text-align: right; .rdg-header-sort-cell { justify-content: flex-end; } } .textAlignCenter { text-align: center; } ` as any export interface VaultTxsTableProps { rawData: R[] showloading: boolean onItemClick: (item: R) => void pagination: { pageSize: number total: number } getOrderList: (props: Omit) => Promise } export const VaultTxTable = withTranslation(['tables', 'common'])( (props: VaultTxsTableProps & WithTranslation) => { const { rawData, showloading, pagination, getOrderList, t } = props const [page, setPage] = React.useState(0) const { isMobile, upColor } = useSettings() const history = useHistory() const getColumnModeTransaction = React.useCallback( (): Column[] => [ { key: 'Type', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelVaultTxType'), formatter: ({ row }: FormatterProps) => { const isForcedCloseOut = ((row.raw_data.operation.operateSubType as string) === 'VAULT_FORCE_SETTLEMENT' || (row.raw_data.operation.operateSubType as string) === 'VAULT_FORCE_WITHDRAW') && row.type === VaultRecordType.closeout if (row.erc20Symbol === 'USDT' && row.type === 'trade') { var longOrShort: 'long' | 'short' | undefined = 'long' } else if (row.erc20Symbol !== 'USDT' && row.type === 'trade') { longOrShort = 'short' } else { longOrShort = undefined } const upColorStr = upColor === 'green' ? 'var(--color-success)' : 'var(--color-error)' const downColorStr = upColor === 'green' ? 'var(--color-error)' : 'var(--color-success)' return ( {longOrShort === 'long' ? t('labelVaultBuyLong') : longOrShort === 'short' ? t('labelVaultSellShort') : isForcedCloseOut ? t(`labelVaultcloseoutForced`) : t(`labelVault${row.type}`)} ) }, }, { key: 'Filled', name: t('labelVaultTxFilled'), formatter: ({ row }: FormatterProps) => { return <>{row.mainContentRender} }, }, { key: 'status', name: t('labelVaultTxStatus'), formatter: ({ row }: FormatterProps) => { const color = [ sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED, 'VAULT_STATUS_EARNING', ].includes(row.status) ? 'var(--color-success)' : [ sdk.VaultOperationStatus.VAULT_STATUS_PENDING, sdk.VaultOperationStatus.VAULT_STATUS_PROCESSING, ].includes(row.status) ? 'var(--color-primary)' : row.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? 'var(--color-error)' : 'var(--color-text-primary)' return ( {t(`labelVault${row.status}`) + `${ row.type === VaultRecordType.trade && row.status !== sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? '(' + row.percentage + '%)' : '' }`} ) }, }, { key: 'Time', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelVaultTxTime'), formatter: ({ row }: FormatterProps) => { return <>{moment(row?.raw_data?.operation?.createdAt).fromNow()} }, }, { key: 'Action', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: 'Action', formatter: ({ row }: FormatterProps) => { return ( ) }, }, ], [t, props], ) const getColumnMobileTransaction = React.useCallback( (): Column[] => [ { key: 'Type', cellClass: 'textAlignLeft', headerCellClass: 'textAlignLeft', name: t('labelVaultTxType'), formatter: ({ row }: FormatterProps) => { return ( {t(`labelVault${row.type}`)} ) }, }, { key: 'Filled', name: t('labelVaultTxFilled'), formatter: ({ row }: FormatterProps) => { return ( <> {row.mainContentRender} ) }, }, { key: 'statusTime', cellClass: 'textAlignRight', headerCellClass: 'textAlignRight', name: t('labelVaultTxStatus') + '/' + t('labelVaultTxTime'), formatter: ({ row }: FormatterProps) => { const color = row.status === sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED ? 'var(--color-success)' : row.status === sdk.VaultOperationStatus.VAULT_STATUS_PENDING ? 'var(--color-primary)' : row.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? 'var(--color-error)' : 'var(--color-text-primary)' return ( {t(`labelVault${row.status}`) + `${ row.type === VaultRecordType.trade && row.status !== sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? '(' + row.percentage + '%)' : '' }`} {moment(row?.raw_data?.order?.createdAt).fromNow()} ) }, }, ], [history, upColor, t], ) const updateData = _.debounce( ({ currPage = page, pageSize = pagination.pageSize, }: { currPage?: number pageSize?: number }) => { getOrderList({ limit: pageSize, offset: (currPage - 1) * pageSize, }) }, globalSetup.wait, ) const handlePageChange = React.useCallback( async (currPage: number) => { setPage(currPage) updateData({ currPage: currPage }) }, [updateData], ) const defaultArgs: any = { columnMode: isMobile ? getColumnMobileTransaction() : getColumnModeTransaction(), generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], } React.useEffect(() => { updateData.cancel() handlePageChange(1) return () => { updateData.cancel() } }, [pagination?.pageSize]) return ( { props.onItemClick(row) }} {...{ ...defaultArgs, ...props, rawData, showloading, }} /> {pagination && !!rawData.length && ( )} ) }, ) export const VaultCloseDetail = withTranslation(['common'])( ({ t, vaultCloseDetail, }: { vaultCloseDetail: R & any } & WithTranslation) => { return ( {vaultCloseDetail.statusType === 'failed' ? ( ) : vaultCloseDetail.statusType === 'processing' ? ( ) : ( )} {vaultCloseDetail.statusLabel} {t('labelVaultExitCloseAmount')} {vaultCloseDetail?.amount} { {t('labelVaultRedeemDetail')} {vaultCloseDetail?.executionHistory.length ? ( vaultCloseDetail?.executionHistory?.map((item, index) => { return ( {item} ) }) ) : ( ( {t('labelNoContent')} )} /> )} } ) }, ) export const VaultOperationDetail = (props: { statusColor: string statusLabel: string statusType: "success" | "processing" | "failed" time: number type: 'VAULT_BORROW' | 'VAULT_MARGIN_CALL' | 'VAULT_REPAY' | 'VAULT_OPEN_POSITION' | 'VAULT_JOIN_REDEEM' | 'VAULT_CLOSE_SHORT' amount: string amountSymbol: string }) => { const { coinJson } = useSettings() const { statusColor, statusLabel, time, type, statusType, amount, amountSymbol } = props const { t } = useTranslation() return ( {statusType === 'failed' ? ( ) : statusType === 'processing' ? ( ) : ( )} {statusLabel} {t('labelType')} {type === 'VAULT_OPEN_POSITION' ? t('labelVaultJoin') : type === 'VAULT_MARGIN_CALL' ? t('labelVaultMarginCall') : type === 'VAULT_BORROW' ? t('labelVaultBorrow') : type === 'VAULT_JOIN_REDEEM' ? t('labelVaultJoinRedeem') : type === 'VAULT_CLOSE_SHORT' ? t('labelVaultCloseShort') : t('labelVaultRepay')} {t('labelVaultStatus')} {statusLabel} {type === 'VAULT_BORROW' || type === 'VAULT_REPAY' || type === 'VAULT_JOIN_REDEEM' || type === 'VAULT_CLOSE_SHORT' ? t('labelVaultAmount') : t('labelVaultCollateral')} {amount ? ( <> {' '} {amount} {mapSpecialTokenName(amountSymbol)} ) : ( EmptyValueTag )} {t('labelVaultTime')} {time && moment(time).format(YEAR_DAY_MINUTE_FORMAT)} ) } export const VaultTradeDetail = withTranslation(['common'])( ( props: { statusColor: string statusLabel: string statusType: "success" | "processing" | "failed" fromSymbol: string toSymbol: string placedAmount: string executedAmount: string executedRate: string convertedAmount: string price: string feeSymbol: string feeAmount: string time: number } & WithTranslation, ) => { const { statusColor, statusLabel, statusType, fromSymbol, toSymbol, placedAmount, executedAmount, executedRate, convertedAmount, price, feeSymbol, feeAmount, time, } = props const { coinJson } = useSettings() const { t } = useTranslation() return ( {statusType === 'failed' ? ( ) : statusType === 'processing' ? ( ) : ( )} {statusLabel} {t('labelType')} {t('labelVaultSwap')} {t('labelVaultStatus')} {statusLabel} {t('labelVaultPlacedAmount')} {t('labelVaultPlacedAmountTip')}}> {' '} {placedAmount} {fromSymbol} {t('labelVaultExecutedAmount')} {t("labelVaultExecutedAmountTip")}}> {' '} {executedAmount} {fromSymbol} {t('labelVaultExecutedRate')} {executedRate} {t('labelVaultConvertedAmount')} {t("labelVaultConvertedAmountTip")}}> {' '} {convertedAmount} {toSymbol} {t('labelVaultPrice')} {price} {t('labelVaultTxFee')} {' '} {feeAmount} {feeSymbol} {t('labelVaultTime')} {time && moment(time).format(YEAR_DAY_MINUTE_FORMAT)} ) }, ) type VaultConvertDetailProps = { totalValueInCurrency: string convertedInUSDT: string repaymentInUSDT?: string time: number dusts: { symbol: string coinJSON: any amount: string valueInCurrency: string }[] status: 'success' | 'processing' | 'failed' } export const VaultConvertDetail = (props: VaultConvertDetailProps) => { const { totalValueInCurrency, convertedInUSDT, repaymentInUSDT, time, dusts, status } = props const theme = useTheme() const { t } = useTranslation('tables') const iconDiv = React.useMemo(() => { switch (status) { case 'failed': return ( {t('labelVaultVAULT_STATUS_FAILED')} ) case 'success': return ( {t('labelVaultVAULT_STATUS_SUCCEED')} ) case 'processing': return ( {t('labelVaultVAULT_STATUS_PROCESSING')} ) } }, [status]) return ( {iconDiv} {repaymentInUSDT ? (repaymentInUSDT + ' USDT') : '--'} {totalValueInCurrency} {t('labelVaultRepayment')} } rightNode={ {repaymentInUSDT ? repaymentInUSDT + ' USDT' : '--'} } marginBottom={2} /> {t('labelVaultTime')} } rightNode={ {time ? moment(time).format(YEAR_DAY_MINUTE_FORMAT) : '--'} } /> {dusts && dusts.map((dust) => { return ( {dust.symbol} } rightNode={ {dust.amount} {dust.valueInCurrency} } /> ) })} {status === 'failed' && ( {t('labelVaultErrorOccurred')} )} ) } ================================================ FILE: packages/component-lib/src/components/tableList/vaultTable/index.ts ================================================ export { type VaultTxsTableProps, VaultTxTable, VaultCloseDetail } from './VaultTxTable' export { type RawDataVaultTxItem, VaultRecordType } from './Interface' ================================================ FILE: packages/component-lib/src/components/text-tooltip/index.tsx ================================================ import { Tooltip } from '@mui/material' type TextTooltipProps = { tooltipTitle: string text: string } const TextTooltip = ({ tooltipTitle, text }: TextTooltipProps) => { return ( {text} ) } export default TextTooltip ================================================ FILE: packages/component-lib/src/components/toast/index.tsx ================================================ import { Alert, AlertTitle, Box, Snackbar, SnackbarOrigin, Typography } from '@mui/material' import { AlertIcon, ErrorIcon, GoodIcon, InfoIcon, SnackbarMessage, } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { withTranslation, WithTranslation } from 'react-i18next' import React from 'react' import { VendorIconItem } from '../tradePanel' export enum ToastType { success = 'success', error = 'error', warning = 'warning', info = 'info', } export type TOASTOPEN = { open: boolean content: JSX.Element | string type: ToastType [key: string]: any } export type TOSTOBJECT = { toastOpen: TOASTOPEN setToastOpen: (state: TOASTOPEN) => void closeToast: () => void } export interface ToastProps { open: boolean severity?: ToastType alertText: string | JSX.Element autoHideDuration?: number onClose: () => void snackbarOrigin?: SnackbarOrigin } const AlertStyled = styled(Alert)` svg:first-of-type { width: 2rem; height: 2rem; margin-top: 0.2rem; } ` export const Toast = withTranslation('common')( ({ t, open, severity = ToastType.success, alertText, autoHideDuration = 2000, onClose, snackbarOrigin, }: ToastProps & WithTranslation) => { const renderTitle = severity === 'success' ? t('labelSuccessfully') : severity === 'warning' ? t('labelWarning') : severity === 'error' ? t('labelFailure') : t('labelPrompt') const renderIcon = severity === 'success' ? ( ) : severity === 'warning' ? ( ) : severity === 'error' ? ( ) : ( ) return ( {renderTitle} {alertText} ) }, ) export const NoticeSnack = ({ messageInfo, handleClose, open, actionEle, }: { open: boolean handleClose: () => void actionEle: JSX.Element messageInfo: SnackbarMessage }) => { return ( {messageInfo.svgIcon && VendorIconItem({ svgIcon: messageInfo.svgIcon })} {messageInfo ? messageInfo.message : undefined} } action={actionEle} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} /> ) } export const NoticePanelSnackBar = ({ noticeSnacksElEs, }: { noticeSnacksElEs: Array }) => { return ( <> {noticeSnacksElEs.map((item, index) => { return {item as any} })} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/Amm/AmmPanel.tsx ================================================ import { AmmChgData, AmmDepositWrap, AmmWithdrawWrap } from '../components' import { Box, BoxProps, Tab, Tabs, Toolbar } from '@mui/material' import { AmmExitData, AmmInData, AmmJoinData, IBData, AmmPanelType, } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import React from 'react' import { CountDownIcon } from '../components/tool/Refresh' import styled from '@emotion/styled' import { boxLiner, toolBarPanel } from '../../styled' import { AmmProps } from './Interface' import { useSettings } from '../../../stores' const WrapStyle = styled(Box)< BoxProps & { _height?: number | string _width?: number | string isMobile: boolean } >` ${({ _width, isMobile }) => isMobile ? `width:100%;height:auto;` : ` width: ${ typeof _width === 'string' ? _width : typeof _width === 'number' ? _width + 'px' : `var(--swap-box-width)` }; height: auto;`} ${({ theme }) => boxLiner({ theme })} ${({ theme }) => toolBarPanel({ theme })} border-radius: ${({ theme }) => theme.unit}px; .trade-panel .coinInput-wrap { background: var(--field-opacity); } .MuiToolbar-root { //padding-left:0; justify-content: space-between; padding: 0 ${({ theme }) => (theme.unit * 5) / 2}px; } ` as ( props: BoxProps & { _height?: number | string _width?: number | string isMobile: boolean }, ) => JSX.Element const TabPanelBtn = ({ t, value, handleChange }: WithTranslation & any) => { return ( ) } export const AmmPanel = withTranslation('common', { withRef: true })( < T extends AmmJoinData ? C : IBData>, TW extends AmmExitData ? C : IBData>, I, ACD extends AmmInData, C = IBData, >({ t, tabSelected = AmmPanelType.Join, ammDepositData, ammWithdrawData, disableDeposit, disableWithdraw, handleAmmAddChangeEvent, handleAmmRemoveChangeEvent, ammCalcDataDeposit, ammCalcDataWithDraw, tokenDepositAProps, tokenDepositBProps, tokenWithDrawAProps, tokenWithDrawBProps, ammDepositBtnStatus, ammWithdrawBtnStatus, ammDepositBtnI18nKey, ammWithdrawBtnI18nKey, onRefreshData, refreshRef, onAmmAddClick, onAmmRemoveClick, // onAmmAddChangeEvent, // onRemoveChangeEvent, handleError, height, width, anchors, propsLPExtends, propsAExtends, propsBExtends, ammType, handleTabChange, // coinAPrecision, // coinBPrecision, ...rest }: AmmProps & WithTranslation) => { const _onChangeAddEvent = React.useCallback( async ({ tradeData, type }: AmmChgData) => { handleAmmAddChangeEvent(tradeData, type) // if (typeof onAmmAddChangeEvent == "function") { // onAmmAddChangeEvent({ tradeData, type } as AmmChgData); // } }, [handleAmmAddChangeEvent], ) const _onChangeRemoveEvent = React.useCallback( async ({ tradeData, }: // type, // percentage { tradeData: TW } & { type: 'lp'; percentage?: number }) => { handleAmmRemoveChangeEvent(tradeData) // if (typeof onRemoveChangeEvent == "function") { // onRemoveChangeEvent({ tradeData, type } as AmmWithdrawChgData); // } }, [handleAmmRemoveChangeEvent], ) // const panelList: Pick< // PanelContent<"ammJoin" | "ammExit">, // "key" | "element" // >[] = [ // { // key: "ammJoin", // element: React.useMemo( // () => ( // // ), // [ // t, // rest, // anchors, // disableDeposit, // ammDepositBtnStatus, // ammDepositBtnI18nKey, // ammCalcDataDeposit, // // onAmmAddClick, // handleError, // // _onChangeAddEvent, // ammDepositData, // tokenDepositAProps, // tokenDepositBProps, // ] // ), // }, // { // key: "ammExit", // element: React.useMemo( // () => ( // // ), // [ // t, // rest, // anchors, // disableWithdraw, // ammWithdrawBtnStatus, // ammWithdrawBtnI18nKey, // ammCalcDataWithDraw, // // onAmmRemoveClick, // // handleError, // // _onChangeRemoveEvent, // ammWithdrawData, // tokenWithDrawAProps, // tokenWithDrawBProps, // ] // ), // }, // ]; // const theme = useTheme(); const { isMobile } = useSettings() return ( handleTabChange(value), ...rest, }} /> {ammType === AmmPanelType.Join && ( key={'ammJoin'} {...{ t, ...rest, anchors, disableDeposit, ammDepositBtnStatus, ammDepositBtnI18nKey, ammCalcData: ammCalcDataDeposit, onAmmAddClick, onAddChangeEvent: _onChangeAddEvent, ammData: ammDepositData, tokenAProps: { ...tokenDepositAProps }, tokenBProps: { ...tokenDepositBProps }, propsAExtends, propsBExtends, }} /> )} {ammType === AmmPanelType.Exit && ( key={'ammExit'} {...{ t, ...rest, anchors, disableWithdraw, ammWithdrawBtnStatus, ammWithdrawBtnI18nKey, ammCalcData: ammCalcDataWithDraw, onAmmRemoveClick, handleError, propsLPExtends, selectedPercentage: -1, onRemoveChangeEvent: _onChangeRemoveEvent, ammData: ammWithdrawData, tokenAProps: { ...tokenWithDrawAProps }, tokenBProps: { ...tokenWithDrawBProps }, }} /> )} ) }, ) as < T extends AmmJoinData ? C : IBData>, TW extends AmmExitData ? C : IBData>, I, ACD extends AmmInData, C = IBData, >( props: AmmProps & React.RefAttributes, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/tradePanel/Amm/Interface.tsx ================================================ import { InputButtonProps } from '../../basic-lib' import { AccountStatus, AmmExitData, AmmJoinData, AmmPanelType, CoinInfo, IBData, } from '@loopring-web/common-resources' import { AmmDepositBaseProps, AmmWithdrawBaseProps } from '../components' /** * */ export type AmmPanelBaseProps = { ammDepositData: T ammWithdrawData: TW tabSelected?: AmmPanelType disableDeposit?: boolean disableWithdraw?: boolean ammCalcDataDeposit: ACD ammCalcDataWithDraw: ACD tokenDepositAProps?: Partial>> tokenDepositBProps?: Partial>> tokenWithDrawAProps?: Partial>> tokenWithDrawBProps?: Partial>> ammDepositBtnI18nKey?: string ammWithdrawBtnI18nKey?: string height?: number width?: number } export type AmmProps< T extends AmmJoinData ? C : IBData>, TW extends AmmExitData ? C : IBData>, I, ACD, C = IBData, > = AmmPanelBaseProps & { handleAmmAddChangeEvent: (data: T, focusOn: 'coinA' | 'coinB') => void handleAmmRemoveChangeEvent: (data: TW) => void // onAmmAddChangeEvent?: (data: AmmChgData) => AmmChgData; // onRemoveChangeEvent?: ( // data: AmmWithdrawChgData // ) => AmmWithdrawChgData; refreshRef: React.Ref onRefreshData?: () => void accStatus?: AccountStatus coinAPrecision?: number coinBPrecision?: number ammType: AmmPanelType handleTabChange: (index: AmmPanelType) => void } & AmmWithdrawBaseProps & AmmDepositBaseProps ================================================ FILE: packages/component-lib/src/components/tradePanel/Amm/index.ts ================================================ export * from './AmmPanel' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tradePanel/Deposit/DepositGroup.tsx ================================================ import { DepositGroupProps, DepositPanelType } from './Interface' import { IBData } from '@loopring-web/common-resources' import { PanelContent } from '../../basic-lib' import { DepositPanel } from '../../modal/ModalPanels/DepositPanel' import { VendorMenu } from './VendorMenu' import { Box, BoxProps, Toolbar, Typography } from '@mui/material' import { useTheme } from '@emotion/react' import { DepositTitleGroup, DepositTitleNewGroup } from './DepositTitle' import React from 'react' import SwipeableViews from 'react-swipeable-views' import styled from '@emotion/styled' import { useTranslation } from 'react-i18next' import { useSettings } from '../../../stores' const ToolbarStyle = styled(Toolbar)` .MuiTabs-root { flex: 1; .MuiTab-root { display: inline-flex; flex-direction: row; } } ` const BoxStyle = styled(Box)` .trade-panel { width: 100%; height: 240px; } .isNew .trade-panel { height: 308px; } .react-swipeable-view-container { .trade-panel .react-swipeable-view-container { & > div { padding: 0; & > .MuiGrid-container { padding: 0; } } padding: 0; } } .way-content > div:first-of-type { position: relative; font-size: ${({ theme }) => theme.fontDefault.body1}; ${({ isMobile, theme }) => isMobile ? ` padding-bottom: ${10 * theme.unit}px; &:before { display: block; content: " "; position: absolute; bottom: 0px; left:0; right:0; bottom: ${6 * theme.unit}px; margin: 0 ${2 * theme.unit}px; color: var(--color-text-third); border: 1px solid var(--color-text-third); opacity: 0.4; } &:after { display: block; content: "OR"; position: absolute; width: 32px; height: 32px; line-height: 32px; text-align: center; left:50%; margin-left: -16px; bottom: ${2 * theme.unit}px; background: ${theme.colorBase.box}; color: var(--color-text-third); }` : ` &:before { display: block; content: " "; position: absolute; right: ${-theme.unit}px; bottom: 0; top:0; height: 100%; color: var(--color-text-third); border-right: 1px solid var(--color-divide); } &:after { display: block; content: "OR"; position: absolute; bottom: 50%; width: 32px; height: 32px; line-height: 32px; text-align: center; right: ${-3 * theme.unit}px; background: ${theme.colorBase.box}; color: var(--color-text-third); }`} } padding-left: 0; padding-right: 0; ` as (props: BoxProps & { isMobile: boolean | undefined }) => JSX.Element export const DepositGroup = , I>({ depositProps, vendorMenuProps, tabIndex = DepositPanelType.Deposit, }: // onTabChange, DepositGroupProps) => { const theme = useTheme() const { t } = useTranslation(['common']) const { isMobile } = useSettings() const [_tabIndex, setTabIndex] = React.useState( tabIndex ?? DepositPanelType.Deposit, ) React.useEffect(() => { setTabIndex(tabIndex) }, [tabIndex]) const panelList: Pick, 'key' | 'element'>[] = [ { key: 'Deposit', element: , }, { key: 'Vendor', element: , }, ] return ( {depositProps.isNewAccount ? t('labelCreateLayer2Title', { layer2: 'Layer 2' }) : t('labelAddAsset')} {depositProps.isNewAccount ? ( {panelList.map((panel, index) => { return ( {DepositTitleNewGroup()[index]} {panel.element} ) })} ) : ( { setTabIndex(index) }} tabIndex={_tabIndex} /> {panelList.map((panel, index) => { return {panel.element} })} )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/Deposit/DepositTitle.tsx ================================================ import { Tab, Tabs, Typography } from '@mui/material' import { Info2Icon, L1L2_NAME_DEFINED, MapChainId } from '@loopring-web/common-resources' import React from 'react' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { bindHover } from 'material-ui-popup-state' import { Trans, useTranslation } from 'react-i18next' import { PopoverPure } from '../../basic-lib' import { DepositPanelType } from './Interface' import { useSettings } from '../../../stores' export const DepositTitle = ({ title, description, isHideDes = false }: any) => { const { t } = useTranslation() const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const popupState = usePopupState({ variant: 'popover', popupId: `popupId-deposit`, }) return ( {title ? title : t('depositTitle')} {!isHideDes && ( <> Once your deposit is confirmed on Ethereum, it will be added to your balance within 2 minutes. )} ) } const ThirdPartTitle = React.memo(() => { const { t } = useTranslation() const popupState = usePopupState({ variant: 'popover', popupId: `popupId-ThirdPart`, }) return ( <> {t('labelVendor')} Make an order form third Loopring-parter, Once your order confirmed by Loopring, it will be added to your balance within 2 minutes. ) }) export const DepositTitleGroup = ({ tabIndex, onTabChange, title, description, }: { title?: string description?: string tabIndex: DepositPanelType onTabChange: (index: DepositPanelType) => void // (event: React.SyntheticEvent, value: DepositTabIndex) => void; }) => { return ( <> onTabChange(value)}> } /> } /> ) } export const DepositTitleNewGroup = () => { return [ , , ] } ================================================ FILE: packages/component-lib/src/components/tradePanel/Deposit/Interface.tsx ================================================ import { DepositProps } from '../Interface' import { VendorMenuProps } from '../../modal' export enum DepositPanelType { Deposit = 0, ThirdPart = 1, } export type DepositGroupProps = { depositProps: DepositProps vendorMenuProps: VendorMenuProps tabIndex?: DepositPanelType // onTabChange: (value: DepositPanelType) => void; } ================================================ FILE: packages/component-lib/src/components/tradePanel/Deposit/VendorMenu.tsx ================================================ import { Avatar, Box, Typography } from '@mui/material' import { MenuBtnStyled } from '../../styled' import { VendorMenuProps } from '../../modal/ModalPanels/Interface' import { useTranslation } from 'react-i18next' import { BanxaIcon, RampIcon, SCENARIO, TradeBtnStatus, TradeTypes, VendorProviders, } from '@loopring-web/common-resources' import { useTheme } from '@emotion/react' import { useSettings } from '../../../stores' import { TagIconList } from '../../block' import { CoinIcon } from '../../basic-lib' export const VendorIconItem = ({ svgIcon }: { svgIcon: string }) => { const theme = useTheme() switch (svgIcon) { case 'BanxaIcon': return ( ) case 'RampIcon': return ( ) } } export const VendorMenu = ({ vendorList, banxaRef, // handleSelect, campaignTagConfig, type = TradeTypes.Buy, vendorForce, }: VendorMenuProps) => { const { t } = useTranslation() const { isMobile } = useSettings() const theme = useTheme() return ( {type === 'Buy' ? t('labelL1toL2ThirdPartOn') : t('labelL1toL2ThirdPartOff')} {t('labelWhatProvider')} {vendorList.map((item) => ( { if (item.handleSelect) { item.handleSelect(e) } }} > {VendorIconItem({ svgIcon: item.svgIcon })} {campaignTagConfig && ( )} {type == TradeTypes.Sell && ( )} {item.key === 'Ramp' ? ( <> ) : ( <> )} {/*{item.flag &&*/} {/* item.flag.startDate < Date.now() &&*/} {/* Date.now() < item.flag.endDate && (*/} {/* <>*/} {/* */} {/* {t(item.flag.highLight ?? "")}*/} {/* */} {/* /!**!/*/} {/* /!* {item.flag.tag}*!/*/} {/* /!**!/*/} {/* */} {/* )}*/} ))} ) { /**/ } } ================================================ FILE: packages/component-lib/src/components/tradePanel/Deposit/index.ts ================================================ export * from './DepositGroup' export * from './Interface' export * from '../../modal/ModalPanels/DepositPanel' export * from './DepositTitle' export * from './VendorMenu' ================================================ FILE: packages/component-lib/src/components/tradePanel/ExportAccount/index.tsx ================================================ import { ExportAccountProps } from '../Interface' import { withTranslation, WithTranslation } from 'react-i18next' import { SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { ExportAccountWrap } from '../components' import React from 'react' export const ExportAccountPanel = withTranslation('common', { withRef: true })( ({ exportAccountProps, setExportAccountToastOpen, ...rest }: ExportAccountProps & WithTranslation) => { const props: SwitchPanelProps<'tradeMenuList' | 'trade'> = { index: 0, // show default show panelList: [ { key: 'trade', element: React.useMemo( () => ( ), [exportAccountProps, rest, setExportAccountToastOpen], ), toolBarItem: undefined, }, ], } return }, ) // export const TransferModal = withTranslation() ================================================ FILE: packages/component-lib/src/components/tradePanel/Interface.ts ================================================ import { BtradeType, CAMPAIGNTAGCONFIG, CoinKey, FeeInfo, IBData, MarketType, NFTWholeINFO, SCENARIO, TradeBaseType, TradeCalcProData, TradeProType, WithdrawType, WithdrawTypes, } from '@loopring-web/common-resources' import { BasicACoinTradeHookProps, ClaimExtendProps, CreateRedPacketViewProps, DefaultProps, DepositExtendProps, DepositInfoProps as _DepositInfoProps, ExportAccountExtendProps, ForceWithdrawViewProps, NFTDeployViewProps, NFTDepositViewProps, NFTMetaViewProps, NFTMintAdvanceViewProps, NFTMintViewProps, ResetExtendProps, ResetInfoProps as _ResetInfoProps, TransferExtendProps, TransferInfoProps as _TransferInfoProps, WithdrawExtendProps, } from './components/Interface' import { SwapData, SwapTradeBaseEventProps, SwapTradeBaseProps, VaultBorrowBaseProps, VaultRepayWrapProps, } from './components' import { StopTradeLimitInfoProps, TradeLimitInfoProps, TradeMarketInfoProps, TradeProBaseEventProps, } from './tradePro/Interface' import React from 'react' import { TOASTOPEN } from '../toast' import { VaultExitBaseProps, VaultJoinBaseProps } from './components/VaultWrap' import { GetContactsResponse } from '@loopring-web/loopring-sdk' export type SwapTradeData = { sell: T buy: T isChecked?: boolean slippage: number | string __cache__?: { [key: string]: any } btradeType?: BtradeType } export type LimitTradeData = { price: T base: T quote: T type: TradeProType isChecked?: boolean } export type StopLimitTradeData = { price: T stopPrice: T base: T quote: T type: TradeProType } export type MarketTradeData = { // price: T, base: T quote: T type: TradeProType isChecked?: boolean slippage: number | string __cache__?: { [key: string]: any } } export type { SwapData } export type ModalProps = { open: boolean onClose: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] btnDisable?: boolean } export type ResetProps = ResetExtendProps export type ExportAccountProps = ExportAccountExtendProps export type DepositProps = BasicACoinTradeHookProps & DepositExtendProps export type WithdrawProps = BasicACoinTradeHookProps & WithdrawExtendProps export type TransferProps = BasicACoinTradeHookProps & TransferExtendProps export type ClaimProps = BasicACoinTradeHookProps & ClaimExtendProps export type ResetInfoProps = DefaultProps & _ResetInfoProps export type DepositInfoProps = DefaultProps & _DepositInfoProps export type CreateRedPacketProps = Omit< BasicACoinTradeHookProps, 'type' > & CreateRedPacketViewProps export type TransferInfoProps = DefaultProps & _TransferInfoProps> export type SwapInfoProps = SwapTradeBaseProps export type NFTDepositProps = NFTDepositViewProps export type NFTMintProps = Omit, 'metaData'> export type NFTMetaProps = Omit, 'nftMeta'> export type NFTMintAdvanceProps = NFTMintAdvanceViewProps export type NFTDeployProps = NFTDeployViewProps export type ForceWithdrawProps = BasicACoinTradeHookProps & ForceWithdrawViewProps /** * @type SwapProps * @param swapTradeData: SwapTradeData * @callback handleSwapPanelEvent { * @param type='buy'|'sell'|'exchange' * @param to='menu'|'button' to the view of list for select item * @param SwapData * } * @callback onSwapClick :( * @param SwapData * ) => void { * @param tradeCalcData TradeCalcData * @param swapBtnStatus='disable'|'loading' * @param tokenSellProps i18n done string * @param tokenBuyProps i18n done string * @callback onChangeEvent?: ( * @param index=0|1 0:when view on type button, 1: when view on type menu * @param data: SwapData * ) => SwapData */ export type SwapProps = { refreshRef: React.Ref onRefreshData?: () => void titleI8nKey?: string toPro?: () => void tradeData: SwapTradeData campaignTagConfig?: CAMPAIGNTAGCONFIG handleSwapPanelEvent: (data: SwapData>, switchType: SwapType) => Promise market?: MarketType onChangeEvent?: (index: 0 | 1, data: SwapData>) => SwapData> setToastOpen?: (state: TOASTOPEN) => void scenario?: SCENARIO _width?: string hideSecondConfirmation?: boolean scrollDisabled?: boolean bTradeTutorial?: { show: boolean onToggle: () => void checked: boolean } } & SwapInfoProps & SwapTradeBaseEventProps & SwapTradeBaseProps export type TradeLimitProps, I> = { tradeData: L | undefined handleSubmitEvent: (data: L) => Promise onChangeEvent: (data: L, formType: TradeBaseType) => L } & TradeLimitInfoProps & TradeProBaseEventProps export type TradeStopLimitProps, I> = { tradeData: L handleSubmitEvent: (data: L) => Promise onChangeEvent: (data: L, formType: TradeBaseType) => L } & StopTradeLimitInfoProps & TradeProBaseEventProps export type TradeMarketProps< M extends MarketTradeData, T extends IBData, TCD extends TradeCalcProData, I = CoinKey, > = { tradeData: M | undefined handleSubmitEvent: (data: M) => Promise onChangeEvent: (data: M, formType: TradeBaseType) => M } & TradeMarketInfoProps & TradeProBaseEventProps export type SwitchData = { to: 'menu' | 'button' tradeData: T } export enum SwitchType { TO_MENU = 'Tomenu', TO_BTN = 'Tobutton', } export enum SwapType { BUY_CLICK = 'buyTomenu', SEll_CLICK = 'sellTomenu', EXCHANGE_CLICK = 'exchange', BUY_SELECTED = 'buyTobutton', SELL_SELECTED = 'sellTobutton', } export type ModalPanelProps = { open: boolean contentClassName?: string onClose: { bivarianceHack(event: {}, reason: 'backdropClick' | 'escapeKeyDown'): void }['bivarianceHack'] content: JSX.Element _height?: number | string _width?: number | string } export type FeeSelectModalProps = { open: boolean onClose: () => void chargeFeeTokenList: FeeInfo[] feeInfo: FeeInfo | undefined handleToggleChange: (value: FeeInfo) => void disableNoToken?: boolean withdrawInfos?: { types: Partial type: WithdrawType onChangeType: (w: WithdrawType) => void } } export type FeeSelectProps = { chargeFeeTokenList: FeeInfo[] handleToggleChange: (value: FeeInfo) => void feeInfo?: FeeInfo disableNoToken?: boolean open: boolean onClose: () => void onClickFee: () => void feeLoading: boolean isFeeNotEnough: boolean isFastWithdrawAmountLimit?: boolean withdrawInfos?: { types: Partial type: WithdrawType onChangeType: (w: WithdrawType) => void } floatLeft?: boolean middleContent?: JSX.Element feeNotEnoughContent?: JSX.Element networkFeeElement?: JSX.Element } export type VaultJoinProps = BasicACoinTradeHookProps & VaultJoinBaseProps export type VaultBorrowProps = BasicACoinTradeHookProps & VaultBorrowBaseProps export type VaultRepayProps = BasicACoinTradeHookProps & VaultRepayWrapProps export type VaultExitProps = VaultExitBaseProps export type TransferToTaikoAccountProps = { title: string onClickContact: () => void onClickToken: () => void onClickFee: () => void onInputAmount: (str: string) => void onInputAddress: (str: string) => void onClickBalance: () => void onClickBack: () => void onClickClose: () => void fee: string balance: string token: { coinJSON: any symbol: string } feeSelect: FeeSelectProps panel: 'main' | 'contacts' | 'tokenSelection' | 'confirm' contacts: { onSelect: (address: string) => void scrollHeight: string } & Pick tokenSelection: { filter: string tokens: { symbol: string coinJSON: any[] amount: string }[] onChangeFilter: (str: string) => void onClickClearFilter: () => void onClickCancel: () => void onClickToken: (symbol: string) => void } receiptInput: string amountInput: string open: boolean supportedTokens: string[] sendBtn: { disabled: boolean, onClick: () => void text?: string } maxAlert: { show: boolean, message: string, } receiptError: { show: boolean, message: string, } receiptClear: { show: boolean, onClick: () => void, } showReceiptWarning: boolean onClickConfirm: () => void retrySend: () => void hideContactBtn?: boolean } export * from './components/Interface' ================================================ FILE: packages/component-lib/src/components/tradePanel/ModalPanel.tsx ================================================ import { Box, BoxProps, Modal as MuiModal } from '@mui/material' import { AccountStep, ActiveAccountPanel, AnotherNetworkNotice, ClaimProps, CollectionAdvanceProps, DeFiStackRedeemWrap, DeFiStakeRedeemWrapProps, DeployNFTWrap, DepositPanel, DepositProps, ExportAccountPanel, InformationForAccountFrozen, LayerswapNotice, ModalCloseButton, modalContentBaseStyle, ModalPanelProps, NFTDeployProps, RedPacketViewStep, ResetAccountConfirmationPanel, ResetPanel, ResetProps, TransferPanel, TransferProps, useOpenModals, useSettings, WithdrawPanel, WithdrawProps, EditContact, TransferToTaikoAccountModal, TransferToTaikoAccountProps, BridgePanel, } from '../..' import { Account, CollectionMeta, FeeInfo, IBData, SendAssetList, TRADE_TYPE, TradeNFT, } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import { useTheme } from '@emotion/react' import styled from '@emotion/styled' import { CollectionAdvanceWrap } from './components/CollectionAdvanceWrap' import { ClaimWithdrawPanel } from '../modal/ModalPanels/ClaimWithdrawPanel' import { TargetRedpacketWrap } from './components/TargetRedpacketWrap' import { TransferNFTBurn } from './components' const BoxStyle = styled(Box)<{ _height?: number | string; _width?: number | string } & BoxProps>` display: flex; align-items: center; flex-direction: column; justify-content: center; ${({ theme }) => modalContentBaseStyle({ theme: theme })} background: var(--color-pop-bg); .trade-wrap { margin-top: -26px; } > .vault-wrap { .vaultSwap { .MuiToolbar-root { > .MuiTypography-root:first-of-type { align-self: flex-end; font-size: ${({ theme }) => theme.fontDefault.h5}; padding: 0 ${({ theme }) => 3 * theme.unit}px; margin-bottom: ${({ theme }) => 1.5 * theme.unit}px; } > .toolButton { height: 100%; align-items: center; } .MuiTypography-root { height: auto; } margin-bottom: ${({ theme }) => 2 * theme.unit}px; border-bottom: var(--color-divide) 1px solid; .record { visibility: hidden; } } } margin-top: -32px; .MuiToolbar-root { height: 48px; padding: 0; } .toolbarTitle { position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; justify-content: flex-end; } } > .vault-wrap { .vaultSwap { .MuiToolbar-root { > .MuiTypography-root:first-of-type { align-self: flex-end; font-size: ${({ theme }) => theme.fontDefault.h5}; padding: 0 ${({ theme }) => 3 * theme.unit}px; margin-bottom: ${({ theme }) => 1.5 * theme.unit}px; } > .toolButton { height: 100%; align-items: center; } .MuiTypography-root { height: auto; } margin-bottom: ${({ theme }) => 2 * theme.unit}px; border-bottom: var(--color-divide) 1px solid; .record { visibility: hidden; } } } margin-top: -32px; .MuiToolbar-root { height: 48px; padding: 0; } .toolbarTitle { position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; justify-content: flex-end; } } .trade-panel { position: relative; height: ${({ _height }) => _height && Number.isNaN(_height) ? _height + 'px' : _height ? _height : 'auto'}; &.valut-load { padding: 0; border: 0; .react-swipeable-view-container { & > div { padding: 0 ${({ theme }) => (theme.unit * 5) / 2}px 0px; } } } .react-swipeable-view-container { & > div { padding: 0 ${({ theme }) => (theme.unit * 5) / 2}px ${({ theme }) => theme.unit * 5}px; overflow-x: hidden; overflow-y: scroll !important; background: initial; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* Internet Explorer 10+ */ &::-webkit-scrollbar { /* WebKit */ width: 0; } .container { height: 100%; padding-top: 0; } } } } ` as (props: { _height?: number | string; _width?: number | string } & BoxProps) => JSX.Element export const Modal = withTranslation('common')( ({ open, onClose, content, _height, contentClassName, _width, ...rest }: ModalPanelProps & WithTranslation) => { const { isMobile } = useSettings() return ( {content} ) }, ) export const ModalPanel = < T extends IBData, N extends IBData & TradeNFT, C extends CollectionMeta, I, F = FeeInfo, >({ transferProps, withdrawProps, depositProps, nftTransferProps, nftWithdrawProps, nftDeployProps, resetProps, claimProps, nftBurnProps, activeAccountProps, collectionAdvanceProps, sideStackRedeemProps, contactAddProps, assetsData, account, baseURL, transferToTaikoProps, ...rest }: { _width?: number | string _height?: number | string nftBurnProps: TransferProps contactAddProps: any transferProps: TransferProps withdrawProps: WithdrawProps baseURL: string nftTransferProps: TransferProps claimProps: ClaimProps nftWithdrawProps: WithdrawProps nftDeployProps: NFTDeployProps depositProps: DepositProps sideStackRedeemProps: DeFiStakeRedeemWrapProps collectionAdvanceProps: CollectionAdvanceProps resetProps: ResetProps activeAccountProps: ResetProps assetsData: any[] exportAccountProps: any account: Account setExportAccountToastOpen: any transferToTaikoProps: TransferToTaikoAccountProps }) => { const { isMobile } = useSettings() const { modals, setShowTransfer, setShowDeposit, setShowWithdraw, setShowResetAccount, setShowActiveAccount, setShowExportAccount, setShowNFTTransfer, setShowNFTWithdraw, setShowNFTDeploy, setShowAccount, setShowClaimWithdraw, setShowCollectionAdvance, setShowSideStakingRedeem, setShowTargetRedpacketPop, setShowRedPacket, setShowEditContact, setShowTransferToTaikoAccount, setShowBridge, } = useOpenModals() const { isShowTransfer, isShowWithdraw, isShowDeposit, isShowNFTTransfer, isShowNFTWithdraw, isShowNFTDeploy, isShowResetAccount, isShowExportAccount, isShowTradeIsFrozen, isShowActiveAccount, isShowCollectionAdvance, isShowLayerSwapNotice, isShowAnotherNetwork, isShowClaimWithdraw, isShowSideStakingRedeem, isShowTargetRedpacketPop, isShowEditContact, isShowBridge, } = modals const theme = useTheme() const etherscanBaseUrl = 'https://etherscan.io/' return ( <> setShowClaimWithdraw({ isShow: false, claimToken: undefined as any })} content={ } /> { isShowTransfer.info?.onCloseCallBack && isShowTransfer.info?.onCloseCallBack() setShowTransfer({ isShow: false }) }} content={ {...{ ...rest, _width: `calc(var(--modal-width) - ${(theme.unit * 5) / 2}px)`, // _height: DEFAULT_TRANSFER_HEIGHT + 100, ...transferProps, assetsData, _height: 'auto', ...transferProps, assetsData, }} /> } /> { isShowWithdraw.info?.onCloseCallBack && isShowWithdraw.info?.onCloseCallBack() setShowWithdraw({ isShow: false }) }} content={ {...{ ...rest, _width: `calc(var(--modal-width) - ${(theme.unit * 5) / 2}px)`, _height: isMobile ? 'auto' : 480 + (withdrawProps.withdrawMode?.showTrustUI ? 100 : 0) + (withdrawProps.isToMyself ? 0 : 100), ...withdrawProps, assetsData, isFromContact: isShowWithdraw.address ? true : false, contact: isShowWithdraw.address ? { address: isShowWithdraw.address!, name: isShowWithdraw.name!, } : undefined, }} /> } /> setShowNFTTransfer({ isShow: false })} content={ { setShowNFTTransfer({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.SendNFTGateway, }) }} /> } /> setShowNFTWithdraw({ isShow: false })} content={ { setShowNFTWithdraw({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.SendNFTGateway, }) }} /> } /> setShowNFTDeploy({ isShow: false })} content={ {...{ ...nftDeployProps, assetsData, }} /> } /> setShowDeposit({ isShow: false })} content={ } /> setShowNFTTransfer({ isShow: false })} content={ } /> setShowResetAccount({ ...isShowResetAccount, isShow: false })} content={ isShowResetAccount.info?.confirmationType ? ( setShowResetAccount({ ...isShowResetAccount, info: { ...isShowResetAccount.info, confirmationType: undefined }, }) } type={isShowResetAccount?.info?.confirmationType} /> ) : ( {...{ ...rest, _width: `calc(var(--modal-width) - ${(theme.unit * 5) / 2}px)`, _height: `auto`, ...resetProps, assetsData, }} /> ) } /> setShowActiveAccount({ ...isShowActiveAccount, isShow: false })} content={ isShowActiveAccount?.info?.confirmationType ? ( setShowActiveAccount({ ...isShowActiveAccount, info: { ...isShowActiveAccount.info, confirmationType: undefined }, }) } type={isShowActiveAccount?.info?.confirmationType} /> ) : ( {...{ ...rest, _width: `calc(var(--modal-width) - ${(theme.unit * 5) / 2}px)`, _height: `auto`, ...activeAccountProps, }} /> ) } /> setShowExportAccount({ ...isShowExportAccount, isShow: false })} content={ } /> setShowExportAccount({ ...isShowExportAccount, isShow: false })} content={ } /> setShowCollectionAdvance({ isShow: false })} content={ } /> setShowSideStakingRedeem({ isShow: false })} content={ } /> { setShowTargetRedpacketPop({ isShow: false, info: {} }) }} content={ { setShowTargetRedpacketPop({ isShow: false, info: {} }) setShowRedPacket({ isShow: true, info: { ...redpacket, }, step: RedPacketViewStep.OpenPanel, }) }} /> } /> { setShowEditContact({ isShow: false, info: {} }) }} content={ { setShowAccount({ isShow: true, step: AccountStep.SendAssetFromContact, info: { ...contactAddProps?.isEdit?.item, select: SendAssetList.SendAssetToOtherL1.key, }, }) setShowEditContact({ isShow: false, info: {} }) } : undefined } // contacts={isShowAccount.info?.contacts} // setToast={setToast} /> } /> { setShowBridge({ isShow: false }) }} content={ { setShowBridge({ isShow: false }) setShowTransferToTaikoAccount({ isShow: true, from: 'bridge' }) } }} /> } /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/Panel.stories.tsx ================================================ import styled from '@emotion/styled' import { SwapPanel } from './Swap' import { Meta, Story } from '@storybook/react' import { MemoryRouter } from 'react-router-dom' import { Box, Grid } from '@mui/material' import { AccountStatus, AmmExitData, AmmInData, AmmJoinData, IBData, SlippageTolerance, TRADE_TYPE, TradeBtnStatus, AmmPanelType, } from '@loopring-web/common-resources' import { ammCalcData, coinMap, CoinType, DUALCALCDATA, TOKEN_INFO, tradeCalcData, walletMap, } from '../../static' import { Button } from '../basic-lib' import { ResetPanel } from './Reset' import { useTranslation } from 'react-i18next' import { AmmPanel, AmmProps, DepositProps, DualWrap, DualWrapProps, ResetProps, SwapProps, SwapTradeData, SwitchData, TransferProps, WithdrawProps, } from './index' import { DepositPanel, TransferPanel, WithdrawPanel } from '../modal' import { useDispatch } from 'react-redux' import { setShowAmm, setShowDeposit, setShowResetAccount, setShowSwap, setShowTransfer, setShowWithdraw, } from '../../stores' import { SlippagePanel } from './components' import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { boxLiner } from '../styled' const Style = styled.div` background: var(--color-global-bg); height: 100%; flex: 1; ` const BoxLinear = styled(Box)` && { ${({ theme }) => boxLiner({ theme })}; } ` let tradeData: any = {} // @ts-ignore let depositProps: DepositProps = { toIsAddressCheckLoading: false, referIsLoopringAddress: false, referIsAddressCheckLoading: false, type: TRADE_TYPE.NFT, isNewAccount: false, tradeData, coinMap, walletMap, depositBtnStatus: TradeBtnStatus.AVAILABLE, onDepositClick: (tradeData: SwapTradeData) => { console.log('Swap button click', tradeData) }, handlePanelEvent: async (props: SwitchData, switchType: 'Tomenu' | 'Tobutton') => { return new Promise(() => { setTimeout(() => { console.log('wait 100, with props', props, switchType) //res(); }, 500) }) }, } let withdrawProps: Partial> = { disabled: false, type: TRADE_TYPE.TOKEN, isContractAddress: false, isFeeNotEnough: { isFeeNotEnough: false, isOnLoading: false, }, isAddressCheckLoading: false, isCFAddress: false, tradeData, coinMap, walletMap, withdrawBtnStatus: TradeBtnStatus.AVAILABLE, onWithdrawClick: (tradeData: SwapTradeData) => { console.log('Swap button click', tradeData) }, handlePanelEvent: async (props: SwitchData, switchType: 'Tomenu' | 'Tobutton') => { return new Promise((res: any) => { setTimeout(() => { console.log('wait 100, with props', props, switchType) res() }, 500) }) }, withdrawType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, withdrawTypes: { // [sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL]: "Fast", [sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL]: 'Standard', }, feeInfo: { belong: 'ETH', fee: 0.001, __raw__: '' as any }, // @ts-ignore chargeFeeTokenList: [ { belong: 'ETH', fee: 0.001, __raw__: '' as any }, { belong: 'LRC', fee: '1', __raw__: '' as any }, ], handleOnAddressChange: (value: any) => { console.log('handleOnAddressChange', value) }, handleFeeChange(value: { belong: string; fee: number | string; __raw__?: any }): void { console.log('handleWithdrawFee', value) }, handleWithdrawTypeChange: (value: any) => { console.log(value) }, } let transferProps: Partial> = { isFeeNotEnough: { isFeeNotEnough: false, isOnLoading: false, }, tradeData, coinMap, walletMap, transferBtnStatus: TradeBtnStatus.AVAILABLE, onTransferClick: async (tradeData: any) => { console.log('Swap button click', tradeData) }, handlePanelEvent: async (props: SwitchData, switchType: 'Tomenu' | 'Tobutton') => { return new Promise((res: any) => { setTimeout(() => { console.log('wait 100, with props', props, switchType) res() }, 500) }) }, handleFeeChange(value: { belong: string; fee: number | string; __raw__?: any }): void { console.log('handleWithdrawFee', value) }, feeInfo: { belong: 'ETH', fee: 0.001, __raw__: '' as any }, // @ts-ignore chargeFeeTokenList: [ { belong: 'ETH', fee: 0.001, __raw__: '' as any }, { belong: 'LRC', fee: '1', __raw__: '' as any }, ], handleOnAddressChange: (value: any) => { console.log('handleOnAddressChange', value) }, } let resetProps: ResetProps = { isFeeNotEnough: { isFeeNotEnough: false, isOnLoading: false, }, chargeFeeTokenList: [ { belong: 'ETH', fee: 0.001, __raw__: '' as any }, { belong: 'LRC', fee: '1', __raw__: '' as any }, ], feeInfo: { belong: 'ETH', fee: 0.001, __raw__: '' as any }, handleFeeChange(value: { belong: string; fee: number | string; __raw__?: any }): void { console.log('handleWithdrawFee', value) }, onResetClick({}): void {}, } // resetBtnStatus: TradeBtnStatus.AVAILABLE, // handlePanelEvent: async ( // props: SwitchData, // switchType: "Tomenu" | "Tobutton" // ) => { // return new Promise((res: any) => { // setTimeout(() => { // console.log("wait 100, with props", props, switchType); // res(); // }, 500); // }); // }, // fee: { count: 234, price: 123 }, // let swapProps: SwapProps, string, any> = { // refreshRef: React.createRef(), // tradeData: { // sell: { belong: undefined }, // buy: { belong: undefined }, // slippage: "", // } as any, // tradeCalcData, // onSwapClick: (tradeData) => { // console.log("Swap button click", tradeData); // }, // handleSwapPanelEvent: async (data: any, switchType: any) => { // console.log(data, switchType); // }, // }; // @ts-ignore let _ammProps: AmmProps>, AmmExitData>, any, AmmInData> = { refreshRef: React.createRef(), // @ts-ignore ammDepositData: { coinA: { belong: 'ETH', balance: 0.3, tradeValue: 0 }, coinB: { belong: 'LRC', balance: 1000, tradeValue: 0 }, slippage: '', }, // @ts-ignore ammWithdrawData: { coinLP: { belong: 'LP-ETH-LRC', balance: 0.3, tradeValue: 0 }, slippage: '', }, // tradeCalcData, ammCalcDataDeposit: ammCalcData, ammCalcDataWithDraw: ammCalcData, handleAmmAddChangeEvent: (data, type) => { console.log('handleAmmAddChangeEvent', data, type) }, handleAmmRemoveChangeEvent: (data) => { return console.log('handleAmmRemoveChangeEvent', data) }, onAmmRemoveClick: (data) => { console.log('onAmmRemoveClick', data) }, onAmmAddClick: (data) => { console.log('onAmmAddClick', data) }, } const WrapTransferPanel = (rest: any) => { const dispatch = useDispatch() dispatch(setShowTransfer({ isShow: false })) return ( <> ) } const WrapWithdrawPanel = (rest: any) => { const dispatch = useDispatch() dispatch(setShowDeposit({ isShow: false })) return ( <> ) } const WrapDepositPanel = (rest: any) => { // const [open, setOpen] = useState(false) const dispatch = useDispatch() dispatch(setShowDeposit({ isShow: false })) const { t } = useTranslation('common') return ( <> {/**/} ) } const WrapResetPanel = (rest: any) => { const dispatch = useDispatch() dispatch(setShowResetAccount({ isShow: false })) return ( <> ) } const WrapSwapPanel = (rest: any) => { let tradeData: any = { sell: { belong: undefined }, buy: { belong: undefined }, } let swapProps: SwapProps, string, any> = { refreshRef: React.createRef(), campaignTagConfig: { SWAP: [], ORDERBOOK: [], MARKET: [], AMM: [], FIAT: [], } as any, tradeData: tradeData, isStob: true, tradeCalcData, onSwapClick: () => { console.log('Swap button click', tradeData) }, handleSwapPanelEvent: async (data: any, switchType: any) => { console.log(data, switchType) }, } setTimeout(() => { // console.log('swapProps update') // swapProps.swapTradeData = {sell: {belong: "ETH"}, buy: {belong: "LRC"}} as any; }, 500) setTimeout(() => { swapProps.tradeCalcData = { ...tradeCalcData, StoB: 1.123 } }, 800) return ( <> ) } const WrapDualPanel = (rest: any) => { const dualWrapProps: DualWrapProps = { refreshRef: React.createRef(), disabled: false, btnInfo: undefined, tokenMap: TOKEN_INFO.tokenMap as any, isLoading: false, onRefreshData: () => undefined, onSubmitClick: () => undefined, onChangeEvent: (item) => { console.log(item) }, dualCalcData: DUALCALCDATA, tokenSell: TOKEN_INFO.tokenMap['LRC'], btnStatus: TradeBtnStatus.AVAILABLE, accStatus: AccountStatus.ACTIVATED, } return ( <> ) } const WrapAmmPanel = (rest: any) => { // let tradeData: any = { // coinA: {belong: 'ETH', balance: 0.3, tradeValue: 0}, // coinB: {belong: 'LRC', balance: 1000, tradeValue: 0} // }; let ammProps: AmmProps>, any, AmmInData, any> = { ..._ammProps, // refreshRef: React.createRef(), // ammDepositData: tradeData, // AmmExitData: {coinLP:{belong: 'ETH', balance: 0.3, tradeValue: 0}}, // // tradeCalcData, // ammCalcDataDeposit: ammCalcData, // ammCalcDataWithDraw: ammCalcData, // handleAmmAddChangeEvent: (data, type) => { // console.log('handleAmmAddChangeEvent', data, type); // }, // handleAmmRemoveChangeEvent: (data, type) => { // console.log('handleAmmRemoveChangeEvent', data, type); // }, // onAmmRemoveClick: (data) => { // console.log('onAmmRemoveClick', data); // }, // onAmmAddClick: (data) => { // console.log('onAmmAddClick', data); // } } return ( <> ) } const ModalPanelWrap = () => { return ( <> // } // transferProps={transferProps as TransferProps} // withdrawProps={withdrawProps as WithdrawProps} // nftTransferProps={transferProps as TransferProps} // nftWithdrawProps={withdrawProps as WithdrawProps} // resetProps={resetProps} // assetsData={{} as any} // exportAccountProps={{} as any} // setExportAccountToastOpen={{} as any} // activeAccountProps={{} as any} // // nftMintAdvanceProps={{} as any} // nftDeployProps={{} as any} // account={{} as any} // baseURL={""} // collectionAdvanceProps={{} as any} // dualTradeProps={{} as any} // /> ) } const Template: Story = () => { const dispatch = useDispatch() const { t, ...rest } = useTranslation() const slippageArray: Array = SlippageTolerance.concat(`slippage:0.8`) as Array< number | string > return ( ) } export default { title: 'components/Swap&TradePanel', component: WrapSwapPanel, argTypes: {}, } as Meta export const SwapPanelStory = Template.bind({}) // SwitchPanel.args = {} ================================================ FILE: packages/component-lib/src/components/tradePanel/Reset/ActiveAccountPanel.tsx ================================================ import { ResetProps } from '../Interface' import { withTranslation, WithTranslation } from 'react-i18next' import { FeeInfo, IBData } from '@loopring-web/common-resources' import { SwitchPanel, SwitchPanelProps } from '../../basic-lib' import React from 'react' import { ResetWrap } from '../components' export const ActiveAccountPanel = withTranslation('common', { withRef: true })( ({ ...rest }: ResetProps & WithTranslation) => { const props: SwitchPanelProps<'tradeMenuList' | 'trade'> = { index: 0, // show default show panelList: [ { key: 'trade', element: ( key={'Reset'} {...{ ...rest, }} /> ), toolBarItem: undefined, }, ], } return }, ) as , I>(props: ResetProps & React.RefAttributes) => JSX.Element // export const TransferModal = withTranslation() ================================================ FILE: packages/component-lib/src/components/tradePanel/Reset/ConfirmationPanel.tsx ================================================ import { withTranslation, WithTranslation } from 'react-i18next' import { Box, Button, Typography } from '@mui/material' export const ResetAccountConfirmationPanel = withTranslation('common', { withRef: true })( ({ type, t, onConfirmation, }: { type: 'lockedReset' | 'unlockedWithDual' | 'unlockedWithoutDual' onConfirmation: () => void } & WithTranslation) => { return ( {t("labelResetLoopringL2")} {type === 'lockedReset' ? ( <> {t("labelResetlockedReset1")} {t("labelResetlockedReset2")} ) : type === 'unlockedWithDual' ? ( <> {t("labelResetunlockedWithDual1")} {t("labelResetunlockedWithDual2")} ) : ( <> {t("labelResetunlockedWithoutDual")} )} ) }, ) // export const TransferModal = withTranslation() ================================================ FILE: packages/component-lib/src/components/tradePanel/Reset/ResetPanel.tsx ================================================ import { ResetProps } from '../Interface' import { withTranslation, WithTranslation } from 'react-i18next' import { FeeInfo, IBData } from '@loopring-web/common-resources' import { SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { ResetWrap } from '../components' import React from 'react' export const ResetPanel = withTranslation('common', { withRef: true })( ({ onResetClick, resetBtnStatus, chargeFeeTokenList, assetsData, ...rest }: ResetProps & WithTranslation) => { const props: SwitchPanelProps<'tradeMenuList' | 'trade'> = { index: 0, // show default show panelList: [ { key: 'trade', element: ( key={'transfer'} {...{ ...rest, resetBtnStatus, chargeFeeTokenList, onResetClick, assetsData, }} /> ), toolBarItem: undefined, }, ], } return }, ) as , I>(props: ResetProps & React.RefAttributes) => JSX.Element // export const TransferModal = withTranslation() ================================================ FILE: packages/component-lib/src/components/tradePanel/Reset/index.ts ================================================ export * from './ResetPanel' export * from './ActiveAccountPanel' export * from './ConfirmationPanel' ================================================ FILE: packages/component-lib/src/components/tradePanel/Swap/index.tsx ================================================ import { SwapProps, SwapTradeData, SwapType } from '../Interface' import { withTranslation, WithTranslation } from 'react-i18next' import React, { useCallback, useState } from 'react' import { Box, Checkbox, Grid, Popover, Switch, Tooltip, Typography } from '@mui/material' import { SwitchPanel, SwitchPanelProps } from '../../basic-lib' import { BtradeTradeCalcData, CheckBoxIcon, CheckedIcon, defaultBlockTradeSlipage, IBData, Info2Icon, myLog, OrderListIcon, RecordTabIndex, RouterPath, SCENARIO, SlippageBtradeTolerance, SlippageTolerance, SwapSettingIcon, SwapTradeCalcData, TokenType, TradeCalcData, VaultTradeCalcData, } from '@loopring-web/common-resources' import { SlippagePanel, SwapData, SwapMenuList, SwapTradeWrap } from '../components' import { CountDownIcon } from '../components/tool/Refresh' import { IconButtonStyled } from '../components/Styled' import { useHistory } from 'react-router-dom' import { TagIconList } from '../../block' import { useSettings } from '../../../stores' import styled from '@emotion/styled' import { useTheme } from '@emotion/react' import { ToastType } from '../../toast' const PopoverStyled = styled(Popover)` .MuiPaper-elevation2 { box-shadow: none; padding: 0; width: 250px; } .MuiBackdrop-root { background: transparent; } ` export const SwapPanel = withTranslation('common', { withRef: true })( < T extends IBData, I, TCD extends BtradeTradeCalcData | SwapTradeCalcData | VaultTradeCalcData, >({ disabled, tradeCalcData, swapBtnStatus, tokenSellProps, tokenBuyProps, handleSwapPanelEvent, handleError, onSwapClick, toPro, market, onRefreshData, campaignTagConfig, refreshRef, tradeData, setToastOpen, titleI8nKey = 'swapTitle', scenario = SCENARIO.SWAP, hideSecondConfirmation, bTradeTutorial, marginLevelChange, vaultLeverage, refreshTime, ...rest }: SwapProps & WithTranslation & {}) => { let history = useHistory() const [index, setIndex] = React.useState(0) const [type, setType] = React.useState<'buy' | 'sell' | 'exchange'>('buy') const [to, setTo] = React.useState<'button' | 'menu'>('button') const onChangeEvent = (_index: 0 | 1, { type, to, tradeData }: SwapData>) => { myLog('hookSwap onChangeEvent', tradeData) if (_index !== index) { setIndex(_index) } switch (type) { case 'exchange': setType('buy') break default: setType(type) } setTo(to) handleSwapPanelEvent && handleSwapPanelEvent( { to, tradeData, type, }, (type === 'exchange' ? 'exchange' : `${type}To${to}`) as SwapType, ) myLog('hookSwap panelEventNext', tradeData.slippage, tradeData) } const [anchorEl, setAnchorEl] = useState(null) const [settingPopoverOpen, setSettingPopoverOpen] = useState(false) const settingPopoverId = settingPopoverOpen ? 'setting-popover' : undefined const { slippage, swapSecondConfirmation, setSwapSecondConfirmation } = useSettings() const onSwitchChangeCallback = useCallback(() => { setToastOpen && setToastOpen({ open: true, content: rest.t('labelSwapSettingToggleSuccess', { onOrOff: !swapSecondConfirmation ? 'on' : 'off', }), type: ToastType.success, }) setSwapSecondConfirmation(!swapSecondConfirmation) }, [swapSecondConfirmation, setSwapSecondConfirmation, setToastOpen]) const onSlippageChangeCallBack = React.useCallback( (slippage: number | string, customSlippage: number | string | undefined) => { myLog('hookSwap slippage', slippage, tradeData) onChangeEvent(0, { tradeData: { ...tradeData, slippage: slippage, __cache__: { ...tradeData?.__cache__, customSlippage: customSlippage, }, }, type: 'sell', to: 'button', }) }, [tradeData, onChangeEvent], ) const theme = useTheme() const props: SwitchPanelProps<'tradeMenuList' | 'trade'> = { index: index, // show default show panelList: [ { key: 'trade', element: React.useMemo(() => { myLog('hookSwap view tradeData', tradeData) return ( // @ts-ignore key={'trade'} {...{ ...rest, tradeData, tradeCalcData, onSwapClick, onChangeEvent, disabled, swapBtnStatus, tokenSellProps, tokenBuyProps, handleError, marginLevelChange }} /> ) }, [ rest, tradeCalcData, onSwapClick, tradeData, onChangeEvent, disabled, swapBtnStatus, tokenSellProps, tokenBuyProps, handleError, ]), toolBarItem: React.useMemo( () => ( <> {rest.t(titleI8nKey)} {campaignTagConfig && ( )} { setSettingPopoverOpen(true) setAnchorEl(e.currentTarget) }} sx={{ backgroundColor: 'var(--field-opacity)' }} className={'switch outlined'} aria-label='to Transaction' aria-describedby={settingPopoverId} size={'large'} > { setSettingPopoverOpen(false) setAnchorEl(null) }} anchorOrigin={{ vertical: 'bottom', horizontal: 'left', }} sx={{ background: 'transparent' }} > {rest.t('labelSwapSettingTitle')} {rest.t('swapTolerance')} )?.isBtrade || (tradeCalcData as VaultTradeCalcData)?.isVault ? (SlippageBtradeTolerance.concat(`slippage:${slippage}`) as Array< number | string >) : (SlippageTolerance.concat(`slippage:${slippage}`) as Array< number | string >) } slippage={ tradeData?.slippage ? tradeData?.slippage : tradeCalcData?.slippage ? tradeCalcData?.slippage : defaultBlockTradeSlipage } handleChange={(slippage, customSlippage) => { onSlippageChangeCallBack(slippage, customSlippage) }} /> {bTradeTutorial?.show && ( {rest.t("Block Trade Tutorial")} { bTradeTutorial?.onToggle() }} /> )} {(tradeCalcData as any).isVault && !vaultLeverage?.hideLeverage && ( {' ' + rest.t('labelVaultLeverage')} { vaultLeverage?.onClickLeverage && vaultLeverage?.onClickLeverage() }} > {vaultLeverage?.leverage}x )} {!hideSecondConfirmation && ( {' ' + rest.t('labelSwapSettingSecondConfirm')} { onSwitchChangeCallback() }} checked={swapSecondConfirmation !== false} /> )} { // @ts-ignore tradeCalcData.isBtrade ? history.push( `${RouterPath.l2records}/${RecordTabIndex.BtradeSwapRecords}?market=${market}`, ) : // @ts-ignore tradeCalcData.isVault ? history.push( `${RouterPath.l2records}/${RecordTabIndex.VaultRecords}?market=${market}`, ) : history.push( `${RouterPath.l2records}/${RecordTabIndex.Trades}?market=${market}`, ) }} sx={{ backgroundColor: 'var(--field-opacity)' }} className={'switch outlined'} aria-label='to Transaction' size={'large'} > ), [ onRefreshData, settingPopoverOpen, swapSecondConfirmation, onSwitchChangeCallback, onSlippageChangeCallBack, tradeData, theme, ], ), }, { key: 'tradeMenuList', element: React.useMemo( () => ( // @ts-ignore key={'tradeMenuList'} {...{ ...rest, tokenType: (tradeCalcData as VaultTradeCalcData)?.isVault ? TokenType.vault : undefined, onChangeEvent, tradeCalcData, swapData: { tradeData, type, to, }, }} /> ), [onChangeEvent, tradeCalcData, type, to, rest], ), toolBarItem: undefined, }, ], } return ( ) }, ) as , I, TCD extends TradeCalcData>( props: SwapProps & React.RefAttributes, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/tradePanel/components/AddressType.tsx ================================================ import { Box, IconButton, MenuItem, Typography } from '@mui/material' import { useTranslation } from 'react-i18next' import styled from '@emotion/styled' import React, { ForwardedRef } from 'react' import { AddressItemType, CloseIcon, EXCHANGE_TYPE, Info2Icon, RoundCheckIcon, RoundCircleIcon, WALLET_TYPE, hexToRGB, } from '@loopring-web/common-resources' import { MenuItemProps, TextField } from '../../basic-lib' import { useOpenModals } from '../../../stores' import { useAddressTypeLists } from './hook/useAddressType' import { useTheme } from '@emotion/react' const MenuItemStyle = styled(MenuItem) & { maxWidth?: string | number }>` height: auto; max-width: ${({ maxWidth }) => (maxWidth ? maxWidth : 'auto')}; border: 1px solid; border-color: ${({ checked }) => (checked ? 'var(--color-primary)' : 'var(--color-border)')}; width: 100%; display: flex; justify-content: space-between; border-radius: ${({ theme }) => theme.unit}px; padding: ${({ theme }) => 2 * theme.unit}px; min-height: ${({ theme }) => theme.unit * 8}px; align-items: center; cursor: pointer; flex-direction: row; margin-top: ${({ theme }) => 2 * theme.unit}px; ` as (props: MenuItemProps & { maxWidth?: string | number }) => JSX.Element const WarningBoxStyled = styled(Box)` width: 100%; display: flex; justify-content: space-between; border-radius: ${({ theme }) => theme.unit}px; padding: ${({ theme }) => 2 * theme.unit}px; min-height: ${({ theme }) => theme.unit * 8}px; align-items: center; cursor: pointer; flex-direction: row; background: ${({ theme }) => hexToRGB(theme.colorBase.warning, 0.2)}; ` export const WalletItemOptions = React.memo( React.forwardRef( ( { description, label, myValue, selectedValue, maxWidth, disabled = false, handleSelected, }: { myValue: T handleSelected: (value: WALLET_TYPE | EXCHANGE_TYPE) => void selectedValue: T | undefined } & AddressItemType, ref: ForwardedRef, ) => { return ( { handleSelected(myValue) }} > {label} {description} {selectedValue === myValue ? ( ) : ( )} ) }, ), ) export const TransferAddressType = ({ selectedValue, handleSelected, disabled, detectedWalletType, }: { selectedValue: WALLET_TYPE | EXCHANGE_TYPE | undefined handleSelected: (value: WALLET_TYPE | EXCHANGE_TYPE) => void disabled: boolean detectedWalletType: WALLET_TYPE }) => { const { t } = useTranslation('common') const { walletListFn } = useAddressTypeLists() const theme = useTheme() const desMenuItem = React.useMemo(() => { return ( {t('labelWalletTypeDes')} ) }, [t]) const [open, setOpen] = React.useState(false) const onClose = () => { setOpen(false) } const { setShowOtherExchange, modals: { isShowOtherExchange }, } = useOpenModals() React.useEffect(() => { if (isShowOtherExchange.agree) { handleSelected(EXCHANGE_TYPE.Others) onClose() } }, [isShowOtherExchange.agree]) const onOpen = () => { setOpen(true) setShowOtherExchange({ isShow: false, agree: false }) } // const walletType = WALLET_TYPE.EOA return ( walletListFn(detectedWalletType).find((item) => item.value === selectedValue)?.label ?? '', }} label={ {t('labelL2toL1AddressType')} } > {t('labelL2toL1AddressType')} {desMenuItem} {walletListFn(detectedWalletType).map( ({ value, label, description, disabled, maxWidth }) => ( { if (value === EXCHANGE_TYPE.Others) { setShowOtherExchange({ isShow: true }) } else { handleSelected(value as T) onClose() } // handleSelected(value); // onClose(); }} maxWidth={maxWidth} selectedValue={selectedValue} label={label} description={description} disabled={disabled} /> ), )} ) } export const FullAddressType = ({ selectedValue, handleSelected, disabled, detectedWalletType, }: { selectedValue: WALLET_TYPE | EXCHANGE_TYPE | undefined handleSelected: (value: WALLET_TYPE | EXCHANGE_TYPE) => void disabled: boolean detectedWalletType: WALLET_TYPE }) => { const { t } = useTranslation('common') const { walletListFn } = useAddressTypeLists() const { setShowOtherExchange, modals: { isShowOtherExchange }, } = useOpenModals() const [open, setOpen] = React.useState(false) const onClose = () => { setOpen(false) } React.useEffect(() => { if (isShowOtherExchange.agree) { handleSelected(EXCHANGE_TYPE.Others) onClose() } }, [isShowOtherExchange.agree]) const onOpen = () => { setOpen(true) setShowOtherExchange({ isShow: false, agree: false }) } return ( walletListFn(detectedWalletType).find((item) => item.value === selectedValue)?.label ?? '', }} label={ {t('labelL2toL1AddressType')} } > {t('labelL2toL1AddressType')} {t('labelExchangeTypeDes')} {walletListFn(detectedWalletType).map( ({ value, label, description, disabled, maxWidth }) => ( { if (value === EXCHANGE_TYPE.Others) { setShowOtherExchange({ isShow: true }) } else { handleSelected(value) onClose() } }} selectedValue={selectedValue} label={label} maxWidth={maxWidth} description={description} disabled={disabled} /> ), )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/AmmWrap/AmmDeposit.tsx ================================================ import { AmmInData, AmmJoinData, defaultSlipage, EmptyValueTag, IBData, L1L2_NAME_DEFINED, LinkedIcon, MapChainId, ReverseIcon, SlippageTolerance, TradeBtnStatus, } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import { AmmDepositWrapProps } from './Interface' import { InputCoin, LinkActionStyle, PopoverPure } from '../../../basic-lib' import React from 'react' import { usePopupState } from 'material-ui-popup-state/hooks' import { Box, Grid, Typography } from '@mui/material' import { bindHover, bindPopover } from 'material-ui-popup-state/es' import { SlippagePanel } from '../tool' import { SvgStyled } from './styled' import { useSettings } from '../../../../stores' import { ButtonStyle, IconButtonStyled } from '../Styled' export const AmmDepositWrap = < T extends AmmJoinData ? C : IBData>, I, ACD extends AmmInData, C = IBData, >({ t, disabled, isStob, switchStobEvent, ammDepositBtnStatus, ammCalcData, ammDepositBtnI18nKey, onAmmAddClick, tokenAProps, tokenBProps, onAddChangeEvent, ammData, propsAExtends = {}, propsBExtends = {}, // coinAPrecision, // coinBPrecision, ...rest }: AmmDepositWrapProps & WithTranslation) => { const coinARef = React.useRef() const coinBRef = React.useRef() const { slippage, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const slippageArray: Array = SlippageTolerance.concat( `slippage:${slippage}`, ) as Array const [_isStoB, setIsStoB] = React.useState(isStob ?? true) const stob = React.useMemo(() => { if (ammCalcData && ammCalcData?.lpCoinA && ammCalcData?.lpCoinB && ammCalcData.AtoB) { let price: string if (_isStoB) { price = `1 ${ammCalcData?.lpCoinA?.belong} \u2248 ${ ammCalcData.AtoB ? ammCalcData.AtoB : EmptyValueTag } ${ammCalcData?.lpCoinB?.belong}` } else { price = `1 ${ammCalcData?.lpCoinB?.belong} \u2248 ${ ammCalcData.BtoA ? ammCalcData.BtoA : EmptyValueTag } ${ammCalcData?.lpCoinA?.belong}` } return ( <> {price} setIsStoB(!_isStoB)} > ) } else { return EmptyValueTag } }, [_isStoB, ammCalcData]) const getDisabled = () => { return disabled || ammCalcData === undefined || ammCalcData.coinInfoMap === undefined } const handleError = () => { if ( ammDepositBtnStatus === TradeBtnStatus.DISABLED && ammDepositBtnI18nKey && (/labelAMMNoEnough/.test(ammDepositBtnI18nKey) || /labelAMMMax/.test(ammDepositBtnI18nKey)) ) { return { error: true } } return { error: false } } const handleCountChange = React.useCallback( (ibData: IBData, _name: string, _ref: any) => { const focus: 'coinA' | 'coinB' = _ref?.current === coinARef.current ? 'coinA' : 'coinB' if (ammData[focus].tradeValue !== ibData.tradeValue) { onAddChangeEvent({ tradeData: { ...ammData, [focus]: ibData }, type: focus, }) } }, [ammData, onAddChangeEvent], ) const propsA: any = { label: t('labelTokenAmount'), subLabel: t('labelAvailable'), placeholderText: '0.00', maxAllow: true, ...tokenAProps, handleError, handleCountChange, ...rest, } const propsB: any = { label: t('labelTokenAmount'), subLabel: t('labelAvailable'), placeholderText: '0.00', maxAllow: true, ...tokenBProps, handleError, handleCountChange, ...rest, } const popupState = usePopupState({ variant: 'popover', popupId: 'slippagePop', }) const _onSlippageChange = React.useCallback( (slippage: number | string, customSlippage: number | string | undefined) => { popupState.close() onAddChangeEvent({ tradeData: { ...ammData, slippage: slippage, __cache__: { ...ammData.__cache__, customSlippage: customSlippage, }, }, type: 'coinA', }) }, [ammData, onAddChangeEvent], ) const label = React.useMemo(() => { if (ammDepositBtnI18nKey) { const key = ammDepositBtnI18nKey.split('|') return t( key[0], key && key[1] ? { arg: key[1].toString(), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(`labelAddLiquidityBtn`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [ammDepositBtnI18nKey]) return ( ref={coinARef} disabled={getDisabled() || ammDepositBtnStatus === TradeBtnStatus.LOADING} {...{ ...propsA, name: 'coinA', isHideError: true, order: 'right', inputData: ammData ? ammData.coinA : ({} as any), coinMap: ammCalcData ? ammCalcData.coinInfoMap : ({} as any), ...propsAExtends, // coinPrecision: coinAPrecision, }} /> {/* */} ref={coinBRef} disabled={getDisabled() || ammDepositBtnStatus === TradeBtnStatus.LOADING} {...{ ...propsB, name: 'coinB', isHideError: true, order: 'right', inputData: ammData ? ammData.coinB : ({} as any), coinMap: ammCalcData ? ammCalcData.coinInfoMap : ({} as any), ...propsBExtends, // coinPrecision: coinBPrecision, }} /> {stob} {t('swapTolerance')} {ammCalcData ? ( <> {ammData.slippage ? ammData.slippage : ammCalcData.slippage ? ammCalcData.slippage : 0.5} % ) : ( EmptyValueTag )} {t('labelNetworkFee')} {ammCalcData?.fee && ammCalcData?.fee != '0' ? ammCalcData.fee + ' ' + ammCalcData.myCoinB.belong : EmptyValueTag} { onAmmAddClick(ammData) }} loading={ !getDisabled() && ammDepositBtnStatus === TradeBtnStatus.LOADING ? 'true' : 'false' } disabled={ getDisabled() || ammDepositBtnStatus === TradeBtnStatus.DISABLED || ammDepositBtnStatus === TradeBtnStatus.LOADING } fullWidth={true} > {label} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/AmmWrap/AmmWithdraw.tsx ================================================ import { AmmExitData, AmmInData, CoinInfo, defaultSlipage, EmptyValueTag, ExchangeIcon, getValuePrecisionThousand, IBData, L1L2_NAME_DEFINED, MapChainId, myLog, ReverseIcon, SlippageTolerance, TokenType, TradeBtnStatus, } from '@loopring-web/common-resources' import { AmmWithdrawWrapProps } from './Interface' import { WithTranslation } from 'react-i18next' import React from 'react' import { usePopupState } from 'material-ui-popup-state/hooks' import { Box, Grid, Link, Typography } from '@mui/material' import { BtnPercentage, ButtonStyle, CoinIcons, IconButtonStyled, InputCoin, LinkActionStyle, PopoverPure, } from '../../../index' import { bindHover, bindPopover } from 'material-ui-popup-state/es' import { SlippagePanel } from '../tool' import { useSettings } from '../../../../stores' import { SvgStyled } from './styled' import * as sdk from '@loopring-web/loopring-sdk' import _ from 'lodash' export const AmmWithdrawWrap = < T extends AmmExitData ? C : IBData>, I, ACD extends AmmInData, C = IBData, >({ t, disabled, isStob, switchStobEvent, ammWithdrawBtnStatus, ammCalcData, onAmmRemoveClick, tokenLPProps, anchors, ammWithdrawBtnI18nKey, onRemoveChangeEvent, handleError, propsLPExtends = {}, ammData, ...rest }: AmmWithdrawWrapProps & WithTranslation) => { const { coinJson, slippage, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const coinLPRef = React.useRef() const tokenAIcon = coinJson[ammCalcData?.lpCoinA?.belong as string] const tokenBIcon = coinJson[ammCalcData?.lpCoinB?.belong as string] const slippageArray: Array = SlippageTolerance.concat( `slippage:${slippage}`, ) as Array const [isPercentage, setIsPercentage] = React.useState(true) const percentage = React.useMemo(() => { return ammData?.coinLP?.tradeValue && ammData.coinLP.balance ? getValuePrecisionThousand( (ammData.coinLP.tradeValue / ammData.coinLP.balance) * 100, 2, 2, 2, false, ) : 0 }, [ammData?.coinLP?.tradeValue, ammData.coinLP.balance]) const label = React.useMemo(() => { if (ammWithdrawBtnI18nKey) { const key = ammWithdrawBtnI18nKey.split('|') return t( key[0], key && key[1] ? { arg: key[1].toString(), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(`labelRemoveLiquidityBtn`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [ammWithdrawBtnI18nKey]) const [_isStoB, setIsStoB] = React.useState(isStob ?? true) const stob = React.useMemo(() => { if (ammCalcData && ammCalcData?.lpCoinA && ammCalcData?.lpCoinB && ammCalcData.AtoB) { let price: string if (_isStoB) { price = `1 ${ammCalcData?.lpCoinA?.belong} \u2248 ${ ammCalcData.AtoB ? ammCalcData.AtoB : EmptyValueTag } ${ammCalcData?.lpCoinB?.belong}` } else { price = `1 ${ammCalcData?.lpCoinB?.belong} \u2248 ${ ammCalcData.BtoA ? ammCalcData.BtoA : EmptyValueTag } ${ammCalcData?.lpCoinA?.belong}` } return ( <> {price} setIsStoB(!_isStoB)} > ) } else { return EmptyValueTag } }, [_isStoB, ammCalcData]) const getDisabled = () => { return disabled || ammCalcData === undefined || ammCalcData.coinInfoMap === undefined } const handleCountChange = React.useCallback( (ibData: IBData, _ref: any) => { // myLog(_ref?.current, coinLPRef.current); if (_ref) { if (ammData?.coinLP.tradeValue !== ibData.tradeValue && ammData?.coinLP.balance) { onRemoveChangeEvent({ tradeData: { ...ammData, coinLP: ibData }, type: 'lp', }) } } else { onRemoveChangeEvent({ tradeData: { ...ammData, coinLP: ibData }, type: 'lp', }) } }, [ammData, onRemoveChangeEvent, coinLPRef], ) const onPercentage = (value: any) => { if (ammData?.coinLP && ammData?.coinLP?.belong) { myLog('selected', Number(value)) // setSelectedPercentage(value); const cloneLP = _.cloneDeep(ammData.coinLP) cloneLP.tradeValue = sdk .toBig(cloneLP?.balance ?? 0) .times(value) .div(100) .toNumber() handleCountChange(cloneLP, null) } } const _onSlippageChange = React.useCallback( (slippage: number | string, customSlippage: number | string | undefined) => { popupState.close() onRemoveChangeEvent({ tradeData: { ...ammData, slippage: slippage, __cache__: { ...ammData.__cache__, customSlippage: customSlippage, }, }, type: 'lp', }) }, [ammData, onRemoveChangeEvent], ) const propsLP: any = { label: t('labelTokenAmount'), subLabel: t('labelAvailable'), placeholderText: '0.00', maxAllow: true, ...tokenLPProps, handleError, ...rest, } const popupState = usePopupState({ variant: 'popover', popupId: 'slippagePop', }) // const { label, stob } = useAmmViewData({ // i18nKey: ammWithdrawBtnI18nKey, // t, // _isStoB, // ammCalcData, // _onSwitchStob, // isAdd: false, // }); const showPercentage = percentage < 0 || percentage > 100 ? EmptyValueTag + '%' : `${percentage}%` const lpTradeValue = ammData?.coinLP?.tradeValue let lpBalance: any = ammData?.coinLP?.balance lpBalance = parseFloat(lpBalance) const showLP = lpBalance && lpTradeValue && lpTradeValue > 0 && lpTradeValue <= lpBalance ? getValuePrecisionThousand(lpTradeValue, 2, 6) : '0' const miniA = ammData?.coinA?.tradeValue ? getValuePrecisionThousand(ammData?.coinA?.tradeValue) : EmptyValueTag const miniB = ammData?.coinB?.tradeValue ? getValuePrecisionThousand(ammData?.coinB?.tradeValue) : EmptyValueTag return ( ammWithdrawBtnStatus !== TradeBtnStatus.LOADING && setIsPercentage(!isPercentage) } > {t('labelAmmSwitch')} {showPercentage} {t('labelMinReceive')} {ammData?.coinA?.belong} {miniA} {ammData?.coinB?.belong} {miniB} {stob} {t('swapTolerance')} {ammCalcData ? ( <> {ammData.slippage ? ammData.slippage : ammCalcData?.slippage ? ammCalcData?.slippage : 0.5} % ) : ( EmptyValueTag )} {t('labelNetworkFee')} {ammCalcData?.fee && ammCalcData?.fee != '0' ? ammCalcData.fee + ' ' + ammCalcData.myCoinB.belong : EmptyValueTag} { onAmmRemoveClick(ammData) // setSelectedPercentage(0); }} loading={ !getDisabled() && ammWithdrawBtnStatus === TradeBtnStatus.LOADING ? 'true' : 'false' } disabled={ getDisabled() || ammWithdrawBtnStatus === TradeBtnStatus.DISABLED || ammWithdrawBtnStatus === TradeBtnStatus.LOADING } fullWidth={true} > {label} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/AmmWrap/Interface.ts ================================================ import { InputButtonProps } from '../../../basic-lib' import { CoinInfo, TradeBtnStatus } from '@loopring-web/common-resources' export type AmmChgData = { type: 'coinA' | 'coinB' tradeData: AT } export type AmmWithdrawChgData = { type: 'lp' tradeData: AT } export type AmmDepositBaseProps = { ammDepositBtnStatus?: keyof typeof TradeBtnStatus | undefined onAmmAddClick: (AmmSendData: T) => void | any ammDepositBtnI18nKey?: string propsAExtends?: Partial> propsBExtends?: Partial> } & Partial, 'handleError'>> export type AmmDepositExtendProps = { isStob?: boolean switchStobEvent?: (_isStoB: boolean) => void disabled?: boolean onAddChangeEvent: (data: AmmChgData) => void tokenAProps?: Partial>> tokenBProps?: Partial>> ammCalcData: ACD } export type AmmDepositWrapProps = AmmDepositBaseProps & AmmDepositExtendProps & { ammData: T coinAPrecision?: number coinBPrecision?: number } export type AmmWithdrawBaseProps = { ammWithdrawBtnStatus?: keyof typeof TradeBtnStatus | undefined onAmmRemoveClick: (AmmSendData: T) => void | any ammWithdrawBtnI18nKey?: string anchors?: number[] propsLPExtends?: Partial> } & Partial, 'handleError'>> export type AmmWithdrawExtendProps = { disabled?: boolean isStob?: boolean switchStobEvent?: (_isStoB: boolean) => void onRemoveChangeEvent: (data: AmmWithdrawChgData) => void tokenLPProps?: Partial>> ammCalcData: ACD } export type AmmWithdrawWrapProps = AmmWithdrawBaseProps & AmmWithdrawExtendProps & { ammData: T selectedPercentage: number // anchor } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/AmmWrap/index.ts ================================================ // this is a private component, only used in panel component // please do not export this index to global // export * from './Interface' export * from './Interface' export * from './AmmDeposit' export * from './AmmWithdraw' ================================================ FILE: packages/component-lib/src/components/tradePanel/components/AmmWrap/styled.ts ================================================ import styled from '@emotion/styled' export const SvgStyled = styled.div` & svg { width: 24px; height: 24px; } ` ================================================ FILE: packages/component-lib/src/components/tradePanel/components/BanxaConfirm.tsx ================================================ import { useTranslation } from 'react-i18next' import { Box, Grid, Typography } from '@mui/material' import { EmptyValueTag, FeeInfo, IBData, TOAST_TIME, TradeBtnStatus, } from '@loopring-web/common-resources' import { BanxaViewProps, Button, FeeSelect, Toast, ToastType, } from '../../index' import { useSettings } from '../../../stores' import React from 'react' export const BanxaConfirm = , I, C extends FeeInfo>({ tradeData, onTransferClick, disabled, isFeeNotEnough, handleFeeChange, chargeFeeTokenList, transferBtnStatus, transferI18nKey, feeInfo, memo, balanceNotEnough, offBanxaValue, }: BanxaViewProps & { balanceNotEnough: { isEnough: boolean reason?: string } }) => { const { isMobile } = useSettings() const { t } = useTranslation() const [open, setOpen] = React.useState(false) const [showFeeModal, setShowFeeModal] = React.useState(false) const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } const getDisabled = React.useMemo(() => { return disabled || transferBtnStatus === TradeBtnStatus.DISABLED }, [disabled, transferBtnStatus]) return ( {t('labelL2toBanxaTitle')} {t('labelL2toL2TokenAmount')} {tradeData?.tradeValue + ' '} {tradeData?.belong} {balanceNotEnough.isEnough && ( {t( // @ts-ignore balanceNotEnough?.reason == 1 ? 'labelBanxaFeeNoBalance' : 'labelRampNoBalance', { belong: tradeData?.belong }, )} )} {t('labelFiatAmount')} {offBanxaValue?.fiat_amount + ' ' + offBanxaValue?.fiat_code} {t('labelL2toL2AddressType')} {t('labelWalletTypeOptions', { type: 'Banxa' })} {t('labelMemo')} {memo ?? EmptyValueTag} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} { setOpen(false) }} severity={ToastType.error} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/BasicACoinInput.tsx ================================================ import { CoinInfo, CoinMap, IBData } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import React from 'react' import { BasicACoinInputProps } from './Interface' import { InputCoin, InputCoinProps } from '../../basic-lib' import * as sdk from '@loopring-web/loopring-sdk' export const BasicACoinInput = >, I>({ t, tradeData, onChangeEvent, coinMap, walletMap, disabled, handleError, inputCoinRef, inputCoinProps, inputCoinDefaultProps, className, tokenNotEnough = 'tokenNotEnough', ...rest }: BasicACoinInputProps & WithTranslation) => { const getDisabled = () => { if (disabled || tradeData === undefined || walletMap === undefined || coinMap === undefined) { return true } else { return false } } const handleOnClick = React.useCallback( (_event: React.MouseEvent, _ref: any) => { onChangeEvent(1, { tradeData: { ...tradeData, tradeValue: 0 }, to: 'menu', }) }, [tradeData, onChangeEvent], ) const handleCountChange: any = React.useCallback( (_tradeData: T, _name: string, _ref: any) => { //const focus: 'buy' | 'sell' = _ref?.current === buyRef.current ? 'buy' : 'sell'; if (tradeData.tradeValue !== _tradeData.tradeValue) { onChangeEvent(0, { tradeData: { ...tradeData, ..._tradeData }, to: 'button', }) } // onCoinValueChange(ibData); }, [onChangeEvent, tradeData], ) if (typeof handleError !== 'function') { handleError = ({ belong, balance, tradeValue }: T) => { const minimum = inputCoinProps?.minimum ?? inputCoinDefaultProps?.minimum const maxValue = inputCoinProps?.maxValue if ( (typeof tradeValue !== 'undefined' && balance && balance < tradeValue) || (tradeValue && !balance) ) { return { error: true, message: t(tokenNotEnough, { belong: belong }), } } else if ( typeof tradeValue !== 'undefined' && minimum !== undefined && tradeValue < Number(minimum) ) { return { error: true, message: t('errorMinError', { value: minimum, ns: ['error', 'common'], }), } } else if ( typeof tradeValue !== 'undefined' && maxValue !== undefined && sdk.toBig(tradeValue).gt(maxValue) ) { return { error: true, message: t('errorMaxError', { value: maxValue, ns: ['error', 'common'], }), } } return { error: false, message: '' } } } const _inputCoinProps: InputCoinProps, I> = { subLabel: t('tokenMax'), emptyText: t('tokenSelectToken'), placeholderText: '0.00', maxAllow: true, order: 'right', handleError: handleError as any, handleCountChange, handleOnClick, label: t('labelInput'), ...inputCoinDefaultProps, ...inputCoinProps, ...rest, } as InputCoinProps, I> return ( >), }} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/BasicACoinTrade.tsx ================================================ import { CoinInfo, CoinMap, IBData } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import React from 'react' import { BasicACoinTradeProps } from './Interface' import { InputButton, InputButtonProps, InputMaxButton } from '../../basic-lib' import * as sdk from '@loopring-web/loopring-sdk' import { getOptionalDecimal } from '@loopring-web/core' export const BasicACoinTrade = >, I>({ t, tradeData, onChangeEvent, coinMap, walletMap, disabled, inputButtonDefaultProps, handleError, inputBtnRef, inputButtonProps, className, isMaxBtn = false, tokenNotEnough = 'tokenNotEnough', ...rest }: BasicACoinTradeProps & WithTranslation) => { const getDisabled = () => { if (disabled || tradeData === undefined || walletMap === undefined || coinMap === undefined) { return true } else { return false } } const handleOnClick = React.useCallback( (_event: React.MouseEvent, _ref: any) => { onChangeEvent(1, { tradeData: { ...tradeData, tradeValue: 0 }, to: 'menu', }) }, [tradeData, onChangeEvent], ) const handleCountChange: any = React.useCallback( (_tradeData: T, _name: string, _ref: any) => { //const focus: 'buy' | 'sell' = _ref?.current === buyRef.current ? 'buy' : 'sell'; if (tradeData.tradeValue !== _tradeData.tradeValue) { onChangeEvent(0, { tradeData: { ...tradeData, ..._tradeData }, to: 'button', }) } // onCoinValueChange(ibData); }, [onChangeEvent, tradeData], ) if (typeof handleError !== 'function') { handleError = ({ belong, balance, tradeValue }: T) => { const minimum = inputButtonProps?.minimum ?? inputButtonDefaultProps?.minimum const maxValue = inputButtonProps?.maxValue if ( (typeof tradeValue !== 'undefined' && balance && balance < tradeValue) || (getOptionalDecimal(tradeValue)?.gt('0') && !balance) ) { return { error: true, message: t(tokenNotEnough, { belong: belong }), } } else if ( typeof tradeValue !== 'undefined' && minimum !== undefined && tradeValue < Number(minimum) ) { return { error: true, message: t('errorMinError', { value: minimum, ns: ['error', 'common'], }), } } else if ( typeof tradeValue !== 'undefined' && maxValue !== undefined && sdk.toBig(tradeValue).gt(maxValue) ) { return { error: true, message: t('errorMaxError', { value: maxValue, ns: ['error', 'common'], }), } } return { error: false, message: '' } } } const inputBtnProps: InputButtonProps, I> = { subLabel: t('tokenMax'), emptyText: t('tokenSelectToken'), placeholderText: '0.00', maxAllow: true, handleError: handleError as any, handleCountChange, handleOnClick, label: t('labelInput'), ...inputButtonDefaultProps, ...inputButtonProps, ...rest, } as InputButtonProps, I> return isMaxBtn ? ( >), }} /> ) : ( >), }} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/BasicANFTTrade.tsx ================================================ import { CoinInfo, CoinMap, IBData, ImageIcon, NFTWholeINFO, TRADE_TYPE, } from '@loopring-web/common-resources' import { Trans, useTranslation } from 'react-i18next' import React, { ForwardedRef } from 'react' import { BasicANFTTradeProps } from './Interface' import { InputButton, InputButtonProps, InputCoin, InputCoinProps, InputSize, } from '../../basic-lib' import { Avatar, Box, Typography } from '@mui/material' import * as sdk from '@loopring-web/loopring-sdk' import styled from '@emotion/styled' const BoxInput = styled(Box)` & .main-label { color: var(--color-text-secondary); font-size: ${({ theme }) => theme.fontDefault.body1}; } ` as typeof Box export const _BasicANFTTrade = & Partial, I extends any>( { tradeData, onChangeEvent, disabled, isBalanceLimit, handleError: _handleError, inputNFTRef, baseURL, getIPFSString, inputNFTProps, inputNFTDefaultProps, // isSelected = false, // isRequired = false, isThumb, ...rest }: BasicANFTTradeProps, _ref: ForwardedRef, ) => { const { t } = useTranslation('common') const getDisabled = () => { return disabled || tradeData === undefined } const handleCountChange: any = React.useCallback( (_tradeData: T, _name: string, _ref: any) => { //const focus: 'buy' | 'sell' = _ref?.current === buyRef.current ? 'buy' : 'sell'; if ((tradeData as T)?.tradeValue !== _tradeData.tradeValue) { onChangeEvent && onChangeEvent(0, { tradeData: { ...tradeData, ..._tradeData }, to: 'button', }) } }, [onChangeEvent, tradeData], ) const handleOnClick = React.useCallback( (_event: React.MouseEvent, _ref: any) => { onChangeEvent(1, { tradeData: { ...tradeData, tradeValue: 0 }, to: 'menu', }) }, [tradeData, onChangeEvent], ) let handleError: any if (typeof _handleError !== 'function') { handleError = ({ balance, tradeValue }: T) => { if ( (isBalanceLimit && balance && typeof tradeValue !== 'undefined' && isBalanceLimit && sdk.toBig(balance).lt(tradeValue)) || (typeof tradeValue !== 'undefined' && Number(tradeValue) < 1) ) { return { error: true, message: t('tokenNotEnough', { belong: 'NFT' }), } } return { error: false, message: '' } } } else { handleError = _handleError } const CoinIconElement = isThumb && tradeData?.nftData ? ( tradeData?.image ? ( {tradeData?.belong ) : ( ) ) : undefined const noSelectElement = React.useMemo(() => { const inputCoinProps: InputCoinProps> = { subLabel: t('labelAvailable'), placeholderText: '0', decimalsLimit: 0, allowDecimals: false, isHideError: true, isShowCoinInfo: !!isThumb, isShowCoinIcon: false, CoinIconElement, order: 'right', noBalance: '0', // coinLabelStyle , coinPrecision: 0, maxAllow: isBalanceLimit, handleError: handleError as any, handleCountChange, ...inputNFTDefaultProps, ...inputNFTProps, ...rest, } as InputCoinProps> return ( > ref={inputNFTRef} disabled={getDisabled()} {...{ ...inputCoinProps, inputData: tradeData ? { ...tradeData, belong: tradeData?.belong ? tradeData?.belong : 'NFT', } : ({} as T), coinMap: {} as CoinMap>, }} /> ) }, [ tradeData, getDisabled, isBalanceLimit, handleError, inputNFTRef, inputNFTProps, inputNFTDefaultProps, rest?.isSelected, ]) const chooseElement = React.useMemo(() => { const inputBtnProps: InputButtonProps = { subLabel: t('labelAvailable'), emptyText: t('labelChooseNFT'), placeholderText: '0', decimalsLimit: 0, allowDecimals: false, CoinIconElement, order: 'right', noBalance: '0', coinPrecision: 0, maxAllow: isBalanceLimit, handleError: handleError as any, handleCountChange, handleOnClick: handleOnClick as any, ...inputNFTDefaultProps, ...inputNFTProps, ...rest, } as InputButtonProps return ( >, }} /> ) }, [ tradeData, getDisabled, isBalanceLimit, handleError, inputNFTRef, inputNFTProps, inputNFTDefaultProps, rest?.isSelected, ]) return <>{rest?.isSelected ? chooseElement : noSelectElement} } export const BasicANFTTrade = React.memo(React.forwardRef(_BasicANFTTrade)) as < T extends IBData & Partial, I extends any, >( props: BasicANFTTradeProps, ref: ForwardedRef, ) => JSX.Element export const NFTInput = React.memo( & Partial, I extends any>({ isThumb, tradeData, isBalanceLimit = true, onCopy, inputNFTDefaultProps, inputNFTRef, type, disabled, getIPFSString, baseURL, fullwidth, ...rest }: BasicANFTTradeProps & { onCopy?: (content: string) => Promise type?: TRADE_TYPE.NFT }) => { const { t } = useTranslation('common') return ( <> {isThumb ? ( {inputNFTDefaultProps?.label ? ( inputNFTDefaultProps?.label ) : ( Choose NFT {'\uFE61'} )} ) : ( {t( typeof inputNFTDefaultProps?.label === 'string' ? inputNFTDefaultProps?.label : 'labelNFTTitle', )} ), }, inputNFTRef, ...(typeof rest?.isSelected !== undefined ? ({ isSelected: rest?.isSelected, isRequired: !!rest?.isRequired, // handleOnChoose, } as any) : {}), }} /> ) : ( )} ) }, ) as & Partial, I extends any>( props: BasicANFTTradeProps & { onCopy?: (content: string) => Promise type?: TRADE_TYPE.NFT }, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/tradePanel/components/CollectionAdvanceWrap.tsx ================================================ import { Trans, useTranslation } from 'react-i18next' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import React, { useState } from 'react' import { Box, Grid, Typography } from '@mui/material' import { CollectionMeta, CollectionMetaJSON, copyToClipBoard, Info2Icon, TOAST_TIME, TradeBtnStatus, } from '@loopring-web/common-resources' import { bindHover } from 'material-ui-popup-state/es' import { Button, PopoverPure, TextareaAutosizeStyled } from '../../basic-lib' import { CollectionAdvanceProps } from '../Interface' import styled from '@emotion/styled' import { useSettings } from '../../../stores' import { Toast, ToastType } from '../../toast' const GridStyle = styled(Grid)` .coinInput-wrap { border: 1px solid var(--color-border); } .MuiInputLabel-root { font-size: ${({ theme }) => theme.fontDefault.body2}; } ` as typeof Grid export const CollectionAdvanceWrap = >({ handleDataChange, btnInfo, btnStatus, disabled = false, allowTrade, metaData, onSubmitClick, }: CollectionAdvanceProps) => { const { t } = useTranslation(['common']) const { isMobile } = useSettings() const styles = isMobile ? { flex: 1, width: 'var(--swap-box-width)' } : { width: 'var(--modal-width)' } const [copyToastOpen, setCopyToastOpen] = useState({ isShow: false, type: 'json', }) const popupState = usePopupState({ variant: 'popover', popupId: `popupId-nftMint`, }) const getDisabled = React.useMemo(() => { return disabled || allowTrade.collectionNFT === true || btnStatus === TradeBtnStatus.DISABLED }, [disabled, btnStatus]) // @ts-ignore return ( {t('labelCollectionMetaTitle')} This is a quick way to import Collection metaData information, please make sure the metaData json include name & tileUri NFT Collection information follow this format { const metaDemo: Partial = { banner_uri: 'ipfs://`${cid (storage image type media)}`', avatar_uri: 'ipfs://`${cid (storage image type media)}`', tile_uri: 'ipfs://`${cid (storage image type media)}`', name: '`${COLLECTION_NAME (string, required)}`', description: '`${COLLECTION_DESCRIPTION}`', } copyToClipBoard(JSON.stringify(metaDemo)) setCopyToastOpen({ isShow: true, type: 'json' }) }} > {t('labelCopyDemo')} { const value = _event.target.value handleDataChange(value ?? '') }} value={metaData} /> { setCopyToastOpen({ isShow: false, type: '' }) }} severity={ToastType.success} > ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/CollectionManageWrap.tsx ================================================ import { useTranslation } from 'react-i18next' import React from 'react' import { Box, Tab, Tabs, Tooltip, Typography } from '@mui/material' import { useOpenModals, useSettings } from '../../../stores' import { CollectionMeta, EmptyValueTag, Info2Icon, NFTWholeINFO, TOAST_TIME, } from '@loopring-web/common-resources' import { Button, NFTList } from '../../basic-lib' import { CollectionManageProps, CollectionMethod } from './Interface' import styled from '@emotion/styled' import * as sdk from '@loopring-web/loopring-sdk' import { Toast, ToastType } from '../../toast' import { sanitize } from 'dompurify' const BoxStyle = styled(Box)` .nft-list-wrap { padding: 0 0; } ` export const CollectionManageWrap = >({ onNFTSelected, onFilterNFT, collection, filter, baseURL, listNFT, total, toastObj, page, isLoading, selectedNFTS = [], getIPFSString, onNFTSelectedMethod, }: CollectionManageProps) => { const { t } = useTranslation(['common']) const { isMobile } = useSettings() const [tab, setTab] = React.useState(sdk.LegacyNFT.undecided) const handleTabChange = React.useCallback( (_e, value) => { let _filter = { ...filter } setTab(value) switch (value) { case 'all': if (tab === 'all') { return } _filter = { ...filter, legacyFilter: 'all', page: 1 } break case sdk.LegacyNFT.undecided: if (tab === sdk.LegacyNFT.undecided) { return } _filter = { ...filter, legacyFilter: sdk.LegacyNFT.undecided, page: 1, } break case sdk.LegacyNFT.outside: if (tab === sdk.LegacyNFT.outside) { return } _filter = { ...filter, legacyFilter: sdk.LegacyNFT.outside, page: 1 } break case sdk.LegacyNFT.inside: if (tab === sdk.LegacyNFT.inside) { return } _filter = { ...filter, legacyFilter: sdk.LegacyNFT.inside, page: 1 } break } onFilterNFT({ ..._filter }) }, [filter, onFilterNFT], ) const { setNFTMetaNotReady } = useOpenModals() const Btn = React.useMemo(() => { switch (tab) { case sdk.LegacyNFT.undecided: return ( ) case sdk.LegacyNFT.outside: return ( ) case sdk.LegacyNFT.inside: return ( ) case 'all': return <> } }, [onNFTSelectedMethod, selectedNFTS, t, tab]) // @ts-ignore return ( {isMobile ? ( Sorry Mobile web is not support this feature, Please try it on website ) : ( {t('labelNFTCollectionName')} {[sdk.LegacyNFT.undecided, sdk.LegacyNFT.outside, sdk.LegacyNFT.inside, 'all'].map( (item) => { return ( {t(`labelImportCollection${item}`)} } /> ) }, )} {tab !== 'all' && ( {Btn} )} { onFilterNFT({ ...filter, page }) }} setNFTMetaNotReady={setNFTMetaNotReady} isManage={false} isSelectOnly={tab !== 'all'} isMultipleSelect={tab !== 'all'} getIPFSString={getIPFSString} baseURL={baseURL} nftList={listNFT} isLoading={isLoading} total={total} page={page} size={'small'} selected={selectedNFTS} onClick={async (_item) => { if (tab !== 'all') { onNFTSelected(_item as NFT) } }} /> )} ) // Undecided 12 // Others 100 // Current Collection 100 // All 1000 // Search } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/ContactSelection.tsx ================================================ import { Avatar, Box, InputAdornment, OutlinedInput, Typography } from '@mui/material' import { SearchIcon, CloseIcon, SoursURL, hexToRGB } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { useTheme } from '@emotion/react' import styled from '@emotion/styled' import React, { JSX } from 'react' import { useTranslation } from 'react-i18next' import * as sdk from '@loopring-web/loopring-sdk' import { AddressTypeTag } from '../../basic-lib' type SingleContactProps = { editing: boolean name: string address: string addressType?: (typeof sdk.AddressType)[sdk.AddressTypeKeys] onSelect: (address: string) => void hidden: boolean } const AvatarContainer = styled(Box)` background-color: white; border-radius: 20px; width: 40px; height: 40px; ` const getInitials = (name: string) => { let initials const nameSplit = name.split(' ') const nameLength = nameSplit.length if (nameLength > 1) { initials = nameSplit[0].substring(0, 1) + nameSplit[nameLength - 1].substring(0, 1) } else if (nameLength === 1) { initials = nameSplit[0].substring(0, 1) } else return return initials.toUpperCase() } // @ts-ignore export const InitialNameAvatar = React.memo( ({ name, ...rest }: { name: string } & any) => { const theme = useTheme() return ( {getInitials(name)} ) }, ) as ({ name, ...rest }: { name: string } & any) => JSX.Element export const SingleContact = (props: SingleContactProps) => { const { editing, name, address, addressType, hidden, onSelect } = props return ( { onSelect(address) }} > {editing ? ( ) : ( <> {name} )} {address} ) } const CloseIconStyled = styled(CloseIcon)` position: absolute; top: 55%; transform: translateY(-50%); right: ${({ theme }) => theme.unit}px; cursor: pointer; ` // OutlinedInput type ContactSelectionProps = { onSelect: (address: string) => void scrollHeight: string } & Pick export const ContactSelection = (props: ContactSelectionProps) => { // const { t } = useTranslation(); const { onSelect, contacts, scrollHeight } = props const { isMobile } = useSettings() const theme = useTheme() const [inputValue, setInputValue] = React.useState('') const handleOnFiler = (value: string) => { setInputValue(value) } const filterContacts = inputValue ? contacts.filter((contact) => { return ( contact.contactAddress.toLowerCase().includes(inputValue.toLowerCase()) || contact.contactName.toLowerCase().includes(inputValue.toLowerCase()) ) }) : contacts const { t } = useTranslation() const normalView = ( <> } value={inputValue} endAdornment={ { handleOnFiler('') }} /> } onChange={(e) => { handleOnFiler(e.target.value) }} /> {filterContacts && filterContacts.map((contact) => { return ( ) const loadingView = ( {'loading'} ) const emptyView = ( {t('labelContactsNoContact')} ) return ( {t('labelContactsSelectReciepient')} {contacts === undefined ? loadingView : contacts.length === 0 ? emptyView : normalView} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/CreateCollectionWrap.tsx ================================================ import { Box, FormLabel, Grid, Tooltip, Typography } from '@mui/material' import React from 'react' import { BtnInfo, Button, ImageUploadWrapper, IpfsFile, IPFSSourceUpload, TextareaAutosizeStyled, TextField, } from '../../basic-lib' import { Trans, useTranslation } from 'react-i18next' import { CollectionMeta, GET_IPFS_STRING, htmlDecode, Info2Icon, TradeBtnStatus, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' export type CreateCollectionViewProps = { keys: { [key: string]: undefined | IpfsFile } onFilesLoad: (key: string, value: IpfsFile) => void onDelete: (key: string) => void btnStatus: TradeBtnStatus resetEdit?: () => void btnInfo?: BtnInfo isEdit?: boolean disabled?: boolean onSubmitClick: () => Promise handleOnDataChange: (key: string, value: any) => void collectionValue: Co getIPFSString: GET_IPFS_STRING baseURL: string } export const CreateCollectionWrap = >({ keys, onFilesLoad, onDelete, btnStatus, btnInfo, isEdit, resetEdit, disabled, handleOnDataChange, collectionValue, onSubmitClick, getIPFSString, baseURL, }: CreateCollectionViewProps) => { const { t } = useTranslation('common') const getDisabled = React.useMemo(() => { return disabled || btnStatus === TradeBtnStatus.DISABLED }, [disabled, btnStatus]) const { isMobile } = useSettings() return ( Tile (Dimensions: 5:7) {'\uFE61'} { onDelete('tileUri') }} onChange={(value) => { onFilesLoad('tileUri', value) }} /> {t('labelAvatar')} { onDelete('avatar') }} onChange={(value) => { onFilesLoad('avatar', value) }} /> {t('labelBanner')} { onDelete('banner') }} onChange={(value) => { onFilesLoad('banner', value) }} /> Collection Name {'\uFE61'} } type={'text'} onChange={(e: React.ChangeEvent<{ value: string }>) => handleOnDataChange('name', e.target.value) } /> Description handleOnDataChange('description', event.target.value)} draggable={true} /> {isEdit && resetEdit && ( )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/CreateRedPacketWrap.tsx ================================================ import { Box, CardContent, Checkbox, FormControlLabel, FormLabel, Grid, IconButton, Tab, Tooltip, Typography, // TextField as MuiTextField, } from '@mui/material' import React from 'react' import { Button, CardStyleItem, InputButtonProps, InputCoin, InputSearch, NftImageStyle, Tabs, TextField, } from '../../basic-lib' import { useTranslation, WithTranslation, withTranslation, Trans } from 'react-i18next' import { BackIcon, CoinInfo, EmptyValueTag, FeeInfo, getValuePrecisionThousand, IBData, LuckyRedPacketItem, REDPACKET_ORDER_LIMIT, RedPacketOrderData, SoursURL, TradeBtnStatus, REDPACKET_ORDER_NFT_LIMIT, Info2Icon, RedPacketOrderType, ScopePublic, HelpIcon, TokenType, CheckBoxIcon, CheckedIcon, YEAR_DAY_MINUTE_FORMAT, ScopeQR, ScopeTarget, isAddress, LuckyRedPacketList, myLog, BlindBoxIcon, NormalRedpacketIcon, } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' import { CreateRedPacketViewProps, RedPacketStep, SwitchData, TargetRedPacketStep, TargetRedpacktInputAddressStepProps, TargetRedpacktSelectStepProps, } from '../Interface' import { MenuBtnStyled } from '../../styled' import styled from '@emotion/styled' import { BasicACoinTrade } from './BasicACoinTrade' import { BtnMain } from './tool' import * as sdk from '@loopring-web/loopring-sdk' import moment from 'moment' import { NFTInput } from './BasicANFTTrade' import { DateTimeRangePicker } from '../../datetimerangepicker' import BigNumber from 'bignumber.js' import { CoinIcons, FeeSelect, InitialNameAvatar, Modal } from '../../../components' import { useTheme } from '@emotion/react' import { useHistory } from 'react-router' import { TFunction } from 'i18next' const StyledTextFiled = styled(TextField)`` const RedPacketBoxStyle = styled(Box)` padding-top: ${({ theme }) => theme.unit}px; .MuiFormGroup-root { align-items: flex-start; } .coinInput-wrap { border: 1px solid var(--color-border); } .MuiInputLabel-root { font-size: ${({ theme }) => theme.fontDefault.body2}; } .MuiButtonBase-root.step { padding-left: ${({ theme }) => theme.unit * 4}px; padding-right: ${({ theme }) => theme.unit * 4}px; } //width: 100%; //display: flex; textarea { background: var(--field-opacity); border-color: var(--opacity); :hover { border-color: var(--color-border-hover); } } ` export const CreateRedPacketStepWrap = withTranslation()( >, I, F extends FeeInfo>({ btnStatus, btnInfo, disabled, tradeType, handleFeeChange, handleOnDataChange, onCreateRedPacketClick, walletMap, tradeData, coinMap, tokenMap, isFeeNotEnough, setActiveStep, feeInfo, chargeFeeTokenList, lastFailed, selectedType, minimum, maximum, idIndex, selectNFTDisabled, redPacketConfig, ...rest }: CreateRedPacketViewProps & { selectedType: LuckyRedPacketItem } & WithTranslation) => { const { t } = useTranslation('common') const inputButtonDefaultProps = { label: selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? t('labelAmountEach') : t('labelRedPacketTotalAmount'), decimalsLimit: (tokenMap && tokenMap[tradeData?.belong as string])?.precision ?? 8, minimum, placeholderText: tradeData?.belong ? t('labelRedPacketsMinRange', { value: minimum }) + (sdk.toBig(maximum ?? 0).lt(sdk.toBig(tradeData.balance ?? 0)) ? ' - ' + t('labelRedPacketsMaxRange', { value: maximum }) : '') : '0.00', } const inputNFTButtonDefaultProps: Partial>> = { label: selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? t('labelAmountEach') : t('labelRedPacketTotalAmount'), decimalsLimit: 0, minimum, placeholderText: '0', } // const [dayValue, setDayValue] = React.useState(moment()); // const [durationValue, setDurationValue] = React.useState(1); const getDisabled = React.useMemo(() => { return disabled || btnStatus === TradeBtnStatus.DISABLED }, [disabled, btnStatus]) const [showFeeModal, setShowFeeModal] = React.useState(false) const inputBtnRef = React.useRef() const inputSplitRef = React.useRef() const isToken = tradeType === RedPacketOrderType.TOKEN || (tradeType === RedPacketOrderType.BlindBox && !tradeData.isNFT) const { total: redPacketTotalValue, splitValue } = React.useMemo(() => { // if (tradeType == TRADE_TYPE.TOKEN) { // // } else { // const splitValue = // selectedType.value.value == 2 // ? (tradeData?.tradeValue ?? 0) / (tradeData?.numbers ?? 1) // : tradeData?.tradeValue ?? 0; // return { // total: tradeData.tradeValue ?? EmptyValueTag, // splitValue: splitValue && EmptyValueTag, // }; // } if (tradeData?.tradeValue && tradeData.belong && tokenMap) { const splitValue = selectedType.value.partition == sdk.LuckyTokenAmountType.RANDOM ? sdk.toBig(tradeData?.tradeValue ?? 0).div(tradeData?.numbers ?? 1) : sdk.toBig(tradeData?.tradeValue ?? 0) const total = selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? sdk.toBig(tradeData?.tradeValue ?? 0).times(tradeData?.numbers ?? 0) : sdk.toBig(tradeData?.tradeValue ?? 0) if (isToken) { return { total: tokenMap[tradeData?.belong as string] && getValuePrecisionThousand( total, tokenMap[tradeData?.belong as string].precision, tokenMap[tradeData?.belong as string].precision, tokenMap[tradeData?.belong as string].precision, false, // { isFait: true } ) + ' ' + tradeData.belong, splitValue: tokenMap[tradeData?.belong as string] && getValuePrecisionThousand( splitValue, tokenMap[tradeData?.belong as string].precision, tokenMap[tradeData?.belong as string].precision, tokenMap[tradeData?.belong as string].precision, false, // { isFait: true } ) + ' ' + tradeData.belong, } } else { return { total: getValuePrecisionThousand( total, 0, 0, 1, false, // { isFait: true } ) + ' ' + (total.gt(1) ? 'NFTs' : 'NFT'), splitValue: getValuePrecisionThousand( splitValue.toFixed(0, 1), 0, 0, 1, false, // { isFait: true } ) + ' ' + 'NFT', } } } else { return { total: EmptyValueTag, splitValue: selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE && EmptyValueTag, } } }, [tradeData, tradeData?.numbers, selectedType.value.partition, coinMap, tradeType]) const inputSplitProps = React.useMemo(() => { const inputSplitProps: any = { label: selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? t('labelQuantity') : t('labelSplit'), //t("labelTokenAmount"), placeholderText: t('labelQuantity'), isHideError: true, isShowCoinInfo: false, handleCountChange: (ibData: IBData, _name: string, _ref: any) => { handleOnDataChange({ numbers: ibData.tradeValue, } as unknown as Partial) }, fullWidth: true, } let inputSplitExtendProps = {}, balance: any = undefined if (tradeData?.tradeValue && Number(tradeData?.tradeValue) && maximum) { if (selectedType.value.partition === sdk.LuckyTokenAmountType.AVERAGE) { balance = sdk .toBig(tradeData?.balance ?? 0) .div(tradeData.tradeValue) .toFixed(0, 1) } else { balance = sdk .toBig(tradeData.tradeValue) .div(Number(minimum) ?? 1) .toFixed(0, 1) } balance = sdk.toBig(balance).lte(REDPACKET_ORDER_LIMIT) ? balance : REDPACKET_ORDER_LIMIT inputSplitExtendProps = { // maxAllow: true, // subLabel: t("labelAvailable"), // handleError: (data: any) => { // handleOnDataChange({ // numbers: data.tradeValue, // } as unknown as Partial); // if (data.tradeValue && data.tradeValue > data.balance) { // return { // error: true, // }; // } // return { // error: false, // }; // }, inputData: { belong: selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? t('labelQuantity') : t('labelSplit'), tradeValue: tradeData?.numbers, balance: balance, }, } } else { inputSplitExtendProps = { // maxAllow: false, // subLabel: "", // handleError: () => undefined, inputData: { belong: selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? t('labelAmountEach') : t('labelSplit'), tradeValue: tradeData?.numbers, // count: tradeData?.numbers, }, } } return { ...inputSplitProps, ...inputSplitExtendProps, } }, [tradeData?.numbers, selectedType.value.partition, maximum, minimum, tradeType]) const handleToggleChange = (value: F) => { if (handleFeeChange) { handleFeeChange(value) } } const _balance = React.useMemo(() => { if ( tradeData.belong !== undefined && // tradeData?.numbers && // @ts-ignore // tradeData.numbers !== "0" && tradeData.balance && tradeType === RedPacketOrderType.NFT ) { if (selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE) { const value = BigNumber.min(tradeData.balance, REDPACKET_ORDER_NFT_LIMIT).toString() return sdk .toBig(value) .div(tradeData?.numbers && tradeData?.numbers != 0 ? tradeData?.numbers : 1) .toFixed(0, 1) } else { return BigNumber.min(tradeData.balance, REDPACKET_ORDER_NFT_LIMIT).toString() } } else if ( selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE && tradeData.belong !== undefined && tradeData?.numbers && // @ts-ignore tradeData.numbers !== '0' && tradeData.balance ) { return sdk.toBig(tradeData.balance).div(tradeData.numbers).toString() } else { return tradeData.balance } }, [selectedType.value.partition, tradeData.balance, tradeData?.numbers]) const { isMobile } = useSettings() const startDateTime = tradeData.validSince ? moment(tradeData.validSince) : null const endDateTime = tradeData.validUntil ? moment(tradeData.validUntil) : null const now = moment() const startMinDateTime = endDateTime ? moment.max(now, endDateTime.clone().subtract(7, 'days')) : now const startMaxDateTime = endDateTime ? endDateTime.clone() : now.clone().add(1, 'days') const endMinDateTime = startDateTime ? moment.max(now, startDateTime.clone()) : now const timeRangeMaxInSeconds = isToken ? redPacketConfig.timeRangeMaxInSecondsToken : redPacketConfig.timeRangeMaxInSecondsNFT // ?? 14 * 24 * 60 * 60; const endMaxDateTime = startDateTime ? startDateTime.clone().add(timeRangeMaxInSeconds, 'seconds') : undefined // @ts-ignore return ( {t( selectedType.value.mode == sdk.LuckyTokenClaimType.BLIND_BOX ? 'labelLuckyBlindBox' : selectedType.value.mode == sdk.LuckyTokenClaimType.RELAY ? 'labelRelayRedPacket' : selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? 'labelRedPacketSendAverageTitle' : 'labelRedPacketSenRandomTitle', ) + ' — ' + t(`labelRedPacketViewType${tradeData?.type?.scope ?? 0}`)} {isToken ? ( // @ts-ignore ) : ( { return { error: false, message: '' } }, onChangeEvent: (_index: 0 | 1, { to, tradeData: newTradeData }: SwitchData) => { if (_index === 1) { if (selectNFTDisabled) return handleOnDataChange({ collectionInfo: undefined, tokenId: undefined, tradeValue: undefined, balance: undefined, nftData: undefined, belong: undefined, image: undefined, } as T) if (tradeData.type?.scope === sdk.LuckyTokenViewType.TARGET) { setActiveStep(TargetRedPacketStep.NFTList) } else { setActiveStep(RedPacketStep.NFTList) } } else if (to === 'button') { handleOnDataChange({ tradeValue: newTradeData.tradeValue, belong: newTradeData.belong, balance: tradeData.balance, nftData: newTradeData.nftData, } as any) } }, inputNFTDefaultProps: inputNFTButtonDefaultProps, inputNFTRef: inputBtnRef, } as any)} /> )} {t('labelAssetAmount', { value: getValuePrecisionThousand(tradeData.balance, 8, 8, 8, false), })} {tradeData.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX && ( // ref={inputSplitRef} label={t('labelBlindBoxRedPacketWithGift')} placeholderText={t('labelQuantity')} isHideError={false} isShowCoinInfo={false} // handleError={(data: any) => { // handleOnDataChange({ // giftNumbers: data.tradeValue, // } as unknown as Partial); // return { // error: // tradeData.giftNumbers && // tradeData.numbers && // tradeData.giftNumbers > tradeData.numbers // ? true // : false, // }; // }} name={'giftnumbers'} order={'right'} handleCountChange={(data) => { handleOnDataChange({ giftNumbers: data.tradeValue, } as unknown as Partial) }} inputData={{ belong: selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? t('labelQuantity') : t('labelSplit'), tradeValue: tradeData?.giftNumbers, }} coinMap={{}} coinPrecision={undefined} disabled={disabled} // inputError={ // tradeData.giftNumbers && // tradeData.numbers && // tradeData.giftNumbers > tradeData.numbers // ? { error: true } // : { error: false } // } /> )} ref={inputSplitRef} {...{ ...inputSplitProps, name: 'numbers', order: 'right', handleCountChange: (data) => { handleOnDataChange({ numbers: data.tradeValue, } as unknown as Partial) }, coinMap: {}, coinPrecision: undefined, }} disabled={disabled} /> {t('labelRedPacketMemo')} } value={tradeData.memo} onChange={(event) => handleOnDataChange({ memo: event.target.value, //event?.target?.value, } as unknown as Partial) } size={'large'} inputProps={{ placeholder: t('labelRedPacketMemoPlaceholder'), maxLength: 25, }} fullWidth={true} /> {selectedType.value.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? t('labelRedPacketTimeRangeBlindbox') : t('labelRedPacketTimeRange')} { handleOnDataChange({ validSince: m ? m.toDate().getTime() : undefined, validUntil: m ? m.clone().add(1, 'days').toDate().getTime() : undefined, } as unknown as Partial) }} onStartOpen={() => { handleOnDataChange({ validUntil: undefined, } as unknown as Partial) }} endValue={endDateTime} endMinDateTime={endMinDateTime} endMaxDateTime={endMaxDateTime} onEndChange={(m) => { if ( startDateTime && m && startDateTime?.toDate().getTime() > m?.toDate().getTime() ) { handleOnDataChange({ validUntil: endDateTime, } as unknown as Partial) } else { const maximunTimestamp = startDateTime ? moment(startDateTime).add(timeRangeMaxInSeconds, 'seconds').toDate().getTime() : 0 handleOnDataChange({ validUntil: m ? m.toDate().getTime() > maximunTimestamp ? maximunTimestamp : m.toDate().getTime() : undefined, } as unknown as Partial) } }} customeEndInputPlaceHolder={ tradeData.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? t('labelBlindBoxEndDate2') : undefined } /> {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as F) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough && isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough && isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} floatLeft /> )} {redPacketTotalValue} {selectedType.value.partition == sdk.LuckyTokenAmountType.AVERAGE ? t('labelRedPacketsSplitCommonDetail', { value: splitValue }) : t('labelRedPacketsSplitLuckyDetail')} {lastFailed && ( {t('labelConfirmAgainByFailed')} )} {tradeData.isNFT ? t('labelBlindBoxExpirationExplainationForNFT') : tradeData.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? t('labelBlindBoxExpirationExplainationForTokenBlindbox') : t('labelBlindBoxExpirationExplainationForToken')} ) }, ) as >, I, F extends FeeInfo>( props: CreateRedPacketViewProps & { selectedType: LuckyRedPacketItem }, ) => JSX.Element export const CreateRedPacketStepType = withTranslation()( , I, C = FeeInfo>({ // handleOnSelectedType, tradeType, tradeData, // handleOnDataChange, setActiveStep, backToScope, selectedType, disabled = false, btnInfo, onClickNext, showNFT, onSelecteValue, redPacketConfig, handleOnDataChange, t, }: Omit, 'tokenMap'> & { selectedType: LuckyRedPacketItem // setSelectType: (value: LuckyRedPacketItem) => void; } & WithTranslation) => { const { isMobile } = useSettings() const getDisabled = React.useMemo(() => { return disabled }, [disabled]) const showERC20Blindbox = redPacketConfig.showERC20Blindbox const isTokens = (tradeType === RedPacketOrderType.BlindBox && !tradeData.isNFT) || tradeType === RedPacketOrderType.TOKEN const setIsTokens = React.useCallback( (isTokens: boolean) => { myLog('isTokens', tradeData) myLog('isTokens', isTokens) if (tradeType === RedPacketOrderType.BlindBox) { handleOnDataChange({ ...tradeData, isNFT: !isTokens, }) } else { handleOnDataChange({ ...tradeData, tradeType: isTokens ? RedPacketOrderType.TOKEN : RedPacketOrderType.NFT, }) } }, [tradeData], ) const showList = LuckyRedPacketList.filter((item) => tradeType === RedPacketOrderType.FromNFT ? tradeData.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? item.tags?.includes('showInBlindBox') : item.tags?.includes('showInNormal') : tradeType === RedPacketOrderType.BlindBox ? item.tags?.includes('showInBlindBox') : item.tags?.includes('showInNormal'), ) const enabledList = LuckyRedPacketList.filter((item) => tradeType === RedPacketOrderType.FromNFT ? tradeData.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? item.tags?.includes('enableInBlindBox') : item.tags?.includes('enableInNFTS') : tradeType === RedPacketOrderType.BlindBox ? item.tags?.includes('enableInBlindBox') : tradeType === RedPacketOrderType.TOKEN ? item.tags?.includes('enableInERC20') : item.tags?.includes('enableInNFTS'), ).filter((item) => tradeData.type?.scope === sdk.LuckyTokenViewType.TARGET ? !item.tags?.includes('disabledForExclusive') : true, ) myLog('tradeData.type?.mode', tradeData.type?.mode) return ( {t( selectedType.value.mode == sdk.LuckyTokenClaimType.BLIND_BOX ? 'labelLuckyBlindBox' : 'labelNormalRedPacketTitle', ) + ' — ' + t(`labelRedPacketViewType${tradeData?.type?.scope ?? 0}`)} {tradeType === RedPacketOrderType.BlindBox && ( )} {tradeType === RedPacketOrderType.FromNFT ? ( { const found = value === 'Normal' ? LuckyRedPacketList.find((config) => config.tags?.includes('enableInNFTS') && config.value.partition === sdk.LuckyTokenAmountType.RANDOM, ) : LuckyRedPacketList.find((config) => config.value.partition === sdk.LuckyTokenAmountType.RANDOM && config.value.mode === sdk.LuckyTokenClaimType.BLIND_BOX ) onSelecteValue!(found!) }} aria-label='l2-history-tabs' variant='scrollable' > ) : ( { setIsTokens(value === 'Tokens') }} aria-label='l2-history-tabs' variant='scrollable' > {tradeData.type?.scope !== sdk.LuckyTokenViewType.PUBLIC && ( )} )} {showList.map((item: LuckyRedPacketItem, index) => { const enabled = enabledList.find((enableItem) => enableItem.value === item.value) return ( { onSelecteValue && onSelecteValue(item) }} sx={{ '&&&&': { borderRadius: 2, }, '&&&&.Mui-disabled': { backgroundColor: 'transparent', borderStyle: 'solid', opacity: 0.25, }, }} > {t(item.labelKey)} {t(item.desKey)} ) })} getDisabled, onClick: () => { onClickNext() }, }} /> ) }, ) export const CreateRedPacketStepTokenType = withTranslation()( , I, C = FeeInfo>({ tradeType, // setActiveStep, disabled = false, btnInfo, onClickNext, onClickBack, showNFT, t, onChangeTradeType, }: Omit, 'tradeData' | 'tokenMap'> & WithTranslation) => { const { isMobile } = useSettings() const getDisabled = React.useMemo(() => { return disabled }, [disabled]) const isNormal = tradeType === RedPacketOrderType.TOKEN || tradeType === RedPacketOrderType.NFT return ( onChangeTradeType!(RedPacketOrderType.TOKEN)} > {t('labelLuckyTokenNormal')} onChangeTradeType!(RedPacketOrderType.BlindBox)} > {t('labelRedpacketBlindBox')} getDisabled, onClick: () => { onClickNext() }, }} /> ) }, ) const ScopeOption = styled(Box)<{ selected?: boolean; disabled?: boolean }>` display: flex; border: 1px solid ${({ selected }) => (selected ? 'var(--color-border-select)' : 'var(--color-border)')}; padding: ${({ theme }) => 3 * theme.unit}px; border-radius: ${({ theme }) => theme.unit}px; width: 47%; cursor: ${({ disabled }) => (disabled ? '' : 'pointer')}; opacity: ${({ disabled }) => (disabled ? '0.5' : '')}; ` type CreateRedPacketScopeProps = { selectedScope: sdk.LuckyTokenViewType onSelecteScope: (scope: sdk.LuckyTokenViewType) => void onClickNext: () => void palazaPublicDisabled: boolean exclusiveDisabled: boolean showBackBtn: boolean showExclusiveOption: boolean } export const CreateRedPacketScope = withTranslation()( ({ selectedScope, onClickNext, onSelecteScope, palazaPublicDisabled, exclusiveDisabled, showBackBtn, showExclusiveOption, t, }: CreateRedPacketScopeProps & WithTranslation) => { const theme = useTheme() const history = useHistory() return ( {t('labelLuckyTokenViewTypePublic')} {/* {palazaPublicDisabled && ( */} { !palazaPublicDisabled && onSelecteScope(sdk.LuckyTokenViewType.PUBLIC) }} selected={selectedScope === sdk.LuckyTokenViewType.PUBLIC} disabled={palazaPublicDisabled} > {t('labelRedPacketPlazaPublic')} {t('labelRedPacketPlazaPublicDes')} {/* )} */} onSelecteScope(sdk.LuckyTokenViewType.PRIVATE)} selected={selectedScope === sdk.LuckyTokenViewType.PRIVATE} > {t('labelRedPacketQRPublic')} {t('labelRedPacketQRPublicDes')} {showExclusiveOption && ( {t('labelLuckyTokenViewTypePrivate')}{' '} ), br:
    , li: ( ), }} /> } >
    { !exclusiveDisabled && onSelecteScope(sdk.LuckyTokenViewType.TARGET) }} selected={selectedScope === sdk.LuckyTokenViewType.TARGET} disabled={exclusiveDisabled} > {t('labelRedPacketExclusive')} {t('labelRedPacketExclusiveDes')}
    )} {showBackBtn && ( )} false, onClick: () => { onClickNext() }, }} />
    ) }, ) const TargetRedpacktOption = styled(Box)<{ selected: boolean }>` display: flex; border: 1px solid ${({ selected }) => (selected ? 'var(--color-border-select)' : 'var(--color-border)')}; padding: ${({ theme }) => 2 * theme.unit}px; border-radius: ${({ theme }) => 0.5 * theme.unit}px; width: 31%; margin-right: 2%; margin-bottom: 2%; flex-direction: column; cursor: pointer; ` export const ReceiptListModal = (props: { open: boolean onClose: () => void targets: string[] t: TFunction<'translation', undefined> }) => { const { open, t, onClose, targets } = props return ( {t('labelRedpacketRecipientList')} {t('labelRedPacketTotal', { count: targets.length, })} {targets.length > 0 ? ( {targets.map((target) => target + ';').join('\n')} ) : ( {t('labelRedpacketreceiptListEmpty')} )} } /> ) } export const TargetRedpacktSelectStep = withTranslation()( (props: TargetRedpacktSelectStepProps & WithTranslation) => { const { onClickCreateNew, targetRedPackets, onClickExclusiveRedpacket, onClickViewDetail, popRedPacket, popRedPacketAmountStr, onCloseRedpacketPop, backToScope, idIndex, t, } = props const theme = useTheme() const { coinJson, isMobile } = useSettings() const [showReceipts, setShowReceipts] = React.useState(false) // const [enlarged, setEnlarged] = React.useState(false) return ( {t('labelTargetRedpacketOption1')} {t('labelTargetRedpacketOption2')}{' '} {t('labelRedpacketExclusiveReady', { count: targetRedPackets.length })} {targetRedPackets?.length > 0 ? ( {targetRedPackets && targetRedPackets .filter((redpacket) => (redpacket.tokenAmount as any).remainTargetCount > 0) .map((redpacket) => ( { onClickExclusiveRedpacket({ hash: redpacket.hash, remainCount: (redpacket.tokenAmount as any).remainTargetCount as number, }) }} selected={false} key={redpacket.hash} > {redpacket.isNft ? ( ) : ( )} {redpacket.isNft ? redpacket.nftTokenInfo?.metadata?.base.name : idIndex[redpacket.tokenId]} {redpacket.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? t('labelLuckyBlindBox') : redpacket.type.mode === sdk.LuckyTokenClaimType.RELAY ? t('labelRelayRedPacket') : redpacket.type.partition === sdk.LuckyTokenAmountType.RANDOM ? t('labelLuckyRedPacket') : t('labelNormalRedPacket')} { e.stopPropagation() onClickViewDetail(redpacket.hash) }} color={'var(--color-primary)'} > {t('labelRedPacketExclusiveViewDetails')}
    {t('labelRedpacketSentMaxLimit')} {redpacket.tokenAmount.totalCount - (redpacket.tokenAmount as any).remainTargetCount}{' '} / {redpacket.tokenAmount.totalCount}
    ))}
    ) : ( {t('labelTargetRedpacketNoRedpacket')} {t('labelTargetRedpacketNoRedpacketDes')} )} {/* {targetRedPackets?.length > 0 && ( setEnlarged(!enlarged)} /> )} */}
    {/* false, onClick: () => { onClickCreateNew() }, }} /> */} { onCloseRedpacketPop() }} content={ {popRedPacket?.luckyToken!.isNft ? ( ) : ( )} {popRedPacketAmountStr && {popRedPacketAmountStr}} {popRedPacket?.luckyToken.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && ( {t('labelRedpacketGiftRedPacket')} {popRedPacket.luckyToken.tokenAmount.giftCount} )} {t('labelRedpacketRedPacketscount')} {popRedPacket?.luckyToken.tokenAmount.totalCount} {t('labelBlindBoxStartTime')} {moment(popRedPacket?.luckyToken.validSince).format(YEAR_DAY_MINUTE_FORMAT)} {popRedPacket?.luckyToken.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? t('labelRedpacketRevealTime') : t('labelBlindBoxEndTime')} {moment(popRedPacket?.luckyToken.validUntil).format(YEAR_DAY_MINUTE_FORMAT)} {t('labelRedpacketBestwishes')} {popRedPacket?.luckyToken.info.memo} } /> setShowReceipts(false)} t={t} targets={(popRedPacket && (popRedPacket as any).targets) ?? []} />
    ) }, ) const MultiLineInput = styled('textarea')` background: transparent; height: 150px; border: 1px solid var(--color-border); outline: none; color: var(--color-text-primary); padding: ${({ theme }) => theme.unit * 1}px; border-radius: ${({ theme }) => theme.unit * 0.5}px; width: 100%; ::placeholder { color: var(--color-text-secondary); font-family: Roboto; } ` const isAddressValid = (address: string, previousAddress: string[]) => { return isAddress(address) } const getValidAddresses = (input: string, sentAddress: string[]) => { const addresses = input .split(';') .filter((str) => str.trim()) .map((str) => str.trim()) return addresses.filter((str, index) => { return isAddressValid(str, addresses.slice(0, index).concat(sentAddress)) }) } const getInvalidAddresses = (input: string, sentAddress: string[]) => { const addresses = input .split(';') .filter((str) => str.trim()) .map((str) => str.trim()) return addresses.filter((str, index) => { return !isAddressValid(str, addresses.slice(0, index).concat(sentAddress)) }) } export const TargetRedpacktInputAddressStep = withTranslation()( (props: TargetRedpacktInputAddressStepProps & WithTranslation) => { const { contacts, popupChecked, addressListString, onChangePopupChecked, onFileInput, onClickSend, onConfirm, onManualEditInput, popUpOptionDisabled, maximumTargetsLength, onClickBack, sentAddresses, clearInput, t, } = props const theme = useTheme() const { isMobile } = useSettings() const [showContactModal, setShowContactModal] = React.useState(false) const [showPopUpTips, setShowPopUpTips] = React.useState(false) const [inputDisabled, setInputDisabled] = React.useState(false) const [showChangeTips, setShowChangeTips] = React.useState<{ previousInputType?: 'text' | 'contact' | 'edit' show: boolean confirmCallBack?: () => void contactImportCaches?: string[] }>({ show: false, }) const [showAddressReview, setShowAddressReview] = React.useState(false) const [selectedAddresses, setSelectedAddresses] = React.useState([] as string[]) const [search, setSearch] = React.useState('') const overMaximum = getValidAddresses(addressListString, sentAddresses ?? []).length > maximumTargetsLength return ( { setShowContactModal(false) }} content={ {t('labelRedpacketContactImport')} { setSearch(e as unknown as string) }} value={search} /> {contacts ?.filter((contact) => { return contact.contactAddress && contact.contactName && search ? contact.contactAddress.toLowerCase().includes(search.toLowerCase()) || contact.contactName.toLowerCase().includes(search.toLowerCase()) : true }) .map((contact) => { return ( {contact.contactName} {contact.contactAddress} { const newSelectedAddresses = selectedAddresses.find( (addr) => addr === contact.contactAddress, ) ? selectedAddresses.filter((addr) => addr !== contact.contactAddress) : [contact.contactAddress, ...selectedAddresses] setSelectedAddresses(newSelectedAddresses) }} checked={ selectedAddresses.find((addr) => addr === contact.contactAddress) ? true : false } /> ) })} {t('labelRedpacketExclusiveSelected', { count: selectedAddresses.length })} } /> { setShowPopUpTips(false) }} content={ {t('labelRedpacketTips')} {t('labelRedpacketPopPpDes')} } /> { setShowChangeTips({ ...showChangeTips, show: false, }) }} content={ {t('labelRedpacketTips')} {t('labelRedpacketChangeImportTips')} } /> { setShowAddressReview(false) }} content={ {t('labelRedpacketAddressesReview')} {addressListString && addressListString .split(';') .filter((str) => str.trim()) .map((str, index) => ( <> str.trim()) .slice(0, index) .concat(sentAddresses ?? []), ) ? 'var(--color-text-primary)' : 'var(--color-error)' } component={'span'} > {str} {' '} ;
    ))} {/* This list contains 2 valid addresses, 1 invalid addresses. To proceed, invalid addresses will be automatically removed from the list. */}
    {t('labelRedpacketAddressesReviewPart1', { count: getValidAddresses(addressListString, sentAddresses ?? []).length, })}{' '} {t('labelRedpacketAddressesReviewPart2', { count: getInvalidAddresses(addressListString, sentAddresses ?? []).length, })} {t('labelRedpacketAddressesReviewPart3')} {/* */}
    } /> {t('labelRedPacketExclusive')} {t('labelExclusiveWhitelistDes')} { setShowChangeTips({ ...showChangeTips, previousInputType: 'edit', }) onManualEditInput(e.currentTarget.value) }} value={addressListString} placeholder={`eg:0x60eEB5870ebEf49ce7cDc354dac49906CF8d9285;\n0xF61f3C9cEcB8d206DeA1faEd99A693e6d3BAAEf2;`} /> {maximumTargetsLength - getValidAddresses(addressListString, sentAddresses ?? []).length >= 0 ? t('labelSendRedPacketMax', { count: maximumTargetsLength - getValidAddresses(addressListString, sentAddresses ?? []).length, }) : t('labelRedPacketMaxValueExceeded')} { } { const reader = new FileReader() reader.onload = (event) => { setInputDisabled(true) onFileInput(event.target?.result ? (event.target.result as string) : '') setShowChangeTips({ ...showChangeTips, previousInputType: 'text', }) } e.currentTarget.files && reader.readAsText(e.currentTarget.files[0]) e.currentTarget.value = '' }} style={{ display: 'none' }} id='file-upload' type='file' accept='.txt' /> } label={ } /> {t('labelRedpacketNotificationDisplay')} } icon={} color='default' /> } label={t('labelRedpacketBadge')} /> {t('labelRedpacketRedDotDes')} { if (popUpOptionDisabled) { setShowPopUpTips(true) } else { onChangePopupChecked(!popupChecked) } }} checkedIcon={} icon={} color='default' /> } label={ {t('labelRedpacketPopUp')}{' '} {' '} } /> {t('labelRedpacketPopPpDes')} {overMaximum ? ( ) : ( { return ( getValidAddresses(addressListString, sentAddresses ?? []).length === 0 || overMaximum ) }, onClick: () => { if (getInvalidAddresses(addressListString, sentAddresses ?? []).length > 0) { setShowAddressReview(true) } else { setShowChangeTips({ ...showChangeTips, contactImportCaches: undefined, previousInputType: undefined, confirmCallBack: undefined, }) onClickSend() } }, }} /> )}
    ) }, ) ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DeFiWrap/DeFiStackOneSideWrap.tsx ================================================ import { DeFiSideCalcData, EmptyValueTag, getValuePrecisionThousand, HelpIcon, IBData, Info2Icon, L1L2_NAME_DEFINED, MapChainId, myLog, TradeBtnStatus, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { DeFiSideType, DeFiSideWrapProps } from './Interface' import { Trans, useTranslation } from 'react-i18next' import React from 'react' import { Box, Grid, Tooltip, Typography } from '@mui/material' import { InputCoin, Button } from '../../../basic-lib' import { ButtonStyle } from '../Styled' import * as sdk from '@loopring-web/loopring-sdk' import moment from 'moment' import styled from '@emotion/styled' import { useSettings } from '../../../../stores' const GridStyle = styled(Grid)` input::placeholder { font-size: ${({ theme }) => theme.fontDefault.h5}; } .coinInput-wrap { background-color: var(--color-global-bg); border: 1px solid var(--color-border); } ` export const DeFiSideDetail = ({ // stakeViewInfo, tokenSell, order, onRedeem, }: DeFiSideType) => { const { t } = useTranslation() // myLog( // moment(new Date(order.stakeAt ?? "")) // .utc() // .startOf("days") // .toString() // ); const diff = moment(Date.now()).diff( moment(new Date(order.stakeAt ?? '')) .utc() .startOf('days'), 'days', false, ) return ( Amount {order.remainAmount && order.remainAmount != '0' ? getValuePrecisionThousand( sdk.toBig(order.remainAmount).div('1e' + tokenSell.decimals), tokenSell.precision, tokenSell.precision, tokenSell.precision, false, { floor: false, isAbbreviate: true }, ) + ' ' + tokenSell.symbol : EmptyValueTag} {t('labelDeFiSideProduct')} {order?.productId ?? EmptyValueTag} {t('labelDeFiSidePoolShare')} {order?.remainAmount && order?.staked && order.staked != '0' ? getValuePrecisionThousand( sdk.toBig(order.remainAmount).div(order.staked).times(100), 2, 2, undefined, ) + '%' : EmptyValueTag} APR {order.apr && order.apr !== '0.00' ? order.apr + '%' : EmptyValueTag} {' '} Cumulative Earnings {order.totalRewards && order.totalRewards != '0' ? getValuePrecisionThousand( sdk.toBig(order.totalRewards).div('1e' + tokenSell.decimals), tokenSell.precision, tokenSell.precision, undefined, false, { floor: false, isAbbreviate: true }, ) + ' ' + tokenSell.symbol : EmptyValueTag} Previous Day's Earnings {order.lastDayPendingRewards && order.lastDayPendingRewards != '0' ? getValuePrecisionThousand( sdk.toBig(order.lastDayPendingRewards).div('1e' + tokenSell.decimals), tokenSell.precision, tokenSell.precision, undefined, false, { floor: false, isAbbreviate: true }, ) + ' ' + tokenSell.symbol : EmptyValueTag} Lock duration to claim reward {'≥ ' + (order.claimableTime - order.stakeAt) / 86400000 + ' ' + t('labelDay')} {' '} Subscribe Time {moment(new Date(order.stakeAt)) // .utc() // .startOf("days") .format(YEAR_DAY_MINUTE_FORMAT)} Holding Time {diff ? diff + ' ' + t('labelDays') : '< 1' + ' ' + t('labelDays')} ) } export const DeFiSideWrap = , I, ACD extends DeFiSideCalcData>({ disabled, isJoin, btnInfo, onSubmitClick, switchStobEvent, onChangeEvent, handleError, deFiSideCalcData, accStatus, tokenSell, isLoading, btnStatus, tokenSellProps, minSellAmount, maxSellAmount, setShowLRCStakePopup, ...rest }: DeFiSideWrapProps) => { // @ts-ignore const coinSellRef = React.useRef() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { t } = useTranslation() const getDisabled = React.useMemo(() => { return disabled || deFiSideCalcData === undefined }, [btnStatus, deFiSideCalcData, disabled]) const handleCountChange = React.useCallback( (ibData: T, _name: string, _ref: any) => { if (deFiSideCalcData.coinSell.tradeValue !== ibData.tradeValue) { myLog('defi handleCountChange', _name, ibData) onChangeEvent({ tradeData: { ...ibData }, }) } }, [deFiSideCalcData, onChangeEvent], ) const propsSell = { label: t('tokenEnterPaymentToken'), subLabel: t('tokenMax'), emptyText: t('tokenSelectToken'), placeholderText: minSellAmount && maxSellAmount ? t('labelInvestMaxDefi', { minValue: getValuePrecisionThousand( minSellAmount, tokenSell.precision, tokenSell.precision, tokenSell.precision, false, { floor: false, isAbbreviate: true }, ), maxValue: getValuePrecisionThousand( maxSellAmount, tokenSell.precision, tokenSell.precision, tokenSell.precision, false, { floor: false, isAbbreviate: true }, ), }) : // // // '0.00', isShowCoinInfo: true, isShowCoinIcon: true, maxAllow: true, ...tokenSellProps, handleError: (data: any) => { if ( data.tradeValue && (data.tradeValue > data.balance || sdk.toBig(data.tradeValue).gt(maxSellAmount) || sdk.toBig(data.tradeValue).lt(minSellAmount)) ) { return { error: true, } } return { error: false, } }, handleCountChange: handleCountChange as any, ...rest, } as any const label = React.useMemo(() => { if (btnInfo?.label) { const key = btnInfo?.label.split('|') return t( key[0], key && key[1] ? { arg: key[1], layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return isJoin ? t(`labelInvestBtn`, { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) : t(`labelRedeemBtn`, { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [isJoin, t, btnInfo]) const daysDuration = Math.ceil( Number(deFiSideCalcData?.stakeViewInfo?.rewardPeriod ?? 0) / 86400000, ) let dalyEarn = deFiSideCalcData?.stakeViewInfo?.dalyEarn ? getValuePrecisionThousand( sdk .toBig(deFiSideCalcData.stakeViewInfo.dalyEarn) .div('1e' + tokenSell.decimals) .div(100), tokenSell.precision, tokenSell.precision, tokenSell.precision, false, ) : undefined dalyEarn = dalyEarn && dalyEarn !== '0' ? dalyEarn + ' ' + tokenSell.symbol : EmptyValueTag myLog('deFiSideCalcData.stakeViewInfo', deFiSideCalcData.stakeViewInfo) return ( {t('labelInvestLRCTitle')} { setShowLRCStakePopup({ isShow: true, confirmationNeeded: false }) }} /> ref={coinSellRef} disabled={getDisabled} {...{ ...propsSell, name: 'coinSell', isHideError: true, order: 'right', inputData: deFiSideCalcData ? deFiSideCalcData.coinSell : ({} as any), coinMap: {}, coinPrecision: tokenSell.precision, }} label={Amount} /> <> {/* Your assets for investment will be locked until your redemption. */} APR {deFiSideCalcData?.stakeViewInfo?.apr && deFiSideCalcData?.stakeViewInfo?.apr !== '0.00' ? deFiSideCalcData.stakeViewInfo.apr + '%' : EmptyValueTag} Earn {dalyEarn} Duration {daysDuration ? '≥ ' + daysDuration + ' ' + t('labelDay') : EmptyValueTag} {/* The staked LRC is locked in Loopring L2 and won't be able to used for other purpose although it can be redeemed any time; while if the staking is redeemed before 90 days, the accumulated reward will be dismissed. */} { onSubmitClick() }} loading={!getDisabled && btnStatus === TradeBtnStatus.LOADING ? 'true' : 'false'} disabled={ getDisabled || btnStatus === TradeBtnStatus.LOADING || btnStatus === TradeBtnStatus.DISABLED } > {label} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DeFiWrap/DeFiStackRedeemWrap.tsx ================================================ import { CheckBoxIcon, CheckedIcon, DeFiSideRedeemCalcData, EmptyValueTag, getValuePrecisionThousand, IBData, L1L2_NAME_DEFINED, MapChainId, myLog, TradeBtnStatus, } from '@loopring-web/common-resources' import { DeFiStakeRedeemWrapProps } from './Interface' import { Trans, useTranslation } from 'react-i18next' import React from 'react' import { Checkbox, FormControlLabel as MuiFormControlLabel, Grid, Typography } from '@mui/material' import { InputCoin } from '../../../basic-lib' import { ButtonStyle } from '../Styled' import * as sdk from '@loopring-web/loopring-sdk' import styled from '@emotion/styled' import { useSettings } from '../../../../stores' const GridStyle = styled(Grid)` input::placeholder { font-size: ${({ theme }) => theme.fontDefault.h5}; } div { textarea, .coinInput-wrap, .btnInput-wrap, .MuiOutlinedInput-root { background: var(--field-opacity); border-color: var(--opacity); :hover { border-color: var(--color-border-hover); } } } ` export const DeFiStackRedeemWrap = , I, ACD extends DeFiSideRedeemCalcData>({ disabled, isJoin, btnInfo, onSubmitClick, switchStobEvent, onChangeEvent, handleError, deFiSideRedeemCalcData, accStatus, tokenSell, isLoading, btnStatus, minSellAmount, maxSellAmount, isFullTime, ...rest }: DeFiStakeRedeemWrapProps) => { // @ts-ignore const coinSellRef = React.useRef() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { t } = useTranslation() const [agree, setAgree] = React.useState(!isJoin && !isFullTime ? false : true) const getDisabled = React.useMemo(() => { return disabled || deFiSideRedeemCalcData === undefined }, [btnStatus, deFiSideRedeemCalcData, disabled]) const handleCountChange = React.useCallback( (ibData: T, _name: string, _ref: any) => { if (deFiSideRedeemCalcData.coinSell.tradeValue !== ibData.tradeValue) { myLog('defi handleCountChange', _name, ibData) onChangeEvent({ tradeData: { ...ibData }, }) } }, [deFiSideRedeemCalcData, onChangeEvent], ) const propsSell = { label: t('tokenEnterPaymentToken'), subLabel: t('tokenMax'), emptyText: t('tokenSelectToken'), placeholderText: '0.00', isShowCoinInfo: true, isShowCoinIcon: true, maxAllow: true, isHideError: false, handleError: (data: any) => { const value = sdk.toBig(data.balance).minus(data.tradeValue) if (data.tradeValue && data.tradeValue > data.balance) { return { error: true, message: t('tokenNotEnough', { belong: data.belong }), } } else if (value.lt(minSellAmount) && !value.eq(0)) { return { error: true, message: t('labelRemainingAmount', { symbol: getValuePrecisionThousand( minSellAmount, tokenSell.precision, tokenSell.precision, tokenSell.precision, false, ) + ' ' + deFiSideRedeemCalcData.coinSell.belong, }), } } else if (data.tradeValue && sdk.toBig(data.tradeValue).lt(minSellAmount)) { return { error: true, } } return { error: false, } }, handleCountChange: handleCountChange as any, ...rest, } as any const label = React.useMemo(() => { if (btnInfo?.label) { const key = btnInfo?.label.split('|') return t( key[0], key && key[1] ? { arg: key[1], loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(`labelRedeemBtn`, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [t, btnInfo, network]) const { currentTotalEarnings, remainingEarn, forfeitedEarn, forfeitedEarnColor } = React.useMemo(() => { const { remainAmount, totalRewards } = deFiSideRedeemCalcData.stakeViewInfo const tradeVol = sdk .toBig(deFiSideRedeemCalcData.coinSell.tradeValue ?? 0) .times('1e' + tokenSell.decimals) const rateEarn = tradeVol.gt(0) ? tradeVol.div(remainAmount).times(totalRewards) : sdk.toBig(remainAmount).div(remainAmount).times(totalRewards) // .toBig(remainAmount) // .div(remainAmount) return { currentTotalEarnings: totalRewards !== '0' ? getValuePrecisionThousand( sdk.toBig(totalRewards).div('1e' + tokenSell.decimals), tokenSell.precision, tokenSell.precision, undefined, false, ) + ' ' + deFiSideRedeemCalcData.coinSell.belong : EmptyValueTag, ...(tradeVol.gt(remainAmount) || deFiSideRedeemCalcData.coinSell.tradeValue == undefined ? { forfeitedEarn: EmptyValueTag, forfeitedEarnColor: 'var(--color-text-primary)', } : { forfeitedEarnColor: 'var(--color-error)', forfeitedEarn: rateEarn.gt(0) ? '-' + getValuePrecisionThousand( rateEarn.div('1e' + tokenSell.decimals), tokenSell.precision, tokenSell.precision, undefined, false, ) + ' ' + deFiSideRedeemCalcData.coinSell.belong : EmptyValueTag, }), remainingEarn: tradeVol.lte(remainAmount) && rateEarn.gt(0) && sdk.toBig(totalRewards).minus(rateEarn).gt(0) ? getValuePrecisionThousand( sdk .toBig(totalRewards) .minus(rateEarn) .div('1e' + tokenSell.decimals), tokenSell.precision, tokenSell.precision, undefined, false, ) + ' ' + deFiSideRedeemCalcData.coinSell.belong : EmptyValueTag, } }, [deFiSideRedeemCalcData.stakeViewInfo, deFiSideRedeemCalcData.coinSell.tradeValue]) myLog( 'deFiSideRedeemCalcData.stakeViewInfo', deFiSideRedeemCalcData.stakeViewInfo, deFiSideRedeemCalcData.coinSell, ) return ( {t('labelInvestLRCTitle')} ref={coinSellRef} disabled={getDisabled} {...{ ...propsSell, name: 'coinSell', order: 'right', inputData: deFiSideRedeemCalcData ? deFiSideRedeemCalcData.coinSell : ({} as any), coinMap: {}, coinPrecision: tokenSell.precision, }} /> {isFullTime ? ( <> {t('labelLRCStakeProduct')} {(deFiSideRedeemCalcData?.stakeViewInfo as any)?.productId} The staked LRC is locked in Loopring L2 and won't be able to used for other purpose although it can be redeemed any time; while if the staking is redeemed before 90 days, the accumulated reward will be dismissed. ) : ( <> Current Total Earnings {currentTotalEarnings} Forfeited Reward {forfeitedEarn} Remaining Earnings {remainingEarn} {t('labelLRCStakeProduct')} {(deFiSideRedeemCalcData?.stakeViewInfo as any)?.productId} { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={ {t('labelLRCStakeRedeemAgree')} } /> )} { onSubmitClick() }} loading={!getDisabled && btnStatus === TradeBtnStatus.LOADING ? 'true' : 'false'} disabled={ getDisabled || btnStatus === TradeBtnStatus.LOADING || btnStatus === TradeBtnStatus.DISABLED || !agree } > {label} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DeFiWrap/Interface.ts ================================================ import { BtnInfo, InputButtonProps } from '../../../basic-lib' import { AccountStatus, CoinInfo, DeFiChgType, MarketType, TradeBtnStatus, } from '@loopring-web/common-resources' import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { RawDataDefiSideStakingItem } from '../../../tableList' export type DeFiChgData = { type: DeFiChgType tradeData?: undefined | T } export type DeFiWrapProps = { isStoB?: boolean isJoin: boolean disabled?: boolean btnInfo?: BtnInfo refreshRef: React.Ref onRefreshData?: (shouldFeeUpdate?: boolean, clearTrade?: boolean) => void isLoading: boolean market: MarketType maxBuyVol?: string maxSellVol?: string confirmShowLimitBalance: boolean // btnStatus: keyof typeof TradeBtnStatus | undefined; onSubmitClick: () => void onConfirm: () => void switchStobEvent?: (_isStoB: boolean) => void onChangeEvent: (data: DeFiChgData) => void handleError?: (data: T) => void tokenSellProps?: Partial>> tokenBuyProps?: Partial>> deFiCalcData: ACD tokenSell: sdk.TokenInfo tokenBuy: sdk.TokenInfo btnStatus?: keyof typeof TradeBtnStatus | undefined accStatus?: AccountStatus type: string title: string isLeverageETH?: boolean apr?: string setShowRETHStakePopup?: (props: { isShow: boolean; [key: string]: any }) => void setShowWSTETHStakePopup?: (props: { isShow: boolean; [key: string]: any }) => void setShowLeverageETHPopup?: (props: { isShow: boolean; [key: string]: any }) => void onAprDetail: () => void } export type DeFiSideType = { tokenSell: sdk.TokenInfo order: R onRedeem: (item: R) => void } export type DeFiSideWrapProps = { isJoin: true disabled?: boolean btnInfo?: BtnInfo isLoading: boolean minSellAmount: string maxSellAmount: string onSubmitClick: () => void switchStobEvent?: (_isStoB: boolean) => void onChangeEvent: (data: { tradeData?: undefined | T }) => void handleError?: (data: T) => void tokenSellProps?: Partial>> deFiSideCalcData: ACD tokenSell: sdk.TokenInfo btnStatus?: keyof typeof TradeBtnStatus | undefined accStatus?: AccountStatus } export type DeFiStakeRedeemWrapProps = { isJoin: false isFullTime: boolean disabled?: boolean btnInfo?: BtnInfo isLoading: boolean minSellAmount: string maxSellAmount: string onSubmitClick: () => void switchStobEvent?: (_isStoB: boolean) => void onChangeEvent: (data: { tradeData?: undefined | T }) => void handleError?: (data: T) => void deFiSideRedeemCalcData: ACD tokenSell: sdk.TokenInfo btnStatus?: keyof typeof TradeBtnStatus | undefined accStatus?: AccountStatus } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DeFiWrap/deFiWrap.tsx ================================================ import { BackIcon, DeFiCalcData, DeFiChgType, EmptyValueTag, getValuePrecisionThousand, HelpIcon, IBData, Info2Icon, L1L2_NAME_DEFINED, MapChainId, myLog, OrderListIcon, RecordTabIndex, ReverseIcon, RouterPath, TokenType, TradeBtnStatus, UpColor, } from '@loopring-web/common-resources' import { DeFiWrapProps } from './Interface' import { Trans, useTranslation } from 'react-i18next' import React from 'react' import { Box, Divider, Grid, Tooltip, Typography } from '@mui/material' import { Button, InputCoin } from '../../../basic-lib' import { ButtonStyle, IconButtonStyled } from '../Styled' import { CountDownIcon } from '../tool/Refresh' import { useHistory } from 'react-router-dom' import BigNumber from 'bignumber.js' import styled from '@emotion/styled' import { useSettings } from '../../../../stores' import { useTheme } from '@emotion/react' import { CoinIcons } from '../../../tableList' const BoxStyle = styled(Box)` ul { list-style: disc; padding-left: ${({ theme }) => theme.unit}px; } ` const InputCoinStyled = styled(InputCoin)` &&& .coinInput-wrap { background-color: var(--color-global-bg); border: 1px solid var(--color-border); } ` export const DeFiWrap = , I, ACD extends DeFiCalcData>({ disabled, isJoin, isStoB, btnInfo, refreshRef, onRefreshData, onSubmitClick, onConfirm, type, confirmShowLimitBalance, switchStobEvent, onChangeEvent, handleError, deFiCalcData, accStatus, tokenSell, tokenBuy, isLoading, btnStatus, tokenSellProps, tokenBuyProps, maxSellVol, maxBuyVol, market, title, setShowRETHStakePopup, setShowWSTETHStakePopup, setShowLeverageETHPopup, isLeverageETH, apr, onAprDetail, ...rest }: DeFiWrapProps) => { // @ts-ignore const [, baseSymbol, _quoteSymbol] = market.match(/(\w+)-(\w+)/i) const coinSellRef = React.useRef() const { t } = useTranslation() const theme = useTheme() const { defaultNetwork, coinJson } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const history = useHistory() const [_isStoB, setIsStoB] = React.useState(isStoB ?? true) const { upColor } = useSettings() const colorRight = upColor === UpColor.green ? ['var(--color-success)', 'var(--color-error)'] : ['var(--color-error)', 'var(--color-success)'] const showVal = deFiCalcData.coinSell?.belong && deFiCalcData.coinBuy?.belong && deFiCalcData?.AtoB const convertStr = React.useMemo(() => { return deFiCalcData.coinSell && deFiCalcData.coinBuy ? _isStoB ? `1 ${deFiCalcData.coinSell.belong} \u2248 ${ // @ts-ignore // eslint-disable-next-line eqeqeq deFiCalcData?.AtoB && deFiCalcData?.AtoB !== 'NaN' ? getValuePrecisionThousand( deFiCalcData?.AtoB, tokenBuy.precision, tokenBuy.precision, tokenBuy.precision, false, { floor: true }, ) : EmptyValueTag } ${deFiCalcData.coinBuy.belong}` : `1 ${deFiCalcData.coinBuy.belong} \u2248 ${ // @ts-ignore deFiCalcData.BtoA && deFiCalcData?.BtoA !== 'NaN' ? getValuePrecisionThousand( deFiCalcData?.BtoA, tokenSell.precision, tokenSell.precision, tokenSell.precision, false, { floor: true }, ) : EmptyValueTag } ${deFiCalcData.coinSell.belong}` : t('labelCalculating') }, [ deFiCalcData?.AtoB, deFiCalcData.BtoA, deFiCalcData.coinBuy, deFiCalcData.coinSell, _isStoB, t, ]) const getDisabled = React.useMemo(() => { return disabled || deFiCalcData === undefined || deFiCalcData.AtoB === undefined }, [btnStatus, deFiCalcData, disabled]) const handleCountChange = React.useCallback( (ibData: T, _name: string, _ref: any) => { const focus: DeFiChgType = _ref?.current === coinSellRef.current ? DeFiChgType.coinSell : DeFiChgType.coinBuy if (deFiCalcData[focus].tradeValue !== ibData.tradeValue) { myLog('defi handleCountChange', _name, ibData) onChangeEvent({ tradeData: { ...ibData }, type: focus, }) } }, [deFiCalcData, onChangeEvent], ) const propsSell = { label: t('labelETHStakingEnterPaymentToken'), subLabel: t('tokenMax'), emptyText: t('tokenSelectToken'), placeholderText: '0.00', isShowCoinInfo: true, isShowCoinIcon: true, maxAllow: true, ...tokenSellProps, handleError: handleError as any, handleCountChange: handleCountChange as any, ...rest, } as any const label = React.useMemo(() => { if (btnInfo?.label) { const key = btnInfo?.label.split('|') return t( key[0], key && key[1] ? { arg: key[1], loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return isJoin ? t(`labelInvestBtn`, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) : t(`labelRedeemBtn`, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [isJoin, t, btnInfo]) const maxValue = tokenBuy && tokenBuy.symbol && maxBuyVol && `${getValuePrecisionThousand( new BigNumber(maxBuyVol ?? 0).div('1e' + tokenBuy.decimals), tokenBuy.precision, tokenBuy.precision, tokenBuy.precision, false, { floor: true }, )} ${tokenBuy.symbol}` const fee = deFiCalcData?.fee && deFiCalcData.fee !== '0' ? deFiCalcData?.fee + ` ${tokenBuy.symbol}` : EmptyValueTag return ( {title} { if (isLeverageETH) { setShowLeverageETHPopup && setShowLeverageETHPopup({ isShow: true, confirmationNeeded: false }) } else if (market === 'RETH-ETH') { setShowRETHStakePopup && setShowRETHStakePopup({ isShow: true, confirmationNeeded: false }) } else if (market === 'WSTETH-ETH') { setShowWSTETHStakePopup && setShowWSTETHStakePopup({ isShow: true, confirmationNeeded: false }) } }} /> { if (isLeverageETH) { history.push(`${RouterPath.l2records}/leverageETHRecords`) } else { history.push( `${RouterPath.l2records}/${RecordTabIndex.DefiRecords}?market=${market}`, ) } }} sx={{ backgroundColor: 'var(--field-opacity)' }} className={'switch outlined'} aria-label='to Transaction' size={'large'} > {t('labelReceiveToken')} {deFiCalcData?.coinBuy?.tradeValue ? ( <> {deFiCalcData.coinBuy.tradeValue + ' ' + deFiCalcData.coinBuy.belong} ) : ( EmptyValueTag )} {isJoin && ( {t('labelAPR')} )} {t('labelDefiRate')} {showVal ? convertStr : t('labelCalculating')} setIsStoB(!_isStoB)} > {isJoin && ( {t('labelDefiDuration')} {t('labelFlexible')} )} {t('labelTradingFee')} {fee} { onSubmitClick() }} loading={!getDisabled && btnStatus === TradeBtnStatus.LOADING ? 'true' : 'false'} disabled={ getDisabled || btnStatus === TradeBtnStatus.LOADING || btnStatus === TradeBtnStatus.DISABLED } > {label} {confirmShowLimitBalance && ( {isJoin ? ( The quota is almost sold out and can't fulfil your complete order. You can only subscribe {{ maxValue }} now. Loopring will setup the pool soon, please revisit for subscription later. ) : ( Loopring rebalance pool can't satisfy your complete request. You can only redeem{' '} {{ maxValue }} now. For the remaining investment, you can choose one of the approaches
      {isLeverageETH ? ( }} tOptions={{ symbol: baseSymbol, type, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }} >
    • Wait some time for Loopring to seto for redeem
    • ) : ( }} tOptions={{ symbol: baseSymbol, type, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }} >
    • Withdraw wstETH to L1 and trade through CRV or LIDO directly
    • Wait some time for Loopring to seto for redeem
    • )}
    )}
    )}
    ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DeFiWrap/index.tsx ================================================ export * from './Interface' export * from './deFiWrap' export * from './DeFiStackOneSideWrap' export * from './DeFiStackRedeemWrap' ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DeployNFTWrap.tsx ================================================ import { FeeInfo, TradeBtnStatus, TradeNFT } from '@loopring-web/common-resources' import { NFTDeployViewProps } from './Interface' import { useTranslation } from 'react-i18next' import React from 'react' import { Box, Grid, Link, Toolbar, Typography } from '@mui/material' import { Button, ModalBackButton } from '../../basic-lib' import { FeeSelect } from '../../../components/modal' export const DeployNFTWrap = < T extends TradeNFT & { broker: string }, I, C extends FeeInfo, >({ tradeData, title, btnInfo, nftDeployBtnStatus, onNFTDeployClick, chargeFeeTokenList = [], feeInfo, disabled, isFeeNotEnough, handleFeeChange, onBack, assetsData = [], }: NFTDeployViewProps) => { const { t } = useTranslation(['common']) const [showFeeModal, setShowFeeModal] = React.useState(false) const getDisabled = React.useMemo(() => { if (disabled || nftDeployBtnStatus === TradeBtnStatus.DISABLED) { return true } else { return false } }, [nftDeployBtnStatus, disabled]) const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } // @ts-ignore return ( {!!onBack && ( )} {title ? title : t('nftDeployTitle')} {t('labelNFTDetail')} {/**/} {/* */} {/* {t("labelNFTName")}*/} {/* */} {/* */} {/* {tradeData?.name}*/} {/* */} {/**/} {/**/} {/* */} {/* {t("labelNFTID")}*/} {/* */} {/* */} {/* {tradeData?.nftIdView ?? ""}*/} {/* */} {/**/} {t('labelNFTTYPE')} {tradeData.nftType} {t('labelNFTContractAddress')} {tradeData.tokenAddress} {t('labelNFTDeployBroker')} {tradeData.broker} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DepositConfirm.tsx ================================================ import { EmptyValueTag, IBData, TOAST_TIME } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import React from 'react' import { Box, Grid, Typography } from '@mui/material' import { Button, DepositTitle, Toast, ToastType, useSettings } from '../../../index' import { DepositViewProps } from './Interface' export const DepositConfirm = < T extends { toAddress?: string addressError?: { error: boolean; message?: string } } & IBData, I, >({ t, tradeData, onDepositClick, handleConfirm, title, lastFailed, realToAddress, }: // ...rest DepositViewProps & { handleConfirm: (index: number) => void } & WithTranslation) => { const { isMobile } = useSettings() const [open, setOpen] = React.useState(false) return ( {t('labelL2toL2TokenAmount')} {tradeData?.tradeValue} {tradeData?.belong} {t('labelDepositTo')} {realToAddress} {lastFailed && ( {t('labelConfirmAgainByFailedWithBalance', { symbol: ` ${tradeData?.belong}` ?? EmptyValueTag, count: tradeData?.balance, })} )} { setOpen(false) }} severity={ToastType.error} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DepositNFTWrap.tsx ================================================ import { CloseIcon, LoadingIcon, myLog, SoursURL, TradeBtnStatus, TradeNFT, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import React from 'react' import { Box, Grid, Typography } from '@mui/material' import { EmptyDefault, IconClearStyled, InputSize, TGItemData, ToggleButtonGroup } from '../../' import { Button, TextField, useSettings } from '../../../index' import { NFTDepositViewProps } from './Interface' import { NFTInput } from './BasicANFTTrade' import { NFTType } from '@loopring-web/loopring-sdk' import styled from '@emotion/styled' const GridStyle = styled(Grid)` .coinInput-wrap { .input-wrap { //background: var(--field-opacity); border-radius: ${({ theme }) => theme.unit / 2}px; border: 1px solid var(--color-border); } } .MuiInputLabel-root { font-size: ${({ theme }) => theme.fontDefault.body2}; } ` as typeof Grid const NFT_TYPE: TGItemData[] = [ { value: NFTType.ERC1155, key: 'ERC1155', label: 'ERC1155', disabled: false, }, { value: NFTType.ERC721, key: 'ERC721', label: 'ERC721', // after 18n disabled: false, }, ] export const DepositNFTWrap = , I>({ disabled, walletMap, tradeData, getIPFSString, btnInfo, baseURL = `https://${process.env.REACT_APP_API_URL}`, handleOnNFTDataChange, nftDepositBtnStatus, isNFTCheckLoading, onNFTDepositClick, }: // wait = globalSetup.wait, NFTDepositViewProps) => { const { t } = useTranslation(['common']) const inputBtnRef = React.useRef() const getDisabled = React.useMemo(() => { return disabled || nftDepositBtnStatus === TradeBtnStatus.DISABLED }, [nftDepositBtnStatus, disabled]) React.useMemo(() => { return disabled || nftDepositBtnStatus === TradeBtnStatus.DISABLED }, [nftDepositBtnStatus, disabled]) myLog(getDisabled, 'getDisabled') const { isMobile } = useSettings() // const styles = isMobile // ? { flex: 1, width: "var(--swap-box-width)" } // : { width: "var(--modal-width)" }; // @ts-ignore return ( {'ipfs'} {tradeData.image ? ( {'NFT'} ) : isNFTCheckLoading ? ( ) : ( ( {t('labelNoContent')} )} /> )} handleOnNFTDataChange({ tokenAddress: event.target?.value, } as T) } fullWidth={true} /> {tradeData.tokenAddress && tradeData.tokenAddress !== '' ? ( isNFTCheckLoading ? ( ) : ( handleOnNFTDataChange({ tokenAddress: '' } as T)} > ) ) : ( '' )} handleOnNFTDataChange({ nftIdView: event.target?.value, nftId: '', } as T) } helperText={ isMobile ? ( <> ) : ( {tradeData.nftId} ) } fullWidth={true} /> {tradeData.nftIdView && tradeData.nftIdView !== '' ? ( isNFTCheckLoading ? ( ) : ( handleOnNFTDataChange({ nftIdView: '', nftId: '', } as T) } > ) ) : ( '' )} {t('labelNFTType')} { handleOnNFTDataChange({ nftType: value } as T) }} size={'medium'} /> {isNFTCheckLoading ? ( ) : ( handleOnNFTDataChange({ tradeValue: data.tradeData?.tradeValue ?? '0', } as T) } tradeData={ { ...tradeData, belong: tradeData?.tokenAddress ?? undefined, } as any } walletMap={walletMap} /> )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DepositWrap.tsx ================================================ import { AccountStatus, AddressError, Bridge, CloseIcon, globalSetup, IBData, L1L2_NAME_DEFINED, LoadingIcon, MapChainId, SoursURL, TRADE_TYPE, TradeBtnStatus, FailedIcon } from '@loopring-web/common-resources' import { Trans, WithTranslation } from 'react-i18next' import React from 'react' import { Box, Grid, Link, Typography } from '@mui/material' import { Button, DepositTitle, GridWrapStyle, IconClearStyled, TextField, useSettings, } from '../../../index' import { DepositViewProps } from './Interface' import { BasicACoinTrade } from './BasicACoinTrade' import * as sdk from '@loopring-web/loopring-sdk' export const DepositWrap = < T extends { accountReady?: AccountStatus toAddress?: string addressError?: { error: boolean; message?: string } } & IBData, I, >({ t, disabled, walletMap, tradeData, coinMap, title, isHideDes, description, btnInfo, depositBtnStatus, accountReady, onDepositClick, isNewAccount, handleError, addressDefault, chargeFeeTokenList, onChangeEvent, handleClear, handleConfirm, isAllowInputToAddress, toIsAddressCheckLoading, // toIsLoopringAddress, realToAddress, isToAddressEditable, toAddressStatus, wait = globalSetup.wait, allowTrade, toAddress, handleAddressChange, isLoopringSmartWallet, onClose, ...rest }: DepositViewProps & { accountReady?: AccountStatus handleConfirm: (index: number) => void } & WithTranslation) => { const inputBtnRef = React.useRef() let { feeChargeOrder, isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [minFee, setMinFee] = React.useState<{ minFee: string } | undefined>(undefined) const getDisabled = React.useMemo(() => { return disabled || depositBtnStatus === TradeBtnStatus.DISABLED }, [depositBtnStatus, disabled]) const inputButtonDefaultProps = { label: [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) ? t('depositLabelEnterTokenEarn') : t('depositLabelEnterToken'), } const isNewAlert = React.useMemo(() => { if (isNewAccount && chargeFeeTokenList && tradeData && tradeData.belong) { const index = chargeFeeTokenList?.findIndex(({ belong }) => belong === tradeData.belong) if (index === -1) { setMinFee(undefined) return ( {t('labelIsNotFeeToken', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, symbols: chargeFeeTokenList.slice(0, chargeFeeTokenList.length - 1).map((item) => item.belong ?? '').join(', '), lastSymbol: chargeFeeTokenList[chargeFeeTokenList.length - 1].belong, })} ) } const Max = sdk .toBig(chargeFeeTokenList[index].fee.toString().replaceAll(sdk.SEP, '')) .times(4) setMinFee({ minFee: t('labelMinFeeForActive', { symbol: tradeData.belong.toString(), fee: Max.toString(), }), }) if (!tradeData?.tradeValue || Max.lte(tradeData.tradeValue ?? 0)) { return <> } else { return ( <> {t('labelIsNotEnoughFeeToken', { symbol: tradeData.belong, fee: Max.toString(), })} ) } } else if (isNewAccount) { setMinFee(undefined) return ( // // // // {t('labelIsNotFeeToken', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, symbols: chargeFeeTokenList?.slice(0, chargeFeeTokenList.length - 1).map((item) => item.belong ?? '').join(', '), lastSymbol: chargeFeeTokenList ? chargeFeeTokenList[chargeFeeTokenList.length - 1].belong : '', })} ) } else { return <> } }, [isNewAccount, chargeFeeTokenList, tradeData, t, feeChargeOrder]) if (isNewAccount && isLoopringSmartWallet) { return {t("labelSmartWalletDepositError1")}
    {t("labelSmartWalletDepositError2")}
    {t("labelSmartWalletDepositError3")}
    } return ( {isNewAccount && <>{isNewAlert}} {isAllowInputToAddress ? ( { handleAddressChange && handleAddressChange(_event.target.value) }} fullWidth={true} /> {toAddress ? ( toIsAddressCheckLoading ? ( ) : ( isToAddressEditable && ( { handleClear() }} > ) ) ) : ( '' )} {realToAddress && toAddressStatus !== AddressError.NoError ? ( {t('labelInvalidAddress')} ) : toAddress && realToAddress && !toIsAddressCheckLoading ? ( {realToAddress} ) : ( <> )} {!isToAddressEditable && (!realToAddress ? ( {t('labelBridgeSendTo')} {tradeData.toAddress} ) : toIsAddressCheckLoading ? ( {'loading'} ) : realToAddress && toAddressStatus !== AddressError.NoError ? ( {toAddressStatus === AddressError.ENSResolveFailed ? ( <>{t('labelENSShouldConnect')} ) : ( Invalid Wallet Address, Pay Loopring L2 of ERC20 is disabled! Click to input another receive address , )} ) : ( <> {t('labelReceiveAddress')} {realToAddress} ))} ) : ( <> {toAddress && toAddressStatus !== AddressError.NoError ? ( {toAddressStatus === AddressError.ENSResolveFailed ? ( <>{t('labelENSShouldConnect')} ) : toAddressStatus === AddressError.TimeOut ? ( { handleAddressChange && handleAddressChange(toAddress ?? '') }} rel={'noopener noreferrer'} color={'textPrimary'} /> ), }} > L1 Account checking request was rejected or some unknown error occurred, please Retry ) : ( Invalid Wallet Address, Pay Loopring L2 of ERC20 is disabled! Click to input another receive address , )} ) : ( <> )} )} {tradeData.belong === 'ETH' && ( {t('labelIsETHDepositAlert')} )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DualWrap/Interface.ts ================================================ import { BtnInfo, InputButtonProps } from '../../../basic-lib' import { AccountStatus, CoinInfo, DualCurrentPrice, DualViewBase, DualViewInfo, DualViewType, TradeBtnStatus, } from '@loopring-web/common-resources' import { TokenInfo } from '@loopring-web/loopring-sdk' import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' export enum DualDisplayMode { nonBeginnerMode = 1, beginnerModeStep1, beginnerModeStep2, } export type DualDetailType = { dualViewInfo: DualViewBase & (Partial | Partial) currentPrice: DualCurrentPrice lessEarnView: string greaterEarnView: string lessEarnTokenSymbol: string greaterEarnTokenSymbol: string isOrder?: boolean dualProducts?: DualViewInfo[] getProduct?: () => void order: any | undefined __raw__?: any } export type DualChgData = { tradeData?: undefined | T } export type DualWrapProps = { disabled?: boolean btnInfo?: BtnInfo refreshRef: React.Ref onRefreshData?: (shouldFeeUpdate?: boolean, clearTrade?: boolean) => void isLoading: boolean tokenMap: { [key: string]: TokenInfo } // maxSellVol?: string; onSubmitClick: () => void onChangeEvent: (data: DualChgData) => void handleError?: (data: T) => void tokenSellProps?: Partial>> dualCalcData: DUAL tokenSell: TokenInfo btnStatus?: keyof typeof TradeBtnStatus | undefined accStatus?: AccountStatus dualProducts?: DualViewInfo[] toggle: { enable: boolean; reason?: string | undefined } viewType?: DualViewType setShowAutoDefault: (show: boolean) => void showAutoDefault: boolean } export type DualDetailProps< R = { isRenew: boolean; renewTargetPrice?: string; renewDuration?: number }, > = DualDetailType & { coinSell: R btnConfirm?: any onChange: (props: R) => void isPriceEditable: boolean dualProducts: DualViewInfo[] getProduct?: () => void displayMode?: DualDisplayMode toggle: { enable: boolean; reason?: string } inputPart?: JSX.Element | undefined showClock?: boolean setShowAutoDefault: (show: boolean) => void onChangeOrderReinvest: ( info: { on: boolean; renewTargetPrice?: string; renewDuration?: number }, item: R, ) => void showAutoDefault: boolean } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DualWrap/ModifyParameter.tsx ================================================ import { BtnPercentage, ButtonStyle, CoinIcons, DualDetailProps, TickCardStyleItem, useSettings, } from '@loopring-web/component-lib' import React from 'react' import { Box, Divider, Grid, Tooltip, Typography } from '@mui/material' import * as sdk from '@loopring-web/loopring-sdk' import { DUAL_TYPE } from '@loopring-web/loopring-sdk' import { Mark } from '@mui/base/SliderUnstyled/SliderUnstyledProps' import { Trans, useTranslation } from 'react-i18next' import { DAY_MINUTE_FORMAT, getValuePrecisionThousand, Info2Icon, TokenType, WarningIcon2, } from '@loopring-web/common-resources' import moment from 'moment' import _ from 'lodash' import BigNumber from 'bignumber.js' export const ModifyParameter = ({ dualViewInfo, coinSell, onClose, onChange, isPriceEditable, dualProducts, btnConfirm, // getProduct, maxDuration = 10, isOrder, onChangeOrderReinvest }: DualDetailProps & { maxDuration?: number; onClose: () => void }) => { const { t } = useTranslation() const { coinJson } = useSettings() const { stepLength, // strike, currentPrice: { currentPrice, precisionForPrice, base, quote, quoteUnit }, } = dualViewInfo const stepEle = React.useMemo(() => { if (isPriceEditable) { const listELE: JSX.Element[] = [] const method = dualViewInfo.dualType === DUAL_TYPE.DUAL_BASE ? 'plus' : 'minus' // let method = sdk // .toBig(currentPrice ?? 0) // .minus(strike ?? 0) // .gte(0)? 'minus' : 'plus' let start = sdk.toBig(stepLength ?? 0).times( sdk .toBig(currentPrice ?? 0) .div(stepLength ?? 1) .toFixed(0), ) // if(strike) if (method === 'minus' && start.gt(currentPrice ?? 0)) { start = sdk.toBig(start).minus(stepLength ?? 0) } else if (method === 'plus' && start.lt(currentPrice ?? 0)) { start = sdk.toBig(start).plus(stepLength ?? 0) } const strikes = _.range(12).map((index) => method === 'minus' ? start.minus(sdk.toBig(stepLength ?? 0).times(index) ?? 0) : start.plus(sdk.toBig(stepLength ?? 0).times(index) ?? 0), ) let strikes2: BigNumber[] if ( !strikes.find((strike) => coinSell.renewTargetPrice && strike.eq(coinSell.renewTargetPrice)) ) { strikes2 = [sdk.toBig(coinSell.renewTargetPrice ?? 0), ...strikes.slice(0, 11)] } else { strikes2 = strikes } return strikes2 .sort((a, b) => a .minus(b) .times(method === 'minus' ? '-1' : '1') .toNumber(), ) .map((item, index) => { const dualProduct = dualProducts?.find((dualProduct) => sdk.toBig(dualProduct.strike).eq(item), ) const value = item.toString() return ( onChange({ ...coinSell, renewDuration: coinSell.renewDuration, renewTargetPrice: value, }) } > {value} {dualProduct && t('labelDualModifyAPR', { value: dualProduct.apy })} ) }) } else { return [] } }, [dualProducts, coinSell.renewDuration, , coinSell?.renewTargetPrice, isPriceEditable]) return ( {/* eslint-disable-next-line react/jsx-no-undef */} {t('labelDualModifyParameter')} {isPriceEditable && ( <> {t('labelDualModifySettlementDateDes')} {t('labelDualModifySettlementDate', { date: moment(new Date(dualViewInfo.expireTime)).format(DAY_MINUTE_FORMAT), interpolation: { escapeValue: false, }, })} LRC Current price: price : {stepEle} )} Modify Longest Settlement Date { onChange({ ...coinSell, renewDuration: value, }) }} anchors={ Array.from({ length: maxDuration }, (_, index) => ({ value: index + 1, label: (index + 1) % 5 == 0 || index == 0 || index == maxDuration - 1 ? t('labelDayDisplay', { item: index + 1 }) : '', })) as Mark[] } min={1} max={maxDuration} valueLabelDisplay='on' valuetext={(item) => t('labelDayDisplay', { item })} step={1} /> {btnConfirm ? ( btnConfirm ) : ( { if (isOrder) { onChangeOrderReinvest({ on: true, renewDuration: coinSell.renewDuration, renewTargetPrice: coinSell.renewTargetPrice }, coinSell); onClose() } else { onClose() } }} > {t('labelDualModifyConfirm')} )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DualWrap/ModifySetting.tsx ================================================ import { BtnPercentage, ButtonStyle, OutlineSelect, OutlineSelectItem, useSettings, } from '@loopring-web/component-lib' import { Box, Divider, FormControlLabel, Grid, Switch, Typography } from '@mui/material' import { Mark } from '@mui/base' import { Trans, useTranslation } from 'react-i18next' import { DropDownIcon, Info2Icon } from '@loopring-web/common-resources' export const ModifySetting = ({ onClose, maxDuration = 10, }: { maxDuration?: number onClose: () => void }) => { const { t } = useTranslation() const { dualAuto, setDualDefault } = useSettings() return ( {t('labelDualAutoReinvest')} Default Enable Auto Reinvest { setDualDefault({ ...dualAuto, auto: checked, }) }} control={} label={''} /> Modify Longest Settlement Date { setDualDefault({ ...dualAuto, day: e.target.value as unknown as any, }) }} > {t('labelDualLongestSettlementFixed')} {t('labelDualLongestSettlementAutomatic')} {dualAuto?.day !== 'auto' && ( { setDualDefault({ ...dualAuto, day: value, }) }} anchors={ Array.from({ length: maxDuration }, (_, index) => ({ value: index + 1, label: (index + 1) % 5 == 0 || index == 0 || index == maxDuration - 1 ? t('labelDayDisplay', { item: index + 1 }) : '', })) as Mark[] } min={1} max={maxDuration} valueLabelDisplay='on' valuetext={(item) => t('labelDayDisplay', { item })} step={1} /> )} { onClose() }} > {t('labelDualSettingConfirm')} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DualWrap/dualDetail.tsx ================================================ import { DualDetailProps, DualDisplayMode } from './Interface' import React from 'react' import { Trans, useTranslation } from 'react-i18next' import { useSettings } from '../../../../stores' import { BackIcon, DualCurrentPrice, DualViewBase, EmptyValueTag, getValuePrecisionThousand, Info2Icon, MoreIcon, myLog, SoursURL, UpColor, WarningIcon2, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { Box, Divider, Switch, FormControlLabel, Link, Modal, Tooltip, Typography, Tab, IconButton, } from '@mui/material' import { ModalCloseButton, Tabs } from '../../../basic-lib' import { ModifyParameter } from './ModifyParameter' import * as sdk from '@loopring-web/loopring-sdk' import moment from 'moment/moment' import styled from '@emotion/styled' import { SwitchPanelStyled } from '../../../styled' import { LABEL_INVESTMENT_STATUS_MAP } from '../../../tableList' import { CancelDualAlert } from '../tool' import { ModifySetting } from './ModifySetting' import { confirmation } from '@loopring-web/core' const BoxChartStyle = styled(Box)` background-clip: content-box; background-size: contain; background-repeat: no-repeat; min-height: 176px; width: 100%; &.mobile { min-height: 122px; } &.dualHL { background-image: url('${SoursURL}/images/dualHL.png'); } &.dualHH { background-image: url('${SoursURL}/images/dualHH.png'); } &.dualLL { background-image: url('${SoursURL}/images/dualLL.png'); } &.dualLH { background-image: url('${SoursURL}/images/dualLH.png'); } .point { position: absolute; display: flex; flex-direction: column; align-items: center; // top: ${({ theme }) => theme.unit}px; } .point1 { top: 50%; transform: translateY(-50%); right: ${({ theme }) => theme.unit}px; justify-content: flex-end; //left: 50%; //transform: translateX(-50%); } .point2 { top: ${({ theme }) => theme.unit}px; left: ${({ theme }) => theme.unit}px; } .returnV { position: absolute; bottom: 0; height: 50%; width: 50%; left: 50%; display: flex; align-items: center; justify-content: left; text-indent: 0; padding-left: ${({ theme }) => 2 * theme.unit}px; //text-align: left; } .returnV2 { color: var(--color-warning); } .returnV1 { color: var(--color-success); top: 0; } ` export enum DualDetailTab { less = 0, greater = 1, } export const DualDes = ({ dualViewInfo, currentPrice, isOrder, }: { currentPrice: DualCurrentPrice isOrder: boolean dualViewInfo: DualViewBase & (Partial | Partial) }) => { const { t } = useTranslation(['common', 'tables']) const { upColor } = useSettings() const { precisionForPrice, quoteUnit } = currentPrice const quoteAlice = /USD/gi.test(quoteUnit ?? '') ? 'USDT' : quoteUnit const targetView = React.useMemo(() => { return dualViewInfo?.strike ? getValuePrecisionThousand( dualViewInfo.strike, precisionForPrice, precisionForPrice, precisionForPrice, true, { floor: true }, ) : EmptyValueTag }, [dualViewInfo?.strike]) return ( {isOrder && ( <> Status {dualViewInfo?.side ?? ''} {t('labelDualAmount')} {dualViewInfo?.amount} {dualViewInfo.outSymbol && ( {t('labelDualTxsSettlement')} {dualViewInfo.outAmount + ' ' + dualViewInfo.outSymbol} )} {dualViewInfo?.deliveryPrice && ( {t('labelDualDeliver')} {dualViewInfo.deliveryPrice + ' ' + quoteAlice} )} {dualViewInfo.enterTime && ( <> {t('labelDualSubDate')} {moment(new Date(dualViewInfo.enterTime)).format(YEAR_DAY_MINUTE_FORMAT)} {t('labelDualAuto')} {dualViewInfo.autoIcon && dualViewInfo?.autoStatus ? ( <>{t(dualViewInfo?.autoContent ?? '')} <>{dualViewInfo.autoIcon} ) : ( {dualViewInfo?.autoContent ?? ''} )} )} )} APR {dualViewInfo?.apy} Target Price {targetView + ' ' + quoteAlice} {t('labelDualSettleDate')} {moment(new Date(dualViewInfo.expireTime)).format(YEAR_DAY_MINUTE_FORMAT)} {t('labelDualSettleDateDur')} {getValuePrecisionThousand( (dualViewInfo.expireTime - (isOrder && dualViewInfo.enterTime ? dualViewInfo.enterTime : Date.now())) / (1000 * 60 * 60 * 24), 1, 1, 1, true, { floor: true }, )} ) } export const DualDetail = ({ isOrder = false, displayMode = DualDisplayMode.nonBeginnerMode, isPriceEditable = true, coinSell, toggle, btnConfirm, inputPart, showClock = false, setShowAutoDefault, showAutoDefault, ...rest }: DualDetailProps) => { const { dualViewInfo, currentPrice, lessEarnView, greaterEarnView, onChange, onChangeOrderReinvest, } = rest const [showEdit, setShowEdit] = React.useState(false) const { t } = useTranslation(['common', 'tables']) const { upColor, isMobile } = useSettings() const { base, quote, precisionForPrice, quoteUnit } = currentPrice const [tab, setTab] = React.useState(DualDetailTab.less) const currentView = React.useMemo( () => base ? getValuePrecisionThousand( currentPrice.currentPrice, precisionForPrice, precisionForPrice, precisionForPrice, true, { floor: true }, ) : EmptyValueTag, [dualViewInfo.currentPrice.currentPrice, precisionForPrice], ) const quoteAlice = /USD/gi.test(quoteUnit ?? '') ? 'USDT' : quoteUnit const renewTargetPriceView = React.useMemo(() => { return coinSell?.renewTargetPrice ? getValuePrecisionThousand( coinSell?.renewTargetPrice, precisionForPrice, precisionForPrice, undefined, true, { floor: true }, ) : EmptyValueTag }, [coinSell?.renewTargetPrice]) const targetView = React.useMemo(() => { return dualViewInfo?.strike ? getValuePrecisionThousand( dualViewInfo.strike, precisionForPrice, precisionForPrice, precisionForPrice, true, { floor: true }, ) : EmptyValueTag }, [dualViewInfo?.strike]) myLog( 'dualViewInfo?.__raw__?.order?.investmentStatus', dualViewInfo?.__raw__?.order?.investmentStatus, ) const [showCancelOneAlert, setShowCancelOneAlert] = React.useState({ open: false, row: undefined as any, }) return ( <> setShowAutoDefault(false)} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description' > setShowAutoDefault(false)} t={t} /> setShowAutoDefault(false)} /> setShowEdit(false)} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description' > setShowEdit(false)} t={t} {...rest} /> setShowEdit(false)} {...rest} btnConfirm={btnConfirm} coinSell={coinSell} isPriceEditable={isPriceEditable} isOrder={isOrder} /> { onChangeOrderReinvest({ on: false }, coinSell) }} handleClose={() => setShowCancelOneAlert({ open: false, row: undefined })} /> {isOrder && showClock && ( {t('labelDualAutoSearchingDes')} )} {displayMode !== DualDisplayMode.beginnerModeStep2 && ( )} {inputPart ? <>{inputPart} : <>} {(displayMode !== DualDisplayMode.beginnerModeStep2 && toggle?.enable && !isOrder) || isOrder ? ( // RETRY_SUCCESS | RETRY_FAILED | isRecursive=false Auto Reinvest { if (isOrder) { if (coinSell.isRenew) { setShowCancelOneAlert({ open: true, row: { expireTime: dualViewInfo.__raw__?.order?.timeOrigin?.expireTime, }, }) } else { setShowEdit(true) } } else { onChange({ ...coinSell, isRenew: checked, }) } }} control={} label={''} /> setShowAutoDefault(true)}> {coinSell.isRenew && ( Auto Reinvest will try to find a new product which based on the following rule at 16:00 on the settlement day. )} {coinSell.isRenew && ( type Price {isPriceEditable ? ( { setShowEdit(true) }} component={'a'} variant={'body1'} display={'inline-flex'} alignItems={'center'} > {renewTargetPriceView + ' ' + quoteAlice} ) : ( {renewTargetPriceView + ' ' + quoteAlice} )} Duration { setShowEdit(true) }} component={'a'} variant={'body1'} display={'inline-flex'} alignItems={'center'} > {t('labelDayDisplay', { item: coinSell.renewDuration })} )} ) : ( <> )} {displayMode === DualDisplayMode.beginnerModeStep2 && ( {t('labelDualBeginnerAtSettlementDay')} {t('labelDualBeginnerIndexPriceDes')} {t('labelDualBeginnerLockingDes')} )} {displayMode !== DualDisplayMode.beginnerModeStep1 && ( <> {t('labelDualSettlementCalculator')} {/**/} {/* */} {/* {t('labelDualCurrentPrice3', {*/} {/* symbol: base,*/} {/* })}*/} {/* */} {/* */} {/* {currentView + ' ' + quoteAlice}*/} {/* */} {/**/} setTab(newVaule)} sx={{ marginBottom: 2 }} > {t( dualViewInfo.isUp ? 'labelDualNewPriceLessThan' : 'labelDualNewPriceLessOrEqualThan', { value: targetView, base: currentPrice.base, quote: quoteAlice, }, )} } /> {t( dualViewInfo.isUp ? 'labelDualNewPriceGreaterThanOrEqual' : 'labelDualNewPriceGreaterThan', { value: targetView, quote: quoteAlice, base: base, }, )} } /> {/**/} {/* {t('labelDualTargetPrice3')}*/} {/**/} {targetView + ' ' + quoteAlice} LRC Current price: price {quote && t('labelDualReturn', { symbol: (greaterEarnView === '0' ? EmptyValueTag : greaterEarnView) + ' ' + quote, })} {base && t('labelDualReturn', { symbol: (lessEarnView === '0' ? EmptyValueTag : lessEarnView) + ' ' + base, })} )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DualWrap/dualWrap.tsx ================================================ import React from 'react' import { DualCalcData, DualViewInfo, EmptyValueTag, getValuePrecisionThousand, IBData, L1L2_NAME_DEFINED, MapChainId, myLog, TradeBtnStatus, } from '@loopring-web/common-resources' import { DualDisplayMode, DualWrapProps } from './Interface' import { useTranslation } from 'react-i18next' import { Box, Grid, Typography } from '@mui/material' import { useSettings } from '../../../../stores' import { ButtonStyle } from '../Styled' import * as sdk from '@loopring-web/loopring-sdk' import { DualDetail } from './dualDetail' import { InputCoin, InputMaxCoin } from '../../../basic-lib' import BigNumber from 'bignumber.js' export const DualWrap = < T extends IBData & { isRenew: boolean; targetPrice: string; duration: string }, I, DUAL extends DualCalcData, R extends DualViewInfo, >({ refreshRef, disabled, btnInfo, isLoading, onRefreshData, onSubmitClick, onChangeEvent, tokenSellProps, dualCalcData, handleError, tokenSell, btnStatus, tokenMap, accStatus, isBeginnerMode, dualProducts, toggle, setShowAutoDefault, showAutoDefault, ...rest }: DualWrapProps & { isBeginnerMode: boolean // setConfirmDualAutoInvest: (state: boolean) => void }) => { const coinSellRef = React.useRef() const { t } = useTranslation() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const priceSymbol = dualCalcData?.dualViewInfo?.currentPrice?.quote const [displayMode, setDisplayMode] = React.useState( isBeginnerMode ? DualDisplayMode.beginnerModeStep1 : DualDisplayMode.nonBeginnerMode, ) const getDisabled = React.useMemo(() => { return disabled || dualCalcData === undefined }, [btnStatus, dualCalcData, disabled]) const handleCountChange = React.useCallback( (ibData: T, _name: string, _ref: any) => { if (dualCalcData['coinSell'].tradeValue !== ibData.tradeValue) { myLog('dual handleCountChange', _name, ibData) onChangeEvent({ tradeData: { ...dualCalcData?.coinSell, ...ibData }, }) } }, [dualCalcData, onChangeEvent], ) const propsSell = { label: t('labelTokenEnterDualToken'), subLabel: t('labelTokenMaxBalance'), emptyText: t('tokenSelectToken'), placeholderText: dualCalcData.miniSellVol ? t('labelInvestMiniDual', { value: getValuePrecisionThousand( sdk.toBig(dualCalcData.miniSellVol).div('1e' + dualCalcData.sellToken?.decimals), dualCalcData.sellToken?.precision, dualCalcData.sellToken?.precision, dualCalcData.sellToken?.precision, false, { floor: false, isAbbreviate: true }, ), }) : '0.00', maxAllow: true, noBalance: EmptyValueTag, name: 'coinSell', isHideError: true, order: 'left' as any, decimalsLimit: tokenSell?.precision, coinPrecision: tokenSell?.precision, inputData: { ...(dualCalcData ? dualCalcData.coinSell : ({} as any)), max: BigNumber.min( dualCalcData.maxSellAmount ?? 0, dualCalcData?.coinSell?.balance ?? 0, dualCalcData?.quota ?? 0, ), }, coinMap: {}, ...tokenSellProps, handleError: handleError as any, handleCountChange, isShowCoinInfo: true, isShowCoinIcon: true, // CoinIconElement: tokenSell.symbol, ...rest, } as any const label = React.useMemo(() => { if (btnInfo?.label) { const key = btnInfo?.label.split('|') return t( key[0], key && key[1] ? { arg: key[1], loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return displayMode === DualDisplayMode.beginnerModeStep1 ? t('labelContinue', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) : t(`labelInvestBtn`, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [t, btnInfo]) const lessEarnView = React.useMemo( () => dualCalcData?.lessEarnVol && tokenMap[dualCalcData.lessEarnTokenSymbol] ? getValuePrecisionThousand( sdk .toBig(dualCalcData?.lessEarnVol ?? 0) .times(sdk.toBig(1).plus(dualCalcData!.dualViewInfo.settleRatio)) .div('1e' + tokenMap[dualCalcData.lessEarnTokenSymbol].decimals), tokenMap[dualCalcData.lessEarnTokenSymbol].precision, tokenMap[dualCalcData.lessEarnTokenSymbol].precision, tokenMap[dualCalcData.lessEarnTokenSymbol].precision, false, { floor: true }, ) : EmptyValueTag, [dualCalcData.lessEarnTokenSymbol, dualCalcData.lessEarnVol, tokenMap, dualCalcData!.dualViewInfo?.settleRatio], ) const greaterEarnView = React.useMemo( () => dualCalcData?.greaterEarnVol && tokenMap[dualCalcData.greaterEarnTokenSymbol] ? getValuePrecisionThousand( sdk .toBig(dualCalcData?.greaterEarnVol) .times(sdk.toBig(1).plus(dualCalcData!.dualViewInfo.settleRatio)) .div('1e' + tokenMap[dualCalcData.greaterEarnTokenSymbol].decimals), tokenMap[dualCalcData.greaterEarnTokenSymbol].precision, tokenMap[dualCalcData.greaterEarnTokenSymbol].precision, tokenMap[dualCalcData.greaterEarnTokenSymbol].precision, false, { floor: true }, ) : EmptyValueTag, [dualCalcData.greaterEarnTokenSymbol, dualCalcData.greaterEarnVol, tokenMap, dualCalcData.dualViewInfo?.settleRatio], ) const totalQuota = React.useMemo( () => dualCalcData.quota && dualCalcData.sellToken ? getValuePrecisionThousand( dualCalcData.quota, dualCalcData.sellToken.precision, dualCalcData.sellToken.precision, dualCalcData.sellToken.precision, false, { floor: false, isAbbreviate: true }, ) : EmptyValueTag, [dualCalcData], ) // const renewDuration = // !dualCalcData?.coinSell?.renewDuration && dualCalcData?.dualViewInfo // ? calc < 1 // ? Math.ceil(calc) // : Math.floor(calc) // : dualCalcData.coinSell?.renewDuration ?? 0 const inputView = displayMode !== DualDisplayMode.beginnerModeStep2 && ( ref={coinSellRef} disabled={getDisabled} {...{ ...propsSell, }} /> {t('labelDualQuota')} {totalQuota + ' ' + dualCalcData.coinSell.belong} ) return ( {dualCalcData.dualViewInfo && priceSymbol && ( <> ref={coinSellRef} disabled={getDisabled} {...{ ...propsSell, }} /> {t('labelDualQuota')} {totalQuota + ' ' + dualCalcData.coinSell.belong} ) : undefined } onChange={(data) => { onChangeEvent({ tradeData: { ...dualCalcData?.coinSell, isRenew: data.isRenew, renewTargetPrice: data.renewTargetPrice, renewDuration: data.renewDuration, } as any, }) }} /> { if (!btnInfo?.label && displayMode === DualDisplayMode.beginnerModeStep1) { setDisplayMode(DualDisplayMode.beginnerModeStep2) } else { onSubmitClick() } }} loading={!getDisabled && btnStatus === TradeBtnStatus.LOADING ? 'true' : 'false'} disabled={ getDisabled || btnStatus === TradeBtnStatus.LOADING || btnStatus === TradeBtnStatus.DISABLED } > {label} )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/DualWrap/index.tsx ================================================ export * from './Interface' export * from './dualWrap' export * from './dualDetail' export * from './ModifySetting' ================================================ FILE: packages/component-lib/src/components/tradePanel/components/ExportAccountWrap.tsx ================================================ import React from 'react' import { WithTranslation } from 'react-i18next' import { Box, Grid, TextareaAutosize, Typography } from '@mui/material' import { Button } from '../../basic-lib' import { ExportAccountExtendProps } from './Interface' import styled from '@emotion/styled' import { copyToClipBoard, NoPhotosIcon } from '@loopring-web/common-resources' import { useSettings } from '../../../stores' const TextareaAutosizeStyled = styled(TextareaAutosize)` width: 100%; padding: ${({ theme }: any) => theme.unit * 2}px; color: var(--color-text-secondary); background: var(--color-global-bg); ` as any export const ExportAccountWrap = ({ t, setExportAccountToastOpen, ...rest }: ExportAccountExtendProps & WithTranslation) => { const [info, setInfo] = React.useState() const { exportAccountProps: { accountInfo }, } = rest const { isMobile } = useSettings() React.useEffect(() => { if (accountInfo) { try { const info = JSON.stringify(accountInfo, null, 4) setInfo(info) } finally { } } }, [accountInfo]) return ( {t('labelExportAccount')} {t('labelExportAccountNoPhotos')} {t('labelExportAccountDescription')} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/ForceWithdrawConfirm.tsx ================================================ import { WithTranslation } from 'react-i18next' import { Box, Grid, ListItem, ListItemText, Typography } from '@mui/material' import { FeeInfo, IBData, L1L2_NAME_DEFINED, MapChainId, TOAST_TIME, } from '@loopring-web/common-resources' import { Button, ForceWithdrawViewProps, Toast, ToastType } from '../../index' import { useSettings } from '../../../stores' import React from 'react' import { ListStyle } from './ForceWithdrawWrap' export const ForceWithdrawConfirm = , I, C extends FeeInfo>({ t, handleConfirm, tradeData, realAddr, onWithdrawClick, feeInfo, }: Partial> & { handleConfirm: (index: number) => void } & WithTranslation) => { const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [open, setOpen] = React.useState(false) return ( {t('labelForceWithdrawTitle')} {t('labelForceWithdrawAddress')} {realAddr} {t('labelForceWithdrawToken')} {tradeData?.balance + ' ' + tradeData?.belong} {t('labelForceWithdrawFee')} {feeInfo?.fee + ' '} {feeInfo?.belong} {t('labelForceWithdrawConfirm', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {t('labelForceWithdrawConfirm1', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} { setOpen(false) }} severity={ToastType.error} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/ForceWithdrawWrap.tsx ================================================ import { Trans, WithTranslation } from 'react-i18next' import React from 'react' import { bindHover } from 'material-ui-popup-state/es' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { Box, Grid, List, ListItem, ListItemText, Typography } from '@mui/material' import { AssetsRawDataItem, CloseIcon, DropDownIcon, FeeInfo, globalSetup, IBData, Info2Icon, L1L2_NAME_DEFINED, LoadingIcon, MapChainId, TRADE_TYPE, TradeBtnStatus, } from '@loopring-web/common-resources' import { FeeSelect, ForceWithdrawViewProps, InputButtonDefaultProps, PopoverPure } from '../..' import { Button, IconClearStyled, TextField, useSettings } from '../../../index' import { BasicACoinTrade } from './BasicACoinTrade' import styled from '@emotion/styled' export const ListStyle = styled(List)` li { height: auto; padding: 0; &:before { content: '•'; padding-top: ${({ theme }) => theme.unit}px; color: var(--color-warning); display: inline-flex; padding-right: ${({ theme }) => theme.unit}px; } display: inline-flex; &:hover { background-color: initial; } } //list-style: disc outside; //list-style: ; .MuiListItemText-root span { color: var(--color-warning); line-height: 1.2em; padding-bottom: 0; } font-size: ${({ theme }) => theme.fontDefault.body1}; ` as typeof List export const ForceWithdrawWrap = , I, C extends FeeInfo>({ t, disabled, walletMap, tradeData, coinMap, withdrawI18nKey, addressDefault, isNotAvailableAddress, isActiveAccount, isLoopringAddress = false, chargeFeeTokenList = [], feeInfo, lastFailed, // handleConfirm, isFeeNotEnough, onWithdrawClick, withdrawBtnStatus, handleFeeChange, handleOnAddressChange, isAddressCheckLoading, addrStatus, realAddr, wait = globalSetup.wait, assetsData = [], ...rest }: ForceWithdrawViewProps & WithTranslation & { assetsData: AssetsRawDataItem[] // handleConfirm: (index: number) => void; }) => { const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [showFeeModal, setShowFeeModal] = React.useState(false) const popupState = usePopupState({ variant: 'popover', popupId: `popupId-withdraw`, }) const inputBtnRef = React.useRef() const getDisabled = React.useMemo(() => { return disabled || withdrawBtnStatus === TradeBtnStatus.DISABLED }, [disabled, withdrawBtnStatus]) const inputButtonDefaultProps: InputButtonDefaultProps = { label: t('labelForceWithdrawEnterToken'), disableInputValue: true, maxAllow: false, disabled: !Object.keys(walletMap ?? {}).length, subLabel: '', } const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } // @ts-ignore return ( {t('labelForceWithdrawTitle')} If the receipt account doesn't activate the Loopring L2 account, you will be able to withdraw the token from L2 to Ethereum L1. Usually only when you sent the token to the L2 account of a wrong Btrade address that doesn't support Loopring L2, you will need to do this so that you will be able to claim the token back. {t('labelForceWithdrawConfirm', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {t('labelForceWithdrawConfirm1', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} <> 0) } placeholder={t('labelPleaseForceWithdrawAddress')} onChange={(event) => handleOnAddressChange(event?.target?.value)} label={t('labelForceWithdrawAddress')} SelectProps={{ IconComponent: DropDownIcon }} fullWidth={true} /> {addressDefault !== '' ? ( isAddressCheckLoading ? ( ) : ( handleOnAddressChange('')} > ) ) : ( '' )} {addressDefault !== '' && !isAddressCheckLoading && (walletMap === undefined || !Object.keys(walletMap).length) && (isNotAvailableAddress && realAddr === '' ? ( {t('labelInvalidAddress')} ) : isLoopringAddress && isActiveAccount ? ( {t('labelForceWithdrawNotAvailable', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) : ( {t('labelForceWithdrawNoToken')} ))} {!isAddressCheckLoading && !isNotAvailableAddress && walletMap !== undefined && !!Object.keys(walletMap).length && ( { // @ts-ignore } )} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} {lastFailed && ( {t('labelConfirmAgainByFailed', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/ImportCollectionWrap.tsx ================================================ import { ImportCollectionStep, ImportCollectionViewProps } from './Interface' import { Trans, useTranslation } from 'react-i18next' import React from 'react' import { Box, Link, ListItemText, Typography } from '@mui/material' import { AddIcon, BackIcon, CollectionMeta, DropDownIcon, getShortAddr, NFTWholeINFO, SoursURL, ViewMoreIcon, RouterPath, NFTSubRouter, } from '@loopring-web/common-resources' import { Button, CollectionInput, EmptyDefault, MenuItem, TextField } from '../../basic-lib' import styled from '@emotion/styled' import { useSettings } from '../../../stores' import { CollectionManageWrap } from './CollectionManageWrap' import { NFTMedia } from '../../block' import { useHistory } from 'react-router-dom' import { BtnMain, HorizontalLabelPositionBelowStepper } from './tool' const MintAdStyle = styled(Box)` .MuiFormGroup-root { align-items: flex-start; } .coinInput-wrap { border: 1px solid var(--color-border); } .MuiInputLabel-root { font-size: ${({ theme }) => theme.fontDefault.body2}; } .MuiButtonBase-root.step { padding-left: ${({ theme }) => theme.unit * 4}px; padding-right: ${({ theme }) => theme.unit * 4}px; } ` const steps = [ 'labelImportCollection1', //Prepare NFT metadata 'labelImportCollection2', //labelADMint2 'labelImportCollection3', //Preview & Mint NFT ] export const ImportCollectionWrap = >({ onContractChange, onContractNext, setStep, onCollectionChange, onCollectionNext, onNFTSelected, onNFTSelectedMethod, step, data, baseURL, getIPFSString, disabled, onLoading, }: ImportCollectionViewProps) => { const { t } = useTranslation(['common']) const { isMobile } = useSettings() const history = useHistory() const { contractList, selectContract, collectionInputProps, selectCollection, nftProps, selectNFTList, } = data // myLog("ImportCollectionWrap", contractList); const panelList: Array<{ view: JSX.Element onBack?: undefined | (() => void) height?: any width?: any }> = React.useMemo(() => { return [ { view: ( ) => { onContractChange(event.target?.value) }} inputProps={{ IconComponent: DropDownIcon }} fullWidth={true} > {contractList.map((item, index) => { return ( {getShortAddr(item)} } /> ) })} {selectContract && ( {!onLoading ? ( selectContract.total ? ( <> {selectContract?.list?.map((item, index) => { return ( undefined} isOrigin={false} getIPFSString={getIPFSString} baseURL={baseURL} /> ) })} {selectContract.total > 3 && ( )} ) : ( ( No NFT )} /> ) ) : ( {'loading'} )} )} { return disabled || !selectContract || !selectContract?.total }, onClick: () => { setStep(ImportCollectionStep.SELECTCOLLECTION) onContractNext(selectContract?.value ?? '') }, }} /> ), // onBack: () => setStep(CreateCollectionStep.ChooseMethod) }, { view: ( {t('labelImportChooseCollection')} {onLoading ? ( {'loading'} ) : collectionInputProps?.collectionListProps?.total > 0 ? ( <> { collectionInputProps?.onSelected && collectionInputProps.onSelected(item) onCollectionChange(item) }, }} fullWidth={true} collectionListProps={{ ...collectionInputProps.collectionListProps, size: 'small', }} size={isMobile ? 'small' : 'large'} showCopy={true} /> or Create Collection { return disabled || !selectCollection }, onClick: () => { setStep(ImportCollectionStep.SELECTNFT) selectCollection && onCollectionNext(selectCollection) }, }} /> ) : ( <> )} ), }, { view: ( {selectCollection && ( )} { return disabled || !selectCollection }, onClick: () => { history.push(`${RouterPath.nft}/${NFTSubRouter.myCollection}`) }, }} /> ), }, ] }, [ t, selectContract, contractList, onLoading, isMobile, collectionInputProps, selectCollection, baseURL, selectNFTList, onNFTSelected, onNFTSelectedMethod, nftProps, onContractChange, disabled, setStep, onContractNext, onCollectionChange, onCollectionNext, ]) // @ts-ignore return ( {panelList.map((panel, index) => { return {step === index ? panel.view : <>} })} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/ImportRedPacketWrap.tsx ================================================ import { Trans } from 'react-i18next' import { Box, Typography } from '@mui/material' import { QRCodeUpload } from '../../basic-lib/panel/QRCodeUpload' import React from 'react' export const ImportRedPacketWrap = React.forwardRef(({}, _ref: React.ForwardedRef) => { // @ts-ignore return ( Import QR code to receive red packet ) }) ================================================ FILE: packages/component-lib/src/components/tradePanel/components/Interface.ts ================================================ import { BtnInfo, BtnInfoProps, InputButtonProps, InputCoinProps, IpfsFile, SwitchPanelProps, } from '../../basic-lib' import React, { ChangeEvent } from 'react' import { XOR } from '../../../types/lib' import { CollectionInputProps } from './tool' import * as sdk from '@loopring-web/loopring-sdk' import { TOSTOBJECT } from '../../toast' import { Account, AccountStatus, AddressError, AssetsRawDataItem, BanxaOrder, CLAIM_TYPE, CoinInfo, CoinKey, CoinMap, EXCHANGE_TYPE, FeeInfo, GET_IPFS_STRING, LuckyRedPacketItem, NFTWholeINFO, RedPacketOrderType, RequireOne, TRADE_TYPE, TradeBtnStatus, WALLET_TYPE, WalletCoin, WalletMap, WithdrawType, WithdrawTypes, ContactType, RedPacketConfig, TokenType, } from '@loopring-web/common-resources' export enum RedPacketStep { TradeType = 0, ChooseType = 1, Main = 2, NFTList = 3, } export enum TargetRedPacketStep { TargetChosse = 0, TradeType = 1, ChooseType = 2, Main = 3, NFTList = 4, TargetSend = 5, } /** * private props */ export type TradeMenuListProps = { nonZero: boolean sorted: boolean walletMap: WalletMap> _height?: string | number coinMap: CoinMap> onChangeEvent: (index: 0 | 1, data: SwitchData) => void selected?: string tradeData: T tokenType?: TokenType className?: string hasCancel?: boolean contentEle?: ({ ele }: { ele: any }) => JSX.Element filterWithBorrowed?: boolean } /** * private props */ export type SwitchData = { to: 'menu' | 'button' tradeData: T } export type TransferInfoProps = { transferI18nKey?: string transferBtnStatus?: keyof typeof TradeBtnStatus | undefined chargeFeeTokenList: Array activeAccountPrice: string | undefined // activeAccountFeeList?: Array; feeInfo: C | undefined isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } } export type TransferExtendProps = { isThumb?: boolean addressDefault?: string sureItsLayer2: WALLET_TYPE | EXCHANGE_TYPE | undefined handleSureItsLayer2: (sure: WALLET_TYPE | EXCHANGE_TYPE) => void realAddr?: string isLoopringAddress?: boolean isSmartContractAddress?: boolean isAddressCheckLoading?: boolean isSameAddress?: boolean isActiveAccountFee?: boolean | 'not allow' addrStatus: AddressError onTransferClick: (data: T, isFirstTime?: boolean) => Promise handleFeeChange: (value: C) => void handleOnAddressChange: (value: string | undefined | I, isContactSelection?: boolean) => void isActiveAccount?: boolean feeWithActive?: boolean handleOnFeeWithActive: (value: boolean) => void wait?: number onBack?: () => void memo: string handleOnMemoChange: (e: ChangeEvent) => void contact?: { address: string; name: string; addressType: typeof sdk.AddressType } isFromContact?: boolean loopringSmartWalletVersion?: { isLoopringSmartWallet: boolean; version?: string } isENSWrong?: boolean ens?: string geUpdateContact: () => void // contacts?: { address: string; name: string; addressType: typeof sdk.AddressType }[] } & Pick & TransferInfoProps export type TransferViewProps | string> = TransferExtendProps & BasicACoinTradeViewProps & { onClickContact: () => void } export type RampViewProps> = TransferViewProps export type BanxaViewProps> = TransferViewProps & { offBanxaValue?: BanxaOrder } /** * private props */ export type ResetInfoProps = { assetsData?: any[] resetBtnStatus?: keyof typeof TradeBtnStatus | undefined chargeFeeTokenList: Array feeInfo: C disabled?: boolean isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } handleFeeChange: (value: C) => void } & XOR< { walletMap: WalletMap goToDeposit: () => void isNewAccount?: boolean isReset?: boolean }, {} > export type ResetExtendProps = { onResetClick: (props: { isNotFirstTime?: boolean; isReset?: boolean }) => void } & ResetInfoProps export type ResetViewProps = ResetExtendProps export type ExportAccountExtendProps = { exportAccountProps: any setExportAccountToastOpen: (value: boolean) => void } /** * private props */ export type DepositInfoProps = { depositBtnStatus?: keyof typeof TradeBtnStatus | undefined title?: string description?: string chargeFeeTokenList?: FeeInfo[] isNewAccount: boolean addressDefault?: string // handleOnAddressChange?: (value: string | undefined | I) => void; // handleAddressError?: ( // address: string // ) => { error: boolean; message?: string | JSX.Element } | undefined; wait?: number } & BtnInfoProps export type DepositExtendProps = { isThumb?: boolean title?: string isHideDes?: boolean allowTrade?: any toAddressStatus: AddressError isAllowInputToAddress?: boolean onDepositClick: (data: T) => void toIsAddressCheckLoading: boolean // toIsLoopringAddress: boolean; toAddress?: string realToAddress?: string | JSX.Element handleClear: () => void isToAddressEditable: boolean onBack?: () => void accountReady?: AccountStatus | undefined handleAddressChange: (address: string) => void isLoopringSmartWallet?: boolean onClose: () => void } & DepositInfoProps export type DepositViewProps = BasicACoinTradeViewProps & DepositExtendProps export type WithdrawInfoProps = { withdrawI18nKey?: string withdrawBtnStatus?: keyof typeof TradeBtnStatus | undefined chargeFeeTokenList: Array feeInfo: C isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } } export type WithdrawExtendProps = { isThumb?: boolean addressDefault: string accAddr: string isNotAvailableAddress: | 'isCFAddress' | 'isContract1XAddress' | 'isContractAddress' | 'isLoopringAddress' | 'isSameAddress' | undefined withdrawType: WithdrawType realAddr?: string isAddressCheckLoading: boolean isCFAddress: boolean isLoopringAddress?: boolean isContractAddress: boolean isFastWithdrawAmountLimit?: boolean addrStatus: AddressError disableWithdrawList?: string[] onWithdrawClick: (data: T, isFirstTime?: boolean) => void handleFeeChange: (value: C) => void handleWithdrawTypeChange: (value: WithdrawType) => void handleOnAddressChange: (value: string | undefined | I, isContactSelection?: boolean) => void wait?: number onBack?: () => void isToMyself?: boolean sureIsAllowAddress: WALLET_TYPE | EXCHANGE_TYPE | undefined handleSureIsAllowAddress: (value: WALLET_TYPE | EXCHANGE_TYPE) => void contact?: { address: string; name: string; addressType?: typeof sdk.AddressType } isFromContact?: boolean // onClickContact?: () => void loopringSmartWalletVersion?: { isLoopringSmartWallet: boolean; version?: string } isENSWrong?: boolean ens?: string title?: string geUpdateContact: () => void withdrawMode?: { mode: 'fast' | 'normal', fastModeSupportedTokens: string[], onChange: (mode: 'fast' | 'normal') => void fastMode: undefined | { fee: string, time: string } fastMaxAlert: { show: boolean, message: string } showFastMode: boolean normalMode: undefined | { fee: string, time: string } showTrustUI: boolean } // contacts?: { address: string; name: string; addressType: sdk.AddressType }[] } & Pick & WithdrawInfoProps export type WithdrawViewProps | string> = BasicACoinTradeViewProps & WithdrawExtendProps & { onClickContact: () => void } export type ForceWithdrawExtendProps = { addressDefault: string // accAddr: string; realAddr: string isActiveAccount: boolean isNotAvailableAddress: boolean isAddressCheckLoading: boolean isLoopringAddress: boolean addrStatus: AddressError // disableWithdrawList?: string[]; onWithdrawClick: (data: T, isFirstTime?: boolean) => void handleFeeChange: (value: C) => void handleOnAddressChange: (value: string | undefined | I) => void wait?: number onBack?: () => void } & WithdrawInfoProps export type ForceWithdrawViewProps | string> = BasicACoinTradeViewProps & ForceWithdrawExtendProps export type inputNFTProps> = RequireOne, 'label'> export type InputButtonDefaultProps> = RequireOne< Partial>, 'label' > export type InputCoinDefaultProps> = RequireOne< Partial>, 'label' > export type DefaultProps = { tradeData: T disabled?: boolean lastFailed?: boolean selectNFTDisabled?: boolean tokenNotEnough?: string } & ( | { type: TRADE_TYPE.TOKEN coinMap: CoinMap> walletMap: WalletMap> } | { type: TRADE_TYPE.NFT coinMap?: CoinMap> walletMap?: WalletMap> baseURL?: string getIPFSString?: GET_IPFS_STRING } ) type DefaultWithMethodProps = DefaultProps export type BasicACoinTradeViewProps = Omit, 'lastFailed'> & { lastFailed?: boolean baseURL?: string getIPFSString?: (url: string | undefined, basicUrl: string) => string onChangeEvent: (index: 0 | 1, data: SwitchData) => void } & Pick> | InputCoinProps>, 'handleError'> export type BasicACoinTradeProps = BasicACoinTradeViewProps & { type?: TRADE_TYPE.TOKEN inputBtnRef: React.Ref inputButtonProps?: InputButtonDefaultProps> inputButtonDefaultProps?: InputButtonDefaultProps> className?: string isMaxBtn?: boolean } export type BasicACoinInputProps = BasicACoinTradeViewProps & { type?: TRADE_TYPE.TOKEN inputCoinRef: React.Ref inputCoinProps?: InputCoinDefaultProps> inputCoinDefaultProps?: InputCoinDefaultProps> className?: string tokenNotEnough?: string } export type BasicANFTTradeProps = (Omit< BasicACoinTradeViewProps, 'coinMap' | 'lastFailed' | 'walletMap' > & { type: TRADE_TYPE.NFT baseURL: string fullwidth?: boolean getIPFSString: GET_IPFS_STRING isBalanceLimit?: boolean inputNFTRef: React.Ref inputNFTProps?: inputNFTProps> inputNFTDefaultProps: inputNFTProps> }) & XOR< { isThumb: true isRequired: boolean isSelected: boolean onChangeEvent?: (index: 0 | 1, data: SwitchData) => SwitchData handlePanelEvent?: (props: SwitchData, switchType: 'Tomenu' | 'Tobutton') => Promise myNFTPanel: JSX.Element }, { isThumb?: boolean } > export type BasicACoinTradeHookProps = DefaultWithMethodProps & { type?: TRADE_TYPE handlePanelEvent?: (props: SwitchData, switchType: 'Tomenu' | 'Tobutton') => Promise onChangeEvent?: (index: 0 | 1, data: SwitchData) => SwitchData inputButtonProps?: InputButtonDefaultProps } & Partial> export type NFTDepositInfoProps = DefaultWithMethodProps & { nftDepositBtnStatus?: keyof typeof TradeBtnStatus | undefined title?: string description?: string chargeFeeList?: FeeInfo[] addressDefault?: string handleOnAddressChange?: (value: string | undefined | I) => void handleAddressError?: ( address: string, ) => { error: boolean; message?: string | JSX.Element } | undefined wait?: number } & BtnInfoProps export type NFTDepositViewProps = NFTDepositExtendProps export type NFTDepositExtendProps = { isThumb?: boolean baseURL: string getIPFSString: GET_IPFS_STRING isNFTCheckLoading?: boolean handleOnNFTDataChange: (data: T) => void onNFTDepositClick: (data: T) => void allowTrade?: any } & NFTDepositInfoProps export type NFTMintInfoProps = { nftMintBtnStatus?: keyof typeof TradeBtnStatus | undefined title?: string description?: string chargeFeeTokenList?: Array feeInfo: C // isAvailableId?: boolean; isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } handleFeeChange: (value: C) => void wait?: number } & BtnInfoProps export type NFTMetaInfoProps = { nftMetaBtnStatus?: keyof typeof TradeBtnStatus | undefined title?: string description?: string chargeFeeTokenList?: Array feeInfo: C // isNFTCheckLoading?: boolean; // isAvailableId?: boolean; isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } handleFeeChange: (value: C) => void wait?: number } & BtnInfoProps export type NFTMintExtendProps = { isThumb?: boolean handleMintDataChange: (data: Partial) => void onNFTMintClick: (isFirstMint?: boolean) => void allowTrade?: any amountHandleError?: ( data: T, ref: React.ForwardedRef, ) => { error: boolean; message?: string | JSX.Element } | void } & NFTMintInfoProps export type NFTMetaExtendProps = { handleOnMetaChange: (data: Partial) => void onMetaClick: (data: Partial, isFirstMint?: boolean) => void userAgree: boolean handleUserAgree: (value: boolean) => void allowTrade?: any } & NFTMetaInfoProps export type NFTMintViewProps = { tradeData: MI metaData: ME disabled?: boolean coinMap?: CoinMap> walletMap?: WalletMap> mintService: any baseURL: string getIPFSString: GET_IPFS_STRING } & NFTMintExtendProps export type NFTMetaViewProps = { nftMeta: T domain: string baseURL: string collection?: Co | undefined collectionInputProps: CollectionInputProps disabled?: boolean } & NFTMetaExtendProps export type NFTMetaBlockProps = NFTMetaViewProps & { mintData: Partial handleMintDataChange: (data: Partial) => void amountHandleError?: ( data: Partial, ref: React.ForwardedRef, ) => { error: boolean; message?: string | JSX.Element } | void } // export type NFTMintViewWholeProps = { // metaData: Partial; // disabled?: boolean; // } & NFTMintExtendProps; export type NFTDeployInfoProps = DefaultWithMethodProps & { nftDeployBtnStatus?: keyof typeof TradeBtnStatus | undefined title?: string description?: string assetsData?: any[] chargeFeeTokenList: Array feeInfo: C isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } handleFeeChange: (value: C) => void wait?: number } & BtnInfoProps export type NFTDeployExtendProps = { onBack: () => void handleOnNFTDataChange: (data: T) => void onNFTDeployClick: (data: T, isFirstTime?: boolean) => void allowTrade?: any } & NFTDeployInfoProps export type NFTDeployViewProps = NFTDeployExtendProps export type NFTMintAdvanceInfoProps = DefaultWithMethodProps & { nftMintBtnStatus?: keyof typeof TradeBtnStatus | undefined title?: string description?: string chargeFeeTokenList?: Array feeInfo: C isNFTCheckLoading?: boolean isNotAvailableTokenAddress?: undefined | { reason: string } isNotAvailableCID?: undefined | { reason: string } isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } handleFeeChange: (value: C) => void wait?: number } & BtnInfoProps export type NFTMintAdvanceExtendProps = { isThumb?: boolean baseURL: string getIPFSString: GET_IPFS_STRING collectionInputProps: CollectionInputProps handleOnNFTDataChange: (data: Partial) => void onNFTMintClick: (data: T, isFirstMint?: boolean) => void allowTrade?: any etherscanBaseUrl: string } & NFTMintAdvanceInfoProps export type NFTMintAdvanceViewProps = NFTMintAdvanceExtendProps export type CollectionAdvanceProps<_T> = { handleDataChange: (data: string) => void onSubmitClick: () => Promise allowTrade?: any disabled?: boolean btnStatus: TradeBtnStatus // handleError: (error: { code: number; message: string }) => void; metaData: string } & BtnInfoProps export enum ImportCollectionStep { SELECTCONTRACT = 0, SELECTCOLLECTION = 1, SELECTNFT = 2, } export type CollectionManageData = { listNFT: NFT[] page: number total: number toastObj: TOSTOBJECT onFilterNFT: (filter: { legacyFilter: sdk.LegacyNFT | 'all' limit: number page: number }) => Promise isLoading: boolean filter: any } export type ImportCollectionViewProps = { account: Account onContractChange: (item: string | undefined) => void onContractNext: (item: string) => void onCollectionChange: (item: Co | undefined) => void onCollectionNext: (item: Co) => void onNFTSelected: (item: NFT) => void onNFTSelectedMethod: (item: NFT[], method: CollectionMethod) => void step: ImportCollectionStep baseURL: string setStep: (step: ImportCollectionStep) => void disabled?: boolean getIPFSString: GET_IPFS_STRING onLoading?: boolean onClick: (item: string) => void data: { contractList: string[] selectContract: | { value: string total?: number list?: sdk.UserNFTBalanceInfo[] } | undefined selectCollection: Co | undefined selectNFTList: NFT[] collectionInputProps: CollectionInputProps nftProps: CollectionManageData } } export enum CollectionMethod { moveOut = 'moveOut', moveIn = 'moveIn', } export type CollectionManageProps = { collection: Partial selectedNFTS: NFT[] onNFTSelected: (item: NFT | 'addAll' | 'removeAll') => void baseURL: string getIPFSString: GET_IPFS_STRING onNFTSelectedMethod: (item: NFT[], method: CollectionMethod) => void } & CollectionManageData export type ImportRedPacketProps = { btnStatus: TradeBtnStatus btnInfo?: BtnInfo disabled?: boolean // } export type ImportRedPacketExtendsProps = { handleOnDataChange: (value: Partial) => void onSubmitClick: () => Promise onFilesLoad: (key: string, value: IpfsFile) => void onDelete: (key: string) => void } & ImportRedPacketProps export type ImportRedPacketViewProps = ImportRedPacketExtendsProps export type ClaimInfoProps = { btnInfo?: BtnInfo btnStatus?: TradeBtnStatus | undefined chargeFeeTokenList: Array feeInfo: Fee isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } } export type ClaimExtendProps = { onClaimClick: (data: Partial, isHardwareRetry?: boolean) => void tradeData: Partial lastFailed: boolean tradeType: TRADE_TYPE claimType: CLAIM_TYPE handleFeeChange: (value: Fee) => void isNFT: boolean nftIMGURL?: string } & ClaimInfoProps export type CreateRedPacketInfoProps = { btnStatus: TradeBtnStatus btnInfo?: BtnInfo minimum: string | undefined maximum: string | undefined chargeFeeTokenList: Array feeInfo: Fee isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } disabled?: boolean // } export type CreateRedPacketExtendsProps = { tradeType: RedPacketOrderType handleOnDataChange: (value: Partial) => void handleFeeChange: (value: F) => void onCreateRedPacketClick: () => Promise onBack?: () => void assetsData: AssetsRawDataItem[] onChangePrivateChecked?: () => void privateChecked?: boolean backToScope: () => void onSendTargetRedpacketClick: () => Promise targetRedPackets: sdk.LuckyTokenItemForReceive[] popRedPacket: sdk.LuckTokenClaimDetail | undefined popRedPacketAmountStr: string | undefined onClickViewTargetDetail: (hash: string) => void onCloseRedpacketPop: () => void contacts?: ContactType[] isWhiteListed?: boolean showExclusiveOption?: boolean redPacketConfig: RedPacketConfig } & CreateRedPacketInfoProps export type CreateRedPacketViewProps = CreateRedPacketExtendsProps< T, F > & XOR< BasicACoinTradeProps, BasicANFTTradeProps & { handleOnChoose: (value: NFT) => void selectNFT: NFT } > & { setActiveStep: (step: RedPacketStep | TargetRedPacketStep) => void activeStep: RedPacketStep tokenMap: { [key: string]: sdk.TokenInfo } idIndex: { [key: string]: string } backToScope: () => void onClickNext: () => void onClickBack: () => void showNFT: boolean onChangeTradeType?: (tradeType: RedPacketOrderType) => void onSelecteValue?: (item: LuckyRedPacketItem) => void } export type TargetRedpacktSelectStepProps = { onClickCreateNew: () => void targetRedPackets: sdk.LuckyTokenItemForReceive[] onClickExclusiveRedpacket: (info: { hash: string; remainCount: number }) => void onClickViewDetail: (hash: string) => void onCloseRedpacketPop: () => void popRedPacket: sdk.LuckTokenClaimDetail | undefined popRedPacketAmountStr: string | undefined backToScope: () => void idIndex: { [key: string]: string } } export type TargetRedpacktInputAddressStepProps = { popupChecked: boolean onChangePopupChecked: (popupChecked: boolean) => void onFileInput: (input: string) => void addressListString: string onClickSend: () => void contacts?: ContactType[] onConfirm: (list: string[]) => void onManualEditInput: (text: string) => void popUpOptionDisabled: boolean maximumTargetsLength: number onClickBack: () => void sentAddresses?: string[] clearInput: () => void } /** * private props */ export type VaultJoinInfoProps = { btnStatus?: keyof typeof TradeBtnStatus | undefined title?: string description?: string chargeFeeTokenList?: FeeInfo[] wait?: number } & BtnInfoProps ================================================ FILE: packages/component-lib/src/components/tradePanel/components/MintAdvanceNFTWrap.tsx ================================================ import { NFTMintAdvanceViewProps } from './Interface' import { Trans, useTranslation } from 'react-i18next' import React from 'react' import { Box, Checkbox, FormControlLabel, FormControlLabel as MuiFormControlLabel, Link, Radio, Typography, } from '@mui/material' import { BackIcon, CheckBoxIcon, CheckedIcon, CloseIcon, EmptyValueTag, FeeInfo, getShortAddr, Info2Icon, L1L2_NAME_DEFINED, LoadingIcon, MapChainId, myLog, RefreshIcon, SoursURL, TOAST_TIME, TradeBtnStatus, TradeNFT, } from '@loopring-web/common-resources' import { Button, CollectionInput, EmptyDefault, InputSize, NftImage, RadioGroupStyle, TextField, TGItemData, } from '../../basic-lib' import { IconClearStyled } from './Styled' import { NFTInput } from './BasicANFTTrade' import { CollectionMeta, DEPLOYMENT_STATUS, NFTType } from '@loopring-web/loopring-sdk' import styled from '@emotion/styled' import { useSettings } from '../../../stores' import { Toast, ToastType } from '../../toast' import { BtnMain, HorizontalLabelPositionBelowStepper } from './tool' import { FeeSelect } from '../../../components/modal' export enum AdMethod { HasData = 'HasData', NoData = 'NoData', } const MintAdStyle = styled(Box)` .MuiFormGroup-root { align-items: flex-start; } .coinInput-wrap { border: 1px solid var(--color-border); } .MuiInputLabel-root { font-size: ${({ theme }) => theme.fontDefault.body2}; } .MuiButtonBase-root.step { padding-left: ${({ theme }) => theme.unit * 4}px; padding-right: ${({ theme }) => theme.unit * 4}px; } ` const NFT_TYPE: TGItemData[] = [ { value: NFTType.ERC1155, key: 'ERC1155', label: 'ERC1155', disabled: false, }, ] const steps = [ 'labelADMint1', //Prepare NFT metadata 'labelADMint2', //labelADMint2 'labelADMint3', //Preview & Mint NFT ] export enum MintStep { SELECTWAY = 0, INPUTCID = 1, MINT = 2, } export const MintAdvanceNFTWrap = < T extends TradeNFT, Co extends CollectionMeta, I extends any, C extends FeeInfo, >({ disabled: gDisabled, walletMap, tradeData, btnInfo, baseURL, handleOnNFTDataChange, nftMintBtnStatus, isFeeNotEnough, handleFeeChange, chargeFeeTokenList, feeInfo, isNotAvailableCID, isNotAvailableTokenAddress, isNFTCheckLoading, collectionInputProps, onNFTMintClick, getIPFSString, etherscanBaseUrl, }: NFTMintAdvanceViewProps) => { const { t } = useTranslation(['common']) const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [activeStep, setActiveStep] = React.useState(MintStep.SELECTWAY) const [address, setAddress] = React.useState(tradeData?.tokenAddress) const [cid, setCid] = React.useState(tradeData?.nftIdView) const [metaDataMissDataInfo, setMetaDataMissDataInfo] = React.useState([]) const [metaDataErrorDataInfo, setMetaDataErrorDataInfo] = React.useState([]) const [checked, setChecked] = React.useState(true) const { collectionListProps: { copyToastOpen }, } = collectionInputProps const inputBtnRef = React.useRef() const [showFeeModal, setShowFeeModal] = React.useState(false) myLog('showFeeModal', showFeeModal) React.useEffect(() => { if (address !== tradeData?.tokenAddress && tradeData?.tokenAddress !== '') { setAddress(tradeData?.tokenAddress) if (!tradeData?.tokenAddress) { setActiveStep(MintStep.SELECTWAY) } } }, [tradeData?.tokenAddress]) React.useEffect(() => { if (cid !== tradeData?.nftIdView && tradeData?.nftIdView !== '') { setCid(tradeData?.nftIdView) if (!tradeData.tokenAddress) { setActiveStep(MintStep.SELECTWAY) } else if (!tradeData?.nftIdView) { setActiveStep(MintStep.INPUTCID) } } }, [tradeData?.nftIdView]) const checkMeta = React.useCallback(() => { const checkResult = ['collection_metadata'].reduce((prev, item) => { if (!tradeData[item]) { prev.push(item) } return prev }, [] as string[]) const checkErrorResult = ['name', 'image', 'royaltyPercentage'].reduce((prev, item) => { if (item === 'royaltyPercentage' && tradeData[item] === undefined) { prev.push('royalty_percentage') } else if (item !== 'royaltyPercentage' && !tradeData[item]) { prev.push(item) } return prev }, [] as string[]) if ( tradeData.nftId && (checkErrorResult.length || !( tradeData.royaltyPercentage !== undefined && Number.isInteger(tradeData.royaltyPercentage) && tradeData.royaltyPercentage >= 0 && tradeData.royaltyPercentage <= 10 )) ) { setMetaDataErrorDataInfo(checkErrorResult) setChecked(false) } else { setMetaDataErrorDataInfo([]) } if (tradeData.nftId && checkResult.length) { setMetaDataMissDataInfo(checkResult) setChecked(false) } else { setMetaDataMissDataInfo([]) setChecked(true) } }, [tradeData]) React.useEffect(() => { if (tradeData?.nftId) { checkMeta() } }, [tradeData?.nftId]) const handleToggleChange = React.useCallback( (value: C) => { if (handleFeeChange) { handleFeeChange(value) } }, [handleFeeChange], ) myLog('mint tradeData', tradeData) const methodLabel = React.useCallback( ({ key }: { key: string }) => { return ( <> {t(`label${key}`)} ) }, [t], ) const [method, setMethod] = React.useState(AdMethod.NoData) const handleMethodChange = React.useCallback((_e: any, value: any) => { setMethod(value) }, []) const [src, setSrc] = React.useState('') const [error, setError] = React.useState(false) const panelList: Array<{ view: JSX.Element onBack?: undefined | (() => void) height?: any width?: any }> = React.useMemo(() => { return [ { view: ( {t('labelADMintSelect')} {Object.keys(AdMethod).map((key) => { return ( } label={methodLabel({ key })} /> ) })} {method === AdMethod.HasData && ( { const tokenAddress = event.target.value setAddress(tokenAddress) if (/^0x[a-fA-F0-9]{40}$/g.test(tokenAddress)) { handleOnNFTDataChange({ tokenAddress, } as T) } else { handleOnNFTDataChange({ tokenAddress: '', } as T) } } // handleOnNFTDataChange({ // tokenAddress: event.target?.value, // } as T) } fullWidth={true} /> {address && (isNFTCheckLoading ? ( ) : ( { setAddress('') handleOnNFTDataChange({ tokenAddress: '', } as T) }} > ))} {isNotAvailableTokenAddress && ( {t(isNotAvailableTokenAddress.reason, { ns: ['error', 'common'], })} )} )} {method === AdMethod.NoData && ( { collectionInputProps.onSelected(item) handleOnNFTDataChange({ tokenAddress: item.contractAddress ?? '', } as T) }, }} fullWidth={true} size={isMobile ? 'small' : 'large'} showCopy={true} /> )} Please fill in the appropriate collection metadata field value in your NFT metadata with this string first, then upload it to IPFS to retrieve the CID to continue. Follow this Guide { return gDisabled || !tradeData.tokenAddress || !tradeData.collectionMeta }, btnStatus: nftMintBtnStatus, onClick: () => { setActiveStep(MintStep.INPUTCID) }, }} /> ), // onBack: () => setStep(CreateCollectionStep.ChooseMethod) }, { view: ( {t('labelMintIPFSCIDDes')} IPFS CID :(Store Metadata Information), { const cid = event.target?.value setCid(cid) if ( /^(Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,})$/.test( cid, ) ) { handleOnNFTDataChange({ nftIdView: cid, nftId: '', } as T) } else { handleOnNFTDataChange({ nftIdView: '', nftId: '', } as T) } }} fullWidth={true} /> {cid && cid !== '' ? ( isNFTCheckLoading ? ( ) : ( { setCid('') handleOnNFTDataChange({ nftIdView: '', nftId: '', } as T) }} > ) ) : ( '' )} {isNotAvailableCID && tradeData?.nftIdView && tradeData?.nftIdView !== '' ? ( {t('labelInvalidCID') + '\n Reason: ' + t(isNotAvailableCID.reason, { ns: 'error' })} ) : ( <> {tradeData?.nftId && tradeData.tokenAddress && tradeData?.nftIdView !== '' && ( {tradeData?.nftId} )} )} {!!( tradeData.nftId && tradeData.tokenAddress && tradeData.collectionMeta && tradeData.collectionMeta.contractAddress ) && (metaDataErrorDataInfo.length ? ( {t('labelCollectionMetaError', { type: metaDataErrorDataInfo.length ? '`' + metaDataErrorDataInfo.join('`, `') + '`' : t('labelCollectionMetaErrorType'), })} ) : metaDataMissDataInfo.length ? ( {t('labelCollectionMetaMiss', { type: '`' + metaDataMissDataInfo.join('`, `') + '`', })} { setChecked(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelBtradeUnderstand')} /> ) : ( // : !tradeData.collectionMeta.name || // !tradeData.collectionMeta.tileUri ? ( // // {t("labelCollectionMetaNoNameORTileUri", { // type: [ // ...(!tradeData.collectionMeta.name ? ["name"] : []), // ...(!tradeData.collectionMeta.tileUri // ? ["tile_uri"] // : []), // ], // })} // // ) <> ))} { return ( gDisabled || !!isNotAvailableCID || !tradeData.nftId || !!metaDataErrorDataInfo.length || !checked ) }, btnStatus: nftMintBtnStatus, onClick: () => { setActiveStep(MintStep.MINT) setSrc(getIPFSString(tradeData?.image, baseURL)) }, }} /> ), }, { view: ( <> {'ipfs'} {!!(tradeData?.nftId && tradeData.image) ? ( {error ? ( { event.stopPropagation() setError(false) setSrc(getIPFSString(tradeData?.image, baseURL)) }} > ) : ( setError(true)} /> )} ) : ( ( {t('labelNoContent')} )} /> )} {t('labelNFTName')} {tradeData.name ?? EmptyValueTag} {t('labelNFTType')} {NFT_TYPE[0].label} {t('labelNFTContractAddress')} {tradeData.collectionMeta && tradeData.collectionMeta.deployStatus == DEPLOYMENT_STATUS.DEPLOYED ? ( {tradeData.collectionMeta.contractAddress} ) : ( {t('labelCounterFactualNFT', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) + getShortAddr(tradeData?.collectionMeta?.contractAddress ?? '')} )} {t('labelNFTCollection')} {tradeData.collectionMeta?.name} Amount {'\uFE61'} ), }} // disabled={!(tradeData?.nftId && tradeData.tokenAddress)} type={'NFT'} inputNFTRef={inputBtnRef} onChangeEvent={(_index, data) => handleOnNFTDataChange({ tradeValue: data.tradeData?.tradeValue ?? '0', } as T) } tradeData={ { ...tradeData, belong: tradeData?.tokenAddress ?? 'NFT', } as any } walletMap={walletMap} /> {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} { return gDisabled || nftMintBtnStatus === TradeBtnStatus.DISABLED }, onClick: () => { // setActiveStep(MintSte) onNFTMintClick(tradeData) }, }} /> ), }, ] }, [ t, isMobile, method, handleMethodChange, address, isNotAvailableTokenAddress, isNFTCheckLoading, collectionInputProps, tradeData, cid, isNotAvailableCID, metaDataErrorDataInfo, metaDataMissDataInfo, checked, error, src, etherscanBaseUrl, baseURL, walletMap, chargeFeeTokenList, feeInfo, isFeeNotEnough.isOnLoading, isFeeNotEnough.isFeeNotEnough, handleToggleChange, btnInfo, methodLabel, handleOnNFTDataChange, gDisabled, getIPFSString, nftMintBtnStatus, onNFTMintClick, showFeeModal, ]) // @ts-ignore return ( {panelList.map((panel, index) => { return ( {activeStep === index ? panel.view : <>} ) })} {copyToastOpen && ( { collectionInputProps?.collectionListProps?.setCopyToastOpen({ isShow: false, type: '', }) }} severity={ToastType.success} /> )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/MintNFTBlock.tsx ================================================ import { NFTMetaBlockProps } from './Interface' import { Trans, useTranslation } from 'react-i18next' import React from 'react' import { Box, FormLabel, Grid, GridProps, Tooltip, Typography } from '@mui/material' import { CoinInfo, CoinMap, CollectionMeta, FeeInfo, Info2Icon, MintTradeNFT, myLog, NFTMETA, TradeBtnStatus, } from '@loopring-web/common-resources' import { Button, InputCoin, InputSize, TextareaAutosizeStyled, TextField } from '../../basic-lib' import styled from '@emotion/styled' import { useSettings } from '../../../stores' import { NFTInput } from './BasicANFTTrade' import { Properties } from './tool/Property' import { CollectionInput } from './tool' const GridStyle = styled(Grid)` .coinInput-wrap { border: 1px solid var(--color-border); } .MuiInputLabel-root { font-size: ${({ theme }) => theme.fontDefault.body2}; } .main-label, .sub-label { color: var(--color-text-secondary); font-size: ${({ theme }) => theme.fontDefault.body1}; } ` as (props: GridProps) => JSX.Element export const MintNFTBlock = < T extends Partial, Co extends CollectionMeta, I extends Partial>, C extends FeeInfo, >({ disabled, nftMeta, mintData, btnInfo, // collection, nftMetaBtnStatus, handleOnMetaChange, handleMintDataChange, baseURL, domain, collectionInputProps, onMetaClick, }: NFTMetaBlockProps) => { const { t } = useTranslation(['common']) const { isMobile } = useSettings() const inputBtnRef = React.useRef() const [_collection, setCollection] = React.useState( collectionInputProps?.collection ?? undefined, ) React.useEffect(() => { setCollection(collectionInputProps.collection) }, [collectionInputProps?.collection?.contractAddress]) const getDisabled = React.useMemo(() => { return disabled || nftMetaBtnStatus === TradeBtnStatus.DISABLED }, [disabled, nftMetaBtnStatus]) myLog('mint nftMeta', nftMeta, mintData) const _handleMintDataChange = React.useCallback( (_mintData: Partial) => { handleMintDataChange({ ..._mintData }) }, [handleMintDataChange], ) // @ts-ignore return ( Name {'\uFE61'} } type={'text'} onChange={(event) => handleOnMetaChange({ name: event.target.value } as Partial)} /> { setCollection(item) handleOnMetaChange({ collection: item, } as any) }, }} size={isMobile ? 'small' : 'medium'} fullWidth={true} /> handleOnMetaChange({ royaltyPercentage: data.tradeValue, } as unknown as Partial) } {...{ maxAllow: true, placeholderText: '0', allowDecimals: false, handleError: (data) => { if (data.tradeValue && data.tradeValue > data.balance) { return { error: true, } } return { error: false, } }, // size = InputSize.middle, isHideError: true, isShowCoinInfo: false, order: 'right', noBalance: '0', coinPrecision: 0, subLabel: t('labelMintRoyaltyPercentageRange'), label: ( Royalty(%) {'\uFE61'} ), size: InputSize.small, inputData: { balance: 10, tradeValue: nftMeta.royaltyPercentage ?? 0, belong: 'royaltyPercentage' as any, }, disabled, coinMap: {} as CoinMap>, }} /> Amount {'\uFE61'} ), }} // disabled={!(nftMintData.nftId && nftMintData.tokenAddress)} type={'NFT'} inputNFTRef={inputBtnRef} onChangeEvent={(_index, data) => _handleMintDataChange({ ...data.tradeData, } as unknown as Partial) } handleError={(data) => { if (!data.tradeValue || data.tradeValue > data.balance) { return { error: true, // message: `Not enough ${belong} perform a deposit`, } } return { error: false, } }} // handleError={(data: I, ref) => { // if (amountHandleError) { // amountHandleError(data, ref); // } // }} tradeData={ { ...mintData, belong: mintData.tokenAddress ?? 'NFT', balance: mintData.nftBalance, } as any } disabled={disabled} walletMap={{}} /> Description handleOnMetaChange({ description: event.target.value, } as unknown as Partial) } draggable={true} /> Properties handleOnMetaChange({ properties: properties, } as unknown as Partial) } properties={nftMeta.properties ?? [{ key: '', value: '' }]} /> {btnInfo?.label === 'labelNFTMintNoMetaBtn' && ( Your NFT nftMeta should identify name, image & royalty_percentage(number from 0 to 10) . )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/MintNFTConfirm.tsx ================================================ import { NFTMintViewProps } from './Interface' import { useTranslation } from 'react-i18next' import React from 'react' import { Box, Grid, Link, Typography } from '@mui/material' import { EmptyValueTag, FeeInfo, getShortAddr, IPFS_LOOPRING_SITE, LinkIcon, MetaProperty, MintTradeNFT, NFTMETA, RefreshIcon, RowConfig, SoursURL, TradeBtnStatus, } from '@loopring-web/common-resources' import { Button, Column, EmptyDefault, NftImage, Table, TextareaAutosizeStyled, } from '../../basic-lib' import styled from '@emotion/styled' import { useSettings } from '../../../stores' import { FeeSelect } from '../../../components/modal' const GridStyle = styled(Grid)` .coinInput-wrap { border: 1px solid var(--color-border); } .MuiInputLabel-root { font-size: ${({ theme }) => theme.fontDefault.body2}; } ` as typeof Grid const TableStyle = styled(Table)` &.rdg { min-height: 60px; height: ${(props: any) => { if (props.currentheight) { return props.currentheight + 2 + 'px' } else { return '100%' } }}; } ` export const MintNFTConfirm = < ME extends Partial, MI extends Partial>, I, C extends FeeInfo, >({ disabled, tradeData: nftMintData, metaData, btnInfo, nftMintBtnStatus, isFeeNotEnough, handleFeeChange, chargeFeeTokenList, feeInfo, baseURL, onNFTMintClick, mintService, getIPFSString, }: NFTMintViewProps) => { const { t, ...rest } = useTranslation(['common']) const { isMobile } = useSettings() const [showFeeModal, setShowFeeModal] = React.useState(false) const getDisabled = React.useMemo(() => { return disabled || nftMintBtnStatus === TradeBtnStatus.DISABLED }, [disabled, nftMintBtnStatus]) const [error, setError] = React.useState(false) const [src, setSrc] = React.useState(getIPFSString(metaData?.image, baseURL)) React.useEffect(() => { setError(false) setSrc(getIPFSString(metaData?.image ?? '', baseURL)) }, [metaData?.image]) const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } const rawData = metaData.properties?.reduce((prev, item) => { if (!!item.key?.trim() && !!item.value?.trim()) { return [...prev, item] } else { return prev } }, [] as Array) ?? [] // myLog("mint nftMintData", nftMintData); // @ts-ignore return ( {'ipfs'} {metaData.image ? ( {error ? ( { event.stopPropagation() setError(false) setSrc(getIPFSString(metaData?.image, baseURL)) }} > ) : ( setError(true)} /> )} ) : ( ( {t('labelNoContent')} )} /> )} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} {t('labelConfirmMint')} {t('labelNFTName')} {metaData.name ?? EmptyValueTag} {t('labelNFTID')} # {' ' + getShortAddr( nftMintData?.nftIdView ? nftMintData.nftIdView : nftMintData.nftId ?? '', )}{' '} {t('labelNFTContractAddress')} {nftMintData.tokenAddress} {t('labelNFTType')} {'ERC1155'} {t('labelNFTAmount')} {nftMintData.tradeValue} {t('labelNFTDescription')} {metaData.description ? ( ) : ( {EmptyValueTag} )} {t('labelNFTProperty')} {rawData.length ? ( <>{row?.key}, }, { key: 'value', name: t('labelMintPropertyValue'), // formatter: ({ row }: any) => <>{row?.value}, }, ], generateRows: (rawData: any) => rawData, generateColumns: ({ columnsRaw }: any) => columnsRaw as Column[], }} /> ) : ( {EmptyValueTag} )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/RampConfirm.tsx ================================================ import { useTranslation } from 'react-i18next' import { Box, Grid, Typography } from '@mui/material' import { EmptyValueTag, FeeInfo, IBData, NFTWholeINFO, TOAST_TIME, TradeBtnStatus, } from '@loopring-web/common-resources' import { Button, FeeSelect, RampViewProps, Toast, ToastType, } from '../../index' import { useSettings } from '../../../stores' import React from 'react' export const RampConfirm = & Partial, I, C extends FeeInfo>({ tradeData, onTransferClick, realAddr, disabled, isFeeNotEnough, handleFeeChange, chargeFeeTokenList, transferBtnStatus, transferI18nKey, feeInfo, memo, balanceNotEnough, }: RampViewProps & { balanceNotEnough: boolean }) => { const { isMobile } = useSettings() const { t } = useTranslation() const [open, setOpen] = React.useState(false) const [showFeeModal, setShowFeeModal] = React.useState(false) const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } const getDisabled = React.useMemo(() => { return disabled || transferBtnStatus === TradeBtnStatus.DISABLED }, [disabled, transferBtnStatus]) return ( {t('labelL2toRampTitle')} {t('labelL2toL2TokenAmount')} {tradeData?.tradeValue + ' '} {tradeData?.belong} {balanceNotEnough && ( {t('labelRampNoBalance', { belong: tradeData?.belong })} )} {t('labelL2toL2Address')} {realAddr} {t('labelL2toL2AddressType')} {t('labelWalletTypeOptions', { type: 'Ramp' })} {t('labelMemo')} {memo ?? EmptyValueTag} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} { setOpen(false) }} severity={ToastType.error} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/ResetWrap.tsx ================================================ import { Trans, WithTranslation } from 'react-i18next' import React from 'react' import { Grid, InputAdornment, Link, TextField, Tooltip, Typography } from '@mui/material' import { EmptyValueTag, FeeInfo, Info2Icon, L1L2_NAME_DEFINED, MapChainId, TradeBtnStatus, } from '@loopring-web/common-resources' import { Button } from '../../basic-lib' import { ResetViewProps } from './Interface' import { useSettings } from '../../../stores' import { FeeSelect } from '../../../components/modal' export const ResetWrap = ({ t, resetBtnStatus, onResetClick, isNewAccount = false, isFeeNotEnough, feeInfo, disabled = false, chargeFeeTokenList = [], goToDeposit, walletMap, isReset = false, handleFeeChange, }: ResetViewProps & WithTranslation) => { const [showFeeModal, setShowFeeModal] = React.useState(false) const { referralCode, setReferralCode } = useSettings() const [value, setValue] = React.useState(referralCode) const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const getDisabled = React.useMemo(() => { return disabled || resetBtnStatus === TradeBtnStatus.DISABLED }, [disabled, resetBtnStatus]) const handleToggleChange = (value: T) => { if (handleFeeChange) { handleFeeChange(value) } } const onRefChange = (e: any) => { const regex = /^[0-9\b]+$/ if (e?.target?.value === '' || regex.test(e?.target.value)) { setValue(e.target.value) if (e.target.value.length >= 5) { setReferralCode(e.target.value) } // searchParams.set('referralcode', e.target.value) // history.replace({ search: searchParams.toString() }) } } return ( {isNewAccount ? t('labelActiveAccountTitle', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, }) : t('resetTitle', { layer2: L1L2_NAME_DEFINED[network].layer2 })} {isNewAccount ? t('labelActiveAccountDescription', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) : t('labelResetDescription', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as T) setShowFeeModal(false) }} feeInfo={feeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} middleContent={ <> {isNewAccount && feeInfo?.fee?.toString() == '0' ? ( {t('labelFriendsPayActivation', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) : ( '' )} {isNewAccount && ( {t('labelYourBalance', { balance: walletMap && feeInfo && feeInfo.belong && walletMap[feeInfo.belong] ? walletMap[feeInfo.belong]?.count + ' ' + feeInfo.belong : EmptyValueTag + ' ' + (feeInfo && feeInfo?.belong), layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} )} } isFeeNotEnough={isFeeNotEnough && isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough && isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} feeNotEnoughContent={ {isNewAccount && goToDeposit ? ( Insufficient balance { goToDeposit() }} variant={'body2'} > Add assets ) : ( t('labelL2toL2FeeNotEnough') )} } /> )} {isNewAccount && !isReset && ( Referral Code (Optional) # ), }} type={'text'} onChange={onRefChange} // onChange={(event) => // handleOnMetaChange({ name: event.target.value } as Partial) // } /> )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/Styled.ts ================================================ import styled from '@emotion/styled' import { Box, Grid, IconButton, IconProps, LinearProgress, linearProgressClasses, Tabs, } from '@mui/material' import { css } from '@emotion/react' import { Button } from '../../basic-lib' import { DropDownIcon } from '@loopring-web/common-resources' export const FeeTokenItemWrapper = styled(Box)` background-color: var(--color-global-bg); ` as typeof Box export const DropdownIconStyled = styled(DropDownIcon)` transform: rotate( ${({ status }: any) => { return status === 'down' ? '0deg' : '180deg' }} ); ` as unknown as (props: IconProps & { status: string }) => JSX.Element export const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ height: 10, borderRadius: 5, [`&.${linearProgressClasses.colorPrimary}`]: { backgroundColor: theme.colorBase.textSecondary, //theme.palette.grey[theme.palette.mode === 'light' ? 200 : 800], }, [`& .${linearProgressClasses.bar}`]: { borderRadius: 5, backgroundColor: theme.colorBase.primary, }, })) export const IconClearStyled = styled(IconButton)` position: absolute; top: 20px; right: 4px; ` as typeof IconButton export const IconButtonStyled = styled(IconButton)` .MuiToolbar-root &.MuiButtonBase-root { svg { } &.outlined { background-color: var(--color-box); margin: 0 ${({ theme }) => theme.unit / 2}px; ${({ theme }) => theme.border.defaultFrame({ c_key: 'transparent' })}; &:last-child { margin-right: 0; } } } ` as typeof IconButton const cssAutoRefresh = (_props: any) => css` @keyframes rotate { 25% { transform: rotate(-135deg); } 50% { transform: rotate(-135deg); } 75% { transform: rotate(-315deg); } 100% { transform: rotate(-315deg); } } @keyframes hide1 { 25% { left: -0.5em; opacity: 0; } 50% { left: 0; opacity: 1; } } @keyframes hide2 { 25% { right: -0.5em; opacity: 0; } 50% { right: 0; opacity: 1; } } @keyframes container { 25% { transform: translate3d(0, -50%, 0); width: 0.5em; } 50% { transform: translate3d(-100%, -50%, 0); width: 0.5em; } 75% { transform: translate3d(-50%, -50%, 0); width: 1em; } } ` export const CountDownStyled = styled(Box)<{countDownSeconds?: number}>` ${({ theme }) => cssAutoRefresh({ theme })} width: var(--btn-icon-size); height: var(--btn-icon-size); position: relative; background-size: 68%; background-repeat: no-repeat; background-position: center; &.outlined { background-color: var(--field-opacity); margin: 0 ${({ theme }) => theme.unit / 2}px; ${({ theme }) => theme.border.defaultFrame({ c_key: 'transparent' })}; text-align: center; line-height: var(--btn-icon-size); &:last-child { margin-right: 0; } } &.logo { background-image: ${({ color }) => `url("data:image/svg+xml,%3Csvg width='34' height='27' viewBox='0 0 34 27' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fillRule='evenodd' clipRule='evenodd' d='M19.354 12.7874H33.4527V12.8709L11.4393 26.1381L22.351 17.5019L19.354 12.7874ZM11.1439 0V26.3259L0 17.5228L11.1439 0Z' fill='${encodeURIComponent( color ? (color as string) : 'var(--auto-refresh-color)', )}'/%3E%3C/svg%3E%0A")`}; } &.countdown { font-size: ${({ theme }) => theme.fontDefault.h6}; display: inline-block; color: var(--color-primary); .circle { font-size: ${({ theme }) => theme.fontDefault.h4}; width: 1em; height: 1em; position: absolute; left: 50%; top: 50%; transform: translate3d(-50%, -50%, 0); animation: container ${({countDownSeconds}) => countDownSeconds ? `${(countDownSeconds - 1) * 2}s` : 'var(--durationInternal)'} steps(1) infinite; overflow: hidden; &::before, &::after { display: block; content: ''; box-sizing: border-box; border: .125em solid transparent; border-radius: 50%; position: absolute; top: 0; bottom: 0; width: 1em; transform: rotate(45deg); animation-timing-function: linear, steps(1); animation-duration: ${({countDownSeconds}) => countDownSeconds ? `${(countDownSeconds - 1) * 2}s` : 'var(--durationInternal)'}, ${({countDownSeconds}) => countDownSeconds ? `${(countDownSeconds - 1) * 2}s` : 'var(--durationInternal)'}; animation-iteration-count: infinite, infinite; } &::before { border-color: ${({ color }) => color ? `transparent transparent ${color} ${color}` : 'transparent transparent var(--auto-refresh-color) var(--auto-refresh-color)'}; animation-name: rotate, hide1; left: 0; } &::after { border-color: ${({ color }) => color ? `${color} ${color} transparent transparent` : 'var(--auto-refresh-color) var(--auto-refresh-color) transparent transparent'}; animation-delay: ${({countDownSeconds}) => countDownSeconds ? `${(countDownSeconds - 1) / 2}s` : 'var(--delay)'}, ${({countDownSeconds}) => countDownSeconds ? `${(countDownSeconds - 1) / 2}s` : 'var(--delay)'}; animation-name: rotate, hide2; right: 0; } } ` as typeof Box export const ButtonStyle = styled(Button)` font-size: 1.6rem; &.MuiButton-root.Mui-disabled { ${({ loading }) => { return ( loading === 'true' && ` &.vaultInProcessing{ &::after{ } }` ) }}` as typeof Button export const TabsStyle = styled(Tabs)` &&.trade-tabs { background: var(--color-global-bg); min-height: 28px; height: 28px; border-radius: ${({ theme }) => theme.unit / 2}px; margin-top: ${({ theme }) => theme.unit * 2}px; .MuiTab-fullWidth.MuiTab-root { min-height: 28px; height: 28px; line-height: 28px; padding: 0; font-size: ${({ theme }) => theme.fontDefault.h6}; &:focus-visible, &:active:after { background-color: initial; } &.Mui-selected { overflow: unset; border-radius: ${({ theme }) => theme.unit / 2}px; color: var(--color-text-button); min-height: 28px; height: 28px; &.trade-tab-buy { background: var(--color-success); } &.trade-tab-sell { background: var(--color-error); } &.trade-tab-quantity, &.trade-tab-speed { background: var(--color-primary); } &.trade-tab-sell:after, &.trade-tab-speed:after { background-color: var(--color-error); mask-size: cover; mask-image: url('data:image/svg+xml,\ \ \ '); top: 0; height: 28px; left: -8px; width: 17px; transform: rotate(180deg); } &.trade-tab-buy:after, &.trade-tab-quantity:after { background-color: var(--color-success); mask-size: cover; mask-image: url('data:image/svg+xml,\ \ \ '); top: 0; left: auto; height: 28px; right: -8px; width: 17px; } &.trade-tab-quantity:after, &.trade-tab-speed:after { background-color: var(--color-primary); margin: 0; } } } } ` as typeof Tabs const loadingGif = './static/loading-1.gif' export const GridWrapStyle = styled(Grid)` &.loading { position: relative; &:after { content: ''; display: block; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: url(${loadingGif}) no-repeat 50% 50%; background-size: 80px; } } ` as typeof Grid ================================================ FILE: packages/component-lib/src/components/tradePanel/components/SwapWrap/Interface.ts ================================================ import { SwapTradeData, SwitchData } from '../../Interface' import { InputButtonProps } from '../../../basic-lib' import { CoinInfo, TokenType, TradeBtnStatus } from '@loopring-web/common-resources' export type SwapData = { type: 'buy' | 'sell' | 'exchange' } & SwitchData export type SwapMenuListProps = { tokenType?: TokenType swapData: SwapData> onChangeEvent: (index: 0 | 1, data: SwapData>) => void tradeCalcData: TCD } /** * private props */ export type SwapTradeBaseProps = { disabled?: boolean isStob?: boolean swapBtnI18nKey?: string swapBtnStatus?: keyof typeof TradeBtnStatus | undefined tradeCalcData: TCD tokenSellProps?: Partial>> tokenBuyProps?: Partial>> classWrapName?: string BtnEle?: JSX.Element marginLevelChange?: { from: { marginLevel: string type: 'safe' | 'warning' | 'danger' } to: { marginLevel: string type: 'safe' | 'warning' | 'danger' } } vaultLeverage?: { leverage: string hideLeverage: boolean onClickLeverage: () => void } } export type SwapTradeBaseEventProps = { onSwapClick: () => void | any onCancelClick?: () => void | any } & Partial, 'handleError'>> export type SwapTradeExtendProps = { switchStobEvent?: (value: boolean) => void onChangeEvent: (index: 0 | 1, data: SwapData>) => void } export type SwapTradeProps = SwapTradeBaseProps & SwapTradeExtendProps & SwapTradeBaseEventProps & { tradeData: SwapTradeData } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/SwapWrap/SwapMenuList.tsx ================================================ import { SwitchData } from '../../Interface' import { IBData, TradeCalcData } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import React from 'react' import { TradeMenuList } from '../tool' import { SwapMenuListProps } from './Interface' export function SwapMenuList, I, TCD extends TradeCalcData>({ onChangeEvent, tradeCalcData, swapData, tokenType, ...rest }: SwapMenuListProps & WithTranslation) { const selected: string | undefined = swapData.tradeData[swapData.type].belong ? swapData.tradeData[swapData.type]?.belong : '' const tradeData = swapData.tradeData[swapData.type] const coinMap = swapData.type === 'sell' ? tradeCalcData?.coinInfoMap : (tradeCalcData?.buyCoinInfoMap as any) const walletMap = tradeCalcData?.walletMap as any //IBData const handleOnChangeIndex = React.useCallback( (index: 0 | 1, { tradeData, to }: SwitchData>) => { onChangeEvent(index, { ...swapData, tradeData: { ...swapData.tradeData, [swapData.type]: tradeData }, to: to, }) }, [swapData, onChangeEvent], ) return ( ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/SwapWrap/SwapTradeWrap.tsx ================================================ import { SwapTradeData } from '../../Interface' import { BtradeTradeCalcData, BtradeType, CoinInfo, CoinMap, defaultSlipage, EmptyValueTag, getValuePrecisionThousand, IBData, Info2Icon, L1L2_NAME_DEFINED, MapChainId, myLog, ReverseIcon, SwapExchangeIcon, SwapTradeCalcData, TradeBtnStatus, VaultSwapStep, VaultTradeCalcData, } from '@loopring-web/common-resources' import { Trans, WithTranslation } from 'react-i18next' import React from 'react' import { Box, Grid, Tooltip, Typography, Link, Tab } from '@mui/material' import { InputButton } from '../../../basic-lib' import { SwapTradeProps } from './Interface' import { useSettings } from '../../../../stores' import { ButtonStyle, IconButtonStyled, TabsStyle } from '../Styled' import { useHistory } from 'react-router-dom' import styled from '@emotion/styled' import { marginLevelTypeToColor } from '../VaultWrap/utils' import { numberFormat } from '@loopring-web/core' import EastIcon from '@mui/icons-material/East'; const GridStyle = styled(Grid)` .buyInput { margin-top: ${({ theme }) => 1 * theme.unit}px; } .iconChange { top: var(--input-height-swap); transform: translateY(-50%); } ` export const SwapTradeWrap = < T extends IBData, I, TCD extends BtradeTradeCalcData | VaultTradeCalcData | SwapTradeCalcData, >({ t, onChangeEvent, isStob, switchStobEvent, tradeData, disabled, handleError, swapBtnStatus, onSwapClick, swapBtnI18nKey, tradeCalcData, tokenSellProps, tokenBuyProps, onCancelClick, BtnEle, covertOnClickPreCheck, marginLevelChange, ...rest }: SwapTradeProps & WithTranslation) => { const sellRef = React.useRef() const buyRef = React.useRef() const history = useHistory() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [_isStoB, setIsStoB] = React.useState(typeof isStob !== 'undefined' ? isStob : true) const buySymbol = (tradeCalcData as any)?.belongBuyAlice ?? tradeData?.buy?.belong const sellSymbol = (tradeCalcData as any)?.belongSellAlice ?? tradeData?.sell?.belong const _onSwitchStob = React.useCallback( (_event: any) => { setIsStoB(!_isStoB) if (typeof switchStobEvent === 'function') { switchStobEvent(!_isStoB) } }, [switchStobEvent, _isStoB], ) const getDisabled = React.useCallback(() => { return disabled || tradeCalcData === undefined || tradeCalcData.coinInfoMap === undefined }, [disabled, tradeCalcData]) const handleOnClick = React.useCallback( (_event: React.MouseEvent, _name: string, ref: any) => { const focus: 'buy' | 'sell' = ref.current === buyRef.current ? 'buy' : 'sell' onChangeEvent(1, { tradeData, type: focus, to: 'menu', }) }, [tradeData, onChangeEvent], ) const handleCountChange = React.useCallback( (ibData: IBData, _name: string, _ref: any) => { const focus: 'buy' | 'sell' = _ref?.current === buyRef.current ? 'buy' : 'sell' if (tradeData[focus].tradeValue !== ibData.tradeValue) { onChangeEvent(0, { tradeData: { ...tradeData, [focus]: ibData }, type: focus, to: 'button', }) } }, [tradeData, onChangeEvent], ) const covertOnClick = React.useCallback(() => { onChangeEvent(0, { tradeData: { sell: tradeData.buy, buy: tradeData.sell, } as SwapTradeData, type: 'exchange', to: 'button', }) }, [tradeData, onChangeEvent]) if (typeof handleError !== 'function') { handleError = ({ belong, balance, tradeValue }: any) => { if (balance < tradeValue || (tradeValue && !balance)) { const _error = { error: true, message: t('tokenNotEnough', { belong: belong }), } return _error } return { error: false, message: '' } } } const propsSell = { label: t('tokenEnterPaymentToken'), subLabel: t('tokenMax'), emptyText: t('tokenSelectToken'), placeholderText: '0.00', maxAllow: true, ...tokenSellProps, isShowCoinInfo: true, isShowCoinIcon: true, handleError, handleCountChange, handleOnClick, ...rest, } as any const propsBuy = { label: t('tokenEnterReceiveToken'), // subLabel: t('tokenHave'), emptyText: t('tokenSelectToken'), placeholderText: '0.00', maxAllow: false, ...tokenBuyProps, isShowCoinInfo: true, isShowCoinIcon: true, handleCountChange, handleOnClick, ...rest, } as any const label = React.useMemo(() => { myLog(swapBtnI18nKey, 'swapBtnI18nKey useMemo') const keyParams = { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } if (swapBtnI18nKey) { const key = swapBtnI18nKey.split('|') if (key) { return t( key[0], key && key[1] ? { arg: key[1], ...keyParams, } : { ...keyParams, }, ) } else { return t(swapBtnI18nKey, { ...keyParams, }) } } else { const label = (tradeCalcData as any)?.isBtrade ? `labelBtradeSwapBtn` : (tradeCalcData as any)?.isVault ? `labelVaultSwapBtn` : `swapBtn` return t(label, { ...keyParams, }) } }, [t, swapBtnI18nKey, network, tradeCalcData]) const showVal = buySymbol && sellSymbol && tradeCalcData?.StoB const isL2Swap = !((tradeCalcData as any)?.isBtrade || (tradeCalcData as any)?.isVault) const convertStr = _isStoB ? `1 ${sellSymbol} \u2248 ${ tradeCalcData.StoB && tradeCalcData.StoB !== 'NaN' ? tradeCalcData.StoB : EmptyValueTag } ${buySymbol}` : `1 ${buySymbol} \u2248 ${ tradeCalcData.BtoS && tradeCalcData.BtoS !== 'NaN' ? tradeCalcData.BtoS : EmptyValueTag } ${sellSymbol}` const priceImpactColor = (tradeCalcData as any)?.priceImpactColor ? (tradeCalcData as any).priceImpactColor : 'textPrimary' const priceImpact = (tradeCalcData as any)?.priceImpact !== undefined ? getValuePrecisionThousand( (tradeCalcData as any).priceImpact, undefined, undefined, 2, true, ) + ' %' : EmptyValueTag const fee = tradeCalcData && tradeCalcData.fee ? `${tradeCalcData.fee} ${buySymbol}` //(parseFloat(tradeCalcData.fee) / 100).toString() + "%" : EmptyValueTag myLog('tradeCalcDatatradeCalcData', tradeCalcData) myLog('tradeCalcDatatradeCalcData', tradeData) const userTakerRate = tradeCalcData && tradeCalcData.feeTakerRate ? (tradeCalcData.feeTakerRate / 100).toString() : EmptyValueTag const tradeCostMin = tradeCalcData && tradeCalcData.tradeCost ? `${tradeCalcData.tradeCost} ${buySymbol}` //(parseFloat(tradeCalcData.fee) / 100).toString() + "%" : EmptyValueTag const minimumConverted = tradeCalcData && tradeCalcData.minimumConverted ? `${tradeCalcData.minimumConverted} ${buySymbol}` : EmptyValueTag const { isMobile } = useSettings() return ( {(tradeCalcData as any)?.isBtrade && ( onChangeEvent(0, { tradeData: { ...tradeData, btradeType: value, } as SwapTradeData, type: (tradeCalcData as unknown as any)?.lastStepAt ?? 'sell', to: 'button', }) } > )} > ref={sellRef} disabled={getDisabled()} className={'swapWrap'} order={'left'} {...{ ...propsSell, isHideError: true, inputData: tradeData ? tradeData.sell : ({} as any), coinMap: tradeCalcData && tradeCalcData.coinInfoMap ? tradeCalcData.coinInfoMap : ({} as CoinMap>), }} /> { if (!covertOnClickPreCheck || covertOnClickPreCheck()) { covertOnClick() } }} aria-label={t('tokenExchange')} > > ref={buyRef} disabled={getDisabled()} className={'swapWrap buyInput'} order={'left'} {...{ ...propsBuy, // maxAllow:false, isHideError: true, inputData: tradeData ? tradeData.buy : ({} as any), coinMap: tradeCalcData && tradeCalcData.coinInfoMap ? tradeCalcData.coinInfoMap : ({} as CoinMap>), }} /> {!isL2Swap ? ( {t('labelBtradeQuote')} {tradeCalcData?.totalQuota ? tradeCalcData?.totalQuota + ' ' + sellSymbol : EmptyValueTag} ) : ( <> )} {(tradeCalcData as any)?.isVault && (tradeCalcData as any).step !== VaultSwapStep.Edit && ( )} {showVal ? ( <> {convertStr} ) : ( t('labelCalculating') )} {!isL2Swap ? ( <> {(tradeCalcData as any)?.isVault && tradeCalcData && tradeCalcData.borrowVol ? ( {' ' + t('labelTobeBorrowed')} {tradeCalcData && tradeCalcData.borrowVol ? `${tradeCalcData.borrowStr} ${sellSymbol}` //(parseFloat(tradeCalcData.fee) / 100).toString() + "%" : EmptyValueTag} ) : undefined} {(tradeCalcData as any)?.isVault && tradeCalcData && tradeCalcData.borrowVol ? ( {' ' + t('labelVaultMarginLevel')} {marginLevelChange ? ( <> {numberFormat(marginLevelChange.from.marginLevel, { fixed: 2 })} {numberFormat(marginLevelChange.to.marginLevel, { fixed: 2 })} ) : ( EmptyValueTag )} ) : undefined} {(tradeCalcData as any)?.isVault && tradeCalcData && tradeCalcData.borrowVol ? ( {' ' + t('labelHourlyInterestRate')} {tradeCalcData && (tradeCalcData as any).hourlyRateInPercent && (tradeCalcData as any).yearlyRateInPercent && `${(tradeCalcData as any).hourlyRateInPercent}% (APR: ${ (tradeCalcData as any).yearlyRateInPercent }%)`} ) : undefined} {' ' + t('labelTradingFeeEst')} {fee} {t('labelSwapMinConverted')} {minimumConverted} {' ' + t('swapTolerance')} {tradeCalcData ? (tradeData.slippage ? tradeData.slippage : tradeCalcData.slippage ? tradeCalcData.slippage : defaultSlipage) + '%' : EmptyValueTag} ) : ( <> {' ' + t('labelTradingFeeEst')} {fee} {' ' + t('swapPriceImpact')} {priceImpact} {t('labelSwapMinConverted')} {minimumConverted} {' ' + t('swapTolerance')} {tradeCalcData ? (tradeData.slippage ? tradeData.slippage : tradeCalcData.slippage ? tradeCalcData.slippage : defaultSlipage) + '%' : EmptyValueTag} )} {(tradeCalcData as any)?.isVault && BtnEle ? ( <>{BtnEle} ) : ( { onSwapClick() }} loading={!getDisabled() && swapBtnStatus === TradeBtnStatus.LOADING ? 'true' : 'false'} disabled={ getDisabled() || swapBtnStatus === TradeBtnStatus.DISABLED || swapBtnStatus === TradeBtnStatus.LOADING } fullWidth={true} > {label} )} {isL2Swap && (tradeCalcData as any)?.isShowBtradeAllow && ( { history.push('/trade/btrade/' + sellSymbol + '-' + buySymbol) }} target='_blank' rel='noopener noreferrer' variant={'inherit'} color={'primary'} /> ), }} > Swapping on the DEX will result in a large Price Impact (loss of assets). We recommend using the Block Trade option to help minimize potential losses. )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/SwapWrap/index.ts ================================================ // this is a private component, only used in panel component // please do not export this index to global // export * from './Interface' export * from './SwapTradeWrap' export * from './SwapMenuList' export * from './Interface' ================================================ FILE: packages/component-lib/src/components/tradePanel/components/TargetRedpacketWrap.tsx ================================================ import { useTranslation } from 'react-i18next' import { Box, Typography, Tooltip, DialogTitle, DialogContent } from '@mui/material' import { CoinSource, SoursURL, TokenType } from '@loopring-web/common-resources' import { Button, NftImageStyle } from '../../basic-lib' import { useTheme } from '@emotion/react' import { CoinIcons } from '../../tableList' import * as sdk from '@loopring-web/loopring-sdk' export const TargetRedpacketWrap = (props: { exclusiveRedPackets?: (sdk.LuckyTokenItemForReceive & { tokenIcon: CoinSource tokenName: string })[] onClickOpenExclusive: (redpacket: sdk.LuckyTokenItemForReceive) => void }) => { const { exclusiveRedPackets, onClickOpenExclusive } = props const { t } = useTranslation(['common']) const theme = useTheme() return ( <> {t('labelExclusiveRedpacket')} {exclusiveRedPackets && exclusiveRedPackets.map((redpacket) => ( {redpacket.isNft ? ( ) : ( )} {redpacket.tokenName} {redpacket.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && ( {t('labelRedpacketFromBlindbox')}}> )} ))} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/TransferConfirm.tsx ================================================ import { WithTranslation } from 'react-i18next' import { Box, Grid, Typography } from '@mui/material' import { EmptyValueTag, FeeInfo, getShortAddr, IBData, L1L2_NAME_DEFINED, MapChainId, NFTWholeINFO, TOAST_TIME, } from '@loopring-web/common-resources' import { Button, Toast, ToastType, useAddressTypeLists } from '../../index' import { TransferViewProps } from './Interface' import { useSettings } from '../../../stores' import React from 'react' import { sanitize } from 'dompurify' export const TransferConfirm = & Partial, I, C extends FeeInfo>({ t, sureItsLayer2, handleConfirm, tradeData, lastFailed, onTransferClick, realAddr, type, feeInfo, feeWithActive, memo, }: Partial> & { handleConfirm: (index: number) => void } & WithTranslation) => { const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { walletList } = useAddressTypeLists() const [open, setOpen] = React.useState(false) return ( {t('labelL2toL2Title', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} {t('labelL2toL2TokenAmount')} {tradeData?.tradeValue} {t('labelL2toL2Address')} {realAddr} {t('labelL2toL2AddressType')} {walletList.find((item) => item.value === sureItsLayer2)?.label} {feeWithActive ? t('labelL2toL2FeeWithActive', { addr: getShortAddr(realAddr ?? ''), }) : t('labelL2toL2Fee')} {feeInfo?.fee + ' '} {feeInfo?.belong} {t('labelMemo')} {memo ?? EmptyValueTag} { setOpen(false) }} severity={ToastType.error} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/TransferNFTBurn.tsx ================================================ import { Trans, useTranslation } from 'react-i18next' import React from 'react' import { Box, Divider, Grid, Typography } from '@mui/material' import { bindHover } from 'material-ui-popup-state' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { FeeInfo, GET_IPFS_STRING, IBData, Info2Icon, L1L2_NAME_DEFINED, MapChainId, NFTWholeINFO, TRADE_TYPE, TradeBtnStatus, myLog, } from '@loopring-web/common-resources' import { Button, FeeSelect, InputSize, SwitchData, TransferProps } from '../../index' import { PopoverPure } from '../..' import { NFTInput } from './BasicANFTTrade' import { useSettings } from '../../../stores' import styled from '@emotion/styled' const BoxStyle = styled(Box)` .coinInput-wrap, .btnInput-wrap, .MuiOutlinedInput-root { background: var(--field-opacity); border-color: var(--opacity); :hover { border-color: var(--color-border-hover); } } ` export const TransferNFTBurn = & Partial, I, C extends FeeInfo>({ type = TRADE_TYPE.NFT, transferBtnStatus, disabled, walletMap, handleFeeChange, chargeFeeTokenList, feeInfo, isFeeNotEnough, onTransferClick, transferI18nKey, tradeData, handlePanelEvent, ...rest }: TransferProps & { getIPFSString: GET_IPFS_STRING assetsData: any[] }) => { const { t } = useTranslation() const inputBtnRef = React.useRef() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] myLog('Burn tradeData', tradeData) const inputButtonDefaultProps = { label: t('labelL2toL2EnterToken'), size: InputSize.middle, } const [showFeeModal, setShowFeeModal] = React.useState(false) const popupState = usePopupState({ variant: 'popover', popupId: `popupId-transfer`, }) const getDisabled = React.useMemo(() => { return disabled || transferBtnStatus === TradeBtnStatus.DISABLED }, [disabled, transferBtnStatus]) const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } return ( {t('labelL2NFTBurnTitle', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} Transfer to any valid Ethereum addresses instantly. Please make sure the recipient address accepts Loopring layer-2 payments before you proceed. '' as any), disabled, walletMap, tradeData, onChangeEvent:async (_index: 0 | 1, { to, tradeData }: SwitchData) => { handlePanelEvent && handlePanelEvent({ to, tradeData }, `Tobutton` as any) }, inputNFTDefaultProps: {...inputButtonDefaultProps, label: '', size: InputSize.middle}, inputNFTRef: inputBtnRef, } as any)} /> {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/TransferWrap.tsx ================================================ import { Trans, WithTranslation } from 'react-i18next' import React from 'react' import { Box, Checkbox, FormControlLabel as MuiFormControlLabel, Grid, IconButton, InputAdornment, Typography, } from '@mui/material' import { bindHover } from 'material-ui-popup-state/es' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { AddressError, AlertIcon, BackIcon, CheckBoxIcon, CheckedIcon, CloseIcon, ContactIcon, copyToClipBoard, DropDownIcon, EmptyValueTag, EXCHANGE_TYPE, FeeInfo, fontDefault, globalSetup, hexToRGB, IBData, Info2Icon, L1L2_NAME_DEFINED, LoadingIcon, MapChainId, myLog, NFTWholeINFO, TOAST_TIME, TradeBtnStatus, WALLET_TYPE, } from '@loopring-web/common-resources' import { Button, FeeSelect, GridWrapStyle, InputSize, TextField, Toast, ToastType, } from '../../index' import { PopoverPure } from '../../' import { TransferViewProps } from './Interface' import { BasicACoinTrade } from './BasicACoinTrade' import { NFTInput } from './BasicANFTTrade' import { useSettings } from '../../../stores' import { TransferAddressType } from './AddressType' import { useTheme } from '@emotion/react' export const TransferWrap = & Partial, I, C extends FeeInfo>({ t, disabled, walletMap, tradeData, // @ts-ignore coinMap, transferI18nKey, type, memo, chargeFeeTokenList, activeAccountPrice, feeInfo, lastFailed, isFeeNotEnough, handleSureItsLayer2, handleFeeChange, isThumb, transferBtnStatus, addressDefault, handleOnAddressChange, sureItsLayer2, wait = globalSetup.wait, assetsData = [], realAddr, isLoopringAddress, addrStatus, handleConfirm, handleOnMemoChange, isAddressCheckLoading, isSameAddress, isSmartContractAddress, baseURL, isActiveAccount, isActiveAccountFee, feeWithActive, handleOnFeeWithActive, contact, isFromContact, onClickContact, loopringSmartWalletVersion, isENSWrong, ens, geUpdateContact, // addressType, ...rest }: TransferViewProps & WithTranslation & { assetsData: any[] handleConfirm: (index: number) => void }) => { const inputBtnRef = React.useRef() const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] // addressType const inputButtonDefaultProps = { label: t('labelL2toL2EnterToken'), size: InputSize.middle, } const [showFeeModal, setShowFeeModal] = React.useState(false) const popupState = usePopupState({ variant: 'popover', popupId: `popupId-transfer`, }) const getDisabled = React.useMemo(() => { return disabled || transferBtnStatus === TradeBtnStatus.DISABLED }, [disabled, transferBtnStatus]) const [copyToastOpen, setCopyToastOpen] = React.useState(false) const onCopy = React.useCallback( async (content: string) => { await copyToClipBoard(content) setCopyToastOpen(true) }, [setCopyToastOpen], ) const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } const isInvalidAddressOrENS = !isAddressCheckLoading && addressDefault && [AddressError.InvalidAddr, AddressError.IsNotLoopringContract].includes(addrStatus) const detectedWalletType = loopringSmartWalletVersion === undefined ? undefined : loopringSmartWalletVersion.isLoopringSmartWallet ? WALLET_TYPE.Loopring : isSmartContractAddress ? WALLET_TYPE.OtherSmart : WALLET_TYPE.EOA const isExchange = React.useMemo(() => { return !!(sureItsLayer2 && sureItsLayer2 in EXCHANGE_TYPE) }, [sureItsLayer2, sureItsLayer2]) const isOtherSmartWallet = detectedWalletType === WALLET_TYPE.OtherSmart const theme = useTheme() myLog('transferWrap', realAddr) const view = React.useMemo(() => { if (isOtherSmartWallet && realAddr) { return ( {t('labelNotOtherSmartWallet', { loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) } else if (isInvalidAddressOrENS && addressDefault) { return ( {t(`labelL2toL2${addrStatus}`)} ) } else if (isExchange && addressDefault) { return ( {t('labelNotExchangeEOA', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} ) } else if (isSameAddress && addressDefault) { return ( {t('labelInvalidisSameAddress', { way: t('labelL2toL2', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), })} ) } else { return ( <> {addressDefault && realAddr && !isAddressCheckLoading && ( {realAddr} )} {!isAddressCheckLoading && addressDefault && addrStatus === AddressError.NoError && isActiveAccount === false && ( {isActiveAccount === false && realAddr && ( This address does not have an activated Loopring L2. Please ensure the recipient can access Loopring L2 before sending. )} {!isActiveAccountFee && realAddr ? ( { handleOnFeeWithActive(state) }} checkedIcon={} icon={} color='default' /> } label={ {t('labelL2toL2AddressFeeActiveFee', { value: activeAccountPrice, layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} } /> ) : ( <> )} )} ) } }, [ addressDefault, isActiveAccount, isActiveAccountFee, feeWithActive, addrStatus, realAddr, isAddressCheckLoading, activeAccountPrice, isInvalidAddressOrENS, isExchange, isOtherSmartWallet, isSameAddress, isLoopringAddress, isAddressCheckLoading, ]) return ( {t('labelL2toL2Title', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, })} Transfer to any valid Ethereum addresses instantly. Please make sure the recipient address accepts Loopring layer-2 payments before you proceed. {type === 'NFT' ? ( '' as any), disabled, walletMap, tradeData, coinMap, inputNFTDefaultProps: { label: '', size: InputSize.middle }, inputNFTRef: inputBtnRef, } as any)} /> ) : ( )} handleOnAddressChange(event?.target?.value)} disabled={!chargeFeeTokenList.length} SelectProps={{ IconComponent: DropDownIcon }} fullWidth={true} InputProps={{ style: { paddingRight: '0', }, endAdornment: isFromContact ? undefined : ( {addressDefault !== '' ? ( isAddressCheckLoading ? ( ) : ( handleOnAddressChange('')} > ) ) : ( '' )} { onClickContact() }} > ), }} /> {view} {isENSWrong && ( <> {ens} )} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(fee as C) setShowFeeModal(false) }} feeInfo={feeInfo as FeeInfo} open={showFeeModal} onClose={() => { setShowFeeModal(false) }} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} feeLoading={isFeeNotEnough.isOnLoading} onClickFee={() => setShowFeeModal((prev) => !prev)} /> )} { setCopyToastOpen(false) }} severity={ToastType.success} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/VaultWrap/Interface.ts ================================================ import { InputButtonProps } from '../../../basic-lib' import { CoinInfo, ForexMap, IBData, TradeBtnStatus } from '@loopring-web/common-resources' import { BasicACoinTradeViewProps, SwitchData } from '../Interface' import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' export type VaultJoinBaseProps = { btnStatus?: keyof typeof TradeBtnStatus | undefined onSubmitClick: (data: T) => void | any btnI18nKey?: string propsExtends?: Partial> vaultJoinData: V tradeData: T isActiveAccount: boolean onRefreshData: () => void refreshRef: React.Ref onToggleAddRedeem: (value: 'Add' | 'Redeem') => void isAddOrRedeem: 'Add' | 'Redeem' panelIndex: number handleConfirm: (index: number) => void basicTrade: { onChangeEvent: any; switchData: any } modalOpen: boolean onCloseModal: () => void, } & Partial, 'handleError'>> export type VaultJoinExtendProps> = { switchStobEvent?: (_isStoB: boolean) => void disabled?: boolean onChangeEvent: (index: 0 | 1, data: SwitchData) => void tokenProps?: Partial>> onBack: () => {} marginLevelChange: { from: { marginLevel: string type: 'safe' | 'warning' | 'danger' } to: { marginLevel: string type: 'safe' | 'warning' | 'danger' } } | undefined holdingCollateral?: string } export type VaultJoinWrapProps = VaultJoinBaseProps & VaultJoinExtendProps export type VaultExitBaseProps = { btnStatus?: keyof typeof TradeBtnStatus | undefined onSubmitClick: () => void | any vaultExitBtnI18nKey?: string onClose: () => void confirmLabel?: string cancelLabel?: string disabled?: boolean } // export type VaultExitWrapProps = VaultExitBaseProps export type VaultBorrowBaseProps = { disabled?: boolean vaultBorrowBtnStatus?: keyof typeof TradeBtnStatus | undefined vaultBorrowBtnI18nKey?: string onVaultBorrowClick: () => void | any tokenProps?: Partial>> onChangeEvent: (index: 0 | 1, data: SwitchData) => void vaultBorrowData: B propsExtends?: Partial>> tradeData: T onRefreshData: () => void refreshRef: React.Ref marginLevelChange: { from: { marginLevel: string type: 'safe' | 'warning' | 'danger' } to: { marginLevel: string type: 'safe' | 'warning' | 'danger' } } | undefined userLeverage: string onClickLeverage: () => void hideLeverage?: boolean } export type VaultBorrowWrapProps = BasicACoinTradeViewProps & VaultBorrowBaseProps export type VaultRepayWrapProps = BasicACoinTradeViewProps & { disabled?: boolean vaultRepayBtnStatus?: keyof typeof TradeBtnStatus | undefined onVaultRepayClick: () => void | any vaultRepayBtnI18nKey?: string tokenProps?: Partial>> propsExtends?: Partial>> vaultRepayData: VR tradeData: T forexMap: ForexMap tokenInfo?: sdk.VaultToken marginLevelChange: { from: { marginLevel: string type: 'safe' | 'warning' | 'danger' } to: { marginLevel: string type: 'safe' | 'warning' | 'danger' } } | undefined initialSymbol: string | undefined } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/VaultWrap/VaultBorrow.tsx ================================================ import { EmptyValueTag, IBData, L1L2_NAME_DEFINED, MapChainId, myLog, TokenType, TRADE_TYPE, TradeBtnStatus, VaultBorrowData, } from '@loopring-web/common-resources' import { CoinIcon, } from '@loopring-web/component-lib' import { VaultBorrowWrapProps } from './Interface' import React from 'react' import { useTranslation } from 'react-i18next' import { useSettings } from '../../../../stores' import { Grid, Typography, Divider, Box } from '@mui/material' import { BasicACoinTrade } from '../BasicACoinTrade' import { ButtonStyle } from '../Styled' import { marginLevelTypeToColor } from './utils' import { numberFormat } from '@loopring-web/core' import EastIcon from '@mui/icons-material/East'; export const VaultBorrowWrap = < T extends IBData & { erc20Symbol: string }, V extends VaultBorrowData, I, >({ disabled, vaultBorrowBtnStatus, vaultBorrowBtnI18nKey, onVaultBorrowClick, tokenProps, onChangeEvent, vaultBorrowData, walletMap, tradeData, coinMap, propsExtends, marginLevelChange, userLeverage, onClickLeverage, hideLeverage, ...rest }: VaultBorrowWrapProps) => { const inputBtnRef = React.useRef() const { t, i18n } = useTranslation() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const getDisabled = () => { return disabled || vaultBorrowData === undefined || vaultBorrowData?.coinInfoMap === undefined } const inputButtonDefaultProps = { label: t('labelVaultBrowserToken'), subLabel: t('labelVaultMaxBrowserLabel'), tokenType: TokenType.vault, placeholderText: vaultBorrowData.minBorrowStr ? t('labelInvestMiniDual', { value: vaultBorrowData.minBorrowStr, }) : '0.00', tokenImageKey: vaultBorrowData?.tradeData?.erc20Symbol, belongAlice: vaultBorrowData?.tradeData?.erc20Symbol, } const label = React.useMemo(() => { if (vaultBorrowBtnI18nKey) { const key = vaultBorrowBtnI18nKey.split('|') return t( key[0], key && key[1] ? { arg: key[1].toString(), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(`labelVaultBorrowBtn`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [vaultBorrowBtnI18nKey]) return ( {!hideLeverage && {t('labelVaultLeverage')} {userLeverage}x } {t('labelVaultBorrowed')} {vaultBorrowData.borrowedAmt !== '0' && vaultBorrowData.borrowedAmt ? ( ) : undefined} {vaultBorrowData.borrowedAmt !== '0' && vaultBorrowData.borrowedAmt ? vaultBorrowData.borrowedStr + ' ' + (vaultBorrowData as any).erc20Symbol : EmptyValueTag} {t('labelHourlyInterestRate')} {vaultBorrowData && (vaultBorrowData as any).hourlyRateInPercent && (vaultBorrowData as any).yearlyRateInPercent ? `${(vaultBorrowData as any).hourlyRateInPercent}% (APR: ${ (vaultBorrowData as any).yearlyRateInPercent }%)` : EmptyValueTag} {t('labelVaultMarginLevel')} {marginLevelChange ? ( <> {numberFormat(marginLevelChange.from.marginLevel, {fixed: 2})} {numberFormat(marginLevelChange.to.marginLevel, {fixed: 2})} ) : ( EmptyValueTag )} { onVaultBorrowClick() }} loading={ !getDisabled() && vaultBorrowBtnStatus === TradeBtnStatus.LOADING ? 'true' : 'false' } disabled={ getDisabled() || vaultBorrowBtnStatus === TradeBtnStatus.DISABLED || vaultBorrowBtnStatus === TradeBtnStatus.LOADING } fullWidth={true} > {label} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/VaultWrap/VaultJoin.tsx ================================================ import { CoinInfo, EmptyValueTag, getValuePrecisionThousand, IBData, Info2Icon, L1L2_NAME_DEFINED, MapChainId, mapSpecialTokenName, TokenType, TRADE_TYPE, TradeBtnStatus, VaultJoinData, } from '@loopring-web/common-resources' import { Trans, useTranslation } from 'react-i18next' import { VaultJoinWrapProps } from './Interface' import React from 'react' import { Box, Grid, Tooltip, Typography } from '@mui/material' import { useSettings } from '../../../../stores' import { ButtonStyle } from '../Styled' import { BasicACoinTrade } from '../BasicACoinTrade' import { InputButtonDefaultProps } from '../Interface' import { BasicACoinInput } from '../BasicACoinInput' import { CoinIcon } from '@loopring-web/component-lib' import InfoIcon from '@mui/icons-material/Info' import { numberFormat } from '@loopring-web/core' import { marginLevelTypeToColor } from './utils' import EastIcon from '@mui/icons-material/East' export const VaultJoinWrap = , I, V extends VaultJoinData>({ disabled, btnStatus, tradeData, vaultJoinData, btnI18nKey, onSubmitClick, onChangeEvent, propsExtends = {}, isActiveAccount, // coinAPrecision, // coinBPrecision, tokenProps, marginLevelChange, holdingCollateral, ...rest }: VaultJoinWrapProps) => { const { t, ...i18n } = useTranslation() const inputBtnRef = React.useRef() // const inputCoinRef = React.useRef() let { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] // const [minFee] = React.useState<{ minFee: string } | undefined>(undefined) const getDisabled = React.useMemo(() => { return disabled || btnStatus === TradeBtnStatus.DISABLED }, [btnStatus, disabled]) const inputButtonDefaultProps: InputButtonDefaultProps> = { label: t('labelEnterToken'), focusOnInput: true, } // myLog('VaultJoinWrap inputBtnRef', inputBtnRef) const label = React.useMemo(() => { if (btnI18nKey) { const key = btnI18nKey.split('|') return t( key[0], key && key[1] ? { arg: key[1].toString(), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(isActiveAccount ? `labelVaultJoinBtn` : `labelVaultConfirm`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [btnI18nKey, isActiveAccount, network, t]) return ( {/* {rest.isAddOrRedeem === 'Add' ? ( {t('labelVaultQuota')} {vaultJoinData && vaultJoinData?.maxShowVal ? ( {vaultJoinData?.maxShowVal + ' ' + mapSpecialTokenName(vaultJoinData?.belong as string) } ) : ( EmptyValueTag )} ) : ( {t('labelVaultHoldingCollateral')} {holdingCollateral ? ( {holdingCollateral + ' ' + mapSpecialTokenName(vaultJoinData?.belong as string)} ) : ( EmptyValueTag )} )} {!isActiveAccount ? ( {t('labelVaultMarginLevel')} {marginLevelChange ? ( <> {numberFormat(marginLevelChange.from.marginLevel, { fixed: 2 })} {numberFormat(marginLevelChange.to.marginLevel, { fixed: 2 })} ) : ( EmptyValueTag )} ) : null} */} {t('labelVaultRedeemAlert')} { onSubmitClick(tradeData) }} loading={!getDisabled && btnStatus === TradeBtnStatus.LOADING ? 'true' : 'false'} disabled={ getDisabled || btnStatus === TradeBtnStatus.DISABLED || btnStatus === TradeBtnStatus.LOADING } fullWidth={true} > {label} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/VaultWrap/VaultRepay.tsx ================================================ import { BackIcon, EmptyValueTag, getValuePrecisionThousand, IBData, L1L2_NAME_DEFINED, MapChainId, myLog, TokenType, TradeBtnStatus, VaultRepayData, } from '@loopring-web/common-resources' import { VaultRepayWrapProps } from './Interface' import { useTranslation } from 'react-i18next' import React from 'react' import { Grid, Typography, Box, Divider } from '@mui/material' import { ButtonStyle } from '../Styled' import { useSettings } from '../../../../stores' import { BasicACoinTrade } from '../BasicACoinTrade' import { CoinIcon, } from '@loopring-web/component-lib' import { numberFormat } from '@loopring-web/core' import { marginLevelTypeToColor } from './utils' import EastIcon from '@mui/icons-material/East'; export const VaultRepayWrap = < T extends IBData & { borrowed: string; max: string }, I, VR extends VaultRepayData, >({ disabled, vaultRepayBtnStatus, onVaultRepayClick, vaultRepayBtnI18nKey, tokenProps, propsExtends, tradeData, vaultRepayData, onChangeEvent, walletMap, tokenInfo, handleError, marginLevelChange, ...rest }: VaultRepayWrapProps) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const coinRef = React.useRef() const { t, i18n } = useTranslation() const getDisabled = () => { return ( disabled || vaultRepayData?.tradeData === undefined || vaultRepayData?.coinInfoMap === undefined ) } const inputButtonDefaultProps = { label: t('labelTokenAmount'), subLabel: '', placeholderText: vaultRepayData.minRepayStr ? t('labelInvestMiniDual', { value: vaultRepayData.minRepayStr, }) : '0.00', maxAllow: true, tokenType: TokenType.vault, order: 'right', tokenImageKey: vaultRepayData?.tradeData?.erc20Symbol, belongAlice: vaultRepayData?.tradeData?.erc20Symbol, // maxValue: vaultRepayData?.tradeData?.borrowed, } const label = React.useMemo(() => { if (vaultRepayBtnI18nKey) { const key = vaultRepayBtnI18nKey.split('|') return t( key[0], key && key[1] ? { arg: key[1].toString(), l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t(`labelVaultRepayBtn`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) } }, [vaultRepayBtnI18nKey, network]) return ( {t('labelVaultRepayBalance')} {getValuePrecisionThousand( vaultRepayData?.tradeData?.balance, tokenInfo?.vaultTokenAmounts.qtyStepScale ?? 6, tokenInfo?.vaultTokenAmounts.qtyStepScale ?? 6, ) + ' ' + (vaultRepayData as any).erc20Symbol} {t('labelRepayQuota')} {getValuePrecisionThousand( vaultRepayData?.tradeData?.borrowed, tokenInfo?.precision ?? 6, tokenInfo?.precision ?? 6, ) + ' ' + (vaultRepayData as any).erc20Symbol} {t('labelVaultMarginLevel')} {marginLevelChange ? ( <> {numberFormat(marginLevelChange.from.marginLevel, {fixed: 2})} {numberFormat(marginLevelChange.to.marginLevel, {fixed: 2})} ) : ( EmptyValueTag )} { onChangeEvent(1, { tradeData: { ...tradeData, tradeValue: 0 }, to: 'menu', }) }} startIcon={} fullWidth={true} > {t('labelBack')} { onVaultRepayClick() }} loading={ !getDisabled() && vaultRepayBtnStatus === TradeBtnStatus.LOADING ? 'true' : 'false' } disabled={ getDisabled() || vaultRepayBtnStatus === TradeBtnStatus.DISABLED || vaultRepayBtnStatus === TradeBtnStatus.LOADING } fullWidth={true} > {label} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/VaultWrap/index.ts ================================================ // this is a private component, only used in panel component // please do not export this index to global // export * from './Interface' export * from './Interface' export * from './VaultJoin' export * from './VaultBorrow' export * from './VaultRepay' ================================================ FILE: packages/component-lib/src/components/tradePanel/components/VaultWrap/styled.ts ================================================ import styled from '@emotion/styled' export const SvgStyled = styled.div` & svg { width: 24px; height: 24px; } ` ================================================ FILE: packages/component-lib/src/components/tradePanel/components/VaultWrap/utils.ts ================================================ export const marginLevelTypeToColor = (type: 'safe' | 'warning' | 'danger') => { return type === 'safe' ? 'var(--color-success)' : type === 'warning' ? 'var(--color-warning)' : 'var(--color-error)' } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/WithdrawConfirm.tsx ================================================ import { useTranslation } from 'react-i18next' import { Box, Grid, Typography } from '@mui/material' import { EmptyValueTag, FeeInfo, IBData, L1L2_NAME_DEFINED, MapChainId, NFTWholeINFO, TOAST_TIME, } from '@loopring-web/common-resources' import { Button, Toast, ToastType, useAddressTypeLists } from '../../index' import { WithdrawViewProps } from './Interface' import { useSettings } from '../../../stores' import React from 'react' import { sanitize } from 'dompurify' export const WithdrawConfirm = & Partial, I, C extends FeeInfo>({ handleConfirm, tradeData, onWithdrawClick, realAddr, lastFailed, type, feeInfo, isToMyself, sureIsAllowAddress, }: Partial> & { handleConfirm: (index: number) => void }) => { const { t } = useTranslation() const { isMobile, defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [open, setOpen] = React.useState(false) const { walletList, exchangeList } = useAddressTypeLists() return ( {(tradeData as NFTWholeINFO)?.isCounterFactualNFT && (tradeData as NFTWholeINFO)?.deploymentStatus === 'NOT_DEPLOYED' ? t('labelL2ToL1DeployTitle', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol }) : isToMyself ? t('labelL2ToMyL1Title', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol }) : t('labelL2ToOtherL1Title', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol })} {t('labelL2toL2TokenAmount')} {tradeData?.tradeValue} {t('labelL2toL2Address')} {realAddr} {!isToMyself && ( {t('labelL2toL1AddressType')} { [...walletList, ...exchangeList].find((item) => item.value === sureIsAllowAddress) ?.label } )} {t('labelForceWithdrawFee')} {feeInfo?.fee + ' '} {feeInfo?.belong} {lastFailed && ( {t('labelConfirmAgainByFailedWithBalance', { symbol: type === 'NFT' ? 'NFT' : ` ${tradeData?.belong}` ?? EmptyValueTag, count: tradeData?.balance, })} )} { setOpen(false) }} severity={ToastType.error} /> ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/WithdrawWrap.tsx ================================================ import { Trans, WithTranslation } from 'react-i18next' import React, { useState } from 'react' import { bindHover } from 'material-ui-popup-state/es' import { bindPopper, usePopupState } from 'material-ui-popup-state/hooks' import { Box, Checkbox, Grid, IconButton, InputAdornment, Tooltip, Typography } from '@mui/material' import { AddressError, AlertIcon, AssetsRawDataItem, BackIcon, CheckBoxIcon, CheckedIcon, CloseIcon, ContactIcon, copyToClipBoard, DropDownIcon, EmptyValueTag, FeeInfo, fontDefault, globalSetup, hexToRGB, IBData, Info2Icon, L1L2_NAME_DEFINED, LoadingIcon, MapChainId, NFTWholeINFO, TOAST_TIME, TradeBtnStatus, WALLET_TYPE, WithdrawType, } from '@loopring-web/common-resources' import { CustomCheckBox, FeeSelect, GridWrapStyle, InputSize, PopoverPure, SpaceBetweenBox, Toast, ToastType } from '../..' import { Button, TextField, useSettings } from '../../../index' import { WithdrawViewProps } from './Interface' import { BasicACoinTrade } from './BasicACoinTrade' import { NFTInput } from './BasicANFTTrade' import { FullAddressType } from './AddressType' import * as sdk from '@loopring-web/loopring-sdk' import { useTheme } from '@emotion/react' import { useSystem } from '@loopring-web/core' import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked' import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'; const formatTokenList = (tokens: string[]):string => { if (!tokens || tokens.length === 0) return ''; if (tokens.length === 1) return tokens[0]; if (tokens.length === 2) return `${tokens[0]} and ${tokens[1]}`; const lastToken = tokens[tokens.length - 1]; const otherTokens = tokens.slice(0, -1); return `${otherTokens.join(', ')}, and ${lastToken}`; } export const WithdrawWrap = < T extends IBData | (NFTWholeINFO & IBData), I, C extends FeeInfo, >({ t, title, disabled, walletMap, tradeData, coinMap, type, withdrawI18nKey, addressDefault, accAddr, isNotAvailableAddress, withdrawType, chargeFeeTokenList = [], feeInfo, lastFailed, handleConfirm, isFeeNotEnough, withdrawBtnStatus, handleFeeChange, handleWithdrawTypeChange, handleOnAddressChange, isAddressCheckLoading, isCFAddress, isLoopringAddress, isContractAddress, isFastWithdrawAmountLimit, addrStatus, disableWithdrawList = [], wait = globalSetup.wait, assetsData = [], realAddr, isThumb, baseURL, isToMyself = false, sureIsAllowAddress, handleSureIsAllowAddress, contact, isFromContact, onClickContact, loopringSmartWalletVersion, isENSWrong, ens, geUpdateContact, withdrawMode, ...rest }: WithdrawViewProps & WithTranslation & { assetsData: AssetsRawDataItem[] handleConfirm: (index: number) => void }) => { const { isMobile, defaultNetwork } = useSettings() const { app } = useSystem() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const theme = useTheme() const [dropdownStatus, setDropdownStatus] = React.useState<'up' | 'down'>('down') const popupState = usePopupState({ variant: 'popover', popupId: `popupId-withdraw`, }) const [copyToastOpen, setCopyToastOpen] = useState(false) const onCopy = React.useCallback( async (content: string) => { copyToClipBoard(content) setCopyToastOpen(true) }, [setCopyToastOpen], ) const inputBtnRef = React.useRef() const getDisabled = React.useMemo(() => { return disabled || withdrawBtnStatus === TradeBtnStatus.DISABLED }, [disabled, withdrawBtnStatus]) const inputButtonDefaultProps = { label: t('labelL2toL1EnterToken'), // loading: isFeeNotEnough.isOnLoading, } const handleToggleChange = (value: C) => { if (handleFeeChange) { handleFeeChange(value) } } const _handleWithdrawTypeChange = React.useCallback( (e: WithdrawType) => { if (handleWithdrawTypeChange) { handleWithdrawTypeChange(e as any) } }, [handleWithdrawTypeChange], ) const isInvalidAddressOrENS = !isAddressCheckLoading && addressDefault && addrStatus === AddressError.InvalidAddr const allowToClickIsSure = React.useMemo(() => { return isAddressCheckLoading || addrStatus === AddressError.InvalidAddr || !realAddr }, [addrStatus, isAddressCheckLoading, realAddr]) const isEarn = app === 'earn'; const label = React.useMemo(() => { if (isEarn) { return t('labelWithdrawBtn') } else if (withdrawI18nKey) { const key = withdrawI18nKey.split('|') return t( key[0], key && key[1] ? { arg: key[1], l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } else { return t( (tradeData as NFTWholeINFO)?.isCounterFactualNFT && (tradeData as NFTWholeINFO)?.deploymentStatus === 'NOT_DEPLOYED' ? `labelSendL1DeployBtn` : `labelSendL1Btn`, { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) } }, [t, tradeData, withdrawI18nKey]) const detectedWalletType = loopringSmartWalletVersion?.isLoopringSmartWallet ? WALLET_TYPE.Loopring : isContractAddress ? WALLET_TYPE.OtherSmart : WALLET_TYPE.EOA return ( {isEarn ? t('labelWithdrawBtn') : title ? title : (tradeData as NFTWholeINFO)?.isCounterFactualNFT && (tradeData as NFTWholeINFO)?.deploymentStatus === 'NOT_DEPLOYED' ? t('labelL2ToL1DeployTitle', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol }) : isToMyself ? t('labelL2ToMyL1Title', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol }) : t('labelL2ToOtherL1Title', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol })} Your withdrawal will be processed in the next batch, which usually takes 30 minutes to 2 hours. (There will be a large delay if the Ethereum gas price exceeds 500 GWei.) {type === 'NFT' ? ( '' as any), disabled, walletMap, tradeData, coinMap, inputNFTDefaultProps: { label: '', size: InputSize.middle }, inputNFTRef: inputBtnRef, } as any)} /> ) : ( )} {!isToMyself ? ( <> handleOnAddressChange(event?.target?.value)} label={t('labelL2toL1Address', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} SelectProps={{ IconComponent: DropDownIcon }} fullWidth={true} InputProps={{ style: { paddingRight: '0', }, endAdornment: isFromContact ? undefined : ( {addressDefault !== '' ? ( isAddressCheckLoading ? ( ) : ( handleOnAddressChange('')} > ) ) : ( '' )} { onClickContact!() }} > ), }} /> ) : ( {t('labelL2toL1MyAddress', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol })} {!!isAddressCheckLoading && ( )} )} {isInvalidAddressOrENS ? ( {t('labelInvalidAddress')} ) : isNotAvailableAddress ? ( {t(`labelInvalid${isNotAvailableAddress}`, { token: type === 'NFT' ? 'NFT' : tradeData.belong, way: t(`labelL2toL1`, { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), })} ) : ( <> {addressDefault && realAddr && !isAddressCheckLoading && ( {realAddr} )} )} {isENSWrong && ( <> {ens} )} {!isToMyself && ( )} {withdrawMode?.showTrustUI && ( {withdrawMode?.showFastMode && ( { withdrawMode.onChange('fast') }} /> Trust Mode: Operated by Loopring’s team to maintain liquidity.
    Supported Assets: {formatTokenList(withdrawMode.fastModeSupportedTokens)}. } > Trust Mode
    {withdrawMode.fastMode?.fee ?? '--'} {withdrawMode.fastMaxAlert.show && ( {withdrawMode.fastMaxAlert.message} )}
    } alignItems={'center'} rightNode={ {withdrawMode.fastMode?.time ?? '--'} } border={'1px solid var(--color-border)'} borderBottom={'none'} /> )} { withdrawMode?.onChange('normal') }} /> {withdrawMode?.showTrustUI ? 'Trustless Mode' : 'Normal Mode'} {withdrawMode?.normalMode?.fee ?? '--'} } alignItems={'center'} rightNode={ {withdrawMode?.normalMode?.time ?? '--'} } border={'1px solid var(--color-border)'} /> )} {!chargeFeeTokenList?.length ? ( {t('labelFeeCalculating')} ) : ( <> { handleToggleChange(feeInfo as C) setDropdownStatus('down') }} feeInfo={feeInfo} open={dropdownStatus === 'up'} onClose={() => { setDropdownStatus('down') }} onClickFee={() => setDropdownStatus((prev) => (prev === 'up' ? 'down' : 'up'))} feeLoading={isFeeNotEnough.isOnLoading} isFeeNotEnough={isFeeNotEnough.isFeeNotEnough} isFastWithdrawAmountLimit={isFastWithdrawAmountLimit} networkFeeElement={ withdrawMode?.showTrustUI ? ( Transaction Fee{' '} ) : ( Network Fee ) } /> )} {lastFailed && ( {t('labelConfirmAgainByFailedWithBalance', { symbol: type === 'NFT' ? 'NFT' : ` ${tradeData?.belong}` ?? EmptyValueTag, count: tradeData?.balance, })} )} { setCopyToastOpen(false) }} severity={ToastType.success} />
    ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/hook/BasicACoinPanelHook.tsx ================================================ import { IBData, MintTradeNFT, TRADE_TYPE } from '@loopring-web/common-resources' import { BasicACoinTradeHookProps } from '../Interface' import React from 'react' import { SwitchData } from '../../Interface' import { ToolBarItemBack } from '../tool' import { debounceTime, Subject } from 'rxjs' export const useBasicTrade = < T extends Partial & MintTradeNFT & { [key: string]: any }>, I, >({ tradeData, handlePanelEvent, walletMap = {}, coinMap, type = TRADE_TYPE.TOKEN, ...rest }: BasicACoinTradeHookProps) => { tradeData = tradeData ? tradeData : ({} as T) // data used on trade input btn click to menu list and back to the input data transfer const [switchData, setSwitchData] = React.useState>({ to: 'button', tradeData, } as SwitchData) // index is switch panel index number 1 is btn view const [index, setIndex] = React.useState(0) React.useEffect(() => { if (tradeData !== switchData.tradeData) { setSwitchData({ ...switchData, tradeData: tradeData }) } }, [tradeData?.tradeValue, tradeData?.belong, tradeData?.balance]) const panelEventSubject = new Subject<{ _index: 0 | 1; switchData: SwitchData } | undefined>() const onChangeEvent = (_index: 0 | 1, { to, tradeData }: SwitchData) => { panelEventSubject.next({ _index: _index, switchData: { to, tradeData } }) } const panelEventNext = React.useCallback( async ({ _index, switchData: { to, tradeData: newTradeData }, }: { _index: 0 | 1 switchData: SwitchData }) => { if (handlePanelEvent) { await handlePanelEvent({ to, tradeData: newTradeData }, `To${to}` as any) } if (typeof rest?.onChangeEvent == 'function') { setSwitchData(rest.onChangeEvent(_index, { to, tradeData: newTradeData })) } else { const _newTradeData = { ...tradeData, ...newTradeData, } if (to === 'menu') { setSwitchData({ tradeData: _newTradeData, to }) } else if (to === 'button' && type === 'TOKEN') { const balance = _newTradeData.belong ? walletMap[_newTradeData.belong]?.count : 0 const tradeValue = _newTradeData.tradeValue ? _newTradeData.tradeValue : undefined setSwitchData({ tradeData: { ..._newTradeData, tradeValue, balance: balance, }, to, }) } else if (to === 'button' && type === 'NFT') { const count = _newTradeData.balance const tradeValue = _newTradeData.tradeValue ? _newTradeData.tradeValue : undefined setSwitchData({ tradeData: { ..._newTradeData, tradeValue, balance: count, }, to, }) } } if (_index !== index) { setIndex(_index) } }, [handlePanelEvent, rest, index, tradeData, type, walletMap], ) React.useEffect(() => { panelEventSubject.pipe(debounceTime(200)).subscribe((result) => { if (result) { panelEventNext(result) } }) return () => { panelEventSubject.unsubscribe() } }, [panelEventSubject]) const toolBarItemBack = React.useMemo( () => , [tradeData, onChangeEvent], ) return { //toolbar UI toolBarItemBack, //Data, panel and function onChangeEvent, index, switchData: { ...switchData, }, } } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/hook/useAddressType.ts ================================================ import { AddressItemType, EXCHANGE_TYPE, L1L2_NAME_DEFINED, MapChainId, WALLET_TYPE, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import { useSettings } from '../../../../stores' export const useAddressTypeLists = () => { const { t } = useTranslation('common') const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const walletList: AddressItemType[] = [ { label: t('labelWalletTypeOptions', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, type: t(`labelWalletType${WALLET_TYPE.EOA}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), }), value: WALLET_TYPE.EOA as T, description: t(`label${WALLET_TYPE.EOA}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), }, { label: t('labelWalletTypeOptions', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, type: t(`labelWalletType${WALLET_TYPE.Loopring}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), }), value: WALLET_TYPE.Loopring as T, description: t(`label${WALLET_TYPE.Loopring}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), }, { label: t('labelWalletTypeOptions', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, type: t(`labelWalletType${WALLET_TYPE.OtherSmart}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), }), disabled: true, value: WALLET_TYPE.OtherSmart as T, description: t(`label${WALLET_TYPE.OtherSmart}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), }, { label: t(WALLET_TYPE.Exchange), value: WALLET_TYPE.Exchange as T, disabled: true, description: t(`label${WALLET_TYPE.Exchange}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }), }, ] const walletListFn: (type: WALLET_TYPE) => AddressItemType[] = (type: WALLET_TYPE) => { if (type === WALLET_TYPE.Exchange) throw 'wrong type' return [ { label: t('labelWalletTypeOptions', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, type: t(`labelWalletType${WALLET_TYPE.EOA}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }), disabled: type === WALLET_TYPE.EOA ? false : true, value: WALLET_TYPE.EOA as T, description: t(`label${WALLET_TYPE.EOA}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, { label: t('labelWalletTypeOptions', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, type: t(`labelWalletType${WALLET_TYPE.Loopring}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }), disabled: type === WALLET_TYPE.Loopring ? false : true, value: WALLET_TYPE.Loopring as T, description: t(`label${WALLET_TYPE.Loopring}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, { label: t('labelWalletTypeOptions', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, type: t(`labelWalletType${WALLET_TYPE.OtherSmart}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }), disabled: type === WALLET_TYPE.OtherSmart ? false : true, value: WALLET_TYPE.OtherSmart as T, description: t(`label${WALLET_TYPE.OtherSmart}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, { label: t(`labelExchange${EXCHANGE_TYPE.Binance}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), disabled: type === WALLET_TYPE.EOA ? false : true, value: EXCHANGE_TYPE.Binance as T, description: t('labelContactsBinanceNotSupportted', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, { label: t(`labelExchange${EXCHANGE_TYPE.Huobi}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), disabled: type === WALLET_TYPE.EOA ? false : true, value: EXCHANGE_TYPE.Huobi as T, description: t('labelContactsHuobiNotSupportted', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, { label: t(`labelExchange${EXCHANGE_TYPE.Others}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), disabled: type === WALLET_TYPE.EOA ? false : true, value: EXCHANGE_TYPE.Others as T, description: t('labelContactsOtherExchangesNotSupportted', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, ] } const nonExchangeList: AddressItemType[] = [ { label: t(`labelNonExchangeType`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), value: EXCHANGE_TYPE.NonExchange as T, disabled: false, description: t(`labelNonExchangeTypeDes`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, ] const exchangeList: AddressItemType[] = [ { label: t(`labelExchange${EXCHANGE_TYPE.Binance}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), value: EXCHANGE_TYPE.Binance as T, disabled: false, description: t(`labelExchange${EXCHANGE_TYPE.Binance}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), maxWidth: 'initial', }, { label: t(`labelExchange${EXCHANGE_TYPE.Huobi}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), value: EXCHANGE_TYPE.Huobi as T, disabled: false, description: t(`labelExchange${EXCHANGE_TYPE.Huobi}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), maxWidth: 'initial', }, { label: t(`labelExchange${EXCHANGE_TYPE.Others}`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), value: EXCHANGE_TYPE.Others as T, disabled: false, description: t(`labelExchange${EXCHANGE_TYPE.Others}Des`, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), maxWidth: 'initial', }, ] return { walletList, walletListFn, nonExchangeList, exchangeList, } } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/index.ts ================================================ // this is a private component, only used in panel component // please do not export this index to global export * from './DepositWrap' export * from './ResetWrap' export * from './WithdrawWrap' export * from './TransferWrap' export * from './TransferNFTBurn' export * from './SwapWrap' export * from './AmmWrap' export * from './VaultWrap' export * from './hook/BasicACoinPanelHook' export * from './tool' export * from './ExportAccountWrap' ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/Dialogs.tsx ================================================ import { Box, Checkbox, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, FormControlLabel as MuiFormControlLabel, IconButton, Link, List, ListItem, Tooltip, Typography, } from '@mui/material' import { Trans, useTranslation, WithTranslation, withTranslation } from 'react-i18next' import { Button, CoinIcon, TextField } from '../../../basic-lib' import React from 'react' import { ConnectProviders } from '@loopring-web/web3-provider' import styled from '@emotion/styled' import { useOpenModals, useSettings } from '../../../../stores' import { useTheme } from '@emotion/react' import { Account, Bridge, CheckBoxIcon, CheckedIcon, CloseIcon, copyToClipBoard, DAY_MINUTE_FORMAT, DualInvestConfirmType, getValuePrecisionThousand, Info2Icon, L1L2_NAME_DEFINED, MapChainId, RiskAlertIcon, RiskIcon, SoursURL, TradeDefi, TradeProType, } from '@loopring-web/common-resources' import { useHistory, useLocation } from 'react-router-dom' import BigNumber from 'bignumber.js' import * as sdk from '@loopring-web/loopring-sdk' import moment from 'moment/moment' // const ModelStyle = styled(Box)` // ${({ theme }) => modalContentBaseStyle({ theme: theme })}; // background: ${({ theme }) => theme.colorBase.box}; // ` as typeof Box const RiskStyle = styled(Dialog)` .MuiPaper-root { width: var(--modal-width); background: var(--color-box); } .MuiDialogTitle-root { margin: ${({ theme }) => 5 * theme.unit}px 0 ${({ theme }) => 1 * theme.unit}px; display: flex; flex-direction: column; align-content: center; align-items: center; } .infomation { ul { background: var(--color-box-enhance); display: flex; flex-direction: column; justify-content: stretch; padding: 0 ${({ theme }) => 2 * theme.unit}px; ${({ theme }) => theme.border.defaultFrame({ c_key: 'rgba(0,0,0,0)', d_R: 0.5 })}; li { height: var(--row-height); display: flex; flex-direction: row; align-items: center; justify-content: space-between; } } } .MuiDialogActions-root { > :not(:first-of-type) { margin-left: 0px !important; } } .titleContent { margin: ${({ theme }) => 2 * theme.unit}px 0 0 0; } .detail { margin: 0 0 ${({ theme }) => 2 * theme.unit}px 0; .des { font-size: ${({ theme }) => theme.fontDefault.body1}; color: var(--color-text-secondary); } } ` export type RiskInformation = { label: string | JSX.Element value: string | JSX.Element color: string } export const RiskComponent = ({ open, handleClose, handleConfirm, infos, description, checkElement, hasNoIcon = false, isAgree = true, confirmLabel = 'labelRiskAgree', cancelLabel = 'labelRiskCancel', strongBtn = 'cancel', title, }: { open: boolean handleClose: () => void handleConfirm: () => void hasNoIcon?: boolean infos?: RiskInformation[] description?: JSX.Element[] checkElement?: JSX.Element strongBtn?: string isAgree?: boolean // if mutiple when all aggree set true confirmLabel?: string cancelLabel?: string title: string | JSX.Element | (() => JSX.Element) }) => { const { t } = useTranslation('common') const size = 60 // myLog('title', title) return ( handleClose()} > <> {!hasNoIcon && ( )} {typeof title === 'string' ? ( {title} ) : ( <> {title} )} {infos && ( {infos.map(({ label, value, color }, index) => { return ( {typeof label === 'string' ? ( {label} ) : ( {label} )} {value} ) })} )} {description && description.map((item, index) => { return ( {item} ) })} {checkElement && ( {checkElement} )} ) } const DialogStyle = styled(Dialog)` &.MuiDialog-root { z-index: 2002; } .MuiList-root { list-style: inside; .MuiListItem-root { display: list-item; margin-bottom: ${({ theme }) => theme.unit}px; height: auto; padding: ${({ theme }) => theme.unit}px 0; font-size: ${({ theme }) => theme.fontDefault.body1}; line-height: 1.5em; } } .MuiDialogContentText-root { white-space: pre-line; } ` export const CancelAllOrdersAlert = withTranslation('common', { withRef: true, })( ({ t, open, handleCancelAll, handleClose, }: WithTranslation & { open: boolean handleCancelAll: () => void handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { return ( handleClose(e)} aria-describedby='alert-dialog-cancel-all-orders-description' > {t('labelCancelAllOrders')} ) }, ) export const CancelDualAlert = withTranslation('common', { withRef: true, })( ({ t, open, handleCancelOne, handleClose, row, }: WithTranslation & { open: boolean row: any handleCancelOne: () => Promise handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { return ( handleClose(e)} aria-describedby='alert-dialog-cancel-all-orders-description' > {t('labelDualAutoCancelConfirm')} {t('labelDualAutoCancelConfirmDes')} {t('labelDualModifySettlementDateDialog')} {moment(new Date(row?.expireTime)).format(DAY_MINUTE_FORMAT)} ) }, ) export const CancelOneOrdersAlert = withTranslation('common', { withRef: true, })( ({ t, open, handleCancelOne, handleClose, }: WithTranslation & { open: boolean handleCancelOne: () => Promise handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { return ( handleClose(e)} aria-describedby='alert-dialog-cancel-all-orders-description' > {t('labelOrderCancelConfirm')} ) }, ) export const AlertNotSupport = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: MouseEvent) => void }) => { return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelNotSupportTitle')} ) }, ) export const AlertImpact = ({ open, handleClose, handleConfirm, variance, marketPrice, settlementPrice, symbol, }: { open: boolean handleClose: () => void handleConfirm: () => void variance: string marketPrice: string settlementPrice: string symbol: string }) => { const { t } = useTranslation('common') const label: RiskInformation[] = [ { label: t('labelExpectedSettlementPrice'), value: `${settlementPrice} ${symbol}`, color: 'var(--color-text-primary)', }, { label: t('labelCurrentMarketPrice'), value: `${marketPrice} ${symbol}`, color: 'var(--color-text-primary)', }, { label: t('labelPriceVariance'), value: `${variance}%`, color: 'var(--color-error)', }, ] return ( This trade will result in a loss of {variance}% of the position’s market value. To proceed, tap Continue to confirm you understand and acknowledge the risk. , ]} handleClose={handleClose} handleConfirm={handleConfirm} /> ) } export const ConfirmImpact = ({ open, handleClose, handleConfirm, priceImpact, color, shouldInputAgree, }: { open: boolean handleClose: () => void handleConfirm: () => void priceImpact: string color: string shouldInputAgree: boolean }) => { const { t } = useTranslation('common') const [agree, setAgree] = React.useState('') React.useEffect(() => { if (open) { setAgree(shouldInputAgree ? '' : 'AGREE') } }, [open, shouldInputAgree]) const label: RiskInformation[] = [ { label: t('labelPriceImpact'), value: `${priceImpact}%`, color: color, }, ] return ( , }} > This trade will affect the pool price by more than {priceImpact}%,which is too high. It may result in significant slippage and potential losses. If you acknowledge the risk and wish to proceed, type the ‘AGREE’ and tap ‘Proceed Anyway’ to confirm again. , ]} isAgree={agree === 'AGREE'} handleClose={handleClose} handleConfirm={handleConfirm} checkElement={ shouldInputAgree ? ( { setAgree(event.target.value) }} margin='dense' id='agree' type='text' fullWidth /> ) : undefined } /> ) } export const SmallOrderAlert = ({ open, handleClose, handleConfirm, estimatedFee, feePercentage, minimumReceived, }: // symbol, { open: boolean handleClose: () => void handleConfirm: () => void estimatedFee: string feePercentage: string minimumReceived: string }) => { const { t } = useTranslation('common') const label: RiskInformation[] = [ { label: t('labelSmallOrderAlertLine3'), value: `${estimatedFee}`, color: 'var(--color-text-primary)', }, { label: t('labelSmallOrderAlertLine5'), value: `${minimumReceived}`, color: 'var(--color-text-primary)', }, { label: t('labelSmallOrderAlertLine4'), value: `${feePercentage}%`, color: 'var(--color-error)', }, ] return ( ) } export const AlertLimitPriceRisk = withTranslation('common')( ({ t, value, open, handleClose, handleConfirm, price, priceSymbol, fromAmount, fromSymbol, toSymbol, toAmount, }: WithTranslation & { open: boolean value: string handleClose: () => void handleConfirm: () => void fromSymbol: string fromAmount: string | number toSymbol: string toAmount: string | number price: string | number priceSymbol: string // handleClose: (event: MouseEvent, isAgree?: boolean) => void // handleConfirm: () => void }) => { const label: RiskInformation[] = [ { label: t('labelExpectedSettlementPrice'), value: `${price} ${priceSymbol}`, color: 'var(--color-text-primary)', }, { label: t('labelSell'), value: `${fromAmount} ${fromSymbol}`, color: 'var(--color-text-primary)', }, { label: t('labelBuy'), value: `${toAmount} ${toSymbol}`, color: 'var(--color-error)', }, ] return ( | <' }} > The price you set is greater or less than 20% the market price. Are you sure you want to make this order? , ]} handleClose={handleClose} handleConfirm={handleConfirm} /> ) }, ) export const SwapSecondConfirmation = withTranslation('common')( ({ t, open, handleClose, handleConfirm, fromSymbol, fromAmount, toSymbol, toAmount, userTakerRate, tradeCostMin, estimateFee, priceImpactColor, priceImpact, minimumReceived, slippage, }: WithTranslation & { open: boolean handleClose: () => void handleConfirm: () => void fromSymbol: string fromAmount: string toSymbol: string toAmount: string userTakerRate: string tradeCostMin: string estimateFee: string priceImpactColor: string priceImpact: string minimumReceived: string slippage: string }) => { // const { isMobile } = useSettings() // const network = MapChainId[defaultNetwork] ?? MapChainId[1]; const infos = [ { label: ( {t('swapFee')} ), value: estimateFee, color: 'var(--color-text-primary)', }, { label: ( {' ' + t('swapPriceImpact')} ), value: priceImpact, color: priceImpactColor, }, { label: ( {' ' + t('swapMinReceive')} ), value: minimumReceived, color: 'var(--color-text-primary)', }, { label: ( {' ' + t('swapTolerance')} ), value: `${slippage}%`, color: 'var(--color-text-primary)', }, ] return ( {t('labelSwapSecondConfirmTitle')} {t('labelFrom')} {fromAmount} {fromSymbol} {'\u2192'} {t('labelTo')} {toAmount} {toSymbol} } open={open} infos={infos} strongBtn={'confirm'} confirmLabel={'labelConfirm'} handleClose={handleClose} handleConfirm={handleConfirm} /> ) }, ) export const WrongNetworkGuide = withTranslation('common', { withRef: true, })( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelWrongNetworkGuideTitle', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} {t('labelWrongNetworkGuide', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} ) }, ) export const ConfirmLinkCopy = withTranslation('common', { withRef: true, })( ({ t, open, handleClose, setCopyToastOpen, }: WithTranslation & { open: boolean setCopyToastOpen: (vale: boolean) => void handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { const { search } = useLocation() const searchParams = new URLSearchParams(search) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelOpenInWalletTitle')} labelOpenInWalletDetail URL for adding fund has been copied. You can choose either way to continue: Open your wallet app and paste the url in its internal Dapp browser Open your desktop Chrome browser and paste the url in Chrome {t('labelCopyManually')} ) }, ) export const InformationForCoinBase = withTranslation('common', { withRef: true, })( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: MouseEvent, notShow?: boolean) => void }) => { const providers = Object.keys(ConnectProviders).filter((item) => item !== 'unknown') return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} Loopring only support and maintain {providers.join(',')} plugin for Wallet Connect, if your installed other Wallet plugin, please make sure it's the {providers.join(',')} popup. {t('labelGuid')} ) }, ) export const InformationForNoMetaNFT = withTranslation('common', { withRef: true, })( ({ t, open, method, handleClose, }: WithTranslation & { open: boolean method?: string handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { return ( handleClose(e, false)} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} Your Minted NFT does not contain Metadata or media information. Are you sure you still wish to {{ method }} this NFT? ) }, ) export const InformationForAccountFrozen = withTranslation('common', { withRef: true, })( ({ t, open, type, messageKey = 'labelNoticeForForAccountFrozen', }: // handleClose, WithTranslation & { open: boolean type: string messageKey?: string // handleClose: (event: MouseEvent, isAgree?: boolean) => void; }) => { const { setShowTradeIsFrozen } = useOpenModals() return ( setShowTradeIsFrozen({ isShow: false })} aria-describedby='alert-dialog-slide-description' maxWidth={'xs'} > Account Locked - Unable to Operate {t(messageKey, { type })} ) }, ) export const LayerswapNotice = withTranslation('common', { withRef: true, })( ({ t, open, account, }: WithTranslation & { open: boolean account: Account }) => { const [agree, setAgree] = React.useState(false) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] React.useEffect(() => { if (!open) { setAgree(false) } }, [open]) const { setShowLayerSwapNotice } = useOpenModals() return ( setShowLayerSwapNotice({ isShow: false })} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} LayerSwap is a 3rd party App service provider to help move tokens from exchange to Loopring L2 directly. If you have any concerns regarding their service, please check out their TOS . { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelLayerSwapUnderstand')} /> ) }, ) export const AnotherNetworkNotice = withTranslation('common', { withRef: true, })( ({ t, open, }: WithTranslation & { open: boolean account: Account }) => { const [agree, setAgree] = React.useState(false) const { defaultNetwork } = useSettings() const theme = useTheme() const network = MapChainId[defaultNetwork] ?? MapChainId[1] React.useEffect(() => { if (!open) { setAgree(false) } }, [open]) const { setShowAnotherNetworkNotice, modals: { isShowAnotherNetwork }, } = useOpenModals() return ( setShowAnotherNetworkNotice({ isShow: false })} aria-describedby='alert-dialog-slide-description' > {t('labelRiskReminder')} Orbiter.finance is a 3rd party App service provider to help move tokens from exchange to Loopring L2 directly. If you have any concerns regarding their service, please check out their TOS . {'orbiter'} Note: Please ensure to check out the "Change Account" option and input the recipient's address carefully. If you want to send token to network other than l1ChainName, the recipient address must be different than the sender address. {/**/} {/* */} {/* If you want to send token to network other than l1ChainName, the recipient address*/} {/* must be different than the sender address; else you will lose that asset for ever.*/} {/* */} {/**/} { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelAnotherNetworkUnderstand')} /> ) }, ) export const OtherExchangeDialog = withTranslation('common', { withRef: true, })( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: MouseEvent, notShow?: boolean) => void }) => { const [agree, setAgree] = React.useState(false) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] React.useEffect(() => { if (!open) { setAgree(false) } }, [open]) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelConfirmBtrade')} Before withdrawing, please check with your Btrade support that they accept deposits from smart contracts. L2 to L1 withdrawing is via a smart contract. The Btrade depositing address may not be able to acknowledge the tokens deposited automatically. If the deposited tokens do not appear at the Btrade address within 24 hours, please contact your Btrade support to manually acknowledge this transaction. { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelBtradeUnderstand')} /> ) }, ) export const ConfirmDefiBalanceIsLimit = withTranslation('common')( ({ t, open, type, defiData, handleClose, }: WithTranslation & { open: boolean type: string defiData: TradeDefi handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const maxValue = defiData.buyToken?.symbol && `${getValuePrecisionThousand( new BigNumber(defiData?.maxBuyVol ?? 0).div('1e' + defiData.buyToken?.decimals), defiData.buyToken?.precision, defiData.buyToken?.precision, defiData.buyToken?.precision, false, { floor: true }, )} ${defiData.buyToken?.symbol}` return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} {new BigNumber(defiData?.maxSellVol ?? 0).gte(defiData?.miniSellVol ?? 0) && ( Your Redeem order is too large and cannot be withdrawn immediately, you can only redeem {{ maxValue }} )} or you can Withdraw to L1 and redeem through crv or lido Wait some time and wait for pool liquidity ) }, ) export const ConfirmAmmExitMiniOrder = withTranslation('common')( ({ t, open, type, handleClose, }: WithTranslation & { open: boolean } & ( | { type: 'Disabled' handleClose: (event: any) => void } | { type: 'Mini' handleClose: (event: any, isAgree?: boolean) => void } )) => { return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} {t(type === 'Disabled' ? 'labelAmmExitMiniOrderDisabled' : 'labelAmmExitMiniOrderMini')} {type === 'Disabled' ? ( ) : ( <> )} ) }, ) export const ConfirmStackingRedeem = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: MouseEvent, isAgree?: boolean) => void }) => { const { setShowLayerSwapNotice } = useOpenModals() return ( setShowLayerSwapNotice({ isShow: false })} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} ) }, ) export const ConfirmDefiNOBalance = withTranslation('common')( ({ t, isJoin, open, market, type, handleClose, isLeverage, }: WithTranslation & { open: boolean type: symbol market: `${string}-${string}` isJoin: boolean handleClose: (event: any) => void isLeverage: boolean }) => { // @ts-ignore const [, baseSymbol, _quoteSymbol] = market.match(/(\w+)-(\w+)/i) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} {isJoin ? ( No quota available. Loopring will setup the pool soon, please revisit for subscription later. ) : ( }} > Loopring rebalance pool can't satisfy your complete request now. For the remaining investment, you can choose one of the approaches. {!isLeverage && ( }} tOptions={{ symbol: baseSymbol, type, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }} > Withdraw WSTETH to L1 and trade through CRV or LIDO directly Wait some time for Loopring to setup the rebalance pool again, then revist the page for redeem )} )} ) }, ) export const ConfirmInvestDefiServiceUpdate = withTranslation('common')( ({ t, open, handleClose, isCianETHJoin }: WithTranslation & { open: boolean handleClose: (event: any) => void isCianETHJoin: boolean }) => { const history = useHistory() return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} {isCianETHJoin ? t('labelDefiCloseCian') : t('labelDefiClose')} ) }, ) export const ConfirmInvestDefiRisk = withTranslation('common')( ({ t, open, type, handleClose, confirmationNeeded, }: WithTranslation & { open: boolean type: 'WSETH' | 'RETH' | 'CiETH' confirmationNeeded: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { const [agree, setAgree] = React.useState(false) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] React.useEffect(() => { if (!open) { setAgree(false) } }, [open]) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t(`label${type}DefiRiskTitle`)} ), }} > Lido is a liquid staking solution for ETH 2.0 backed by industry-leading staking providers. Lido lets users stake their ETH - without locking assets or maintaining infrastructure. When using Lido to stake your ETH on the Ethereum beacon chain, users will receive a token (stETH), which represents their ETH on the Ethereum beacon chain on a 1:1 basis. It effectively acts as a bridge bringing ETH 2.0’s staking rewards to ETH 1.0. wstETH is the wrapped version of stETH. The total amount of wstETH doesn’t change after users receive the token. Instead, the token’s value increase over time to reflect ETH staking rewards earned. {type === 'CiETH' && ( ), }} /> )} {confirmationNeeded && ( { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelDefiAgree')} /> )} {type !== 'CiETH' && ( It is important to note that users can't redeem wstETH for ETH until phase 2 of Ethereum 2.0. However, users are able to trade wstETH for ETH on various exchanges at market prices. Loopring will provide a pool to allow users to trade wstETH for ETH directly on Layer 2. The pool will rebalance periodically when it reaches a specific threshold. If there is not enough inventory on Layer 2, user can always withdraw their wstETH tokens to Layer 1 and swap for ETH in Lido, Curve, or 1inch. )} ) }, ) export const ConfirmInvestDualGainRisk = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [agree, setAgree] = React.useState(false) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInvestDualGainTitle')} ), ol:
      , li: ( ), h5: ( ), }} >

      Covered Gain is an investment strategy to sell digital assets at your Target Price and earn interest while waiting.

      On the Settlement Date, there can be 2 scenarios:

      1. Market Price > Target Price
      2. Market Price ≤ Target Price
      Market Price > Target Price

      Your original investment and earned interest will be sold at the target price.

      This order is then closed regardless of whether "Auto Reinvest" is enabled or not.

      Market Price ≤ Target Price

      Your original investment and earned interest won’t be sold.

      If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully sell crypto at your desired price or disable the feature.

      Auto Reinvest

      When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully sell your crypto at your Target Price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

      Sell Price: the Target Price at which you want to sell your crypto.

      Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

      { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelDualAgree')} />
    ) }, ) export const ConfirmInvestDualDipRisk = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [agree, setAgree] = React.useState(false) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInvestDualDipTitle')} ), ol:
      , li: ( ), h5: ( ), }} >

      Covered Gain is an investment strategy to sell digital assets at your Target Price and earn interest while waiting.

      On the Settlement Date, there can be 2 scenarios:

      1. Market Price > Target Price
      2. Market Price ≤ Target Price
      Market Price > Target Price

      Your original investment and earned interest will be sold at the target price.

      This order is then closed regardless of whether "Auto Reinvest" is enabled or not.

      Market Price ≤ Target Price

      Your original investment and earned interest won’t be sold.

      If you enable the “Auto Reinvest” feature, Loopring will automatically subscribe to a suitable dual investment product based on the agreed terms until you either successfully sell crypto at your desired price or disable the feature.

      Auto Reinvest

      When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully sell your crypto at your Target Price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed.

      Sell Price: the Target Price at which you want to sell your crypto.

      Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled.

      { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelDualAgree')} />
    ) }, ) export const ConfirmInvestDualAutoRisk = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [agree, setAgree] = React.useState(false) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInvestDualAutoTitle')} ), }} >

    Auto Reinvest will automatically reinvest your investment and earned interest into a new term with the same Target Price once the previous term expires, continuing until you successfully buy or sell crypto. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed and your investment and earned interest will be unlocked.

    Reinvest Target Price: The Target Price at which you want to buy or sell crypto.

    Longest Settlement Date: The maximum duration available for selecting the settlement period. Auto Reinvest will automatically match products with settlement periods that do not exceed the Longest Settlement Date.

    { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelDualAgree')} />
    ) }, ) export const ConfirmInvestDualRisk = withTranslation('common')( ({ t, open, USDCOnly, handleClose, }: WithTranslation & { open: boolean USDCOnly: boolean handleClose: (event: any, isAgree?: DualInvestConfirmType | undefined) => void }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [{ agree1, agree2, agree3, agree4, agree5 }, setAgree] = React.useState({ agree1: false, agree2: false, agree3: false, agree4: false, agree5: false, }) // const { language } = useSettings(); return ( handleClose(e, undefined)} aria-describedby='alert-dialog-slide-description' > {t('labelDualRiskTitle')} {USDCOnly ? ( Dual Investment offers you a chance to sell cryptocurrency high or buy cryptocurrency low at your desired price on your desired date. Once subscribed, users are not able to cancel or redeem the subscription until the Settlement Date.\n You may be better off holding your cryptocurrency, and may be required to trade your cryptocurrency at a less favorable rate of exchange than the market rate on Settlement Date. Cryptocurrency trading is subject to high market risk. Please make your trades cautiously. There may be no recourse for any losses. , h6: , }} > When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed. Buy Price: the Target Price at which you want to buy crypto. Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled. { setAgree((_state) => ({ ..._state, agree5: state, })) }} checkedIcon={} icon={} color='default' /> } label={t('labelInvestDualTutorialCheck5')} /> ) : ( Dual Investment offers you a chance to sell cryptocurrency high or buy cryptocurrency low at your desired price on your desired date. Once subscribed, users are not able to cancel or redeem the subscription until the Settlement Date.\n You may be better off holding your cryptocurrency, and may be required to trade your cryptocurrency at a less favorable rate of exchange than the market rate on Settlement Date. Cryptocurrency trading is subject to high market risk. Please make your trades cautiously. There may be no recourse for any losses. {/* {t('labelDualAutoReinvest')} */} , h6: , }} > When you enable the “Auto Reinvest” feature, Loopring will automatically reinvest your funds into a new product with the same target price when the previous product expires, continuing until you successfully buy crypto at your desired price. If there isn’t an available product within 2 hours after the previous settlement, the order will be automatically closed. Buy Price: the Target Price at which you want to buy crypto. Longest Settlement Date: your acceptable investment period. If no suitable products are available within this range, “Auto Reinvest” will not subscribe to any products for you, even if it's enabled. { setAgree((_state) => ({ ..._state, agree1: state, })) }} checkedIcon={} icon={} color='default' /> } label={t('labelInvestDualTutorialCheck1')} /> { setAgree((_state) => ({ ..._state, agree2: state, })) }} checkedIcon={} icon={} color='default' /> } label={t('labelInvestDualTutorialCheck2')} /> { setAgree((_state) => ({ ..._state, agree3: state, })) }} checkedIcon={} icon={} color='default' /> } label={t('labelInvestDualTutorialCheck3')} /> { setAgree((_state) => ({ ..._state, agree4: state, })) }} checkedIcon={} icon={} color='default' /> } label={t('labelInvestDualTutorialCheck4')} /> { setAgree((_state) => ({ ..._state, agree5: state, })) }} checkedIcon={} icon={} color='default' /> } label={t('labelInvestDualTutorialCheck5')} /> )} ) }, ) export const ConfirmInvestLRCStakeRisk = withTranslation('common')( ({ t, open, handleClose, confirmationNeeded, }: WithTranslation & { open: boolean confirmationNeeded: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [agree, setAgree] = React.useState(false) React.useEffect(() => { if (!open) { setAgree(false) } }, [open]) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t(`labelLRCStakingTitle`)} ), }} > LRC staking is incentivized through an allocated portion of the Loopring protocol fee; the exact percentage is determined by the Loopring DAO. The APY is updated daily based on the allocated amount from previous day’s fee. Any LRC holder can participate in LRC staking via L2 to accumulate daily rewards. The assets must be staked for a minimum of 90 days to receive rewards. {confirmationNeeded && ( { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelLRCStakingAgree')} /> )} The staked LRC will be locked in Loopring L2, meaning it cannot be used for other purposes. You may redeem your LRC at any time; however, doing so before the 90-day minimum requirement will forfeit any accumulated reward. ) }, ) export const ConfirmBtradeSwapRisk = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { const [agree, setAgree] = React.useState(false) React.useEffect(() => { if (!open) { setAgree(false) } }, [open]) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t(`labelBtradeSwapTitleDes`)} ), h6: ( ), li:
  • , ul:
      , }} /> { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelLRCStakingAgree')} />
  • ) }, ) export const ConfirmStopLimitRisk = withTranslation('common')( ({ t, open, handleClose, baseSymbol, quoteSymbol, tradeType, limitPrice, stopPrice, baseValue, quoteValue, stopSide, onSubmit, }: WithTranslation & { open: boolean handleClose: (event: any) => void } & Partial<{ baseSymbol: string quoteSymbol: string tradeType: TradeProType baseValue: string | number quoteValue: string | number limitPrice: string stopPrice: string stopSide: sdk.STOP_SIDE onSubmit: (event: any) => void }>) => { return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' sx={{ width: 'var(--swap-box-width)' }} > {t(`labelStopLimit`, { symbol1: baseSymbol, tradeType: tradeType ? tradeType[0].toUpperCase() + tradeType.substring(1) : '', //tradeType, })} {baseSymbol + ' / ' + quoteSymbol} {t('labelStopLimitType', { tradeType: tradeType ? tradeType[0].toUpperCase() + tradeType.substring(1) : '', })} {t('labelStopLimitStopPrice')} {stopPrice + ' ' + quoteSymbol} {t('labelStopLimitPriceLimitPrice')} {limitPrice + ' ' + quoteSymbol} {t('labelStopLimitAmount')} {baseValue + ' ' + baseSymbol} ), }} >

    If the last price goes up to or above value Symbol2 , and order to tradeType value2 Symbol1 at a price of price Symbol2 will be placed.

    ) }, ) export const ConfirmVaultRisk = withTranslation('common')( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { const [agree, setAgree] = React.useState(false) React.useEffect(() => { if (!open) { setAgree(false) } }, [open]) return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t(`labelVaultTitleRisk`)} ), h6: ( ), li:
  • , ul:
      , }} /> { setAgree(state) }} checkedIcon={} icon={} color='default' /> } label={t('labelLRCStakingAgree')} />
  • ) }, ) export const VaultSwapCancel = withTranslation('common', { withRef: true, })( ({ t, open, handleClose, }: WithTranslation & { open: boolean handleClose: (event: any, isAgree?: boolean) => void }) => { return ( handleClose(e)} aria-describedby='alert-dialog-slide-description' > {t('labelInformation')} You are borrowing tokens.\n Are you sure you want to change the token pair or exit the trade? ) }, ) ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/FeeList.tsx ================================================ import { MuToggleButtonGroupStyle } from '../../../basic-lib' import { ToggleButton } from '@mui/material' import { FeeInfo } from '@loopring-web/common-resources' export const FeeToggle = ({ chargeFeeTokenList, handleToggleChange, feeInfo, disableNoToken = false, }: { chargeFeeTokenList: Array handleToggleChange: (value: C) => void feeInfo: C disableNoToken?: boolean }) => { return ( feeInfo?.belong === ele.belong)} exclusive onChange={(_e, value: number) => { handleToggleChange(chargeFeeTokenList[value]) }} > {chargeFeeTokenList?.map((feeInfo, index) => ( {feeInfo.belong} ))} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/Property.tsx ================================================ import { AddIcon, DeleteIcon, MetaProperty, PROPERTY_LIMIT } from '@loopring-web/common-resources' import { Trans, useTranslation } from 'react-i18next' import { Button, TextField } from '../../../basic-lib' import { Box, Grid, IconButton, Typography } from '@mui/material' import React, { ForwardedRef } from 'react' export const Properties = ({ properties = [], handleChange, disabled = false, }: { disabled?: boolean properties: MetaProperty[] handleChange: (properties: Array>) => void }) => { const { t } = useTranslation() const _handleChange = React.useCallback( (_property: Partial, index: number) => { let _properties = [...properties] _properties[index] = { ...(_properties[index] ?? {}), ..._property } handleChange(_properties) // properties.filter((item,_index) => index!== ) // properties[index] }, [properties, handleChange], ) const onDelete = React.useCallback( (index: number) => { let _properties = [...properties] if (_properties.length > 1) { handleChange(_properties.filter((_item, _index) => _index !== index)) } }, [handleChange, properties], ) const addItem = React.useCallback(() => { if (properties.length < PROPERTY_LIMIT) { let _properties = [...properties, { key: '', value: '' }] handleChange(_properties) } }, [handleChange, properties]) React.useEffect(() => { if (!properties.length) { addItem() } }, []) return ( {properties.map((property, index) => ( ))} {properties.length < PROPERTY_LIMIT && ( )} ) } export const Property = React.memo( React.forwardRef( ( { property, index, handleChange, onDelete, disabled = false, }: { disabled?: boolean property: MetaProperty index: number handleChange: (property: Partial, index: number) => void onDelete: (index: number) => void }, ref: ForwardedRef, ) => { // const [,] = React.useState>(); const _handleChange = React.useCallback( (_property: Partial) => { handleChange({ ...property, ..._property }, index) }, [handleChange, index, property], ) return ( <> key} type={'text'} onChange={(e) => _handleChange({ key: e.target.value })} /> value} type={'text'} onChange={(e) => _handleChange({ value: e.target.value })} /> onDelete(index)} > ) }, ), ) // export const PropertyReview = React.memo( // React.forwardRef( // ({ // property, // index, // handleChange, // onDelete, // }: { // property: MetaProperty; // index: number; // handleChange: (property: Partial, index: number) => void; // onDelete: (index: number) => void; // }) => { // // const [,] = React.useState>(); // const _handleChange = React.useCallback( // (_property: Partial) => { // handleChange({ ...property, ..._property }, index); // }, // [handleChange, index, property] // ); // // return ( // <> // // key} // type={"text"} // onChange={(e) => _handleChange({ key: e.target.value })} // /> // // // value} // type={"text"} // onChange={(e) => _handleChange({ value: e.target.value })} // /> // // // onDelete(index)} // > // // // // // ); // } // ) // ); ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/Refresh.tsx ================================================ import { CountDownStyled } from '../Styled' import { Box, Typography } from '@mui/material' import React from 'react' import { globalSetup, refreshTime } from '@loopring-web/common-resources' import * as _ from 'lodash' import { useTheme } from '@emotion/react' // @ts-ignore export const CountDownIcon = React.memo( React.forwardRef( ( { onRefreshData, wait = globalSetup.wait, countDownSeconds }: { wait?: number; onRefreshData?: () => void, countDownSeconds?: number }, ref, ) => { const countDownRef = React.useRef() // React.createRef const [refreshCount, setRefreshCount] = React.useState(0) const nodeTimer = React.useRef(-1) const logoTimer = React.useRef(-1) React.useEffect(() => { if (refreshCount === 0 && onRefreshData) { onRefreshData() } }, [refreshCount]) // React.useEffect(()=>{ // // },[shouldRefresh]) const startCountDown = React.useCallback(() => { //@ts-ignore if (countDownRef && countDownRef.current) { //@ts-ignore countDownRef.current?.classList.add('countdown') //@ts-ignore countDownRef.current?.classList?.remove('logo') // setRefreshCount(refreshTime-1); if (nodeTimer.current !== -1) { clearInterval(nodeTimer.current as NodeJS.Timeout) } nodeTimer.current = setInterval(decreaseNum, 1000) } }, [countDownRef, nodeTimer]) const refresh = React.useCallback( _.debounce(() => { //@ts-ignore if (countDownRef && countDownRef.current) { // setRefreshCount(0) if (nodeTimer.current !== -1) { clearInterval(nodeTimer.current as NodeJS.Timeout) } if (logoTimer.current !== -1) { clearTimeout(logoTimer.current as NodeJS.Timeout) } //@ts-ignore countDownRef.current?.classList?.remove('countdown') //@ts-ignore countDownRef.current?.classList?.add('logo') setRefreshCount(0) logoTimer.current = setTimeout(() => { startCountDown() }, 1000 - wait) } }, wait), [], ) const decreaseNum = React.useCallback( () => setRefreshCount((prev) => { if (prev > 1) { return prev - 1 } else if (prev == 1) { //@ts-ignore countDownRef?.current?.classList?.remove('countdown') //@ts-ignore countDownRef?.current?.classList?.add('logo') return 0 } else { //@ts-ignore countDownRef?.current?.classList?.add('countdown') //@ts-ignore countDownRef?.current?.classList?.remove('logo') return refreshTime - 1 } }), [setRefreshCount, countDownRef, refreshTime], ) const cleanSubscribe = React.useCallback(() => { clearInterval(nodeTimer.current as NodeJS.Timeout) clearTimeout(logoTimer.current as NodeJS.Timeout) }, [nodeTimer, logoTimer]) React.useEffect(() => { // _refresh(); return cleanSubscribe }, []) const theme = useTheme() return ( ) }, ), ) ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/SlippagePanel.tsx ================================================ import { TGItemJSXInterface, ToggleButtonGroup } from '../../../basic-lib' import React from 'react' import CurrencyInput from 'react-currency-input-field' import { globalSetup, myLog } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { Box, FormHelperText, InputAdornment } from '@mui/material' import { TFunction } from 'react-i18next' import { useFocusRef } from '../../../basic-lib/form/hooks' import { useSettings } from '../../../../stores' const Styled = styled(Box)` .MuiFormHelperText-root { font-size: ${({ theme }) => theme.fontDefault.body2}; color: var(--color-error); } .MuiToggleButtonGroup-root { .MuiToggleButtonGroup-grouped:first-of-type { margin-left: -1px; } display: flex; flex: 1; flex-wrap: wrap; justify-content: flex-start; align-content: space-between; } ` const suffix = '%' const InputStyled = styled(CurrencyInput)` position: relative; color: var(--color-text-primary); ::placeholder { color: var(--color-placeholder); } height: 2.4rem; width: 92px; padding: .3rem .3rem .3rem .8rem; background: var(--color-box); ${({ theme }) => theme.border.defaultFrame({ d_R: 1 / 2, c_key: 'var(--color-border)' })}; text-align: left; min-width: 0; padding-right: 2rem; .MuiButtonBase-root & { } :focus { outline: 0; border-color: transparent; } } ` as typeof CurrencyInput const CUSTOMER_SLIPPAGE_NAME = 'customerSlippage' export const SlippagePanel = ({ slippageList, slippage, wait = globalSetup.wait, handleChange, max = 100, alertMax = 5, ...rest }: { t: TFunction } & { slippageList: Array slippage: number | string wait?: number max?: number alertMax?: number handleChange: (newValue: any, customValue: any) => void }) => { let { slippage: _slippage } = useSettings() const [customSlippage, setCustomSlippage] = React.useState(_slippage) const [showAlert, setShowAlert] = React.useState( _slippage !== 'N' && _slippage > alertMax, ) // const [cValue, setCValue] = React.useState(_slippage); const inputEle = useFocusRef({ shouldFocusOn: false, value: _slippage, }) const [value, setValue] = React.useState(slippage) const _handleChange = (event: React.MouseEvent | any, newValue: number | string) => { if (event.target !== inputEle.current && newValue !== undefined) { if (newValue && newValue !== 'N') { setValue(newValue) handleChange(newValue, !slippageList.includes(customSlippage) ? customSlippage : undefined) } } else if (event.target?.name === CUSTOMER_SLIPPAGE_NAME && event.type === 'change') { var _value = event.target?.value ?? '' _value = _value.replace(suffix, '') if (Number(_value) < max) { setValue(_value) setCustomSlippage(_value) if (_value >= alertMax) { setShowAlert(true) } else { setShowAlert(false) } } else { setShowAlert(true) setValue(max) setCustomSlippage(max - 1) } } else { } event.preventDefault() } const handleOnBlur = React.useCallback(() => { try { if (customSlippage !== 'N' && value !== 'N') { handleChange(value, !slippageList.includes(customSlippage) ? customSlippage : undefined) } } catch (e) { myLog('ignore handleOnBlur', e) } }, [value, customSlippage]) const toggleData = React.useMemo( () => slippageList.reduce((pre, value, index) => { let item: TGItemJSXInterface if (RegExp('slippage:').test(value.toString())) { item = { value: customSlippage, JSX: ( { handleOnBlur() }} onBlur={() => { handleOnBlur() }} defaultValue={customSlippage === 'N' ? '' : customSlippage} maxLength={5} autoComplete={'off'} // suffix={suffix} /> {suffix} ), notWrap: true, key: 'custom' + '-' + index, } } else { item = { value: value, JSX: {value}%, tlabel: value + '%', key: value + '-' + index, } } pre.push(item) return pre }, [] as TGItemJSXInterface[]), [customSlippage, _handleChange], ) return ( {showAlert && {rest.t('labelSlippageAlert')}} ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/ToolBarItems.tsx ================================================ import { Box, Grid, Step, StepLabel, Stepper } from '@mui/material' import { BackIcon, DropDownIcon, TradeBtnStatus } from '@loopring-web/common-resources' import { SwitchData } from '../../Interface' import { IconButtonStyled } from '../Styled' import { useTranslation } from 'react-i18next' import { useSettings } from '../../../../stores' import { BtnInfo, Button } from '../../../index' import styled from '@emotion/styled' import React from 'react' export const ToolBarItemBack = ({ onChangeEvent, tradeData, }: { tradeData: T onChangeEvent: (index: 0 | 1, data: SwitchData) => Promise | void }) => { return ( { onChangeEvent(0, { tradeData, to: 'button' }) }} aria-label='to Professional' > ) } const BoxStyle = styled(Box)` .MuiSvgIcon-root.MuiSvgIcon-fontSizeMedium { height: var(--btn-icon-size-large); width: var(--btn-icon-size-large); .MuiStepIcon-text { font-size: ${({ theme }) => theme.fontDefault.body1}; fill: var(--color-text-button); } } .MuiStepConnector-root.Mui-active .MuiStepConnector-line { border-color: var(--color-primary); } ` as typeof Grid export function HorizontalLabelPositionBelowStepper({ activeStep, steps, }: { activeStep: number steps: string[] }) { const { t } = useTranslation('common') const { isMobile } = useSettings() return ( <> {steps.map((label) => ( {t(label)} ))} ) } export const BtnMain = React.memo( ({ defaultLabel = 'labelMintNext', btnInfo, disabled, onClick, btnStatus, fullWidth, }: { defaultLabel?: string btnInfo?: BtnInfo disabled: () => boolean onClick: () => void fullWidth?: boolean btnStatus?: keyof typeof TradeBtnStatus }) => { const { t } = useTranslation() return ( ) }, ) ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/TradeMenuList.tsx ================================================ import { CoinInfo, CoinKey, IBData } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import React from 'react' import { CoinMenu, InputSelect, InputSelectProps } from '../../../basic-lib' import { Box, Link, Typography } from '@mui/material' import { TradeMenuListProps } from '../Interface' import { useTheme } from '@emotion/react' export const TradeMenuList = , I>({ nonZero, sorted, t, onChangeEvent, walletMap, selected, tradeData, coinMap, _height, tokenType, className, hasCancel = true, filterWithBorrowed, ...rest }: TradeMenuListProps & WithTranslation) => { const ref = React.useRef(null) const inputSelectProps: InputSelectProps = { placeholder: 'tokenSearchCoin', focusOnInput: true, allowScroll: true, selected: '', panelRender: () => <>, } const theme = useTheme() const backElement = React.useMemo( () => ( { onChangeEvent(0, { tradeData, to: 'button' }) }} > {t('labelCancel')} ), [onChangeEvent, tradeData], ) const filterBy = (coinInfo: CoinInfo, filterString: string) => { return filterString && filterString.length ? RegExp(filterString, 'i').test(coinInfo.simpleName as string) : true } const panelRender = ({ selected, value }: any) => { return ( ) => { onChangeEvent(0, { ...{ tradeData: { ...tradeData, belong: itemKey } }, to: 'button', }) }, walletMap: walletMap, //tradeCalcData?.walletMap as any, selected, t, ...rest, }} filterWithBorrowed={filterWithBorrowed} ref={ref} /> ) } return ( ) } ================================================ FILE: packages/component-lib/src/components/tradePanel/components/tool/index.ts ================================================ // this is a private component, only used in panel component // please do not export this index to global export * from './SlippagePanel' export * from './ToolBarItems' export * from './TradeMenuList' export * from '../SwapWrap/SwapMenuList' export * from './Dialogs' export * from '../../Deposit/DepositTitle' export * from '../../../basic-lib/form/input/CollectionInput' ================================================ FILE: packages/component-lib/src/components/tradePanel/index.ts ================================================ export * from './Swap' export * from './Reset' export * from './Interface' export * from './ModalPanel' export * from './components/Styled' export * from './Amm' export * from './components/tool/Dialogs' export * from './tradePro' export * from './ExportAccount' export * from './components/DepositNFTWrap' export * from './components/DeployNFTWrap' export * from './components/MintNFTConfirm' export * from './components/MintAdvanceNFTWrap' export * from './components/MintNFTBlock' export * from './Deposit' export * from './components/tool/FeeList' export * from './components/DeFiWrap' export * from './components/DualWrap' export * from './components/ImportRedPacketWrap' export * from './components/CollectionAdvanceWrap' export * from './components/ImportCollectionWrap' export * from '../basic-lib/form/input/CollectionInput' export * from './components/CreateCollectionWrap' export * from './components/CollectionManageWrap' export * from './components/RampConfirm' export * from './components/tool/Refresh' export * from './components/BanxaConfirm' export * from './components/ImportRedPacketWrap' export * from './components/hook/useAddressType' export * from './components/BasicACoinTrade' export { FullAddressType } from './components/AddressType' export { InitialNameAvatar } from './components/ContactSelection' export * from './components/VaultWrap' ================================================ FILE: packages/component-lib/src/components/tradePanel/tradePro/Interface.ts ================================================ import { InputButtonProps } from '../../basic-lib' import { CoinInfo, TradeBaseType, TradeBtnStatus, TradeCalcProData, TradeProType, } from '@loopring-web/common-resources' import React from 'react' export type TradeLimitInfoProps, I> = { tradeLimitI18nKey?: string tradeLimitBtnStyle?: React.CSSProperties tradeCalcProData: Partial tradeLimitBtnStatus?: keyof typeof TradeBtnStatus | undefined tokenPriceProps?: Partial>> tokenBaseProps?: Partial>> tokenQuoteProps?: Partial>> } export type StopTradeLimitInfoProps, I> = { stopPriceProps?: Partial>> // stopRange: [string | undefined, string | undefined]; } & TradeLimitInfoProps export type TradeMarketInfoProps, I> = { tradeMarketI18nKey?: string tradeMarketBtnStyle?: React.CSSProperties tradeCalcProData: Partial tradeMarketBtnStatus?: keyof typeof TradeBtnStatus | undefined tokenBaseProps?: Partial>> tokenQuoteProps?: Partial>> } export type TradeProBaseEventProps = { disabled?: boolean tradeType: TradeProType handleChangeIndex?: (index: TradeProType) => X // onSwapClick: (tradeData: SwapTradeData) => void | any, } & Partial, 'handleError'>> export type TradeCommonProps = { type: 'limit' | 'market' tradeData: X i18nKey: string tradeCalcProData: TCD onChangeEvent: (data: X, formType: TradeBaseType) => X tradeBtnBaseStatus?: keyof typeof TradeBtnStatus | undefined handleCountChange?: (ibData: T, name: string, ref: React.ForwardedRef) => void // tokenPriceProps?: Partial>>, tokenBaseProps?: Partial>> tokenQuoteProps?: Partial>> } ================================================ FILE: packages/component-lib/src/components/tradePanel/tradePro/hookCommon.tsx ================================================ import React from 'react' import { TradeCommonProps, TradeProBaseEventProps } from './Interface' import { IBData, L1L2_NAME_DEFINED, MapChainId, myLog, TradeBaseType, TradeCalcProData, TradeProType, } from '@loopring-web/common-resources' import { WithTranslation } from 'react-i18next' import { LimitTradeData, MarketTradeData } from '../Interface' import { InputSize } from '../../basic-lib' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import { useSettings } from '../../../stores' export const useCommon = < X extends LimitTradeData | MarketTradeData, T extends IBData, TCD extends TradeCalcProData, I, >({ t, type, i18nKey, tradeCalcProData, tradeBtnBaseStatus, tradeData, tradeType, handleCountChange, onChangeEvent, tokenBaseProps, tokenQuoteProps, disabled, handleChangeIndex, ...rest }: TradeProBaseEventProps & TradeCommonProps & WithTranslation) => { const quoteRef = React.useRef() const baseRef = React.useRef() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [selectedPercentage, setSelectedPercentage] = React.useState(0) // const [tabIndex, setTabIndex] = React.useState(tradeData.type ?? TradeProType.buy); const [inputError, setInputError] = React.useState<{ error: boolean message?: string | JSX.Element }>({ error: false, message: '', }) React.useEffect(() => { const inputType = tradeType === TradeProType.sell ? 'base' : 'quote' if (tradeData[inputType].tradeValue && tradeData[inputType].balance) { const _data = tradeData[inputType] //.tradeValue const value = sdk .toBig(_data.tradeValue ?? '') .div(sdk.toBig(_data.balance)) .times(100) .toFixed() setSelectedPercentage(Number(value)) } else { setSelectedPercentage(0) } }, [tradeData['base'].tradeValue, tradeData['quote'].tradeValue]) const handleError = React.useCallback( ({ belong, balance, tradeValue }: any, ref?) => { if (typeof rest.handleError !== 'function') { if (balance < tradeValue || (tradeValue && !balance)) { const _error = { error: true, message: t('tokenNotEnough', { belong: belong }), } setInputError(_error) return _error } setInputError({ error: false, message: '' }) return { error: false, message: '' } } else { return rest.handleError({ belong, balance, tradeValue } as any, ref) } }, [rest.handleError, setInputError], ) const _handleCountChange = React.useCallback( (ibData: T, name: string, _ref: any) => { if (handleCountChange) { handleCountChange(ibData, name, _ref) } else { const _tradeData = { ...tradeData, [name]: ibData, } onChangeEvent(_tradeData, TradeBaseType[name]) } }, [tradeData, type, tradeType], ) const propsBase = React.useMemo(() => { return { label: t('labelProBaseLabel'), subLabel: tradeType === TradeProType.sell ? t('tokenMax') : undefined, emptyText: t('tokenSelectToken'), placeholderText: '0.00', size: InputSize.small, order: '"right"' as any, coinLabelStyle: { color: 'var(--color-text-secondary)' }, isShowCoinIcon: false, ...tokenBaseProps, handleError: tradeType === TradeProType.sell ? handleError : undefined, maxAllow: tradeType === TradeProType.sell, ...rest, } }, [tokenBaseProps, tradeType, TradeProType]) const propsQuote = React.useMemo(() => { return { label: t('labelProQuoteLabel'), subLabel: tradeType === TradeProType.buy ? t('tokenMax') : undefined, emptyText: t('tokenSelectToken'), placeholderText: '0.00', size: InputSize.small, order: '"right"' as any, coinLabelStyle: { color: 'var(--color-text-secondary)' }, isShowCoinIcon: false, ...tokenQuoteProps, handleError: tradeType === TradeProType.buy ? handleError : undefined, maxAllow: tradeType === TradeProType.buy, ...rest, } }, [tokenQuoteProps, tradeType, TradeProType]) const getDisabled = React.useCallback(() => { return disabled || tradeCalcProData === undefined || tradeCalcProData.coinInfoMap === undefined }, [disabled, tradeCalcProData]) const _handleChangeIndex = React.useCallback( (index: TradeProType) => { // setTabIndex(index) if (handleChangeIndex) { tradeData = handleChangeIndex(index) } else { tradeData.type = index setSelectedPercentage(0) } onChangeEvent(tradeData, TradeBaseType.tab) }, [tradeData], ) const btnLabel = React.useMemo(() => { const key = i18nKey.split('|') return t(key[0], { arg: key[1], tradeType: tradeType === TradeProType.sell ? t('labelProSell') : t('labelProBuy'), symbol: tradeCalcProData.coinBase, layer2: L1L2_NAME_DEFINED[network].layer2, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) }, [inputError, t, i18nKey, tradeType, tradeCalcProData]) const onPercentage = React.useCallback( (value: any) => { myLog('hookCommon onPercentage:', value, tradeData) const inputType = tradeType === TradeProType.sell ? 'base' : 'quote' setSelectedPercentage(value) const tradeCoin = _.cloneDeep(tradeData[inputType]) if (tradeCoin && tradeCoin.balance) { tradeCoin.tradeValue = sdk .toBig(tradeCoin.balance) .times(sdk.toBig(value)) .div(100) .toNumber() _handleCountChange(tradeCoin, inputType, { current: 'percentage', } as React.Ref) } }, [_handleCountChange, tradeType, tradeData], ) return { quoteRef, baseRef, btnLabel, getDisabled, handleCountChange: _handleCountChange, selectedPercentage, onPercentage, inputError, _handleChangeIndex, i18nKey, tradeCalcProData, tradeBtnBaseStatus, propsBase, propsQuote, } } ================================================ FILE: packages/component-lib/src/components/tradePanel/tradePro/index.ts ================================================ export * from './limitTrade' export * from './marketTrade' export * from './stopLimitTrade' ================================================ FILE: packages/component-lib/src/components/tradePanel/tradePro/limitTrade.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { BtnPercentage, InputCoin, InputSize } from '../../basic-lib' import { LimitTradeData, TradeLimitProps } from '../Interface' import { CoinInfo, CoinKey, CoinMap, CurrencyToTag, IBData, PriceTag, TradeBtnStatus, TradeCalcProData, TradeProType, } from '@loopring-web/common-resources' import { Box, Tab, } from '@mui/material' import { TabsStyle } from '../components/Styled' import { useCommon } from './hookCommon' import { Button } from './../../index' import React from 'react' import { useSettings } from '../../../stores' export const LimitTrade = withTranslation('common', {withRef: true})( < L extends LimitTradeData, T extends IBData, TCD extends TradeCalcProData, I = CoinKey, >({ tradeData = {type: TradeProType.sell} as L, ...props }: TradeLimitProps & WithTranslation) => { const { t, tradeType, tradeLimitI18nKey, tradeLimitBtnStatus, tradeLimitBtnStyle, tokenPriceProps, handleSubmitEvent, onChangeEvent, } = props const {currency} = useSettings() const priceRef = React.useRef() const { quoteRef, baseRef, btnLabel, getDisabled, _handleChangeIndex, // inputError, tradeCalcProData, tradeBtnBaseStatus, handleCountChange, propsBase, propsQuote, onPercentage, selectedPercentage, } = useCommon({ type: 'limit', ...(props as any), tradeData, tradeType, onChangeEvent, i18nKey: tradeLimitI18nKey ? tradeLimitI18nKey : 'labelProLimitBtn', tradeBtnBaseStatus: tradeLimitBtnStatus, }) const propsPrice = React.useMemo(() => { return { label: t('labelProPrice'), subLabel: `\u2248 ${PriceTag[CurrencyToTag[currency]]}`, emptyText: t('tokenSelectToken'), placeholderText: '0.00', size: InputSize.small, order: '"right"' as any, coinPrecision: 2, coinLabelStyle: {color: 'var(--color-text-secondary)'}, isShowCoinIcon: false, ...tokenPriceProps, handleCountChange, maxAllow: false, t, } }, [tradeType, TradeProType, tokenPriceProps, handleCountChange]) return ( _handleChangeIndex(index)} > > ref={priceRef as any} name={'price'} disabled={false} {...({ ...propsPrice, isShowCoinInfo: true, isShowCoinIcon: false, maxAllow: false, isHideError: true, inputData: tradeData ? tradeData.price : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), } as any)} /> > ref={baseRef as any} name={'base'} disabled={getDisabled()} {...{ ...propsBase, // maxAllow:false, isShowCoinInfo: true, isShowCoinIcon: false, isHideError: true, handleCountChange, inputData: tradeData ? tradeData.base : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), }} /> `${value}%`} getAriaLabel={(value) => `${value}%`} valueLabelFormat={(value) => `${value}%`} valueLabelDisplay={'on'} selected={selectedPercentage} anchors={[ { value: 0, label: '', }, { value: 25, label: '', }, { value: 50, label: '', }, { value: 75, label: '', }, { value: 100, label: '', }, ]} handleChanged={onPercentage} /> > ref={quoteRef} name={'quote'} disabled={getDisabled()} {...{ ...propsQuote, isHideError: true, isShowCoinInfo: true, isShowCoinIcon: false, handleCountChange, inputData: tradeData ? tradeData.quote : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), }} /> {/*{tradeCalcProData.isNotMatchMarketPrice && (*/} {/* */} {/* {*/} {/* onChangeEvent(*/} {/* {*/} {/* ...tradeData,*/} {/* isChecked: !tradeCalcProData?.isChecked,*/} {/* },*/} {/* TradeBaseType.checkMarketPrice,*/} {/* )*/} {/* }}*/} {/* checkedIcon={}*/} {/* icon={}*/} {/* color='default'*/} {/* />*/} {/* }*/} {/* label={*/} {/* */} {/* */} {/* The expected settlement price from this order is symbol = xxxx, while the*/} {/* current market price from a trusted oracle is symbol = xxx. There is a 10.45%*/} {/* variance observed. To proceed, tap here to confirm you understand and*/} {/* acknowledge the risk.*/} {/* */} {/* */} {/* }*/} {/* />*/} {/* */} {/*)}*/} {/*{getDisabled()} {tradeBtnBaseStatus}*/} ) }, ) as < L extends LimitTradeData, T extends IBData, TCD extends TradeCalcProData, I = CoinKey, >( props: TradeLimitProps, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/tradePanel/tradePro/marketTrade.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { MarketTradeData, TradeMarketProps } from '../Interface' import { CoinInfo, CoinKey, CoinMap, defaultSlipage, EmptyValueTag, getValuePrecisionThousand, IBData, Info2Icon, SlippageTolerance, TradeBaseType, TradeBtnStatus, TradeCalcProData, TradeProType, } from '@loopring-web/common-resources' import { Box, Grid, Tab, Tooltip, Typography } from '@mui/material' import { BtnPercentage, InputCoin, LinkActionStyle, PopoverPure } from '../../basic-lib' import { useCommon } from './hookCommon' import { ButtonStyle, TabsStyle } from '../components/Styled' import { bindHover, bindPopover } from 'material-ui-popup-state/es' import { SlippagePanel } from '../components' import React from 'react' import { usePopupState } from 'material-ui-popup-state/hooks' import { useSettings } from '../../../stores' export const MarketTrade = withTranslation('common', { withRef: true })( < M extends MarketTradeData, T extends IBData, TCD extends TradeCalcProData, I = CoinKey, >({ tradeData = {} as M, ...props }: TradeMarketProps & WithTranslation) => { // const {slippage} = useSettings(); // onChangeEvent?: (data:L,type:TradeProType) => L, const { slippage } = useSettings() const slippageArray: Array = SlippageTolerance.concat( `slippage:${slippage}`, ) as Array const { t, // disabled, tradeMarketI18nKey, tradeMarketBtnStyle, tradeType, tradeMarketBtnStatus, handleSubmitEvent, onChangeEvent, // ...rest } = props const { quoteRef, baseRef, btnLabel, getDisabled, _handleChangeIndex, // inputError, tradeCalcProData, tradeBtnBaseStatus, propsBase, propsQuote, onPercentage, handleCountChange, selectedPercentage, } = useCommon({ type: 'market', ...(props as any), tradeData, tradeType, onChangeEvent, i18nKey: tradeMarketI18nKey ? tradeMarketI18nKey : 'labelProMarketBtn', tradeBtnBaseStatus: tradeMarketBtnStatus, }) const popupState = usePopupState({ variant: 'popover', popupId: 'slippagePop', }) const _onSlippageChange = React.useCallback( (slippage: number | string, customSlippage: number | string | undefined) => { popupState.close() onChangeEvent( { ...tradeData, slippage: slippage, __cache__: { ...tradeData.__cache__, customSlippage: customSlippage, }, }, TradeBaseType.slippage, ) }, [tradeData, onChangeEvent], ) const priceImpactColor = tradeCalcProData?.priceImpactColor ? tradeCalcProData.priceImpactColor : 'textPrimary' const priceImpact = tradeCalcProData?.priceImpact !== undefined ? getValuePrecisionThousand(tradeCalcProData.priceImpact, 2, undefined, undefined, false, { floor: true, }) + ' %' : EmptyValueTag const fee = tradeCalcProData && tradeCalcProData.fee ? `${tradeCalcProData.fee} ${ tradeType === TradeProType.sell ? tradeData.quote?.belong : tradeData.base?.belong }` //(parseFloat(tradeCalcData.fee) / 100).toString() + "%" : EmptyValueTag const userTakerRate = tradeCalcProData && tradeCalcProData.feeTakerRate ? (tradeCalcProData.feeTakerRate / 100).toString() : EmptyValueTag const tradeCostMin = tradeCalcProData && tradeCalcProData.tradeCost ? `${tradeCalcProData.tradeCost} ${tradeData.quote?.belong}` //(parseFloat(tradeCalcData.fee) / 100).toString() + "%" : EmptyValueTag const minimumConverted = tradeCalcProData && tradeCalcProData.minimumConverted ? `${tradeCalcProData.minimumConverted} ${ tradeType === TradeProType.buy ? tradeData.base.belong : tradeData.quote.belong }` : EmptyValueTag return ( _handleChangeIndex(index)} > > ref={baseRef as any} name={'base'} disabled={getDisabled()} {...{ ...propsBase, // maxAllow:false, isShowCoinInfo: true, isShowCoinIcon: false, handleCountChange, isHideError: true, inputData: tradeData ? tradeData.base : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), }} /> {/*
    */} {/**/} `${value}%`} getAriaLabel={(value) => `${value}%`} valueLabelFormat={(value) => `${value}%`} valueLabelDisplay={'on'} selected={selectedPercentage} anchors={[ { value: 0, label: '', }, { value: 25, label: '', }, { value: 50, label: '', }, { value: 75, label: '', }, { value: 100, label: '', }, ]} handleChanged={onPercentage} /> > ref={quoteRef} name={'quote'} disabled={getDisabled()} {...{ ...propsQuote, isHideError: true, handleCountChange, isShowCoinInfo: true, isShowCoinIcon: false, inputData: tradeData ? tradeData.quote : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), }} /> {/**/} {/**/} {/*< label={tradeCalcProData.baseToken} coinMap={tradeCalcProData.coinMap} />*/} {' ' + t('labelTradingFeeEst')} {fee} {' ' + t('swapTolerance')} {tradeCalcProData ? ( <> {tradeData.slippage ? tradeData.slippage : tradeCalcProData.slippage ? tradeCalcProData.slippage : defaultSlipage} % ) : ( EmptyValueTag )} {' ' + t('swapPriceImpact')} {priceImpact} {' ' + t('labelSwapMinConverted')} {minimumConverted !== EmptyValueTag ? minimumConverted : EmptyValueTag} {/**/} {/* isNotMatchMarketPrice*/} {/* marketPrice*/} {/* marketRatePrice*/} {/* */} {/**/} {/*{tradeCalcProData.isNotMatchMarketPrice && (*/} {/* */} {/* {*/} {/* onChangeEvent(*/} {/* {*/} {/* ...tradeData,*/} {/* isChecked: !tradeCalcProData?.isChecked,*/} {/* },*/} {/* tradeCalcProData?.lastStepAt === TradeBaseType.quote*/} {/* ? TradeBaseType.quote*/} {/* : TradeBaseType.base,*/} {/* )*/} {/* }}*/} {/* checkedIcon={}*/} {/* icon={}*/} {/* color='default'*/} {/* />*/} {/* }*/} {/* label={*/} {/* */} {/* */} {/* The expected settlement price from this order is symbol = value, while the*/} {/* current market price from a trusted oracle is symbol= marketPrice. There is*/} {/* marketRatePrice% variance observed. Please acknowledge the risk if you still*/} {/* want to continue.*/} {/* */} {/* */} {/* }*/} {/* />*/} {/* */} {/*)}*/} { handleSubmitEvent(tradeData) }} loading={ !getDisabled() && tradeBtnBaseStatus === TradeBtnStatus.LOADING ? 'true' : 'false' } disabled={ getDisabled() || tradeBtnBaseStatus === TradeBtnStatus.DISABLED || tradeBtnBaseStatus === TradeBtnStatus.LOADING } fullWidth={true} > {btnLabel} ) }, ) as < M extends MarketTradeData, T extends IBData, TCD extends TradeCalcProData, I = CoinKey, >( props: TradeMarketProps, ) => JSX.Element ================================================ FILE: packages/component-lib/src/components/tradePanel/tradePro/stopLimitTrade.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { BtnPercentage, InputCoin, InputSize } from '../../basic-lib' import { LimitTradeData, StopLimitTradeData, TradeLimitProps, TradeStopLimitProps, } from '../Interface' import { CoinInfo, CoinKey, CoinMap, CurrencyToTag, IBData, Info2Icon, PriceTag, TradeBaseType, TradeBtnStatus, TradeCalcProData, TradeProType, } from '@loopring-web/common-resources' import { Box, Icon, Tab, Tooltip } from '@mui/material' import { TabsStyle } from '../components/Styled' import { useCommon } from './hookCommon' import { Button } from './../../index' import React from 'react' import { useSettings } from '../../../stores' import * as sdk from '@loopring-web/loopring-sdk' import styled from '@emotion/styled' const BoxStyle = styled(Box)` .stopPrice { input::placeholder { white-space: pre-wrap; font-size: 10px; position: absolute; height: fit-content; align-items: center; top: 50%; left: 50%; width: 90%; transform: translate(-50%, -50%); } .MuiGrid-grid-xs-3 { z-index: 2; } } .limit-price { .MuiGrid-grid-xs-3 { z-index: 2; } } ` as typeof Box export const StopLimitTrade = withTranslation('common', { withRef: true })( < L extends StopLimitTradeData, T extends IBData, TCD extends TradeCalcProData, I = CoinKey, >({ tradeData = { type: TradeProType.sell } as L, ...props }: TradeStopLimitProps & WithTranslation) => { const { t, tradeType, tradeLimitI18nKey, tradeLimitBtnStatus, tradeLimitBtnStyle, tokenPriceProps, stopPriceProps, handleSubmitEvent, onChangeEvent, } = props const { currency } = useSettings() const priceRef = React.useRef() const stopPriceRef = React.useRef() const { quoteRef, baseRef, btnLabel, getDisabled, _handleChangeIndex, // inputError, tradeCalcProData, tradeBtnBaseStatus, handleCountChange, propsBase, propsQuote, onPercentage, selectedPercentage, } = useCommon({ type: 'limit', ...(props as any), tradeData, tradeType, onChangeEvent, i18nKey: tradeLimitI18nKey ? tradeLimitI18nKey : 'labelProLimitBtn', tradeBtnBaseStatus: tradeLimitBtnStatus, }) const propsPrice = React.useMemo(() => { return { label: ( {tradeType === TradeProType.buy ? t('labelStopPrice') : t('labelStopPriceSell')} ), subLabel: `\u2248 ${PriceTag[CurrencyToTag[currency]]}`, emptyText: t('tokenSelectToken'), placeholderText: '0.00', size: InputSize.small, order: '"right"' as any, coinPrecision: 2, coinLabelStyle: { color: 'var(--color-text-secondary)' }, isShowCoinIcon: false, ...tokenPriceProps, handleCountChange, maxAllow: false, t, } }, [tradeType, TradeProType, tokenPriceProps, handleCountChange]) const propsStopPrice = React.useMemo(() => { return { label: ( {t('labelStopStopPrice')} ), subLabel: `\u2248 ${PriceTag[CurrencyToTag[currency]]}`, emptyText: t('tokenSelectToken'), placeholderText: tradeCalcProData.stopRange && tradeCalcProData.stopRange[0] && tradeCalcProData.stopRange[1] ? t('labelStopLimitMinMax', { minValue: tradeCalcProData.stopRange[0], maxValue: tradeCalcProData.stopRange[1], }) : '0.00', size: InputSize.small, order: '"right"' as any, coinPrecision: 2, coinLabelStyle: { color: 'var(--color-text-secondary)' }, isShowCoinIcon: false, ...stopPriceProps, handleCountChange, maxAllow: false, handleError: (data: T) => { if ( data.tradeValue && tradeCalcProData.stopRange && tradeCalcProData.stopRange[1] && tradeCalcProData.stopRange[0] && (sdk .toBig(data.tradeValue) .gt(tradeCalcProData.stopRange[1]?.replaceAll(sdk.SEP, '')) || sdk.toBig(data.tradeValue).lt(tradeCalcProData.stopRange[0]?.replaceAll(sdk.SEP, ''))) ) { return { error: true, } } return { error: false, } }, t, } }, [tradeType, TradeProType, stopPriceProps, handleCountChange]) return ( _handleChangeIndex(index)} > > ref={stopPriceRef as any} name={TradeBaseType.stopPrice} className={'stopPrice'} disabled={false} {...({ ...propsStopPrice, isShowCoinInfo: true, isShowCoinIcon: false, maxAllow: false, isHideError: true, inputData: tradeData ? tradeData.stopPrice : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), } as any)} /> > ref={priceRef as any} name={TradeBaseType.price} disabled={false} className={'limit-price'} {...({ ...propsPrice, isShowCoinInfo: true, isShowCoinIcon: false, maxAllow: false, isHideError: true, inputData: tradeData ? tradeData.price : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), } as any)} /> > ref={baseRef as any} name={TradeBaseType.base} disabled={getDisabled()} {...{ ...propsBase, // maxAllow:false, isShowCoinInfo: true, isShowCoinIcon: false, isHideError: true, handleCountChange, inputData: tradeData ? tradeData.base : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), }} /> {/**/} {/**/} `${value}%`} getAriaLabel={(value) => `${value}%`} valueLabelFormat={(value) => `${value}%`} valueLabelDisplay={'on'} selected={selectedPercentage} anchors={[ { value: 0, label: '', }, { value: 25, label: '', }, { value: 50, label: '', }, { value: 75, label: '', }, { value: 100, label: '', }, ]} handleChanged={onPercentage} /> > ref={quoteRef} name={TradeBaseType.quote} disabled={getDisabled()} {...{ ...propsQuote, isHideError: true, isShowCoinInfo: true, isShowCoinIcon: false, handleCountChange, inputData: tradeData ? tradeData.quote : ({} as any), coinMap: tradeCalcProData && tradeCalcProData.coinInfoMap ? tradeCalcProData.coinInfoMap : ({} as CoinMap>), }} /> {/*{getDisabled()} {tradeBtnBaseStatus}*/} ) }, ) as < L extends LimitTradeData, T extends IBData, TCD extends TradeCalcProData, I = CoinKey, >( props: TradeLimitProps, ) => JSX.Element ================================================ FILE: packages/component-lib/src/index.ts ================================================ /** * @example * export const HeadMenuItem = withTranslation()(styled(({}:InterFaceProps & WithTranslation)=><>)`` * ) as React.ComponentType; */ export * from './stores' export * from './components' export * from './static' export {VaultPage, useGetVaultAssets, useVaultMarket} from './sharedPages' // export * from "./@loopring-web/common-resources"; ================================================ FILE: packages/component-lib/src/react-app-env.d.ts ================================================ /// ================================================ FILE: packages/component-lib/src/reportWebVitals.ts ================================================ import { ReportHandler } from 'web-vitals' const reportWebVitals = (onPerfEntry?: ReportHandler) => { if (onPerfEntry && onPerfEntry instanceof Function) { import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(onPerfEntry) getFID(onPerfEntry) getFCP(onPerfEntry) getLCP(onPerfEntry) getTTFB(onPerfEntry) }) } } export default reportWebVitals ================================================ FILE: packages/component-lib/src/setupTests.ts ================================================ // jest-dom adds custom jest matchers for asserting on DOM nodes. // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom import '@testing-library/jest-dom' ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/DashBoardPanel.tsx ================================================ import { Box, Container, Typography, Modal, Tooltip, Button, Divider, IconButton, Tab, Tabs, styled, } from '@mui/material' import ArrowForwardIcon from '@mui/icons-material/ArrowForward' import React from 'react' import { MarginLevelIcon, PriceTag, CurrencyToTag, HiddenTag, getValuePrecisionThousand, EmptyValueTag, VaultAction, L1L2_NAME_DEFINED, UpColor, Info2Icon, SoursURL, RouterPath, VaultKey, TradeBtnStatus, WarningIcon2, hexToRGB, OrderListIcon, ViewIcon, HideIcon, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { MarketDetail, ModalCloseButton, ModalCloseButtonPosition, SwitchPanelStyled, VaultAssetsTable, Button as MyButton, VaultJoinPanelModal, VaultPositionsTable, Toast, useSettings, } from '@loopring-web/component-lib' import { Trans } from 'react-i18next' import { fiatNumberDisplay, useVaultJoin, VaultAccountInfoStatus, ViewAccountTemplate, WalletConnectL2Btn, } from '@loopring-web/core' import { AutoRepayConfirmModal, CloseAllConfirmModal, CloseConfirmModal, CollateralDetailsModal, DebtModal, DustCollectorModal, DustCollectorUnAvailableModal, LeverageModal, MaximumCreditModal, NoAccountHintModal, SmallOrderAlert, SupplyCollateralHintModal, VaultSwapModal } from './modals' import { marginLevelTypeToColor } from '@loopring-web/component-lib/src/components/tradePanel/components/VaultWrap/utils' import { marginLevelType } from '@loopring-web/core/src/hooks/useractions/vault/utils' import { useVaultDashboard } from '../hooks/useVaultDashBoard' import { VaultDashBoardPanelUIProps } from '../interface' import { useVaultSwap } from '../hooks/useVaultSwap' const BgButton = styled(Button)<{ customBg: string }>` background-color: ${({ customBg }) => customBg}; transition: all 0.2s ease-in-out; &:hover { background-color: ${({ customBg }) => customBg}; opacity: 0.8; } &:disabled { background-color: var(--color-button-disabled); } ` const zeroTag = '0.0' const VaultDashBoardPanelUI: React.FC = ({ t, forexMap, theme, currency, hideAssets, upColor, showMarginLevelAlert, vaultAccountInfo, localState, setLocalState, colors, assetPanelProps, marketProps, vaultTokenMap, vaultTickerMap, VaultDustCollector, isShowVaultJoin, detail, setShowDetail, hideLeverage, activeInfo, walletMap, _vaultAccountInfo, tokenPrices, getValueInCurrency, history, etherscanBaseUrl, onClickCollateralManagement, onClickSettle, onClickPortalTrade, liquidationThreshold, liquidationPenalty, assetsTab, onChangeAssetsTab, onClickRecord, vaultPositionsTableProps, onClickHideShowAssets, vaultAccountActive, totalEquity, showSettleBtn, btnsDisabled, onClickBuy, onClickSell, didAccountSignIn }) => { const { isMobile } = useSettings() const boxSx = { my: isMobile ? 2 : 2, width: isMobile ? '50%' : '25%', } const activeView = ( <> {showMarginLevelAlert && ( {t('labelVaultMarginLevelAlert')} )} Cross Account {showSettleBtn && ( Settle )} Total Equity {hideAssets ? ( ) : ( )} {vaultAccountActive ? (hideAssets ? HiddenTag : totalEquity) : zeroTag} {t('labelVaultTotalCollateral')} {vaultAccountActive ? hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( Number(vaultAccountInfo?.totalCollateralOfUsdt ?? 0) * (forexMap[currency] ?? 0), 2, 2, 2, false, { isFait: true, floor: true }, ) : zeroTag} {vaultAccountActive && ( { setLocalState({ ...localState, modalStatus: 'collateralDetails', }) }} > {t('labelVaultDetails')} )} {t('labelVaultMarginLevel')} {t('labelVaultMarginLevelTooltips')} {t('labelVaultMarginLevelTooltips2')} {t('labelVaultMarginLevelTooltips3')} {t('labelVaultMarginLevelTooltips4')} {t('labelVaultMarginLevelTooltips5')} {t('labelVaultMarginLevelTooltips6')} {t('labelVaultMarginLevelTooltips7')} {t('labelVaultMarginLevelTooltips8', { liqMarginLevel: liquidationThreshold, })} {t('labelVaultMarginLevelTooltips9')} {t('labelVaultMarginLevelTooltips10', { liqMarginLevel: liquidationThreshold, })} {t('labelVaultMarginLevelTooltips11', { liqMarginLevel: liquidationThreshold, })} } placement={isMobile ? 'bottom' : 'right'} > {(() => { if (!vaultAccountActive) { return ( {zeroTag} ) } const item = vaultAccountInfo?.marginLevel ?? '0' return ( <> {vaultAccountInfo?.marginLevel ? ( {item} ) : ( {EmptyValueTag} )} ) })()} {t('labelVaultTotalDebt')} {vaultAccountActive ? hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( Number(vaultAccountInfo?.totalDebtOfUsdt ?? 0) * (forexMap[currency] ?? 0), 2, 2, 2, false, { isFait: true, floor: true }, ) : zeroTag} {vaultAccountActive && ( { setLocalState({ ...localState, modalStatus: 'debt', }) }} > {t('labelVaultDetails')} )} {t('labelVaultProfit')} {(() => { const profit = (vaultAccountInfo as any)?.accountType === 0 ? sdk .toBig(vaultAccountInfo?.totalEquityOfUsdt ?? 0) .minus(vaultAccountInfo?.totalCollateralOfUsdt ?? 0) : sdk .toBig(vaultAccountInfo?.totalBalanceOfUsdt ?? 0) .minus(vaultAccountInfo?.totalDebtOfUsdt ?? 0) const colorsId = upColor === UpColor.green ? [0, 1] : [1, 0] const colorIs = profit.gte(0) ? colorsId[0] : colorsId[1] return ( <> {vaultAccountActive ? ( {hideAssets ? HiddenTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( profit.times(forexMap[currency] ?? 0).toString(), 2, 2, 2, false, { isFait: false, floor: true, }, )} {getValuePrecisionThousand( profit ?.div( Number(vaultAccountInfo?.totalCollateralOfUsdt) ? vaultAccountInfo?.totalCollateralOfUsdt : 1, ) .times(100) ?? 0, 2, 2, 2, false, { isFait: false, floor: true, }, )} % ) : ( zeroTag )} ) })()} {!hideLeverage && ( {t('labelVaultLeverage')} {vaultAccountInfo?.leverage && vaultAccountActive ? `${vaultAccountInfo?.leverage}x` : EmptyValueTag} {vaultAccountActive && ( { setLocalState({ ...localState, modalStatus: 'leverage', }) }} > {t('labelVaultDetails')} )} {t('labelVaultMaximumCredit')}:{' '} {(vaultAccountInfo as any)?.maxCredit && getValueInCurrency((vaultAccountInfo as any)?.maxCredit) && vaultAccountActive ? fiatNumberDisplay( getValueInCurrency((vaultAccountInfo as any)?.maxCredit), currency, ) : zeroTag} )} Liquidation Threshold {vaultAccountActive ? liquidationThreshold : zeroTag} Liquidation Penalty {vaultAccountActive ? liquidationPenalty : zeroTag} onChangeAssetsTab(value)} > {assetsTab === 'assetsView' ? ( { // @ts-ignore marketProps.onRowClick(index, { // @ts-ignore ...vaultTokenMap[row.name], // @ts-ignore cmcTokenId: vaultTickerMap[row.erc20Symbol].tokenId, ...vaultTickerMap[row.erc20Symbol], }) }} onClickDustCollector={() => { if (VaultDustCollector.enable) { setLocalState({ ...localState, modalStatus: 'dustCollector', }) } else { setLocalState({ ...localState, modalStatus: 'dustCollectorUnavailable', }) } }} showFilter /> ) : ( )} setShowDetail({ isShow: false })} > setShowDetail({ isShow: false } as any)} /> {detail?.detail?.tokenInfo.erc20Symbol ?? detail?.detail?.tokenInfo.symbol} {vaultAccountInfo && walletMap && ([sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus) || activeInfo?.hash) && ( <> {!hideAssets ? walletMap[detail?.detail?.tokenInfo.symbol!]?.count ? getValuePrecisionThousand( walletMap[detail?.detail?.tokenInfo.symbol!]?.count ?? 0, vaultTokenMap[detail?.detail?.tokenInfo.symbol!].precision, vaultTokenMap[detail?.detail?.tokenInfo.symbol!].precision, undefined, false, { isFait: false, floor: true, }, ) : '0.00' : HiddenTag} {!hideAssets ? walletMap[detail?.detail?.tokenInfo.symbol!]?.count ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk .toBig(walletMap[detail?.detail?.tokenInfo.symbol!]!.count) .times(tokenPrices?.[detail?.detail?.tokenInfo.symbol!] || 0) .times(forexMap[currency] ?? 0), 2, 2, 2, false, { isFait: false, floor: true, }, ) : PriceTag[CurrencyToTag[currency]] + '0.00' : HiddenTag} )} onClickBuy(detail?.detail)} onClickSell={() => onClickSell(detail?.detail)} {...{ ...detail?.detail }} /> {!( (vaultAccountInfo && [sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus)) || activeInfo?.hash ) && ( <> { setShowDetail({ isShow: false }) _vaultAccountInfo.onJoinPop({}) }} loading={'false'} variant={'contained'} sx={{ minWidth: 'var(--walletconnect-width)' }} disabled={ _vaultAccountInfo.joinBtnStatus === TradeBtnStatus.DISABLED || _vaultAccountInfo.joinBtnStatus === TradeBtnStatus.LOADING } > {_vaultAccountInfo.joinBtnLabel} )} ) const inactiveView = ( Loopring Portal functions as an isolated margin account allowing users to borrow and lend tokens using collateral. It enables leveraged trading and provides access to assets beyond Ethereum. ) return ( {activeView} {/* {didAccountSignIn ? activeView : inactiveView} */} ) } export const VaultDashBoardPanel = ({ vaultAccountInfo: _vaultAccountInfo, closeShowLeverage, showLeverage, }: { vaultAccountInfo: VaultAccountInfoStatus closeShowLeverage: () => void showLeverage: { show: boolean; closeAfterChange: boolean } }) => { const { collateralDetailsModalProps, maximumCreditModalProps, leverageModalProps, debtModalProps, dustCollectorModalProps, dustCollectorUnAvailableModalProps, vaultDashBoardPanelUIProps, noAccountHintModalProps, supplyCollateralHintModalProps, closeConfirmModalProps, autoRepayModalProps } = useVaultDashboard({ showLeverage, closeShowLeverage }) const {vaultSwapModalProps, smallOrderAlertProps, toastProps, closeAllConfirmModalProps} = useVaultSwap() const joinVaultProps = useVaultJoin() return ( <> ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/HomePanel.tsx ================================================ import { Box, Container, Divider, IconButton, Modal, Typography } from '@mui/material' import React from 'react' import { CurrencyToTag, EmptyValueTag, getValuePrecisionThousand, HiddenTag, LoadIcon, PriceTag, RouterPath, SoursURL, TradeBtnStatus, VaultAction, VaultIcon, VaultKey, VaultTradeIcon, } from '@loopring-web/common-resources' import { BoxBannerStyle, Button, MarketDetail, MarketTable, ModalCloseButton, useSettings, SwitchPanelStyled, useOpenModals, } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import * as sdk from '@loopring-web/loopring-sdk' import { makeVaultLayer2, useSystem, useVaultLayer2, VaultAccountInfoStatus, useVaultMap, } from '@loopring-web/core' import { useHistory } from 'react-router-dom' import { useTheme } from '@emotion/react' import { useVaultMarket } from '../hooks/useVaultMarket' import { symbol } from 'prop-types' export const VaultHomePanel = ({ vaultAccountInfo: { joinBtnLabel, joinBtnStatus, onJoinPop, onGoToSwap }, }: { vaultAccountInfo: VaultAccountInfoStatus }) => { const { isMobile } = useSettings() const { forexMap, etherscanBaseUrl } = useSystem() const theme = useTheme() const { t } = useTranslation() const { vaultAccountInfo, activeInfo } = useVaultLayer2() const { tokenMap: vaultTokenMap, tokenPrices } = useVaultMap() const { modals: { isShowConfirmedVault, isShowVaultJoin }, } = useOpenModals() const history = useHistory() const tableRef = React.useRef() const { marketProps: vaultMarketProps, detail, setShowDetail } = useVaultMarket({ tableRef }) const walletMap = makeVaultLayer2({ needFilterZero: true }).vaultLayer2Map ?? {} const { hideL2Assets, currency } = useSettings() return ( {/* {t('labelTitleVault')} {t('labelTitleVaultDes')} {(vaultAccountInfo && [sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus)) || activeInfo?.hash ? ( ) : ( )} {!isMobile && ( )} */} {/* */} setShowDetail({ isShow: false })} > setShowDetail({ isShow: false } as any)} /> {detail?.detail?.tokenInfo.erc20Symbol ?? detail?.detail?.tokenInfo.symbol} {vaultAccountInfo && walletMap && ([sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus) || activeInfo?.hash) && ( <> {!hideL2Assets ? walletMap[detail?.detail?.tokenInfo.symbol]?.count ? getValuePrecisionThousand( walletMap[detail?.detail?.tokenInfo.symbol]?.count ?? 0, vaultTokenMap[detail?.detail?.tokenInfo.symbol].precision, vaultTokenMap[detail?.detail?.tokenInfo.symbol].precision, undefined, false, { isFait: false, floor: true, }, ) : '0.00' : HiddenTag} {!hideL2Assets ? walletMap[detail?.detail?.tokenInfo.symbol]?.count ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk .toBig(walletMap[detail?.detail?.tokenInfo.symbol]?.count) .times(tokenPrices?.[detail?.detail?.tokenInfo.symbol] || 0) .times(forexMap[currency] ?? 0), 2, 2, 2, false, { isFait: false, floor: true, }, ) : PriceTag[CurrencyToTag[currency]] + '0.00' : HiddenTag} {/* { history.push( `${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}/${VaultAction.VaultLoan}?symbol=${detail?.detail?.tokenInfo.symbol}`, ) }} > {t('labelVaultLoanBtn')} { history.push( `${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}/${VaultAction.VaultSwap}?symbol=${detail?.detail?.tokenInfo.symbol}`, ) }} > {t('labelVaultTradeSimpleBtn')} */} )} { const symbol = detail.detail?.tokenInfo.symbol?.slice(2) if (symbol === 'USDT') { onGoToSwap({ isSell: true }) } else { onGoToSwap({ symbol}) } }} onClickSell={() => { const symbol = detail.detail?.tokenInfo.symbol?.slice(2) if (symbol === 'USDT') { onGoToSwap({ isSell: false }) } else { onGoToSwap({ symbol, isSell: true }) } }} {...{ ...detail?.detail }} /> {!( (vaultAccountInfo && [sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus)) || activeInfo?.hash ) && ( <> )} ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/ModalWrap.tsx ================================================ import React from 'react' import { useNotify, useVaultJoin, useVaultLayer2, useVaultLoan, useVaultMap, useVaultRedeem, } from '@loopring-web/core' import { Modal, SmallOrderAlert, SwapPanel, Toast, useOpenModals, VaultExitPanel, VaultJoinPanel, VaultJoinPanelModal, VaultLoanPanel, VaultSwapCancel, } from '@loopring-web/component-lib' import { useTheme } from '@emotion/react' import { TOAST_TIME, TokenType, TRADE_TYPE, VaultAction, VaultLoanType, VaultSwapStep, } from '@loopring-web/common-resources' import { useVaultSwapExtends } from './useVaultSwapExtends' import { useTranslation } from 'react-i18next' import * as sdk from '@loopring-web/loopring-sdk' export const ModalVaultWrap = ({onClickLeverage}: {onClickLeverage: () => void}) => { const { t } = useTranslation() const { getVaultMap, tokenMap: vaultTokenMao, idIndex: vaultIndex, coinMap } = useVaultMap() const theme = useTheme() const { campaignTagConfig } = useNotify().notifyMap ?? {} const { modals: { isShowVaultExit, isShowVaultJoin, isShowVaultSwap, isShowVaultLoan }, setShowVaultJoin, setShowVaultExit, setShowVaultLoan, setShowVaultSwap, setShowNoVaultAccount, } = useOpenModals() const { vaultAccountInfo } = useVaultLayer2() const [{ openCancel, shouldClose }, setOpenCancel] = React.useState({ openCancel: false, shouldClose: false, }) const exitVaultProps = useVaultRedeem() // const { // // isMarketInit, // // toastOpen, // // closeToast, // // tradeCalcData, // // tradeData, // // swapBtnI18nKey, // // swapBtnStatus, // // handleSwapPanelEvent, // // refreshData, // // refreshRef, // // onSwapClick, // // tradeVault, // // isSwapLoading, // // market, // // isMobile, // // disabled, // // cancelBorrow, // // borrowedAmount, // // marginLevelChange, // // showSmallTradePrompt, // // setShowSmallTradePrompt, // // hideLeverage // } = useVaultSwap({ path: 'portal' }) // const { BtnEle, maxEle } = useVaultSwapExtends({ // tradeCalcData, // swapBtnI18nKey, // swapBtnStatus, // onSwapClick, // isSwapLoading, // disabled, // handleSwapPanelEvent, // tradeData, // toastOpen, // borrowedAmount // }) const { vaultRepayProps, vaultBorrowProps, vaultLoanType, handleTabChange } = useVaultLoan() return ( <> {/* */} {/* { if ( (tradeCalcData as any)?.isVault && (tradeCalcData as any).step !== VaultSwapStep.Edit ) { setOpenCancel({ openCancel: true, shouldClose: true }) } else { setShowVaultSwap({ isShow: false }) } }} content={ tradeData ? ( // @ts-ignore { if ( (tradeCalcData as any)?.isVault && (tradeCalcData as any).step !== VaultSwapStep.Edit ) { setOpenCancel({ openCancel: true, shouldClose: true }) return false } else { return true } }} onCancelClick={() => { setOpenCancel({ openCancel: true, shouldClose: false }) }} BtnEle={BtnEle} tokenSellProps={{ decimalsLimit: vaultTokenMao[tradeData?.sell?.belong?.toString() ?? '']?.vaultTokenAmounts ?.qtyStepScale, allowDecimals: vaultTokenMao[tradeData?.sell?.belong?.toString() ?? ''] ?.vaultTokenAmounts?.qtyStepScale ? true : false, tokenImageKey: tradeData?.sell?.belong?.slice(2), belongAlice: tradeData?.sell?.belong?.slice(2), tokenType: TokenType.vault, subEle: maxEle, subLabel: undefined, disableInputValue: isMarketInit, disabled: isSwapLoading || isMarketInit, placeholderText: tradeCalcData.sellMaxAmtStr && tradeCalcData.sellMaxAmtStr !== '' ? t('labelBtradeSwapMiniMax', { minValue: tradeCalcData.sellMinAmtStr, maxValue: tradeCalcData.sellMaxAmtStr, }) : t('labelBtradeSwapMini', { minValue: tradeCalcData.sellMinAmtStr, }), }} campaignTagConfig={campaignTagConfig} tradeCalcData={tradeCalcData} tradeData={tradeData as any} onSwapClick={onSwapClick} swapBtnI18nKey={swapBtnI18nKey} swapBtnStatus={swapBtnStatus} handleSwapPanelEvent={handleSwapPanelEvent} onRefreshData={refreshData} refreshRef={refreshRef} tradeVault={tradeVault} market={market} isMobile={isMobile} marginLevelChange={marginLevelChange!} vaultLeverage={{ onClickLeverage: onClickLeverage, leverage: vaultAccountInfo?.leverage ?? '0', hideLeverage }} refreshTime={10} /> ) : ( <> ) } /> */} {/* { setShowSmallTradePrompt({ show: false, estimatedFee: undefined, minimumConverted: undefined, feePercentage: undefined, }) }} handleConfirm={() => { onSwapClick() }} estimatedFee={showSmallTradePrompt.estimatedFee ?? ''} feePercentage={showSmallTradePrompt.feePercentage ?? ''} minimumReceived={showSmallTradePrompt.minimumConverted ?? ''} /> */} { setShowVaultExit({ isShow: false }) }} content={} /> { setShowVaultLoan({ isShow: false }) }} content={ } /> {/* { setOpenCancel({ openCancel: false, shouldClose: false }) if (isAgree) { cancelBorrow(shouldClose) } }} /> */} ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/TradePanel.tsx ================================================ import { Box, Typography } from '@mui/material' import { useVaultSwap } from '../hooks/useVaultSwap' import { VaultSwapView } from './VaultSwapView' import { CloseIcon } from '@loopring-web/common-resources' import { useConfirmation } from '@loopring-web/core/src/stores/localStore/confirmation' // import { SmallOrderAlert, Toast } from '../components/' import { CloseAllConfirmModal } from './modals' import { SmallOrderAlert, Toast } from '../../../components' import { useSettings } from '../../../stores' export const VaultTradePanel = () => { const { vaultSwapModalProps, smallOrderAlertProps, toastProps, closeAllConfirmModalProps } = useVaultSwap() const { confirmation, setShowVaultTradeHint } = useConfirmation() const isMobile = useSettings().isMobile return ( Portal Trade Loopring Portal functions as an isolated margin account, allowing users to borrow and lend tokens using collateral. It enables leveraged trading and provides access to assets beyond Ethereum. setShowVaultTradeHint(false)} fontSize={'large'} sx={{ position: 'absolute', top: 8, right: 8, cursor: 'pointer', color: 'var(--color-text-secondary)', }} /> ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/VaultPageUI.tsx ================================================ import { Box, Button, Container, Divider, Tab, Tabs, Typography } from '@mui/material' import React from 'react' import { HelpIcon, LOOPRING_DOCUMENT, OrderListIcon, SoursURL, VaultKey } from '@loopring-web/common-resources' import { Button as LoopringButton, ConfirmVaultRisk, EmptyDefault, useSettings, } from '@loopring-web/component-lib' import { VaultDashBoardPanel } from '../components/DashBoardPanel' import { VaultHomePanel } from './HomePanel' import { ModalVaultWrap } from '../components/ModalWrap' import { useTranslation } from 'react-i18next' import { VaultTradePanel } from '../components/TradePanel' import { useModals } from '../hooks/useModals' import { CloseConfirmModal } from './modals' export interface VaultPageUIProps { tabIndex: VaultKey error: boolean showLeverage: { show: boolean; closeAfterChange: boolean } isShowConfirmedVault: { isShow: boolean } vaultAccountInfo: any marketArray: any[] handleTabChange: (event: any, value: VaultKey) => void handleConfirmVaultRiskClose: (event: any, isAgree: boolean) => void toggleLeverage: () => void closeLeverage: () => void handleRecordClick: () => void handleTutorialClick: () => void getVaultMap: () => void } export const VaultPageUI: React.FC = ({ tabIndex, error, showLeverage, isShowConfirmedVault, vaultAccountInfo, marketArray, handleTabChange, handleConfirmVaultRiskClose, toggleLeverage, closeLeverage, handleRecordClick, handleTutorialClick, getVaultMap, }) => { const { t } = useTranslation() const { isMobile } = useSettings() const { closeConfirmModalProps } = useModals() return ( {t('labelVaultHomeTitle')} } /> {t('labelVaultMarketTitle')} } /> Trade } /> {!error && marketArray?.length ? ( <> {tabIndex === VaultKey.VAULT_DASHBOARD && ( )} {tabIndex === VaultKey.VAULT_HOME && ( )} {tabIndex === VaultKey.VAULT_TRADE && ( )} ) : ( } message={() => { return error ? ( {t('labelVaultRefresh')} ) : ( <> ) }} /> )} ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/VaultSwapView.tsx ================================================ import { Box, Typography, Modal, IconButton, Slider, Checkbox, Tooltip, Switch, Grid, Input, Select, MenuItem, Button as MuiButton } from '@mui/material' import { CoinIcons, CountDownIcon, IconButtonStyled, InputSearch, SpaceBetweenBox, useSettings } from '@loopring-web/component-lib' import { CheckBoxIcon, CheckedIcon, CloseIcon, CompleteIcon, EmptyValueTag, hexToRGB, Info2Icon, LoadingIcon, ReverseIcon, SwapSettingIcon, TokenType, WaitingIcon } from '@loopring-web/common-resources' import { numberFormat } from '@loopring-web/core' import _ from 'lodash'; import { useTranslation } from 'react-i18next'; import Decimal from 'decimal.js'; // import styled from '@emotion/styled'; import { SlippagePanel } from '@loopring-web/component-lib/src/components/tradePanel/components'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import { useTheme } from '@emotion/react'; import React from 'react'; import { marginLevelTypeToColor } from '@loopring-web/component-lib/src/components/tradePanel/components/VaultWrap/utils'; import EastIcon from '@mui/icons-material/East'; import { Popover } from '@mui/material'; import { styled } from '@mui/material'; const PopoverStyled = styled(Popover)` .MuiPaper-elevation2 { box-shadow: none; padding: 0; width: 250px; } .MuiBackdrop-root { background: transparent; } ` const BgButton = styled(MuiButton)<{ customBg: string }>` background-color: ${({ customBg }) => customBg}; transition: all 0.2s ease-in-out; &:hover { background-color: ${({ customBg }) => customBg}; opacity: 0.8; } &:disabled { background-color: var(--color-button-disabled); } ` export interface VaultSwapViewProps { open: boolean onClose: () => void setting: { hideLeverage?: boolean onClickSettingLeverage: () => void leverage: number onSwitchChange: () => void secondConfirmationChecked: boolean settingPopoverOpen: boolean onCloseSettingPopover: () => void onClickSettingBtn: () => void slippageList: string[] currentSlippage: string onSlippageChange: (slippage: string, customSlippage: string) => void } countdown: { countDownSeconds: number onRefreshData: () => void ref: React.RefObject } isLongOrShort: 'long' | 'short' onClickBalance: () => void onClickToken: () => void balance: string token: { symbol: string coinJSON: any } onInputAmount: (amount: string) => void amountInput: string inputPlaceholder: string inputAlert: { show: boolean message: string type: 'error' | 'warning' | 'success' icon?: 'completed' | 'wait' } swapRatio: string onClickReverse: () => void maxTradeValue: string borrowed: string totalQuota: string marginLevelChange: { from: { type: 'danger' | 'safe' | 'warning' marginLevel: string } to?: { type: 'danger' | 'safe' | 'warning' marginLevel: string } } hourlyInterestRate: string tradingFee: string tradingFeeDescription: string slippageTolerance: string myPositions: | { tokenSymbol: string longOrShort: 'Long' | 'Short' marginLevel: string leverage: string amount: string // marketPrice: string onClickLeverage: () => void onClickTrade: () => void onClickClose: () => void }[] | undefined positionTypeSelection: { onChange: (value: string) => void items: [ { label: string value: string tooltipTitle: string }, ] value: string } leverageSelection: { onChange: (value: string) => void items: [ { label: string value: string disabled?: boolean }, ] value: string } ratioSlider: { currentRatio?: number onClickRatio: (no: number) => void } hideOther: boolean onClickHideOther: () => void onClickLongShort: (v: 'long' | 'short') => void tokenSelection: { show: boolean, search: string, onInputSearch: (search: string) => void tokens: { coinJSON: any, symbol: string, amount: string, onClick: () => void }[] onClickCancel: () => void } tradeBtn: { disabled: boolean, label?: string onClick: () => void loading?: boolean bgColor?: string } mainViewRef: React.RefObject onClickCloseAll: () => void onClickMax: () => void amounInUSDT: string moreToBeBorrowed: string showMyPositions: boolean showLeverageSelect: boolean } export const VaultSwapView = (props: VaultSwapViewProps) => { const { moreToBeBorrowed, onClickMax, amounInUSDT, setting: { hideLeverage, onClickSettingLeverage, leverage, onSwitchChange, secondConfirmationChecked, onCloseSettingPopover, settingPopoverOpen, onClickSettingBtn, slippageList, currentSlippage, onSlippageChange, }, countdown, isLongOrShort, onClickBalance, balance, onClickToken, token, onInputAmount, amountInput, inputPlaceholder, inputAlert, swapRatio, onClickReverse, maxTradeValue, borrowed, totalQuota, marginLevelChange, hourlyInterestRate, tradingFee, tradingFeeDescription, slippageTolerance , myPositions, leverageSelection, positionTypeSelection, ratioSlider, hideOther, onClickHideOther, onClickLongShort, tokenSelection, tradeBtn, mainViewRef, onClickCloseAll, showMyPositions, showLeverageSelect } = props const { t } = useTranslation() const theme = useTheme() const settingPopoverId = 'setting' const anchorRef = React.useRef(null); const isMobile = useSettings().isMobile const mainView = ( Portal Trade { onClickSettingBtn() }} sx={{ backgroundColor: 'var(--field-opacity)' }} className={'switch outlined'} aria-label='to Transaction' aria-describedby={settingPopoverId} size={'large'} ref={anchorRef} > {t('labelSwapSettingTitle')} {t('swapTolerance')} { onSlippageChange(slippage, customSlippage) }} /> {!hideLeverage && ( {' ' + t('labelVaultLeverage')} {leverage}x )} {' ' + t('labelSwapSettingSecondConfirm')} { onSwitchChange() }} checked={secondConfirmationChecked} /> {/* { onClose() }} > */} onClickLongShort('long')} > Buy / Long onClickLongShort('short')} > Sell / Short {/* item.value === positionTypeSelection.value) ?.tooltipTitle ?? '' } > */} {/* */} {showLeverageSelect && } Amount {/* Amount } rightNode={ } /> */} {token.symbol} } inputProps={{ sx: { textAlign: 'right' } }} onInput={(e: any) => onInputAmount(e.target.value)} value={amountInput} /> {inputAlert.icon === 'wait' ? ( ) : ( inputAlert.icon === 'completed' && )} {inputAlert.message || '--'} { ratioSlider.onClickRatio( new Decimal(value as number).div(100).toDecimalPlaces(2).toNumber(), ) }} min={0} max={100} valueLabelDisplay={'on'} valueLabelFormat={(value) => `${new Decimal(value).toFixed(0)}%`} step={1} marks={[ { value: 0, label: '' }, { value: 25, label: '' }, { value: 50, label: '' }, { value: 75, label: '' }, { value: 100, label: '' }, ]} size='small' /> {amounInUSDT} {swapRatio} Available } rightNode={ {balance} } /> Max = Current holdings + borrowing limit
    The borrowing limit is determined by the value of your collateral and your net equity in this portal account. If the requested amount exceeds your current holdings, the system will automatically borrow the required asset. } > Max } rightNode={ {maxTradeValue} } mt={1} /> Borrow } rightNode={ {moreToBeBorrowed} } />
    Total Quota } rightNode={ {totalQuota} } /> Margin Level } rightNode={ {marginLevelChange ? ( <> {numberFormat(marginLevelChange.from.marginLevel, { fixed: 2 })} {marginLevelChange.to && ( <> {numberFormat(marginLevelChange.to.marginLevel, { fixed: 2 })} )} ) : ( EmptyValueTag )} } /> Trading Fee (est.) } rightNode={ {tradingFee} } /> Slippage Tolerance } rightNode={ {slippageTolerance} } /> {tradeBtn.loading ? ( ) : tradeBtn.label ? ( {tradeBtn.label} ) : isLongOrShort === 'long' ? ( 'Buy / Long' ) : ( 'Sell / Short' )} {showMyPositions && My Positions { onClickHideOther() }} sx={{ width: '16px', height: '16px', mr: 1, ml: -0.25, }} checkedIcon={} icon={} color='default' /> Hide other tokens {/* Close All */} {myPositions?.length === 0 && ( No positions )} {myPositions?.map((item) => { return ( {item.tokenSymbol} {item.longOrShort} {/* {item.marginLevel} */} Cross {item.leverage} Amount {item.amount} {/* Market Price (USDT) {item.marketPrice} */} Trade Close ) })} }
    {/* {tradeBtn.loading ? ( ) : tradeBtn.label ? ( tradeBtn.label ) : isLongOrShort === 'long' ? ( 'Buy/Long' ) : ( 'Sell/Short' )} */}
    ) const tokenSelectionModal = ( { tokenSelection.onInputSearch(value) }} /> Cancel {tokenSelection.tokens.map((item) => ( {item.symbol} } rightNode={ {item.amount} } key={item.symbol} sx={{ cursor: 'pointer', '&:hover': { bgcolor: 'var(--color-box-hover)', }, }} /> ))}
    ) return ( <> {mainView} {tokenSelectionModal} ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/modals.tsx ================================================ import { Box, Typography, Modal, Divider, IconButton, Slider, Checkbox, Tooltip, Popover, Switch, Grid, Button as MuiButton, Input, Select, MenuItem, ButtonProps, styled, CircularProgress } from '@mui/material' import { AvatarCoin, Button, CoinIcon, CoinIcons, CountDownIcon, IconButtonStyled, InputSearch, Loading, LoadingStyled, ModalCloseButtonPosition, SpaceBetweenBox, SwapTradeData } from '@loopring-web/component-lib' import { BackIcon, CheckBoxIcon, CheckedIcon, CloseIcon, CompleteIcon, EmptyValueTag, hexToRGB, Info2Icon, InfoIcon, LoadingIcon, mapSpecialTokenName, MarginLevelIcon, OrderListIcon, ReverseIcon, SwapSettingIcon, TokenType, WaitingIcon } from '@loopring-web/common-resources' import { numberFormat, VaultTradeTradeData, ViewAccountTemplate } from '@loopring-web/core' import AddIcon from '@mui/icons-material/Add'; import RemoveIcon from '@mui/icons-material/Remove'; import _ from 'lodash'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import { useTranslation } from 'react-i18next'; import Decimal from 'decimal.js'; import { CollateralDetailsModalProps, MaximumCreditModalProps, LeverageModalProps, DebtModalProps, DustCollectorProps, DustCollectorUnAvailableModalProps } from '../interface'; // import styled from '@emotion/styled'; import { RiskComponent, RiskInformation, SlippagePanel } from '@loopring-web/component-lib/src/components/tradePanel/components'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import { useTheme } from '@emotion/react'; import { current } from '@reduxjs/toolkit'; import React from 'react'; import { marginLevelTypeToColor } from '@loopring-web/component-lib/src/components/tradePanel/components/VaultWrap/utils'; import EastIcon from '@mui/icons-material/East'; import { marginLevelType } from '@loopring-web/core/src/hooks/useractions/vault/utils'; import { VaultSwapViewProps, VaultSwapView } from './VaultSwapView'; export const CollateralDetailsModal = (props: CollateralDetailsModalProps) => { const { open, onClose, collateralTokens, totalCollateral, maxCredit, onClickMaxCredit, coinJSON } = props const { t } = useTranslation() return ( {t('labelVaultCollateralDetails')} {t('labelVaultTotalCollateral')} {totalCollateral} {t('labelVaultMaximumCredit')} {maxCredit} {t('labelVaultMaximumCreditDes')}{' '} {t('labelLearnMore2')} {collateralTokens.map((token) => { return ( {mapSpecialTokenName(token.name)} } rightNode={ {token.amount} {token.valueInCurrency} } /> ) })} ) } export const MaximumCreditModal = (props: MaximumCreditModalProps) => { const { open, onClose, onClickBack, collateralFactors, maxLeverage } = props const { t } = useTranslation() return ( {t('labelVaultMaximumCredit')} {t('labelVaultMaximumLeverage')}: {maxLeverage}x {t('labelVaultMaximumCreditDesLong')}{' '} {t('labelVaultMaximumCreditFormula')} {t('labelVaultPriceFactor')} {collateralFactors.map((collateralFactor, index) => { return ( {collateralFactor.name} } rightNode={{collateralFactor.collateralFactor}} /> ) })} ) } export const LeverageModal = (props: LeverageModalProps) => { const { open, onClose, onClickMaxCredit, currentLeverage, onClickReduce, onClickAdd, maxLeverage, onClickLeverage, borrowAvailable, borrowed, maximumCredit, isLoading } = props const { t } = useTranslation() return ( {isLoading && ( )} {t('labelVaultLeverage')} {currentLeverage ? `${numberFormat(currentLeverage, { fixed: 0 })}x` : EmptyValueTag} {(() => { const unitValue = 10 const valueForLeverage = (leverage: number) => (leverage - 1) * unitValue const leverageForValue = (value: number) => Math.round(value / unitValue) + 1 return ( { onClickLeverage(leverageForValue(_value as number)) }} max={valueForLeverage(maxLeverage)} marks={_.range(1, maxLeverage + 1).map((number) => ({ value: valueForLeverage(number), label: `${number}x`, }))} /> ) })()} {t('labelVaultAvailableBorrow')} } rightNode={{borrowAvailable}} marginBottom={2} /> {t('labelVaultBorrowed')} } rightNode={{borrowed}} marginBottom={2} /> {t('labelVaultMaximumCredit')} } rightNode={{maximumCredit}} /> {t('labelVaultMaximumCreditDes')}{' '} {t('labelLearnMore2')} {t('labelVaultLeverageRisk1')} {t('labelVaultLeverageRisk2')} {t('labelVaultLeverageRisk3')} ) } export const DebtModal = (props: DebtModalProps) => { const { open, onClose, borrowedVaultTokens, totalFundingFee, totalBorrowed, totalDebt, } = props const { t } = useTranslation() return ( {t('labelVaultDebt')} {t('labelVaultDebtDetails')} {t('labelVaultTotalDebt')} } rightNode={ {totalDebt} } /> {t('labelVaultTotalBorrowed')} } rightNode={ {totalBorrowed} } /> {t('labelVaultTotalFundingFee')} } rightNode={ {totalFundingFee} } /> {t('labelVaultBorrowed')} {borrowedVaultTokens && borrowedVaultTokens.length > 0 ? borrowedVaultTokens.map(token => { return ( {token.symbol} } rightNode={ {token.amount} {token.valueInCurrency} } /> ) }) : {t('labelEmptyDefault')}} ) } export const DustCollectorModal = (props: DustCollectorProps) => { const { open, converting, onClose, totalValueInUSDT, totalValueInCurrency, onClickConvert, convertBtnDisabled, onClickRecords, } = props const { t } = useTranslation() return ( {converting && } {t('labelDustCollectorDetail')} {(props.dusts && props.dusts.length > 0) ? props.dusts.map((dust) => { return ( } icon={} color='default' onChange={dust.onCheck} /> {dust.symbol} } rightNode={ {dust.amount} {dust.valueInCurrency} } /> ) }) : {t('labelVaultNoDust')}} {t('labelVaultValueEst')} } rightNode={ {totalValueInUSDT} USDT / {totalValueInCurrency} } /> {t('labelVaultDustCollectorDes')} ) } export const DustCollectorUnAvailableModal = (props: DustCollectorUnAvailableModalProps) => { const { open, onClose, } = props const { t } = useTranslation() return ( {t('labelVaultDustCollectorUnavailableTitle')} {t('labelVaultDustCollectorUnavailableDes')} ) } export interface NoAccountHintModalProps { open: boolean onClose: () => void dialogBtn: React.ReactNode title: string des: React.ReactNode } export const NoAccountHintModal = (props: NoAccountHintModalProps) => { const { open, onClose, title, dialogBtn, des } = props const { t } = useTranslation() return ( {t(title)} {des} <>{dialogBtn} } /> ) } export interface SmallOrderAlertProps { open: boolean handleClose: () => void handleConfirm: () => void estimatedFee: string feePercentage: string minimumReceived: string } export const SmallOrderAlert = (props: SmallOrderAlertProps) => { const { open, handleClose, handleConfirm, estimatedFee, feePercentage, minimumReceived } = props const { t } = useTranslation('common') const label: RiskInformation[] = [ { label: t('labelSmallOrderAlertLine3'), value: `${estimatedFee}`, color: 'var(--color-text-primary)', }, { label: t('labelSmallOrderAlertLine5'), value: `${minimumReceived}`, color: 'var(--color-text-primary)', }, { label: t('labelSmallOrderAlertLine4'), value: `${feePercentage}%`, color: 'var(--color-error)', }, ] return ( ) } export const VaultSwapModal = (props: VaultSwapViewProps) => { const { open, onClose } = props return ( ) } export interface SupplyCollateralHintModalProps { open: boolean onClose: () => void onClickSupply: () => void } export const SupplyCollateralHintModal = (props: SupplyCollateralHintModalProps) => { const { open, onClose, onClickSupply} = props return ( Supply Collateral } rightNode={ } /> To initiate a trade in Portal, please add collateral to your account. ) } export interface SettleConfirmModalProps { open: boolean onClose: () => void onConfirm: () => void } export const SettleConfirmModal = (props: SettleConfirmModalProps) => { const { open, onClose, onConfirm } = props const { t } = useTranslation('common') return ( Settle You can only settle your account after all existing positions have been closed. · If there is a loss (due to an unprofitable trade or interest payments), a portion of your collateral may be used to cover the deficit. In this case, only the remaining collateral will be available for withdrawal from Portal. · If your trades are profitable, your full collateral will be available for withdrawal, and any profits will be credited to your Loopring DeFi account accordingly. ) }; export interface CloseAllConfirmModalProps { open: boolean onClose: () => void onConfirm: () => void } export const CloseAllConfirmModal = (props: CloseAllConfirmModalProps) => { const { open, onClose, onConfirm } = props return ( Close All Position This operation will close all existing positions in your account. ) } export interface CloseConfirmModalProps { open: boolean onClose: () => void onConfirm: () => void } export const CloseConfirmModal = (props: CloseConfirmModalProps) => { const { open, onClose, onConfirm } = props return ( Close Position Executing this operation will fully close the SHORT or LONG position associated with the selected token, eliminating any positive or negative exposure. Additionally, the corresponding debt will be repaid. · For a LONG position: Your held asset will be sold for USDT, and any USDT debt will be repaid. · For a SHORT position: You will need to borrow USDT to purchase the token required for debt repayment. In other words, your debt will shift from the original token to USDT. ) } export interface AutoRepayModalProps { open: boolean onClose: () => void onConfirm: () => void } export const AutoRepayConfirmModal = ({ open, onClose, onConfirm, }: AutoRepayModalProps) => { return ( It looks like you're holding the same asset as your debt. To avoid extra interest charges, consider repaying your debt with your holdings now. ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/components/useVaultSwapExtends.tsx ================================================ import React from 'react' import { EmptyValueTag, getValuePrecisionThousand, Info2Icon, L1L2_NAME_DEFINED, MapChainId, myLog, TradeBtnStatus, WaitingIcon, CompleteIcon, VaultSwapStep, ToastType, hexToRGB, AlertIcon, ErrorIcon, WarningIcon, } from '@loopring-web/common-resources' import { Box, Grid, Link, Tooltip, Typography, Stepper, StepLabel, Step } from '@mui/material' import { ButtonStyle, SwapType, useSettings } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import { useTheme } from '@emotion/react' enum ActiveStep { Edit = 0, Borrow = 0, Swap = 1, Swaping = 1, } export const useVaultSwapExtends = ({ tradeCalcData, swapBtnI18nKey, swapBtnStatus, onSwapClick, disabled, handleSwapPanelEvent, tradeData, isSwapLoading, // btnBorrowStatus, // onBorrowClick, // borrowBtnI18nKey, toastOpen, borrowedAmount }) => { const { t } = useTranslation() const { defaultNetwork, coinJson } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const label = React.useMemo(() => { myLog(swapBtnI18nKey, 'swapBtnI18nKey useMemo') const keyParams = { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } if (swapBtnI18nKey) { const key = swapBtnI18nKey.split('|') if (key) { return t( key[0], key && key[1] ? { arg: key[1], ...keyParams, } : { ...keyParams, }, ) } else { return t(swapBtnI18nKey, { ...keyParams, }) } } else { const label = `labelVaultSwapBtn` return t(label, { ...keyParams, }) } }, [swapBtnI18nKey, network, tradeCalcData, t]) const labelBorrowHint = React.useMemo(() => { const keyParams = { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } if (toastOpen?.type == ToastType.error && toastOpen?.step == VaultSwapStep.Borrow) { return ( {t('labelVaultActiveLoanError2', { symbol: tradeCalcData.belongSellAlice, value: borrowedAmount })} ) } else if ( tradeCalcData?.step === VaultSwapStep.Swap || tradeCalcData?.step === VaultSwapStep.Swaping ) { return ( {t('labelVaultActiveLoanSuccessful', { symbol: tradeCalcData.belongSellAlice, value: borrowedAmount })} ) } else if ( tradeCalcData.step === VaultSwapStep.Borrow && swapBtnStatus === TradeBtnStatus.LOADING ) { return ( {t('labelBorrowing', { symbol: tradeCalcData.belongSellAlice, value: borrowedAmount, })} ) } else if (swapBtnI18nKey && tradeCalcData?.isRequiredBorrow) { let key = swapBtnI18nKey.split('|') if (key) { const i18nKey = key.shift() return t( i18nKey, key?.length ? key.reduce( (prev, item, index) => { prev[`arg${index ? index : ''}`] = item return prev }, { ...keyParams }, ) : { ...keyParams, }, ) } else { return t(swapBtnI18nKey, { ...keyParams, }) } } else { return '' } }, [swapBtnI18nKey, network, tradeCalcData, tradeCalcData?.step, swapBtnStatus, toastOpen?.type, toastOpen?.step, borrowedAmount]) const getDisabled = disabled || tradeCalcData === undefined || tradeCalcData.coinInfoMap === undefined const maxEle = React.useMemo(() => { return ( { handleSwapPanelEvent( { type: 'sell', tradeData: { ...tradeData, sell: { ...tradeData?.sell, tradeValue: tradeData?.sell?.balance, }, }, }, SwapType.SELL_SELECTED, ) }} > {t('labelVaultSwapBorrow')} {tradeData?.sell?.balance !== '0' ? tradeData?.sell?.balance : EmptyValueTag} {/**/} 0 ? `max-allow ${isSwapLoading ? 'disabled' : ''}` : `no-balance ${isSwapLoading ? 'disabled' : ''}` } onClick={() => { handleSwapPanelEvent( { type: 'sell', tradeData: { ...tradeData, sell: { ...tradeData?.sell, tradeValue: tradeData?.sell?.count, }, }, }, SwapType.SELL_SELECTED, ) }} > {/**/} {t('labelVaultSwapHold')} {tradeData?.sell?.count > 0 ? getValuePrecisionThousand( tradeData?.sell?.count, tradeCalcData?.sellPrecision, tradeCalcData?.sellPrecision, tradeCalcData?.sellPrecision, false, ) : EmptyValueTag} {/**/} ) }, [tradeData]) const theme = useTheme() const BtnEle = React.useMemo(() => { return ( {toastOpen?.type == ToastType.error && toastOpen?.step == VaultSwapStep.Borrow ? ( {t('labelVaultActiveLoanError', { symbol: tradeCalcData.belongSellAlice, value: '' })} ) : ( (tradeCalcData as any)?.showHasBorrow && ( {t('labelVaultActiveLoanAlert')} ) )} <> {tradeCalcData?.isRequiredBorrow && ( <> {[ { label: t('labelStep1Borrow'), value: VaultSwapStep.Borrow }, { label: t('labelStep2Swap'), value: VaultSwapStep.Swap, }, ].map(({ label, value }) => { return ( {label} ) })} )} {labelBorrowHint && ( {labelBorrowHint} )} { onSwapClick() }} loading={!getDisabled && swapBtnStatus === TradeBtnStatus.LOADING ? 'true' : 'false'} disabled={ getDisabled || swapBtnStatus === TradeBtnStatus.DISABLED || swapBtnStatus === TradeBtnStatus.LOADING } fullWidth={true} > {tradeCalcData?.isRequiredBorrow ? t('labelBorrowSwap') : label} ) }, [swapBtnStatus, onSwapClick, getDisabled, label]) return { BtnEle, maxEle, } } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/hooks/useModals.ts ================================================ import { ToastType, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { useOpenModals, } from '@loopring-web/component-lib' import { useVaultLayer2, } from '@loopring-web/core' import _ from 'lodash' import { closePositionAndRepayIfNeeded } from '../utils' import { CloseConfirmModalProps } from '../components/modals' export const useModals = (): { closeConfirmModalProps: CloseConfirmModalProps } => { const { updateVaultLayer2 } = useVaultLayer2() const { modals: { isShowVaultCloseConfirm, }, setShowVaultCloseConfirm, setShowGlobalToast, } = useOpenModals() return { closeConfirmModalProps: { open: isShowVaultCloseConfirm.isShow, onClose: () => { setShowVaultCloseConfirm({ isShow: false, symbol: undefined }) }, onConfirm: async () => { const { symbol } = isShowVaultCloseConfirm setShowVaultCloseConfirm({ isShow: false, symbol: undefined }) closePositionAndRepayIfNeeded(symbol!) .then((response2) => { if (response2?.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED) { throw new Error('failed') } setShowGlobalToast({ isShow: true, info: { content: 'Closed position successfully', type: ToastType.success, }, }) }) .catch(() => { setShowGlobalToast({ isShow: true, info: { content: 'Close position failed', type: ToastType.error, }, }) }) .finally(() => { updateVaultLayer2({}) }) }, }, } } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/hooks/useVaultDashBoard.tsx ================================================ import { Box, Typography, Button, } from '@mui/material' import React, { useEffect } from 'react' import { PriceTag, CurrencyToTag, EmptyValueTag, VaultAction, L1L2_NAME_DEFINED, MapChainId, SoursURL, RouterPath, VaultKey, TradeBtnStatus, VaultLoanType, SUBMIT_PANEL_CHECK, SUBMIT_PANEL_AUTO_CLOSE, fnType, SagaStatus, AccountStatus, myLog, TokenType, globalSetup, MarketType, ToastType, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { useOpenModals, useSettings, AccountStep, useToggle, VaultDataAssetsItem, VaultAssetsTableProps, setShowWrongNetworkGuide, Button as MyButton, setHideSmallBalances, } from '@loopring-web/component-lib' import { Trans, useTranslation } from 'react-i18next' import { accountReducer, accountStaticCallBack, DAYS, fiatNumberDisplay, getTimestampDaysLater, LoopringAPI, makeVaultLayer2, numberFormat, numberFormatThousandthPlace, numberStringListSum, store, useAccount, useVaultAccountInfo, useSystem, useTokenMap, useVaultLayer2, useVaultMap, useVaultTicker, useWalletLayer2, VaultAccountInfoStatus, WalletConnectL2Btn, // useVaultSwap, MAPFEEBIPS, vaultSwapDependAsync, useToast, toPercent, bipsToPercent, tryFn, } from '@loopring-web/core' import { useTheme } from '@emotion/react' import { useVaultMarket } from './useVaultMarket' import { useHistory, useLocation, useRouteMatch } from 'react-router' import { utils, BigNumber } from 'ethers' import Decimal from 'decimal.js' import _, { keys } from 'lodash' import { CollateralDetailsModalProps, DebtModalProps, DustCollectorProps, DustCollectorUnAvailableModalProps, LeverageModalProps, MaximumCreditModalProps, VaultDashBoardPanelUIProps } from '../interface' import { PositionItem, VaultPositionsTableProps } from '@loopring-web/component-lib/src/components/tableList/assetsTable/VaultPositionsTable' import { AutoRepayModalProps, CloseConfirmModalProps, NoAccountHintModalProps, SettleConfirmModalProps, SmallOrderAlertProps, SupplyCollateralHintModalProps, VaultSwapModalProps } from '../components/modals' import { useVaultSwap } from './useVaultSwap' import { checkHasTokenNeedRepay, closePositionAndRepayIfNeeded, filterPositions, repayIfNeeded } from '../utils' import { promiseAllSequently } from '@loopring-web/core/src/utils/promise' const VaultPath = `${RouterPath.vault}/:item/:method?` const parseVaultTokenStatus = (status: number) => ({ show: status & 1, join: status & 2, exit: status & 4, loan: status & 8, repay: status & 16, }) export const useGetVaultAssets = ({ onClickTrade, onClickRepay }: { onClickTrade: (symbol: string) => void onClickRepay: (symbol: string) => void // onClickRepay: (symbol: string) => void }): VaultAssetsTableProps & { totalAsset: string [key: string]: any } => { const _vaultAccountInfo = useVaultAccountInfo() let match: any = useRouteMatch(VaultPath) const history = useHistory() const { search, pathname } = useLocation() const searchParams = new URLSearchParams(search) const { t } = useTranslation(['common']) const { vaultAccountInfoStatus, vaultAccountInfo, activeInfo, onJoinPop, onGoToSwap, onBorrowPop, onRedeemPop, } = _vaultAccountInfo const [assetsRawData, setAssetsRawData] = React.useState([]) const [totalAsset, setTotalAsset] = React.useState(EmptyValueTag) const { account } = useAccount() const { allowTrade, forexMap } = useSystem() const { setShowNoVaultAccount, setShowVaultSwap, setShowVaultLoan, modals: { isShowNoVaultAccount: { isShow: showNoVaultAccount, whichBtn, ...btnProps }, }, } = useOpenModals() const { isMobile, defaultNetwork, hideL2Assets, hideSmallBalances, setHideSmallBalances } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const btnClickCallbackArray = { [fnType.ERROR_NETWORK]: [ function () { store.dispatch(accountReducer.changeShowModel({ _userOnModel: false })) store.dispatch(setShowWrongNetworkGuide({ isShow: true })) }, ], [fnType.UN_CONNECT]: [ function (key: VaultAction) { setShowNoVaultAccount({ isShow: true, whichBtn: key }) }, ], [fnType.NO_ACCOUNT]: [ function (key: VaultAction) { setShowNoVaultAccount({ isShow: true, whichBtn: key }) }, ], [fnType.DEPOSITING]: [ function (key: VaultAction) { setShowNoVaultAccount({ isShow: true, whichBtn: key }) }, ], [fnType.NOT_ACTIVE]: [ function (key: VaultAction) { setShowNoVaultAccount({ isShow: true, whichBtn: key }) }, ], [fnType.LOCKED]: [ function (key: VaultAction) { setShowNoVaultAccount({ isShow: true, whichBtn: key }) }, ], [fnType.ACTIVATED]: [ ( accountStatus: sdk.VaultAccountStatus, activeInfo: { hash: string isInActive: true }, key: any, ) => { if ( [sdk.VaultAccountStatus.IN_STAKING].includes(accountStatus as any) || activeInfo?.hash ) { switch (key) { case VaultAction.VaultJoin: onJoinPop({}) // setShowVaultJoin({ isShow: true, info: { isActiveAccount: false } }) break case VaultAction.VaultExit: onRedeemPop({ isShow: true }) break case VaultAction.VaultLoan: onBorrowPop({ isShow: true }) break case VaultAction.VaultSwap: onGoToSwap({}) break } } else if ( [sdk.VaultAccountStatus.IN_REDEEM].includes(vaultAccountInfo?.accountStatus as any) ) { setShowNoVaultAccount({ isShow: true, des: 'labelRedeemDesMessage', title: 'labelRedeemTitle', }) } else { setShowNoVaultAccount({ isShow: true, whichBtn: VaultAction.VaultJoin, des: 'labelJoinDesMessage', title: 'labelVaultJoinTitle', }) } }, [vaultAccountInfo?.accountStatus, activeInfo?.hash], ], } const onActionBtnClick = (props: string) => { accountStaticCallBack(btnClickCallbackArray, [props]) } React.useEffect(() => { if ( match?.params?.item == VaultKey.VAULT_DASHBOARD && vaultAccountInfoStatus === SagaStatus.UNSET ) { const { vaultAccountInfo } = store.getState().vaultLayer2 if (vaultAccountInfo?.accountStatus) { if ([sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus as any)) { if (match?.params?.method) { switch (match?.params?.method) { case VaultAction.VaultJoin: onJoinPop({}) // setShowVaultJoin({ isShow: true, info: { isActiveAccount: false } }) break case VaultAction.VaultExit: onRedeemPop({ isShow: true, symbol: searchParams.get('symbol') }) break case VaultAction.VaultLoan: onBorrowPop({ isShow: true, symbol: searchParams.get('symbol') }) break case VaultAction.VaultSwap: onGoToSwap({ symbol: searchParams.get('symbol') }) break } history.replace(`${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}`) } } } else { } } }, [vaultAccountInfoStatus, match?.params?.item, match?.params?.method]) const { status: walletL2Status } = useWalletLayer2() const getAssetsRawData = () => { const { // vaultLayer2: { vaultAccountInfo }, tokenMap: { // tokenMap: erc20TokenMap, idIndex: erc20IdIndex, }, vaultLayer2: {vaultLayer2}, invest: { vaultMap: { tokenMap, tokenPrices, marketMap }, }, } = store.getState() const walletMap = makeVaultLayer2({ needFilterZero: false }).vaultLayer2Map ?? {} myLog('asdfhsjdhfjsd', tokenMap, walletMap) if ( tokenMap && !!Object.keys(tokenMap).length && !!Object.keys(walletMap ?? {}).length ) { const data: Array = Object.keys(tokenMap ?? {}) .map((key, _index) => { let item: any let tokenInfo = { ...tokenMap[key], token: key, erc20Symbol: erc20IdIndex[tokenMap[key].tokenId], } const erc20Symbol = key.slice(2) if (walletMap && walletMap[key]) { tokenInfo = { ...tokenInfo, detail: walletMap[key], erc20Symbol: erc20IdIndex[tokenMap[key].tokenId], } const totalAmount = utils.formatUnits(vaultLayer2?.[key].total ?? 0, tokenMap[key].decimals) const borrowedAmount = sdk.toBig(tokenInfo.detail?.borrowed ?? 0) const tokenValueDollar = new Decimal(totalAmount).times(tokenPrices?.[tokenInfo.symbol] ?? 0) const tokenBorrowedValueDollar = borrowedAmount?.times(tokenPrices?.[tokenInfo.symbol] ?? 0) const isSmallBalance = tokenValueDollar.lt(1) && tokenBorrowedValueDollar.lt(1) const minRepayAmount = utils.formatUnits( tokenInfo.vaultTokenAmounts.minLoanAmount, tokenInfo.decimals, ) item = { token: { type: TokenType.vault, value: key, belongAlice: erc20Symbol, }, amount: totalAmount?.toString(), available: tokenInfo?.detail?.count ?? 0, smallBalance: isSmallBalance, tokenValueDollar: tokenValueDollar.toString(), name: tokenInfo.token, erc20Symbol, debt:borrowedAmount.toString(), equity: numberFormat(tokenInfo.detail?.equity ?? '0', { fixed: tokenInfo.precision, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }), repayDisabled: borrowedAmount.isZero() || new Decimal(totalAmount).isZero() || new Decimal(totalAmount).lt(minRepayAmount) || borrowedAmount.lte(minRepayAmount), } } else { item = { ...tokenInfo, token: { type: TokenType.vault, value: key, belongAlice: erc20Symbol, }, amount: 0, available: 0, locked: 0, smallBalance: true, tokenValueDollar: 0, name: key, tokenValueYuan: 0, erc20Symbol, debt: '0', equity: '0', repayDisabled: true } } if (item) { let precision = tokenMap[item.token.value].precision return { ...item, precision: precision, // holding: '100-todo', // equity: '100-todo', } } else { return undefined } }) .filter((token) => { const tokenInfo: sdk.VaultToken = tokenMap[token.name] if ( _.values(marketMap).every( (market) => market.baseTokenId !== tokenInfo.vaultTokenId && market.quoteTokenId !== tokenInfo.vaultTokenId, ) ) { return false } const status = tokenMap['LV' + token.erc20Symbol].vaultTokenAmounts.status as number return token && parseVaultTokenStatus(status).loan && parseVaultTokenStatus(status).repay }).sort((a, b) => { const deltaDollar = b.tokenValueDollar - a.tokenValueDollar const deltaAmount = sdk.toBig(b.amount).minus(a.amount).toNumber() const deltaName = b.token.value < a.token.value ? 1 : -1 return deltaDollar !== 0 ? deltaDollar : deltaAmount !== 0 ? deltaAmount : deltaName }) const totalAssets = data.reduce( (pre, item) => pre.plus(sdk.toBig(item.tokenValueDollar)), sdk.toBig(0), ) setAssetsRawData(data) setTotalAsset(totalAssets.toString()) } else { setAssetsRawData([]) setTotalAsset(EmptyValueTag) } } const startWorker = _.debounce(getAssetsRawData, globalSetup.wait) React.useEffect(() => { if ( showNoVaultAccount && vaultAccountInfoStatus === SagaStatus.UNSET && vaultAccountInfo?.accountStatus && walletL2Status === SagaStatus.UNSET && whichBtn && vaultAccountInfo?.accountStatus === sdk.VaultAccountStatus.IN_STAKING ) { onActionBtnClick(whichBtn) } // enableBtn() }, [ whichBtn, walletL2Status, vaultAccountInfo?.accountStatus, activeInfo, assetsRawData, // tokenPriceStatus, ]) const walletLayer2Callback = React.useCallback(() => { startWorker() }, []) React.useEffect(() => { if (vaultAccountInfoStatus === SagaStatus.UNSET) { walletLayer2Callback() } }, [vaultAccountInfoStatus]) const onRowClick = React.useCallback( ({ row }: { row: R }) => { if ([sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus ?? '')) { history.push('/portal/portalDashboard') onGoToSwap({ symbol: row?.token?.value }) } else { history.push('/portal') setShowNoVaultAccount({ isShow: true, whichBtn: VaultAction.VaultJoin, des: 'labelJoinDesMessage', title: 'labelVaultJoinTitle', }) } }, [vaultAccountInfo?.accountStatus], ) return { btnProps, onBtnClose: () => { setShowNoVaultAccount({ isShow: false, whichBtn: undefined }) }, forexMap, rawData: assetsRawData as R[], hideAssets: hideL2Assets, allowTrade, setHideSmallBalances, hideSmallBalances, showFilter: true, totalAsset, showNoVaultAccount, actionRow: ({ row }) => { return ( ) }, onRowClick: (_, row) => { onRowClick({ row }) }, positionOpend: [sdk.VaultAccountStatus.IN_REDEEM, sdk.VaultAccountStatus.IN_STAKING] .includes(vaultAccountInfo?.accountStatus as any), onRowClickTrade: ({ row }: { row: R }) => { // debugger onClickTrade(row?.token?.value) // if ([sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus ?? '')) { // history.push('/portal/portalDashboard') // onSwapPop({ symbol: row?.token?.value }) // } else { // history.push('/portal/portalDashboard') // // setstate // // setShowNoVaultAccount({ // // isShow: true, // // whichBtn: VaultAction.VaultJoin, // // des: 'labelJoinDesMessage', // // title: 'labelVaultJoinTitle', // // }) // } }, onRowClickRepay: ({ row }: { row: R }) => { onClickRepay(row?.token?.value) }, // onClickRepay(row?.token?.value) // // if ([sdk.VaultAccountStatus.IN_STAKING].includes(vaultAccountInfo?.accountStatus ?? '')) { // // history.push('/portal/portalDashboard') // // // todo repay // // } else { // // history.push('/portal') // // setShowNoVaultAccount({ // // isShow: true, // // whichBtn: VaultAction.VaultJoin, // // des: 'labelJoinDesMessage', // // title: 'labelVaultJoinTitle', // // }) // // } // }, noMinHeight: true } } export const useVaultDashboard = ({ showLeverage, closeShowLeverage, }: { showLeverage: { show: boolean; closeAfterChange: boolean } closeShowLeverage: () => void }): { vaultDashBoardPanelUIProps: Omit dustCollectorUnAvailableModalProps: DustCollectorUnAvailableModalProps dustCollectorModalProps: DustCollectorProps debtModalProps: DebtModalProps leverageModalProps: LeverageModalProps maximumCreditModalProps: MaximumCreditModalProps collateralDetailsModalProps: CollateralDetailsModalProps noAccountHintModalProps: NoAccountHintModalProps // vaultSwapModalProps: VaultSwapModalProps // smallOrderAlertProps: SmallOrderAlertProps supplyCollateralHintModalProps: SupplyCollateralHintModalProps closeConfirmModalProps: CloseConfirmModalProps settleConfirmModalProps: SettleConfirmModalProps autoRepayModalProps: AutoRepayModalProps } => { const _vaultAccountInfo = useVaultAccountInfo() const { vaultAccountInfo, activeInfo, tokenFactors, maxLeverage, collateralTokens, onGoToSwap, onJoinPop, onRepayPop, onRedeemPop, joinBtnStatus, joinBtnLabel, } = _vaultAccountInfo const { t } = useTranslation() const { setShowNoVaultAccount, modals: { isShowNoVaultAccount: { isShow: showNoVaultAccount, ...btnProps }, isShowVaultJoin, isShowVaultCloseConfirm }, setShowAccount, setShowVaultSwap, setShowVaultCloseConfirm, setShowGlobalToast } = useOpenModals() const { forexMap, etherscanBaseUrl, getValueInCurrency, exchangeInfo } = useSystem() const { isMobile, currency, hideL2Assets: hideAssets, upColor, defaultNetwork, coinJson, setHideL2Assets, slippage, hideSmallBalances, setHideSmallBalances } = useSettings() const {setToastOpen, closeToast} =useToast() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const priceTag = PriceTag[CurrencyToTag[currency]] const colors = ['var(--color-success)', 'var(--color-error)', 'var(--color-warning)'] const theme = useTheme() const tableRef = React.useRef() const { detail, setShowDetail, marketProps } = useVaultMarket({ tableRef }) const walletMap = makeVaultLayer2({ needFilterZero: true }).vaultLayer2Map ?? {} const { tokenMap: vaultTokenMap, tokenPrices, idIndex: vaultIdIndex, marketMap, marketArray, } = useVaultMap() const { tokenMap, idIndex } = useTokenMap() const history = useHistory() const { vaultTickerMap } = useVaultTicker() const [localState, setLocalState] = React.useState({ modalStatus: 'noModal' as | 'noModal' | 'collateralDetails' | 'collateralDetailsMaxCredit' | 'leverage' | 'leverageMaxCredit' | 'debt' | 'dustCollector' | 'dustCollectorUnavailable' | 'supplyCollateralHint' | 'settleConfirm' | 'autoRepayConfirm', unselectedDustSymbol: [] as string[], leverageLoading: false, checkedAutoRepay: false, penaltyFeeBips: undefined as undefined | number, liquidationMarginLevel: undefined as undefined | string, }) const assetPanelProps = useGetVaultAssets({ onClickTrade(symbol) { if (sdk.VaultAccountStatus.IN_STAKING === vaultAccountInfo?.accountStatus) { if (symbol === 'LVUSDT') { onGoToSwap({ symbol: 'ETH'}) } else { onGoToSwap({ symbol: symbol.slice(2) }) } } else { setLocalState({ ...localState, modalStatus: 'supplyCollateralHint', }) } }, onClickRepay(symbol) { if (sdk.VaultAccountStatus.IN_STAKING === vaultAccountInfo?.accountStatus) { onRepayPop({ symbol: symbol.slice(2) }) } else { setLocalState({ ...localState, modalStatus: 'supplyCollateralHint', }) } }, // onClickTrade(symbol) { // if (sdk.VaultAccountStatus.IN_STAKING === vaultAccountInfo?.accountStatus) { // onRe({ symbol: symbol }) // } else { // setLocalState({ // ...localState, // modalStatus: 'supplyCollateralHint', // }) // } // }, }) const { account } = useAccount() const { updateVaultLayer2 } = useVaultLayer2() const { toggle: { VaultDustCollector }, } = useToggle() const changeLeverage = async (leverage: number) => { setLocalState({ ...localState, leverageLoading: true, }) const response = await LoopringAPI.vaultAPI ?.submitLeverage( { request: { accountId: account.accountId.toString(), leverage: leverage.toString(), }, apiKey: account.apiKey, }, '1', ) .finally(() => { setTimeout(() => { setLocalState({ ...localState, leverageLoading: false, }) }, 500) }) if ((response as any)?.resultInfo?.code) { if (response.resultInfo.message.includes('ERR_VAULT_LEVERAGE_TOO_LARGE')) { setShowAccount({ isShow: true, step: AccountStep.General_Failed, info: { errorMessage: t('labelVaultChangeLeverageFailedTooSmall'), title: t('labelVaultChangeLeverageFailed'), }, }) } else { setShowAccount({ isShow: true, step: AccountStep.General_Failed, info: { errorMessage: t('labelUnknown'), title: t('labelVaultChangeLeverageFailed'), }, }) } throw response } updateVaultLayer2({}) } const dustsAssets = vaultAccountInfo?.userAssets?.filter((asset) => { const foundKey = keys(marketMap).find((key) => { const market = marketMap[key] // @ts-ignore return market.baseTokenId === asset.tokenId }) const minimum = foundKey && marketMap[foundKey].minTradeAmount.base return ( minimum && new Decimal(asset.total).greaterThan('0') && new Decimal(asset.total).lessThan(minimum) ) }) const dusts = dustsAssets?.map((asset) => { // @ts-ignore const token = vaultTokenMap[vaultIdIndex[asset.tokenId]] const vaultSymbol = token.symbol const originSymbol = vaultSymbol.slice(2) const checked = !localState.unselectedDustSymbol.includes(originSymbol) const price = tokenPrices[vaultSymbol] return { symbol: originSymbol, coinJSON: coinJson[originSymbol], amount: numberFormat(utils.formatUnits(asset.total, token.decimals), { fixed: token.precision, removeTrailingZero: true, }), checked, valueInCurrency: price && getValueInCurrency( new Decimal(price).mul(utils.formatUnits(asset.total, token.decimals)).toString(), ) ? fiatNumberDisplay( getValueInCurrency( new Decimal(price).mul(utils.formatUnits(asset.total, token.decimals)).toString(), ), currency, ) : EmptyValueTag, onCheck() { if (checked) { setLocalState({ ...localState, unselectedDustSymbol: localState.unselectedDustSymbol.concat(originSymbol), }) } else { setLocalState({ ...localState, unselectedDustSymbol: localState.unselectedDustSymbol.filter( (symbol) => symbol !== originSymbol, ), }) } }, } }) const checkedDusts = dustsAssets?.filter((asset) => { const token = vaultTokenMap[vaultIdIndex[asset.tokenId!]] const vaultSymbol = token.symbol const originSymbol = vaultSymbol.slice(2) return !localState.unselectedDustSymbol.includes(originSymbol) }) const totalDustsInUSDT = checkedDusts ? numberFormat( numberStringListSum( checkedDusts.map((asset) => { // @ts-ignore const token = vaultTokenMap[vaultIdIndex[asset.tokenId]] const vaultSymbol = token.symbol const price = tokenPrices[vaultSymbol] return new Decimal(price).mul(utils.formatUnits(asset.total, token.decimals)).toString() }), ), { fixed: 2, removeTrailingZero: true }, // 2 is USDT precision ) : undefined const totalDustsInCurrency = totalDustsInUSDT && getValueInCurrency(totalDustsInUSDT) ? fiatNumberDisplay(getValueInCurrency(totalDustsInUSDT), currency) : EmptyValueTag const [converting, setConverting] = React.useState(false) const convert = async () => { if (!checkedDusts || !exchangeInfo) return setConverting(true) try { const { broker } = await LoopringAPI.userAPI?.getAvailableBroker({ type: 4, })! const dustTransfers = await Promise.all( checkedDusts.map(async (asset) => { const tokenId = asset.tokenId as unknown as number const { offchainId } = await LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: tokenId, }, account.apiKey, )! return { exchange: exchangeInfo.exchangeAddress, payerAddr: account.accAddress, payerId: account.accountId, payeeId: 0, payeeAddr: broker, storageId: offchainId, token: { tokenId: tokenId, volume: asset.total, }, maxFee: { tokenId: tokenId, volume: '0', }, validUntil: getTimestampDaysLater(DAYS), memo: '', } }), ) const dustList = checkedDusts.map((dust) => { const vaultToken = vaultTokenMap[vaultIdIndex[dust.tokenId!]] const price = tokenPrices[vaultToken.symbol] const originTokenSymbol = vaultToken.symbol.slice(2) return { symbol: originTokenSymbol, coinJSON: coinJson[originTokenSymbol], amount: numberFormat(utils.formatUnits(dust.total, vaultToken.decimals), { fixed: vaultToken.precision, removeTrailingZero: true, }), amountRaw: utils.formatUnits(dust.total, vaultToken.decimals), valueInCurrency: price && getValueInCurrency( new Decimal(price).mul(utils.formatUnits(dust.total, vaultToken.decimals)).toString(), ) ? fiatNumberDisplay( getValueInCurrency( new Decimal(price) .mul(utils.formatUnits(dust.total, vaultToken.decimals)) .toString(), ), currency, ) : undefined, valueInCurrencyRaw: price ? getValueInCurrency( new Decimal(price) .mul(utils.formatUnits(dust.total, vaultToken.decimals)) .toString(), ) : undefined, } }) setShowAccount({ isShow: true, step: AccountStep.VaultDustCollector_In_Progress, info: { totalValueInCurrency: fiatNumberDisplay( numberStringListSum(dustList.map((dust) => dust.valueInCurrencyRaw ?? '0')), currency, ), convertedInUSDT: numberFormat( numberStringListSum(dustList.map((dust) => dust.valueInCurrencyRaw ?? '0')), { fixed: 2 }, ), repaymentInUSDT: undefined, time: undefined, dusts: dustList.map((dust) => { return { symbol: dust.symbol, coinJSON: dust.coinJSON, amount: dust.amount, valueInCurrency: dust.valueInCurrency, } }), }, }) const response = await LoopringAPI.vaultAPI?.submitDustCollector( { dustTransfers: dustTransfers, apiKey: account.apiKey, accountId: account.accountId, eddsaKey: account.eddsaKey.sk, }, '1', ) if ( (response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message || !response ) { throw response } setLocalState({ ...localState, modalStatus: 'noModal', unselectedDustSymbol: [], }) updateVaultLayer2({}) await sdk.sleep(SUBMIT_PANEL_CHECK) const response2 = await LoopringAPI?.vaultAPI?.getVaultGetOperationByHash( { accountId: account?.accountId?.toString(), hash: response.hash, }, account.apiKey, '1', ) if (response2?.raw_data?.operation?.status == sdk.VaultOperationStatus.VAULT_STATUS_FAILED) { throw sdk.VaultOperationStatus.VAULT_STATUS_FAILED } setConverting(false) const status = response2?.raw_data?.operation?.status === sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED ? 'success' : 'pending' setShowAccount({ isShow: store.getState().modals.isShowAccount.isShow, step: status == 'success' ? AccountStep.VaultDustCollector_Success : AccountStep.VaultDustCollector_In_Progress, info: { totalValueInCurrency: fiatNumberDisplay( numberStringListSum(dustList.map((dust) => dust.valueInCurrencyRaw ?? '0')), currency, ), convertedInUSDT: numberFormat( numberStringListSum(dustList.map((dust) => dust.valueInCurrencyRaw ?? '0')), { fixed: 2 }, ), repaymentInUSDT: numberFormat(utils.formatUnits(response2!.operation.amountOut, 6), { fixed: 2, }), time: response2?.operation.createdAt, dusts: dustList.map((dust) => { return { symbol: dust.symbol, coinJSON: dust.coinJSON, amount: dust.amount, valueInCurrency: dust.valueInCurrency, } }), }, }) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) updateVaultLayer2({}) } finally { setConverting(false) } } const showMarginLevelAlert = vaultAccountInfo?.marginLevel && new Decimal(vaultAccountInfo.marginLevel).lessThan('1.15') const collateralToken = vaultAccountInfo && vaultAccountInfo.collateralInfo && tokenMap[idIndex[vaultAccountInfo.collateralInfo.collateralTokenId]] ? tokenFactors.find( (token) => token.symbol === tokenMap[idIndex[vaultAccountInfo.collateralInfo.collateralTokenId]].symbol, ) : undefined const hideLeverage = (vaultAccountInfo as any)?.accountType === 0 const [assetsTab, setAssetsTab] = React.useState('assetsView' as 'assetsView' | 'positionsView') const {vaultLayer2, status: vaultLayer2Status} = useVaultLayer2() const vaultPositionsTableProps: VaultPositionsTableProps = { rawData: filterPositions(vaultLayer2!, vaultTokenMap, tokenPrices) .map((symbol) => { const originSymbol = symbol.slice(2) const asset = vaultLayer2 ? vaultLayer2[symbol] : undefined const tokenInfo = vaultTokenMap?.[symbol] if (!asset || !tokenInfo) return undefined const position = new Decimal( utils.formatUnits(BigNumber.from(asset.netAsset).add(asset.interest), tokenInfo.decimals), ) if (symbol === 'LVUSDT' || position.isZero()) return undefined return { tokenPair: { coinJson: [coinJson[originSymbol], coinJson['USDT']], pair: `${originSymbol}/USDT`, leverage: vaultAccountInfo?.leverage + 'x', marginLevel: vaultAccountInfo?.marginLevel ?? '', }, direction: position.isPos() ? 'long' : 'short' as 'long' | 'short', holding: numberFormatThousandthPlace(position.abs().toString(), { fixed: tokenInfo.precision, removeTrailingZero: true }), onClickTrade: () => { onGoToSwap({ symbol: originSymbol }) }, onClickClose: () => { // setShowGlobalToast({ // isShow: true, // info: { // content: 'aaa', // type: ToastType.error // } // }) // setToastOpenset({ // open: true, // content: 'e.message', // type: ToastType.error // }) setShowVaultCloseConfirm({ isShow: true, symbol: symbol }) // closePosition(symbol) // .then(response2 => { // if (response2?.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED) { // throw new Error('failed') // } // setShowGlobalToast({ // isShow: true, // info: { // content: 'Closed position successfully', // type: ToastType.success // } // }) // }).catch((e) => { // setShowGlobalToast({ // isShow: true, // info: { // content: 'Close position failed', // type: ToastType.error // } // }) // }).finally(() => { // updateVaultLayer2({}) // }) }, valueInUSD: position.abs().mul(tokenPrices[symbol]) } }), onRowClick: (index: number, row: PositionItem) => {}, isLoading: false, hideAssets: hideAssets, showFilter: true, onClickDustCollector: () => { if (VaultDustCollector.enable) { setLocalState({ ...localState, modalStatus: 'dustCollector', }) } else { setLocalState({ ...localState, modalStatus: 'dustCollectorUnavailable', }) } }, hideDustCollector: false, hideSmallBalances: hideSmallBalances, setHideSmallBalances: setHideSmallBalances, } const vaultDashBoardPanelUIProps = { vaultAccountInfo, activeInfo, tokenFactors, maxLeverage, collateralTokens, t, forexMap, etherscanBaseUrl, getValueInCurrency, exchangeInfo, isMobile, currency, hideAssets, upColor, defaultNetwork, coinJson, network, isShowVaultJoin, setShowAccount, // setShowVaultLoan, priceTag, // onActionBtnClick, showNoVaultAccount, setShowNoVaultAccount, btnProps, colors, theme, tableRef, detail, setShowDetail, marketProps, walletMap, vaultTokenMap, tokenPrices, vaultIdIndex, marketMap, marketArray, tokenMap, idIndex, history, vaultTickerMap, localState, setLocalState, account, updateVaultLayer2, VaultDustCollector, changeLeverage, dustsAssets, dusts, checkedDusts, totalDustsInUSDT, totalDustsInCurrency, converting, setConverting, convert, showMarginLevelAlert: showMarginLevelAlert ? true : false, collateralToken, hideLeverage, assetPanelProps, marginLevel: vaultAccountInfo?.marginLevel ?? '', _vaultAccountInfo: _vaultAccountInfo, onClickCollateralManagement: () => { if (vaultAccountInfo?.accountStatus === sdk.VaultAccountStatus.IN_REDEEM) { setShowNoVaultAccount({ isShow: true, des: 'labelRedeemDesMessage', title: 'labelRedeemTitle', }) } else { onJoinPop({}) } }, onClickSettle: () => { onRedeemPop({}) }, liquidationThreshold: localState.liquidationMarginLevel ? localState.liquidationMarginLevel : EmptyValueTag, liquidationPenalty: localState.penaltyFeeBips ? toPercent(localState.penaltyFeeBips * 100, 2) : EmptyValueTag, onClickPortalTrade: () => { if (vaultAccountInfo?.accountStatus === sdk.VaultAccountStatus.IN_STAKING) { onGoToSwap({}) } else { setLocalState({ ...localState, modalStatus: 'supplyCollateralHint', }) } }, assetsTab: assetsTab, onChangeAssetsTab: (tab: 'assetsView' | 'positionsView') => { setAssetsTab(tab) }, onClickRecord: () => { history.push('/l2assets/history/VaultRecords') }, vaultPositionsTableProps, onClickHideShowAssets: () => { setHideL2Assets(!hideAssets) }, vaultAccountActive: vaultAccountInfo?.accountStatus === sdk.VaultAccountStatus.IN_STAKING, totalEquity: tryFn( () => fiatNumberDisplay( getValueInCurrency( new Decimal(vaultAccountInfo!.totalEquityOfUsdt) .add(vaultAccountInfo!.totalCollateralOfUsdt) .toString(), ), currency, ), () => EmptyValueTag, ), showSettleBtn: vaultAccountInfo?.accountStatus === sdk.VaultAccountStatus.IN_STAKING, btnsDisabled: account.readyState !== AccountStatus.ACTIVATED, onClickBuy: (detail) => { const symbol = detail?.tokenInfo.symbol?.slice(2) if (symbol === 'USDT') { onGoToSwap({ isSell: true }) } else { onGoToSwap({ symbol }) } }, onClickSell: (detail) => { const symbol = detail?.tokenInfo.symbol?.slice(2) if (symbol === 'USDT') { onGoToSwap({ isSell: false }) } else { onGoToSwap({ symbol }) } }, didAccountSignIn: account.readyState === AccountStatus.ACTIVATED, } const noVaultAccountDialogBtn = (() => { switch (account.readyState) { case AccountStatus.UN_CONNECT: case AccountStatus.LOCKED: case AccountStatus.NO_ACCOUNT: case AccountStatus.NOT_ACTIVE: return case AccountStatus.DEPOSITING: return ( {'loading'} {t('describeTitleOpenAccounting', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} ) case AccountStatus.ERROR_NETWORK: return ( {t('describeTitleOnErrorNetwork', { connectName: account.connectName, })} ) case AccountStatus.ACTIVATED: if (sdk.VaultAccountStatus.IN_REDEEM === vaultAccountInfo?.accountStatus) { return ( {'loading'} ) } else if ( [sdk.VaultAccountStatus.UNDEFINED, sdk.VaultAccountStatus.FREE].includes( (vaultAccountInfo?.accountStatus ?? '') as any, ) || vaultAccountInfo == undefined || vaultAccountInfo?.accountStatus == undefined ) { return ( { setShowNoVaultAccount({ isShow: false, whichBtn: undefined }) onJoinPop(true) }} variant={'contained'} fullWidth={true} sx={{ minWidth: 'var(--walletconnect-width)' }} loading={(joinBtnStatus === TradeBtnStatus.LOADING ? 'true' : 'false') as any} disabled={ joinBtnStatus === TradeBtnStatus.DISABLED || joinBtnStatus === TradeBtnStatus.LOADING } > {joinBtnLabel} ) } else if ( activeInfo?.hash || (vaultAccountInfo?.accountStatus && sdk.VaultAccountStatus.IN_STAKING !== vaultAccountInfo?.accountStatus) ) { return ( {'loading'} {t('labelVaultAccountWait', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} ) } else { return ( { setShowNoVaultAccount({ isShow: false, whichBtn: undefined }) }} loading={'false'} variant={'contained'} fullWidth={true} sx={{ minWidth: 'var(--walletconnect-width)' }} > {t('labelVaultCloseBtn')} ) } default: return <> } })() React.useEffect(() => { if (!account.apiKey) return LoopringAPI.vaultAPI?.getVaultConfig(account.apiKey, '1').then((res) => { if (res && res.data.penaltyFeeBips !== undefined) { setLocalState((state) => ({ ...state, penaltyFeeBips: res.data.penaltyFeeBips, liquidationMarginLevel: res.data.liquidationMarginLevel, })) } }) }, [account.apiKey]) React.useEffect(() => { if (localState.checkedAutoRepay || vaultLayer2Status !== SagaStatus.UNSET) return const list = checkHasTokenNeedRepay() if (list.length > 0) { setLocalState((state) => ({ ...state, modalStatus: 'autoRepayConfirm', checkedAutoRepay: true })) } else { setLocalState((state) => ({ ...state, checkedAutoRepay: true })) } }, [vaultLayer2Status, localState.checkedAutoRepay]) return { // vaultSwapModalProps: vaultSwapModalProps, vaultDashBoardPanelUIProps, dustCollectorUnAvailableModalProps: { open: localState.modalStatus === 'dustCollectorUnavailable' && !showLeverage.show, onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal', }) }, }, dustCollectorModalProps: { open: localState.modalStatus === 'dustCollector' && !showLeverage.show, onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal', unselectedDustSymbol: [], }) }, converting, dusts, onClickConvert: convert, convertBtnDisabled: (dusts?.find((dust) => dust.checked) ? false : true) || converting, totalValueInCurrency: totalDustsInCurrency, totalValueInUSDT: totalDustsInUSDT ?? EmptyValueTag, onClickRecords: () => { history.push('/l2assets/history/VaultRecords') }, }, debtModalProps: { open: localState.modalStatus === 'debt' && !showLeverage.show, onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal', }) }, borrowedVaultTokens: vaultAccountInfo?.userAssets ?.filter((asset) => new Decimal(asset.borrowed).greaterThan('0')) .map((asset) => { const vaultSymbol = vaultIdIndex[asset.tokenId as unknown as number] as unknown as string const vaultToken = vaultTokenMap[vaultSymbol] const originSymbol = vaultSymbol?.replace('LV', '') const price = tokenPrices[vaultSymbol] const borrowedAmount = vaultToken ? utils.formatUnits(asset.borrowed, vaultToken.decimals) : undefined return { symbol: vaultSymbol?.slice(2), coinJSON: coinJson[originSymbol], amount: borrowedAmount ? numberFormat(borrowedAmount, { fixed: vaultToken?.precision, removeTrailingZero: true, }) : EmptyValueTag, valueInCurrency: price && borrowedAmount && getValueInCurrency(new Decimal(price).mul(borrowedAmount).toString()) ? fiatNumberDisplay( getValueInCurrency(new Decimal(price).mul(borrowedAmount).toString()), currency, ) : EmptyValueTag, onClick: () => { setShowVaultLoan({ isShow: true, info: { symbol: vaultSymbol }, type: VaultLoanType.Repay, }) }, } }), totalDebt: vaultAccountInfo?.totalDebtOfUsdt && getValueInCurrency(vaultAccountInfo?.totalDebtOfUsdt) ? fiatNumberDisplay(getValueInCurrency(vaultAccountInfo?.totalDebtOfUsdt), currency) : EmptyValueTag, totalFundingFee: vaultAccountInfo?.totalInterestOfUsdt && getValueInCurrency(vaultAccountInfo?.totalInterestOfUsdt) ? fiatNumberDisplay(getValueInCurrency(vaultAccountInfo?.totalInterestOfUsdt), currency) : EmptyValueTag, totalBorrowed: vaultAccountInfo?.totalBorrowedOfUsdt && getValueInCurrency(vaultAccountInfo?.totalBorrowedOfUsdt) ? fiatNumberDisplay(getValueInCurrency(vaultAccountInfo?.totalBorrowedOfUsdt), currency) : EmptyValueTag, }, leverageModalProps: { isLoading: localState.leverageLoading, open: localState.modalStatus === 'leverage' || (showLeverage.show && localState.modalStatus !== 'leverageMaxCredit'), onClose: () => { closeShowLeverage() setLocalState({ ...localState, modalStatus: 'noModal', }) }, onClickMaxCredit: () => { setLocalState({ ...localState, modalStatus: 'leverageMaxCredit', }) }, maxLeverage: maxLeverage ? Number(maxLeverage) : 0, onClickReduce: async () => { if (!vaultAccountInfo?.leverage) return await changeLeverage(Number(vaultAccountInfo?.leverage) - 1) if (showLeverage.show && showLeverage.closeAfterChange) { closeShowLeverage() } }, onClickAdd: async () => { if (!vaultAccountInfo?.leverage) return await changeLeverage(Number(vaultAccountInfo?.leverage) + 1) if (showLeverage.show && showLeverage.closeAfterChange) { closeShowLeverage() } }, onClickLeverage: async (leverage) => { await changeLeverage(leverage) if (showLeverage.show && showLeverage.closeAfterChange) { closeShowLeverage() } }, currentLeverage: vaultAccountInfo?.leverage ? Number(vaultAccountInfo?.leverage) : 0, maximumCredit: (vaultAccountInfo as any)?.maxCredit && getValueInCurrency((vaultAccountInfo as any)?.maxCredit) ? fiatNumberDisplay(getValueInCurrency((vaultAccountInfo as any)?.maxCredit), currency) : EmptyValueTag, borrowed: vaultAccountInfo?.totalBorrowedOfUsdt && getValueInCurrency(vaultAccountInfo?.totalBorrowedOfUsdt) ? fiatNumberDisplay(getValueInCurrency(vaultAccountInfo?.totalBorrowedOfUsdt), currency) : EmptyValueTag, borrowAvailable: vaultAccountInfo && collateralToken && getValueInCurrency( new Decimal(vaultAccountInfo.totalEquityOfUsdt) .add(vaultAccountInfo.totalCollateralOfUsdt) .mul(vaultAccountInfo.leverage) .mul(collateralToken.factor) .minus(vaultAccountInfo.totalBorrowedOfUsdt) .toString(), ) ? fiatNumberDisplay( getValueInCurrency( new Decimal(vaultAccountInfo.totalEquityOfUsdt) .add(vaultAccountInfo.totalCollateralOfUsdt) .mul(vaultAccountInfo.leverage) .mul(collateralToken.factor) .minus(vaultAccountInfo.totalBorrowedOfUsdt) .toString(), ), currency, ) : EmptyValueTag, }, maximumCreditModalProps: { open: localState.modalStatus === 'leverageMaxCredit' || (localState.modalStatus === 'collateralDetailsMaxCredit' && !showLeverage.show), onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal', }) }, onClickBack: () => { if (localState.modalStatus === 'collateralDetailsMaxCredit') { setLocalState({ ...localState, modalStatus: 'collateralDetails', }) } else if (localState.modalStatus === 'leverageMaxCredit') { setLocalState({ ...localState, modalStatus: 'leverage', }) } }, collateralFactors: tokenFactors?.map((tokenFactor) => ({ name: tokenFactor.symbol, collateralFactor: numberFormat(tokenFactor.factor, { fixed: 1 }), })) ?? [], maxLeverage: maxLeverage ?? EmptyValueTag, }, collateralDetailsModalProps: { open: localState.modalStatus === 'collateralDetails' && !showLeverage.show, onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal', }) }, onClickMaxCredit: () => { setLocalState({ ...localState, modalStatus: 'collateralDetailsMaxCredit', }) }, collateralTokens: (collateralTokens ?? []).map((collateralToken) => { const tokenSymbol = idIndex[collateralToken.collateralTokenId] const amount = collateralToken.collateralTokenAmount && tokenMap[tokenSymbol] ? utils.formatUnits( collateralToken.collateralTokenAmount, tokenMap[tokenSymbol].decimals, ) : undefined return { name: tokenSymbol, amount: amount ? numberFormat(amount, { fixed: tokenMap[tokenSymbol].precision, removeTrailingZero: true, }) : EmptyValueTag, logo: '', valueInCurrency: amount && tokenPrices['LV' + tokenSymbol] && forexMap && forexMap[currency] && getValueInCurrency(new Decimal(tokenPrices['LV' + tokenSymbol]).mul(amount).toString()) ? fiatNumberDisplay( getValueInCurrency( new Decimal(tokenPrices['LV' + tokenSymbol]).mul(amount).toString(), ), currency, ) : EmptyValueTag, } }), maxCredit: (vaultAccountInfo as any)?.maxCredit && getValueInCurrency((vaultAccountInfo as any)?.maxCredit) ? numberFormatThousandthPlace(getValueInCurrency((vaultAccountInfo as any)?.maxCredit), { fixed: 2, currency, }) : EmptyValueTag, totalCollateral: vaultAccountInfo?.totalCollateralOfUsdt && getValueInCurrency(vaultAccountInfo?.totalCollateralOfUsdt) ? numberFormatThousandthPlace( getValueInCurrency(vaultAccountInfo?.totalCollateralOfUsdt), { fixed: 2, currency, }, ) : EmptyValueTag, coinJSON: vaultAccountInfo?.collateralInfo && coinJson[idIndex[vaultAccountInfo.collateralInfo.collateralTokenId]], }, noAccountHintModalProps: { open: showNoVaultAccount && !isShowVaultJoin?.isShow, onClose: () => setShowNoVaultAccount({ isShow: false, whichBtn: undefined }), title: t(btnProps.title), des: ( ), dialogBtn: noVaultAccountDialogBtn, }, // smallOrderAlertProps: smallOrderAlertProps, supplyCollateralHintModalProps: { open: localState.modalStatus === 'supplyCollateralHint', onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal' }) }, onClickSupply: () => { setLocalState({ ...localState, modalStatus: 'noModal' }) onJoinPop({}) }, }, closeConfirmModalProps: { open: isShowVaultCloseConfirm.isShow, onClose: () => { setShowVaultCloseConfirm({ isShow: false, symbol: undefined }) }, onConfirm: async () => { const { symbol } = isShowVaultCloseConfirm setShowVaultCloseConfirm({ isShow: false, symbol: undefined }) closePositionAndRepayIfNeeded(symbol!) .then(response2 => { if (response2?.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED) { throw new Error('failed') } setShowGlobalToast({ isShow: true, info: { content: 'Closed position successfully', type: ToastType.success } }) }).catch((e) => { setShowGlobalToast({ isShow: true, info: { content: 'Close position failed', type: ToastType.error } }) }).finally(() => { updateVaultLayer2({}) }) }, }, settleConfirmModalProps: { open: localState.modalStatus === 'settleConfirm', onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal' }) }, onConfirm: async () => { }, }, autoRepayModalProps: { open: localState.modalStatus === 'autoRepayConfirm', onClose: () => { setLocalState({ ...localState, modalStatus: 'noModal' }) }, onConfirm: async () => { const list = checkHasTokenNeedRepay() setLocalState({ ...localState, modalStatus: 'noModal' }) await promiseAllSequently(list.map((symbol) => { return () => repayIfNeeded(symbol).catch(() => {}) })) updateVaultLayer2({}) }, }, } } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/hooks/useVaultMarket.ts ================================================ import React from 'react' import { myLog, RouterPath, RowConfig, RowConfigType, SagaStatus, TickerNew, VAULT_MARKET_REFRESH, VaultKey, } from '@loopring-web/common-resources' import { LoopringAPI, useTokenMap, useVaultMap, useVaultTicker, useMarket } from '@loopring-web/core' import { useHistory } from 'react-router-dom' import * as sdk from '@loopring-web/loopring-sdk' import _ from 'lodash' export const useVaultMarket = < R extends TickerNew & { cmcTokenId: number; isFavorite: boolean }, T = sdk.TokenInfo, >({ tableRef, rowConfig = RowConfig, }: { tableRef: React.Ref rowConfig?: RowConfigType }) => { let history = useHistory() const { tokenMap } = useTokenMap() const { tokenMap: vaultTokenMap, marketMap: vaultMarketMap } = useVaultMap() const [detail, setShowDetail] = React.useState<{ isShow: boolean isLoading?: boolean row?: R detail?: { tokenInfo: Partial; trends: any } }>({ isShow: false, isLoading: true, }) const autoRefresh = React.useRef(-1) const { status: vaultTickerStatus, vaultTickerMap, updateVaultTickers } = useVaultTicker() const handleRowClick = React.useCallback(async (_index, row) => { setShowDetail({ isShow: true, isLoading: true, row, detail: { tokenInfo: { ...tokenMap[row?.erc20Symbol ?? ''], ...row, name: '' }, trends: undefined, }, }) try { const [ tokneInfoDetail, // quoteTokenTrend, ...quoteTokenTrends ] = await Promise.all([ LoopringAPI?.exchangeAPI?.getTokenInfo({ cmcTokenId: row.cmcTokenId, // cmcTokenId // token: tokenMap[row?.erc20Symbol ?? '']?.address, currency: 'USD', }), // LoopringAPI?.exchangeAPI?.getQuoteTokenInfo({ // cmcTokenId: row.cmcTokenId, // currency: 'USD', // }), LoopringAPI?.exchangeAPI?.getQuoteTokenInfo({ cmcTokenId: row.cmcTokenId, currency: 'USD', //@ts-ignore range: sdk.DatacenterRange.RANGE_ONE_HOUR, }), LoopringAPI?.exchangeAPI?.getQuoteTokenInfo({ cmcTokenId: row.cmcTokenId, currency: 'USD', //@ts-ignore range: sdk.DatacenterRange.RANGE_ONE_DAY, }), LoopringAPI?.exchangeAPI?.getQuoteTokenInfo({ cmcTokenId: row.cmcTokenId, currency: 'USD', //@ts-ignore range: sdk.DatacenterRange.RANGE_ONE_WEEK, }), LoopringAPI?.exchangeAPI?.getQuoteTokenInfo({ cmcTokenId: row.cmcTokenId, currency: 'USD', //@ts-ignore range: sdk.DatacenterRange.RANGE_ONE_MONTH, }), // LoopringAPI?.exchangeAPI?.getQuoteTokenOhlcv({ // token: tokenMap[row?.erc20Symbol ?? ''].address, // currency: 'USD', // //@ts-ignore // range: sdk.OHLCVDatacenterRange.OHLCV_RANGE_ONE_YEAR, // }), ]) if ( (tokneInfoDetail as sdk.RESULT_INFO).code || (tokneInfoDetail as sdk.RESULT_INFO).message // || // (quoteTokenTrend as sdk.RESULT_INFO).code || // (quoteTokenTrend as sdk.RESULT_INFO).message ) { // throw tokneInfoDetail } setShowDetail({ isShow: true, isLoading: false, row, detail: { tokenInfo: { ...tokenMap[row?.erc20Symbol ?? ''], ...(tokneInfoDetail && tokneInfoDetail.list && tokneInfoDetail.list[0]), ...row, name: tokneInfoDetail && tokneInfoDetail.list && tokneInfoDetail.list[0].name // symbol: row.symbol, }, trends: [ ...quoteTokenTrends?.map((item) => { return item?.list?.reverse().map((trend: string[]) => { const [ timestamp, price, volume24H, volumeChange24H, percentChange1H, percentChange24H, percentChange7D, percentChange30D, marketCap, ] = trend return { close: Number(price).toFixed(2), timeStamp: Number(timestamp), timestamp, price, volume24H, volumeChange24H, percentChange1H, percentChange24H, percentChange7D, percentChange30D, marketCap, } }) }), ] as any, }, }) } catch (e) { myLog('handleRowClick', e) } // }, []) const handleItemClick = React.useCallback( (row: R) => { history.push(`${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}`) }, [history], ) const _marketProps = useMarket({ tableRef, handleItemClick: handleRowClick, handleRowClick, tickerMap: vaultTickerMap as any, tokenMap: vaultTokenMap, }) const marketProps = { ..._marketProps, rawData: _marketProps.rawData.filter((item) => { if ( _.values(vaultMarketMap).every( (market) => market.baseTokenId !== vaultTokenMap['LVUSDC']?.tokenId && market.quoteTokenId !== vaultTokenMap['LVUSDC']?.tokenId, ) && item.symbol === 'LVUSDC' ) { return false } return true }), } const autoReCalc = React.useCallback(() => { updateVaultTickers() if (autoRefresh.current !== -1) { clearTimeout(autoRefresh.current as NodeJS.Timeout) } autoRefresh.current = setTimeout(() => { autoReCalc() }, VAULT_MARKET_REFRESH) }, []) React.useEffect(() => { autoReCalc() return () => { if (autoRefresh.current !== -1) { clearTimeout(autoRefresh.current as NodeJS.Timeout) } } }, []) React.useEffect(() => { if (vaultTickerStatus === SagaStatus.UNSET && vaultTickerMap) { // const data = getFilteredTickList(); marketProps.handleTableFilterChange({}) } }, [vaultTickerStatus]) return { marketProps, detail, setShowDetail } } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/hooks/useVaultPage.ts ================================================ import { useHistory, useRouteMatch } from 'react-router-dom' import React from 'react' import { confirmation, store, useVaultAccountInfo, useVaultMap, } from '@loopring-web/core' import { RouterPath, VaultKey, SagaStatus, RecordTabIndex, } from '@loopring-web/common-resources' import { useOpenModals } from '@loopring-web/component-lib' import { useConfirmation } from '@loopring-web/core/src/stores/localStore/confirmation' import { VaultAccountStatus } from '@loopring-web/loopring-sdk' export const useVaultPage = () => { const VaultPath = `${RouterPath.vault}/:item` let match: any = useRouteMatch(VaultPath) const history = useHistory() const vaultAccountInfo = useVaultAccountInfo() const { status: vaultStatus, getVaultMap, marketArray } = useVaultMap() const [tabIndex, setTabIndex] = React.useState(() => { return ( Object.values(VaultKey).find( (item) => item.toLowerCase() === match?.params?.item?.toLowerCase(), ) ?? undefined ) }) const { setShowVaultJoin, setShowConfirmedVault, modals: { isShowConfirmedVault }, } = useOpenModals() const { setConfirmedOpenVaultPosition } = useConfirmation() const [error, setError] = React.useState(false) const [showLeverage, setShowLeverage] = React.useState({ show: false, closeAfterChange: false }) React.useEffect(() => { const { marketArray } = store.getState().invest.vaultMap if (vaultStatus === SagaStatus.UNSET && marketArray?.length) { setError(false) } else if (vaultStatus === SagaStatus.ERROR) { setError(true) } }, [vaultStatus]) const handleTabChange = (_e: any, value: VaultKey) => { history.push(`${RouterPath.vault}/${value}`) } React.useEffect(() => { setTabIndex( Object.values(VaultKey).find( (item) => item.toLowerCase() === match?.params?.item?.toLowerCase(), ) ) }, [match?.params?.item]) const handleConfirmVaultRiskClose = (_e: any, isAgree: boolean) => { if (!isAgree) { history.push(`${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}`) setShowConfirmedVault({ isShow: false }) } else { history.push(`${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}`) setConfirmedOpenVaultPosition() setShowVaultJoin({ isShow: true, info: { isActiveAccount: true } }) setShowConfirmedVault({ isShow: false }) } } const toggleLeverage = () => { setShowLeverage({ show: !showLeverage.show, closeAfterChange: true }) } const closeLeverage = () => { setShowLeverage({ show: false, closeAfterChange: false }) } const handleRecordClick = () => { history.push(`${RouterPath.l2records}/${RecordTabIndex.VaultRecords}`) } const handleTutorialClick = () => { window.open(`${LOOPRING_DOCUMENT}vault_tutorial_en.md`, '_blank') window.opener = null } return { tabIndex: tabIndex ? tabIndex : vaultAccountInfo?.vaultAccountInfo?.accountStatus === VaultAccountStatus.IN_STAKING ? VaultKey.VAULT_DASHBOARD : VaultKey.VAULT_TRADE, error, showLeverage, isShowConfirmedVault, vaultAccountInfo, marketArray, getVaultMap, handleTabChange, handleConfirmVaultRiskClose, toggleLeverage, closeLeverage, handleRecordClick, handleTutorialClick, } } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/hooks/useVaultSwap.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' import { useGetSet } from 'react-use' import { AccountStatus, CustomErrorWithCode, EmptyValueTag, getValuePrecisionThousand, L1L2_NAME_DEFINED, MapChainId, MarketType, myLog, RouterPath, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, SUBMIT_PANEL_CHECK, TOAST_TIME, TradeBtnStatus, UIERROR_CODE, VaultKey, VaultSwapStep, } from '@loopring-web/common-resources' import { AccountStep, ToastType, useOpenModals, useSettings, useToggle, } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import BigNumberJS from 'bignumber.js' import { btradeOrderbookService, DAYS, getTimestampDaysLater, isNumberStr, l2CommonService, LoopringAPI, MAPFEEBIPS, numberFormat, numberFormatThousandthPlace, onchainHashInfo, store, tryFn, useAccount, useSocket, useSystem, useToast, useTokenMap, useVaultAccountInfo, useVaultLayer2, useVaultMap, VaultBorrowTradeData, vaultSwapDependAsync, toPercent, strNumDecimalPlacesLessThan, useSubmitBtn, } from '@loopring-web/core' import { merge } from 'rxjs' import Decimal from 'decimal.js' import { calcMarinLevel, marginLevelType } from '@loopring-web/core/src/hooks/useractions/vault/utils' import { CloseAllConfirmModalProps } from '../components/modals' import { utils, BigNumber } from 'ethers' import _ from 'lodash' import { closePositionsAndRepayIfNeeded, filterPositions, repayIfNeeded } from '../utils' import { useHistory, useLocation } from 'react-router' import { useAppKitNetwork } from '@reown/appkit/react' import { useConfirmation } from '@loopring-web/core/src/stores/localStore/confirmation' const tWrap = (t: any, label: string, chainId: number) => { const [theLable, ...rest] = label.split('|') const obj = _.fromPairs(rest.map((value, index) => [index === 0 ? 'arg' : 'arg' + index, value])) const network = MapChainId[chainId] return t(theLable, { ...obj, l1ChainName: network ? L1L2_NAME_DEFINED[network].l1ChainName : undefined, loopringL2: network ? L1L2_NAME_DEFINED[network].loopringL2 : undefined, l2Symbol: network ? L1L2_NAME_DEFINED[network].l2Symbol : undefined, l1Symbol: network ? L1L2_NAME_DEFINED[network].l1Symbol : undefined, ethereumL1: network ? L1L2_NAME_DEFINED[network].ethereumL1 : undefined, }) } const calcDexWrap = (input: { info: R input: string sell: string buy: string isAtoB: boolean marketArr: string[] tokenMap: sdk.LoopringMap marketMap: sdk.LoopringMap depth: sdk.DepthData feeBips: string slipBips: string }) => { const output = sdk.calcDex(input) return { ...output, amountB: output?.amountB !== 'NaN' ? output?.amountB : undefined, feeBips: output?.feeBips !== 'NaN' ? output?.feeBips : undefined, amountS: output?.amountS !== 'NaN' ? output?.amountS : undefined, sellVol: output?.amountS !== 'NaN' ? output?.amountS : undefined, buyVol: output?.amountB !== 'NaN' ? output?.amountB : undefined, amountBSlipped: { minReceived: output?.amountBSlipped?.minReceived !== 'NaN' ? output?.amountBSlipped?.minReceived : undefined, minReceivedVal: output?.amountBSlipped?.minReceivedVal !== 'NaN' ? output?.amountBSlipped?.minReceivedVal : undefined, }, } } export type VaultTradeTradeData = VaultBorrowTradeData & { borrowAvailable: string } // } export const useVaultSwap = () => { const borrowHash = React.useRef(null) const refreshRef = React.useRef() const mainViewRef = React.useRef(null) const timerRef = React.useRef(null) const { tokenMap: originTokenMap } = useTokenMap() const { account } = useAccount() const { tokenMap, marketMap, marketArray, getVaultMap, tokenPrices: vaultTokenPrices, } = useVaultMap() const { setShowSupport, setShowTradeIsFrozen, modals: { isShowVaultSwap }, setShowAccount, setShowVaultJoin, setShowGlobalToast, setShowVaultCloseConfirm, setShowConfirmedVault } = useOpenModals() const { toggle: { VaultInvest }, } = useToggle() const { t } = useTranslation(['common', 'error']) const { exchangeInfo, allowTrade } = useSystem() const { updateVaultLayer2, vaultLayer2 } = useVaultLayer2() const { sendSocketTopic, socketEnd } = useSocket() const subjectBtradeOrderbook = React.useMemo(() => btradeOrderbookService.onSocket(), []) const { toastOpen, setToastOpen, closeToast } = useToast() const { coinJson, swapSecondConfirmation, setSwapSecondConfirmation, slippage, setSlippage } = useSettings() const { chainInfos, updateVaultBorrowHash } = onchainHashInfo.useOnChainInfo() const { vaultAccountInfo, maxLeverage } = useVaultAccountInfo() const initLocalState = { hideOther: false, isLongOrShort: undefined as 'long' | 'short' | undefined, showTokenSelection: false, amount: '', selectedToken: undefined as string | undefined, isSwapRatioReversed: false, borrowedAmount: undefined as string | undefined, tokenSelectionInput: '', showSetting: false, isSwapLoading: false, showSmallTradePrompt: { show: false, estimatedFee: undefined as string | undefined, minimumConverted: undefined as string | undefined, feePercentage: undefined as string | undefined, }, swapStatus: { status: 'init', borrowAmount: '10', } as { status: 'borrowing' | 'swapping' | 'init' borrowAmount?: string }, maxBorrowableSellToken: undefined as string | undefined, depth: undefined as sdk.DepthData | undefined, slideValue: 0, closeAllConfirmModal: { show: false, symbol: undefined as undefined | string } } const [getLocalState, setLocalState] = useGetSet(initLocalState) const refreshData = async () => { const { account, settings: { defaultNetwork }, invest: { vaultMap: { marketMap }, }, } = store.getState() const market: MarketType = `LV${getSelectedTokenSymbol()}-LVUSDT` myLog('useVaultSwap: refreshData', market) if (!market || !sellToken || !buyToken) return getVaultMap() updateVaultLayer2({}) if (chainInfos.vaultBorrowHashes && chainInfos.vaultBorrowHashes[account.accAddress]?.length) { chainInfos.vaultBorrowHashes[account.accAddress].forEach(({ hash }) => { LoopringAPI?.vaultAPI ?.getVaultGetOperationByHash( { accountId: account.accountId?.toString(), hash, }, account.apiKey, ) .then(({ operation }) => { if ( [ sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED, sdk.VaultOperationStatus.VAULT_STATUS_FAILED, ].includes(operation.status) ) { updateVaultBorrowHash( operation.hash, account.accAddress, operation.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? 'failed' : 'success', ) } }) }) } const { depth } = await vaultSwapDependAsync({ market: marketMap[market]?.vaultMarket as MarketType, tokenMap, }) setLocalState((state) => ({ ...state, depth, })) const network = MapChainId[defaultNetwork] ?? MapChainId[1] const networkWallet: sdk.NetworkWallet = [ sdk.NetworkWallet.ETHEREUM, sdk.NetworkWallet.GOERLI, sdk.NetworkWallet.SEPOLIA, ].includes(network as sdk.NetworkWallet) ? sdk.NetworkWallet.ETHEREUM : sdk.NetworkWallet[network] const item = marketMap[market] if (depth?.symbol && item?.wsMarket) { sendSocketTopic({ [sdk.WsTopicType.btradedepth]: { showOverlap: false, markets: [item.wsMarket], level: 0, count: 50, snapshot: false, }, [sdk.WsTopicType.l2Common]: { address: account.accAddress, network: networkWallet, }, }) } else { socketEnd() } LoopringAPI.vaultAPI ?.getMaxBorrowable( { accountId: account.accountId, symbol: sellToken.symbol.slice(2), }, account.apiKey, '1', ) .then((maxBorrowable) => { const tokenPrice = vaultTokenPrices[sellToken.symbol] const maxBorrowableOfSellToken = numberFormat( new Decimal(maxBorrowable!.maxBorrowableOfUsdt).div(tokenPrice).toString(), { fixed: sellToken.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }, ) setLocalState((state) => ({ ...state, maxBorrowableSellToken: maxBorrowableOfSellToken, })) }) } const localState = getLocalState() const showSmallTradePrompt = localState.showSmallTradePrompt const setIsSwapLoading = (loading: boolean) => { setLocalState((state) => ({ ...state, isSwapLoading: loading, })) } const setShowSmallTradePrompt = (showSmallTradePrompt: { show: boolean estimatedFee: string | undefined minimumConverted: string | undefined feePercentage: string | undefined }) => { setLocalState((state) => ({ ...state, showSmallTradePrompt, })) } const { pathname, search } = useLocation() const searchParams = new URLSearchParams(search) const isOnPortalTradeRoute = pathname === RouterPath.vault + '/' + VaultKey.VAULT_TRADE const getSelectedTokenSymbol = () => { const localState = getLocalState() return localState.selectedToken ? localState.selectedToken : searchParams.get('symbol') ? searchParams.get('symbol') : 'ETH' } const isLongOrShort = localState.isLongOrShort ? localState.isLongOrShort : searchParams.get('isSell') === 'true' ? 'short' : 'long' const selectedTokenSymbol = getSelectedTokenSymbol() const selectedVTokenSymbol = 'LV' + selectedTokenSymbol const selectedVTokenInfo = tokenMap ? tokenMap[selectedVTokenSymbol] : undefined const LVUSDTInfo = tokenMap ? tokenMap['LVUSDT'] : undefined const USDTInfo = originTokenMap ? originTokenMap['USDT'] : undefined const sellToken = isLongOrShort === 'long' ? LVUSDTInfo : selectedVTokenInfo const sellTokenOriginSymbol = isLongOrShort === 'long' ? 'USDT' : selectedVTokenSymbol.slice(2) const buyToken = isLongOrShort === 'long' ? selectedVTokenInfo : LVUSDTInfo const buyTokenOriginSymbol = isLongOrShort === 'long' ? selectedVTokenSymbol.slice(2) : 'USDT' const market: MarketType = `${selectedVTokenSymbol}-LVUSDT` const marketInfo = marketMap ? marketMap[market] : undefined const smallTradePromptAmtBN = sellToken ? isLongOrShort === 'long' ? marketInfo?.minTradePromptAmount?.quote : marketInfo?.minTradePromptAmount?.base : undefined const smallAmtFeeBips = marketInfo?.upSlippageFeeBips as unknown as number const sellTokenAsset = sellToken && vaultLayer2 ? vaultLayer2[sellToken.symbol] : undefined const borrowAble = localState.maxBorrowableSellToken ? localState.maxBorrowableSellToken : undefined const userMaxSellValueWithoutBorrow = sellToken ? numberFormat( utils.formatUnits( BigNumber.from(sellTokenAsset?.total ?? 0), sellToken?.decimals, ), { fixed: sellToken.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }, ) : undefined const userMaxSellValue = sellToken ? numberFormat( utils.formatUnits( BigNumber.from(sellTokenAsset?.total ?? 0).add( utils.parseUnits(borrowAble ?? '0', sellToken?.decimals), ), sellToken?.decimals, ), { fixed: sellToken.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }, ) : undefined const slippageReal = slippage ? (slippage === 'N' ? 0.1 : slippage) : 0.1 const maxAmountCalcDexOutput = sellToken && buyToken && localState.depth && marketInfo ? calcDexWrap({ info: marketInfo as sdk.VaultMarket, input: userMaxSellValue ?? '0', sell: sellToken.symbol, buy: buyToken.symbol, isAtoB: isLongOrShort === 'long', marketArr: marketArray, tokenMap, marketMap: marketMap as any, depth: localState.depth, feeBips: (marketInfo.feeBips ?? MAPFEEBIPS).toString(), slipBips: slippageReal.toString(), }) : undefined const maxAmountWithoutBorrowCalcDexOutput = sellToken && buyToken && localState.depth && marketInfo ? calcDexWrap({ info: marketInfo as sdk.VaultMarket, input: userMaxSellValueWithoutBorrow ?? '0', sell: sellToken.symbol, buy: buyToken.symbol, isAtoB: isLongOrShort === 'long', marketArr: marketArray, tokenMap, marketMap: marketMap as any, depth: localState.depth, feeBips: (marketInfo.feeBips ?? MAPFEEBIPS).toString(), slipBips: slippageReal.toString(), }) : undefined const userMaxTradeValue = isLongOrShort === 'short' ? userMaxSellValue : maxAmountCalcDexOutput?.amountB const userMaxTradeValueWithoutBorrow = isLongOrShort === 'short' ? userMaxSellValueWithoutBorrow : maxAmountWithoutBorrowCalcDexOutput?.amountB const preCalcDexOutput = sellToken && buyToken && localState.depth && marketInfo ? calcDexWrap({ info: marketInfo as sdk.VaultMarket, input: localState.amount ?? '0', sell: sellToken.symbol, buy: buyToken.symbol, isAtoB: isLongOrShort === 'short', marketArr: marketArray, tokenMap, marketMap: marketMap as any, depth: localState.depth, feeBips: (marketInfo.feeBips ?? MAPFEEBIPS).toString(), slipBips: slippageReal.toString(), }) : undefined const sellAmountEstimate = preCalcDexOutput?.amountS && sellToken ? numberFormat(preCalcDexOutput?.amountS, { fixed: sellToken.decimals }) : undefined const sellAmountEstimateBN = sellToken && sellAmountEstimate && sellAmountEstimate !== 'NaN' ? utils.parseUnits(sellAmountEstimate, sellToken.decimals) : undefined const maxFeeBips = sellAmountEstimateBN && smallTradePromptAmtBN && sellAmountEstimateBN.lt(smallTradePromptAmtBN) ? smallAmtFeeBips : marketInfo?.feeBips ?? MAPFEEBIPS const calcDexOutput = sellToken && buyToken && localState.depth && marketInfo ? calcDexWrap({ info: marketInfo as sdk.VaultMarket, input: localState.amount ?? '0', sell: sellToken.symbol, buy: buyToken.symbol, isAtoB: isLongOrShort === 'short', marketArr: marketArray, tokenMap, marketMap: marketMap as any, depth: localState.depth, feeBips: maxFeeBips.toString(), slipBips: slippageReal.toString(), }) : undefined const swapRatio = tryFn( () => { return localState.isSwapRatioReversed ? `1 USDT ≈ ${numberFormat(new Decimal(1).div(localState.depth!.mid_price).toString(), { fixed: 6, removeTrailingZero: true, })} ${selectedTokenSymbol}` : `1 ${selectedTokenSymbol} ≈ ${numberFormat(localState.depth!.mid_price.toString(), { fixed: marketInfo!.precisionForPrice, removeTrailingZero: true, })} USDT` }, (e) => { return EmptyValueTag }, ) const sellAmount = calcDexOutput?.amountS && calcDexOutput.amountS !== 'NaN' && sellToken ? numberFormat(calcDexOutput?.amountS, { fixed: sellToken.decimals }) : undefined const sellAmountBN = sellToken && sellAmount ? utils.parseUnits(sellAmount, sellToken.decimals) : undefined const buyAmount = calcDexOutput?.amountB && calcDexOutput.amountB !== 'NaN' && buyToken ? numberFormat(calcDexOutput?.amountB, { fixed: buyToken.decimals }) : undefined const buyAmountBN = buyToken && buyAmount ? utils.parseUnits(buyAmount, buyToken.decimals) : undefined const amounInUSDT = isLongOrShort === 'long' ? sellAmount : buyAmount const borrowRequired = sellToken && sellAmountBN ? sellAmountBN.gt( sellTokenAsset?.total && sellTokenAsset?.total !== 'NaN' ? sellTokenAsset?.total : 0, ) : false const moreToBeBorrowedBN = tryFn( () => { const subbed = sellToken && borrowRequired && sellAmountBN ? sellAmountBN.sub(sellTokenAsset?.total ?? 0) : undefined const oneUnit = utils.parseUnits( '1', sellToken!.decimals - sellToken!.vaultTokenAmounts?.qtyStepScale, ) const floorred = subbed?.div(oneUnit).mul(oneUnit) if (subbed && floorred && floorred.eq(subbed)) { return floorred } else { return floorred?.add(oneUnit) } }, () => undefined, ) const moreToBeBorrowed = moreToBeBorrowedBN ? utils.formatUnits(moreToBeBorrowedBN, sellToken?.decimals) : undefined const feeAmountBN = calcDexOutput?.amountBSlipped?.minReceived ? BigNumber.from(calcDexOutput?.amountBSlipped?.minReceived ?? 0) .mul(maxFeeBips) .div(10000) : undefined const feeAmount = feeAmountBN && buyToken ? utils.formatUnits(feeAmountBN, buyToken.decimals) : undefined const totalSellQuota = tryFn( () => { const value = isLongOrShort === 'long' ? new Decimal( utils.formatUnits(localState.depth!.asks_volTotal, LVUSDTInfo!.decimals), ) : new Decimal( utils.formatUnits(localState.depth!.bids_amtTotal, selectedVTokenInfo!.decimals), ) return value.mul('0.99').toString() }, () => undefined, ) const totalTradeQuota = tryFn( () => { const value = new Decimal( utils.formatUnits(localState.depth!.bids_amtTotal, selectedVTokenInfo!.decimals), ) return value.mul('0.99').toString() }, () => undefined, ) const maxSellValue = Decimal.min(userMaxSellValue ?? '0', totalSellQuota ?? '0').toString() const maxTradeValue = Decimal.min(userMaxTradeValue ?? '0', totalTradeQuota ?? '0').toString() const tradeMinAmt = tryFn( () => { const quoteMinAmtInfo = utils.formatUnits( marketInfo!.minTradeAmount.base, selectedVTokenInfo!.decimals, ) const sellDust = utils.formatUnits( selectedVTokenInfo!.orderAmounts.dust, selectedVTokenInfo!.decimals, ) return BigNumberJS.max(sellDust, quoteMinAmtInfo).toString() }, () => undefined, ) const tradeBtnStatus = (() => { if (vaultAccountInfo?.accountStatus !== sdk.VaultAccountStatus.IN_STAKING) { return { label: undefined, disabled: false, } } if ((!tokenMap || !vaultTokenPrices) && sellToken) { return { label: undefined, disabled: true, } } if (!sellToken || !buyToken || !tradeMinAmt || !sellAmount) { return { label: undefined, disabled: true, } } const { account } = store.getState() const sellTokenSymbol = sellToken.symbol const tradeValue = localState.amount ?? '0' const sellExceed = maxTradeValue ? new Decimal(maxTradeValue).lt(tradeValue ?? 0) : false const amtValid = !!( tradeValue && tradeMinAmt && sdk.toBig(tradeValue).gte(tradeMinAmt) && !sellExceed ) const notEnough = userMaxTradeValue ? sdk.toBig(userMaxTradeValue).lt(tradeValue) : true if (localState.isSwapLoading || localState.swapStatus.status !== 'init') { return { label: undefined, disabled: true, } } else { if (account.readyState === AccountStatus.ACTIVATED) { if (!sellAmountBN || !buyAmountBN || !sellAmountBN.gt('0') || !buyAmountBN.gt('0')) { return { label: 'labelEnterAmount', disabled: true, } } else if (notEnough) { return { label: 'labelVaultBorrowNotEnough', disabled: true, } } else if (sellExceed) { const maxOrderSize = maxTradeValue + ' ' + sellTokenSymbol return { label: maxTradeValue ? `labelLimitMax| ${maxOrderSize}` : `labelVaultTradeInsufficient`, disabled: true, } } else if ( borrowRequired && localState.swapStatus.status === 'init' && moreToBeBorrowedBN && BigNumber.from(sellToken.vaultTokenAmounts?.minLoanAmount).gt(moreToBeBorrowedBN) ) { return { label: `labelTradeVaultMiniBorrow|${ getValuePrecisionThousand( utils.formatUnits(sellToken.vaultTokenAmounts?.minLoanAmount, sellToken.decimals), sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, false, { floor: false, isAbbreviate: true }, ) + ' ' + sellTokenSymbol.slice(2) }|${ getValuePrecisionThousand( utils.formatUnits( BigNumber.from(sellToken.vaultTokenAmounts?.minLoanAmount).add( sellTokenAsset?.total ?? 0, ), sellToken.decimals, ), sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, false, { floor: false, isAbbreviate: true }, ) + ' ' + sellTokenSymbol.slice(2) }`, disabled: true, } } else if (!amtValid) { // const sellSymbol = tradeData?.sell.belong if (tradeMinAmt === undefined || tradeMinAmt === 'NaN') { return { label: 'labelEnterAmount', disabled: true, } } else { const minOrderSize = selectedVTokenInfo && getValuePrecisionThousand( sdk.toBig(tradeMinAmt ?? 0), //.div("1e" + sellToken.decimals), selectedVTokenInfo.vaultTokenAmounts?.qtyStepScale, selectedVTokenInfo.vaultTokenAmounts?.qtyStepScale, selectedVTokenInfo.vaultTokenAmounts?.qtyStepScale, false, { floor: false, isAbbreviate: true }, ) if (isNaN(Number(minOrderSize))) { return { label: `labelLimitMin| ${EmptyValueTag + ' ' + selectedTokenSymbol}`, disabled: true, } } else { return { label: `labelLimitMin| ${minOrderSize + ' ' + selectedTokenSymbol}`, disabled: true, } } } } else { return { label: undefined, disabled: false, } } } else { return { label: undefined, disabled: false, } } } })() const myPositions = (() => { return filterPositions(vaultLayer2!, tokenMap, vaultTokenPrices) .map((symbol) => { const asset = vaultLayer2 ? vaultLayer2[symbol] : undefined const tokenInfo = tokenMap?.[symbol] if (!asset || !tokenInfo) return undefined const position = new Decimal(utils.formatUnits(BigNumber.from(asset.netAsset).add(asset.interest), tokenInfo.decimals)) if ( symbol === 'LVUSDT' || position.isZero() || (localState.hideOther && symbol !== selectedVTokenSymbol) ) return undefined return { tokenSymbol: symbol.slice(2), longOrShort: position.isPos() ? 'Long' : 'Short', marginLevel: vaultAccountInfo?.marginLevel ? numberFormat(vaultAccountInfo.marginLevel, { fixed: 2 }) : EmptyValueTag, leverage: vaultAccountInfo?.leverage + 'x', amount: numberFormat(position.abs().toString(), { fixed: tokenInfo.precision, removeTrailingZero: true }), // onClickLeverage: () => {}, onClickTrade: () => { mainViewRef.current?.scrollTo(0, 0) setLocalState({ ...localState, selectedToken: symbol.slice(2), amount: '', slideValue: 0, }) }, onClickClose: () => { setShowVaultCloseConfirm({ isShow: true, symbol: symbol }) }, } }) .filter((item) => { return item !== undefined }) })() const restartTimer = () => { if (timerRef.current) { clearInterval(timerRef.current) } timerRef.current = setInterval(() => { refreshRef.current && (refreshRef.current as any).firstElementChild.click() }, 10 * 1000) refreshRef.current && (refreshRef.current as any).firstElementChild.click() } const clearData = () => { setLocalState(initLocalState) } const swapSubmit = async () => { const { depth } = localState const account = store.getState().account setIsSwapLoading(true) try { const ok = account.readyState === AccountStatus.ACTIVATED && sellToken && buyToken && exchangeInfo && selectedVTokenInfo && sellAmountBN && buyAmountBN if (!ok) { throw new Error('invalid data') } const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: sellToken?.vaultTokenId ?? 0, }, account.apiKey, ) const request: sdk.VaultOrderRequest = { exchange: exchangeInfo.exchangeAddress, storageId: storageId!.orderId, accountId: account.accountId, sellToken: { tokenId: sellToken?.vaultTokenId ?? 0, volume: sellAmountBN.toString(), }, buyToken: { tokenId: buyToken?.vaultTokenId ?? 0, volume: buyAmountBN.toString(), }, validUntil: getTimestampDaysLater(DAYS), maxFeeBips: maxFeeBips, fillAmountBOrS: false, allOrNone: false, eddsaSignature: '', clientOrderId: '', orderType: sdk.OrderTypeResp.TakerOnly, fastMode: false, } myLog('useVaultSwap: submitOrder request', request) // const selectedVTokenInfo = 'selectedVTokenInfo' const item = { fromSymbol: sellToken.symbol, fromAmount: utils.formatUnits(sellAmountBN, sellToken.decimals), settledFromAmount: undefined, toSymbol: buyToken.symbol, feeAmount: feeAmount, settledToAmount: undefined, } const info: any = { sellToken: sellToken.symbol.slice(2), buyToken: buyToken.symbol.slice(2), sellVToken: sellToken.symbol, buyVToken: buyToken.symbol, sellStr: getValuePrecisionThousand( utils.formatUnits(sellAmountBN, sellToken.decimals), sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, false, { floor: false }, ), buyStr: getValuePrecisionThousand( utils.formatUnits(buyAmountBN, buyToken.decimals), buyToken.precision, buyToken.precision, buyToken.precision, false, { floor: false }, ), price: getValuePrecisionThousand( depth?.mid_price, selectedVTokenInfo.precision, selectedVTokenInfo.precision, selectedVTokenInfo.precision, false, { floor: false }, ), sellFStr: undefined, buyFStr: undefined, convertStr: swapRatio, feeStr: undefined, time: Date.now(), fromSymbol: sellToken.symbol, toSymbol: buyToken.symbol, placedAmount: tokenMap && item.fromSymbol && sellToken.symbol.slice(2) && item.fromAmount && sdk.toBig(item.fromAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.fromAmount), undefined, undefined, tokenMap[item.fromSymbol].precision, false, { isAbbreviate: true }, )} ${sellToken.symbol.slice(2)}` : EmptyValueTag, executedAmount: EmptyValueTag, executedRate: EmptyValueTag, convertedAmount: EmptyValueTag, settledAmount: EmptyValueTag, } setShowAccount({ isShow: true, step: AccountStep.VaultTrade_In_Progress, info: { percentage: undefined, status: t('labelPending'), ...info, }, }) const response1 = await LoopringAPI.vaultAPI?.submitVaultOrder( { request, privateKey: account.eddsaKey.sk, apiKey: account.apiKey, }, '1', ) if ((response1 as sdk.RESULT_INFO).code || (response1 as sdk.RESULT_INFO).message) { throw new CustomErrorWithCode({ code: (response1 as sdk.RESULT_INFO).code, message: (response1 as sdk.RESULT_INFO).message, ...SDK_ERROR_MAP_TO_UI[(response1 as sdk.RESULT_INFO)?.code ?? UIERROR_CODE.UNKNOWN], }) } else { setLocalState((state) => ({ ...state, swapStatus: { status: 'init', }, })) l2CommonService.sendUserUpdate() await sdk.sleep(SUBMIT_PANEL_CHECK) if (refreshRef.current) { // @ts-ignore refreshRef.current.firstElementChild.click() } const response2: { hash: string } | any = await LoopringAPI.vaultAPI?.getVaultGetOperationByHash( { accountId: account.accountId as any, // @ts-ignore hash: response1.hash, }, account.apiKey, '1', ) const item = { fromSymbol: sellToken.symbol, fromAmount: sdk.toBig(response2.order.amountS).div('1e' + sellToken.decimals), settledFromAmount: sdk.toBig(response2.order.fillAmountS).div('1e' + sellToken.decimals), toSymbol: buyToken.symbol, settledToAmount: sdk.toBig(response2.order.fillAmountB).div('1e' + buyToken.decimals), feeAmount: sdk.toBig(response2.order.fee).div('1e' + buyToken.decimals), } const info: any = { sellToken: sellToken.symbol.slice(2), buyToken: buyToken.symbol.slice(2), sellFStr: getValuePrecisionThousand( item.settledFromAmount, sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, sellToken.vaultTokenAmounts?.qtyStepScale, false, { floor: false }, ), buyFStr: getValuePrecisionThousand( item.settledToAmount, buyToken.precision, buyToken.precision, buyToken.precision, false, { floor: false }, ), price: getValuePrecisionThousand( depth?.mid_price, selectedVTokenInfo.precision, selectedVTokenInfo.precision, selectedVTokenInfo.precision, false, { floor: false }, ), convertStr: swapRatio, feeStr: feeAmount, time: Date.now(), fromSymbol: sellToken.symbol, toSymbol: buyToken.symbol, placedAmount: tokenMap && item.fromSymbol && item.fromAmount && sdk.toBig(item.fromAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.fromAmount), undefined, undefined, tokenMap[item.fromSymbol].precision, false, { isAbbreviate: true }, )} ${sellToken.symbol.slice(2)}` : EmptyValueTag, executedAmount: tokenMap && item.fromSymbol && sellToken.symbol.slice(2) && item.settledFromAmount && sdk.toBig(item.settledFromAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.settledFromAmount), undefined, undefined, tokenMap[item.fromSymbol].precision, false, { isAbbreviate: true }, )} ${sellToken.symbol.slice(2)}` : EmptyValueTag, executedRate: tokenMap && item.fromSymbol && item.settledFromAmount && item.fromAmount && sdk.toBig(item.fromAmount).gt(0) ? `${sdk .toBig(item.settledFromAmount) .div(item.fromAmount) .multipliedBy('100') .toFixed(2)}%` : EmptyValueTag, convertedAmount: tokenMap && item.toSymbol && buyToken.symbol.slice(2) && item.settledToAmount && sdk.toBig(item.settledToAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.settledToAmount), undefined, undefined, tokenMap[item.toSymbol].precision, false, { isAbbreviate: true }, )} ${buyToken.symbol.slice(2)}` : EmptyValueTag, settledAmount: tokenMap && item.toSymbol && item.settledToAmount && item.feeAmount && sdk.toBig(item.settledToAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.settledToAmount).minus(item.feeAmount), undefined, undefined, tokenMap[item.toSymbol].precision, false, { isAbbreviate: true }, )} ${item.toSymbol}` : EmptyValueTag, } if ( response2?.raw_data?.operation?.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ) { setShowAccount({ isShow: store.getState().modals.isShowAccount.isShow, step: AccountStep.VaultTrade_Failed, info: { ...info, price: response2?.raw_data.order.price, percentage: '', status: t('labelFailed'), }, }) return } const status = [sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED].includes( response2?.raw_data?.operation?.status, ) ? 'labelSuccessfully' : 'labelPending' setShowAccount({ isShow: store.getState().modals.isShowAccount.isShow, step: status === 'labelSuccessfully' ? AccountStep.VaultTrade_Success : AccountStep.VaultTrade_In_Progress, info: { price: response2?.raw_data.order.price, percentage: sdk .toBig(response2?.raw_data?.order?.fillAmountS ?? 0) .div(response2?.raw_data?.order?.amountS ?? 1) .times(100) .toFixed(2), status: t(status), ...info, }, }) sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE).then(() => { l2CommonService.sendUserUpdate() if ( store.getState().modals.isShowAccount.isShow && [AccountStep.VaultTrade_Success, AccountStep.VaultTrade_In_Progress].includes( store.getState().modals.isShowAccount.step, ) ) { setShowAccount({ isShow: false }) } }) setLocalState((state) => ({ ...state, amount: '', })) } } catch (error: any) { if ([102024, 102025, 114001, 114002].includes(error?.code || 0)) { } else { sdk.dumpError400(error) } setShowAccount({ isShow: false, }) setToastOpen({ open: true, type: ToastType.error, content: t('labelVaultTradeFailed') + ' ' + t(error.messageKey, { ns: 'error' }), step: VaultSwapStep.Swap, }) } finally { setLocalState((state) => { return { ...state, swapStatus: { status: 'init' }, isSwapLoading: false, } }) await sdk.sleep(1000) updateVaultLayer2({}) await sdk.sleep(1000) repayIfNeeded(selectedVTokenSymbol) .finally(() => { return repayIfNeeded('LVUSDT') }) .finally(() => { updateVaultLayer2({}) }) } } const borrowSubmit = async () => { const { account } = store.getState() setLocalState((state) => ({ ...state, swapStatus: { status: 'borrowing', borrowAmount: moreToBeBorrowed, }, isSwapLoading: true, })) try { if (exchangeInfo && sellAmountBN && sellToken && moreToBeBorrowedBN) { const vaultBorrowRequest: sdk.VaultBorrowRequest = { accountId: account.accountId, token: { tokenId: sellToken.vaultTokenId as unknown as number, volume: moreToBeBorrowedBN.toString(), }, timestamp: Date.now(), } let response = await LoopringAPI.vaultAPI?.submitVaultBorrow( { request: vaultBorrowRequest, privateKey: account.eddsaKey?.sk, apiKey: account.apiKey, }, '1', ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } const recursiveCheckBorrow = async (hash: string) => { const { account } = store.getState() return LoopringAPI.vaultAPI ?.getVaultGetOperationByHash( { accountId: account.accountId?.toString(), hash: hash, }, account.apiKey, ) .then(({ operation }) => { if (sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED === operation?.status) { l2CommonService.sendUserUpdate() setLocalState((state) => { return { ...state, swapStatus: { status: 'swapping', borrowAmount: moreToBeBorrowed, }, } }) } else if (sdk.VaultOperationStatus.VAULT_STATUS_FAILED === operation?.status) { l2CommonService.sendUserUpdate() throw operation } else { return sdk.sleep(SUBMIT_PANEL_CHECK).then(() => { return recursiveCheckBorrow(hash) }) } }) } // borrowHash.current = { hash: (response as any).hash } await recursiveCheckBorrow((response as any).hash) return swapSubmit() } } catch (e) { const code = (e as any)?.message === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? UIERROR_CODE.ERROR_ORDER_FAILED : (e as sdk.RESULT_INFO)?.code ?? UIERROR_CODE.UNKNOWN const error = new CustomErrorWithCode({ code, message: (e as sdk.RESULT_INFO)?.message, ...SDK_ERROR_MAP_TO_UI[code], }) setToastOpen({ open: true, type: ToastType.error, content: t('labelVaultBorrowFailed') + ' ' + t(error.messageKey, { ns: 'error' }), step: VaultSwapStep.Borrow, }) setLocalState((state) => { return { ...state, swapStatus: { status: 'init' }, isSwapLoading: false, } }) } } const history = useHistory() const { confirmation: { confirmedOpenVaultPosition }, } = useConfirmation() const _onClickTradeBtn = async () => { if (!allowTrade?.order?.enable) { setShowSupport({ isShow: true }) setIsSwapLoading(false) return } if ((market && !marketMap[market]?.enabled) || !VaultInvest.enable) { setShowTradeIsFrozen({ isShow: true, type: 'Vault', }) setIsSwapLoading(false) return } if (vaultAccountInfo?.accountStatus !== sdk.VaultAccountStatus.IN_STAKING) { if (!confirmedOpenVaultPosition) { setShowConfirmedVault({ isShow: true }) } else { history.push(`${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}`) setShowConfirmedVault({ isShow: false }) setShowVaultJoin({ isShow: true}) } return } if ( smallTradePromptAmtBN && sellAmountBN && BigNumber.from(smallTradePromptAmtBN).gt(sellAmountBN) && !showSmallTradePrompt.show ) { const minimumConverted = tryFn( () => { return numberFormatThousandthPlace( sdk .toBig(calcDexOutput!.amountB!) .times(sdk.toBig(1).minus(sdk.toBig(slippageReal).div('10000'))) .toString(), { fixed: buyToken!.precision, removeTrailingZero: true }, ) }, () => '0', ) setShowSmallTradePrompt({ show: true, feePercentage: maxFeeBips ? new Decimal(maxFeeBips).div('100').toFixed(2) : '', estimatedFee: feeAmount + ' ' + buyTokenOriginSymbol, minimumConverted: minimumConverted + ' ' + buyTokenOriginSymbol, }) return } if (borrowRequired) { borrowSubmit() } else { swapSubmit() } } const { chainId: _chainId } = useAppKitNetwork() const chainId = Number(_chainId) const { btnStatus, onBtnClick: onClickTradeBtn, btnLabel, isAccountActive } = useSubmitBtn({ availableTradeCheck: (a: any) => ({ tradeBtnStatus: tradeBtnStatus.disabled ? TradeBtnStatus.DISABLED : localState.isSwapLoading ? TradeBtnStatus.LOADING : TradeBtnStatus.AVAILABLE, label: tradeBtnStatus.label, }), isLoading: localState.isSwapLoading, submitCallback: _onClickTradeBtn, }) React.useEffect(() => { if (isOnPortalTradeRoute && refreshRef.current) { restartTimer() } else { timerRef.current && clearInterval(timerRef.current) clearData() if (borrowHash?.current?.hash) { updateVaultBorrowHash(borrowHash?.current?.hash, account.accAddress) } setToastOpen({ open: false, content: '', type: ToastType.info, step: '' }) } return () => { if (borrowHash?.current?.hash) { updateVaultBorrowHash(borrowHash?.current?.hash, account.accAddress) } if (borrowHash.current?.timer) { clearTimeout(borrowHash.current?.timer) setIsSwapLoading(false) borrowHash.current = null } } }, [isOnPortalTradeRoute, refreshRef.current]) React.useEffect(() => { const subscription = merge(subjectBtradeOrderbook).subscribe(({ btradeOrderbookMap }) => { const localState = getLocalState() const { invest: { vaultMap: { marketMap }, }, } = store.getState() const item = marketMap && marketMap[market] if ( item && btradeOrderbookMap && item?.wsMarket && btradeOrderbookMap[item.wsMarket] && localState.depth?.symbol && item.wsMarket === btradeOrderbookMap[item.wsMarket]?.symbol ) { setLocalState({ ...localState, depth: { ...btradeOrderbookMap[item.wsMarket], symbol: localState.depth.symbol }, }) myLog('useVaultSwap: depth', btradeOrderbookMap[item.wsMarket]) } }) return () => { subscription.unsubscribe() } }, [market]) const [showCloseAllConfirm, setShowCloseAllConfirm] = React.useState({ show: false }) const closeAllConfirmModalProps: CloseAllConfirmModalProps = { open: showCloseAllConfirm.show, onClose: () => { setShowCloseAllConfirm({ show: false }) }, onConfirm: async () => { const symbols = filterPositions(vaultLayer2!, tokenMap, vaultTokenPrices) closePositionsAndRepayIfNeeded(symbols) .then((resList) => { const foundErr = resList.find( (res) => res.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED, ) if (foundErr) { throw new Error('failed') } setShowGlobalToast({ isShow: true, info: { content: 'Closed position successfully', type: ToastType.success, }, }) }) .catch((e) => { setShowGlobalToast({ isShow: true, info: { content: 'Close position failed', type: ToastType.error, }, }) }) .finally(() => { setShowCloseAllConfirm({ show: false }) updateVaultLayer2({}) }) } } const vaultSwapModalProps = { onClickCloseAll: () => { setShowCloseAllConfirm({ show: true, }) }, mainViewRef, open: isShowVaultSwap.isShow, hideOther: localState.hideOther, onClickHideOther: () => { setLocalState({ ...localState, hideOther: !localState.hideOther, }) }, onClose: () => {}, setting: { hideLeverage: true, onClickSettingLeverage: () => {}, leverage: 1, onSwitchChange: () => { setSwapSecondConfirmation(!swapSecondConfirmation) }, secondConfirmationChecked: swapSecondConfirmation ?? false, settingPopoverOpen: localState.showSetting, onCloseSettingPopover: () => { setLocalState({ ...localState, showSetting: false, }) }, onClickSettingBtn: () => { setLocalState({ ...localState, showSetting: !localState.showSetting, }) }, slippageList: ['0.1', '0.5', '1', `slippage:${slippage}`], currentSlippage: slippage, onSlippageChange: (_slippage, customSlippage) => { setSlippage(_slippage) }, }, countdown: { countDownSeconds: 10, onRefreshData: () => { refreshData() }, ref: refreshRef, }, isLongOrShort: isLongOrShort, onClickBalance: () => { userMaxTradeValueWithoutBorrow && setLocalState({ ...localState, amount: numberFormat(userMaxTradeValueWithoutBorrow, { fixed: selectedVTokenInfo!.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }), }) }, onClickMax: () => { maxTradeValue && setLocalState({ ...localState, amount: numberFormat(maxTradeValue, { fixed: selectedVTokenInfo?.vaultTokenAmounts.qtyStepScale, fixedRound: Decimal.ROUND_FLOOR, removeTrailingZero: true, }), }) }, onClickToken: () => { setLocalState({ ...localState, showTokenSelection: true, }) }, balance: tryFn( () => { if (new Decimal(userMaxSellValueWithoutBorrow!).lessThanOrEqualTo('0')) return '0 ' + sellTokenOriginSymbol return ( numberFormatThousandthPlace(userMaxSellValueWithoutBorrow!, { fixed: sellToken!.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }) + ' ' + sellTokenOriginSymbol ) }, () => { return EmptyValueTag + ' ' + sellTokenOriginSymbol }, ), token: { symbol: selectedTokenSymbol, coinJSON: coinJson[selectedTokenSymbol], }, onInputAmount: (amount: string) => { if ( amount && (!isNumberStr(amount) || !selectedVTokenInfo || !strNumDecimalPlacesLessThan( amount, selectedVTokenInfo.vaultTokenAmounts.qtyStepScale + 1, )) ) return setLocalState({ ...localState, slideValue: 0, amount, }) }, inputPlaceholder: tradeMinAmt ? `Min ${numberFormat(tradeMinAmt, { fixed: selectedVTokenInfo?.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_CEIL, })}` : '0.00', amountInput: localState.amount, inputAlert: { show: btnStatus !== TradeBtnStatus.AVAILABLE || localState.swapStatus.status !== 'init', type: tradeBtnStatus.label !== undefined ? 'error' : localState.swapStatus.status === 'borrowing' ? 'warning' : localState.swapStatus.status === 'swapping' ? 'success' : undefined, message: tryFn( () => { if (tradeBtnStatus.label) return tWrap(t, tradeBtnStatus.label, chainId ?? 1) if (localState.swapStatus.status === 'borrowing') { return ( localState.swapStatus.borrowAmount && sellToken && `Borrowing ${numberFormatThousandthPlace(localState.swapStatus.borrowAmount!, { fixed: sellToken.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, })} ${sellTokenOriginSymbol}` ) } else if (localState.swapStatus.status === 'swapping') { return ( localState.swapStatus.borrowAmount && sellToken && `Borrowed ${numberFormatThousandthPlace(localState.swapStatus.borrowAmount!, { fixed: sellToken.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, })} ${sellTokenOriginSymbol}` ) } }, () => '', ), icon: localState.swapStatus.status === 'borrowing' ? 'wait' : localState.swapStatus.status === 'swapping' ? 'completed' : undefined, }, swapRatio, onClickReverse: () => { setLocalState({ ...localState, isSwapRatioReversed: !localState.isSwapRatioReversed, }) }, amounInUSDT: amounInUSDT ? numberFormatThousandthPlace(amounInUSDT, { fixed: LVUSDTInfo?.precisionForOrder, removeTrailingZero: true, }) + ' USDT' : EmptyValueTag + ' USDT', maxTradeValue: tryFn( () => { return ( numberFormatThousandthPlace(maxSellValue, { fixed: sellToken?.vaultTokenAmounts.qtyStepScale, fixedRound: Decimal.ROUND_FLOOR, removeTrailingZero: true, }) + ' ' + sellTokenOriginSymbol ) }, () => EmptyValueTag + ' ' + sellTokenOriginSymbol, ), borrowed: tryFn( () => { if (new Decimal(sellTokenAsset!.borrowed).lessThanOrEqualTo(0)) return EmptyValueTag return ( numberFormatThousandthPlace( utils.formatUnits(sellTokenAsset!.borrowed!, sellToken?.decimals), { fixed: sellToken!.precision, fixedRound: Decimal.ROUND_FLOOR, removeTrailingZero: true, }, ) + ' ' + sellTokenOriginSymbol ) }, () => EmptyValueTag, ), moreToBeBorrowed: tryFn( () => { return ( numberFormatThousandthPlace(moreToBeBorrowed!, { fixed: sellToken!.vaultTokenAmounts.qtyStepScale, fixedRound: Decimal.ROUND_CEIL, removeTrailingZero: true, }) + ' ' + sellTokenOriginSymbol ) }, () => EmptyValueTag, ), totalQuota: tryFn( () => numberFormatThousandthPlace(totalSellQuota!, { fixed: sellToken?.vaultTokenAmounts.qtyStepScale, fixedRound: Decimal.ROUND_FLOOR, removeTrailingZero: true, }) + ' ' + sellTokenOriginSymbol, () => EmptyValueTag, ), marginLevelChange: tryFn( () => { if (moreToBeBorrowed && new Decimal(moreToBeBorrowed).greaterThan('0')) { const moreToBorrowInUSD = moreToBeBorrowed && new Decimal(moreToBeBorrowed).greaterThan('0') ? new Decimal(moreToBeBorrowed).mul(vaultTokenPrices[sellToken!.symbol!]).toString() : undefined var nextMarginLevel = calcMarinLevel( vaultAccountInfo!.totalCollateralOfUsdt, vaultAccountInfo!.totalDebtOfUsdt, vaultAccountInfo!.totalBalanceOfUsdt, moreToBorrowInUSD!, '0', ) } return { from: { marginLevel: vaultAccountInfo!.marginLevel, type: marginLevelType(vaultAccountInfo!.marginLevel), }, to: nextMarginLevel ? { marginLevel: nextMarginLevel, type: marginLevelType(nextMarginLevel!), } : undefined, } }, () => undefined, ), tradeBtn: { disabled: btnStatus === TradeBtnStatus.DISABLED, onClick: () => { onClickTradeBtn() }, label: btnLabel ? tWrap(t, btnLabel, chainId ?? 1) : vaultAccountInfo?.accountStatus === sdk.VaultAccountStatus.FREE ? 'Supply Collateral' : isLongOrShort === 'long' ? 'Buy / Long' : 'Sell / Short', loading: localState.isSwapLoading, bgColor: !isAccountActive || vaultAccountInfo?.accountStatus === sdk.VaultAccountStatus.FREE ? 'var(--color-primary)' : isLongOrShort === 'long' ? 'var(--color-success)' : 'var(--color-error)', }, hourlyInterestRate: sellTokenAsset?.borrowed && new Decimal(sellTokenAsset.borrowed).gt(0) && sellToken ? toPercent(sellToken.interestRate, 7) : EmptyValueTag, tradingFee: tryFn( () => { if (new Decimal(feeAmount!).lessThanOrEqualTo('0')) { return EmptyValueTag + ' ' + buyTokenOriginSymbol } return ( numberFormatThousandthPlace(feeAmount!, { fixed: buyToken!.precision, removeTrailingZero: true, }) + ' ' + buyTokenOriginSymbol ) }, () => EmptyValueTag + ' ' + buyTokenOriginSymbol, ), tradingFeeDescription: tryFn( () => { const feeBips = maxFeeBips ?? marketInfo?.feeBips ?? MAPFEEBIPS return `The trading fee is ${toPercent((feeBips / 100).toString(), 2)}.` }, () => '', ), slippageTolerance: `${slippageReal}%`, onClickLongShort: (v: 'long' | 'short') => { setLocalState({ ...localState, isLongOrShort: v, maxBorrowableSellToken: undefined, slideValue: 0, amount: '', depth: undefined, }) restartTimer() }, myPositions, showMyPositions: account.readyState === 'ACTIVATED', leverageSelection: { value: vaultAccountInfo?.leverage, onChange: async (value: string) => { await LoopringAPI.vaultAPI ?.submitLeverage( { request: { accountId: account.accountId.toString(), leverage: value, }, apiKey: account.apiKey, }, '1', ) .finally(() => { refreshData() }) }, items: maxLeverage ? _.range(1, Number(maxLeverage) + 1).map((leverage: number) => ({ label: `${leverage}x`, value: leverage.toString(), disabled: false, })) .concat(Number(vaultAccountInfo?.leverage) > Number(maxLeverage) ? [ { label: vaultAccountInfo?.leverage + 'x', value: vaultAccountInfo?.leverage ?? '', disabled: true, }, ] : []) : [], }, positionTypeSelection: { value: 'cross', onChange: () => {}, items: [ { label: 'Cross Position', value: 'cross', tooltipTitle: 'All positions within this account share a single health factor, determined by the real-time prices of your collateral, holdings, and debts. If the health factor falls below the liquidation threshold, all positions under this account are subject to liquidation.', }, ], }, ratioSlider: { currentRatio: localState.slideValue, onClickRatio: (no: number) => { if (!maxTradeValue || new Decimal(maxTradeValue).eq('0')) return setLocalState((state) => { return { ...state, amount: new Decimal(maxTradeValue) .mul(new Decimal(no)) .toDecimalPlaces(selectedVTokenInfo?.vaultTokenAmounts.qtyStepScale) .toString(), slideValue: no, } }) }, }, tokenSelection: { show: localState.showTokenSelection, search: localState.tokenSelectionInput, onClickCancel: () => { setLocalState({ ...localState, showTokenSelection: false, tokenSelectionInput: '', }) }, tokens: tokenMap ? _.keys(tokenMap) .map((symbol) => { const asset = vaultLayer2 ? vaultLayer2[symbol] : undefined const tokenInfo = tokenMap[symbol] return tokenInfo ? { symbol: symbol.slice(2), amount: numberFormatThousandthPlace( utils.formatUnits(asset?.total ?? '0', tokenInfo.decimals), { fixed: tokenInfo.precision, removeTrailingZero: true }, ), coinJSON: coinJson[symbol.slice(2)], onClick: () => { setLocalState({ ...localState, showTokenSelection: false, selectedToken: symbol === 'LVUSDT' ? 'ETH' : symbol.slice(2), tokenSelectionInput: '', amount: '', slideValue: 0, depth: undefined, }) restartTimer() refreshData() }, } : undefined }) .filter( (item) => item !== undefined && !['USDT', 'USDC', 'LRTAIKO'].includes(item?.symbol) && (!localState.tokenSelectionInput || item.symbol.toLowerCase().includes(localState.tokenSelectionInput.toLowerCase())), ) : [], onInputSearch: (v: string) => { setLocalState({ ...localState, tokenSelectionInput: v, amount: '', }) }, }, showLeverageSelect: account.readyState === AccountStatus.ACTIVATED, } const smallOrderAlertProps = { open: showSmallTradePrompt.show, handleClose: () => { setShowSmallTradePrompt({ show: false, estimatedFee: undefined, minimumConverted: undefined, feePercentage: undefined, }) }, handleConfirm: () => { setShowSmallTradePrompt({ show: false, estimatedFee: undefined, minimumConverted: undefined, feePercentage: undefined, }) if (borrowRequired) { borrowSubmit() } else { swapSubmit() } }, estimatedFee: showSmallTradePrompt.estimatedFee ?? '', feePercentage: showSmallTradePrompt.feePercentage ?? '', minimumReceived: showSmallTradePrompt.minimumConverted ?? '', } const toastProps = { alertText: toastOpen?.content ?? '', open: toastOpen?.open ?? false, autoHideDuration: TOAST_TIME, onClose: closeToast, severity: toastOpen?.type, } return { vaultSwapModalProps, smallOrderAlertProps, toastProps, closeAllConfirmModalProps, } } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/index.tsx ================================================ import { useSettings, } from '@loopring-web/component-lib' import { useVaultPage } from './hooks/useVaultPage' import { VaultPageUI } from './components/VaultPageUI' export { useVaultMarket } from './hooks/useVaultMarket' export { useGetVaultAssets } from './hooks/useVaultDashBoard' export const VaultPage = () => { const props = useVaultPage() return ( ) } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/interface.ts ================================================ import { ReactNode } from 'react' import { CurrencyToTag, VaultAction, UpColor, ForexMap, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { ModalStatePlayLoad, } from '@loopring-web/component-lib' import { TFunction } from 'react-i18next' import { Theme } from '@emotion/react' import { VaultPositionsTableProps } from '@loopring-web/component-lib/src/components/tableList/assetsTable/VaultPositionsTable' export interface VaultDashBoardPanelUIProps { t: TFunction; forexMap: ForexMap; theme: Theme; currency: CurrencyToTag; hideAssets: boolean; upColor: keyof typeof UpColor; showMarginLevelAlert: boolean; vaultAccountInfo?: sdk.VaultAccountInfo; localState: any; setLocalState: (state: any) => void; colors: string[]; assetPanelProps: any; marketProps: any; vaultTokenMap: any; vaultTickerMap: any; VaultDustCollector: any; isShowVaultJoin: ModalStatePlayLoad; detail: any; setShowDetail: (detail: any) => void; hideLeverage: boolean; activeInfo: any; walletMap: any; _vaultAccountInfo: any; tokenPrices: any; getValueInCurrency: (value: string) => string; history: any; etherscanBaseUrl: string; onClickCollateralManagement: () => void; onClickSettle: () => void; onClickPortalTrade: () => void; liquidationThreshold: string; liquidationPenalty: string; assetsTab: 'assetsView' | 'positionsView'; onChangeAssetsTab: (tab: 'assetsView' | 'positionsView') => void; onClickRecord: () => void; vaultPositionsTableProps: VaultPositionsTableProps; onClickHideShowAssets: () => void; vaultAccountActive: boolean totalEquity: string showSettleBtn: boolean btnsDisabled: boolean onClickBuy: (market: any) => void; onClickSell: (market: any) => void; didAccountSignIn: boolean } export interface CollateralDetailsModalProps { open: boolean; onClose: () => void; onClickMaxCredit: () => void; collateralTokens: { name: string; logo: string; amount: string; valueInCurrency: string; }[]; totalCollateral: string; maxCredit: string; coinJSON: any; } export interface MaximumCreditModalProps { open: boolean; onClose: () => void; onClickBack: () => void; collateralFactors: { name: string; collateralFactor: string; }[]; maxLeverage: string; } export interface LeverageModalProps { open: boolean; maxLeverage: number; onClose: () => void; onClickMaxCredit: () => void; onClickReduce: () => void; onClickAdd: () => void; onClickLeverage: (leverage: number) => void; currentLeverage: number | undefined; borrowAvailable: string; borrowed: string; maximumCredit: string; isLoading: boolean; } export interface DebtModalProps { open: boolean onClose: () => void totalFundingFee: string totalBorrowed: string totalDebt: string borrowedVaultTokens: | { symbol: string coinJSON: any amount: string valueInCurrency: string onClick: () => void }[] | undefined } export interface DustCollectorUnAvailableModalProps { open: boolean onClose: () => void } export interface DustCollectorProps { open: boolean converting: boolean onClose: () => void dusts?: { symbol: string; amount: string; valueInCurrency: string; checked: boolean; coinJSON: any; onCheck: () => void; }[] totalValueInUSDT: string totalValueInCurrency: string onClickConvert: () => void onClickRecords: () => void convertBtnDisabled: boolean } ================================================ FILE: packages/component-lib/src/sharedPages/VaultPage/utils.ts ================================================ import { DAYS, getTimestampDaysLater, LoopringAPI, NETWORKEXTEND, VaultLayer2Map, withRetry } from "@loopring-web/core" import { ChainId, ConnectorNames, toBig, VaultToken } from "@loopring-web/loopring-sdk" import { ConnectProviders, connectProvides } from "@loopring-web/web3-provider" import { SUBMIT_PANEL_CHECK, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { numberFormat, store, MAPFEEBIPS, vaultSwapDependAsync, bignumberFix } from '@loopring-web/core' import { utils, BigNumber } from 'ethers' import Decimal from 'decimal.js' import _, { keys } from 'lodash' import { promiseAllSequently } from "@loopring-web/core/src/utils/promise" import { updateVaultLayer2 } from "@loopring-web/core/src/stores/vaultLayer2/reducer" const checkIfNeedRepay = (symbol: string) => { const { vaultLayer2: { vaultLayer2 }, invest: { vaultMap: { tokenMap }, }, } = store.getState() const asset = vaultLayer2?.[symbol] const tokenInfo = tokenMap[symbol] if (!asset) return false const minLoanAmount = tokenInfo?.vaultTokenAmounts?.minLoanAmount const repayVolume = BigNumber.from(asset.borrowed).lte(BigNumber.from(asset.total)) ? asset.borrowed : asset.total return new Decimal(asset?.borrowed ?? '0').gt('0') && new Decimal(asset?.total ?? '0').gt('0') && (!minLoanAmount || toBig(repayVolume ?? '0').gte(minLoanAmount)) } export const checkHasTokenNeedRepay = () => { const { vaultLayer2: { vaultLayer2 }, } = store.getState() return vaultLayer2 ? keys(vaultLayer2).filter((symbol) => checkIfNeedRepay(symbol) && symbol !== 'undefined') : [] } export const repayIfNeeded = async (symbol: string) => { const { vaultLayer2: { vaultLayer2 }, invest: { vaultMap: { tokenMap, }, }, system: { exchangeInfo, chainId }, account } = store.getState() const asset = vaultLayer2?.[symbol] const tokenInfo = tokenMap[symbol] as VaultToken if (!checkIfNeedRepay(symbol)) return const [{ broker }, { offchainId }] = await Promise.all([ LoopringAPI.userAPI!.getAvailableBroker({ type: 4, }), LoopringAPI.userAPI!.getNextStorageId( { accountId: account.accountId, sellTokenId: tokenInfo.vaultTokenId, }, account.apiKey, ), ]) const volume = BigNumber.from(asset!.borrowed).lte(BigNumber.from(asset!.total)) ? asset!.borrowed : asset!.total const volumeFixed = bignumberFix( BigNumber.from(volume), tokenInfo.decimals, tokenInfo.vaultTokenAmounts.qtyStepScale, 'FLOOR' ) const request = { exchange: exchangeInfo!.exchangeAddress, payerAddr: account.accAddress, payerId: account.accountId, payeeId: 0, payeeAddr: broker, storageId: offchainId, token: { tokenId: tokenInfo.vaultTokenId, volume: volumeFixed.toString(), }, maxFee: { tokenId: tokenInfo.vaultTokenId, volume: '0', }, validUntil: getTimestampDaysLater(DAYS), memo: '', } return await LoopringAPI.vaultAPI?.submitVaultRepay( { request: request, web3: connectProvides.usedWeb3 as any, chainId: chainId === NETWORKEXTEND.NONETWORK ? ChainId.MAINNET : chainId, walletType: (ConnectProviders[account.connectName] ?? account.connectName) as unknown as ConnectorNames, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, }, { accountId: account.accountId, counterFactualInfo: account.eddsaKey.counterFactualInfo, }, '1' ) } const recursiveCheckHash = async (hash: string): Promise<{ operation: sdk.VaultOperation; order: sdk.VaultOrder; raw_data: { operation: sdk.VaultOperation; order: sdk.VaultOrder; }; }> => { const { account } = store.getState() return LoopringAPI.vaultAPI!.getVaultGetOperationByHash( { accountId: account.accountId as any, // @ts-ignore hash: hash, }, account.apiKey, '1', ).then((res) => { if (res.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_PROCESSING || res.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_PENDING) { return sdk.sleep(2000) .then(() => { return recursiveCheckHash(hash) }) } else if (res.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_FAILED) { throw res } else { return res } }) } const closeShort = async (symbol: string) => { const { invest: { vaultMap: { tokenMap }, }, account, } = store.getState() const res = await LoopringAPI.vaultAPI?.closeShort( { request: { accountId: account.accountId, tokenId: tokenMap[symbol].vaultTokenId, timestamp: Date.now(), }, }, account.apiKey, account.eddsaKey.sk, ) if ((res as sdk.RESULT_INFO).code || (res as sdk.RESULT_INFO).message) { throw res } else { const hash = (res as {hash: string}).hash return recursiveCheckHash(hash) } } const closeLongDust = async (symbol: string) => { const { vaultLayer2: { vaultLayer2 }, invest: { vaultMap: { tokenMap }, }, account, system: { exchangeInfo }, } = store.getState() const vaultAsset = vaultLayer2![symbol] const closeTokenInfo = tokenMap[symbol] const tokenId = closeTokenInfo.vaultTokenId const { offchainId } = await LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: tokenId, }, account.apiKey, )! const { broker } = await LoopringAPI.userAPI?.getAvailableBroker({ type: 4, })! const dustTransfer = { exchange: exchangeInfo!.exchangeAddress, payerAddr: account.accAddress, payerId: account.accountId, payeeId: 0, payeeAddr: broker, storageId: offchainId, token: { tokenId: tokenId, volume: vaultAsset.total, }, maxFee: { tokenId: tokenId, volume: '0', }, validUntil: getTimestampDaysLater(DAYS), memo: '', } const res = await LoopringAPI.vaultAPI?.submitDustCollector( { dustTransfers: [dustTransfer], apiKey: account.apiKey, accountId: account.accountId, eddsaKey: account.eddsaKey.sk, }, '1', ) if ((res as sdk.RESULT_INFO).code || (res as sdk.RESULT_INFO).message) { throw res } else { const hash = (res as {hash: string}).hash return recursiveCheckHash(hash) } } const closeLongDustIfNeeded = async (symbol: string) => { const { vaultLayer2: { vaultLayer2 } } = store.getState() const vaultAsset = vaultLayer2 && symbol ? vaultLayer2[symbol] : undefined if (!vaultAsset || !new Decimal(vaultAsset.netAsset).isPos()) { return undefined } else { return closeLongDust(symbol) } } const closeLong = async (symbol: string, depth: sdk.DepthData) => { const { vaultLayer2: { vaultLayer2 }, invest: { vaultMap: { tokenMap, marketMap, marketArray }, }, account, settings: { slippage }, system: { exchangeInfo }, } = store.getState() const vaultAsset = vaultLayer2![symbol] const sellToken = tokenMap[symbol] as VaultToken const buyToken = tokenMap['LVUSDT'] const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: sellToken?.vaultTokenId ?? 0, }, account.apiKey, ) const market = `${symbol}-LVUSDT` const marketInfo = marketMap[market] as sdk.VaultMarket const slippageReal = Math.max(1, slippage === 'N' ? 1 : slippage) const sellAmountBN = bignumberFix( BigNumber.from(vaultAsset.total).abs(), sellToken.decimals, sellToken.vaultTokenAmounts.qtyStepScale, 'FLOOR', ) const output = sdk.calcDex({ info: marketInfo, input: utils.formatUnits( sellAmountBN.toString(), tokenMap[symbol].decimals, ), sell: sellToken.symbol, buy: buyToken.symbol, isAtoB: true, marketArr: marketArray, tokenMap: tokenMap, marketMap: marketMap as any, depth: depth!, feeBips: (marketInfo.feeBips ?? MAPFEEBIPS).toString(), slipBips: slippageReal.toString(), }) const buyAmountBN = utils.parseUnits( numberFormat(output!.amountB!, { fixed: buyToken.decimals }), buyToken.decimals, ) const request: sdk.VaultOrderRequest = { exchange: exchangeInfo!.exchangeAddress, storageId: storageId!.orderId, accountId: account.accountId, sellToken: { tokenId: sellToken?.vaultTokenId ?? 0, volume: sellAmountBN.toString(), }, buyToken: { tokenId: buyToken?.vaultTokenId ?? 0, volume: buyAmountBN.toString(), }, validUntil: getTimestampDaysLater(DAYS), maxFeeBips: MAPFEEBIPS, fillAmountBOrS: false, allOrNone: false, eddsaSignature: '', clientOrderId: '', orderType: sdk.OrderTypeResp.TakerOnly, fastMode: false, } const response = await LoopringAPI.vaultAPI?.submitVaultOrder( { request, privateKey: account.eddsaKey.sk, apiKey: account.apiKey, }, '1', ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else { const hash = (response as {hash: string}).hash return recursiveCheckHash(hash) } } const checkIsDust = async (symbol: string) => { const { vaultLayer2: { vaultLayer2 }, invest: { vaultMap: { tokenMap, marketMap, marketArray }, }, settings: { slippage }, } = store.getState() const vaultAsset = vaultLayer2![symbol] const sellToken = tokenMap[symbol] const buyToken = tokenMap['LVUSDT'] const market = `${symbol}-LVUSDT` const marketInfo = marketMap[market] as sdk.VaultMarket const { depth } = await vaultSwapDependAsync({ market: (marketInfo as any).vaultMarket, tokenMap: tokenMap, }) const slippageReal = slippage === 'N' ? 0.1 : slippage const output = sdk.calcDex({ info: marketInfo, input: utils.formatUnits(BigNumber.from(vaultAsset.netAsset).abs().toString(), sellToken.decimals), sell: sellToken.symbol, buy: buyToken.symbol, isAtoB: true, marketArr: marketArray, tokenMap: tokenMap, marketMap: marketMap as any, depth: depth!, feeBips: (marketInfo.feeBips ?? MAPFEEBIPS).toString(), slipBips: slippageReal.toString(), }) const USDTAmount = output!.amountB! return { isDust: new Decimal(USDTAmount).lt('10'), depth } } const closePosition = async (symbol: string) => { const { vaultLayer2: { vaultLayer2 }, system: { exchangeInfo }, } = store.getState() const vaultAsset = vaultLayer2 && symbol ? vaultLayer2[symbol] : undefined if (!symbol || !vaultAsset || !exchangeInfo || new Decimal(vaultAsset.netAsset).isZero()) throw new Error('error') if (!new Decimal(vaultAsset.netAsset).isPos()) { var response = await closeShort(symbol) } else { const { isDust, depth } = await checkIsDust(symbol) if (isDust) { response = await closeLongDust(symbol) } else { response = await withRetry(closeLong, 2)(symbol, depth!) } } return response } export const closePositionAndRepayIfNeeded = async (symbol: string) => { const response = await closePosition(symbol) updateVaultLayer2({}) sdk.sleep(2 * 1000).then(() => { return promiseAllSequently( [symbol, 'LVUSDT'].map((symbol) => () => repayIfNeeded(symbol).catch(() => undefined)), ) }) return response } const closePositions = async (symbols: string[]) => { return promiseAllSequently( symbols.map((symbol) => async () => { const res = await closePosition(symbol) await sdk.sleep(500) return res }), ) as Promise<{ operation: sdk.VaultOperation; order: sdk.VaultOrder; }[]> } export const closePositionsAndRepayIfNeeded = async (symbols: string[]) => { const responses = await closePositions(symbols) updateVaultLayer2({}) await sdk.sleep(500) await promiseAllSequently( symbols.concat('LVUSDT').map((symbol) => () => repayIfNeeded(symbol).catch(() => undefined)), ) return responses } export const filterPositions = (vaultLayer2: VaultLayer2Map, tokenMap: sdk.LoopringMap, tokenPrices: sdk.LoopringMap) => { return _.keys(vaultLayer2).filter((symbol) => { const asset = vaultLayer2 ? vaultLayer2[symbol] : undefined const tokenInfo = tokenMap?.[symbol] const tokenPrice = tokenPrices?.[symbol] if (!asset || !tokenInfo || !tokenPrice) return false const position = new Decimal( utils.formatUnits(BigNumber.from(asset.netAsset).add(asset.interest), tokenInfo.decimals), ) const positionValue = position.abs().times(tokenPrice) return symbol !== 'LVUSDT' && positionValue.greaterThan('10') }) } ================================================ FILE: packages/component-lib/src/sharedPages/index.ts ================================================ export {VaultPage, useGetVaultAssets, useVaultMarket} from './VaultPage' ================================================ FILE: packages/component-lib/src/static.ts ================================================ import { Account, AccountStatus, AmmInData, BanxaOrder, CoinInfo, CoinMap, DualCalcData, DualViewInfo, ForexMap, HeaderMenuItemInterface, TradeCalcData, WalletCoin, WalletMap, } from '@loopring-web/common-resources' import { List } from 'immutable' import { ConnectProviders } from '@loopring-web/web3-provider' import { Currency, DUAL_TYPE, LuckyTokenItemForReceive } from '@loopring-web/loopring-sdk' export const account: Account = { hasUnknownCollection: true, __timer__: -1, frozen: false, accAddress: 'xxxxxxxxxxxxxxxxxxx', qrCodeUrl: '', readyState: AccountStatus.UN_CONNECT, accountId: -1, apiKey: '', eddsaKey: '', publicKey: {}, level: '', keySeed: '', nonce: undefined, keyNonce: undefined, connectName: ConnectProviders.Unknown, } export const coinMap: CoinMap> = { ETH: { icon: 'https://exchange.loopring.io/assets/images/ethereum/assets/0x9A0aBA393aac4dFbFf4333B06c407458002C6183/logo.png', name: 'ETH', simpleName: 'ETH', description: '', company: 'ETH', }, LRC: { icon: 'https://exchange.loopring.io/assets/images/ethereum/assets/0x565aBA393aac4dFbFf4333B06c407458002C6183/logo.png', name: 'LRC', simpleName: 'LRC', description: '', company: 'LRC', }, USDT: { icon: 'https://exchange.loopring.io/assets/images/ethereum/assets/0x565aBA393aac4dFbFf4333B06c407458002C6183/logo.png', name: 'USDT', simpleName: 'USDT', description: '', company: 'USDT', }, USDC: { icon: 'https://exchange.loopring.io/assets/images/ethereum/assets/0x565aBA393aac4dFbFf4333B06c407458002C6183/logo.png', name: 'USDC', simpleName: 'USDC', description: '', company: 'USDC', }, LRCA: { icon: 'red', name: 'LRCA', simpleName: 'LRCA', description: '', company: 'LRC', }, LRCB: { icon: 'red', name: 'LRCA', simpleName: 'LRCB', description: '', company: 'LRC', }, DPR: { icon: 'blue', name: 'DPR', simpleName: 'DPR', description: '', company: 'DPR', }, CCB: { icon: 'blue', name: 'CCB', simpleName: 'CCB', description: '', company: 'ETH', }, OKB: { icon: 'blue', name: 'OKB', simpleName: 'OKB', description: '', company: 'ETH', }, CRV: { icon: 'blue', name: 'CRV', simpleName: 'CRV', description: '', company: 'CRV', }, TEST: { icon: 'blue', name: 'TEST', simpleName: 'TEST', description: '', company: 'TEST', }, TEST2: { icon: 'blue', name: 'TEST3', simpleName: 'TEST2', description: '', company: 'CRV', }, TEST3: { icon: 'blue', name: 'TEST3', simpleName: 'TEST3', description: '', company: 'TEST3', }, } export const walletMap = { ETH: { belong: 'ETH', count: 11, }, LRC: { belong: 'LRC', count: 11111111111111, }, } export enum ButtonComponentsMap { Download, Notification, Theme, Language, } export const inputProps = { label: 'Enter Payment Token', subLabel: 'Max', emptyText: 'Select Token', placeholderText: '0.00', coinMap: coinMap, } export const coinType = { ETH: 'ETH', USDT: 'USDT', USDC: 'USDC', LRC: 'LRC', CRV: 'CRV', DPR: 'DPR', CCB: 'CCB', OKB: 'OKB', LRCA: 'LRCA', LRCB: 'LRCB', TEST: 'TEST', TEST2: 'TEST2', TEST3: 'TEST3', } export const tradeCalcData: TradeCalcData = { isBtrade: false, coinSell: 'ETH', //name coinBuy: 'LRC', BtoS: '1,11', StoB: '1,11', buyPrecision: 5, sellPrecision: 7, coinInfoMap: coinMap, sellCoinInfoMap: coinMap, buyCoinInfoMap: coinMap, walletMap: walletMap as WalletMap>, slippage: 0.5, priceImpact: '12', priceImpactColor: 'var(--color-success)', minimumReceived: '1%', fee: '1%', } as any export const ammCalcData: AmmInData = { myCoinA: { belong: 'ETH', balance: 1000, tradeValue: 0 }, myCoinB: { belong: 'LRC', balance: 1000, tradeValue: 0 }, lpCoinA: { belong: 'ETH', balance: 1000, tradeValue: 0 }, lpCoinB: { belong: 'LRC', balance: 122, tradeValue: 0 }, lpCoin: { belong: 'ETH', balance: 1000, tradeValue: 0 }, AtoB: 50, BtoA: 50, coinInfoMap: coinMap, slippage: 0.5, fee: '0.01', fees: {}, percentage: '0.01', } export const layer2ItemData = List([ { label: { id: 'lite', i18nKey: 'labelClassic', description: 'Simple and easy-to-user interface', }, router: { path: '' }, }, { label: { id: 'pro', i18nKey: 'labelAdvanced', description: 'Full access to all trading tools', }, router: { path: '' }, }, ]) export const TOKEN_INFO = { tokenMap: { ETH: { type: 'ETH', tokenId: 0, symbol: 'ETH', name: 'Ethereum', address: '0x0000000000000000000000000000000000000000', decimals: 18, precision: 7, precisionForOrder: 3, orderAmounts: { minimum: '5000000000000000', maximum: '1000000000000000000000', dust: '200000000000000', }, luckyTokenAmounts: { minimum: '50000000000000', maximum: '1000000000000000000000', dust: '50000000000000', }, fastWithdrawLimit: '100000000000000000000', gasAmounts: { distribution: '85000', deposit: '100000', }, enabled: true, isLpToken: false, tradePairs: ['LRC', 'USDT', 'USDC'], }, LRC: { type: 'erc20Trade', tokenId: 1, symbol: 'LRC', name: 'Loopring', address: '0xfc28028d9b1f6966fe74710653232972f50673be', decimals: 18, precision: 3, precisionForOrder: 3, orderAmounts: { minimum: '5000000000000000000', maximum: '5000000000000000000000000', dust: '5000000000000000000', }, luckyTokenAmounts: { minimum: '50000000000000000', maximum: '5000000000000000000000000', dust: '50000000000000000', }, fastWithdrawLimit: '750000000000000000000000', gasAmounts: { distribution: '101827', deposit: '200000', }, enabled: true, isLpToken: false, tradePairs: ['ETH'], }, USDT: { type: 'erc20Trade', tokenId: 2, symbol: 'USDT', name: 'USDT', address: '0xd4e71c4bb48850f5971ce40aa428b09f242d3e8a', decimals: 6, precision: 2, precisionForOrder: 3, orderAmounts: { minimum: '5000000', maximum: '2000000000000', dust: '250000', }, luckyTokenAmounts: { minimum: '50000', maximum: '200000000000', dust: '50000', }, fastWithdrawLimit: '250000000000', gasAmounts: { distribution: '106233', deposit: '200000', }, enabled: true, isLpToken: false, tradePairs: ['ETH', 'DAI'], }, 'LP-LRC-ETH': { type: 'erc20Trade', tokenId: 4, symbol: 'LP-LRC-ETH', name: 'AMM-LRC-ETH', address: '0xfeb069407df0e1e4b365c10992f1bc16c078e34b', decimals: 8, precision: 6, precisionForOrder: 3, orderAmounts: { minimum: '100000000', maximum: '10000000000000000000', dust: '100000000', }, luckyTokenAmounts: { minimum: '100000000', maximum: '10000000000000000000', dust: '100000000', }, fastWithdrawLimit: '20000000000', gasAmounts: { distribution: '150000', deposit: '200000', }, enabled: true, isLpToken: true, }, 'LP-ETH-USDT': { type: 'erc20Trade', tokenId: 7, symbol: 'LP-ETH-USDT', name: 'LP-ETH-USDT', address: '0x049a02fa9bc6bd54a2937e67d174cc69a9194f8e', decimals: 8, precision: 6, precisionForOrder: 3, orderAmounts: { minimum: '100000000', maximum: '10000000000000', dust: '100000000', }, luckyTokenAmounts: { minimum: '100000000', maximum: '10000000000000', dust: '100000000', }, fastWithdrawLimit: '20000000000', gasAmounts: { distribution: '150000', deposit: '200000', }, enabled: true, isLpToken: true, }, DAI: { type: 'erc20Trade', tokenId: 6, symbol: 'DAI', name: 'dai', address: '0xcd2c81b322a5b530b5fa3432e57da6803b0317f7', decimals: 18, precision: 6, precisionForOrder: 3, orderAmounts: { minimum: '10000000000000000000', maximum: '100000000000000000000000', dust: '10000000000000000', }, luckyTokenAmounts: { minimum: '10000000000000000000', maximum: '100000000000000000000000', dust: '10000000000000000000', }, fastWithdrawLimit: '10000000000000000000000', gasAmounts: { distribution: '150000', deposit: '200000', }, enabled: true, isLpToken: false, tradePairs: ['USDT'], }, USDC: { type: 'USDC', tokenId: 8, symbol: 'USDC', name: 'USDC', address: '0x47525e6a5def04c9a56706e93f54cc70c2e8f165', decimals: 6, precision: 6, precisionForOrder: 3, orderAmounts: { minimum: '1000', maximum: '10000000000000000000', dust: '100', }, luckyTokenAmounts: { minimum: '1000000', maximum: '10000000000', dust: '1000000', }, fastWithdrawLimit: '20000000000000000000', gasAmounts: { distribution: '150000', deposit: '200000', }, enabled: true, isLpToken: false, tradePairs: ['ETH'], }, 'LP-USDC-ETH': { type: 'LP-USDC-ETH', tokenId: 9, symbol: 'LP-USDC-ETH', name: 'LP-USDC-ETH', address: '0xf37cf4ced77b985708d591acc6bfd08586ab3409', decimals: 8, precision: 7, precisionForOrder: 3, orderAmounts: { minimum: '100000', maximum: '1000000000000000000000000000000000000000', dust: '10000', }, luckyTokenAmounts: { minimum: '1000000000000000', maximum: '10000000000000000000', dust: '1000000000000000', }, fastWithdrawLimit: '20000000000000000000', gasAmounts: { distribution: '150000', deposit: '200000', }, enabled: true, isLpToken: true, }, }, idIndex: { '0': 'ETH', '1': 'LRC', '2': 'USDT', '4': 'LP-LRC-ETH', '6': 'DAI', '7': 'LP-ETH-USDT', '8': 'USDC', '9': 'LP-USDC-ETH', }, marketMap: { 'LRC-ETH': { baseTokenId: 1, enabled: true, market: 'LRC-ETH', orderbookAggLevels: 5, precisionForPrice: 6, quoteTokenId: 0, status: 3, isSwapEnabled: true, createdAt: 1617967800000, }, 'ETH-USDT': { baseTokenId: 0, enabled: true, market: 'ETH-USDT', orderbookAggLevels: 3, precisionForPrice: 3, quoteTokenId: 2, status: 3, isSwapEnabled: true, createdAt: 1617972300000, }, 'DAI-USDT': { baseTokenId: 6, enabled: true, market: 'DAI-USDT', orderbookAggLevels: 2, precisionForPrice: 4, quoteTokenId: 2, status: 3, isSwapEnabled: true, createdAt: 0, }, 'USDC-ETH': { baseTokenId: 8, enabled: true, market: 'USDC-ETH', orderbookAggLevels: 3, precisionForPrice: 3, quoteTokenId: 0, status: 3, isSwapEnabled: true, createdAt: 1636974420000, }, }, } export type CoinType = typeof coinType export const DUALVIEWINFO: DualViewInfo = { apy: '57.49%', settleRatio: '0.1656', term: 'a day', productId: 'LRC-USDT-220907-0.36-P-USDT', expireTime: 1662537600000, currentPrice: { quote: 'USDC', base: 'LRC', precisionForPrice: 3, currentPrice: 0.4482078, quoteUnit: 'USD', }, __raw__: { info: { baseSize: '111', productId: 'LRC-USDT-220907-0.36-P-USDT', base: 'LRC', quote: 'USDT', currency: 'USDT', createTime: 1662336421000, expireTime: 1662537600000, strike: '0.36', expired: false, dualType: 'DUAL_CURRENCY' as DUAL_TYPE, ratio: 0.46, profit: '', }, index: { index: '0.36206575', base: 'LRC', quote: 'USDT', indexTime: 1662446738246, }, rule: { base: 'LRC', quote: 'USDT', currency: 'USDT', basePrecision: 8, currencyPrecision: 8, baseMin: '20', currencyMin: '50', baseMax: '200000', currencyMax: '200000', granulation: 10, baseProfitStep: 4, }, }, strike: '0.36', isUp: false, sellSymbol: 'USDC', buySymbol: 'LRC', } export const DUALCALCDATA: DualCalcData = { balance: {}, coinSell: { balance: 100, belong: 'USDC', tradeValue: undefined, }, quota: '1000', feeTokenSymbol: 'LRC', feeVol: undefined, greaterEarnTokenSymbol: '', greaterEarnVol: '', lessEarnTokenSymbol: '', lessEarnVol: '', maxFeeBips: 0, maxSellAmount: '', miniSellVol: '', sellToken: TOKEN_INFO.tokenMap['USDC'], sellVol: '', dualViewInfo: DUALVIEWINFO, } export const FOREXMAP: ForexMap = { [Currency.usd]: 1, [Currency.cny]: 6.7, } as any export const REDPACKETMOCK: LuckyTokenItemForReceive = { hash: '', sender: { accountId: 10008, address: '0xxxxxxxx', ens: '', }, champion: { accountId: 10008, address: '0xxxxxxxx', ens: '', amount: 100000, }, tokenId: 1, tokenAmount: { totalCount: 10, remainCount: 0, totalAmount: '100000000000000000', remainAmount: '0', } as any, type: { partition: 0, scope: 0, mode: 0, }, //@ts-ignore status: 'RANDOM', validSince: 1662669827, validUntil: 1662769827, info: { memo: 'Best wishes Best wishes Best wishes wishes Best wishes Best wishes Best wishes Best wishes Best wishes', signer: '', signerUrl: '', logoUrl: '', }, templateNo: 0, createdAt: 1662769827, } export const LUCKTOKENLIST = [ { id: 117080, hash: '0x2635dc5a575d8b2972bfa60db73667b0eb236ec1314885f5a0b42e15165ca806', sender: { accountId: 41441, address: '0x85992e1fc5f0f1a6edb6f4dac3a072fb0426b6c5', ens: '', }, champion: { accountId: 83208, address: '0x8bc49232d786cbaddc699b0e57783ccda913aeea', ens: '', amount: '121163022576739548', }, tokenId: 1, tokenAmount: { totalCount: 2, remainCount: 0, totalAmount: '220000000000000000', remainAmount: '0', }, type: { partition: 0, scope: 0, mode: 1, }, status: 'COMPLETED', validSince: 1672331822000, validUntil: 1672418222000, info: { memo: '10LRC to carl.loopring.eth > 2 part nft collection', signer: '', signerUrl: '', logoUrl: '', }, templateNo: 0, createdAt: 1672245500416, isNft: false, }, { id: 117076, hash: '0x2f5c482b0711f5204491fe31ee927f0dde79906e2261a8e46106dd309ad0100f', sender: { accountId: 41441, address: '0x85992e1fc5f0f1a6edb6f4dac3a072fb0426b6c5', ens: '', }, champion: { accountId: 66702, address: '0x4a860d9764882ca402d380964f81438c407765fd', ens: '', amount: '137774109408598431', }, tokenId: 1, tokenAmount: { totalCount: 2, remainCount: 0, totalAmount: '220000000000000000', remainAmount: '0', }, type: { partition: 0, scope: 0, mode: 1, }, status: 'COMPLETED', validSince: 1672231875000, validUntil: 1672318275000, info: { memo: '10LRC to carl.loopring.eth > 2 part nft collection', signer: '', signerUrl: '', logoUrl: '', }, templateNo: 0, createdAt: 1672145547112, isNft: false, }, ] export const mockReturn: { order: BanxaOrder } = { order: { id: 'dd734aec66eb781ecc7f7bb01274ec63', account_id: '324a77f69fc5797c2afbe67efefddbba', account_reference: '0xff7d59d9316eba168837e3ef924bcdfd64b237d8', order_type: 'CRYPTO-SELL', payment_type: null, ref: null, fiat_code: 'AUD', fiat_amount: 0, coin_code: 'USDC', coin_amount: 0, wallet_address: null, wallet_address_tag: null, fee: null, fee_tax: null, payment_fee: null, payment_fee_tax: null, commission: null, tx_hash: null, tx_confirms: 0, created_date: '30-Nov-2022', created_at: '30-Nov-2022 17:48:25', status: 'pendingPayment', completed_at: null, merchant_fee: null, merchant_commission: null, meta_data: null, blockchain: { id: 30, code: 'LRC', description: 'Loopring ' }, }, } ================================================ FILE: packages/component-lib/src/stores/index.ts ================================================ export * from './reducer/settings' export * from './reducer/modals' export * from './reducer/toggle' ================================================ FILE: packages/component-lib/src/stores/reducer/modals/hook.ts ================================================ import { useDispatch, useSelector } from 'react-redux' import { ModalState, ModalStatePlayLoad, Transaction } from './interface' import { setNFTMetaNotReady, setShowAccount, setShowActiveAccount, setShowAmm, setShowAnotherNetworkNotice, setShowClaimWithdraw, setShowCollectionAdvance, setShowConnect, setShowDeposit, setShowDual, setShowExportAccount, setShowFeeSetting, setShowGlobalToast, setShowIFrame, setShowLayerSwapNotice, setShowNFTDeploy, setShowNFTDeposit, setShowNFTDetail, setShowNFTMintAdvance, setShowNFTTransfer, setShowNFTWithdraw, setShowOtherExchange, setShowRedPacket, setShowResetAccount, setShowSideStakingRedeem, setShowSupport, setShowSwap, setShowTargetRedpacketPop, setShowTradeIsFrozen, setShowTransfer, setShowWithdraw, setShowWrongNetworkGuide, setShowEditContact, setShowVaultJoin, setShowVaultExit, setShowVaultSwap, setShowVaultLoan, setShowNoVaultAccount, setShowConfirmedVault, setShowETHStakingApr, setShowTransferToTaikoAccount, setShowVaultCloseConfirm, setShowBridge, setShowClosureAnnouncement, } from './reducer' import React from 'react' import { CLAIM_TYPE, ClaimToken, DualViewInfo, NFTWholeINFO, TradeNFT, AmmPanelType, CoinSource, Contact, VaultAction, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { ToggleState } from '../toggle' import { ToastType } from '../../../components' export const useOpenModals = () => { const dispatch = useDispatch() const toggle = useSelector((state: any) => state.toggle) as ToggleState return { modals: useSelector((state: any) => state.modals) as ModalState, setShowRedPacket: React.useCallback( ( state: ModalStatePlayLoad & { step?: number info?: { [key: string]: any } }, ) => dispatch(setShowRedPacket(state)), [dispatch], ), setNFTMetaNotReady: React.useCallback( ( state: ModalStatePlayLoad & { step?: number info?: { [key: string]: any } }, ) => dispatch(setNFTMetaNotReady(state)), [dispatch], ), setShowSupport: React.useCallback( (state: ModalStatePlayLoad & Transaction) => dispatch(setShowSupport(state)), [dispatch], ), setShowOtherExchange: React.useCallback( ( state: ModalStatePlayLoad & { agree?: boolean } & Transaction, ) => dispatch(setShowOtherExchange(state)), [dispatch], ), setShowWrongNetworkGuide: React.useCallback( (state: ModalStatePlayLoad & Transaction) => dispatch(setShowWrongNetworkGuide(state)), [dispatch], ), setShowTransfer: React.useCallback( (state: ModalStatePlayLoad & Transaction & Contact) => { if (!toggle.transfer.enable && state.isShow) { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Transfer' })) return } dispatch(setShowTransfer(state)) }, [dispatch, toggle.transfer.enable], ), setShowTransferToTaikoAccount: React.useCallback( (state: ModalStatePlayLoad & { from?: string }) => { if (toggle.transferToTaikoAccount.enable) { dispatch(setShowTransferToTaikoAccount(state)) } else { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'TransferToTaikoAccount' })) } }, [dispatch, toggle.transferToTaikoAccount.enable], ), setShowNFTDeploy: React.useCallback( (state: ModalStatePlayLoad & Transaction) => { dispatch(setShowNFTDeploy(state)) }, [dispatch], ), setShowDeposit: React.useCallback( (state: ModalStatePlayLoad & Transaction & { partner?: boolean }) => { if (!toggle.deposit.enable && state.isShow) { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Deposit' })) return } dispatch(setShowDeposit(state)) }, [dispatch, toggle.deposit.enable], ), setShowWithdraw: React.useCallback( (state: ModalStatePlayLoad & Transaction & Contact) => { if (!toggle.withdraw.enable && state.isShow) { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Withdraw' })) return } dispatch(setShowWithdraw(state)) }, [dispatch, toggle.withdraw.enable], ), setShowNFTDetail: React.useCallback( (state: ModalStatePlayLoad & Partial) => { dispatch(setShowNFTDetail(state)) }, [dispatch], ), setShowNFTTransfer: React.useCallback( (state: ModalStatePlayLoad & Partial) => { if (toggle.transferNFT.enable) { dispatch(setShowNFTTransfer(state)) } else { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Transfer' })) } }, [dispatch, toggle.transferNFT.enable], ), setShowNFTDeposit: React.useCallback( (state: ModalStatePlayLoad & Partial>) => { if (toggle.depositNFT.enable) { dispatch(setShowNFTDeposit(state)) } else { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Deposit' })) } }, [dispatch, toggle.depositNFT.enable], ), setShowCollectionAdvance: React.useCallback( (state: ModalStatePlayLoad & Partial>) => { if (toggle.collectionNFT.enable) { dispatch(setShowCollectionAdvance(state)) } else { dispatch(setShowCollectionAdvance({ isShow: true, type: 'Collection' })) } }, [dispatch, toggle.collectionNFT.enable], ), setShowNFTMintAdvance: React.useCallback( (state: ModalStatePlayLoad & Partial>) => { if (toggle.mintNFT.enable) { dispatch(setShowNFTMintAdvance(state)) } else { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Mint' })) } }, [dispatch, toggle.mintNFT.enable], ), setShowNFTWithdraw: React.useCallback( (state: ModalStatePlayLoad & Partial) => { if (toggle.withdrawNFT.enable) { dispatch(setShowNFTWithdraw(state)) } else { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Withdraw' })) } }, [dispatch, toggle.withdrawNFT.enable], ), setShowResetAccount: React.useCallback( (state: ModalStatePlayLoad) => { if (!toggle.updateAccount.enable && state.isShow) { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'reset-account' })) return } dispatch(setShowResetAccount(state)) }, [dispatch, toggle.updateAccount.enable], ), setShowActiveAccount: React.useCallback( (state: ModalStatePlayLoad) => { if (toggle.updateAccount.enable) { dispatch(setShowActiveAccount(state)) } else { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'active-account' })) } }, [dispatch, toggle.updateAccount.enable], ), setShowAmm: React.useCallback( (state: ModalStatePlayLoad & Transaction & { type?: AmmPanelType }) => dispatch(setShowAmm(state)), [dispatch], ), setShowSwap: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowSwap(state)), [dispatch], ), setShowAccount: React.useCallback( ( state: ModalStatePlayLoad & { step?: number error?: sdk.RESULT_INFO info?: { [key: string]: any } }, ) => dispatch(setShowAccount(state)), [dispatch], ), setShowDual: React.useCallback( (state: ModalStatePlayLoad & { dualInfo: DualViewInfo | undefined }) => dispatch(setShowDual(state)), [dispatch], ), setShowClaimWithdraw: React.useCallback( ( state: ModalStatePlayLoad & { claimToken?: ClaimToken claimType?: CLAIM_TYPE }, ) => { if (toggle.claim.enable) { dispatch(setShowClaimWithdraw(state)) } else { dispatch(setShowTradeIsFrozen({ isShow: true, type: 'Claim' })) } }, [dispatch, toggle.claim.enable], ), setShowConnect: React.useCallback( ( state: ModalStatePlayLoad & { step?: number error?: sdk.RESULT_INFO info?: { [key: string]: any } }, ) => dispatch(setShowConnect(state)), [dispatch], ), setShowIFrame: React.useCallback( (state: ModalStatePlayLoad & { url: string }) => dispatch(setShowIFrame(state)), [dispatch], ), setShowExportAccount: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowExportAccount(state)), [dispatch], ), setShowFeeSetting: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowFeeSetting(state)), [dispatch], ), setShowLayerSwapNotice: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowLayerSwapNotice(state)), [dispatch], ), setShowAnotherNetworkNotice: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowAnotherNetworkNotice(state)), [dispatch], ), setShowTradeIsFrozen: React.useCallback( (state: ModalStatePlayLoad & { type?: string; messageKey?: string }) => dispatch(setShowTradeIsFrozen(state)), [dispatch], ), setShowSideStakingRedeem: React.useCallback( (state: ModalStatePlayLoad & { symbol?: string }) => dispatch(setShowSideStakingRedeem(state)), [dispatch], ), setShowGlobalToast: React.useCallback( (state: { isShow: boolean info: { content?: string type: ToastType messageKey?: string } }) => dispatch(setShowGlobalToast(state)), [dispatch], ), setShowTargetRedpacketPop: React.useCallback( (state: { isShow: boolean info: { exclusiveRedPackets?: (sdk.LuckyTokenItemForReceive & { tokenIcon: CoinSource tokenName: string })[] } }) => dispatch(setShowTargetRedpacketPop(state)), [dispatch], ), setShowEditContact: React.useCallback( (state: { isShow: boolean; info?: any }) => dispatch(setShowEditContact(state)), [dispatch], ), setShowETHStakingApr: React.useCallback( (state: ModalStatePlayLoad & Transaction) => dispatch(setShowETHStakingApr(state)), [dispatch], ), setShowVaultExit: React.useCallback( (state: ModalStatePlayLoad & Transaction) => dispatch(setShowVaultExit(state)), [dispatch], ), setShowVaultJoin: React.useCallback( (state: ModalStatePlayLoad & Transaction) => dispatch(setShowVaultJoin(state)), [dispatch], ), setShowVaultSwap: React.useCallback( (state: ModalStatePlayLoad & Transaction & {isSell?: boolean}) => dispatch(setShowVaultSwap(state)), [dispatch], ), setShowVaultLoan: React.useCallback( (state: ModalStatePlayLoad & Transaction & { type?: string }) => dispatch(setShowVaultLoan(state)), [dispatch], ), setShowVaultCloseConfirm: React.useCallback( (state: ModalStatePlayLoad & Transaction ) => dispatch(setShowVaultCloseConfirm(state)), [dispatch], ), setShowNoVaultAccount: React.useCallback( ( state: ModalStatePlayLoad & Transaction & { whichBtn?: VaultAction | undefined; des?: string; title?: string }, ) => dispatch(setShowNoVaultAccount(state)), [dispatch], ), setShowConfirmedVault: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowConfirmedVault(state)), [dispatch], ), setShowBridge: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowBridge(state)), [dispatch], ), setShowClosureAnnouncement: React.useCallback( (state: ModalStatePlayLoad) => dispatch(setShowClosureAnnouncement(state)), [dispatch], ), } } ================================================ FILE: packages/component-lib/src/stores/reducer/modals/index.ts ================================================ export * from './hook' export * from './interface' export * from './reducer' ================================================ FILE: packages/component-lib/src/stores/reducer/modals/interface.ts ================================================ import { CLAIM_TYPE, ClaimToken, DualViewInfo, NFTWholeINFO, TradeNFT, AmmPanelType, VaultAction, CoinSource, ToastType, Contact, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' export enum ModalType { transfer = 'transfer', deposit = 'deposit', withdraw = 'withdraw', } export type ModalTypeKeys = keyof typeof ModalType export type ModalStatePlayLoad = { isShow: boolean info?: { [key: string]: any } } export type Transaction = { symbol?: undefined | string } export interface ModalState { isShowSupport: ModalStatePlayLoad isShowNFTMetaNotReady: ModalStatePlayLoad isShowOtherExchange: ModalStatePlayLoad & { agree?: boolean } isWrongNetworkGuide: ModalStatePlayLoad isShowClaimWithdraw: ModalStatePlayLoad & { claimToken: ClaimToken | undefined claimType: CLAIM_TYPE | undefined } isShowTransfer: ModalStatePlayLoad & Transaction & Contact isShowWithdraw: ModalStatePlayLoad & Transaction & Contact isShowDeposit: ModalStatePlayLoad & Transaction & { partner?: boolean } isShowNFTDetail: ModalStatePlayLoad & Partial isShowNFTTransfer: ModalStatePlayLoad & Partial> & Contact isShowNFTWithdraw: ModalStatePlayLoad & Partial> & Contact isShowNFTDeploy: ModalStatePlayLoad & Partial> isShowNFTDeposit: ModalStatePlayLoad & Partial> isShowNFTMintAdvance: ModalStatePlayLoad & Partial> isShowCollectionAdvance: ModalStatePlayLoad isShowDual: ModalStatePlayLoad & { dualInfo: DualViewInfo | undefined } isShowResetAccount: ModalStatePlayLoad isShowActiveAccount: ModalStatePlayLoad isShowExportAccount: ModalStatePlayLoad isShowLayerSwapNotice: ModalStatePlayLoad isShowAnotherNetwork: ModalStatePlayLoad isShowSwap: ModalStatePlayLoad isShowAmm: ModalStatePlayLoad & Transaction & { type?: AmmPanelType } isShowTradeIsFrozen: ModalStatePlayLoad & { type?: string messageKey?: string } isShowConnect: ModalStatePlayLoad & { step: number error?: sdk.RESULT_INFO info?: { [key: string]: any } } isShowEditContact: ModalStatePlayLoad & { error?: sdk.RESULT_INFO info?: { [key: string]: any } } isShowAccount: ModalStatePlayLoad & { step: number error?: sdk.RESULT_INFO // info?: { [key: string]: any }; } isShowRedPacket: ModalStatePlayLoad & { step: number // info?: { [key: string]: any }; } isShowFeeSetting: ModalStatePlayLoad isShowIFrame: ModalStatePlayLoad & { url: string } isShowSideStakingRedeem: ModalStatePlayLoad & { symbol?: string } isShowGlobalToast: { isShow: boolean info: { id?: string messageKey?: string content?: string type: ToastType } & any } isShowTargetRedpacketPop: { isShow: boolean info: { exclusiveRedPackets?: (sdk.LuckyTokenItemForReceive & { tokenIcon: CoinSource tokenName: string })[] } } isShowETHStakingApr: ModalStatePlayLoad & { symbol?: string } isShowVaultExit: ModalStatePlayLoad & Transaction isShowVaultJoin: ModalStatePlayLoad & Transaction isShowVaultSwap: ModalStatePlayLoad & Transaction & {isSell?: boolean} isShowVaultLoan: ModalStatePlayLoad & Transaction & { type: string } isShowVaultCloseConfirm: ModalStatePlayLoad & Transaction isShowNoVaultAccount: ModalStatePlayLoad & { whichBtn: VaultAction | undefined; des?: string } isShowConfirmedVault: ModalStatePlayLoad isShowTransferToTaikoAccount: ModalStatePlayLoad & { from?: string } isShowBridge: ModalStatePlayLoad & Partial isShowClosureAnnouncement: ModalStatePlayLoad } ================================================ FILE: packages/component-lib/src/stores/reducer/modals/reducer.ts ================================================ import { createSlice, PayloadAction, Slice } from '@reduxjs/toolkit' import { ModalState, ModalStatePlayLoad, Transaction } from './interface' import { CLAIM_TYPE, ClaimToken, DualViewInfo, NFTWholeINFO, TradeNFT, AmmPanelType, VaultLoanType, VaultAction, CoinSource, Contact, ToastType, } from '@loopring-web/common-resources' import { RESULT_INFO, LuckyTokenItemForReceive } from '@loopring-web/loopring-sdk' const initialState: ModalState = { isShowGlobalToast: { isShow: false, info: { content: '', type: ToastType.info, }, }, isShowNFTMetaNotReady: { isShow: false }, isShowRedPacket: { isShow: false, step: 0 }, isShowSupport: { isShow: false }, isShowOtherExchange: { isShow: false }, isWrongNetworkGuide: { isShow: false }, isShowTransfer: { isShow: false, symbol: undefined }, isShowWithdraw: { isShow: false, symbol: undefined }, isShowDeposit: { isShow: false, symbol: undefined }, isShowResetAccount: { isShow: false }, isShowActiveAccount: { isShow: false }, isShowExportAccount: { isShow: false }, isShowSwap: { isShow: false }, isShowAmm: { isShow: false, type: AmmPanelType.Join }, isShowConnect: { isShow: false, step: 0 }, isShowAccount: { isShow: false, step: 0 }, isShowLayerSwapNotice: { isShow: false }, isShowAnotherNetwork: { isShow: false }, isShowFeeSetting: { isShow: false }, isShowTradeIsFrozen: { isShow: false, type: '' }, isShowIFrame: { isShow: false, url: '' }, isShowNFTTransfer: { isShow: false }, isShowNFTWithdraw: { isShow: false }, isShowNFTDeposit: { isShow: false }, isShowNFTMintAdvance: { isShow: false }, isShowDual: { isShow: false, dualInfo: undefined }, isShowCollectionAdvance: { isShow: false }, isShowNFTDeploy: { isShow: false }, isShowNFTDetail: { isShow: false }, isShowEditContact: { isShow: false }, isShowClaimWithdraw: { isShow: false, claimToken: undefined, claimType: undefined, }, isShowSideStakingRedeem: { isShow: false, symbol: undefined }, isShowTargetRedpacketPop: { isShow: false, info: {} }, isShowETHStakingApr: { isShow: false, symbol: undefined }, isShowVaultExit: { isShow: false }, isShowVaultJoin: { isShow: false }, isShowVaultSwap: { isShow: false, isSell: undefined }, isShowVaultLoan: { isShow: false, type: VaultLoanType.Borrow, symbol: undefined }, isShowVaultCloseConfirm: { isShow: false, symbol: undefined }, isShowNoVaultAccount: { isShow: false, whichBtn: undefined }, isShowConfirmedVault: { isShow: false }, isShowTransferToTaikoAccount: { isShow: false, from: undefined }, isShowBridge: { isShow: false, symbol: undefined, info: undefined }, isShowClosureAnnouncement: { isShow: false }, } export const modalsSlice: Slice = createSlice({ name: 'modals', initialState, reducers: { setShowGlobalToast( state, action: PayloadAction<{ isShow: boolean info: { content: string messageKey: string type: ToastType } }>, ) { const { isShow, info } = action.payload state.isShowGlobalToast = { isShow, info, } }, setNFTMetaNotReady( state, action: PayloadAction<{ isShow: boolean step?: number info?: { [key: string]: any } }>, ) { const { isShow, info } = action.payload state.isShowNFTMetaNotReady = { isShow, info, } }, setShowRedPacket( state, action: PayloadAction<{ isShow: boolean step?: number info?: { [key: string]: any } }>, ) { const { isShow, step, info } = action.payload state.isShowRedPacket = { isShow, step: step ? step : 0, info, } }, setShowIFrame(state, action: PayloadAction<{ isShow: boolean; url: string }>) { const { isShow, url } = action.payload state.isShowIFrame = { isShow, url, } }, setShowSupport(state, action: PayloadAction) { const { isShow } = action.payload state.isShowSupport.isShow = isShow }, setShowOtherExchange( state, action: PayloadAction< ModalStatePlayLoad & { agree?: boolean } >, ) { const { isShow, agree, ...rest } = action.payload state.isShowOtherExchange = { isShow, agree: isShow ? false : agree, ...rest, } }, setShowWrongNetworkGuide(state, action: PayloadAction) { const { isShow } = action.payload state.isWrongNetworkGuide.isShow = isShow }, setShowAmm( state, action: PayloadAction, ) { const { isShow, symbol, info, type } = action.payload state.isShowAmm = { isShow, symbol, info, type, } }, setShowSwap(state, action: PayloadAction) { const { isShow } = action.payload state.isShowSwap.isShow = isShow }, setShowNFTDetail(state, action: PayloadAction>) { const { isShow, ...rest } = action.payload state.isShowNFTDetail = { isShow, ...rest, } }, setShowNFTTransfer(state, action: PayloadAction) { const { isShow, nftData, nftType, total, locked, info, ...rest } = action.payload state.isShowNFTTransfer = { isShow, nftData, nftType, info, ...rest, balance: total ? Number(total) - Number(locked ?? 0) : 0, } }, setShowNFTDeploy(state, action: PayloadAction) { const { isShow, nftData, nftType, total, locked, info, ...rest } = action.payload state.isShowNFTDeploy = { isShow, nftData, nftType, info, ...rest, balance: total ? Number(total) - Number(locked ?? 0) : 0, } }, setShowNFTDeposit(state, action: PayloadAction>) { const { isShow, nftData, nftType, ...rest } = action.payload state.isShowNFTDeposit = { isShow, nftType, ...rest, } }, setShowCollectionAdvance(state, action: PayloadAction) { const { isShow, info } = action.payload state.isShowCollectionAdvance = { isShow, info, } }, setShowNFTMintAdvance(state, action: PayloadAction>) { const { isShow, nftData, nftType, ...rest } = action.payload state.isShowNFTMintAdvance = { isShow, nftData, nftType, ...rest, } }, setShowNFTWithdraw(state, action: PayloadAction) { const { isShow, nftData, nftType, total, locked, info, ...rest } = action.payload state.isShowNFTWithdraw = { isShow, nftData, nftType, info, ...rest, balance: total ? Number(total) - Number(locked ?? 0) : 0, } }, setShowTransfer(state, action: PayloadAction) { const { isShow, symbol, info, name, address, addressType } = action.payload state.isShowTransfer = { isShow, symbol, info, name, address, addressType, } }, setShowTransferToTaikoAccount(state, action: PayloadAction) { const { isShow, info, from } = action.payload state.isShowTransferToTaikoAccount = { isShow, info, from } }, setShowWithdraw(state, action: PayloadAction) { const { isShow, symbol, info, name, address, addressType } = action.payload state.isShowWithdraw = { isShow, symbol, info, name, address, addressType, } }, setShowDeposit( state, action: PayloadAction, ) { const { isShow, symbol, partner } = action.payload state.isShowDeposit = { isShow, symbol, partner, } }, setShowResetAccount(state, action: PayloadAction) { const { isShow, info } = action.payload state.isShowResetAccount.isShow = isShow state.isShowResetAccount.info = info }, setShowActiveAccount(state, action: PayloadAction) { const { isShow, info } = action.payload state.isShowActiveAccount.isShow = isShow state.isShowActiveAccount.info = info }, setShowExportAccount(state, action: PayloadAction) { const { isShow } = action.payload state.isShowExportAccount.isShow = isShow }, setShowDual( state, action: PayloadAction, ) { const { isShow, dualInfo } = action.payload if (dualInfo) { state.isShowDual = { isShow, dualInfo, } } else { state.isShowDual.isShow = false state.isShowDual.dualInfo = undefined } }, setShowConnect( state, action: PayloadAction<{ isShow: boolean step?: number error?: RESULT_INFO info?: { [key: string]: any } }>, ) { const { isShow, step, error, info } = action.payload state.isShowConnect = { isShow, step: step ? step : 0, error: error ?? undefined, info: info ?? undefined, } }, setShowAccount( state, action: PayloadAction<{ isShow: boolean step?: number error?: RESULT_INFO info?: { [key: string]: any } }>, ) { const { isShow, step, error, info } = action.payload state.isShowAccount = { isShow, step: step ? step : 0, error: error ?? undefined, info: info ?? undefined, } }, setShowFeeSetting(state, action: PayloadAction<{ isShow: boolean }>) { const { isShow } = action.payload state.isShowFeeSetting = { isShow, } }, setShowLayerSwapNotice(state, action: PayloadAction<{ isShow: boolean }>) { const { isShow } = action.payload state.isShowLayerSwapNotice = { isShow, } }, setShowAnotherNetworkNotice(state, action: PayloadAction<{ isShow: boolean; info: any }>) { const { isShow, info } = action.payload state.isShowAnotherNetwork = { isShow, info, } }, setShowTradeIsFrozen( state, action: PayloadAction<{ isShow: boolean type: string messageKey?: string }>, ) { const { isShow, type, messageKey } = action.payload state.isShowTradeIsFrozen = { isShow, type, messageKey, } }, setShowClaimWithdraw( state, action: PayloadAction< ModalStatePlayLoad & { claimToken: ClaimToken claimType: CLAIM_TYPE } >, ) { const { isShow, claimToken, claimType } = action.payload state.isShowClaimWithdraw = { isShow, claimToken, claimType, } }, setShowSideStakingRedeem( state, action: PayloadAction, ) { const { isShow, symbol } = action.payload state.isShowSideStakingRedeem = { isShow, symbol, } }, setShowEditContact( state, action: PayloadAction< ModalStatePlayLoad & { info: any } >, ) { const { isShow, info } = action.payload state.isShowEditContact = { isShow, info, } }, setShowTargetRedpacketPop( state, action: PayloadAction< ModalStatePlayLoad & { info: { exclusiveRedPackets?: (LuckyTokenItemForReceive & { tokenIcon: CoinSource tokenName: string })[] } } >, ) { const { isShow, info: { exclusiveRedPackets }, } = action.payload state.isShowTargetRedpacketPop = { isShow, info: { exclusiveRedPackets, }, } }, setShowETHStakingApr(state, action: PayloadAction) { const { isShow, symbol, info } = action.payload state.isShowETHStakingApr = { isShow, symbol, info, } }, setShowVaultExit(state, action: PayloadAction) { state.isShowVaultExit = { ...action.payload } }, setShowVaultJoin(state, action: PayloadAction) { state.isShowVaultJoin = { ...action.payload } }, setShowVaultSwap(state, action: PayloadAction) { state.isShowVaultSwap = { ...action.payload } }, setShowVaultLoan( state, action: PayloadAction, ) { state.isShowVaultLoan = { ...action.payload } }, setShowVaultCloseConfirm( state, action: PayloadAction, ) { state.isShowVaultCloseConfirm = { ...action.payload } }, setShowNoVaultAccount( state, action: PayloadAction, ) { state.isShowNoVaultAccount = { ...action.payload } }, setShowConfirmedVault(state, action: PayloadAction) { state.isShowConfirmedVault = action.payload }, setShowBridge(state, action: PayloadAction>) { state.isShowBridge = action.payload }, setShowClosureAnnouncement(state, action: PayloadAction) { state.isShowClosureAnnouncement = action.payload }, }, }) export const { setShowNFTDeploy, setShowNFTDetail, setShowNFTTransfer, setShowNFTDeposit, setShowNFTWithdraw, setShowNFTMintAdvance, setShowCollectionAdvance, setShowTransfer, setShowWithdraw, setShowDeposit, setShowResetAccount, setShowExportAccount, setShowDual, setShowSwap, setShowAmm, setShowConnect, setShowAccount, setShowFeeSetting, setShowActiveAccount, setShowIFrame, setShowTradeIsFrozen, setShowSupport, setShowWrongNetworkGuide, setShowOtherExchange, setShowLayerSwapNotice, setShowClaimWithdraw, setShowRedPacket, setNFTMetaNotReady, setShowSideStakingRedeem, setShowAnotherNetworkNotice, setShowGlobalToast, setShowTargetRedpacketPop, setShowEditContact, setShowETHStakingApr, setShowVaultExit, setShowVaultJoin, setShowVaultSwap, setShowVaultLoan, setShowVaultCloseConfirm, setShowNoVaultAccount, setShowConfirmedVault, setShowBridge, setShowTransferToTaikoAccount, setShowClosureAnnouncement } = modalsSlice.actions ================================================ FILE: packages/component-lib/src/stores/reducer/settings/hook.ts ================================================ import { useDispatch, useSelector } from 'react-redux' import { setCoinJson, setCurrency, setDefaultNetwork, setFeeChargeOrder, setHideL2Action, setHideL2Assets, setHideLpToken, setHideSmallBalances, setIsDevToggle, setIsMobile, setDualDefault, setLanguage, setLayouts, setPlatform, setReferralCode, setSlippage, setStopLimitLayouts, setSwapSecondConfirmation, setTheme, setUpColor, setBTradeShowTutorial } from './reducer' import { PlatFormType, SettingsState } from './interface' import { CurrencyToTag, LanguageKeys, LanguageType, ThemeKeys, ThemeType, UpColor, } from '@loopring-web/common-resources' import React from 'react' import { Layouts } from 'react-grid-layout' export function useSettings(): SettingsState & { setPlatform(value: keyof typeof PlatFormType): void setTheme(value: ThemeKeys): void setDefaultNetwork(value: 1 | 5 | number): void setUpColor(value: keyof typeof UpColor): void setCurrency(value: CurrencyToTag): void setLanguage(value: LanguageKeys): void setSlippage(value: 'N' | number): void setCoinJson(value: any): void setHideL2Assets(value: boolean): void setHideL2Action(value: boolean): void setHideLpToken(value: boolean): void setHideSmallBalances(value: boolean): void setLayouts(value: Layouts): void setStopLimitLayouts(value: Layouts): void setFeeChargeOrder(value: string[]): void setIsMobile(value: boolean): void setSwapSecondConfirmation(value: boolean): void setIsDevToggle(value: boolean): void setReferralCode(value: string): void setDualDefault(vaule: { auto: boolean; day: number | 'auto' }): void setBTradeShowTutorial(value: boolean): void } { const settings: SettingsState = useSelector((state: any) => state.settings) const dispatch = useDispatch() React.useEffect(() => { if (['usd', 'cny'].includes(settings.currency)) { dispatch(setCurrency(settings?.currency?.toUpperCase())) } }, []) return { ...settings, setReferralCode: React.useCallback( (value: string) => dispatch(setReferralCode(value)), [dispatch], ), setDefaultNetwork: React.useCallback( (value: number) => dispatch(setDefaultNetwork(value)), [dispatch], ), setTheme: React.useCallback( (value: keyof typeof ThemeType) => dispatch(setTheme(value)), [dispatch], ), setLanguage: React.useCallback( (value: keyof typeof LanguageType) => dispatch(setLanguage(value)), [dispatch], ), setPlatform: React.useCallback( (value: keyof typeof PlatFormType) => dispatch(setPlatform(value)), [dispatch], ), setCurrency: React.useCallback( (value: CurrencyToTag) => dispatch(setCurrency(value)), [dispatch], ), setUpColor: React.useCallback( (value: keyof typeof UpColor) => dispatch(setUpColor(value)), [dispatch], ), setSlippage: React.useCallback( (value: 'N' | number) => dispatch(setSlippage(value)), [dispatch], ), setCoinJson: React.useCallback((value: any) => dispatch(setCoinJson(value)), [dispatch]), setHideL2Assets: React.useCallback( (value: boolean) => dispatch(setHideL2Assets(value)), [dispatch], ), setHideL2Action: React.useCallback( (value: boolean) => dispatch(setHideL2Action(value)), [dispatch], ), setHideLpToken: React.useCallback( (value: boolean) => dispatch(setHideLpToken(value)), [dispatch], ), setHideSmallBalances: React.useCallback( (value: boolean) => dispatch(setHideSmallBalances(value)), [dispatch], ), setLayouts: React.useCallback((value: Layouts) => dispatch(setLayouts(value)), [dispatch]), setStopLimitLayouts: React.useCallback( (value: Layouts) => dispatch(setStopLimitLayouts(value)), [dispatch], ), setFeeChargeOrder: React.useCallback( (value: string[]) => dispatch(setFeeChargeOrder(value)), [dispatch], ), setIsDevToggle: React.useCallback( (value: boolean) => dispatch(setIsDevToggle(value)), [dispatch], ), setIsMobile: React.useCallback((value: boolean) => dispatch(setIsMobile(value)), [dispatch]), setSwapSecondConfirmation: React.useCallback( (value: boolean) => dispatch(setSwapSecondConfirmation(value)), [dispatch], ), setDualDefault: React.useCallback( (value: { auto: boolean; day: number | 'auto' }) => dispatch(setDualDefault(value)), [dispatch], ), setBTradeShowTutorial: React.useCallback( (value: boolean) => dispatch(setBTradeShowTutorial(value)), [dispatch], ), } } ================================================ FILE: packages/component-lib/src/stores/reducer/settings/index.ts ================================================ export * from './hook' export * from './interface' export * from './reducer' ================================================ FILE: packages/component-lib/src/stores/reducer/settings/interface.ts ================================================ import { CoinSource, CurrencyToTag, LanguageKeys, ThemeKeys, UpColor, } from '@loopring-web/common-resources' import { Layouts } from 'react-grid-layout' import * as sdk from '@loopring-web/loopring-sdk' export enum PlatFormType { mobile = 'mobile', desktop = 'desktop', tablet = 'tablet', } export type PlatFormKeys = keyof typeof PlatFormType export interface SettingsState { themeMode: ThemeKeys language: LanguageKeys platform: PlatFormKeys currency: CurrencyToTag upColor: keyof typeof UpColor slippage: number | 'N' coinJson: { [key: string]: CoinSource } hideL2Assets: boolean hideL2Action: boolean hideInvestToken: boolean isMobile: boolean hideSmallBalances: boolean proLayout: Layouts stopLimitLayout: Layouts feeChargeOrder: string[] swapSecondConfirmation: boolean | undefined defaultNetwork: sdk.ChainId referralCode: string isDevToggle: boolean dualAuto: { auto: boolean; day: number | 'auto' } bTradeShowTutorial: boolean } ================================================ FILE: packages/component-lib/src/stores/reducer/settings/reducer.ts ================================================ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { PlatFormType, SettingsState } from './interface' import { i18n, LanguageKeys, layoutConfigs, stopLimitLayoutConfigs, ThemeKeys, ThemeType, UpColor, CurrencyToTag, NetworkMap, FeeChargeOrderDefaultMap, } from '@loopring-web/common-resources' import moment from 'moment' import { Slice } from '@reduxjs/toolkit/src/createSlice' import { Layouts } from 'react-grid-layout' import * as sdk from '@loopring-web/loopring-sdk' const initialState: SettingsState = { themeMode: ThemeType.dark, //localStore.getItem('ThemeType')?localStore.getItem('ThemeType') as ThemeKeys :ThemeType.dark, language: i18n.language as LanguageKeys, //localStore.getItem('LanguageKey')?localStore.getItem('LanguageKey') as LanguageKeys: i18n.language as LanguageKeys, platform: PlatFormType.desktop, currency: CurrencyToTag.USD, //localStore.getItem('Currency')?localStore.getItem('Currency') as keyof typeof Currency: Currency.usd, upColor: UpColor.green, //localStore.getItem('UpColor')?localStore.getItem('UpColor') as keyof typeof UpColor: UpColor.green, coinJson: {}, slippage: 'N', feeChargeOrder: FeeChargeOrderDefaultMap.get(sdk.ChainId.MAINNET)!, hideL2Assets: false, hideL2Action: true, hideInvestToken: false, hideSmallBalances: true, isMobile: false, proLayout: layoutConfigs[0].layouts, stopLimitLayout: stopLimitLayoutConfigs[0].layouts, swapSecondConfirmation: true, defaultNetwork: NetworkMap['ETHEREUM']?.chainId ?? 1, referralCode: '', isDevToggle: false, dualAuto: { auto: true, day: 'auto' }, bTradeShowTutorial: true } export const settingsSlice: Slice = createSlice({ name: 'settings', initialState, reducers: { setReferralCode(state, action: PayloadAction) { const referralCode = action.payload const regex = /^[0-9\b]+$/ if (regex.test(referralCode) && referralCode.length < 8) { state.referralCode = action.payload } else { state.referralCode = '' } }, setDefaultNetwork(state, action: PayloadAction) { state.defaultNetwork = action.payload }, setIsDevToggle(state, action: PayloadAction) { state.isDevToggle = action.payload }, setTheme(state, action: PayloadAction) { // localStore.setItem('ThemeType',action.payload) state.themeMode = action.payload }, setLanguage(state, action: PayloadAction) { i18n.changeLanguage(action.payload) if (action.payload) { // action.payload === 'en_US' ? moment.locale('en') : moment.locale(action.payload.toLocaleLowerCase()); action.payload === 'en_US' ? moment.updateLocale('en', { relativeTime: { future: (diff) => (diff == 'just now' ? diff : `in ${diff}`), past: (diff) => (diff == 'just now' ? diff : `${diff} ago`), s: 'just now', ss: 'just now', }, }) : moment.updateLocale('zh-cn', { relativeTime: { future: '%s后', past: '%s前', s: '几秒', ss: '%d 秒', m: '1 分钟', mm: '%d 分钟', h: '1 小时', hh: '%d 小时', d: '1 天', dd: '%d 天', w: '1 周', ww: '%d 周', M: '1 个月', MM: '%d 个月', y: '1 年', yy: '%d 年', }, }) state.language = action.payload } }, setIsMobile(state, action: PayloadAction) { // localStore.setItem('UpColor',action.payload) state.isMobile = action.payload }, setPlatform(state, action: PayloadAction) { state.platform = action.payload }, setCurrency(state, action: PayloadAction) { if (['usd', 'cyn'].includes(action.payload)) { // @ts-ignore state.currency = action?.payload?.toUpperCase() } else { state.currency = action.payload } }, setUpColor(state, action: PayloadAction) { // localStore.setItem('UpColor',action.payload) state.upColor = action.payload }, setSlippage(state, action: PayloadAction<'N' | number>) { // localStore.setItem('UpColor',action.payload) state.slippage = action.payload }, setCoinJson(state, action: PayloadAction) { // localStore.setItem('UpColor',action.payload) state.coinJson = action.payload }, setHideL2Assets(state, action: PayloadAction) { state.hideL2Assets = action.payload }, setHideL2Action(state, action: PayloadAction) { state.hideL2Action = action.payload }, setHideLpToken(state, action: PayloadAction) { state.hideInvestToken = action.payload }, setHideSmallBalances(state, action: PayloadAction) { state.hideSmallBalances = action.payload }, setFeeChargeOrder(state, action: PayloadAction) { state.feeChargeOrder = action.payload }, setLayouts(state, action: PayloadAction) { // localStore.setItem('UpColor',action.payload) const result: Layouts = { ...state.proLayout, ...action.payload, } state.proLayout = result }, setStopLimitLayouts(state, action: PayloadAction) { // localStore.setItem('UpColor',action.payload) const result: Layouts = { ...state.stopLimitLayout, ...action.payload, } state.stopLimitLayout = result }, setSwapSecondConfirmation(state, action: PayloadAction) { state.swapSecondConfirmation = action.payload }, setDualDefault(state, action: PayloadAction<{ auto: boolean; day: number | 'auto' }>) { state.dualAuto = action.payload }, setBTradeShowTutorial(state, action: PayloadAction) { state.bTradeShowTutorial = action.payload }, }, }) export const { setLayouts, setStopLimitLayouts, setTheme, setLanguage, setPlatform, setCurrency, setUpColor, setSlippage, setCoinJson, setFeeChargeOrder, setHideL2Assets, setHideLpToken, setHideL2Action, setHideSmallBalances, setIsMobile, setSwapSecondConfirmation, setDefaultNetwork, setReferralCode, setIsDevToggle, setDualDefault, setBTradeShowTutorial } = settingsSlice.actions // export const { setTheme,setPlatform,setLanguage } = settingsSlice.actions ================================================ FILE: packages/component-lib/src/stores/reducer/toggle/hook.ts ================================================ import { useDispatch, useSelector } from 'react-redux' import { ToggleState } from './interface' import { updateToggleStatus } from './reducer' import React from 'react' export function useToggle() { const toggle: ToggleState = useSelector((state: any) => state.toggle) const dispatch = useDispatch() return { toggle, updateToggleStatus: React.useCallback(() => { dispatch(updateToggleStatus(undefined)) }, [dispatch]), } } ================================================ FILE: packages/component-lib/src/stores/reducer/toggle/index.ts ================================================ export * from './hook' export * from './reducer' export * from './interface' ================================================ FILE: packages/component-lib/src/stores/reducer/toggle/interface.ts ================================================ import { Account } from '@loopring-web/common-resources' export type ToggleState = { order: { enable: boolean; reason?: string } joinAmm: { enable: boolean; reason?: string } exitAmm: { enable: boolean; reason?: string } transfer: { enable: boolean; reason?: string } transferNFT: { enable: boolean; reason?: string } deposit: { enable: boolean; reason?: string } depositNFT: { enable: boolean; reason?: string } withdraw: { enable: boolean; reason?: string } withdrawNFT: { enable: boolean; reason?: string } mintNFT: { enable: boolean; reason?: string } deployNFT: { enable: boolean; reason?: string } updateAccount: { enable: boolean; reason?: string } defiInvest: { enable: boolean; reason?: string } WSTETHInvest: { enable: boolean; reason?: string } RETHInvest: { enable: boolean; reason?: string } dualInvest: { enable: boolean; reason?: string } leverageETHInvest: { enable: boolean; reason?: string } collectionNFT: { enable: boolean; reason?: string } claim: { enable: boolean; reason?: string } redPacketNFTV1: { enable: boolean; reason?: string } LRCStackInvest: { enable: boolean; reason?: string } BTradeInvest: { enable: boolean; reason?: string } StopLimit: { enable: boolean; reason?: string } VaultInvest: { enable: boolean reason?: string } VaultDustCollector: { enable: boolean; reason?: string } // @ts-ignore send: { orbiter: string[] } // @ts-ignore receive: { layerSwap: string[] orbiter: string[] } CIETHInvest: { enable: boolean; reason?: string } redpacket_exclusive: { enable: boolean; reason?: string } [key: string]: { enable?: boolean; reason?: string; [key: string]: any } dual_reinvest: { enable: boolean; reason?: string } taikoFarming: { enable: boolean; reason?: string } whiteList: any // @ts-ignore isSupperUser: any rabbitWithdraw: { enable: boolean; reason?: string } transferToTaikoAccount: { enable: boolean; reason?: string } } export type TogglePlayLoad = Partial & { account?: Account chainId: any } ================================================ FILE: packages/component-lib/src/stores/reducer/toggle/reducer.ts ================================================ import { createSlice, PayloadAction, Slice } from '@reduxjs/toolkit' import { SliceCaseReducers } from '@reduxjs/toolkit/src/createSlice' import { TogglePlayLoad, ToggleState } from './interface' import { MapChainId } from '@loopring-web/common-resources' const initialState: ToggleState = { order: { enable: true }, joinAmm: { enable: true }, exitAmm: { enable: true }, transfer: { enable: true }, transferNFT: { enable: true }, deposit: { enable: true }, depositNFT: { enable: true }, withdraw: { enable: true }, withdrawNFT: { enable: true }, mintNFT: { enable: true }, deployNFT: { enable: true }, updateAccount: { enable: true }, collectionNFT: { enable: true }, WSTETHInvest: { enable: true }, RETHInvest: { enable: true }, leverageETHInvest: { enable: true }, defiInvest: { enable: true }, dualInvest: { enable: true }, claim: { enable: true }, redPacketNFTV1: { enable: true }, LRCStackInvest: { enable: true }, BTradeInvest: { enable: true }, StopLimit: { enable: true }, send: { orbiter: ['ETH'], }, receive: { layerSwap: ['ETH', 'LRC', 'USDC'], orbiter: ['ETH'], }, CIETHInvest: { enable: true }, redpacket_exclusive: { enable: true }, dual_reinvest: { enable: true }, VaultInvest: { enable: true }, VaultDustCollector: { enable: true }, taikoFarming: { enable: false }, rabbitWithdraw: { enable: true }, transferToTaikoAccount: {enable: true}, whiteList: {}, isSupperUser: false as any, } export const toggleSlice: Slice = createSlice< ToggleState, SliceCaseReducers >({ name: 'toggle', initialState: initialState, reducers: { updateToggleStatus(state, action: PayloadAction) { const { account, chainId, ...rest } = action.payload // let toggle = {} Reflect.ownKeys(state).forEach((key) => { if (rest.hasOwnProperty(key) && rest[key.toString()] !== undefined) { state[key.toString()] = rest[key.toString()] as any // toggle[key.toString()] = rest[key.toString()] as any } }) const network = MapChainId[chainId] ?? MapChainId[1] let isSupperUser = false, list: any = [] if ( account && account.accAddress && state?.whiteList && state?.whiteList[network?.toUpperCase()] && state?.whiteList[network?.toUpperCase()] ) { // const toggle = _.cloneDeep(_toggle) const newToggle = { ...state, } state?.whiteList[network?.toUpperCase()].forEach((item: string) => { // @ts-ignore isSupperUser = item?.superUserAddress?.find( (addr: string) => addr?.toLowerCase() == account?.accAddress?.toLowerCase(), ) if (isSupperUser) { // @ts-ignore item?.superUserFunction?.forEach((fn: string) => { const key: string | undefined = Reflect.ownKeys(newToggle).find( // @ts-ignore (_toggle: string) => _toggle?.toUpperCase() == fn?.toUpperCase(), ) if (key && newToggle[key].enable == false && state[key].reason === 'no view') { state[key] = { ...newToggle[key], enable: true, reason: 'whiteList' } list.push(key) } }) } }) } // updateToggleStatus(state, action: PayloadAction>) { // const rest = action.payload // Reflect.ownKeys(state).forEach((key) => { // if (rest.hasOwnProperty(key) && rest[key.toString()] !== undefined) { // state[key.toString()] = rest[key.toString()] as any // } // }) // }, }, }, }) export const { updateToggleStatus } = toggleSlice.actions ================================================ FILE: packages/component-lib/src/types/lib.ts ================================================ export type Without = { [P in Exclude]?: never } export type XOR = T | U extends { [key: string]: any } ? (Without & U) | (Without & T) : T | U // export type Omit = Pick> ================================================ FILE: packages/component-lib/src/utils/closureAnnouncementUtils.ts ================================================ const CLOSURE_ANNOUNCEMENT_KEY = 'loopring_closure_announcement_dismissed' export const isClosureAnnouncementDismissed = (): boolean => { try { return localStorage.getItem(CLOSURE_ANNOUNCEMENT_KEY) === 'true' } catch (error) { return false } } export const setClosureAnnouncementDismissed = (): void => { try { localStorage.setItem(CLOSURE_ANNOUNCEMENT_KEY, 'true') } catch (error) { console.warn('Failed to save closure announcement state:', error) } } ================================================ FILE: packages/component-lib/tsconfig.json ================================================ { "extends": "../../tsconfig.build.json", "compilerOptions": { "jsx": "react-jsx", "noUnusedLocals": true, "noUnusedParameters": true, "resolveJsonModule": true, "allowSyntheticDefaultImports": true, "baseUrl": "./src", "rootDir": "./src" }, "include": [ "src", "src/**/*.json", "src/*" ], "exclude": [ "node_modules", "build", "dist", "example", "rollup.config.js" ] } ================================================ FILE: packages/component-lib/tsconfig.test.json ================================================ { "extends": "./tsconfig.json", "compilerOptions": { "module": "commonjs" }, "include": [ "src", "src/**/*.json", "src/*" ] } ================================================ FILE: packages/core/.babelrc ================================================ { "presets": [ "@babel/typescript", "@babel/preset-env", "@babel/preset-react", "@emotion/babel-preset-css-prop", [ "@babel/env", { "modules": false } ] ], "plugins": [ "@babel/plugin-proposal-class-properties" ] } ================================================ FILE: packages/core/.eslintignore ================================================ *.css *.scss *.svg ================================================ FILE: packages/core/.eslintrc.json ================================================ { "plugins": [ "react-hooks" ], "extends": [ "react-app" ], "overrides": [ { "files": [ "**/*.tsx", "**/*.ts" ], "rules": { "import/no-anonymous-default-export": "off", "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/ban-types": "off", "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" } } ] } ================================================ FILE: packages/core/.gitignore ================================================ # See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies node_modules # builds build dist .rpt2_cache # misc .DS_Store .env .env.local .env.development.local .env.test.local .env.production.local .idea/** .vscode/** ./lib/** npm-debug.log* yarn-debug.log* yarn-error.log* src/assets storybook-static ================================================ FILE: packages/core/.travis.yml ================================================ language: node_js node_js: - 8 env: - SKIP_PREFLIGHT_CHECK=true ================================================ FILE: packages/core/README.md ================================================ # component-lib > Loopring UI component lib [![NPM](https://img.shields.io/npm/v/component-lib.svg)](https://www.npmjs.com/package/component-lib) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) ## Install ```bash npm install --save component-lib ``` ## Usage ```tsx import * as React from 'react' < ProviderComposer providers = { [ provider(LocalizationProvider, {dateAdapter: MomentUtils}), provider(I18nextProvider, {i18n}), provider(ThemeProvider), ] }> < Story {... context } /> < ProviderComposer providers = { [ provider(LocalizationProvider, {dateAdapter: MomentUtils}), provider(I18nextProvider, {i18n}), provider(ThemeProvider), ] }> < Story {... context } /> "@loopring-web/static-resource": "file:../static-resource/src", ``` ## License MIT © [Loopring dev Team](https://github.com/Loopring dev Team) --- This hook is created using [create-react-hook](https://github.com/hermanya/create-react-hook). ================================================ FILE: packages/core/package.json ================================================ { "name": "@loopring-web/core", "version": "1.0.0", "main": "src/index.ts", "private": true, "resolutions": { "**/@emotion/styled": "^11.1.5" }, "dependencies": { "@loopring-web/common-resources": "1.0.0", "@loopring-web/component-lib": "1.0.0", "react-scripts": "5.0.1", "cross-env": "^7.0.3" }, "scripts": { "rollup:build": "rollup --config --no-stdin", "release:version": "lerna version --exact --no-changelog --no-push --no-git-tag-version", "release:build": "lerna run --parallel --scope \"@loopring-web/*\" build", "test": "react-scripts-rewired test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app" ], "overrides": [ { "files": [ "**/*.stories.*" ], "rules": { "import/no-anonymous-default-export": "off" } }, { "files": [ "**/*.stories.*" ], "rules": { "import/no-anonymous-default-export": "off" } } ] }, "browserslist": { "production": [ "last 2 chrome version", "last 2 firefox version", "last 2 safari version" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "@emotion/react": "^11.1.5", "@emotion/server": "^11.0.0", "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-typescript": "^8.2.1", "@rollup/plugin-url": "^6.0.0", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@types/d3-format": "^2.0.0", "@types/d3-time-format": "^3.0.0", "@typescript-eslint/eslint-plugin": "^4.21.0", "arr-flatten": "^1.1.0", "babel-plugin-react-require": "^3.1.3", "repeat-element": "^1.1.3", "rollup": "2.30", "rollup-plugin-font": "^1.1.1", "rollup-plugin-json": "^4.0.0", "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-svg": "^2.0.0", "rollup-plugin-typescript2": "^0.30.0", "snapdragon-node": "^3.0.0", "storybook-addon-redux-listener": "^0.1.7", "storybook-react-router": "^1.0.8" } } ================================================ FILE: packages/core/rollup.config.js ================================================ import ts from 'rollup-plugin-typescript2' import typescript from 'typescript' import commonjs from '@rollup/plugin-commonjs' import resolve from '@rollup/plugin-node-resolve' import external from 'rollup-plugin-peer-deps-external' import svg from 'rollup-plugin-svg' import url from '@rollup/plugin-url' import json from 'rollup-plugin-json' // importfont from "rollup-plugin-font"; // import pkg from './package.json' export default { input: './src/DualListPanel.tsx', output: [ { file: './dist/bundle.cjs', //pkg.main, format: 'cjs', preferConst: true, interop: false, //exports: 'named', sourcemap: true, }, { file: './dist/bundle.js', //pkg.module, format: 'es', preferConst: true, //exports: 'named', sourcemap: true, }, ], plugins: [ external(), url({ exclude: ['**/*.svg'] }), url({ include: ['**/*.ttf', '**/*.eot', '**/*.woff', '**/*.woff2'], limit: Infinity, }), resolve(), ts({ typescript }), svg(), commonjs({ extensions: ['.js', '.ts'] }), json({ // 默认情况下将解析所有JSON文件, // 但您可以专门包含/排除文件 // exclude: [ 'node_modules/foo/**', 'node_modules/bar/**' ], // exclude: [ 'node_modules/foo/**', 'node_modules/bar/**' ], // 对于 tree-shaking, 属性将声明为 // 变量, 使用 `var` 或者 `const` preferConst: true, // 默认是 false // 为生成的默认导出指定缩进 — // 默认为 '\t' indent: ' ', // 忽略缩进并生成最小的代码 compact: true, // 默认是 false // 为JSON对象的每个属性生成一个命名导出 namedExports: true, // 默认是 true }), //font() ], } ================================================ FILE: packages/core/src/TimeoutCheckProvider.tsx ================================================ import { AccountStatus, myLog } from '@loopring-web/common-resources' import { debounce } from 'lodash' import React from 'react' import { accountServices } from './services/account/accountServices' import { UserStorage } from './storage' import { store } from './index' const WindowEvent = { Click: 'click', Scroll: 'scroll', Mouseover: 'mouseover', } const events = [WindowEvent.Click, WindowEvent.Mouseover, WindowEvent.Scroll] const forceReset = debounce(() => { // myLog('active!!!!! reset active time!!!! ') UserStorage.checkTimeout(true) }, 100) export const TimeoutCheckProvider = ({ children }: { children: React.ReactNode }) => { React.useEffect(() => { events.forEach((event: string) => { document.addEventListener(event, forceReset) }) const handler = setInterval(() => { if (UserStorage.checkTimeout()) { // timeout const account = store.getState().account if (account.readyState === AccountStatus.ACTIVATED) { myLog('[*****] got timeout! try to lock!!!!!') accountServices.sendAccountLock() } } }, 1000) return () => { events.forEach((event: string) => { document.removeEventListener(event, forceReset) }) if (handler) { clearInterval(handler) } } }, []) return <>{children} } ================================================ FILE: packages/core/src/abi/erc20ABI.ts ================================================ export default [ { constant: true, inputs: [], name: 'name', outputs: [ { name: '', type: 'string', }, ], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { name: '_spender', type: 'address', }, { name: '_value', type: 'uint256', }, ], name: 'approve', outputs: [ { name: '', type: 'bool', }, ], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'totalSupply', outputs: [ { name: '', type: 'uint256', }, ], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { name: '_from', type: 'address', }, { name: '_to', type: 'address', }, { name: '_value', type: 'uint256', }, ], name: 'transferFrom', outputs: [ { name: '', type: 'bool', }, ], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [], name: 'decimals', outputs: [ { name: '', type: 'uint8', }, ], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [ { name: '_owner', type: 'address', }, ], name: 'balanceOf', outputs: [ { name: 'balance', type: 'uint256', }, ], payable: false, stateMutability: 'view', type: 'function', }, { constant: true, inputs: [], name: 'symbol', outputs: [ { name: '', type: 'string', }, ], payable: false, stateMutability: 'view', type: 'function', }, { constant: false, inputs: [ { name: '_to', type: 'address', }, { name: '_value', type: 'uint256', }, ], name: 'transfer', outputs: [ { name: '', type: 'bool', }, ], payable: false, stateMutability: 'nonpayable', type: 'function', }, { constant: true, inputs: [ { name: '_owner', type: 'address', }, { name: '_spender', type: 'address', }, ], name: 'allowance', outputs: [ { name: '', type: 'uint256', }, ], payable: false, stateMutability: 'view', type: 'function', }, { payable: true, stateMutability: 'payable', type: 'fallback', }, { anonymous: false, inputs: [ { indexed: true, name: 'owner', type: 'address', }, { indexed: true, name: 'spender', type: 'address', }, { indexed: false, name: 'value', type: 'uint256', }, ], name: 'Approval', type: 'event', }, { anonymous: false, inputs: [ { indexed: true, name: 'from', type: 'address', }, { indexed: true, name: 'to', type: 'address', }, { indexed: false, name: 'value', type: 'uint256', }, ], name: 'Transfer', type: 'event', }, ] ================================================ FILE: packages/core/src/abi/index.ts ================================================ import erc20ABI from './erc20ABI' import taikoDepositABI from './taikoDepositABI' export { taikoDepositABI, erc20ABI } ================================================ FILE: packages/core/src/abi/taikoDepositABI.ts ================================================ export default [ { inputs: [{ internalType: 'address', name: '_exchange', type: 'address' }], stateMutability: 'nonpayable', type: 'constructor', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'address', name: 'from', type: 'address' }, { indexed: false, internalType: 'address', name: 'to', type: 'address' }, { indexed: false, internalType: 'address', name: 'token', type: 'address' }, { indexed: false, internalType: 'uint96', name: 'amount', type: 'uint96' }, { indexed: false, internalType: 'uint256', name: 'duration', type: 'uint256' }, ], name: 'Deposited', type: 'event', }, { inputs: [ { internalType: 'address', name: 'from', type: 'address' }, { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'address', name: 'tokenAddress', type: 'address' }, { internalType: 'uint96', name: 'amount', type: 'uint96' }, { internalType: 'uint256', name: 'duration', type: 'uint256' }, { internalType: 'bytes', name: 'extraData', type: 'bytes' }, ], name: 'deposit', outputs: [], stateMutability: 'payable', type: 'function', }, { inputs: [], name: 'exchange', outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', type: 'function', }, ] ================================================ FILE: packages/core/src/api_wrapper/index.ts ================================================ import { AmmpoolAPI, ChainId, DefiAPI, DelegateAPI, ExchangeAPI, GlobalAPI, LuckTokenAPI, NFTAPI, UserAPI, WalletAPI, WsAPI, ContactAPI, VaultAPI, RabbitWithdrawAPI } from '@loopring-web/loopring-sdk' export class LoopringAPI { public static userAPI: UserAPI | undefined = undefined public static exchangeAPI: ExchangeAPI | undefined = undefined public static ammpoolAPI: AmmpoolAPI | undefined = undefined public static walletAPI: WalletAPI | undefined = undefined public static wsAPI: WsAPI | undefined = undefined public static nftAPI: NFTAPI | undefined = undefined public static delegate: DelegateAPI | undefined = undefined public static globalAPI: GlobalAPI | undefined = undefined public static defiAPI: DefiAPI | undefined = undefined public static luckTokenAPI: LuckTokenAPI | undefined = undefined public static contactAPI: ContactAPI | undefined = undefined public static vaultAPI: VaultAPI | undefined = undefined public static rabbitWithdrawAPI: RabbitWithdrawAPI | undefined = undefined public static __chainId__: ChainId | undefined = undefined public static InitApi = (chainId: ChainId) => { LoopringAPI.userAPI = new UserAPI({ chainId }, 15000) LoopringAPI.luckTokenAPI = new LuckTokenAPI({ chainId }, 15000) LoopringAPI.exchangeAPI = new ExchangeAPI({ chainId }, 15000) LoopringAPI.globalAPI = new GlobalAPI({ chainId }, 25000) LoopringAPI.ammpoolAPI = new AmmpoolAPI({ chainId }, 15000) LoopringAPI.walletAPI = new WalletAPI({ chainId }, 15000) LoopringAPI.wsAPI = new WsAPI({ chainId }, 15000) LoopringAPI.nftAPI = new NFTAPI({ chainId }, 15000) LoopringAPI.delegate = new DelegateAPI({ chainId }, 15000) LoopringAPI.defiAPI = new DefiAPI({ chainId }, 12000) LoopringAPI.contactAPI = new ContactAPI({ chainId }, 15000) LoopringAPI.vaultAPI = new VaultAPI({ chainId }, 15000) LoopringAPI.rabbitWithdrawAPI = new RabbitWithdrawAPI({ chainId }, 15000) LoopringAPI.__chainId__ = chainId } public static setBaseURL = (baseURL: string, { walletAPIURL, rabbitWithdrawAPIURL }: { walletAPIURL?: string rabbitWithdrawAPIURL?: string }) => { LoopringAPI.userAPI?.setBaseUrl(baseURL) LoopringAPI.luckTokenAPI?.setBaseUrl(baseURL) LoopringAPI.exchangeAPI?.setBaseUrl(baseURL) LoopringAPI.globalAPI?.setBaseUrl(baseURL) LoopringAPI.ammpoolAPI?.setBaseUrl(baseURL) LoopringAPI.walletAPI?.setBaseUrl(walletAPIURL ? walletAPIURL : baseURL) LoopringAPI.wsAPI?.setBaseUrl(baseURL) LoopringAPI.nftAPI?.setBaseUrl(baseURL) LoopringAPI.delegate?.setBaseUrl(baseURL) LoopringAPI.defiAPI?.setBaseUrl(baseURL) LoopringAPI.contactAPI?.setBaseUrl(baseURL) LoopringAPI.vaultAPI?.setBaseUrl(baseURL) LoopringAPI.rabbitWithdrawAPI?.setBaseUrl(rabbitWithdrawAPIURL ? rabbitWithdrawAPIURL : baseURL) } } export { getContractTypeByNetwork } from './wallet' ================================================ FILE: packages/core/src/api_wrapper/wallet.ts ================================================ import { ContractType } from "@loopring-web/loopring-sdk"; import { LoopringAPI } from "./" export const getContractTypeByNetwork = async ( wallet: string ,network?: string) => { const res = await LoopringAPI.walletAPI!.getContractType({ wallet: wallet }) const data = (res.raw_data as any).data as ContractType[] if (network && data?.find(ele => ele.network === network)) { return data?.find(ele => ele.network === network); } else { return data && data[0] } } ================================================ FILE: packages/core/src/component/BtnConnect.tsx ================================================ import { withTranslation } from 'react-i18next' import { accountStaticCallBack, btnClickMap, btnLabel, isCoinbaseSmartWallet, LoopringAPI, store, useAccount, useSystem, useUpdateAccount } from '../index' import { Button, useSettings, ButtonProps, useOpenModals, AccountStep, } from '@loopring-web/component-lib' import React from 'react' import _ from 'lodash' import { coinbaseSmartWalletChains, fnType, i18n, L1L2_NAME_DEFINED, LoadingIcon, MapChainId, SagaStatus, } from '@loopring-web/common-resources' import { ChainId, NetworkWallet, toBig } from '@loopring-web/loopring-sdk' import { useAppKit } from '@reown/appkit/react' export const WalletConnectL2Btn = withTranslation(['common'], { withRef: true, })(({ t, btnLabelProps = {}, btnClickMapProps = {}, className, size = 'large', width }: any) => { const { status: accountStatus, account } = useAccount() const { defaultNetwork } = useSettings() const { app, exchangeInfo } = useSystem() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const _btnLabel = Object.assign(_.cloneDeep(btnLabel), { [fnType.ERROR_NETWORK]: [ function () { return `labelWrongNetwork` }, ], ...btnLabelProps, }) const labelUI = React.useMemo(() => { if (!exchangeInfo || ![SagaStatus.UNSET, SagaStatus.DONE].includes(accountStatus)) return return t( accountStaticCallBack(_btnLabel, [ { chainId: defaultNetwork, isEarn: app === 'earn', readyState: account.readyState, }, ]), { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) }, [accountStatus, account.readyState, i18n.language, defaultNetwork, app, exchangeInfo]) const _btnClickMap = Object.assign(_.cloneDeep({ ...btnClickMap, ...btnClickMapProps }), {}) const { setShowDeposit, setShowAccount } = useOpenModals() const { goUpdateAccount } = useUpdateAccount() return ( ) }) as (props: ButtonProps & { [key: string]: any }) => JSX.Element export const BtnConnectL1 = withTranslation(['common', 'layout'], { withRef: true, })(({ t }: any) => { const { status: accountStatus, account: { readyState }, } = useAccount() const { exchangeInfo } = useSystem() const labelUI = React.useMemo(() => { if (!exchangeInfo || accountStatus !== SagaStatus.UNSET) return return t( accountStatus === SagaStatus.UNSET ? accountStaticCallBack(Object.assign(_.cloneDeep(btnLabel))) : 'labelConnectWallet', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) }, [accountStatus, exchangeInfo]) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const modal = useAppKit() return ( <> ) }) as typeof Button ================================================ FILE: packages/core/src/component/NotificationItem.tsx ================================================ import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { useHistory } from 'react-router-dom' import { Button, useSettings } from '@loopring-web/component-lib' import { ConvertToIcon, L1L2_NAME_DEFINED, Layer2RouterID, MapChainId, MessageIcon, RouterPath, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import { Box, IconButton, Link, Snackbar, Typography } from '@mui/material' import moment from 'moment' import styled from '@emotion/styled' import { useNotification, useNotificationFunc } from '../hooks' const BoxStyle = styled(Box)` .point { right: 4px; top: 4px; position: absolute; width: 8px; height: 8px; background: var(--color-error); border-radius: 50%; } &.headerItem { padding: 0; .MuiGrid-item { padding: 0; } .message { word-wrap: break-word; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; width: 274px; display: inline-block; margin-top: ${({ theme }) => (1 / 2) * theme.unit}px; svg { display: none; } } .time { margin-top: ${({ theme }) => (1 / 2) * theme.unit}px; font-size: ${({ theme }) => theme.fontDefault.body2}; } .point { width: 4px; height: 4px; right: 0; top: 0; } } ` export const NoticePop = ({ isShow, setNotificationPush, ...rest }: sdk.UserNotification & { isShow: boolean setNotificationPush: (props: { isShow: boolean; item: any }) => void }) => { const history = useHistory() const { t } = useTranslation() const { onReadClick } = useNotificationFunc({}) // const [open, setOpen] = React.useState(false) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const handleClose = () => { onReadClick(0, rest) setNotificationPush({ isShow: false, item: null }) } const ele = useNotification({ ...rest, index: 0, onReadClick, }) const actionEle = React.useMemo(() => { return ( ) }, [ele]) return ( {t(ele.i18nKey, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {rest.message} {t('labelNotificationTime', { time: moment(rest.createAt).fromNow(), interpolation: { escapeValue: false, }, })} } action={actionEle} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} /> ) } export const NotificationItem = React.memo( ({ index, onReadClick, className = '', size = 'large', noAction = false, ...rest }: sdk.UserNotification & { index: number className?: string noAction?: boolean size?: 'small' | 'medium' | 'large' onReadClick?: (index: number, rest: any) => void }) => { const { message, createAt } = rest const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { t } = useTranslation() const { onReadClick: defaultOnRead } = useNotificationFunc({}) const ele = useNotification({ index, onReadClick: defaultOnRead, ...rest, }) const _onReadIconClick = onReadClick ? onReadClick : defaultOnRead return ( { _onReadIconClick(index, rest) }} > {!rest.read && } noAction && onReadClick && onReadClick(index, rest)} flex={1} display={'flex'} flexDirection={'column'} alignItems={'flex-start'} > {t(ele.i18nKey, { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, })} {!noAction && ele.active ? ( ele.active()} className={'message'} display={'inline-flex'} justifyContent={'space-between'} alignItems={'center'} width={'100%'} > {message} ) : ( {message} )} {t('labelNotificationTime', { time: moment(createAt).fromNow(), interpolation: { escapeValue: false, }, })} ) }, ) ================================================ FILE: packages/core/src/component/index.ts ================================================ export * from './BtnConnect' export * from './styled' export * from './NotificationItem' ================================================ FILE: packages/core/src/component/styled.ts ================================================ import styled from '@emotion/styled' import { Box, Grid } from '@mui/material' import { TablePaddingX } from '@loopring-web/component-lib' import { LAYOUT } from '../defs' export const StylePaper = styled(Box)` display: flex; flex-direction: column; width: 100%; flex: 1; background: var(--color-box-third); border-radius: ${({ theme }) => theme.unit}px; margin-bottom: ${({ theme }) => 2 * theme.unit}px; .title { font-size: ${({ theme }) => theme.unit * 3}px; margin-left: ${({ theme }) => 3 * theme.unit}px; margin-top: ${({ theme }) => 3 * theme.unit}px; } .tableWrapper { display: flex; margin-top: ${({ theme }) => 3 * theme.unit}px; flex: 1; .rdg { flex: 1; } } .extraTradeClass { .rdg-header-row { background-color: inherit !important; } } ` as typeof Box export const TableWrapStyled = styled(Box)` & { .toolbar { padding: 0; } border-radius: ${({ theme }) => theme.unit}px; .rdg { .rdg-header-row { border-radius: ${({ theme }) => theme.unit}px ${({ theme }) => theme.unit}px 0 0; } } } &.min-height .rdg { min-height: initial; } & .min-height .rdg { min-height: initial; } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })}; &.fixed { position: relative; .toolbar { position: fixed; top: ${LAYOUT.HEADER_HEIGHT}px; @media (min-width: 1200px) { max-width: calc(1200px - 48px); } max-width: calc(100% - ${({ theme }) => 6 * theme.unit}px); z-index: 209; background: var(--color-box); } } ` as typeof Grid export const TableProWrapStyled = styled(Box)` & { .toolbar { padding: 0; } background: var(--color-pop-bg); border-radius: ${({ theme }) => theme.unit}px; .rdg { .rdg-header-row { border-radius: ${({ theme }) => theme.unit}px ${({ theme }) => theme.unit}px 0 0; background: var(--color-pop-bg) !important; } } } ${({ theme }) => TablePaddingX({ pLeft: theme.unit * 3, pRight: theme.unit * 3 })}; &.fixed { position: relative; .toolbar { position: fixed; top: ${LAYOUT.HEADER_HEIGHT}px; @media (min-width: 1200px) { max-width: calc(1200px - 48px); } max-width: calc(100% - ${({ theme }) => 6 * theme.unit}px); z-index: 209; background: var(--color-box); } } ` as typeof Grid export const FixedStyle = styled(Box)` @media only screen and (min-height: 784px) and (min-width: 1024px) { position: fixed; } ` as typeof Box // ${({theme}) => theme.border.defaultFrame({c_key: 'blur', d_R: 1})}; ================================================ FILE: packages/core/src/defs/index.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' export enum ActionResultCode { NoError, DataNotReady, GetAccError, GenEddsaKeyError, UpdateAccountError, } export type EddsaKey = { eddsaKey: any; accInfo?: sdk.AccountInfo } export const LAYOUT = { HEADER_HEIGHT: 64, FOOT_COMMON_HEIGHT: 48, } export const REFRESH_RATE = 1000 export const DAYS = 30 export const BIGO = sdk.toBig(0) export const MAPFEEBIPS = 200 ================================================ FILE: packages/core/src/hookConnect.tsx ================================================ import React from 'react' import { OutlineSelect, OutlineSelectItem, setShowAccount, useOpenModals, useSettings, WalletConnectStep, } from '@loopring-web/component-lib' import { ConnectProviders, connectProvides, ErrorType, ProcessingStep, ProcessingType, } from '@loopring-web/web3-provider' import { AccountStatus, BaseIcon, ChainETHEREUMIcon, ChainGOERLIIcon, ChainTAIKOIcon, DoneIcon, DropDownIcon, L1L2_NAME_DEFINED, MapChainId, myLog, NetworkMap, SagaStatus, SoursURL, SPECIAL_ACTIVATION_NETWORKS, ThemeType, UIERROR_CODE, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { accountReducer, useAccount } from './stores/account' import { resetDepositData, resetTransferData, resetWithdrawData, useModalData, useSystem } from './stores' import { checkAccount, networkUpdate, resetLayer12Data, useConnectHook } from './services' import { REFRESH_RATE } from './defs' import { createImageFromInitials, store, WalletConnectL2Btn } from './index' import { useTranslation } from 'react-i18next' import { Avatar, Box, Button, Modal, SelectChangeEvent, Typography } from '@mui/material' import { updateAccountStatus } from './stores/account/reducer' import styled from '@emotion/styled' import { useAppKit } from '@reown/appkit/react' export const OutlineSelectStyle = styled(OutlineSelect)` &.walletModal { background: var(--field-opacity); height: var(--row-height); } .MuiAvatar-root { background: var(--color-white); margin-right: ${({ theme }) => theme.unit}px; width: 20px; height: 20px; position: relative; svg { position: absolute; width: 18px; height: 18px; left: 50%; top: 50%; transform: translate(-50%, -50%); } } &.test .MuiSelect-outlined .label { display: inline-flex; &:after { color: var(--network-text); content: ' test'; padding-left: 0.5em; display: inline-flex; font-size: var(body2); } } .MuiSelect-outlined.MuiSelect-outlined { padding-right: ${({ theme }) => theme.unit * 3}px; padding-left: ${({ theme }) => theme.unit * 3}px; display: inline-flex; align-items: center; } &.mobile .MuiSelect-outlined.MuiSelect-outlined { padding-left: 0px; padding-right: ${({ theme }) => theme.unit * 2}px; } &.header { .MuiSelect-outlined { } &.mobile { .MuiAvatar-root { display: flex; } .label { display: none; } padding-left: 0; padding-right: ${({ theme }) => theme.unit * 4}px; position: relative; } } ` as typeof OutlineSelect export const OutlineSelectItemStyle = styled(OutlineSelectItem)` .MuiAvatar-root { width: 24px; height: 24px; svg { width: 20px; height: 20px; } background: var(--color-white); margin-right: ${({ theme }) => theme.unit}px; } &.provider-test .label { &:after { content: ' test'; padding-left: 0.5em; display: inline-flex; font-size: var(body2); color: var(--network-text); } } ` as typeof OutlineSelectItem const Icon = ({ label = '' }: { label: string }) => { switch (label) { case 'GOERLI': return ( ) case 'ETHEREUM': return ( ) case 'SEPOLIA': return ( ) case 'TAIKO': return ( ) case 'TAIKOHEKLA': return ( ) case 'BASE': return ( ) case 'BASESEPOLIA': return ( ) default: const child = label.split(' ')?.map((item) => item[0]) return } } const onConnect = async (accAddress: string, chainId: any) => { const { settings: { defaultNetwork }, account: { readyState, accAddress: _accAddress }, } = store.getState() if ( _accAddress?.toLowerCase() === accAddress?.toUpperCase() && defaultNetwork.toString() == chainId.toString() && [ AccountStatus.NO_ACCOUNT, AccountStatus.DEPOSITING, AccountStatus.NOT_ACTIVE, AccountStatus.LOCKED, ].includes(readyState as AccountStatus) ) { myLog('After connect >>,onChinId change') } else { myLog('After connect >>,network part start: step1 networkUpdate') store.dispatch(updateAccountStatus({ _chainId: chainId })) const networkFlag = await networkUpdate(chainId) if (networkFlag) { resetLayer12Data() checkAccount(accAddress, chainId !== 'unknown' ? chainId : undefined) } store.dispatch(resetWithdrawData(undefined)) store.dispatch(resetTransferData(undefined)) store.dispatch(resetDepositData(undefined)) } } const onDisConnect = async () => { resetLayer12Data() store.dispatch(resetWithdrawData(undefined)) store.dispatch(resetTransferData(undefined)) store.dispatch(resetDepositData(undefined)) } export const callSwitchChain = async (_chainId: string | number) => { const { defaultNetwork, themeMode } = store.getState().settings if (Number(defaultNetwork) !== Number(_chainId)) { try { await connectProvides.sendChainIdChange(defaultNetwork, themeMode === ThemeType.dark) } catch (error) { throw { code: UIERROR_CODE.ERROR_SWITCH_ETHEREUM } } if (defaultNetwork && Number(defaultNetwork) !== Number(await connectProvides?.usedWeb3?.eth?.getChainId())) { throw { code: UIERROR_CODE.ERROR_SWITCH_ETHEREUM } } } } export const useSelectNetwork = ({ className }: { className?: string }) => { const { t } = useTranslation() const { defaultNetwork: _defaultNetwork, isMobile } = useSettings() const { account: { connectName, accAddress }, } = useAccount() const [defaultNetwork, setDefaultNetwork] = React.useState(_defaultNetwork) const handleOnNetworkSwitch = React.useCallback( async (value: sdk.ChainId) => { myLog('defaultNetwork', value) const account = store.getState().account setDefaultNetwork((state) => { if (Number(value) !== Number(state)) { if (account.readyState !== AccountStatus.UN_CONNECT) { if (connectProvides?.usedWeb3 && connectProvides.usedProvide) { onConnect(account.accAddress, value) } else { onDisConnect() return state } } else { networkUpdate(Number(value)) } } return value }) }, [defaultNetwork], ) React.useEffect(() => { setDefaultNetwork((state) => { if (_defaultNetwork && state !== _defaultNetwork) { networkUpdate() return Number(_defaultNetwork) } else { return state } }) }, [_defaultNetwork]) const disable = React.useCallback( (id: any) => { myLog(connectName, ConnectProviders.WalletConnect) if (connectName == ConnectProviders.GameStop.toString()) { return ![1, 5].includes(Number(id)) } else if ( (connectProvides.usedProvide as any)?.session && (connectProvides.usedProvide as any)?.namespace ) { // @ts-ignore const optionalChains = connectProvides.usedProvide?.session?.namespaces[ (connectProvides.usedProvide as any).namespace ]?.chains ?? [`${(connectProvides.usedProvide as any).namespace}:${defaultNetwork}`] return !optionalChains.includes( `${(connectProvides.usedProvide as any).namespace}:${Number(id)}`, ) } return false }, [connectName, connectProvides.usedProvide, defaultNetwork], ) const { open } = useAppKit() const NetWorkItems: JSX.Element = React.useMemo(() => { myLog('defaultNetwork NetWorkItems', defaultNetwork) return ( <> {defaultNetwork && NetworkMap[defaultNetwork] && ( open({ view: 'Networks' })} disabled={disable(defaultNetwork)} className={`viewNetwork${defaultNetwork} ${ NetworkMap[defaultNetwork]?.isTest ? 'provider-test' : '' }`} aria-label={NetworkMap[defaultNetwork].label} value={defaultNetwork} > )} ) }, [defaultNetwork, NetworkMap, connectName, connectProvides.usedProvide, accAddress]) React.useEffect(() => {}, []) return { NetWorkItems, handleOnNetworkSwitch, } } export function useConnect(_props: { state: keyof typeof SagaStatus }) { const { account, shouldShow, resetAccount, statusUnset: statusAccountUnset, setShouldShow, status: accountStatus, } = useAccount() // const {updateWalletLayer2, resetLayer2} = useWalletLayer2() const { resetWithdrawData, resetTransferData, resetDepositData } = useModalData() const { setShowConnect } = useOpenModals() const [stateAccount, setStateAccount] = React.useState('DONE') React.useEffect(() => { if (stateAccount === SagaStatus.PENDING && accountStatus === SagaStatus.DONE) { setStateAccount('DONE') statusAccountUnset() } }, [stateAccount, accountStatus]) const handleConnect = React.useCallback( async ({ accounts, chainId, }: { accounts: string provider: any chainId: sdk.ChainId | 'unknown' }) => { const accAddress = accounts[0] await onConnect(accAddress, chainId) setShouldShow(false) setShowConnect({ isShow: !!shouldShow ?? false, step: WalletConnectStep.SuccessConnect, }) await sdk.sleep(REFRESH_RATE) setShowConnect({ isShow: false, step: WalletConnectStep.SuccessConnect }) }, [ resetWithdrawData, resetTransferData, resetDepositData, setShouldShow, setShowConnect, shouldShow, ], ) const handleAccountDisconnect = React.useCallback( async ({ reason, code }: { reason?: string; code?: number }) => { myLog('handleAccountDisconnect:', account, reason, code) resetAccount({ shouldUpdateProvider: true }) setStateAccount(SagaStatus.PENDING) onDisConnect() }, [account], ) const handleProcessing = React.useCallback( ({ opts, type }: { type: ProcessingType; opts: any }) => { if (type == ProcessingType.nextStep) { if (opts.step !== undefined && opts.step == ProcessingStep.showQrcode) { store.dispatch(accountReducer.updateAccountStatus({ qrCodeUrl: opts.QRcode })) setShowConnect({ isShow: false, }) } else { const { qrCodeUrl } = opts if (qrCodeUrl) { store.dispatch(accountReducer.updateAccountStatus({ qrCodeUrl })) setShowConnect({ isShow: true, step: WalletConnectStep.WalletConnectQRCode, }) } } } // const { qrCodeUrl } = opts; }, [setShowConnect], ) const handleError = React.useCallback( (props: { type: keyof typeof ErrorType; opts?: any }) => { if (!!account.accAddress) { myLog('try to resetAccount...') resetAccount() } statusAccountUnset() setShowAccount({ isShow: false }) if (props?.opts?.error?.code === -32002) { } else if ( props?.opts?.connectName === ConnectProviders.WalletConnect && props?.opts?.error && props?.opts?.error?.code === UIERROR_CODE.ERROR_WALLECTCONNECT_MANUALLY_CLOSE ) { setShowConnect({ isShow: false }) } else { } }, [ account._chainId, account.accAddress, shouldShow, statusAccountUnset, setShowConnect, resetAccount, ], ) useConnectHook({ handleAccountDisconnect, handleProcessing, handleError, handleConnect, }) } const ViewAccountTemplateStyle = styled(Box)` &.inModal, &.inBlock { > .MuiTypography-root { font-size: ${({ theme }) => theme.fontDefault.body1}; line-height: 1.2em; font-weight: 400; } > h1.MuiTypography-root { font-size: ${({ theme }) => theme.fontDefault.h5}; line-height: 1.2em; font-weight: 500; } } ` export const ViewAccountTemplate = React.memo( ({ activeViewTemplate, className, size = 'large' }: { activeViewTemplate: JSX.Element className?: string size?: string onClickCompleteSignIn?: () => void }) => { const { account } = useAccount() const { t } = useTranslation(['common', 'layout']) const { isMobile, defaultNetwork } = useSettings() const { app } = useSystem() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const isSpecialActivation = app === 'earn' && SPECIAL_ACTIVATION_NETWORKS.includes(defaultNetwork) const viewTemplate = React.useMemo(() => { switch (account.readyState) { case AccountStatus.UN_CONNECT: return ( {t('describeTitleConnectToWallet', { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} ) break case AccountStatus.LOCKED: return ( {t('describeTitleLocked')} ) break case AccountStatus.NO_ACCOUNT: return ( {isSpecialActivation ? t('labelEarnDepositDes') : t('describeTitleNoAccount', { layer2: L1L2_NAME_DEFINED[network].layer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} ) break case AccountStatus.NOT_ACTIVE: return ( {isSpecialActivation ? ( <> {t("labelAccountCreatedHint")} ) : ( <> {t('describeTitleNotActive', { layer2: 'Layer 2', loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l1ChainName: 'Ethereum', })} )} ) break case AccountStatus.DEPOSITING: return ( {'loading'} {isSpecialActivation ? t('labelEarnDepositDes') : t('describeTitleOpenAccounting', { l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, })} ) break case AccountStatus.ERROR_NETWORK: return ( {t('describeTitleOnErrorNetwork', { connectName: account.connectName, })} ) break case AccountStatus.ACTIVATED: return activeViewTemplate default: break } }, [account.readyState, account.connectName, isMobile, t, activeViewTemplate, isSpecialActivation]) return ( <> {viewTemplate} ) }, ) ================================================ FILE: packages/core/src/hooks/common/index.ts ================================================ export * from './useAddrCheck' export * from './useAllowances' export * from './useBtnStatus' export * from './usePairMatch' export * from './useToast' export * from './useTrade' export * from './useGetOrderHistorys' export * from './useHookBtn' export * from './useMyCollection' export * from './useMyNFTCollection' export * from './useCollectionManage' export * from './useCollectionImport' export * from './useNFT' export * from './useIsHebao' export * from './useNotification' export * from './useInjectWeb3Modal' export { useUserWallets } from './useUserWallets' export { useThrottle } from './useThrottle' export { useDebouncedCallback } from './useDebounce' export { useMarket } from './useMarket' ================================================ FILE: packages/core/src/hooks/common/useAddrCheck.ts ================================================ import React from 'react' import { connectProvides } from '@loopring-web/web3-provider' import { AddressError, globalSetup, myLog, SDK_ERROR_MAP_TO_UI, UIERROR_CODE, NetworkMap, CustomError, ErrorMap, HEBAO_CONTRACT_MAP, } from '@loopring-web/common-resources' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import { checkAddr } from '../../utils' import { getContractTypeByNetwork, LoopringAPI, store, useAccount, useContacts, useSystem } from '../../index' import { ToastType, useOpenModals, useSettings } from '@loopring-web/component-lib' export const useAddressCheck = (checkLayer2Status: boolean = true) => { const [address, setAddress] = React.useState('') const _address = React.useRef('') const { chainId } = useSystem() const { defaultNetwork } = useSettings() const [{ realAddr, ens, isENSWrong }, setRealAddr] = React.useState<{ realAddr: string ens: string isENSWrong: boolean }>({ realAddr: '', ens: '', isENSWrong: false, }) // const [ens, setENS] = React.useState('') const [addrStatus, setAddrStatus] = React.useState(AddressError.NoError) const [checkAddAccountId, setCheckAddaccountId] = React.useState() const [isAddressCheckLoading, setIsAddressCheckLoading] = React.useState(false) const [isLoopringAddress, setIsLoopringAddress] = React.useState(false) const [isActiveAccount, setIsActiveAccount] = React.useState(false) const [isActiveAccountFee, setIsActiveAccountFee] = React.useState(false) const [isSameAddress, setIsSameAddress] = React.useState(false) const [isCFAddress, setIsCFAddress] = React.useState(false) const [isContractAddress, setIsContractAddress] = React.useState(false) const [isContract1XAddress, setIsContract1XAddress] = React.useState(false) const [loopringSmartWalletVersion, setLoopringSmartWalletVersion] = React.useState( undefined as { isLoopringSmartWallet: boolean; version?: string } | undefined, ) const { setShowGlobalToast } = useOpenModals() const { account: { accAddress, apiKey, accountId }, } = useAccount() const { updateContacts } = useContacts() const check = React.useCallback(async (address: any, web3: any) => { try { if (LoopringAPI.walletAPI && LoopringAPI.exchangeAPI) { if ( /^0x[a-fA-F0-9]{40}$/g.test(address) || /.*\.eth$/gi.test(address) || (/^\d{5,8}$/g.test(address) && Number(address) > 10000) ) { myLog('address update ', address) setIsAddressCheckLoading(true) const { realAddr, addressErr, isContract, ens } = await checkAddr(address, web3) if (_address.current == address) { setRealAddr({ realAddr, ens, isENSWrong: false }) setAddrStatus(addressErr) if (isContract) { setIsContractAddress(isContract) } if (addressErr === AddressError.NoError) { const [{ walletType }, response, _contractType] = await Promise.all([ LoopringAPI.walletAPI.getWalletType({ wallet: realAddr, network: sdk.NetworkWallet[NetworkMap[defaultNetwork]?.walletType], }), LoopringAPI.exchangeAPI.getAccount({ owner: realAddr, }), getContractTypeByNetwork(realAddr, sdk.NetworkWallet[NetworkMap[defaultNetwork]?.walletType]) ]) // for debounce & promise clean (next user input sync function will cover by async) if (_address.current == address) { if (walletType && walletType?.isInCounterFactualStatus) { setIsCFAddress(true) } else { setIsCFAddress(false) } if (walletType && walletType.isContract) { setIsContractAddress(true) } else { setIsContractAddress(false) } if (walletType && walletType.loopringWalletContractVersion !== '') { setLoopringSmartWalletVersion({ isLoopringSmartWallet: true, version: walletType.loopringWalletContractVersion, }) } else { setLoopringSmartWalletVersion({ isLoopringSmartWallet: false, }) } if (walletType && walletType.loopringWalletContractVersion?.startsWith('V1_')) { setIsContract1XAddress(true) } else { setIsContract1XAddress(false) } if ( _contractType && _contractType.network !== NetworkMap[defaultNetwork].walletType ) { setIsLoopringAddress(true) setAddrStatus(AddressError.InvalidAddr) setIsActiveAccount(false) setIsActiveAccountFee('not allow') } else if ( realAddr && _contractType?.network && _contractType?.network === NetworkMap[defaultNetwork].walletType && (_contractType as any)?.extra?.createWalletFromInfo?.fromWallet && (_contractType as any)?.extra?.createWalletFromInfo?.fromWallet?.toLowerCase() === realAddr.toLowerCase() ) { setIsLoopringAddress(true) setIsActiveAccount( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ? false : response?.accInfo?.nonce !== 0, ) setIsActiveAccountFee('not allow') } else if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { setIsLoopringAddress(false) setIsActiveAccount(false) setIsActiveAccountFee(false) } else { setIsLoopringAddress(true) setIsActiveAccount(response.accInfo.nonce !== 0) setIsActiveAccountFee( response.accInfo.nonce === 0 && /FirstUpdateAccountPaid/gi.test(response.accInfo.tags ?? ''), ) setCheckAddaccountId(response.accInfo.accountId) } } } else { setIsCFAddress(false) setIsLoopringAddress(false) setLoopringSmartWalletVersion({ isLoopringSmartWallet: false, }) } } myLog('address update async', address, realAddr) setIsAddressCheckLoading(false) } else { throw new CustomError(ErrorMap.ERROR_ADDRESS_CHECK_ERROR) // Error('wrong address format') } } else { throw new CustomError(ErrorMap.ERROR_WRONG_APIKEY) } } catch (error) { error = { ...(error as any), ...LoopringAPI?.walletAPI?.genErr(error as any), } // @ts-ignore if (_address?.current == address && error?.code == sdk.LoopringErrorCode.HTTP_ERROR) { _address.current = '' setAddrStatus(AddressError.TimeOut) setRealAddr({ realAddr: '', ens: '', isENSWrong: false }) setIsAddressCheckLoading(false) } else { setAddrStatus(address === '' ? AddressError.EmptyAddr : AddressError.InvalidAddr) myLog('address update address async', address, error) setRealAddr({ realAddr: '', ens: '', isENSWrong: false }) _address.current = '' setIsLoopringAddress(false) setIsAddressCheckLoading(false) } if (address !== '' && (error as any)?.code !== 500000) { setShowGlobalToast({ isShow: true, info: { type: ToastType.info, messageKey: ( SDK_ERROR_MAP_TO_UI[(error as any)?.code ?? UIERROR_CODE.ERROR_ADDRESS_CHECK_ERROR] ?? SDK_ERROR_MAP_TO_UI[UIERROR_CODE.ERROR_ADDRESS_CHECK_ERROR] )?.messageKey, }, }) } } }, [defaultNetwork]) const debounceCheck = _.debounce( async (address) => { myLog('address update sync', address) const found = store .getState() .contacts?.contacts?.find((contact) => contact.contactAddress === address) const listNoCheckRequired = [ sdk.AddressType.LOOPRING_HEBAO_CF, sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_1_6, sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_2_0, sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_0_0, sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_1_0, sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_2_0, sdk.AddressType.LOOPRING_HEBAO_CONTRACT_3_0_0, sdk.AddressType.EXCHANGE_OTHER, sdk.AddressType.EXCHANGE_BINANCE, sdk.AddressType.EXCHANGE_OKX, sdk.AddressType.EXCHANGE_HUOBI, sdk.AddressType.EXCHANGE_COINBASE, sdk.AddressType.CONTRACT, sdk.AddressType.EOA ] if (found && listNoCheckRequired.includes(found.addressType)) { let ens = '', isENSWrong = false if (found?.ens && connectProvides?.usedWeb3) { const ensAddr = await connectProvides?.usedWeb3.eth?.ens?.getAddress(found?.ens) if (ensAddr?.toLowerCase() !== address?.toLowerCase()) { isENSWrong = true } } setRealAddr({ realAddr: address, ens, isENSWrong }) switch (found.addressType) { case sdk.AddressType.LOOPRING_HEBAO_CF: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_1_6: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_2_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_0_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_1_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_2_2_0: case sdk.AddressType.LOOPRING_HEBAO_CONTRACT_3_0_0: { if (found.addressType === sdk.AddressType.LOOPRING_HEBAO_CF) { // recheck CF Wallet const walletTypeResponse = await LoopringAPI.walletAPI?.getWalletType({ wallet: realAddr, network: sdk.NetworkWallet[NetworkMap[defaultNetwork]?.walletType], }) setIsCFAddress(!!walletTypeResponse?.walletType?.isInCounterFactualStatus) if ( !walletTypeResponse?.walletType?.isInCounterFactualStatus && walletTypeResponse?.walletType?.loopringWalletContractVersion ) { const addressType: number | string = HEBAO_CONTRACT_MAP.find( (x) => x[0] === walletTypeResponse?.walletType?.loopringWalletContractVersion, )![1] await LoopringAPI.contactAPI?.updateContact( { // contactAddress: found.xaddress, // isHebao: isHebao ? true : false, // contactName: found.name, ...found, isHebao: true, accountId: accountId, addressType: addressType as number, }, apiKey, ) updateContacts() } } else { setIsCFAddress(false) } setIsContractAddress(true) setIsContract1XAddress( found.addressType === sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_1_6 || found.addressType === sdk.AddressType.LOOPRING_HEBAO_CONTRACT_1_2_0, ) setLoopringSmartWalletVersion({ isLoopringSmartWallet: true, version: undefined, }) setAddrStatus(AddressError.NoError) if (checkLayer2Status) { try { const response = await LoopringAPI.exchangeAPI?.getAccount({ owner: address, //realAddr != "" ? realAddr : address, }) if ( (response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message)) || !response ) { setIsLoopringAddress(false) setIsActiveAccount(false) setIsActiveAccountFee(false) } else { setIsLoopringAddress(true) setIsActiveAccount(response.accInfo.nonce !== 0) setIsActiveAccountFee( response.accInfo.nonce === 0 && /FirstUpdateAccountPaid/gi.test(response.accInfo.tags ?? ''), ) } } catch {} setIsAddressCheckLoading(false) } break } case sdk.AddressType.EXCHANGE_OTHER: case sdk.AddressType.EXCHANGE_BINANCE: case sdk.AddressType.EXCHANGE_OKX: case sdk.AddressType.EXCHANGE_HUOBI: case sdk.AddressType.EXCHANGE_COINBASE: { setIsLoopringAddress(false) setIsActiveAccount(false) setIsActiveAccountFee(false) setIsCFAddress(false) setIsContractAddress(false) setIsContract1XAddress(false) setLoopringSmartWalletVersion({ isLoopringSmartWallet: false, }) setAddrStatus(AddressError.NoError) break } case sdk.AddressType.CONTRACT: { setIsLoopringAddress(false) setIsActiveAccount(false) setIsActiveAccountFee(false) setIsCFAddress(false) setIsContractAddress(true) setIsContract1XAddress(false) setLoopringSmartWalletVersion({ isLoopringSmartWallet: false, }) setAddrStatus(AddressError.IsNotLoopringContract) break } case sdk.AddressType.EOA: { setIsCFAddress(false) setIsContractAddress(false) setIsContract1XAddress(false) setLoopringSmartWalletVersion({ isLoopringSmartWallet: false, }) setAddrStatus(AddressError.NoError) if (checkLayer2Status) { setIsAddressCheckLoading(true) try { const response = await LoopringAPI.exchangeAPI?.getAccount({ owner: address, //realAddr != "" ? realAddr : address, }) if ( (response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message)) || !response ) { setIsLoopringAddress(false) setIsActiveAccount(false) setIsActiveAccountFee(false) } else { setIsLoopringAddress(true) setIsActiveAccount(response.accInfo.nonce !== 0) setIsActiveAccountFee( response.accInfo.nonce === 0 && /FirstUpdateAccountPaid/gi.test(response.accInfo.tags ?? ''), ) } } catch {} setIsAddressCheckLoading(false) } break } } } else { check(address, connectProvides.usedWeb3) } }, globalSetup.wait, { maxWait: 1000, leading: false, trailing: true }, ) const initAddresss = () => { setRealAddr({ realAddr: '', ens: '', isENSWrong: false }) setAddrStatus(AddressError.NoError) setCheckAddaccountId(undefined) setIsLoopringAddress(false) setIsActiveAccount(false) setIsActiveAccountFee(false) setIsSameAddress(false) setIsCFAddress(false) setIsContractAddress(false) setIsContract1XAddress(false) setLoopringSmartWalletVersion(undefined) } React.useEffect(() => { // myLog("checkAddress", address, _address.current, isAddressCheckLoading); myLog('current address', _address.current, address) if (_address.current !== address) { if (isAddressCheckLoading == true) { //to async norsure should deleted initAddresss() debounceCheck.cancel() } _address.current = address debounceCheck(address) } return () => { debounceCheck.cancel() } }, [address, chainId]) const reCheck = React.useCallback(() => { debounceCheck(address) _address.current = address return () => { debounceCheck.cancel() } }, [address]) React.useEffect(() => { setIsSameAddress(realAddr.toLowerCase() === accAddress.toLowerCase()) }, [realAddr, accAddress]) return { address, ens, realAddr, checkAddAccountId, setAddress, addrStatus, isAddressCheckLoading, isLoopringAddress, isActiveAccount, isCFAddress, isSameAddress, isContract1XAddress, isContractAddress, isActiveAccountFee, isENSWrong, loopringSmartWalletVersion, reCheck, } } ================================================ FILE: packages/core/src/hooks/common/useAllowances.ts ================================================ import BigNumber from 'bignumber.js' import { LoopringAPI, useSystem, useTokenMap } from '../../index' import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' export function useAllowances({ owner, symbol }: { owner: string; symbol: string }) { const { tokenMap } = useTokenMap() const { status: systemStatus } = useSystem() const [allowanceInfo, setAllowanceInfo] = React.useState<{ allowance: BigNumber needCheck: boolean tokenInfo: sdk.TokenInfo }>() const updateAllowance = React.useCallback( async (symbol: string) => { if (owner && tokenMap && LoopringAPI.exchangeAPI && symbol) { const tokenInfo = tokenMap[symbol] if (tokenInfo) { let allowance = sdk.toBig(0) let needCheck = false if (tokenInfo?.symbol.toUpperCase() !== 'ETH') { const { tokenAllowances } = await LoopringAPI.exchangeAPI.getAllowances({ owner, token: [tokenInfo.address], }) allowance = sdk.toBig(tokenAllowances.get(tokenInfo.address) ?? '') needCheck = true } setAllowanceInfo({ allowance, needCheck, tokenInfo, }) } else { setAllowanceInfo(undefined) } } }, [tokenMap, owner], ) React.useEffect(() => { updateAllowance(symbol) }, [owner, symbol, systemStatus]) return { allowanceInfo, } } ================================================ FILE: packages/core/src/hooks/common/useBtnStatus.ts ================================================ import React from 'react' import { BtnInfo } from '@loopring-web/component-lib' import { TradeBtnStatus } from '@loopring-web/common-resources' export function useBtnStatus() { const [btnStatus, setBtnStatus] = React.useState(TradeBtnStatus.AVAILABLE) const [btnInfo, setBtnInfo] = React.useState(undefined) const setLabelAndParams = React.useCallback( (label: string, params: { [key: string]: string }) => { setBtnInfo({ label, params }) }, [setBtnInfo], ) const resetBtnInfo = React.useCallback(() => { setBtnInfo(undefined) }, [setBtnInfo]) const enableBtn = React.useCallback(() => { setBtnStatus(TradeBtnStatus.AVAILABLE) }, [setBtnStatus]) const disableBtn = React.useCallback(() => { setBtnStatus(TradeBtnStatus.DISABLED) }, [setBtnStatus]) const setLoadingBtn = React.useCallback(() => { setBtnStatus(TradeBtnStatus.LOADING) }, [setBtnStatus]) React.useEffect(() => { enableBtn() }, []) return { btnInfo, btnStatus, setLabelAndParams, resetBtnInfo, enableBtn, disableBtn, setLoadingBtn, } } ================================================ FILE: packages/core/src/hooks/common/useCollectionImport.ts ================================================ import { BigNumber } from 'bignumber.js' import React from 'react' import { Account, CollectionMeta, CustomError, ErrorMap, L2CollectionFilter, NFTSubRouter, NFTWholeINFO, RouterPath, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { getIPFSString, LoopringAPI, makeMeta, useAccount, useCollectionManage, useMyCollection, useWalletL2Collection, } from '../../index' import { ImportCollectionStep, ImportCollectionViewProps } from '@loopring-web/component-lib' import { useHistory, useRouteMatch } from 'react-router-dom' // const enum MINT_VIEW_STEP { // METADATA, // MINT_CONFIRM, // } BigNumber.config({ EXPONENTIAL_AT: 100 }) export const useCollectionImport = < Co extends CollectionMeta, NFT extends Partial, >(): ImportCollectionViewProps => { const { account } = useAccount() const history = useHistory() let match: any = useRouteMatch('/nft/importLegacyCollection/:id?') const stepList = match?.params?.id?.split('--') const contract = stepList && stepList[0]?.startsWith('0x') && stepList[0] const contractId = stepList && stepList[1] const [step, setStep] = React.useState(ImportCollectionStep.SELECTCONTRACT) const { legacyContract } = useWalletL2Collection() const [onLoading, setOnLoading] = React.useState(false) // const [contractList, setContractList] = React.useState([""]); const [selectContract, setSelectContract] = React.useState< { value: string; total?: number; list?: sdk.UserNFTBalanceInfo[] } | undefined >(undefined) const [filter, setFilter] = React.useState<{ isLegacy: boolean tokenAddress: string }>({ isLegacy: true, tokenAddress: selectContract?.value ?? '', }) const { onPageChange: onCollectionPageChange, ...collectionListProps } = useMyCollection( filter as any, ) const [selectCollection, setSelectCollection] = React.useState(undefined) const onContractChange = React.useCallback( async (item) => { setSelectContract({ value: item }) setOnLoading(true) let _filter = { offset: 0 } if (LoopringAPI.userAPI) { const { userNFTBalances, totalNum } = await LoopringAPI.userAPI .getUserNFTLegacyBalance( { collectionId: -1, accountId: account.accountId, tokenAddress: item, limit: 3, ..._filter, metadata: true, // close metadata }, account.apiKey, ) .catch((_error) => { throw new CustomError(ErrorMap.TIME_OUT) }) setSelectContract((state) => ({ ...state, value: item, list: userNFTBalances, total: totalNum, })) } setOnLoading(false) }, [account.accountId, account.apiKey], ) React.useEffect(() => { if ( collectionListProps.isLoading === false && selectCollection === undefined && collectionListProps.total ) { onCollectionChange(collectionListProps.collectionList[0]) } ;() => { setSelectCollection(undefined) } }, [collectionListProps.isLoading]) const { collection, selectedNFTS, onNFTSelected, baseURL, onNFTSelectedMethod, ...nftProps } = useCollectionManage({ collection: selectCollection }) const onCollectionChange = React.useCallback((item: Co | undefined) => { setSelectCollection(item) }, []) const onContractNext = React.useCallback( async (value: string) => { const _filter = { isLegacy: true, tokenAddress: value, } setFilter(_filter) setOnLoading(true) collectionListProps.collectionList = [] onCollectionPageChange(1, _filter) }, [collectionListProps], ) const onCollectionNext = React.useCallback(() => {}, []) const onClick = React.useCallback(() => {}, []) React.useEffect(() => { if (stepList && contract) { onContractChange(contract) onContractNext(contract) setStep(ImportCollectionStep.SELECTCOLLECTION) } else if (stepList && contractId) { setStep(ImportCollectionStep.SELECTNFT) } else { if (legacyContract?.length) { onContractChange(legacyContract[0]) } setStep(ImportCollectionStep.SELECTCONTRACT) } }, [legacyContract]) React.useEffect(() => { if (!collectionListProps.isLoading && onLoading === true) { setOnLoading(false) } }, [collectionListProps.isLoading]) return { account: account as Account, onContractChange, onContractNext, onCollectionChange, onCollectionNext, onNFTSelected, onNFTSelectedMethod, step, baseURL, getIPFSString, setStep: (item) => { switch (item) { case ImportCollectionStep.SELECTCOLLECTION: history.replace( `${RouterPath.nft}/${NFTSubRouter.importLegacyCollection}/${selectContract?.value}`, ) break case ImportCollectionStep.SELECTNFT: history.replace( `${RouterPath.nft}/${NFTSubRouter.importLegacyCollection}/${selectContract?.value}--${selectCollection?.id}`, ) break case ImportCollectionStep.SELECTCONTRACT: default: history.replace(`${RouterPath.nft}/${NFTSubRouter.importLegacyCollection}`) break } setStep(item) }, disabled: false, onLoading, onClick, data: { contractList: legacyContract, selectContract, selectNFTList: selectedNFTS as NFT[], selectCollection, collectionInputProps: { collection: selectCollection, collectionListProps: { onPageChange: React.useCallback( (page: number, _filter?: L2CollectionFilter | undefined) => { onCollectionPageChange(page, { ...filter, ..._filter }) }, [filter], ), ...collectionListProps, }, domain: LoopringAPI.delegate?.getCollectionDomain(), makeMeta, } as any, nftProps: nftProps as any, }, } } ================================================ FILE: packages/core/src/hooks/common/useCollectionManage.ts ================================================ import { BigNumber } from 'bignumber.js' import React from 'react' import { CollectionMeta, NFTLimit, NFTWholeINFO } from '@loopring-web/common-resources' import { getIPFSString, LoopringAPI, store, useAccount, useNFTListDeep, useSystem, useToast, } from '../../index' import { CollectionManageProps, CollectionMethod, ToastType } from '@loopring-web/component-lib' import * as sdk from '@loopring-web/loopring-sdk' import { useTranslation } from 'react-i18next' import { nextAccountSyncStatus } from '../../stores/account/reducer' BigNumber.config({ EXPONENTIAL_AT: 100 }) export const useCollectionManage = >({ collection, pageSize = NFTLimit, }: { collection?: CollectionMeta | undefined pageSize?: number }): CollectionManageProps => { const { account } = useAccount() const { chainId } = useSystem() const { t } = useTranslation() const [filter, setFilter] = React.useState({}) const toastObj = useToast() const { renderNFTPromise, nftListReduce } = useNFTListDeep() const [selectedNFTS, setSelectedNFTS] = React.useState([]) const [{ listNFT, total, page }, setListNFTValue] = React.useState<{ listNFT: NFT[] total: number page: number }>({ listNFT: [], total: 0, page: 1, }) const [isLoading, setIsLoading] = React.useState(false) const onFilterNFT = React.useCallback( async (props: { legacyFilter?: sdk.LegacyNFT | 'all'; limit?: number; page?: number }) => { if (collection && LoopringAPI.userAPI) { onNFTSelected('removeAll') setFilter(props) setIsLoading(true) const { legacyFilter = sdk.LegacyNFT.undecided, limit = pageSize, page: _page = 1 } = props const response = await LoopringAPI.userAPI.getUserNFTLegacyBalance( { accountId: account.accountId, tokenAddress: collection.contractAddress, // @ts-ignore collectionId: legacyFilter !== 'all' ? collection?.id : undefined, filter: legacyFilter !== 'all' ? legacyFilter : undefined, offset: (_page - 1) * limit, limit, metadata: true, }, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { } else { setListNFTValue({ total: response.totalNum, page: _page, listNFT: nftListReduce(response.userNFTBalances), }) setIsLoading(false) renderNFTPromise({ nftLists: response.userNFTBalances as any }).then((meta: any[]) => { if (page === _page) { setListNFTValue((state) => { return { ...state, listNFT: state.listNFT?.map((item, index) => { return { ...item, ...meta[index], tokenAddress: item.tokenAddress?.toLowerCase(), // etherscanBaseUrl, } }), } }) } }) } setIsLoading(false) } }, [collection], ) const { baseURL } = useSystem() const onNFTSelected = React.useCallback( (_item: NFT | 'addAll' | 'removeAll') => { if (_item === 'addAll') { setSelectedNFTS(listNFT) } else if (_item === 'removeAll') { setSelectedNFTS([]) } else { setSelectedNFTS((state) => { let has = false const previewList = (state ?? []).reduce((prev, item) => { if (_item.nftData === item.nftData) { has = true return prev } else { return [...prev, item] } }, [] as NFT[]) return has ? previewList : [_item, ...selectedNFTS] }) } }, [selectedNFTS, listNFT], ) const onNFTSelectedMethod = React.useCallback( async (items: NFT[], method: CollectionMethod) => { let hashList: string[] = [] if (collection && items.length && collection?.id) { let collectionId: number = 0 hashList = items.reduce((prev, item) => { return [...prev, item.nftData ?? ''] }, [] as string[]) setIsLoading(true) switch (method) { case CollectionMethod.moveOut: collectionId = 0 break case CollectionMethod.moveIn: collectionId = Number(collection?.id) break default: break } const response = await LoopringAPI.userAPI?.submitUpdateNFTLegacyCollection( { accountId: account.accountId, nftHashes: hashList, collectionId, }, chainId as any, account.apiKey, account.eddsaKey.sk, ) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { toastObj.setToastOpen({ open: true, type: ToastType.error, content: t('labelNFTMoveFailed'), }) } else { toastObj.setToastOpen({ open: true, type: ToastType.success, content: t('labelNFTMoveSuccess'), }) } LoopringAPI.nftAPI ?.getHadUnknownCollection({ accountId: account.accountId, }) .then((_response) => { if ((_response as unknown as sdk.RESULT_INFO)?.code) { return } store.dispatch( nextAccountSyncStatus({ ...store.getState().account, hasUnknownCollection: _response }), ) }) onFilterNFT({ ...filter }) } }, [filter], ) React.useEffect(() => { if (collection?.id && account.accountId) { onNFTSelected('removeAll') onFilterNFT({}) } }, [collection?.id]) return { collection: (collection ?? {}) as Partial, selectedNFTS, onNFTSelected, total, toastObj, page, listNFT, baseURL, getIPFSString, onNFTSelectedMethod, onFilterNFT, isLoading, filter, } } ================================================ FILE: packages/core/src/hooks/common/useDebounce.ts ================================================ import _ from "lodash" import { useEffect, useRef } from "react"; export function useDebouncedCallback( callback: (...args: A) => void, wait: number ) { // track args & timeout handle between calls const argsRef = useRef(); const timeout = useRef>(); function cleanup() { if(timeout.current) { clearTimeout(timeout.current); } } // make sure our timeout gets cleared if // our consuming component gets unmounted useEffect(() => cleanup, []); return function debouncedCallback( ...args: A ) { // capture latest args argsRef.current = args; // clear debounce timer cleanup(); // start waiting again timeout.current = setTimeout(() => { if(argsRef.current) { callback(...argsRef.current); } }, wait); }; } ================================================ FILE: packages/core/src/hooks/common/useGetOrderHistorys.ts ================================================ import React from 'react' import { RawDataTradeItem } from '@loopring-web/component-lib' import { Side, toBig, GetUserTradesRequest } from '@loopring-web/loopring-sdk' import { store, LoopringAPI, useAccount, volumeToCount, volumeToCountAsBigNumber, } from '../../index' import { TradeTypes } from '@loopring-web//common-resources' export function useGetOrderHistorys() { const [userTrades, setUserTrades] = React.useState([]) const [showLoading, setShowLoading] = React.useState(true) const [userOrderDetailList, setUserOrderDetailList] = React.useState([]) const { account: { accountId, apiKey }, } = useAccount() const tokenMap = store.getState().tokenMap.tokenMap const getUserTrade = React.useCallback( async (props?: Omit) => { if (LoopringAPI && LoopringAPI.userAPI && accountId && apiKey && tokenMap) { const userTrades = await LoopringAPI.userAPI.getUserTrades( { ...props, accountId, }, apiKey, ) return userTrades } }, [accountId, apiKey, tokenMap], ) const getUserOrderDetailTradeList = React.useCallback( async (props?: Omit) => { if (LoopringAPI && LoopringAPI.userAPI && accountId && apiKey && tokenMap) { const userTrades = await getUserTrade({ ...props }) if (userTrades && userTrades.userTrades) { setUserOrderDetailList( userTrades.userTrades.map((o) => { const marketList = o.market.split('-') // due to AMM case, we cannot use first index // const side = o.side === 'BUY' ? TradeTypes.Buy : TradeTypes.Sell const tokenFirst = marketList[marketList.length - 2] const tokenLast = marketList[marketList.length - 1] const baseToken = o.side === 'BUY' ? tokenFirst : tokenLast const quoteToken = o.side === 'BUY' ? tokenLast : tokenFirst const volumeToken = o.side === 'BUY' ? baseToken : quoteToken const volume = volumeToCount(volumeToken, o.volume) const feeKey = o.side === 'BUY' ? baseToken : quoteToken return { market: o.market, amount: volume, filledPrice: o.price, time: Number(o.tradeTime), volumeToken, fee: { key: feeKey, value: o.fee ? volumeToCount(feeKey, o.fee) : undefined, }, } }), ) } } }, [accountId, apiKey, getUserTrade, tokenMap], ) const getUserTradeList = React.useCallback(async () => { if (LoopringAPI && LoopringAPI.userAPI && accountId && apiKey && tokenMap) { const userTrades = await getUserTrade() if (userTrades && userTrades.userTrades) { setUserTrades( // @ts-ignore userTrades.userTrades.map((o) => { const marketList = o.market.split('-') // due to AMM case, we cannot use first index const side = o.side === Side.Buy ? TradeTypes.Buy : TradeTypes.Sell const tokenFirst = marketList[marketList.length - 2] const tokenLast = marketList[marketList.length - 1] const baseToken = side === TradeTypes.Buy ? tokenFirst : tokenLast const quoteToken = side === TradeTypes.Buy ? tokenLast : tokenFirst // const amt = toBig(o.volume).times(o.price).toString() const feeKey = o.side === Side.Buy ? baseToken : quoteToken return { side: side, price: { key: baseToken, // value: StringToNumberWithPrecision(o.price, baseToken) value: toBig(o.price).toNumber(), }, fee: { key: feeKey, // value: VolToNumberWithPrecision(o.fee, quoteToken), value: feeKey ? volumeToCount(feeKey, o.fee)?.toFixed(6) : undefined, }, time: Number(o.tradeTime), amount: { from: { key: baseToken, // value: VolToNumberWithPrecision(o.volume, baseToken), value: baseToken ? volumeToCount(baseToken, o.volume) : undefined, }, to: { key: quoteToken, // value: VolToNumberWithPrecision(amt, quoteToken) value: baseToken ? volumeToCountAsBigNumber(baseToken, o.volume)?.times(o.price).toNumber() : undefined, }, }, } }), ) setShowLoading(false) } } }, [accountId, apiKey, getUserTrade, tokenMap]) React.useEffect(() => { getUserTradeList() }, [getUserTradeList]) // useCustomDCEffect(async() => { // if (!LoopringAPI.userAPI || !accountId || !apiKey) { // return // } // const response = await LoopringAPI.userAPI.getUserTrades({accountId: accountId}, apiKey) // let userTrades: RawDataTradeItem[] = [] // response.userTrades.forEach((item: UserTrade, index: number) => { // }) // setUserTrades(userTrades) // }, [accountId, apiKey, LoopringAPI.userAPI]) return { userTrades, showLoading, userOrderDetailList, getUserOrderDetailTradeList, } } ================================================ FILE: packages/core/src/hooks/common/useHookBtn.ts ================================================ import React, { useEffect } from 'react' import { AccountStatus, coinbaseSmartWalletChains, fnType, MapChainId, TradeBtnStatus } from '@loopring-web/common-resources' import * as _ from 'lodash' import { accountStaticCallBack, btnClickMap, btnLabel } from '../help' import { useAccount, useSystem, useWalletLayer2 } from '../../stores' import { AccountStep, useOpenModals, useSettings } from '@loopring-web/component-lib' import { ChainId, NetworkWallet, toBig } from '@loopring-web/loopring-sdk' import { LoopringAPI } from '../../api_wrapper' import { useUpdateAccount } from '../../hooks/useractions' import { isCoinbaseSmartWallet } from '../../utils' export const useSubmitBtn = ({ availableTradeCheck, isLoading, submitCallback, ...rest }: { // [ key: string ]: any, submitCallback: (...props: any[]) => any availableTradeCheck: (...props: any[]) => { tradeBtnStatus: TradeBtnStatus label: string | undefined } isLoading: boolean }) => { // let {calcTradeParams} = usePageTradePro(); let { account, } = useAccount() let { app, exchangeInfo } = useSystem() let { defaultNetwork } = useSettings() const { goUpdateAccount } = useUpdateAccount() const { setShowDeposit, setShowAccount } = useOpenModals() const btnStatus = React.useMemo((): TradeBtnStatus | undefined => { if (account.readyState === AccountStatus.ACTIVATED) { if (isLoading) { // myLog("tradeBtnStatus", TradeBtnStatus.LOADING); return TradeBtnStatus.LOADING } else { const { tradeBtnStatus } = availableTradeCheck(rest) // myLog("tradeBtnStatus", tradeBtnStatus); return tradeBtnStatus } } else { return TradeBtnStatus.AVAILABLE } }, [account.readyState, availableTradeCheck, isLoading, rest]) const btnStyle: Partial | undefined = React.useMemo((): | Partial | undefined => { if (account.readyState === AccountStatus.ACTIVATED) { return undefined } else { return { backgroundColor: 'var(--color-primary)' } } }, [account.readyState]) const _btnLabelArray = Object.assign(_.cloneDeep(btnLabel), { [fnType.ACTIVATED]: [ (rest: any) => { const { label } = availableTradeCheck(rest) return label }, ], }) const _btnLabel = React.useMemo((): string => { if (!exchangeInfo) { return '...' } return accountStaticCallBack(_btnLabelArray, [{ ...rest, chainId: defaultNetwork, isEarn: app === 'earn', readyState: account.readyState, }]) }, [_btnLabelArray, rest, app, defaultNetwork, account.readyState, exchangeInfo]) const btnClickCallbackArray = Object.assign(_.cloneDeep(btnClickMap), { [fnType.ACTIVATED]: [submitCallback], }) const onBtnClick = React.useCallback( (props?: any) => { accountStaticCallBack(btnClickCallbackArray, [{ ...props, chainId: defaultNetwork, isEarn: app === 'earn', readyState: account.readyState, specialActivation: async () => { const [walletType, coinbaseSW] = await Promise.all([ LoopringAPI?.walletAPI?.getWalletType({ wallet: account.accAddress, network: MapChainId[defaultNetwork] as NetworkWallet, }), isCoinbaseSmartWallet(account.accAddress, defaultNetwork as ChainId), ]) const isActivationSupported = !walletType?.walletType?.isContract || (coinbaseSW && coinbaseSmartWalletChains.includes(defaultNetwork)) if (!isActivationSupported) { setShowAccount({ isShow: true, step: AccountStep.UpdateAccount_SmartWallet_NotSupported_Alert, }) return } const feeInfo = await LoopringAPI?.globalAPI?.getActiveFeeInfo({ accountId: account._accountIdNotActive, }) const { userBalances } = await LoopringAPI?.globalAPI?.getUserBalanceForFee({ accountId: account._accountIdNotActive!, }) const found = Object.keys(feeInfo.fees).find((key) => { const fee = feeInfo.fees[key].fee const foundBalance = userBalances[feeInfo.fees[key].tokenId] return (foundBalance && toBig(foundBalance.total).gte(fee)) || toBig(fee).eq('0') }) await goUpdateAccount({ isFirstTime: true, isReset: false, // @ts-ignore feeInfo: { token: feeInfo.fees[found!].fee, belong: found!, fee: feeInfo.fees[found!].fee, feeRaw: feeInfo.fees[found!].fee, }, }) }, specialActivationDeposit: async () => { const [walletType, coinbaseSW] = await Promise.all([ LoopringAPI?.walletAPI?.getWalletType({ wallet: account.accAddress, network: MapChainId[defaultNetwork] as NetworkWallet, }), isCoinbaseSmartWallet(account.accAddress, defaultNetwork as ChainId), ]) const isActivationSupported = !walletType?.walletType?.isContract || (coinbaseSW && coinbaseSmartWalletChains.includes(defaultNetwork)) if (!isActivationSupported) { setShowAccount({ isShow: true, step: AccountStep.UpdateAccount_SmartWallet_NotSupported_Alert, }) return } setShowDeposit({isShow: true}) }, exchangeInfoLoaded: exchangeInfo ? true : false, }]) }, [btnClickCallbackArray, app, defaultNetwork, account], ) return { btnStatus, onBtnClick, btnLabel: _btnLabel, btnStyle, isAccountActive: account.readyState === AccountStatus.ACTIVATED, // btnClickCallbackArray } } ================================================ FILE: packages/core/src/hooks/common/useInjectWeb3Modal.ts ================================================ import React from 'react' import { SUPPORTING_NETWORKS, SagaStatus, SoursURL, myLog } from '@loopring-web/common-resources' import { setDefaultNetwork, useSettings } from '@loopring-web/component-lib' import { accountServices, checkAccount, isSameEVMAddress, store, useAccount, useSelectNetwork, useSystem, useWalletLayer1 } from '@loopring-web/core' import { ConnectProviders, connectProvides, walletServices } from '@loopring-web/web3-provider' import { clearSystem, updateSystem } from '@loopring-web/core/src/stores/system/reducer' import { updateAccountStatus, cleanAccountStatus } from '@loopring-web/core/src/stores/account/reducer' import { useDispatch } from 'react-redux' import { providers } from 'ethers' import Web3 from 'web3' import { useTheme } from '@emotion/react' import { ChainId, toHex } from '@loopring-web/loopring-sdk' import { createAppKit, useAppKitEvents, useAppKitTheme, useAppKitAccount, useAppKitProvider, useAppKitNetwork } from "@reown/appkit/react"; import { mainnet, sepolia, base, baseSepolia, taiko, taikoHekla, Chain } from "@reown/appkit/networks"; import { Ethers5Adapter } from "@reown/appkit-adapter-ethers5"; const projectId = process.env.REACT_APP_WALLET_CONNECT_V2_ID! const networks: Chain[] = [ mainnet, sepolia, base, baseSepolia, { ...taiko, name: 'Taiko' }, taikoHekla, ] const metadata = { name: process.env.REACT_APP_NAME!, description: process.env.REACT_APP_NAME!, url: process.env.REACT_APP_DOMAIN!, icons: ['https://static.loopring.io/assets/svg/logo.svg'], } const chainIds = SUPPORTING_NETWORKS.map(Number) export const appKit = createAppKit({ adapters: [new Ethers5Adapter()], metadata: metadata, networks: networks.filter((item) => chainIds.includes(item.id)), projectId, chainImages: { 167000: `${SoursURL}earn/taiko.svg`, 8453: `${SoursURL}images/base.webp`, }, enableInjected: true, featuredWalletIds: [ 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96', 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', ], features: { analytics: true, email: false, socials: false, swaps: false, onramp: false, history: true, send: false, }, }) export const useInjectWeb3Modal = (type: 'MAIN' | 'EARN' | 'BRIDGE' | 'GUARDIAN') => { const { status } = useSystem() const { currency } = useSettings() const { address } = useAppKitAccount() const { chainId: _chainId } = useAppKitNetwork() const chainId = typeof _chainId === 'number' ? _chainId : Number(_chainId) const { walletProvider } = useAppKitProvider('eip155') const { resetAccount, account: {accAddress} } = useAccount() const event = useAppKitEvents() const dispatch = useDispatch() const { mode } = useTheme() const { setThemeMode } = useAppKitTheme() const { updateWalletLayer1 } = useWalletLayer1() const { defaultNetwork } = useSettings() React.useEffect(() => { setThemeMode(mode) }, [mode]) const combinedEvent = ['DISCONNECT_SUCCESS', 'DISCONNECT_ERROR'].includes(event.data.event) ? event.data.event : !isSameEVMAddress(address || '', accAddress) ? 'ADDRESS_CHANGED' : event.data.event const checkEvent = React.useCallback(() => { if (['DISCONNECT_SUCCESS', 'DISCONNECT_ERROR'].includes(combinedEvent)) { if (type === 'BRIDGE') { resetAccount() walletServices.sendDisconnect('', 'customer click disconnect') updateSystem({ chainId }) store.dispatch(setDefaultNetwork(undefined)) } else { walletServices.sendDisconnect('', 'customer click disconnect') resetAccount() store.dispatch(setDefaultNetwork(undefined)) } } if (["SWITCH_NETWORK", "INITIALIZE", 'CONNECT_SUCCESS', 'CONNECT_ERROR'].includes(combinedEvent)) { store.dispatch( clearSystem(undefined) ); store.dispatch( updateSystem({ chainId, }), ) store.dispatch(setDefaultNetwork(chainId)) } if (['ADDRESS_CHANGED'].includes(combinedEvent)) { if (address) { if ((address.toLowerCase() !== accAddress?.toLowerCase()) || (defaultNetwork && chainId !== defaultNetwork)) { accountServices.sendAccountLock() checkAccount(address, chainId) } else if (chainId && chainIds.includes(chainId)) { checkAccount(address, chainId) } } } }, [combinedEvent, walletProvider, chainId, address, accAddress, defaultNetwork]) React.useEffect(() => { checkEvent() }, [combinedEvent]) React.useEffect(() => { ;(async () => { if (type === 'BRIDGE' && address && status === SagaStatus.DONE) { updateWalletLayer1() } })() }, [address, walletProvider, chainId, status, accAddress, defaultNetwork]) React.useEffect(() => { if (type === 'BRIDGE' && address) { store.dispatch( updateSystem({ chainId, }), ) store.dispatch(setDefaultNetwork(chainId)) checkAccount(address, chainId) } }, [address]) React.useEffect(() => { if (walletProvider) { if ((walletProvider as any).isMetaMask) { dispatch( updateAccountStatus({ connectName: ConnectProviders.MetaMask, }), ) } else if ((walletProvider as any).isWalletConnect) { dispatch( updateAccountStatus({ connectName: ConnectProviders.WalletConnect, }), ) } else if ((walletProvider as any).isCoinbaseWallet) { dispatch( updateAccountStatus({ connectName: ConnectProviders.Coinbase, }), ) } else { dispatch( updateAccountStatus({ connectName: ConnectProviders.Unknown, }), ) } connectProvides.usedProvide = new providers.Web3Provider(walletProvider as any) // @ts-ignore connectProvides.usedWeb3 = new Web3(walletProvider as any) } else { dispatch( updateAccountStatus({ connectName: ConnectProviders.Unknown, }), ) } }, [walletProvider]) } ================================================ FILE: packages/core/src/hooks/common/useIsHebao.ts ================================================ import React from 'react' import { LoopringAPI, useAccount } from '../../index' import { NetworkWallet } from '@loopring-web/loopring-sdk' import { MapChainId, NetworkMap } from '@loopring-web/common-resources' import { useSettings } from '@loopring-web/component-lib' export const useIsHebao = () => { const [isHebao, setIsHebao] = React.useState(undefined) const { account: { accAddress }, } = useAccount() const { defaultNetwork } = useSettings() React.useEffect(() => { setIsHebao(undefined) LoopringAPI.walletAPI ?.getWalletType({ wallet: accAddress, network: MapChainId[defaultNetwork] as NetworkWallet }) .then((walletType) => { const isHebao = walletType?.walletType?.loopringWalletContractVersion !== '' setIsHebao(isHebao) }) }, [accAddress]) return { isHebao, } } ================================================ FILE: packages/core/src/hooks/common/useMarket.ts ================================================ import { RowConfig, RowConfigType, TableFilterParams, TickerNew, } from '@loopring-web/common-resources' import React, { useCallback } from 'react' import { favoriteVaultMarket as favoriteMarketReducer, LAYOUT, store, TokenMap, useAccount, useSystem, } from '@loopring-web/core' import * as sdk from '@loopring-web/loopring-sdk' export const useMarket = < R extends TickerNew & { cmcTokenId: number; isFavorite: boolean }, T = sdk.TokenInfo, >({ tableRef, rowConfig = RowConfig, tickerMap, handleRowClick, handleItemClick, tokenMap = store.getState()?.tokenMap?.tokenMap, }: { tickerMap: { [key: string]: R } tableRef: React.Ref rowConfig?: RowConfigType handleRowClick?: (index: number, props: T & R) => void handleItemClick?: (index: number, props: T & R) => void tokenMap: TokenMap }) => { const { account } = useAccount() // const { marketMap, tokenMap } = useTokenMap() const [tableTabValue, setTableTabValue] = React.useState(TableFilterParams.all) const [searchValue, setSearchValue] = React.useState('') const [filteredData, setFilteredData] = React.useState<(sdk.TokenInfo & R)[]>([]) const [tableHeight, setTableHeight] = React.useState(0) const { favoriteMarket, removeMarket, addMarket } = favoriteMarketReducer.useFavoriteVaultMarket() // const { tickList } = useQuote() const handleCurrentScroll = React.useCallback((currentTarget, tableRef) => { if (currentTarget && tableRef.current) { const calcHeight = tableRef.current?.offsetTop - LAYOUT.HEADER_HEIGHT - currentTarget.scrollY if (calcHeight < 2) { tableRef.current.classList.add('fixed') } else { tableRef.current.classList.remove('fixed') } } }, []) const currentScroll = React.useCallback( (event) => { handleCurrentScroll(event.currentTarget, tableRef) }, [handleCurrentScroll, tableRef], ) const handleTableFilterChange = React.useCallback( ({ type = tableTabValue, // keyword, ...rest }: { type?: TableFilterParams keyword?: string }) => { let filter = '' const favoriteMarket = store.getState().localStore.favoriteVaultMarket setSearchValue((state) => { filter = state if (rest.hasOwnProperty('keyword')) { filter = rest.keyword ?? '' } return filter }) let data: Array = Object.values(tickerMap) ?? [] data = data .filter((item) => { if (!(item as any).vaultTokenAmounts) { return false } const status = (item as any).vaultTokenAmounts.status as number return item.enabled && status & 1 }) .map((item) => { return { ...tokenMap[item.symbol], ...item, isFavorite: favoriteMarket?.includes(item.symbol), } }) if (type === TableFilterParams.favourite) { // myLog("tickList", data); data = data.filter((item) => { return favoriteMarket?.includes(item.symbol) }) } if (filter) { data = data.filter((o: any) => { return new RegExp(filter, 'ig').test(o.symbol) }) } setFilteredData(data) setTableHeight( (rowConfig.rowHeaderHeight ?? RowConfig.rowHeaderHeight) + data.length * (rowConfig?.rowHeight ?? RowConfig.rowHeaderHeight), ) }, [ tickerMap, tableTabValue, searchValue, // swapRankingList, // getFilteredTickList, ], ) const handleTabChange = useCallback( (_event: any, newValue: TableFilterParams) => { // if (tickList?.length) { setTableTabValue(newValue) handleTableFilterChange({ keyword: searchValue, type: newValue, }) // } }, [handleTableFilterChange, searchValue], ) const handleSearchChange = React.useCallback( (value) => { handleTableFilterChange({ keyword: value, type: tableTabValue }) }, [tableTabValue], ) const { forexMap } = useSystem() return { campaignTagConfig: {} as any, tableTabValue, handleTabChange, searchValue, handleStartClick: (symbol: string, rowIdx) => { if (favoriteMarket.includes(symbol)) { removeMarket(symbol) } else { addMarket(symbol) } handleTableFilterChange({}) }, favoriteMarket, handleSearchChange, addFavoriteMarket: addMarket, showLoading: !Object.keys(tickerMap ?? {})?.length, // tickList, rawData: filteredData, currentheight: tableHeight, onRowClick: handleRowClick, account, forexMap, rowConfig, handleTableFilterChange, onItemClick: handleItemClick, } } ================================================ FILE: packages/core/src/hooks/common/useMyCollection.ts ================================================ import { BigNumber } from 'bignumber.js' import React, { useState } from 'react' import { SagaStatus, CollectionMeta, L2CollectionFilter } from '@loopring-web/common-resources' import { getIPFSString, LoopringAPI, makeMeta, useSystem, useWalletL2Collection } from '../../index' import { CollectionListProps } from '@loopring-web/component-lib' // const enum MINT_VIEW_STEP { // METADATA, // MINT_CONFIRM, // } BigNumber.config({ EXPONENTIAL_AT: 100 }) export const useMyCollection = ( filter?: L2CollectionFilter, ): CollectionListProps => { const [collectionList, setCollectionList] = React.useState([]) const [copyToastOpen, setCopyToastOpen] = React.useState({ isShow: false, type: 'json', }) const [isLoading, setIsLoading] = React.useState(true) const domain = LoopringAPI.delegate?.getCollectionDomain() ?? '' const { status: walletL2CollectionStatus, walletL2Collection, total, page: page_reudex, updateWalletL2Collection, } = useWalletL2Collection() const { etherscanBaseUrl, baseURL } = useSystem() const [page, setPage] = useState(1) const onPageChange = (page: number, filter?: L2CollectionFilter) => { setPage(page) setIsLoading(true) updateWalletL2Collection({ page, filter }) } const renderCollection = React.useCallback(async () => { // let mediaPromise: any[] = []; setCollectionList(walletL2Collection as C[]) setIsLoading(false) }, [etherscanBaseUrl, page, walletL2Collection]) React.useEffect(() => { onPageChange(1, filter) }, []) React.useEffect(() => { if (walletL2CollectionStatus === SagaStatus.UNSET && page_reudex === page) { renderCollection() } }, [walletL2CollectionStatus, page, page_reudex]) return { setCopyToastOpen, collectionList, etherscanBaseUrl, onPageChange, domain, makeMeta, total, page, isLoading, copyToastOpen, baseURL, getIPFSString, } } ================================================ FILE: packages/core/src/hooks/common/useMyNFTCollection.ts ================================================ import { CollectionMeta, SagaStatus } from '@loopring-web/common-resources' import React from 'react' import { getIPFSString, LoopringAPI, makeMeta, useSystem, useWalletL2NFTCollection, } from '../../index' import { useHistory, useLocation } from 'react-router-dom' export const useMyNFTCollection = () => { const { etherscanBaseUrl, baseURL } = useSystem() const history = useHistory() const { search, ...rest } = useLocation() const searchParams = new URLSearchParams(search) const defaultPage = Number(searchParams.get('collectionPage')) ?? 1 const [page, setPage] = React.useState(defaultPage ? defaultPage : -1) const [collectionList, setCollectionList] = React.useState([]) const [isLoading, setIsLoading] = React.useState(true) const [copyToastOpen, setCopyToastOpen] = React.useState({ isShow: false, type: 'json', }) const domain = LoopringAPI.delegate?.getCollectionDomain() ?? '' const { status: walletL2NFTCollectionStatus, total, walletL2NFTCollection, page: page_reudex, updateWalletL2NFTCollection, } = useWalletL2NFTCollection() const onPageChange = (page: number) => { setPage(page) setIsLoading(true) searchParams.set('collectionPage', page.toString()) history.replace({ ...rest, search: searchParams.toString() }) updateWalletL2NFTCollection({ page }) } const renderCollection = React.useCallback(async () => { // let mediaPromise: any[] = []; setCollectionList(walletL2NFTCollection as C[]) setIsLoading(false) }, [etherscanBaseUrl, page, walletL2NFTCollection]) React.useEffect(() => { onPageChange(defaultPage && defaultPage !== -1 ? defaultPage : 1) }, []) React.useEffect(() => { if (walletL2NFTCollectionStatus === SagaStatus.UNSET && page_reudex === page) { // const { walletL2NFTCollection } = store.getState().walletL2NFTCollection; renderCollection() } }, [walletL2NFTCollectionStatus, page, page_reudex]) return { setCopyToastOpen, collectionList, etherscanBaseUrl, onPageChange, domain, makeMeta, total, page, isLoading, copyToastOpen, baseURL, getIPFSString, } } ================================================ FILE: packages/core/src/hooks/common/useNFT.tsx ================================================ import * as sdk from '@loopring-web/loopring-sdk' import { IPFS_LOOPRING_SITE, LOOPRING_NFT_METADATA, LOOPRING_TAKE_NFT_META_KET, myLog, NFTWholeINFO, } from '@loopring-web/common-resources' import { LoopringAPI } from '../../api_wrapper' import { BigNumber } from 'bignumber.js' import { connectProvides } from '@loopring-web/web3-provider' import Web3 from 'web3' import React from 'react' import { getIPFSString } from '../../utils' import { useSystem } from '../../stores' import { getMediaType } from '@loopring-web/component-lib' export const useNFTListDeep = >() => { const { baseURL } = useSystem() const getMetaFromContractORIpfs = ({ tokenAddress, nftId, isCounterFactualNFT, deploymentStatus, metadata, }: sdk.UserNFTBalanceInfo): Promise => { if (!!metadata?.imageSize?.original) { return Promise.resolve({}) } else if ( LoopringAPI.nftAPI && tokenAddress && (!metadata?.uri || tokenAddress.toLowerCase() === '0x1cACC96e5F01e2849E6036F25531A9A064D2FB5f'.toLowerCase()) && nftId && (!isCounterFactualNFT || (isCounterFactualNFT && deploymentStatus !== sdk.DEPLOYMENT_STATUS.NOT_DEPLOYED)) ) { const _id = new BigNumber(nftId ?? '', 16) myLog('nftId', _id, _id.toString()) return LoopringAPI?.nftAPI ?.getContractNFTMeta({ _id: _id.toString(), // @ts-ignore nftId, web3: connectProvides.usedWeb3 as unknown as Web3, tokenAddress, }) .then((response) => { if ((response as sdk.RESULT_INFO).code) { console.log('Contract NFTMeta error:', response) return {} } else { return Reflect.ownKeys(LOOPRING_TAKE_NFT_META_KET).reduce((prev, key) => { return { ...prev, [key]: response[key] } }, {} as LOOPRING_NFT_METADATA) } }) .catch((_error) => { return {} }) } else { try { const cid = LoopringAPI?.nftAPI?.ipfsNftIDToCid(nftId ?? '') const uri = IPFS_LOOPRING_SITE + cid return fetch(uri) .then((response) => response.json()) .catch((_error) => { return {} }) } catch (e) { return Promise.resolve({}) } } } const nftListReduce = (items: sdk.UserNFTBalanceInfo[]) => { return items.map((item) => { return { ...item, nftIdView: new BigNumber(item?.nftId ?? '0', 16).toString(), image: item.metadata?.uri, ...item.metadata?.base, ...item.metadata?.extra, } }) as any } const infoDetail = React.useCallback(async (item: Partial) => { const nftData: sdk.NftData = item.nftData as sdk.NftData let [nftMap] = await Promise.all([ LoopringAPI.nftAPI?.getInfoForNFTTokens({ nftDatas: [nftData], }), ]) const nftToken: Partial = nftMap && nftMap[nftData as sdk.NftData] ? nftMap[nftData as sdk.NftData] : {} let tokenInfo: NFTWholeINFO = { ...item, ...item.metadata?.base, ...item.metadata?.extra, pendingOnSync: item.metadata?.base && Object.keys(item.metadata?.base)?.length > 0 ? false : true, ...nftToken, } as NFTWholeINFO tokenInfo = { ...tokenInfo, nftIdView: new BigNumber(tokenInfo.nftId ?? '0', 16).toString(), nftBalance: tokenInfo.total ? Number(tokenInfo.total) - Number(tokenInfo.locked ?? 0) : 0, } if (!tokenInfo.name) { const meta = (await getMetaFromContractORIpfs(tokenInfo)) as LOOPRING_NFT_METADATA let metadata_tokenId: number | undefined = undefined if (meta.hasOwnProperty('tokenId')) { metadata_tokenId = meta['tokenId'] delete meta['tokenId'] } if (meta && Reflect.ownKeys(meta ?? {}).length > 0 && (meta.name || meta.image)) { tokenInfo = Object.assign(metadata_tokenId !== undefined ? { metadata_tokenId } : {}, { ...tokenInfo, ...(meta as any), animationUrl: (meta as any)?.animation_url, royaltyPercentage: (meta as any)?.royalty_percentage, isFailedLoadMeta: false, }) } else { tokenInfo = { ...tokenInfo, isFailedLoadMeta: true, } } } else { tokenInfo = { ...tokenInfo, isFailedLoadMeta: false, } } if ( tokenInfo.hasOwnProperty('animationUrl') && tokenInfo.animationUrl && tokenInfo?.animationUrl !== '' ) { try { const req = await fetch(getIPFSString(tokenInfo?.animationUrl, baseURL), { method: 'HEAD', }) tokenInfo.__mediaType__ = getMediaType(req?.headers?.get('content-type') ?? '') } catch (error) { console.log('nft animationUrl', error) } // myLog("animationUrl", "content-type", req.headers.get("content-type")); } return tokenInfo }, []) const renderNFTPromise = React.useCallback(async ({ nftLists }: { nftLists: T[] }) => { let mediaPromise: any[] = [] for (const nftBalanceItem of nftLists) { mediaPromise.push(infoDetail(nftBalanceItem)) } return Promise.all(mediaPromise) }, []) return { renderNFTPromise, infoDetail, nftListReduce } } ================================================ FILE: packages/core/src/hooks/common/useNotification.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import { InvestAssetRouter, MapChainId, RecordTabIndex, RouterPath, SDK_ERROR_MAP_TO_UI, } from '@loopring-web/common-resources' import { useHistory } from 'react-router-dom' import React from 'react' import { LoopringAPI } from '../../api_wrapper' import { TOASTOPEN, ToastType, useSettings } from '@loopring-web/component-lib' import { useAccount, useNotify } from '../../stores' import { useTranslation } from 'react-i18next' export const useNotificationFunc = ({ setToastOpen, }: { setToastOpen?: (state: TOASTOPEN) => void }) => { const { account } = useAccount() const { t } = useTranslation() const { defaultNetwork } = useSettings() const { getUserNotify } = useNotify() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const networkWallet: sdk.NetworkWallet = [ sdk.NetworkWallet.ETHEREUM, sdk.NetworkWallet.GOERLI, sdk.NetworkWallet.SEPOLIA, ].includes(network as sdk.NetworkWallet) ? sdk.NetworkWallet.ETHEREUM : sdk.NetworkWallet[network] const onReadClick = React.useCallback( async (_index: number, item: R) => { try { const response = await LoopringAPI.userAPI?.submitNotificationReadOne( { id: item.id, accountId: account.accountId, }, account?.eddsaKey?.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else { } } catch (error) { let errorItem if (typeof (error as sdk.RESULT_INFO)?.code === 'number') { errorItem = SDK_ERROR_MAP_TO_UI[(error as sdk.RESULT_INFO)?.code ?? 700001] } else { errorItem = SDK_ERROR_MAP_TO_UI[700012] } setToastOpen && setToastOpen({ open: true, type: ToastType.error, content: 'error : ' + errorItem ? t(errorItem.messageKey) : (error as any)?.message, }) } getUserNotify() }, [account.accAddress], ) // setShowLoading(true) const onReadAllClick = React.useCallback(async () => { try { const response = await LoopringAPI.userAPI?.submitNotificationReadAll( { network: networkWallet, accountId: account.accountId, }, account?.eddsaKey?.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else { } getUserNotify() } catch (error) { let errorItem if (typeof (error as sdk.RESULT_INFO)?.code === 'number') { errorItem = SDK_ERROR_MAP_TO_UI[(error as sdk.RESULT_INFO)?.code ?? 700001] } else { errorItem = SDK_ERROR_MAP_TO_UI[700012] } setToastOpen && setToastOpen({ open: true, type: ToastType.error, content: 'error : ' + errorItem ? t(errorItem.messageKey) : (error as any)?.message, }) } }, [account.accAddress]) const onClearAllClick = React.useCallback(async () => { try { const response = await LoopringAPI.userAPI?.submitNotificationClear( { network: networkWallet, accountId: account.accountId, }, account?.eddsaKey?.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else { } getUserNotify() } catch (error) { let errorItem if (typeof (error as sdk.RESULT_INFO)?.code === 'number') { errorItem = SDK_ERROR_MAP_TO_UI[(error as sdk.RESULT_INFO)?.code ?? 700001] } else { errorItem = SDK_ERROR_MAP_TO_UI[700012] } setToastOpen && setToastOpen({ open: true, type: ToastType.error, content: 'error : ' + errorItem ? t(errorItem.messageKey) : (error as any)?.message, }) } }, [account.accAddress]) return { onReadClick, onReadAllClick, onClearAllClick, } } export const useNotification = ({ onReadClick, index, ...rest }: R & { index: number onReadClick: (index: number, rest: any) => void }) => { const history = useHistory() const { messageType, redirectionContext } = rest let ele: any = { i18nKey: '', active: undefined, } const params = JSON.parse(redirectionContext ? redirectionContext : '{}') switch (messageType) { case sdk.NotificationMessageType.L1_CREATED: ele.i18nKey = 'labelActiveL1successfulNote' //Active L1 Account successful ele.active = undefined break case sdk.NotificationMessageType.L2_CREATED: ele.i18nKey = 'labelActiveL2successfulNote' //Active L2 Account successful ele.active = undefined break case sdk.NotificationMessageType.L1_CREATING: ele.i18nKey = 'labelActivatingL1AccountNote' //Active L2 Account successful ele.active = undefined break case sdk.NotificationMessageType.L1_RECEIVE: ele.i18nKey = 'labelL1ReceiveNote' ele.active = undefined break case sdk.NotificationMessageType.L1_SEND: ele.i18nKey = 'labelL1SendNote' ele.active = undefined break case sdk.NotificationMessageType.L2_RECEIVE: ele.i18nKey = 'labelL2ReceiveNote' ele.active = () => { onReadClick(index, rest) history.push(`${RouterPath.l2records}/${RecordTabIndex.Transactions}`) } break case sdk.NotificationMessageType.L2_SEND: ele.i18nKey = 'labelL2SendNote' ele.active = undefined ele.active = () => { onReadClick(index, rest) history.push(`${RouterPath.l2records}/${RecordTabIndex.Transactions}`) } break case sdk.NotificationMessageType.DEPOSIT: ele.i18nKey = 'labelL2DepositNote' ele.active = () => { onReadClick(index, rest) history.push(`${RouterPath.l2records}/${RecordTabIndex.Transactions}`) } break case sdk.NotificationMessageType.WITHDRAW: ele.i18nKey = 'labelL2WithdrawNote' ele.active = () => { onReadClick(index, rest) history.push(`${RouterPath.l2records}/${RecordTabIndex.Transactions}`) } break case sdk.NotificationMessageType.DUAL_SETTLED: ele.i18nKey = 'labelL2DualNote' ele.active = () => { onReadClick(index, rest) history.push( `${RouterPath.l2records}/${RecordTabIndex.DualRecords}?hash=${params.hash} &show=detail`, ) } break case sdk.NotificationMessageType.DUAL_RECURES_ORDER_SWAP: ele.i18nKey = 'labelL2DualNote' ele.active = () => { onReadClick(index, rest) history.push( `${RouterPath.l2records}/${RecordTabIndex.DualRecords}?hash=${params.hash} &show=detail`, ) } break case sdk.NotificationMessageType.DUAL_RECURES_RETRY_FAILED: ele.i18nKey = 'labelL2DualNote' ele.active = () => { onReadClick(index, rest) history.push( `${RouterPath.l2records}/${RecordTabIndex.DualRecords}?hash=${params.hash} &show=detail`, ) } break case sdk.NotificationMessageType.DUAL_RECURES_RETRY_SUCCESS: ele.i18nKey = 'labelL2DualNote' ele.active = () => { onReadClick(index, rest) history.push( `${RouterPath.investBalance}/${InvestAssetRouter.DUAL}?hash=${params.hash} &show=detail`, ) } break case sdk.NotificationMessageType.DUAL_PRICE_ALERT: ele.i18nKey = 'labelL2DualNote' ele.active = () => { onReadClick(index, rest) history.push( `${RouterPath.investBalance}/${InvestAssetRouter.DUAL}?hash=${params.hash} &show=detail`, ) } break default: ele.i18nKey = 'labelNotificationLabel' ele.active = () => { onReadClick(index, rest) history.push( `${RouterPath.investBalance}/${InvestAssetRouter.DUAL}?hash=${params.hash} &show=detail`, ) } break } return ele } ================================================ FILE: packages/core/src/hooks/common/usePairMatch.ts ================================================ import { useRouteMatch } from 'react-router-dom' import { useTokenMap } from '../../stores/token' import { getExistedMarket } from '@loopring-web/loopring-sdk' import { MarketType } from '@loopring-web/common-resources' export function usePairMatch({ path, coinA = 'LRC', coinB = 'ETH', marketArray = undefined, tokenMap = undefined, }: { path: string coinA?: string coinB?: string marketArray?: string[] tokenMap?: any }): { realMarket: MarketType | undefined realPair: any } { const { coinMap, tokenMap: _tokenMap, marketArray: _marketArray } = useTokenMap() if (marketArray) { } else { marketArray = _marketArray tokenMap = _tokenMap } const match: any = useRouteMatch(`${path}/:market`) // const [pair, setPair] = useState<{ coinAInfo: CoinInfo | undefined, coinBInfo: CoinInfo | undefined }>({ coinAInfo: undefined, coinBInfo: undefined}) // const [realMarket, setRealMarket] = useState('') // React.useEffect(()=>{ if (!coinMap || !tokenMap || !marketArray) { return { realMarket: undefined, realPair: undefined } } let market = match?.params?.market // myLog('router pair',market) // let coinA = "LRC"; // // let coinB = "ETH"; let realMarket: MarketType | undefined = `${coinA}-${coinB}` let coinAInfo = coinMap[coinA] let coinBInfo = coinMap[coinB] if (market) { const matchRes = market.match(/(\w+)-(\w+)/i) if (matchRes && matchRes.length >= 3 && coinMap[matchRes[1]] && coinMap[matchRes[2]]) { coinA = matchRes[1] coinB = matchRes[2] } const marketTemp = getExistedMarket(marketArray, coinA, coinB).market if (marketTemp) { realMarket = marketTemp coinAInfo = coinMap[coinA] coinBInfo = coinMap[coinB] } else { if (tokenMap[coinA]) { coinAInfo = coinMap[coinA] coinB = tokenMap[coinA]?.tradePairs[0] coinBInfo = coinMap[coinB] realMarket = `${coinA}-${coinB}` } else { // @ts-ignore ;[_, coinA, coinB] = marketArray[0]?.match(/(\w+)-(\w+)/i) ?? ['', 'LRC', 'ETH'] coinAInfo = coinMap[coinA] coinBInfo = coinMap[coinB] realMarket = `${coinA}-${coinB}` } } } // setPair({ coinAInfo, coinBInfo, }) // setRealMarket(realMarket) // },[]) return { realMarket, realPair: { coinAInfo, coinBInfo }, // setPair, // setMarket, } } export const isTradePairMarket = (tradePair: MarketType, market: MarketType) => { const [_, base, quote] = tradePair.match(/(\w+)-(\w+)/i) ?? [] return ( market.toUpperCase() === `${base}-${quote}`.toUpperCase() || market.toUpperCase() === `${quote}-${base}`.toUpperCase() ) } ================================================ FILE: packages/core/src/hooks/common/useThrottle.ts ================================================ import _ from "lodash" import { useCallback, useEffect, useRef } from "react"; export function useThrottle(cb, delay) { const options = { leading: true, trailing: false }; // add custom lodash options const cbRef = useRef(cb); // use mutable ref to make useCallback/throttle not depend on `cb` dep useEffect(() => { cbRef.current = cb; }); return useCallback( _.throttle((...args) => cbRef.current(...args), delay, options), [delay] ); } ================================================ FILE: packages/core/src/hooks/common/useToast.ts ================================================ import React from 'react' import { TOASTOPEN, ToastType, TOSTOBJECT } from '@loopring-web/component-lib' export const useToast = (): TOSTOBJECT => { const [toastOpen, setToastOpen] = React.useState({ open: false, content: '', type: ToastType.info, }) const closeToast = React.useCallback(() => { setToastOpen((state) => { return { ...state, content: '', open: false, } }) }, [setToastOpen]) return { toastOpen, setToastOpen, closeToast, } } ================================================ FILE: packages/core/src/hooks/common/useTrade.ts ================================================ import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { AccountStatus, defaultSlipage, getValuePrecisionThousand, myError, myLog, } from '@loopring-web/common-resources' import { DAYS, getTimestampDaysLater, MAPFEEBIPS, OrderInfoPatch, store, useAccount, useAmmMap, useSystem, useTicker, useTokenMap, } from '../../index' import * as _ from 'lodash' import BigNumber from 'bignumber.js' export const DefaultFeeBips = '1' export enum PriceLevel { Normal, Lv1, Lv2, } export type ReqParams = { isBuy?: boolean price?: number amountBase?: number amountQuote?: number base?: string quote?: string market?: string tokenMap?: sdk.LoopringMap marketArray?: string[] marketMap?: any exchangeAddress?: string accountId?: number storageId?: number feeBips?: string // key is ETH or USDT tokenAmtMap?: { [key: string]: sdk.TokenAmount } tokenMarketMap?: { [key: string]: sdk.TokenAmount } ammPoolSnapshot?: sdk.AmmPoolSnapshot depth?: sdk.DepthData slippage?: string } export function makeMarketReq({ isBuy, amountBase, amountQuote, base, quote, tokenMap, marketArray, marketMap, exchangeAddress, accountId, storageId, feeBips, // tokenAmtMap, tokenMarketMap, depth, ammPoolSnapshot, slippage = (defaultSlipage * 100).toString(), }: ReqParams) { if ( !tokenMap || !exchangeAddress || !marketArray || accountId === undefined || !base || !quote || (!depth && !ammPoolSnapshot) ) { return { sellUserOrderInfo: undefined, buyUserOrderInfo: undefined, minOrderInfo: undefined, calcTradeParams: undefined, marketRequest: undefined, } } if (isBuy === undefined) { isBuy = true } if (feeBips === undefined) { feeBips = DefaultFeeBips } if (!storageId) { storageId = 0 } const baseTokenInfo = tokenMap[base] const quoteTokenInfo = tokenMap[quote] const sellTokenInfo = isBuy ? quoteTokenInfo : baseTokenInfo const buyTokenInfo = isBuy ? baseTokenInfo : quoteTokenInfo let input = ( amountBase !== undefined ? amountBase : amountQuote !== undefined ? amountQuote : 0 )?.toString() const sell = sellTokenInfo.symbol const buy = buyTokenInfo.symbol // buy. amountSell is not null. const isAtoB = (isBuy && amountQuote !== undefined) || (!isBuy && amountBase !== undefined) const calcTradeParams = sdk.getOutputAmount({ input, sell, buy, isAtoB, marketArr: marketArray, tokenMap: tokenMap as any, marketMap: marketMap as any, depth: depth as sdk.DepthData, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: slippage, }) let minOrderInfo, totalFeeRaw, totalFee, tradeCost, feeTakerRate, maxFeeBips: number = MAPFEEBIPS, buyUserOrderInfo, sellUserOrderInfo if (tokenMarketMap && slippage) { buyUserOrderInfo = tokenMarketMap[buy] ? tokenMarketMap[buy].userOrderInfo : undefined sellUserOrderInfo = tokenMarketMap[sell] ? tokenMarketMap[sell].userOrderInfo : undefined const minSymbol = buy const inputAmount = buyUserOrderInfo const minInput = sdk .toBig(inputAmount?.minAmount ?? '') .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div('1e' + buyTokenInfo.decimals) .toString() feeTakerRate = buyUserOrderInfo?.takerRate const calcForMinAmt = sdk.getOutputAmount({ input: minInput, sell, buy, isAtoB: false, marketArr: marketArray, tokenMap: tokenMap as any, marketMap: marketMap as any, depth: depth as sdk.DepthData, ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: slippage, }) myLog( `inputAmount ${minSymbol} minAmount:`, inputAmount?.minAmount, `, Market minAmount: with slippage:${slippage}:`, sdk .toBig(inputAmount?.minAmount ?? '') .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .toString(), `, dustToken:`, sell, ) /*** calc for Price Impact ****/ const sellMinAmtInfo = tokenMarketMap[sellTokenInfo.symbol] const sellMinAmtInput = sdk .toBig(sellMinAmtInfo.baseOrderInfo.minAmount) .div('1e' + sellTokenInfo.decimals) .toString() const calcForPriceImpact = sdk.getOutputAmount({ input: sellMinAmtInput, sell, buy, isAtoB: true, marketArr: marketArray, tokenMap: tokenMap as any, marketMap: marketMap as any, depth: depth as sdk.DepthData, ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: '10', }) myLog( 'calcForPriceImpact input:', sellMinAmtInput, ', calcForPriceImpact basePrice: ', sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput).toNumber(), ) const basePrice = sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput) const tradePrice = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(isAtoB ? input.toString() : calcTradeParams?.output) const priceImpact = sdk .toBig(1) .minus(sdk.toBig(tradePrice).div(basePrice ?? 1)) .minus(0.001) if (calcTradeParams && priceImpact.gte(0)) { calcTradeParams.priceImpact = priceImpact.toFixed(4, 1) } else { calcTradeParams && (calcTradeParams.priceImpact = '0') } myLog( 'calcTradeParams input:', input.toString(), ', calcTradeParams Price: ', sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(input.toString()) .toNumber(), `isBuy:${isAtoB}, ${isAtoB ? input.toString() : calcTradeParams?.output} tradePrice: `, tradePrice.toString(), 'basePrice: ', basePrice?.toString(), 'toBig(tradePrice).div(basePrice)', sdk .toBig(tradePrice) .div(basePrice ?? 1) .toNumber(), 'priceImpact (1-tradePrice/basePrice) - 0.001', priceImpact.toNumber(), 'priceImpact view', calcTradeParams?.priceImpact, ) /**** calc for min Cost ****/ tradeCost = tokenMarketMap[buy].tradeCost let dustToken = tokenMap[buy] let sellToken = tokenMap[sell] let calcForMinCostInput = BigNumber.max( sdk.toBig(tradeCost).times(2), dustToken.orderAmounts.dust, ) myLog(dustToken.symbol) const tradeCostInput = sdk .toBig(calcForMinCostInput) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div('1e' + tokenMap[buy].decimals) .toString() const calcForMinCost = sdk.getOutputAmount({ input: tradeCostInput, sell, buy, isAtoB: false, marketArr: marketArray, tokenMap: tokenMap as any, marketMap: marketMap as any, depth: depth as sdk.DepthData, ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: slippage, }) const minAmt = BigNumber.max(sellToken.orderAmounts.dust, calcForMinCost?.amountS ?? 0) minOrderInfo = { minAmount: minAmt, minAmtShow: minAmt && sdk .toBig(minAmt) .times(1.1) .div('1e' + tokenMap[sell].decimals) .toNumber(), symbol: sell, minAmtCheck: sdk.toBig(calcTradeParams?.amountS ?? 0).gte(sdk.toBig(minAmt).times(1.1) ?? 0), } if ( tradeCost && calcTradeParams && calcTradeParams.amountBOutSlip?.minReceived && feeTakerRate ) { let value = sdk .toBig(calcTradeParams.amountBOutSlip?.minReceived) .times(feeTakerRate) .div(10000) myLog( 'input Accounts', calcTradeParams?.amountS, '100 U calcForMinAmt:', calcForMinAmt?.amountS, ) let validAmt = !!( calcTradeParams?.amountS && calcForMinAmt?.amountS && sdk.toBig(calcTradeParams?.amountS).gte(calcForMinAmt.amountS) ) myLog( `${minSymbol} tradeCost:`, tradeCost, 'useTakeRate Fee:', value.toString(), 'calcForMinAmt?.amountS:', calcForMinAmt?.amountS, `is setup minTrade amount, ${calcForMinAmt?.amountS}:`, validAmt, ) if (!validAmt) { if (sdk.toBig(tradeCost).gte(value)) { totalFeeRaw = sdk.toBig(tradeCost) } else { totalFeeRaw = value } myLog( 'maxFeeBips update for tradeCost before value:', maxFeeBips, 'totalFeeRaw', totalFeeRaw.toString(), ) maxFeeBips = Math.ceil( totalFeeRaw.times(10000).div(calcTradeParams.amountBOutSlip?.minReceived).toNumber(), ) myLog('maxFeeBips update for tradeCost after value:', maxFeeBips) } else { totalFeeRaw = sdk.toBig(value) } totalFee = getValuePrecisionThousand( totalFeeRaw.div('1e' + tokenMap[minSymbol].decimals).toString(), tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, false, { floor: true }, ) tradeCost = getValuePrecisionThousand( sdk .toBig(tradeCost) .div('1e' + tokenMap[minSymbol].decimals) .toString(), tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, false, { floor: true }, ) myLog('totalFee view value:', totalFee, tradeCost) myLog('tradeCost view value:', tradeCost) } } else { myError('undefined minOrderInfo') } const tradeChannel = calcTradeParams ? calcTradeParams.exceedDepth ? sdk.TradeChannel.BLANK : sdk.TradeChannel.MIXED : undefined const orderType = calcTradeParams ? calcTradeParams.exceedDepth ? sdk.OrderType.ClassAmm : sdk.OrderType.TakerOnly : undefined const sellTokenVol3: sdk.TokenVolumeV3 = { tokenId: sellTokenInfo.tokenId, volume: calcTradeParams?.amountS as string, } const buyTokenVol3: sdk.TokenVolumeV3 = { tokenId: buyTokenInfo.tokenId, volume: calcTradeParams?.amountBOutSlip.minReceived as string, } const marketRequest: sdk.SubmitOrderRequestV3 = { exchange: exchangeAddress, accountId, storageId, sellToken: sellTokenVol3, buyToken: buyTokenVol3, allOrNone: false, validUntil: getTimestampDaysLater(DAYS), maxFeeBips, fillAmountBOrS: false, // amm only false orderType, tradeChannel, eddsaSignature: '', } return { sellUserOrderInfo, buyUserOrderInfo, minOrderInfo, calcTradeParams: { ...calcTradeParams, maxFeeBips, }, marketRequest, totalFee, maxFeeBips, feeTakerRate, tradeCost, } } export function makeLimitReq({ isBuy, depth, price, amountBase, amountQuote, base, quote, tokenMap, exchangeAddress, accountId, storageId, feeBips, tokenAmtMap, }: ReqParams) { if ( !tokenMap || !exchangeAddress || !depth || accountId === undefined || !base || !quote || (!amountBase && !amountQuote) ) { myLog('got empty input!') return { sellUserOrderInfo: undefined, buyUserOrderInfo: undefined, calcTradeParams: undefined, limitRequest: undefined, } } if (price === undefined) { price = 0 } if (isBuy === undefined) { isBuy = true } if (feeBips === undefined) { feeBips = DefaultFeeBips } if (!storageId) { storageId = 0 } const baseTokenInfo = tokenMap[base] const quoteTokenInfo = tokenMap[quote] const sellTokenInfo = isBuy ? quoteTokenInfo : baseTokenInfo const buyTokenInfo = isBuy ? baseTokenInfo : quoteTokenInfo const sell = sellTokenInfo.symbol const buy = buyTokenInfo.symbol const sellUserOrderInfo = tokenAmtMap && tokenAmtMap[sell] ? tokenAmtMap[sell].userOrderInfo : undefined const buyUserOrderInfo = tokenAmtMap && tokenAmtMap[buy] ? tokenAmtMap[buy].userOrderInfo : undefined let baseVol: any = undefined let quoteVol: any = undefined let baseVolShow: any = undefined let quoteVolShow: any = undefined if (amountBase !== undefined) { baseVolShow = amountBase baseVol = sdk.toBig(baseVolShow).times('1e' + baseTokenInfo.decimals) quoteVolShow = sdk.toBig(amountBase).times(sdk.toBig(price)).toFixed(quoteTokenInfo.precision) quoteVol = sdk .toBig(amountBase) .times(sdk.toBig(price)) .times('1e' + quoteTokenInfo.decimals) } else if (amountQuote !== undefined) { baseVolShow = sdk.toBig(amountQuote).div(sdk.toBig(price)).toFixed(baseTokenInfo.precision) baseVol = sdk .toBig(amountQuote) .div(sdk.toBig(price)) .times('1e' + baseTokenInfo.decimals) quoteVolShow = amountQuote quoteVol = sdk.toBig(amountQuote).times('1e' + quoteTokenInfo.decimals) } else { throw Error('no amount info!') } const baseTokenVol3: sdk.TokenVolumeV3 = { tokenId: baseTokenInfo.tokenId, volume: baseVol.toFixed(0, 0), } const quoteTokenVol3: sdk.TokenVolumeV3 = { tokenId: quoteTokenInfo.tokenId, volume: quoteVol.toFixed(0, 0), } let minOrderInfo: (sdk.OrderInfo & OrderInfoPatch) | undefined = undefined if (sellUserOrderInfo && buyUserOrderInfo) { if (!isBuy) { minOrderInfo = _.cloneDeep(buyUserOrderInfo) const minAmount = sdk .toBig(minOrderInfo.minAmount) .div('1e' + buyTokenInfo.decimals) .div(sdk.toBig(price)) minOrderInfo.minAmtShow = minAmount.toNumber() minOrderInfo.minAmount = minAmount.times('1e' + sellTokenInfo.decimals).toString() minOrderInfo.symbol = sell minOrderInfo.minAmtCheck = baseVol.gte(sdk.toBig(minOrderInfo.minAmount)) } else { minOrderInfo = _.cloneDeep(buyUserOrderInfo) minOrderInfo.minAmtShow = sdk .toBig(minOrderInfo.minAmount) .div('1e' + buyTokenInfo.decimals) .toNumber() minOrderInfo.symbol = buy minOrderInfo.minAmtCheck = baseVol.gte(sdk.toBig(minOrderInfo.minAmount)) } } else { // throw Error('undefined minOrderInfo') myError('undefined minOrderInfo') } const takerRate = tokenAmtMap && tokenAmtMap[baseTokenInfo.symbol] ? tokenAmtMap[baseTokenInfo.symbol].userOrderInfo.takerRate : 0 const maxFeeBips = parseInt(sdk.toBig(feeBips).plus(sdk.toBig(takerRate)).toString()) const sellToken = isBuy ? quoteTokenVol3 : baseTokenVol3 const buyToken = isBuy ? baseTokenVol3 : quoteTokenVol3 const limitRequest: sdk.SubmitOrderRequestV3 = { exchange: exchangeAddress, accountId, storageId, sellToken, buyToken, allOrNone: false, validUntil: getTimestampDaysLater(DAYS), maxFeeBips: MAPFEEBIPS, fillAmountBOrS: false, // amm only false orderType: sdk.OrderType.LimitOrder, tradeChannel: sdk.TradeChannel.MIXED, eddsaSignature: '', } // myLog('limitRequest:', limitRequest) let priceImpact = 0 const ask1 = depth.asks_prices[0] const bid1 = depth.bids_prices[depth.bids_prices.length - 1] if (isBuy && ask1 && price > ask1) { priceImpact = (price - ask1) / ask1 } else if (!isBuy && bid1 && price < bid1) { priceImpact = (bid1 - price) / bid1 } const calcTradeParams = { isBuy, priceImpact, baseVol: baseVol.toFixed(), baseVolShow, quoteVol: quoteVol.toFixed(), quoteVolShow, takerRate, feeBips, maxFeeBips, } return { sellUserOrderInfo, buyUserOrderInfo, minOrderInfo, calcTradeParams, limitRequest, } } export function usePlaceOrder() { const { account } = useAccount() const { tokenMap, marketArray } = useTokenMap() const { tickerMap } = useTicker() const { ammMap } = useAmmMap() const { exchangeInfo } = useSystem() const getTokenAmtMap = React.useCallback( (params: ReqParams) => { const { amountMap } = store.getState().amountMap if (ammMap && marketArray && amountMap) { let base = params.base let quote = params.quote let market = params.market let ammMarket: string if (market) { const result = market.match(/([\w,#]+)-([\w,#]+)/i) if (result) { ;[, base, quote] = result } } const existedMarket = sdk.getExistedMarket(marketArray, base, quote) params.base = existedMarket.baseShow params.quote = existedMarket.quoteShow market = existedMarket.market ammMarket = existedMarket.amm as string const tokenAmtMap = amountMap ? ammMap[ammMarket] ? amountMap[ammMarket] : amountMap[market as string] : undefined const tokenMarketMap = amountMap ? amountMap[market as string] : undefined const feeBips = ammMap[ammMarket] ? ammMap[ammMarket].__rawConfig__.feeBips : 0 return { feeBips, tokenAmtMap, tokenMarketMap, } } else { return { feeBips: undefined, tokenAmtMap: undefined, tokenMarketMap: undefined, } } }, [ammMap, marketArray], ) // {isBuy, amountB or amountS, (base, quote / market), feeBips, takerRate, depth, ammPoolSnapshot, slippage, } const makeMarketReqInHook = React.useCallback( (params: ReqParams) => { const { tokenAmtMap, tokenMarketMap, feeBips } = getTokenAmtMap(params) // myLog('makeMarketReqInHook tokenAmtMap:', tokenAmtMap, feeBips) if (exchangeInfo) { const fullParams: ReqParams = { ...params, exchangeAddress: exchangeInfo.exchangeAddress, accountId: account.accountId, tokenMap, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, tokenAmtMap, tokenMarketMap, } return makeMarketReq(fullParams) } else { return { sellUserOrderInfo: undefined, buyUserOrderInfo: undefined, minOrderInfo: undefined, calcTradeParams: undefined, marketRequest: undefined, } } }, [getTokenAmtMap, exchangeInfo, account.accountId, tokenMap], ) // {isBuy, price, amountB or amountS, (base, quote / market), feeBips, takerRate, } const makeLimitReqInHook = React.useCallback( (params: ReqParams) => { const { tokenAmtMap, feeBips } = getTokenAmtMap(params) myLog('makeLimitReqInHook tokenAmtMap:', tokenAmtMap, feeBips) if (exchangeInfo) { const fullParams: ReqParams = { ...params, exchangeAddress: exchangeInfo.exchangeAddress, accountId: account.accountId, tokenMap, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, tokenAmtMap: tokenAmtMap, } return makeLimitReq(fullParams) } else { myLog('makeLimitReqInHook error no tokenAmtMap', tokenAmtMap) return { sellUserOrderInfo: undefined, buyUserOrderInfo: undefined, minOrderInfo: undefined, calcTradeParams: undefined, limitRequest: undefined, } } }, [getTokenAmtMap, exchangeInfo, account.accountId, tokenMap], ) const makeStopLimitReqInHook = React.useCallback( (params: T) => { const { tokenAmtMap, feeBips } = getTokenAmtMap(params) const { tickerMap } = store.getState().tickerMap myLog('makeLimitReqInHook tokenAmtMap:', tokenAmtMap, feeBips) let sellUserOrderInfo: any = undefined, buyUserOrderInfo: any = undefined, minOrderInfo: any = undefined, calcTradeParams: any = undefined, stopLimitRequest: any = undefined, stopSide: any = undefined if (exchangeInfo && params?.depth?.symbol && params.quote && tickerMap) { const ticker = tickerMap[params.depth.symbol] let midStopPrice = ticker?.close if (params.stopLimitPrice == undefined) { params.stopLimitPrice = 0 } stopSide = midStopPrice ? sdk.toBig(params.stopLimitPrice).lte(midStopPrice) ? sdk.STOP_SIDE.LESS_THAN_AND_EQUAL : sdk.STOP_SIDE.GREAT_THAN_AND_EQUAL : undefined myLog( 'stopSide', stopSide, 'stopLimitPrice', midStopPrice, 'stopLimitPrice', params.stopLimitPrice, ) const fullParams: T = { ...params, exchangeAddress: exchangeInfo.exchangeAddress, accountId: account.accountId, tokenMap, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, tokenAmtMap: tokenAmtMap, } const result = makeLimitReq(fullParams) sellUserOrderInfo = result.sellUserOrderInfo buyUserOrderInfo = result.buyUserOrderInfo minOrderInfo = result.minOrderInfo calcTradeParams = result.calcTradeParams stopLimitRequest = { ...result.limitRequest, stopPrice: params.stopLimitPrice, stopSide, extraOrderType: sdk.EXTRAORDER_TYPE.STOP_LIMIT, } } else { myLog('makeLimitReqInHook error no tokenAmtMap', tokenAmtMap) } return { // stopRange, sellUserOrderInfo, buyUserOrderInfo, minOrderInfo, calcTradeParams, stopLimitRequest, } }, [getTokenAmtMap, exchangeInfo, account.accountId, tokenMap, tickerMap], ) return { makeMarketReqInHook, makeLimitReqInHook, makeStopLimitReqInHook, } } export const getPriceImpactInfo = ( calcTradeParams: any, accountStatus: keyof typeof AccountStatus | 'unknown', isMarket: boolean = true, ) => { let priceImpact: any = calcTradeParams?.priceImpact ? parseFloat(calcTradeParams?.priceImpact) * 100 : undefined let priceImpactColor = 'var(--color-success)' let priceLevel = PriceLevel.Normal if (isMarket) { if (priceImpact) { if (priceImpact > 0.1 && priceImpact <= 1) { priceImpactColor = 'var(--color-success)' } else if (priceImpact > 1 && priceImpact <= 3) { priceImpactColor = 'textPrimary' } else if (priceImpact > 3 && priceImpact <= 5) { priceImpactColor = 'var(--color-warning)' } else if (priceImpact > 5 && priceImpact <= 10) { priceImpactColor = 'var(--color-error)' priceLevel = PriceLevel.Lv1 } else if (priceImpact > 10) { priceImpactColor = 'var(--color-error)' priceLevel = PriceLevel.Lv2 } } else { priceImpactColor = 'var(--color-text-primary)' } } else { if (priceImpact > 10) { priceImpactColor = 'var(--color-error)' priceLevel = PriceLevel.Lv1 } } return { value: priceImpact, priceImpactColor: accountStatus === AccountStatus.ACTIVATED ? priceImpactColor : 'var(--color-text-primary)', priceLevel, } } ================================================ FILE: packages/core/src/hooks/common/useUserWallets.ts ================================================ import React from 'react' import { SagaStatus } from '@loopring-web/common-resources' import { useWalletLayer1, useWalletLayer2, useVaultLayer2,store } from '../../stores' export const useUserWallets = (): { updateUserWallets: () => void } => { const { updateWalletLayer1 } = useWalletLayer1() const { updateWalletLayer2 } = useWalletLayer2() const { updateVaultLayer2 } = useVaultLayer2() const updateUserWallets = React.useCallback(() => { const walletLayer1Status = store.getState().walletLayer1.status const walletLayer2Status = store.getState().walletLayer2.status const vaultLayer2Status = store.getState().vaultLayer2.status if (walletLayer1Status !== SagaStatus.PENDING) { updateWalletLayer1() } if (walletLayer2Status !== SagaStatus.PENDING) { updateWalletLayer2() } if (vaultLayer2Status !== SagaStatus.PENDING) { updateVaultLayer2({}) } }, [ updateWalletLayer1, updateWalletLayer2, updateVaultLayer2, ]) return { updateUserWallets } } ================================================ FILE: packages/core/src/hooks/help/coinPairInit.ts ================================================ export function coinPairInit({ coinKey, _tradeCalcData, tokenMap, coinMap }: any) { if (coinKey) { const [_match, sell, buy] = coinKey.match(/(\w+)-(\w+)/i) // @ts-ignore if (sell && coinMap && coinMap[sell]) { _tradeCalcData.coinSell = sell } // @ts-ignore if ( sell !== buy && buy && -1 !== tokenMap[sell].tradePairs.findIndex((ele: any) => ele === buy) ) { _tradeCalcData.coinBuy = buy return _tradeCalcData } } if (!_tradeCalcData.coinSell || _tradeCalcData.coinSell === '') { _tradeCalcData.coinSell = 'LRC' _tradeCalcData.coinBuy = 'ETH' return _tradeCalcData } if ( !_tradeCalcData.coinBuy || _tradeCalcData.coinBuy === '' || _tradeCalcData.coinBuy === 'undefined' ) { // @ts-ignore if (tokenMap && tokenMap[_tradeCalcData.coinSell]?.tradePairs) { _tradeCalcData.coinBuy = tokenMap[_tradeCalcData.coinSell]?.tradePairs[0] } else { _tradeCalcData.coinSell = 'LRC' _tradeCalcData.coinBuy = 'ETH' } } return _tradeCalcData } ================================================ FILE: packages/core/src/hooks/help/connectStatusCallback.tsx ================================================ import { AccountStep, setShowAccount, setShowWrongNetworkGuide, } from '@loopring-web/component-lib' import { AccountStatus, fnType, myLog, SPECIAL_ACTIVATION_NETWORKS } from '@loopring-web/common-resources' import { accountReducer, store, unlockAccount, appKit } from '../../index' import _ from 'lodash' import { ChainId } from '@loopring-web/loopring-sdk' export const accountStaticCallBack = ( onclickMap: { [key: number]: [fn: (props: any) => any, args?: any[]] }, deps?: any[], ) => { const { readyState } = store.getState().account let fn, args ;[fn, args] = onclickMap[readyState] ? onclickMap[readyState] : [] if (typeof fn === 'function') { args = [...(args ?? []), ...(deps ?? [])] as [props: any] return fn.apply(this, args) } } const activeBtnLabelFn = function (options?: {chainId: ChainId, isEarn: boolean, readyState: AccountStatus}) { const isSpecialActivation = options?.isEarn && SPECIAL_ACTIVATION_NETWORKS.includes(options.chainId) if (isSpecialActivation) { return options?.readyState === AccountStatus.NOT_ACTIVE ? 'Complete Sign in' : 'labelDeposit' } else { return 'labelDepositAndActiveBtn' } } export const btnLabel = { [fnType.UN_CONNECT]: [ function () { return `labelConnectWallet` }, ], [fnType.ERROR_NETWORK]: [ function () { return `labelWrongNetwork` }, ], [fnType.NO_ACCOUNT]: [activeBtnLabelFn], [fnType.DEFAULT]: [activeBtnLabelFn], [fnType.DEPOSITING]: [activeBtnLabelFn], [fnType.NOT_ACTIVE]: [activeBtnLabelFn], [fnType.ACTIVATED]: [ function () { return undefined }, ], [fnType.LOCKED]: [ function () { return `labelUnLockLayer2` }, ], } export const goActiveAccount = async (options?: { chainId: ChainId isEarn: boolean readyState: AccountStatus specialActivation: () => Promise specialActivationDeposit: () => Promise }) => { const isSpecialActivation = options?.isEarn && SPECIAL_ACTIVATION_NETWORKS.includes(options.chainId) if (isSpecialActivation) { if (options.readyState === AccountStatus.NOT_ACTIVE) { return options.specialActivation() } else { return options.specialActivationDeposit() } } else { store.dispatch(accountReducer.changeShowModel({ _userOnModel: false })) store.dispatch(setShowAccount({ isShow: true, step: AccountStep.CreateAccount_EOA_Only_Alert })) } } export const geDepositingActive = () => { // const { system, localStore, account } = store.getState(); // const isDepositing = // localStore.chainHashInfos[system?.chainId].depositHashes[ // account?.accAddress // ]; // if(isDepositing){ // // }else{ // // } } export const btnClickMap: { [key: string]: [fn: (props: any) => any, args?: any[]] } = { [fnType.ERROR_NETWORK]: [ function () { store.dispatch(accountReducer.changeShowModel({ _userOnModel: false })) store.dispatch(setShowWrongNetworkGuide({ isShow: true })) }, ], [fnType.UN_CONNECT]: [ function () { myLog('UN_CONNECT!') appKit.open() }, ], [fnType.NO_ACCOUNT]: [goActiveAccount], [fnType.DEPOSITING]: [goActiveAccount], [fnType.NOT_ACTIVE]: [goActiveAccount], [fnType.LOCKED]: [ async function () { unlockAccount() store.dispatch(accountReducer.changeShowModel({ _userOnModel: true })) }, ], } export const btnConnectL1kMap = Object.assign(_.cloneDeep(btnClickMap), { [fnType.ACTIVATED]: [ function () { appKit.open() }, ], [fnType.NO_ACCOUNT]: [ function () { appKit.open() }, ], [fnType.DEPOSITING]: [ function () { appKit.open() }, ], [fnType.NOT_ACTIVE]: [ function () { appKit.open() }, ], [fnType.LOCKED]: [ function () { appKit.open() }, ], }) ================================================ FILE: packages/core/src/hooks/help/formatPrice.ts ================================================ import { getExistedMarket, toBig } from '@loopring-web/loopring-sdk' import { store } from '../../index' export function formatedVal(rawData: string, base: string, quote: string) { const { marketMap, marketArray } = store.getState().tokenMap if (!rawData || !base || !quote || !marketMap || !marketArray) { return '' } const { market } = getExistedMarket(marketArray, base, quote) const marketInfo = marketMap[market] const showVal = toBig(rawData).toFixed(marketInfo.precisionForPrice) return showVal } ================================================ FILE: packages/core/src/hooks/help/index.ts ================================================ export * from './coinPairInit' export * from './connectStatusCallback' export * from './makeCache' export * from './makeTickView' export * from './makeUIAmmActivityMap' export * from './makeWallet' export * from './marketTable' export * from './volumeToCount' export * from './useAmmTotalValue' export * from './marketPairHelp' export * from './makeInvest' export * from './makeDual' export * from './marketRedPacket' export * from './makeDefiSideStaking' export * from './providorConnect' export * from './makeMarketTrend' export {parseRabbitConfig, parseRabbitConfig2, networkById} from './parseRabbitConfig' ================================================ FILE: packages/core/src/hooks/help/makeCache.ts ================================================ import { store } from '../../index' import { setSlippage } from '@loopring-web/component-lib' type Cache = { customSlippage?: number } export const makeCache = (__cache__: Cache) => { if (typeof __cache__.customSlippage !== undefined) { store.dispatch(setSlippage(__cache__.customSlippage as number)) } } ================================================ FILE: packages/core/src/hooks/help/makeDefiSideStaking.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import { DUAL_TYPE, toBig } from '@loopring-web/loopring-sdk' import { DeFiSideCalcData, DeFiSideRedeemCalcData, DualViewInfo, DualViewOrder, getValuePrecisionThousand, IBData, myLog, } from '@loopring-web/common-resources' import moment from 'moment' import { BigNumber } from 'bignumber.js' export const makeDefiSideStaking = ( info: sdk.DualProductAndPrice, index: sdk.DualIndex, rule: sdk.DualRulesCoinsInfo, sellSymbol: string, buySymbol: string, market: sdk.DefiMarketInfo, ): DualViewInfo => { const { expireTime, strike, ratio, profit, dualType } = info const { precisionForPrice } = market myLog('makeDualViewItem', expireTime, strike, ratio, dualType) const [base, quote] = dualType.toUpperCase() === DUAL_TYPE.DUAL_BASE ? [sellSymbol, buySymbol] : [buySymbol, sellSymbol] const settleRatio = toBig(profit).times(ratio).toFixed(6, BigNumber.ROUND_DOWN) const apy = toBig(settleRatio) .div((expireTime - Date.now()) / 86400000) .times(36500) // year APY const term = moment().to(new Date(expireTime), true) return { apy: (getValuePrecisionThousand(apy, 2, 2, 2, true) + '%') as any, settleRatio, term, strike, isUp: dualType.toUpperCase() === DUAL_TYPE.DUAL_BASE ? true : false, productId: info.productId, expireTime, currentPrice: { base, quote, precisionForPrice, currentPrice: Number(index.index), quoteUnit: quote, //index.quote, }, sellSymbol, buySymbol, __raw__: { info, index, rule, }, } } export const calcSideStaking = ({ inputValue, deFiSideCalcData, tokenSell, }: { inputValue: string isJoin: true deFiSideCalcData: DeFiSideCalcData tokenSell: sdk.TokenInfo }): { minSellVol: string | undefined maxSellVol: string | undefined sellVol: string isJoin: true deFiSideCalcData: DeFiSideCalcData } => { const sellVol = sdk.toBig(inputValue ? inputValue : 0).times('1e' + tokenSell.decimals) let dalyEarn: undefined | string = undefined if ( inputValue && deFiSideCalcData.stakeViewInfo.apr && deFiSideCalcData.stakeViewInfo.apr !== '' && deFiSideCalcData.stakeViewInfo.apr !== '0.00' ) { dalyEarn = sdk.toBig(sellVol).times(deFiSideCalcData.stakeViewInfo.apr).div(365).toString() } else { dalyEarn = undefined } const maxSellAmount = sdk .toBig(deFiSideCalcData.stakeViewInfo.maxAmount) .div('1e' + tokenSell.decimals) .toString() const minSellAmount = sdk .toBig(deFiSideCalcData.stakeViewInfo.minAmount) .div('1e' + tokenSell.decimals) .toString() if (deFiSideCalcData.stakeViewInfo.symbol) { return { sellVol: sellVol.toString(), deFiSideCalcData: { ...deFiSideCalcData, stakeViewInfo: { ...deFiSideCalcData.stakeViewInfo, minSellVol: deFiSideCalcData.stakeViewInfo.minAmount, maxSellVol: deFiSideCalcData.stakeViewInfo.maxAmount, maxSellAmount, minSellAmount, dalyEarn, }, }, isJoin: true, minSellVol: deFiSideCalcData.stakeViewInfo.minAmount, maxSellVol: deFiSideCalcData.stakeViewInfo.maxAmount, } } else { return { sellVol: sellVol.toString(), deFiSideCalcData: { ...deFiSideCalcData, stakeViewInfo: { ...deFiSideCalcData?.stakeViewInfo, minSellVol: undefined, maxSellVol: undefined, maxSellAmount, minSellAmount, dalyEarn, }, }, isJoin: true, minSellVol: undefined, maxSellVol: undefined, } } } export const calcRedeemStaking = , R>({ inputValue, // isJoin, deFiSideRedeemCalcData: { stakeViewInfo, coinSell, ...rest }, tokenSell, }: { inputValue: string isJoin: false deFiSideRedeemCalcData: DeFiSideRedeemCalcData tokenSell: sdk.TokenInfo }): { minSellVol: string | undefined maxSellVol: string | undefined sellVol: string isJoin: boolean deFiSideRedeemCalcData: DeFiSideRedeemCalcData } => { let sellVol if (inputValue?.toString() == coinSell.balance?.toString()) { sellVol = (stakeViewInfo as any).remainAmount } else { sellVol = sdk.toBig(inputValue ? inputValue : 0).times('1e' + tokenSell.decimals) } const maxSellAmount = sdk .toBig((stakeViewInfo as any)?.maxAmount) .div('1e' + tokenSell.decimals) .toString() const minSellAmount = sdk .toBig((stakeViewInfo as any).minAmount) .div('1e' + tokenSell.decimals) .toString() if ((stakeViewInfo as any).symbol) { return { sellVol: sellVol.toString(), deFiSideRedeemCalcData: { ...rest, coinSell, stakeViewInfo: { ...(stakeViewInfo as any), minSellVol: (stakeViewInfo as any).minAmount, maxSellVol: (stakeViewInfo as any).maxAmount, maxSellAmount, minSellAmount, }, }, isJoin: true, minSellVol: (stakeViewInfo as any).minAmount, maxSellVol: (stakeViewInfo as any).maxAmount, } } else { return { sellVol: sellVol.toString(), deFiSideRedeemCalcData: { ...rest, coinSell, stakeViewInfo: { ...(stakeViewInfo as any), minSellVol: undefined, maxSellVol: undefined, maxSellAmount, minSellAmount, }, }, isJoin: true, minSellVol: undefined, maxSellVol: undefined, } } } ================================================ FILE: packages/core/src/hooks/help/makeDual.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import { DUAL_TYPE, toBig } from '@loopring-web/loopring-sdk' import { DualViewInfo, DualViewOrder, getValuePrecisionThousand, } from '@loopring-web/common-resources' import moment from 'moment' import { BigNumber } from 'bignumber.js' export const makeDualViewItem = ( info: sdk.DualProductAndPrice, index: sdk.DualIndex, rule: sdk.DualRulesCoinsInfo, sellSymbol: string, buySymbol: string, market: sdk.DefiMarketInfo, ): DualViewInfo => { const { expireTime, strike, ratio, profit, dualType } = info // @ts-ignore const { precisionForPrice, stepLength } = market // myLog('makeDualViewItem', expireTime, strike, ratio, dualType) const [base, quote] = dualType.toUpperCase() === DUAL_TYPE.DUAL_BASE ? [sellSymbol, buySymbol] : [buySymbol, sellSymbol] const settleRatio = toBig(profit).times(ratio).toFixed(6, BigNumber.ROUND_DOWN) const apy = toBig(settleRatio) .div((expireTime - Date.now()) / 86400000) .times(36500) // year APY const term = moment().to(new Date(expireTime), true) return { apy: (getValuePrecisionThousand(apy, 2, 2, 2, true) + '%') as any, settleRatio, term, strike, isUp: dualType.toUpperCase() === DUAL_TYPE.DUAL_BASE ? true : false, productId: info.productId, expireTime, currentPrice: { base, quote, precisionForPrice, currentPrice: Number(index.index), quoteUnit: quote, //quote index.quote, }, sellSymbol, buySymbol, stepLength, __raw__: { info, index, rule, }, } } export const makeDualOrderedItem = ( props: sdk.UserDualTxsHistory, sellSymbol: string, buySymbol: string, currentPrice: number, market: sdk.DefiMarketInfo, ): DualViewOrder => { const { settleRatio, dualType, strike, productId, createdAt, timeOrigin: { expireTime }, } = props const [base, quote] = dualType.toUpperCase() === DUAL_TYPE.DUAL_BASE ? [sellSymbol, buySymbol] : [buySymbol, sellSymbol] const apy = toBig(settleRatio) .div((expireTime - createdAt) / 86400000) .times(36500) // year APY const term = moment().to(new Date(expireTime), true) const { precisionForPrice, stepLength } = market ?? {} return { apy: (getValuePrecisionThousand(apy, 2, 2, 2, true) + '%') as any, settleRatio: settleRatio.toString(), // quote Interest term, strike: strike.toString(), isUp: dualType.toUpperCase() === DUAL_TYPE.DUAL_BASE ? true : false, productId, enterTime: createdAt, expireTime, stepLength, currentPrice: { precisionForPrice, base, quote, currentPrice: currentPrice ?? {}, quoteUnit: quote, //quote index.quote, }, sellSymbol, buySymbol, __raw__: { order: props, }, } } ================================================ FILE: packages/core/src/hooks/help/makeInvest.ts ================================================ import { CoinMap, InvestMapType, InvestOpenType, VaultMarketExtends, } from '@loopring-web/common-resources' import { DepartmentRow, RowInvest } from '@loopring-web/component-lib' import { InvestTokenTypeMap, store, btradeReducer } from '../../stores' import { LoopringAPI } from '../../api_wrapper' import * as sdk from '@loopring-web/loopring-sdk' import { uniq } from 'lodash' export const makeInvestRow = ( investTokenTypeMap: InvestTokenTypeMap, key: string, ): R => { const { coinMap } = store.getState().tokenMap const info = investTokenTypeMap[key].detail const coinInfo = coinMap[info.token.symbol] let item = { ...investTokenTypeMap[key].detail, coinInfo, i18nKey: '' as any, children: [], isExpanded: false, type: InvestMapType.Token, } as unknown as R const children = InvestOpenType.reduce((prev, type) => { if (investTokenTypeMap[key][type]) { let _row: any = investTokenTypeMap[key][type] const coinInfo = coinMap[item.token.symbol] _row = { ..._row, coinInfo, token: item.token } prev.push(_row as DepartmentRow) } return prev }, [] as DepartmentRow[]) item.children = children return item } export const makeDefiInvestReward = async () => { const { apiKey, accountId } = store.getState().account if (LoopringAPI.defiAPI && apiKey && accountId) { // @ts-ignore const { totalRewards } = await LoopringAPI.defiAPI.getDefiReward({ accountId }, apiKey) return totalRewards ?? 0 } return '0' } export const findDualMarket = (marketArray: string[], pairASymbol: string, pairBSymbol: string) => marketArray.find((item) => { const regExp = new RegExp( `^(\\w+-)?(${pairASymbol}-${pairBSymbol}|${pairBSymbol}-${pairASymbol})$`, 'i', ) return regExp.test(item) }) export const makeBtrade = (btradeMarkets: any) => { if (btradeMarkets) { const { markets: marketMap, pairs, marketArr: marketArray, tokenArr: marketCoins, } = sdk.makeMarkets({ markets: btradeMarkets }) const tradeMap = Reflect.ownKeys(pairs ?? {}).reduce((prev, key) => { const tradePairs = pairs[key as string]?.tokenList?.sort() prev[key] = { ...pairs[key as string], tradePairs, } return prev }, {}) if (!marketArray?.length) { store.dispatch( btradeReducer.getBtradeMapStatus({ marketArray, marketCoins, marketMap, tradeMap, }), ) } } } /** * @property vaultTokenAmounts.status * show: bit 0 * join: bit 1 * exit: bit 2 * loan: bit 3 * repay:bit 4 * @param vaultTokenMap * @param vaultMarkets * @param enabled */ export const makeVault = ( vaultTokenMap: sdk.VaultToken[] | undefined, vaultMarkets: sdk.VaultMarket[] | undefined, enabled?: 'isFormLocal' | undefined, ) => { const { idIndex: erc20IdIndex, // tokenMap:erc20TokenMap } = store.getState().tokenMap if (vaultTokenMap && vaultMarkets && erc20IdIndex) { let { tokensMap, coinMap, idIndex, addressIndex } = sdk.makeMarket(vaultTokenMap as any) let erc20Array = [], erc20Map = {} const reformat: VaultMarketExtends[] = vaultMarkets.reduce((prev, ele) => { if (/-/gi.test(ele.market) && ele.enabled) { const item = { ...ele, enabled: enabled ?? ele.enabled, vaultMarket: ele.market, market: ele.market.replace(/(\w+)-(\w+)-(\w+)/, '$2-$3'), originalBaseSymbol: erc20IdIndex[tokensMap[idIndex[ele.baseTokenId]].tokenId], originalQuoteSymbol: erc20IdIndex[tokensMap[idIndex[ele.quoteTokenId]].tokenId], } prev.push(item) // const erc20Symbol = erc20IdIndex[tokensMap[idIndex[ele.baseTokenId]]?.tokenId] // @ts-ignore return prev } else { return prev as VaultMarketExtends[] } }, [] as VaultMarketExtends[]) const joinTokenMap = Reflect.ownKeys(tokensMap).reduce((prev, key) => { const status = ( tokensMap[key.toString()] as sdk.VaultToken )?.vaultTokenAmounts?.status?.toString(2) if (status[0] == '1' && status[1] == '1') { prev = { ...prev, [key]: tokensMap[key.toString()] as sdk.VaultToken, } } const erc20Symbol = erc20IdIndex[tokensMap[key.toString()]?.tokenId] // @ts-ignore erc20Array.push(erc20Symbol) erc20Map[erc20Symbol] = { ...tokensMap[key.toString()], } return prev }, {} as { [key: string]: sdk.VaultToken & sdk.TokenInfo }) const { markets: marketMap, pairs, marketArr: marketArray, } = sdk.makeMarketsWithIdIndex({ markets: reformat, },undefined, idIndex) const marketCoins = uniq( reformat.map(market => { return [market.baseTokenId, market.quoteTokenId] }).flat().map(id => idIndex[id]) ) let tokenMap: any = tokensMap const tradeMap = Reflect.ownKeys(pairs ?? {}).reduce((prev, key) => { const tradePairs = pairs[key as string]?.tokenList?.sort() prev[key] = { ...pairs[key as string], tradePairs, } const erc20Symbol = erc20IdIndex[tokensMap[key.toString()].tokenId] tokenMap[key.toString()] = { ...tokensMap[key.toString()], erc20Symbol, belongAlice: erc20Symbol, tradePairs: pairs[key.toString()].tokenList, } return prev }, {}) const _coinMap = Reflect.ownKeys(coinMap).reduce((prev, ele) => { const erc20Symbol = erc20IdIndex[tokensMap[ele.toString()].tokenId] prev[ele.toString()] = { ...coinMap[ele.toString()], belongAlice: erc20Symbol, erc20Symbol: erc20Symbol, } return prev }, {} as CoinMap) return { marketArray, marketCoins, marketMap, tradeMap, coinMap: _coinMap, pairs, idIndex, addressIndex, tokenMap, joinTokenMap, erc20Array, erc20Map, } // } } else { return { marketMap: undefined, pairs: undefined, marketArray: undefined, marketCoins: undefined, tokenMap: undefined, coinMap: undefined, idIndex: undefined, addressIndex: undefined, erc20Array: undefined, erc20Map: undefined, } } } ================================================ FILE: packages/core/src/hooks/help/makeMarketTrend.ts ================================================ export const makeMarketTrend = () => {} ================================================ FILE: packages/core/src/hooks/help/makeTickView.ts ================================================ import { LoopringMap, TickerData, toBig } from '@loopring-web/loopring-sdk' import { store } from '../../index' import { volumeToCount } from './volumeToCount' import { TickerMap } from '../../stores' import { getValuePrecisionThousand, Ticker, TickerNew, TickerNewMap, TokenType, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' export const makeTickView = (tick: TickerData) => { const { tokenPrices } = store.getState().tokenPrices // const { forexMap } = store.getState().system; if (tick && tick.base && tick.quote) { const price = tokenPrices[tick.base] const volume = volumeToCount(tick.quote, tick.quote_token_volume) const priceU = toBig(volume ? volume : 0).times(price) const change = tick.change && tick.change !== 0 ? tick.change * 100 : undefined const qPrice = tick.quote === 'DAI' ? 1 : tokenPrices[tick.quote] || 0 const closeU = toBig(tick.close).times(qPrice).toNumber() // const extraTickerInfo = makeTickView(item); return { ...tick, changeU: toBig(tick.close - (tick.open ?? 0)).toNumber(), volume: volume ? Number(volume) : undefined, closeDollar: closeU, timeUnit: '24h', priceU: priceU.toNumber(), floatTag: tick.close > tick.open ? 'increase' : 'decrease', change: change, close: isNaN(tick.close) ? undefined : tick.close, high: tick.high === 0 ? undefined : tick.high, low: tick.low === 0 ? undefined : tick.low, reward: 0, rewardToken: '', __rawTicker__: tick, } as Ticker } } export const makeTickerMap = ({ tickerMap, }: { tickerMap: LoopringMap }): TickerMap<{ [key: string]: any }> => { const { forexMap } = store.getState().system const { tokenPrices } = store.getState().tokenPrices return Reflect.ownKeys(tickerMap).reduce((prev: TickerMap, key) => { const item = tickerMap[key as any] if (item && item.quote && forexMap && tokenPrices[item.quote]) { const price = tokenPrices[item.quote] const volume = volumeToCount(item.symbol.split('-')[1], item.quote_token_volume) const priceU = toBig(volume ? volume : 0).times(price) const change = item.change && item.change !== 0 ? item.change * 100 : undefined const extraTickerInfo = makeTickView(item) prev[key as keyof R] = { ...item, ...extraTickerInfo, timeUnit: '24h', priceU: priceU?.toNumber() === 0 ? undefined : priceU?.toNumber(), volume: volume ? volume.toString() : undefined, floatTag: item.close > item.open ? 'increase' : 'decrease', change: change, close: isNaN(item.close) ? undefined : item.close, high: item.high === 0 ? undefined : item.high, low: item.low === 0 ? undefined : item.low, // APR: 0, reward: 0, rewardToken: '', __rawTicker__: item, } as unknown as Ticker } return prev }, {} as TickerMap) } export const makeTokenTickerView = ({ item, isVault = false, }: { item: sdk.DatacenterTokenInfoSimple isVault?: boolean }) => { const { tokenMap: { tokenMap: erc20TokenMap }, invest: { vaultMap: { erc20Map, idIndex }, }, } = store.getState() const tokenInfo = erc20TokenMap[item.symbol] const volume = getValuePrecisionThousand( item.volume24H, tokenInfo.precision, tokenInfo.precision, undefined, ) //volumeToCount(item.symbol, item.volume24H) const priceU = sdk.toBig(item.volume24H ?? 0).times(item.price ?? 0) const change = getValuePrecisionThousand(item.percentChange24H ?? 0, 2, 2, 2) // @ts-ignore return { ...tokenInfo, ...item, timeUnit: '24h', volume, priceU, change, type: isVault ? TokenType.vault : TokenType.single, erc20Symbol: tokenInfo.symbol, symbol: isVault ? idIndex[erc20Map[tokenInfo.symbol].vaultTokenId] : tokenInfo.symbol, __rawTicker__: item, rawData: item, } as unknown as TickerNew } export const makeTokenTickerMap = ({ rawData, isVault, }: { rawData: sdk.DatacenterTokenInfoSimple[] isVault?: boolean }): TickerNewMap => { return rawData.reduce((prev: TickerNewMap, item) => { const key = item.symbol if (item && item.symbol && item.price) { const { invest: { vaultMap: { tokenMap }, }, } = store.getState() const priceU = sdk.toBig(item.volume24H ?? 0).times(item.price ?? 0) const change = getValuePrecisionThousand(item.percentChange24H ?? 0, 2, 2, 2) // @ts-ignore prev[key] = { // ...tokenInfo, ...item, ...tokenMap['LV' + item.symbol], timeUnit: '24h', volume: item.volume24H, priceU, change, type: TokenType.vault, erc20Symbol: item.symbol, symbol: 'LV' + item.symbol, __rawTicker__: item, rawData: item, } as unknown as TickerNew } return prev }, {} as TickerNewMap) } ================================================ FILE: packages/core/src/hooks/help/makeUIAmmActivityMap.ts ================================================ import { AmmPoolActivityRule, AmmPoolActivityStatus, AmmUserRewardMap, LoopringMap, toBig, TokenInfo, } from '@loopring-web/loopring-sdk' import { AmmActivity, AmmCardProps, getValuePrecisionThousand, MyAmmLP, myError, TokenType, } from '@loopring-web/common-resources' import { AmmDetailStore, makeWalletLayer2, MyAmmLPMap, store, VolToNumberWithPrecision, } from '../../index' import BigNumber from 'bignumber.js' import * as sdk from '@loopring-web/loopring-sdk' import { volumeToCount, volumeToCountAsBigNumber } from './volumeToCount' import { coinMap, EarningsRow } from '@loopring-web/component-lib' import _ from 'lodash' export const makeClaimRewards = (totalClaims: sdk.ClaimItem[]) => { const { idIndex, tokenMap } = store.getState().tokenMap const { tokenPrices } = store.getState().tokenPrices const list = totalClaims.reduce((prev, _item) => { let tokenValueDollar = 0, amountStr: any const item = { ..._item, } const tokenInfo: TokenInfo = tokenMap[idIndex[item.tokenId]] if (tokenInfo.symbol) { let totalAmount = sdk.toBig(0) // @ts-ignore item.claimableInfo = item.claimable?.map((_claimable) => { totalAmount.plus(_claimable.amount) const amountStr = volumeToCountAsBigNumber(tokenInfo?.symbol, _claimable?.amount ?? 0) const tokenValueDollar = amountStr?.times(tokenPrices[tokenInfo.symbol]) return { ..._claimable, amountStr: amountStr?.toString(), tokenValueDollar: tokenValueDollar?.toString(), token: tokenInfo.symbol, precision: tokenInfo.precision, } }) // @ts-ignore amountStr = volumeToCountAsBigNumber(tokenInfo.symbol, _item.sum) // amountStr = volumeToCountAsBigNumber(tokenInfo.symbol, 1000000000) tokenValueDollar = amountStr?.times(tokenPrices[tokenInfo.symbol]) prev[tokenInfo.symbol] = { token: { type: TokenType.single, value: tokenInfo.symbol, }, detail: item.claimableInfo, precision: tokenInfo.precision, tokenValueDollar: tokenValueDollar.toString(), amountStr: amountStr.toString(), // @ts-ignore amount: _item?.sum, rawData: item, } as unknown as R // prev.push() } return prev }, {} as { [key: string]: R }) return list } export type AmmActivityViewMap = { [key in keyof R]?: AmmActivity[] | undefined } export const makeUIAmmActivityMap = < R extends { [key: string]: any }, I extends { [key: string]: any }, >( { ammActivityMap, type, ammPoolActivityStatus, }: { ammActivityMap: LoopringMap> | undefined type?: 'AMM_MINING' | 'SWAP_VOLUME_RANKING' | 'ORDERBOOK_MINING' | undefined ammPoolActivityStatus: AmmPoolActivityStatus[] }, myReward: AmmUserRewardMap | undefined, ): Array> => { const { coinMap, tokenMap, idIndex } = store.getState().tokenMap let ammActivityViewMap: AmmActivityViewMap = {} if (ammActivityMap) { const genView = (ammActivityMapItem: any) => { let ammActivityViewMapTmp: AmmActivityViewMap = {} ammPoolActivityStatus.forEach((status: AmmPoolActivityStatus) => { if (ammActivityMapItem[status]) { // @ts-ignore ammActivityMapItem[status].reduce( (prev: AmmActivityViewMap, ammPoolActivityRule: AmmPoolActivityRule) => { if (coinMap && ammPoolActivityRule.awardRules[0] && idIndex && tokenMap) { // @ts-ignore const { current } = myReward ? myReward[ammPoolActivityRule.market] : { current: undefined } const symbol = idIndex[ammPoolActivityRule.awardRules[0].tokenId as any] const totalRewards = VolToNumberWithPrecision( ammPoolActivityRule.awardRules[0].volume, symbol, ) const myRewardVol = current?.currentRewards[0]?.volume ?? 0 const item = { // @ts-ignore market: ammPoolActivityRule.market, status: ammPoolActivityRule.status, ruleType: ammPoolActivityRule.ruleType, rewardToken: coinMap[symbol], totalRewards: Number(totalRewards), maxSpread: (ammPoolActivityRule?.maxSpread || 0) * 100, myRewards: status === AmmPoolActivityStatus.InProgress && myReward && myReward[ammPoolActivityRule.market] ? volumeToCount(symbol, myRewardVol) : 0, duration: { from: new Date(ammPoolActivityRule?.rangeFrom), to: new Date(ammPoolActivityRule?.rangeTo), }, isPass: AmmPoolActivityStatus.EndOfGame === status, } if (prev[ammPoolActivityRule.market]) { // @ts-ignore prev[ammPoolActivityRule.market].push(item) } else { // @ts-ignore prev[ammPoolActivityRule.market] = [item] } // return prev; } return prev }, ammActivityViewMapTmp, ) } }) return ammActivityViewMapTmp } if (type === undefined) { const keys = Object.keys(ammActivityMap) keys.forEach((item: any, _ind: number) => { const newMap = genView(ammActivityMap[item]) const copiedNewMap = _.cloneDeep(newMap) ammActivityViewMap = { ...ammActivityViewMap, [item]: copiedNewMap } }) } else { ammActivityViewMap = genView(ammActivityMap[type]) } } const resultArray = makeAsCard(ammActivityViewMap) return resultArray } const makeAsCard = <_R extends { [key: string]: any }, I extends { [key: string]: any }>( ammActivityViewMap: any, _myReward?: any, ): Array> => { const { coinMap } = store.getState().tokenMap const { ammMap } = store.getState().amm.ammMap const { tokenPrices } = store.getState().tokenPrices try { if (ammActivityViewMap && coinMap) { // @ts-ignore // return ammActivityViewMap.map() let finalArray = [] as any[] const catogoriedMap = Object.keys(ammActivityViewMap) catogoriedMap.forEach((item) => { const basicArray = Object.keys(ammActivityViewMap[item]).reduce( (prev: Array>, key: string) => { const activities = ammActivityViewMap[item][key] const _ammInfo = ammMap[key as string] if (activities) { //_ammInfo && _ammInfo.coinA && coinMap && // @ts-ignore const itemArray = activities.map((item: AmmActivity) => { const matchRes = (item.market as string).replace('AMM-', '').match(/(\w+)-(\w+)/i) if (matchRes) { const coinAInfo = coinMap[matchRes[1]] const coinBInfo = coinMap[matchRes[2]] const coinApriceU = tokenPrices ? tokenPrices[coinAInfo?.simpleName] : 0 const coinBpriceU = tokenPrices ? tokenPrices[coinBInfo?.simpleName] : 0 const rewardTokenU = tokenPrices ? tokenPrices[item?.rewardToken?.simpleName] : 0 // myLog('matchRes:', matchRes, ' coinAInfo:', coinAInfo, ' coinBInfo:', coinBInfo) return { ..._.cloneDeep(_ammInfo), coinAInfo, coinBInfo, coinApriceU, coinBpriceU, activity: { ...item, rewardTokenU: rewardTokenU, }, } } return {} }) prev = [...prev, ...itemArray] } return prev }, [] as Array>, ) as Array> finalArray = [...finalArray, ...basicArray] }) return finalArray } else { return [] as Array> } } catch (error: any) { myError(error) return [] } } type Value = undefined | number | string export type SummaryMyInvest = { rewardU: Value feeU: Value investDollar?: Value ammPoolDollar?: Value stakeETHDollar?: Value dualStakeDollar?: Value leverageETHDollar?: Value taikoFarmingDollar?: Value } export const makeSummaryMyAmm = ({ userRewardsMap, }: { userRewardsMap: AmmUserRewardMap | undefined }): { myAmmLPMap: MyAmmLPMap | undefined rewardU: string feeU: string } => { const { idIndex, tokenMap } = store.getState().tokenMap const { ammMap } = store.getState().amm.ammMap if (ammMap && idIndex && tokenMap) { return Object.keys(ammMap).reduce( (prev, key) => { let ammInfo = ammMap[key] if (ammInfo) { const value = getMyAmmInfo({ ammInfo, ammUserReward: userRewardsMap && userRewardsMap[key], }) return { myAmmLPMap: { ...prev.myAmmLPMap, [ammInfo.market]: value, }, rewardU: value.rewardU ? toBig(value.rewardU).plus(prev.rewardU).toString() : prev.rewardU, feeU: value.feeU ? toBig(value.feeU).plus(prev.feeU).toString() : prev.feeU, } } else { return prev } }, { myAmmLPMap: {} as any, rewardU: '0', feeU: '0', } as any, ) } else { return { myAmmLPMap: {} as any, rewardU: '0', feeU: '0', } } } /** userRewradCalc **/ const getRewardCalc = ({ ammInfo, ammUserReward, }: { ammInfo: AmmDetailStore ammUserReward?: { current: sdk.AmmUserReward lastDay: sdk.AmmUserReward } }) => { let rewardToken, rewardToken2, feeA, feeB, feeU, reward, reward2, rewardU, feeA24, feeB24, feeU24, reward24, reward224, rewardU24, extraRewards24: any[] = [], extraU24 = 0 const { current, lastDay } = ammUserReward ?? {} const { tokenPrices } = store.getState().tokenPrices const { idIndex } = store.getState().tokenMap if (current) { rewardToken = current.currentRewards[0] ? idIndex[current.currentRewards[0].tokenId as number] : undefined rewardToken2 = current.currentRewards[1] ? idIndex[current.currentRewards[1].tokenId as number] : undefined feeA = volumeToCountAsBigNumber(ammInfo.coinA, current?.feeRewards[0] ?? 0) ?? toBig(0) feeB = volumeToCountAsBigNumber(ammInfo.coinB, current?.feeRewards[1] ?? 0) ?? toBig(0) feeU = feeA .times(tokenPrices[ammInfo.coinA] ? tokenPrices[ammInfo.coinA] : 0) .plus(feeB.times(tokenPrices[ammInfo.coinB] ? tokenPrices[ammInfo.coinB] : 0)) reward = rewardToken ? (volumeToCountAsBigNumber(rewardToken, current.currentRewards[0].volume) as BigNumber) : toBig(0) reward2 = rewardToken2 ? (volumeToCountAsBigNumber(rewardToken2, current.currentRewards[1].volume) as BigNumber) : toBig(0) reward = reward ? reward : toBig(0) reward2 = reward2 ? reward2 : toBig(0) rewardU = reward .times(rewardToken ? tokenPrices[rewardToken] : 1) .plus(reward2.times(rewardToken2 ? tokenPrices[rewardToken2] : 1)) if (lastDay) { feeA24 = volumeToCountAsBigNumber(ammInfo.coinA, lastDay?.feeRewards[0] ?? 0) ?? toBig(0) feeB24 = volumeToCountAsBigNumber(ammInfo.coinB, lastDay.feeRewards[1] ?? 0) ?? toBig(0) feeU24 = feeA24 .times(tokenPrices[ammInfo.coinA] ? tokenPrices[ammInfo.coinA] : 0) .plus(feeB24.times(tokenPrices[ammInfo.coinB] ? tokenPrices[ammInfo.coinB] : 0)) reward24 = rewardToken ? (volumeToCountAsBigNumber( rewardToken, lastDay?.currentRewards[0]?.volume ?? 0, ) as BigNumber) : toBig(0) reward224 = rewardToken2 ? (volumeToCountAsBigNumber( rewardToken2, lastDay?.currentRewards[1]?.volume ?? 0, ) as BigNumber) : toBig(0) rewardU24 = reward24 .times(rewardToken ? tokenPrices[rewardToken] : 1) .plus(reward24.times(rewardToken2 ? tokenPrices[rewardToken2] : 1)) extraU24 = 0 extraRewards24 = lastDay?.extraRewards?.map((item: any) => { const tokenSymbol = idIndex[item.tokenId as number] const extraItem = volumeToCountAsBigNumber(tokenSymbol, item.volume ?? 0) extraU24 += (extraItem ?? toBig(0)).times(tokenPrices[tokenSymbol] ?? 1).toNumber() return { tokenSymbol, amount: extraItem?.toNumber(), } }) } } return { rewardToken: rewardToken ? coinMap[rewardToken] : undefined, rewardToken2: rewardToken2 ? coinMap[rewardToken2] : undefined, feeA: feeA ? feeA.toNumber() : undefined, feeB: feeB ? feeB.toNumber() : undefined, reward: reward ? reward.toNumber() : undefined, reward2: reward2 ? reward2.toNumber() : undefined, rewardU: rewardU ? rewardU.toNumber() : undefined, feeU: feeU ? feeU.toNumber() : undefined, feeA24: feeA24 ? feeA24.toNumber() : undefined, feeB24: feeB24 ? feeB24.toNumber() : undefined, feeU24: feeU24 ? feeU24.toNumber() : undefined, reward24: reward24 ? reward24.toNumber() : undefined, reward224: reward224 ? reward224.toNumber() : undefined, rewardU24: rewardU24 ? rewardU24.toNumber() : undefined, extraRewards24: extraRewards24, extraU24: extraU24 ? extraU24 : undefined, } } /** userAmmAsset&Reward **/ const getMyAmmInfo = ({ ammInfo, ammUserReward }: any) => { let rewards const { tokenPrices } = store.getState().tokenPrices const { tokenMap, idIndex } = store.getState().tokenMap if (ammUserReward) { rewards = getRewardCalc({ ammInfo, ammUserReward, }) } let coinA, coinB if (ammInfo.coinA) { coinA = ammInfo.coinA coinB = ammInfo.coinB } else if (ammInfo.tokens && ammInfo.tokens.pooled) { coinA = idIndex[ammInfo.tokens.pooled[0]] coinB = idIndex[ammInfo.tokens.pooled[1]] } else { ;[, coinA, coinB] = ammInfo.market.match(/(\w+)(-\w+)?/i) } let balanceA, balanceB, balanceU const { walletMap } = makeWalletLayer2({ needFilterZero: false }) if (walletMap && walletMap['LP-' + ammInfo.market] && ammInfo.totalLPToken) { // @ts-ignore const ratio = sdk.toBig(walletMap['LP-' + ammInfo.market]?.count ?? 0).div(ammInfo.totalLPToken) balanceA = ratio.times(ammInfo.totalA) balanceB = ratio.times(ammInfo.totalB) balanceU = balanceA.times(tokenPrices[coinA]).plus(balanceB.times(tokenPrices[ammInfo.coinB])) } return { ...rewards, balanceA: balanceA ? balanceA.toNumber() : undefined, balanceB: balanceB ? balanceB.toNumber() : undefined, balanceAStr: getValuePrecisionThousand( balanceA, tokenMap[coinA].precision, tokenMap[coinA].precision, undefined, false, ), balanceBStr: getValuePrecisionThousand( balanceB, tokenMap[coinB].precision, tokenMap[coinB].precision, undefined, false, ), balanceU: balanceU ? balanceU.toNumber() : undefined, } as MyAmmLP } // export const makeMyAmmWithSnapshot = ( // market: any, // _walletMap: WalletMapExtend | undefined, // ammUserRewardMap: AmmUserRewardMap | undefined, // snapShotData?: // | { // tickerData?: TickerData | undefined; // ammPoolSnapshot: AmmPoolSnapshot | undefined; // } // | undefined // ) => { // const { coinMap, idIndex, tokenMap } = store.getState().tokenMap; // const { tokenPrices } = store.getState().tokenPrices; // const [, coinA, coinB] = market.match(/(\w+)-(\w+)/i); // let _myAmm: Partial> = {}; // if ( // ammUserRewardMap && // ammUserRewardMap["AMM-" + market] && // snapShotData && // snapShotData.ammPoolSnapshot // ) { // const ammUserReward = ammUserRewardMap["AMM-" + market]; // if (coinMap && tokenMap && idIndex) { // // _myAmm = getOneRewardInfo({ // // coinA, // // coinB, // // ammUserReward, // // idIndex, // // tokenPrices, // // walletMap: _walletMap, // // snapShotData, // // }); // return _myAmm as MyAmmLP; // } // } // return { // feeA: undefined, // feeB: undefined, // feeU: undefined, // reward: undefined, // rewardToken: undefined as any, // balanceA: undefined, // balanceB: undefined, // balanceU: undefined, // }; // }; // export const makeMyAmmWithStat = ( // market: any, // _walletMap: WalletMapExtend | undefined, // ammUserRewardMap: AmmUserRewardMap | undefined, // ammDetail: AmmDetailStore // ) => { // const { coinMap, idIndex, tokenMap } = store.getState().tokenMap; // const { tokenPrices } = store.getState().tokenPrices; // const [, coinA, coinB] = market.match(/(\w+)-(\w+)/i); // let _myAmm = {}, // result = {}; // if (ammUserRewardMap) { // const ammUserReward = ammUserRewardMap["AMM-" + market]; // result = getRewardCalc({ // coinA, // coinB, // ammUserReward, // idIndex, // tokenPrices, // }); // } // // let balanceA, balanceB, balanceU; // // if (_walletMap && _walletMap["LP-" + coinA + "-" + coinB] && coinMap) { // // @ts-ignore // const totalLpAmount = _walletMap["LP-" + coinA + "-" + coinB].count || 0; // // const ratio = new BigNumber(_walletMap[ 'LP-' + coinA + '-' + coinB ].count).div(ammDetail.totalLPToken); // const ratio = totalLpAmount / (ammDetail.totalLPToken || 1); // // balanceA = ratio.times(volumeToCountAsBigNumber(coinA, ammDetail.totalA ? ammDetail.totalA : 0) || 1); // balanceA = ratio * (ammDetail.totalA || 0); // // balanceB = ratio.times(volumeToCountAsBigNumber(coinB, ammDetail.totalB ? ammDetail.totalB : 0) || 1); // balanceB = ratio * (ammDetail.totalB || 0); // // @ts-ignore // // balanceU = balanceA.times(fiatPrices[ coinA ] ? fiatPrices[ coinA ].price : 0).plus(balanceB.times(fiatPrices[ coinB ] ? fiatPrices[ coinB ].price : 0)) // // // WARNING! NOT ACCERATE // balanceU = // balanceA * (tokenPrices[coinA] ? tokenPrices[coinA] : 0) + // balanceB * (tokenPrices[coinB] ? tokenPrices[coinB] : 0); // const isSmallBalance = balanceU < 1; // // _myAmm = { // balanceA: balanceA, // balanceB: balanceB, // balanceAStr: getValuePrecisionThousand( // balanceA, // tokenMap[coinA].precision, // tokenMap[coinA].precision, // undefined // ), // balanceBStr: getValuePrecisionThousand( // balanceB, // tokenMap[coinB].precision, // tokenMap[coinB].precision, // undefined // ), // balanceU: balanceU, // totalLpAmount: totalLpAmount, // smallBalance: isSmallBalance, // ammDetail: { // ...ammDetail, // coinAInfo: coinA ? coinMap[coinA] : undefined, // coinBInfo: coinB ? coinMap[coinB] : undefined, // }, // }; // } // return { // ...result, // ..._myAmm, // }; // }; ================================================ FILE: packages/core/src/hooks/help/makeWallet.ts ================================================ import { BIGO, numberFormat, store } from '../../index' import { AccountStatus, CoinKey, WalletCoin, WalletMap } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import BigNumber from 'bignumber.js' import Decimal from 'decimal.js' import { utils } from 'ethers' export const VaultBorrowFault = 1 export type WalletMapExtend = { [K in CoinKey]?: WalletCoin & { detail: sdk.UserBalanceInfo } } export const makeWalletLayer2 = < C extends { [key: string]: any }, >({ needFilterZero, isActive = false, _isToL1, }: { needFilterZero: boolean isActive?: boolean _isToL1?: boolean _isTotal?: boolean }): { walletMap: WalletMapExtend | undefined } => { const { walletLayer2 } = store.getState().walletLayer2 const { tokenMap } = store.getState().tokenMap const { readyState } = store.getState().account let walletMap: WalletMapExtend | undefined if (walletLayer2) { walletMap = Reflect.ownKeys(walletLayer2).reduce((prev, item) => { const { total, locked } = walletLayer2[item as string] const countBig = sdk.toBig(total).minus(sdk.toBig(locked)) if (needFilterZero && countBig.eq(BIGO)) { return prev } if (_isToL1 && /^LP-/gi.test(item.toString())) { return prev } return { ...prev, [item]: { belong: item, count: sdk.fromWEI(tokenMap, item, countBig.toString()), detail: walletLayer2[item as string], }, } }, {} as WalletMapExtend) } if (isActive) { return { walletMap } } else if (readyState === AccountStatus.ACTIVATED) { return { walletMap } } else { return { walletMap: undefined } } } export const makeWalletLayer2NoStatus = < C extends { [key: string]: any }, >({ needFilterZero, _isToL1, }: { needFilterZero: boolean _isToL1?: boolean }): { walletMap: WalletMapExtend | undefined } => { const { walletLayer2 } = store.getState().walletLayer2 const { tokenMap } = store.getState().tokenMap let walletMap: WalletMapExtend | undefined if (walletLayer2) { walletMap = Reflect.ownKeys(walletLayer2).reduce((prev, item) => { const { total, locked } = walletLayer2[item as string] const countBig = sdk.toBig(total).minus(sdk.toBig(locked)) if (needFilterZero && countBig.eq(BIGO)) { return prev } if (_isToL1 && /^LP-/gi.test(item.toString())) { return prev } return { ...prev, [item]: { belong: item, count: sdk.fromWEI(tokenMap, item, countBig.toString()), detail: walletLayer2[item as string], }, } }, {} as WalletMapExtend) } return { walletMap } } export const makeVaultLayer2 = < C extends { [key: string]: any }, _I = WalletCoin & { detail: any }, >({ needFilterZero, }: // _isTotal, { needFilterZero: boolean }): { vaultLayer2Map: WalletMap | undefined } => { const { vaultAccountInfo,vaultLayer2 } = store.getState().vaultLayer2 const { invest: { vaultMap: { tokenMap, idIndex }, }, tokenMap: { idIndex: erc20IdIndex }, } = store.getState() const { readyState } = store.getState().account let vaultLayer2Map: WalletMap | undefined if (vaultAccountInfo?.userAssets && idIndex) { vaultLayer2Map = vaultAccountInfo?.userAssets.reduce((prev, item) => { const symbol = idIndex[item.tokenId] const vaultToken = tokenMap[symbol] const vaultAsset=(vaultLayer2 && vaultLayer2[symbol]) ??{} const countBig = sdk.toBig(vaultAsset?.l2balance ?? 0).minus(vaultAsset?.locked ?? 0) if (needFilterZero && countBig.eq(BIGO) && !vaultToken) { return prev } const erc20Symbol = erc20IdIndex && erc20IdIndex[vaultToken?.tokenId ?? ''] return { ...prev, [symbol]: { belongAlice: erc20Symbol, erc20Symbol, belong: symbol, asset: numberFormat(utils.formatUnits(item?.total ?? 0, vaultToken.decimals), { fixed: vaultToken.precision, fixedRound: Decimal.ROUND_DOWN, removeTrailingZero: true }), count: countBig .div('1e' + vaultToken.decimals) .toFixed(vaultToken.qtyStepScale ?? vaultToken.precision, BigNumber.ROUND_DOWN), detail: item, borrowed: numberFormat(utils.formatUnits(item?.borrowed ?? 0, vaultToken.decimals), { fixed: vaultToken.precision, fixedRound: Decimal.ROUND_UP, removeTrailingZero: true }), equity: numberFormat(utils.formatUnits(item?.netAsset ?? 0, vaultToken.decimals), { fixed: vaultToken.precision, fixedRound: Decimal.ROUND_DOWN, removeTrailingZero: true }), }, } }, {} as WalletMap) } if (readyState === AccountStatus.ACTIVATED) { return { vaultLayer2Map } } else { return { vaultLayer2Map: undefined } } } export const makeVaultAvaiable2 = < C extends { [key: string]: any }, >({ fault = VaultBorrowFault, }: // needFilterZero, { // needFilterZero: boolean fault?: number }): { vaultAvaiable2Map: WalletMap | undefined } => { const { vaultLayer2: { vaultAccountInfo, vaultLayer2 }, tokenMap: { // tokenMap: erc20TokenMap, idIndex: erc20IdIndex, }, // tokenPrices: { tokenPrices }, invest: { vaultMap: { tokenMap, marketCoins, idIndex: vaultIdIndex, tokenPrices }, }, } = store.getState() //@ts-ignore const { maxBorrowableOfUsdt, accountStatus } = vaultAccountInfo ?? {} if (accountStatus && accountStatus === sdk.VaultAccountStatus.IN_STAKING) { let vaultAvaiable2Map: WalletMap | undefined = marketCoins?.reduce((prev, item) => { const vaultSymbol = vaultIdIndex[tokenMap[item].vaultTokenId] const erc20Symbol = vaultSymbol.slice(2) const price = tokenPrices[vaultSymbol] ?? (vaultSymbol == 'USDT' ? 1 : 1) const vaultToken = tokenMap[vaultSymbol] const status = vaultToken?.vaultTokenAmounts?.status?.toString(2) if (status[1] == '1') { let borrowed = sdk .toBig((vaultLayer2 && vaultLayer2[vaultSymbol]?.borrowed) ?? 0) .div('1e' + vaultToken.decimals) .toFixed(vaultToken?.vaultTokenAmounts?.qtyStepScale ?? vaultToken.precision, 0) borrowed = parseFloat(borrowed) == 0 ? 0 : borrowed let walletCoin = { erc20Symbol, belongAlice: erc20Symbol, belong: vaultSymbol, borrowed: borrowed, count: price ? sdk .toBig(maxBorrowableOfUsdt ?? 0) .div(price ? price : 1) .times(fault) .toFixed( vaultToken?.vaultTokenAmounts?.qtyStepScale ?? vaultToken.precision, BigNumber.ROUND_DOWN, ) : 0, } prev[item] = { ...walletCoin, } } return prev }, {} as WalletMap) return { vaultAvaiable2Map } } else { return { vaultAvaiable2Map: undefined } } } export const makeVaultRepay = < C extends { [key: string]: any }, >({ needFilterZero, }: { needFilterZero: boolean }): { vaultAvaiable2Map: WalletMap | undefined } => { const { vaultLayer2: { vaultAccountInfo }, tokenMap: { // tokenMap: erc20TokenMap, idIndex: erc20IdIndex, }, invest: { vaultMap: { tokenMap, idIndex: vaultIdIndex, tokenPrices }, }, } = store.getState() const { accountStatus, userAssets } = vaultAccountInfo ?? {} if (accountStatus && accountStatus === sdk.VaultAccountStatus.IN_STAKING) { let vaultAvaiable2Map: WalletMap | undefined = userAssets?.reduce((prev, item) => { const vaultSymbol = vaultIdIndex[item?.tokenId ?? ''] const erc20Symbol = erc20IdIndex[tokenMap[vaultSymbol]?.tokenId ?? ''] // const price = tokenPrices[erc20Symbol] ?? 0 const vaultToken = tokenMap[vaultSymbol] const status = vaultToken?.vaultTokenAmounts?.status?.toString(2) if ( status?.[4] == '1' && ((vaultSymbol && needFilterZero && sdk.toBig(item.borrowed).gt(0)) || !needFilterZero) ) { const borrowed = sdk .toBig(item.borrowed) .div('1e' + vaultToken.decimals) .toFixed(vaultToken?.vaultTokenAmounts?.qtyStepScale ?? vaultToken.precision, 0) // .toFixed(tokenInfo.precision, 0) const price = tokenPrices[vaultSymbol] ?? 0 let walletCoin = { erc20Symbol, belongAlice: erc20Symbol, belong: vaultSymbol, count: sdk .toBig(item.total) .div('1e' + vaultToken.decimals) .toString(), borrowed, usd: sdk.toBig(borrowed).times(price).toString(), } // @ts-ignore prev[vaultSymbol] = { ...walletCoin, } } return prev }, {} as WalletMap) return { vaultAvaiable2Map } } else { return { vaultAvaiable2Map: undefined } } } ================================================ FILE: packages/core/src/hooks/help/marketPairHelp.ts ================================================ import { ammMapReducer, LoopringAPI, store, tickerReducer, volumeToCountAsBigNumber, } from '../../index' import { CustomError, ErrorMap, getValuePrecisionThousand, IBData, MarketType, Ticker, } from '@loopring-web/common-resources' import BigNumber from 'bignumber.js' import { SwapTradeData } from '@loopring-web/component-lib' import * as sdk from '@loopring-web/loopring-sdk' export const swapDependAsync = async ( market: MarketType, level?: number, limit?: number, ): Promise<{ ammPoolSnapshot: sdk.AmmPoolSnapshot | undefined tickMap: sdk.LoopringMap depth: sdk.DepthData }> => { const { ammMap } = store.getState().amm.ammMap const poolAddress = ammMap['AMM-' + market]?.address if (LoopringAPI.ammpoolAPI && LoopringAPI.exchangeAPI) { try { const [{ depth }, { ammPoolSnapshot }, { tickMap }] = await Promise.all([ LoopringAPI.exchangeAPI.getMixDepth({ market, level, limit }), poolAddress ? LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress }) : Promise.resolve({ ammPoolSnapshot: undefined }), LoopringAPI.exchangeAPI.getMixTicker({ market: market }), ]) store.dispatch(tickerReducer.updateTicker(tickMap)) const { ammMap } = store.getState().amm.ammMap const ammInfo = ammMap['AMM-' + market] if (ammPoolSnapshot && ammInfo) { store.dispatch( ammMapReducer.updateRealTimeAmmMap({ ammPoolStats: { ['AMM-' + market]: { ...ammInfo.__rawConfig__, liquidity: [ammPoolSnapshot.pooled[0].volume, ammPoolSnapshot.pooled[1].volume], lpLiquidity: ammPoolSnapshot.lp.volume, }, }, }), ) } return { ammPoolSnapshot: ammPoolSnapshot, tickMap, depth, } } catch (error) { throw error } } else { throw new Error('api not ready') } } export const dexSwapDependAsync = ({ market, level = 0, limit, tokenMap, }: { market: MarketType level?: number limit?: number tokenMap?: any }): Promise<{ depth: sdk.DepthData | undefined }> => { return new Promise((resolve, reject) => { if (LoopringAPI.ammpoolAPI && LoopringAPI.exchangeAPI) { Promise.all([ LoopringAPI.defiAPI?.getBtradeDepth({ request: { market, level, limit, }, tokenMap, }), ]) .then(([responseDepth]) => { resolve({ depth: responseDepth?.depth, }) }) .catch() } else { reject(new CustomError(ErrorMap.NO_SDK)) } }) } export const calcPriceByAmmTickMapDepth = <_C>({ market, tradePair, dependencyData: { ticker, ammPoolSnapshot, depth }, noGetValuePrecisionThousand }: { market: MarketType tradePair: MarketType dependencyData: { ticker: Ticker | undefined ammPoolSnapshot: any depth: any }, noGetValuePrecisionThousand?: boolean }): { stob: string | undefined btos: string | undefined close: string | undefined } => { const { tokenMap, idIndex, marketMap } = store.getState().tokenMap // const = store.getState().pageTradeLite.pageTradeLite; // @ts-ignore const [, coinSell] = tradePair.match(/(\w+)-(\w+)/i) // @ts-ignore const [, coinA, coinB] = market.match(/(\w+)-(\w+)/i) let stob: number | string | undefined | BigNumber = undefined, btos: number | string | undefined | BigNumber = undefined, close: number | string | undefined = undefined if (coinA && coinB && tokenMap && marketMap && idIndex && marketMap[market]) { //first getValue from ammPoolSnapshot if (ammPoolSnapshot) { const poolATokenVol: sdk.TokenVolumeV3 = ammPoolSnapshot.pooled[0] const poolBTokenVol: sdk.TokenVolumeV3 = ammPoolSnapshot.pooled[1] stob = volumeToCountAsBigNumber(idIndex[poolBTokenVol.tokenId], poolBTokenVol.volume)?.div( volumeToCountAsBigNumber(idIndex[poolATokenVol.tokenId], poolATokenVol.volume) || 1, ) // @ts-ignore btos = noGetValuePrecisionThousand ? new BigNumber(1).div(stob ?? 1).toNumber() : getValuePrecisionThousand( new BigNumber(1).div(stob ?? 1).toNumber(), tokenMap[coinA].precision, tokenMap[coinA].precision, tokenMap[coinA].precision, true, ) // .toFixed(tokenMap[idIndex[poolATokenVol.tokenId]].precision)) let precision = marketMap[market].precisionForPrice ? marketMap[market].precisionForPrice : tokenMap[coinB].precision stob = noGetValuePrecisionThousand ? stob : getValuePrecisionThousand(stob, precision, precision, precision, true) // stob = Number(stob?.toFixed(tokenMap[idIndex[poolBTokenVol.tokenId]].precision)) close = stob?.toString() if (coinSell !== coinA) { stob = btos btos = close } // myLog('pairDetailDone stob from amm:', stob) } //second getValue from tickerData if ((stob === '0.00' || !stob) && ticker) { // const tickerData = tickerMap[ market ] let precision = marketMap[market].precisionForPrice ? marketMap[market].precisionForPrice : tokenMap[coinB].precision close = noGetValuePrecisionThousand ? ticker.close : getValuePrecisionThousand(ticker.close, precision, precision, precision, true) stob = close btos = Number(ticker.close) !== 0 ? noGetValuePrecisionThousand ? 1 / ticker.close : getValuePrecisionThousand( 1 / ticker.close, tokenMap[coinA].precision, tokenMap[coinA].precision, tokenMap[coinA].precision, true, ) : 0 if (!ticker.__rawTicker__.base === coinSell) { stob = btos btos = close } } //last check from depth if ((stob === '0.00' || !stob) && depth) { let precision = marketMap[market].precisionForPrice ? marketMap[market].precisionForPrice : tokenMap[coinB].precision close = noGetValuePrecisionThousand ? depth.mid_price : getValuePrecisionThousand(depth.mid_price, precision, precision, precision, true) stob = close btos = Number(close) !== 0 ? noGetValuePrecisionThousand ? 1 / Number(close) : getValuePrecisionThousand( 1 / Number(close), tokenMap[coinA].precision, tokenMap[coinA].precision, tokenMap[coinA].precision, true, ) : 0 if (!tradePair === depth.symbol) { stob = btos btos = close } } // const isValidS2B = (stob !== 0 && stob !== undefined && !isNaN(stob)) return { btos: btos as string, stob: stob as string, close: close as string, } } return { btos: undefined, stob: undefined, close: undefined, } } export const reCalcStoB = >, C extends any>({ tokenMap, market, tradeData, tradePair, marketMap, noGetValuePrecisionThousand, }: { market: MarketType tradeData: T tradePair: MarketType marketMap?: any tokenMap?: any noGetValuePrecisionThousand?: boolean }): { stob: string; btos: string } | undefined => { const { marketMap: _marketMap, // marketArray: _marketArray, tokenMap: _tokenMap, } = store.getState().tokenMap if (tokenMap && marketMap) { } else { // marketArray = _marketArray; tokenMap = _tokenMap marketMap = _marketMap } // const marketPrecision = ? marketMap[market].precisionForPrice : 4; //@ts-ignore const [, coinA, coinB] = market.match(/([\w,#]+)-([\w,#]+)/i) // const tokenA = tokenMap[coinA]; // const tokenB =; if (tradeData?.sell.tradeValue && tradeData?.buy.tradeValue && tradeData?.sell.tradeValue !== 0) { const sellBig = sdk.toBig(tradeData?.sell.tradeValue ?? '') const buyBig = sdk.toBig(tradeData?.buy.tradeValue ?? '') const marketPrecision = marketMap[market].precisionForPrice ? marketMap[market].precisionForPrice : tokenMap[coinB].precision let stob, btos if (market === tradePair) { stob = noGetValuePrecisionThousand ? buyBig.div(sellBig).toString() : getValuePrecisionThousand( buyBig.div(sellBig).toString(), marketPrecision, marketPrecision, undefined, true, ) btos = noGetValuePrecisionThousand ? sellBig.div(buyBig).toString() : getValuePrecisionThousand(sellBig.div(buyBig).toString(), 6, 6, undefined, true) } else { stob = noGetValuePrecisionThousand ? buyBig.div(sellBig).toString() : getValuePrecisionThousand(buyBig.div(sellBig).toString(), 6, 6, undefined, true) btos = noGetValuePrecisionThousand ? sellBig.div(buyBig).toString() : getValuePrecisionThousand( sellBig.div(buyBig).toString(), marketPrecision, marketPrecision, undefined, true, ) } return { stob, btos } } else { return undefined } } export const marketInitCheck = ({ market, type, defaultValue = 'LRC-ETH', marketArray, tokenMap, marketMap, defaultA = 'LRC', coinMap, }: // defaultB = 'ETH', { market: string type?: 'sell' | 'buy' defaultValue?: string marketArray?: any tokenMap?: any marketMap?: any coinMap?: any defaultA?: string | null defaultB?: string | null }): { tradePair: MarketType } => { const { coinMap: _coinMap, marketMap: _marketMap, marketArray: _marketArray, tokenMap: _tokenMap, } = store.getState().tokenMap if (coinMap) { } else { coinMap = _coinMap } if (marketArray) { } else { marketArray = _marketArray tokenMap = _tokenMap marketMap = _marketMap coinMap = _coinMap } const { ammMap } = store.getState().amm.ammMap if (coinMap && tokenMap && marketMap && marketArray && ammMap) { let coinA: string = '#null', coinB: string = '#null' const result = market.match(/([\w,#]+)-([\w,#]+)/i) if (market && result) { ;[, coinA, coinB] = result } let whichCoinIndex = [coinA, coinB].findIndex((item) => item !== '#null') if (whichCoinIndex !== -1 && coinMap[[coinA, coinB][whichCoinIndex]] === undefined) { whichCoinIndex === 0 ? (coinA = defaultA as any) : (coinB = defaultA as any) } if (whichCoinIndex === -1) { whichCoinIndex = 0 coinA = defaultA as any } if (type === 'sell' && coinB !== '#null') { if (!tokenMap[coinA]?.tradePairs?.includes(coinB as never)) { coinB = tokenMap[coinA]?.tradePairs ? tokenMap[coinA]?.tradePairs[0] : undefined } } else if (coinB === '#null' || coinA === '#null') { if ( !tokenMap[[coinA, coinB][whichCoinIndex]]?.tradePairs.includes( [coinA, coinB][whichCoinIndex ^ 1] as never, ) ) { whichCoinIndex == 0 ? (coinB = tokenMap[[coinA, coinB][whichCoinIndex]]?.tradePairs[0]) : (coinA = tokenMap[[coinA, coinB][whichCoinIndex]]?.tradePairs[0]) } } return { tradePair: `${coinA}-${coinB}` } } return { tradePair: defaultValue as MarketType } } export const Limit = 14 export const vaultSwapDependAsync = ({ market, level = 0, limit, tokenMap, }: { market: MarketType level?: number limit?: number tokenMap?: any }): Promise<{ depth: sdk.DepthData | undefined }> => { return new Promise((resolve, reject) => { if (LoopringAPI.vaultAPI) { Promise.all([ LoopringAPI.vaultAPI?.getVaultDepth({ request: { market, level, limit, }, tokenMap, }, '1'), ]) .then(([responseDepth]) => { resolve({ depth: responseDepth?.depth, }) }) .catch() } else { reject(new CustomError(ErrorMap.NO_SDK)) } }) } ================================================ FILE: packages/core/src/hooks/help/marketRedPacket.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import { RawDataRedPacketDetailItem } from '@loopring-web/component-lib' import { getShortAddr, getValuePrecisionThousand } from '@loopring-web/common-resources' import { volumeToCountAsBigNumber } from './volumeToCount' import { store } from '../../stores' export const getUserReceiveList = ( claimList: sdk.LuckTokenClaim[], tokenInfo: sdk.TokenInfo, champion?: { accountId: number address: string ens: string amount: string | number }, ): { list: RawDataRedPacketDetailItem[] } => { const { accountId } = store.getState().account const list: RawDataRedPacketDetailItem[] = claimList.reduce((prev, item) => { const amountStr = getValuePrecisionThousand( volumeToCountAsBigNumber(tokenInfo.symbol, item.amount), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, }, ) + ' ' + tokenInfo.symbol const redPacketDetailItem: RawDataRedPacketDetailItem = { accountStr: item.claimer?.ens ? item.claimer.ens : getShortAddr(item.claimer?.address ?? ''), isSelf: accountId === item.claimer.accountId, amountStr, helper: (item.helper?.address ? item.helper?.ens ? item.helper.ens : getShortAddr(item.helper.address.toString()) : '' ).toString(), createdAt: item.createdAt, isMax: prev.find(item => item.isMax) ? false : champion?.accountId === item.claimer.accountId && champion?.amount === item.amount, rawData: item, } return [...prev, redPacketDetailItem] }, [] as RawDataRedPacketDetailItem[]) return { list } } export const getUserNFTReceiveList = ( claimList: sdk.LuckTokenClaim[], _nftInfo: sdk.UserNFTBalanceInfo, champion?: { accountId: number address: string ens: string amount: string | number }, ): { list: RawDataRedPacketDetailItem[] } => { const { accountId } = store.getState().account const list: RawDataRedPacketDetailItem[] = claimList.reduce((prev, item) => { const amountStr = 'x ' + getValuePrecisionThousand(item.amount, 0, 0, undefined, false, { floor: false, }) const redPacketDetailItem: RawDataRedPacketDetailItem = { accountStr: item.claimer?.ens ? item.claimer.ens : getShortAddr(item.claimer?.address ?? ''), isSelf: accountId === item.claimer.accountId, amountStr, helper: (item.referrer?.address ? item.referrer?.ens ? item.referrer.ens : getShortAddr(item.referrer.address.toString()) : '' ).toString(), createdAt: item.createdAt, isMax: prev.find(item => item.isMax) ? false : champion?.accountId === item.claimer.accountId && champion?.amount === item.amount, rawData: item, } return [...prev, redPacketDetailItem] }, [] as RawDataRedPacketDetailItem[]) return { list } } export const amountStrCallback = ( tokenMap: any, idIndex: any, tokenId: any, tokenAmount: string, ) => { const tokenInfo = tokenMap[idIndex[tokenId] ?? ''] if (tokenInfo && tokenAmount) { const symbol = tokenInfo.symbol const amount = getValuePrecisionThousand( volumeToCountAsBigNumber(symbol, tokenAmount), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) return { amountStr: amount + ' ' + symbol, amount, } } return {} // tokenMap[] } export const amountStrNFTCallback = (nftInfo: sdk.UserNFTBalanceInfo, tokenAmount: string) => { if (nftInfo && tokenAmount) { const symbol = 'NFT(s)' // nftInfo?.metadata?.base?.name ?? "NFT"; const amount = getValuePrecisionThousand(tokenAmount, 0, 0, undefined, false, { floor: false, // isTrade: true, }) return { amountStr: amount + ' ' + symbol, amount, } } return {} // tokenMap[] } export const makeViewCard = (luckToken: sdk.LuckyTokenItemForReceive) => { const { account, localStore: { redPacketHistory }, tokenMap: { idIndex, tokenMap }, system: { chainId }, } = store.getState() let claim: string | undefined = undefined let claimed = false if ( redPacketHistory[chainId] && redPacketHistory[chainId][account.accAddress] && redPacketHistory[chainId][account.accAddress][luckToken.hash] ) { const redPacket = redPacketHistory[chainId][account.accAddress][luckToken.hash] claim = redPacketHistory[chainId][account.accAddress][luckToken.hash].claim if (luckToken.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { claimed = redPacket.blindboxClaimed ? true : false } else { claimed = claim ? true : false } } const tokenInfo = tokenMap[idIndex[luckToken.tokenId] ?? ''] // if () return { chainId, account, amountStr: luckToken.isNft ? amountStrNFTCallback(luckToken.nftTokenInfo as any, luckToken.tokenAmount.totalAmount) .amount : amountStrCallback(tokenMap, idIndex, luckToken.tokenId, luckToken.tokenAmount.totalAmount) .amountStr, myAmountStr: claim && (luckToken.isNft ? amountStrNFTCallback(luckToken.nftTokenInfo as any, claim).amountStr : amountStrCallback(tokenMap, idIndex, luckToken.tokenId, claim).amountStr), tokenInfo, claim, claimed, } } ================================================ FILE: packages/core/src/hooks/help/marketTable.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI, store, tradeItemToTableDataItem } from '../../index' import { AmmRecordRow, AmmTradeType, RawDataTradeItem } from '@loopring-web/component-lib' import { volumeToCount } from './volumeToCount' import { myError, myLog } from '@loopring-web/common-resources' export const getUserTrades = (market: string) => { if (!LoopringAPI.userAPI) { return undefined } const { accountId, apiKey } = store.getState().account return LoopringAPI.userAPI .getUserTrades({ accountId, market }, apiKey) .then((response: { totalNum: any; userTrades: sdk.UserTrade[]; raw_data: any }) => { return response.userTrades }) } export const makeMarketArray = ( _coinKey: any, marketTrades: sdk.MarketTradeInfo[], ): RawDataTradeItem[] => { let tradeArray: Array> = [] const { tokenMap } = store.getState().tokenMap marketTrades.forEach((item: sdk.MarketTradeInfo) => { try { if (tokenMap) { tradeArray.push(tradeItemToTableDataItem(item)) } } catch (error: any) { myError(error) } }) return tradeArray as RawDataTradeItem[] } export const getUserAmmTransaction = ({ address, offset, limit, txStatus }: any) => { const { accountId, apiKey } = store.getState().account return LoopringAPI.ammpoolAPI ?.getUserAmmPoolTxs( { accountId, ammPoolAddress: address, limit, offset, txStatus, }, apiKey, ) .then(({ userAmmPoolTxs, totalNum }) => { return { userAmmPoolTxs, totalNum, } }) } // getAmmPoolTxs export const getRecentAmmTransaction = ({ address, offset, limit }: any) => { return LoopringAPI.ammpoolAPI ?.getAmmPoolTxs({ poolAddress: address, limit, offset, }) .then(({ transactions, totalNum }) => { return { ammPoolTrades: transactions, totalNum, } }) } export const makeMyAmmMarketArray = ( _coinKey: string | undefined, marketTransaction: sdk.UserAmmPoolTx[], ): AmmRecordRow[] => { const tradeArray: Array> & { totalBalance: number }> = [] const { tokenMap, coinMap, idIndex } = store.getState().tokenMap if (marketTransaction) { marketTransaction.forEach((item: sdk.UserAmmPoolTx) => { try { if ( coinMap && tokenMap && idIndex /* && !(coinKey && tokenMap['LP-'+coinKey].tokenId !== item.lpToken.tokenId) */ ) { // @ts-ignore const [, coinA, coinB] = idIndex[item.lpToken.tokenId].match(/LP-(\w+)-(\w+)/i) const balance = item.lpToken.actualAmount tradeArray.push({ type: item.txType === sdk.AmmTxType.JOIN ? AmmTradeType.add : AmmTradeType.remove, totalDollar: 0, totalBalance: Number(balance), amountA: volumeToCount(coinA, item.poolTokens[0]?.actualAmount), amountB: volumeToCount(coinB, item.poolTokens[1]?.actualAmount), time: Number(item.updatedAt), // @ts-ignore coinA: coinMap[coinA], // @ts-ignore coinB: coinMap[coinB], status: item.txStatus, }) } return tradeArray } catch (error: any) { //CATCHERROR: myLog('marketTransaction::', error) // new CustomError() } }) } return tradeArray as AmmRecordRow[] } ================================================ FILE: packages/core/src/hooks/help/parseRabbitConfig.ts ================================================ import { MapChainId } from "@loopring-web/common-resources" import { ChainId } from "@loopring-web/loopring-sdk" export const parseRabbitConfig = ( config: any, fromNetwork: string, idIndex: any, ): { toTaikoNetwork: string | undefined toTaikoNetworkSupportedTokens: string[] toL1SupportedTokens: string[] } => { if (!config) return { toTaikoNetwork: undefined, toTaikoNetworkSupportedTokens: [], toL1SupportedTokens: [], } const toNetworks: string[] = config.fromToNetworks[fromNetwork] ?? [] const toTaikoNetwork = toNetworks.find((net) => [ChainId.TAIKO, ChainId.TAIKOHEKLA].map((id) => MapChainId[id]).includes(net), ) const networkL2TokenIds = config.networkL2TokenIds[fromNetwork] ?? [] const toTaikoNetworkL1Tokens = toTaikoNetwork ? config.networkL1Tokens[toTaikoNetwork] ?? {} : {} const toSelfL1Tokens = config.networkL1Tokens[fromNetwork] const toTaikoNetworkSupportedTokens = networkL2TokenIds .map((id: number) => { return idIndex[id] }) .filter((symbol) => { return toTaikoNetworkL1Tokens[symbol] }) const toL1SupportedTokens = networkL2TokenIds .map((id: number) => { return idIndex[id] }) .filter((symbol) => { return toSelfL1Tokens[symbol] }) return { toTaikoNetwork: toTaikoNetwork, toTaikoNetworkSupportedTokens, toL1SupportedTokens: toL1SupportedTokens as string[], } } export const networkById = (id: ChainId) => { return MapChainId[id] ? (167009 === id ? 'TAIKO' : MapChainId[id]) : undefined } export const parseRabbitConfig2 = ( config: any, fromNetwork: string, fromNetworkIdIndex: any, ): { toOtherNetworks: { network: string supportedTokens: string[] }[] agentId?: number agentAddr?: string exchange?: string } => { if (!config) return { toOtherNetworks: [], } const toNetworks: string[] = config.fromToNetworks[fromNetwork] ?? [] const toOtherNetworks = toNetworks.filter(otherNetwork => otherNetwork !== fromNetwork).map((otherNetwork) => { const networkL2TokenIds = config.networkL2TokenIds[fromNetwork] ?? [] const toOtherNetworkL1Tokens = config.networkL1Tokens[otherNetwork] ?? {} const supportedTokens = networkL2TokenIds .map((id: number) => { return fromNetworkIdIndex[id] }) .filter((symbol) => { return toOtherNetworkL1Tokens[symbol] }) return { network: otherNetwork, supportedTokens, // agentId, // agentAddr, // exchange, } }) const agentId = config.networkL2AgentAccountIds[fromNetwork] const agentAddr = config.networkL2AgentAddresses[fromNetwork] const exchange = config.networkExchanges[fromNetwork] return { toOtherNetworks, agentId, agentAddr, exchange, } } ================================================ FILE: packages/core/src/hooks/help/providorConnect.ts ================================================ import { accountReducer, store } from '../../stores' import { ConnectProviders, connectProvides, ErrorType, walletServices, } from '@loopring-web/web3-provider' import { myLog, SUPPORTING_NETWORKS, ThemeType } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { updateSystem } from '../../stores/system/reducer' import { setDefaultNetwork } from '@loopring-web/component-lib' const providerCallback = async () => { const { defaultNetwork } = store.getState().settings if (connectProvides.usedProvide) { let chainId: sdk.ChainId = Number(await connectProvides.usedWeb3?.eth.getChainId()) if (!SUPPORTING_NETWORKS.includes(chainId.toString())) { chainId = sdk.ChainId.MAINNET } if (chainId !== defaultNetwork) { store.dispatch(updateSystem({ chainId })) store.dispatch(setDefaultNetwork(chainId)) } return } } export const metaMaskCallback = async () => { const { defaultNetwork, themeMode, isMobile } = store.getState().settings if (!isMobile) { store.dispatch( accountReducer.updateAccountStatus({ connectName: ConnectProviders.MetaMask, }), ) } await connectProvides.MetaMask({ darkMode: themeMode === ThemeType.dark, chainId: defaultNetwork.toString(), }) if (isMobile) { myLog('connectProvides', connectProvides) store.dispatch( accountReducer.updateAccountStatus({ connectName: connectProvides.provideName, }), ) } providerCallback() } export const CoinbaseCallback = async () => { const { defaultNetwork, themeMode } = store.getState().settings store.dispatch( accountReducer.updateAccountStatus({ connectName: ConnectProviders.Coinbase, }), ) await connectProvides.Coinbase({ darkMode: themeMode === ThemeType.dark, chainId: defaultNetwork.toString(), }) providerCallback() } export const gameStopCallback = async () => { const { defaultNetwork, themeMode } = store.getState().settings store.dispatch( accountReducer.updateAccountStatus({ connectName: ConnectProviders.GameStop, }), ) await connectProvides.GameStop({ darkMode: themeMode === ThemeType.dark, chainId: defaultNetwork.toString(), }) // statusAccountUnset(); providerCallback() } export const walletConnectCallback = async () => { const { defaultNetwork, themeMode } = store.getState().settings store.dispatch( accountReducer.updateAccountStatus({ connectName: ConnectProviders.WalletConnect, }), ) try { await connectProvides .WalletConnect({ darkMode: themeMode === ThemeType.dark, chainId: defaultNetwork, }) .catch((error) => { throw error }) } catch (error) { walletServices.sendError(ErrorType.FailedConnect, { connectName: ConnectProviders.WalletConnect, error: (error as any)?.message ?? error, }) } providerCallback() } // export const walletConnectV1Callback = async () => { // const { themeMode } = store.getState().settings // store.dispatch( // accountReducer.updateAccountStatus({ // connectName: ConnectProviders.WalletConnectV1, // }), // ) // if (!window.process) { // window.process = process // } // // // await connectProvides.WalletConnectV1({ // // darkMode: themeMode === ThemeType.dark, // // // chainId: defaultNetwork, // // }) // providerCallback() // } ================================================ FILE: packages/core/src/hooks/help/useAmmTotalValue.ts ================================================ import React from 'react' import { volumeToCount, useWalletLayer2, useTokenPrices, useSystem } from '../../index' import { Currency } from '@loopring-web/loopring-sdk' export const useAmmTotalValue = () => { const { walletLayer2 } = useWalletLayer2() const { tokenPrices } = useTokenPrices() const { forexMap } = useSystem() type GetAmmLiquidityProps = { market: string balance?: number currency?: Currency } const getAmmLiquidity = React.useCallback( ({ market, balance, currency = Currency.usd }: GetAmmLiquidityProps) => { const price = tokenPrices && tokenPrices[market] let curBalance = 0 if (balance) { curBalance = balance } else { // if balance is not given, use walletl2 total lp token balance instead curBalance = Number( Object.entries(walletLayer2 || {}).find(([token]) => token === market)?.[1].total || 0, ) } const formattedBalance = volumeToCount(market, curBalance) const unit = forexMap[currency] return (price || 0) * (formattedBalance || 0) * (unit as number) }, [forexMap, tokenPrices, walletLayer2], ) return { getAmmLiquidity, } } ================================================ FILE: packages/core/src/hooks/help/volumeToCount.ts ================================================ import { store } from '../../index' import * as sdk from '@loopring-web/loopring-sdk' import BigNumber from 'bignumber.js' export const volumeToCount = ( symbol: string, volumn: string | number | BigNumber, tokenMap = store.getState().tokenMap.tokenMap, ): number | undefined => { const result = volumeToCountAsBigNumber(symbol, volumn, tokenMap) return result ? result.toNumber() : undefined } export const volumeToCountAsBigNumber = ( symbol: string, volumn: string | number | BigNumber, tokenMap = store.getState().tokenMap.tokenMap, ): BigNumber | undefined => { if (tokenMap && tokenMap[symbol] && typeof volumn !== 'undefined') { try { return sdk.toBig(volumn).div('1e' + tokenMap[symbol].decimals) } catch (error: any) { throw error } } else { return undefined } } export const volumeToBigIntCount = ( symbol: string, volumn: string | number | bigint, tokenMap = store.getState().tokenMap.tokenMap, ): string | undefined => { const result = volumeToCountAsBigInt(symbol, volumn, tokenMap) return result ? result.toString(10) : undefined } export const volumeToCountAsBigInt = ( symbol: string, volumn: string | number | bigint, tokenMap = store.getState().tokenMap.tokenMap, ): bigint | undefined => { if (tokenMap && tokenMap[symbol] && typeof volumn !== 'undefined') { try { return BigInt(volumn) / BigInt(Number('1e' + tokenMap[symbol].decimals)) // return toBig(volumn).div('1e' + tokenMap[symbol].decimals) } catch (error: any) { throw error } } else { return undefined } } export const getTokenNameFromTokenId = ( tokenId: number | string, tokenMap = store.getState().tokenMap.tokenMap, ) => { if (tokenMap) { const valueList = Object.values(tokenMap) const hasToken = valueList.find((o) => o.tokenId === tokenId) if (hasToken) { return hasToken.symbol } return '' } return '' } ================================================ FILE: packages/core/src/hooks/index.ts ================================================ export * from './common' export * from './help' export * from './useractions' export * from './rooters' ================================================ FILE: packages/core/src/hooks/rooters/index.ts ================================================ export * from './useLocationChange' ================================================ FILE: packages/core/src/hooks/rooters/useLocationChange.ts ================================================ import { useLocation } from 'react-router-dom' import { Location } from 'history' import React from 'react' const usePrevious = (value: Location): Location => { const ref = React.useRef>() React.useEffect(() => { ref.current = value }) return ref.current as Location } export const useLocationChange = ( action: (locatin: Location, prevLocation: Location) => void, ) => { const location = useLocation() const prevLocation = usePrevious(location) React.useEffect(() => { action(location, prevLocation) }, [location]) } ================================================ FILE: packages/core/src/hooks/useractions/hookAmmCommon.ts ================================================ import React from 'react' import { AccountStatus, SagaStatus, AmmDetail, myLog, ErrorMap, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI, useTokenMap, useSocket, useToast, usePageAmmPool, store, useAmmMap, AmmDetailStore, useTicker, makeWalletLayer2, calcPriceByAmmTickMapDepth, initSlippage, useWalletLayer2Socket, useUserRewards, } from '../../index' import { ToastType, useOpenModals } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' const useAmmSocket = ({ market }: { market: string }) => { const { sendSocketTopic, socketEnd } = useSocket() const { ammMap } = useAmmMap() React.useEffect(() => { const ammInfo: AmmDetail = ammMap['AMM-' + market] if (ammInfo?.address) { sendSocketTopic({ [sdk.WsTopicType.ammpool]: ammInfo?.address ? [ammInfo.address] : [], [sdk.WsTopicType.ticker]: [`${ammInfo.market}`], }) } else { socketEnd() } return () => { socketEnd() } }, [market]) } export function usePairInit({ ammInfo }: { ammInfo: AmmDetailStore }) { const { tickerMap } = useTicker() const { coinMap, tokenMap } = useTokenMap() const [pairInit] = React.useState(() => { let coinACount = 0, coinBCount = 0, percentage = 0 const coinLp = 'LP-' + ammInfo.market const { walletMap } = makeWalletLayer2({ needFilterZero: false }) let ammCalcData: any = { coinInfoMap: coinMap, } const lpToken = tokenMap[coinLp] const { totalLPToken, totalA, totalB } = ammInfo const { close: _close, stob, btos, } = calcPriceByAmmTickMapDepth({ market: ammInfo.market as any, tradePair: ammInfo.market as any, dependencyData: { ammPoolSnapshot: { poolName: ammInfo.name, poolAddress: ammInfo.address, pooled: [ { tokenId: tokenMap[ammInfo.coinA].tokenId, volume: ammInfo.tokens?.pooled[0] ?? 0, }, { tokenId: tokenMap[ammInfo.coinB].tokenId, volume: ammInfo.tokens?.pooled[1] ?? 0, }, ], //[ammInfo., TokenVolumeV3]; lp: { tokenId: lpToken?.tokenId, volume: ammInfo.tokens.lp, }, risky: false, }, ticker: tickerMap[ammInfo?.market], depth: undefined, }, }) ammCalcData = { ...ammCalcData, AtoB: stob, BtoA: btos, myCoinA: { belong: ammInfo.coinA, balance: walletMap ? walletMap[ammInfo?.coinA ?? '']?.count : undefined, tradeValue: undefined, }, myCoinB: { belong: ammInfo.coinB, balance: walletMap ? walletMap[ammInfo?.coinB ?? 0]?.count : undefined, tradeValue: undefined, }, } const lpBalance = walletMap ? walletMap[coinLp ?? '']?.count : 0 if (totalA && totalLPToken && totalB) { percentage = totalLPToken ? sdk .toBig(lpBalance ?? 0) .div(totalLPToken) .toNumber() : 0 coinACount = totalA * percentage coinBCount = totalB * percentage } ammCalcData = { ...ammCalcData, lpCoin: { belong: coinLp, balance: lpBalance }, lpCoinA: { belong: ammInfo.coinA, balance: coinACount, }, lpCoinB: { belong: ammInfo.coinB, balance: coinBCount, }, percentage, } return { ammJoin: { ammData: { coinA: { ...ammCalcData.myCoinA, tradeValue: undefined }, coinB: { ...ammCalcData.myCoinB, tradeValue: undefined }, coinLP: { ...ammCalcData.lpCoin, tradeValue: undefined }, slippage: initSlippage, }, ammCalcData, }, ammExit: { ammData: { coinA: { ...ammCalcData.myCoinA, tradeValue: undefined }, coinB: { ...ammCalcData.myCoinB, tradeValue: undefined }, coinLP: { ...ammCalcData.lpCoin, tradeValue: undefined }, slippage: initSlippage, }, ammCalcData, // ...feePatch, }, } }) return { ...pairInit } } export const useAmmCommon = ({ market }: { market: string }) => { const { t } = useTranslation() const { getUserRewards } = useUserRewards() const { toastOpen, setToastOpen, closeToast } = useToast() const { updateRealTimeAmmMap, ammMap } = useAmmMap() const { marketArray, tokenMap } = useTokenMap() const { setShowAccount } = useOpenModals() const { updatePageAmmExit, updatePageAmmJoin } = usePageAmmPool() const ammInfo = ammMap['AMM-' + market] const { ammExit, ammJoin } = usePairInit({ ammInfo, }) const updateAmmPoolSnapshot = React.useCallback(async () => { const { ammMap } = store.getState().amm.ammMap myLog('ammCommon', 'market', market) if (market && market && LoopringAPI.ammpoolAPI) { const ammInfo: any = ammMap['AMM-' + market] LoopringAPI.ammpoolAPI .getAmmPoolSnapshot({ poolAddress: ammInfo.address, }) .then((response) => { if ( !response || (response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message ) { throw (response as sdk.RESULT_INFO).message } const { ammPoolSnapshot } = response updateRealTimeAmmMap({ ammPoolStats: { ['AMM-' + market]: { ...ammMap['AMM-' + market].__rawConfig__, liquidity: [ammPoolSnapshot.pooled[0].volume, ammPoolSnapshot.pooled[1].volume], lpLiquidity: ammPoolSnapshot.lp.volume, }, } as any, }) }) } await Promise.race([updateExitFee, updateJoinFee]) setShowAccount({ isShow: false }) }, [marketArray, market]) const refreshRef = React.createRef() const getFee = async ( requestType: sdk.OffchainFeeReqType.AMM_EXIT | sdk.OffchainFeeReqType.AMM_JOIN, ) => { const account = store.getState().account const { ammMap } = store.getState().amm.ammMap const ammInfo = ammMap['AMM-' + market] if ( ammInfo?.coinB && LoopringAPI.userAPI && account.status == SagaStatus.UNSET && account.readyState == AccountStatus.ACTIVATED && tokenMap ) { const feeToken: sdk.TokenInfo = tokenMap[ammInfo.coinB ?? ''] const request: sdk.GetOffchainFeeAmtRequest = { accountId: account.accountId, requestType, tokenSymbol: ammInfo?.coinB, } const { fees } = await LoopringAPI.userAPI?.getOffchainFeeAmt(request, account.apiKey) const feeRaw = fees[ammInfo.coinB] ? fees[ammInfo.coinB].fee : 0 const fee = sdk .toBig(feeRaw) .div('1e' + feeToken.decimals) .toNumber() // myLog("new fee:", fee.toString()); return { fee, feeRaw, fees, } } else { throw 'not ready' } } const updateExitFee = React.useCallback(async () => { const account = store.getState().account if (account.readyState === AccountStatus.ACTIVATED && ammInfo?.coinB) { try { const feeInfo = await getFee(sdk.OffchainFeeReqType.AMM_EXIT) const { walletMap } = makeWalletLayer2({ needFilterZero: false }) const { ammExit: _ammExit } = store.getState()._router_pageAmmPool let { ammCalcData, ammData } = _ammExit const lpBalance = walletMap ? walletMap['LP-' + ammInfo.market]?.count : 0 let percentage, coinACount, coinBCount if (ammInfo.totalA && ammInfo.totalLPToken && ammInfo.totalB) { percentage = sdk .toBig(lpBalance ?? 0) .div(ammInfo?.totalLPToken ? ammInfo?.totalLPToken : 1) //totalLPToken ? lpBalance / totalLPToken : 0; coinACount = sdk.toBig(ammInfo.totalA).times(percentage) coinBCount = sdk.toBig(ammInfo.totalB).times(percentage) } if (!ammCalcData) { ammCalcData = ammExit.ammCalcData ammData = ammExit.ammData } updatePageAmmExit({ ammData: { ...ammData, coinLP: { ...ammData?.coinLP, balance: lpBalance, } as any, }, ammCalcData: { ...ammCalcData, lpCoin: { ...ammCalcData?.lpCoin, balance: lpBalance, }, lpCoinA: { ...ammCalcData?.lpCoinA, balance: coinACount, }, lpCoinB: { ...ammCalcData?.lpCoinB, balance: coinBCount, }, percentage, fee: feeInfo?.fee, fees: feeInfo?.fees, } as any, fee: feeInfo?.fee, fees: feeInfo?.fees, }) } catch (error) { console.log(error) setToastOpen({ open: true, type: ToastType.error, content: t(ErrorMap.NO_NETWORK_ERROR.messageKey, { ns: ToastType.error, }), }) } } else { const { ammExit: { ammCalcData }, } = store.getState()._router_pageAmmPool if (!ammCalcData) { updatePageAmmExit({ ammData: { ...ammExit.ammData, coinLP: { ...ammExit.ammData?.coinLP, balance: undefined, } as any, }, ammCalcData: { ...ammExit.ammCalcData, lpCoin: { ...ammExit.ammCalcData.lpCoin, balance: undefined, }, lpCoinA: { ...ammExit.ammCalcData.lpCoinA, balance: undefined, }, lpCoinB: { ...ammExit.ammCalcData.lpCoinB, balance: undefined, }, percentage: 0, fee: undefined, fees: undefined, } as any, fee: undefined, fees: undefined, }) } } }, [ammExit]) const updateJoinFee = React.useCallback(async () => { const account = store.getState().account if (ammInfo?.market && account.readyState === AccountStatus.ACTIVATED) { const feeInfo = await getFee(sdk.OffchainFeeReqType.AMM_JOIN) const { walletMap } = makeWalletLayer2({ needFilterZero: false }) const { ammJoin: _ammJoin } = store.getState()._router_pageAmmPool let { ammCalcData, ammData } = _ammJoin if (!ammCalcData) { ammCalcData = ammJoin.ammCalcData ammData = ammJoin.ammData } const coinA = { ...ammData?.coinA, balance: walletMap ? walletMap[ammInfo.coinA]?.count : undefined, } as any const coinB = { ...ammData?.coinB, balance: walletMap ? walletMap[ammInfo.coinB]?.count : undefined, } as any updatePageAmmJoin({ ammData: { ...ammData, coinA, coinB, }, ammCalcData: { ...ammCalcData, myCoinA: coinA, myCoinB: coinB, fee: feeInfo?.fee, fees: feeInfo?.fees, } as any, fee: feeInfo?.fee, fees: feeInfo?.fees, }) } else { const { ammJoin: { ammCalcData }, } = store.getState()._router_pageAmmPool if (!ammCalcData) { updatePageAmmJoin({ ammData: { ...ammJoin.ammData, coinA: { ...ammJoin.ammData.coinA, balance: undefined, } as any, coinB: { ...ammJoin.ammData.coinB, balance: undefined, } as any, }, ammCalcData: { ...ammJoin.ammCalcData, myCoinA: { ...ammJoin.ammCalcData.myCoinA, balance: undefined, }, myCoinB: { ...ammJoin.ammCalcData.myCoinB, balance: undefined, }, fee: 0, fees: undefined, } as any, fee: 0, fees: undefined, }) } } }, [ammJoin]) const walletLayer2Callback = React.useCallback(async () => { getUserRewards() }, []) useWalletLayer2Socket({ walletLayer2Callback }) useAmmSocket({ market }) return { toastOpen, setToastOpen, closeToast, refreshRef, updateAmmPoolSnapshot, getFee, ammExit, ammJoin, updateExitFee, updateJoinFee, } } ================================================ FILE: packages/core/src/hooks/useractions/hookAmmExit.ts ================================================ import React from 'react' import { AccountStatus, AmmExitData, getValuePrecisionThousand, IBData, myLog, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_QUICK_AUTO_CLOSE, TradeBtnStatus, } from '@loopring-web/common-resources' import { setShowTradeIsFrozen, ToastType, useToggle } from '@loopring-web/component-lib' import { DAYS, getTimestampDaysLater, IdMap, LoopringAPI, makeCache, store, useAccount, useAmmMap, usePageAmmPool, useSubmitBtn, useTokenMap, useUserRewards, useWalletLayer2Socket, walletLayer2Service, } from '../../index' import * as sdk from '@loopring-web/loopring-sdk' import { useTranslation } from 'react-i18next' export const useAmmExit = ({ market, // getFee, setToastOpen, setConfirmExitSmallOrder, updateExitFee, refreshRef, }: // ammCalcDefault, // ammDataDefault, { market: string updateExitFee: () => Promise // getFee: (requestType: sdk.OffchainFeeReqType.AMM_EXIT) => any; setToastOpen: any setConfirmExitSmallOrder: (props: { open: boolean; type: 'Disabled' | 'Mini' }) => void refreshRef: React.Ref // ammCalcDefault: Partial>; // ammDataDefault: Partial, string>>; }) => { const { ammExit: { fees, request, ammCalcData, ammData }, updatePageAmmExit, } = usePageAmmPool() const { t } = useTranslation(['common', ToastType.error]) const [isLoading, setIsLoading] = React.useState(false) const { idIndex, tokenMap } = useTokenMap() const { ammMap } = useAmmMap() const { account } = useAccount() const ammInfo = ammMap['AMM-' + market] const { toggle: { exitAmm }, } = useToggle() const [lpMinAmt, setLpMinAmt] = React.useState(undefined) const lpToken = tokenMap['LP-' + ammInfo.market] const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const ammInfo = ammMap['AMM-' + market] if (account.readyState === AccountStatus.ACTIVATED) { const { ammData, fee } = store.getState()._router_pageAmmPool.ammExit const validAmt = ammData?.coinLP?.tradeValue ? sdk .toBig(ammData?.coinLP?.tradeValue ?? 0) .gte(sdk.toBig(lpMinAmt?.replaceAll(sdk.SEP, '') ?? 0)) : false myLog('availableTradeCheck AMM exit validAmt: fee, lpMinAmt', fee, lpMinAmt?.toString()) if (isLoading || !(ammInfo !== undefined && ammInfo?.market)) { return { tradeBtnStatus: TradeBtnStatus.LOADING, label: '' } } else { if ( ammData === undefined || ammData?.coinLP?.tradeValue === undefined || ammData?.coinLP?.tradeValue === 0 || fee === undefined || !lpMinAmt || fee === 0 ) { myLog('will DISABLED! ', ammData, fee) return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelEnterAmount', } } else if (!validAmt) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelLimitMin| ${lpMinAmt} ${ammData?.coinLP?.belong} `, } } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '', } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } }, [account.readyState, ammMap, ammInfo, isLoading, lpMinAmt]) const submitAmmExit = React.useCallback(async () => { setIsLoading(true) // updatePageAmmExitBtn({ btnStatus: TradeBtnStatus.LOADING }); if (ammInfo?.exitDisable) { setShowTradeIsFrozen({ isShow: true, messageKey: 'labelNoticeForMarketFrozen', type: t('labelAmmExit') + ` ${ammInfo?.__rawConfig__.name}`, }) setIsLoading(false) } else if (!exitAmm.enable) { setShowTradeIsFrozen({ isShow: true, type: t('labelAmmExit') + ` ${ammInfo?.__rawConfig__.name}`, }) setIsLoading(false) } else { sendRequest() } }, [request, account, t, updatePageAmmExit]) const onSubmitBtnClick = React.useCallback( async (_props) => { const ammExit = store.getState()._router_pageAmmPool.ammExit if (ammExit.ammData.coinLP.tradeValue && ammExit.volB_show) { // quoteValue < feeValue const validAmt = ammData?.coinLP?.tradeValue && ammExit.volB_show ? sdk.toBig(ammExit.volB_show).gte(ammExit.fee) : false // Lp value 15% will be charge for Fee should confirm, so for quote token is 15%*2 = 0.3 const validMiniAmt = ammData?.coinLP?.tradeValue && ammExit.volB_show ? sdk.toBig(ammExit.volB_show * 0.3).gte(ammExit.fee) : false if (!validAmt) { // quoteValue < feeValue setConfirmExitSmallOrder({ open: true, type: 'Disabled' }) } else if (!validMiniAmt) { // Lp value 15% will be charge for Fee should confirm, so for quote token is 15%*2 = 0.3 setConfirmExitSmallOrder({ open: true, type: 'Mini' }) } else { submitAmmExit() } } }, [tokenMap, submitAmmExit], ) const { btnStatus, onBtnClick, btnLabel } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback: onSubmitBtnClick, }) const updateMiniTradeValue = React.useCallback(() => { const { ammExit: { fees, ammCalcData, ammData: { slippage }, }, } = store.getState()._router_pageAmmPool const ammPoolSnapshot: any = { poolName: ammInfo.name, poolAddress: ammInfo.address, pooled: [ { tokenId: tokenMap[ammInfo.coinA].tokenId, volume: ammInfo.tokens.pooled[0], }, { tokenId: tokenMap[ammInfo.coinB].tokenId, volume: ammInfo.tokens.pooled[1], }, ], //[ammInfo., TokenVolumeV3]; lp: { tokenId: tokenMap['LP-' + ammInfo.market].tokenId, volume: ammInfo.tokens?.lp as any, }, risky: false, } if (ammCalcData && ammCalcData.lpCoin?.belong) { const { miniLpVal } = sdk.makeExitAmmPoolMini( '0', ammPoolSnapshot, tokenMap as any, idIndex as IdMap, ) let miniFeeLpWithSlippageVal = '' const slippageReal = sdk .toBig(slippage ?? 0.1) .div(100) .toString() if (fees) { const result = sdk.makeExitAmmCoverFeeLP( fees, ammPoolSnapshot, tokenMap, idIndex, slippageReal, ) miniFeeLpWithSlippageVal = result.miniFeeLpWithSlippageVal } setLpMinAmt(() => { const miniVal = sdk .toBig( sdk.toBig(miniFeeLpWithSlippageVal ?? 0).gte(miniLpVal) ? miniFeeLpWithSlippageVal : miniLpVal, ) .times(1.1) myLog( 'updateMiniTradeValue: miniFeeLpWithSlippage, miniLpVal, miniVal = great one * 1.1 ', miniFeeLpWithSlippageVal.toString(), miniLpVal.toString(), miniVal.toString(), ) return getValuePrecisionThousand( miniVal, lpToken.precision, lpToken.precision, lpToken.precision, false, { floor: false }, ) }) } }, [tokenMap, idIndex]) React.useEffect(() => { // const quote: TokenVolumeV3 = ammPoolSnapshot.pooled[1]; if (ammInfo?.market && fees && ammData.slippage) { myLog('updateMiniTradeValue: fees, slippage', fees, ammData.slippage, ammInfo?.totalLPToken) updateMiniTradeValue() } }, [ammInfo?.tokens?.pooled, fees, ammData.slippage]) React.useEffect(() => { updateExitFee() }, [ammInfo?.tokens?.pooled]) const handleAmmPoolEvent = React.useCallback( (data: AmmExitData>, _type: 'coinA' | 'coinB') => { const ammExit = store.getState()._router_pageAmmPool.ammExit const ammInfo = ammMap['AMM-' + market] const { slippage } = data const slippageReal = sdk.toBig(slippage).div(100).toString() let newAmmData = { ...ammData, ...ammExit.ammData, } let rawVal: any = data.coinLP.tradeValue let ammDataPatch = {} if (rawVal === undefined) { rawVal = '0' } const ammPoolSnapshot: any = { poolName: ammInfo.name, poolAddress: ammInfo.address, pooled: [ { tokenId: tokenMap[ammInfo.coinA].tokenId, volume: ammInfo.tokens.pooled[0], }, { tokenId: tokenMap[ammInfo.coinB].tokenId, volume: ammInfo.tokens.pooled[1], }, ], //[ammInfo., TokenVolumeV3]; lp: { tokenId: tokenMap['LP-' + ammInfo.market].tokenId, volume: ammInfo.tokens?.lp as any, }, risky: false, } if (data.coinLP.tradeValue !== undefined && ammExit.ammCalcData) { const { volA_show, volB_show, request } = sdk.makeExitAmmPoolRequest2( rawVal.toString(), slippageReal, account.accAddress, ammExit.ammCalcData?.fees as sdk.LoopringMap, ammPoolSnapshot, tokenMap as any, idIndex as IdMap, 0, ) newAmmData.coinA = { ...ammData.coinA, tradeValue: volA_show as any } newAmmData.coinB = { ...ammData.coinB, tradeValue: volB_show as any } ammDataPatch = { request, volA_show, volB_show } updatePageAmmExit({ ...ammDataPatch, ammData: { ...newAmmData, coinLP: data.coinLP, slippage: data.slippage, }, }) } }, [market], ) const sendRequest = React.useCallback(async () => { const ammExit = store.getState()._router_pageAmmPool.ammExit try { if ( LoopringAPI.ammpoolAPI && LoopringAPI.userAPI && ammExit.request && account?.eddsaKey?.sk && ammInfo?.domainSeparator ) { // let req = _.cloneDeep(request); const patch: sdk.AmmPoolRequestPatch = { chainId: store.getState().system.chainId as sdk.ChainId, ammName: ammInfo?.__rawConfig__.name ?? '', poolAddress: ammInfo?.address ?? '', eddsaKey: account.eddsaKey.sk, } const burnedReq: sdk.GetNextStorageIdRequest = { accountId: account.accountId, sellTokenId: ammExit.request.exitTokens.burned.tokenId as number, } const storageId0 = await LoopringAPI.userAPI.getNextStorageId(burnedReq, account.apiKey) myLog('exit ammpool request:', { ...ammExit.request, domainSeparator: ammInfo.domainSeparator, storageId: storageId0.offchainId, validUntil: getTimestampDaysLater(DAYS), }) const response = await LoopringAPI.ammpoolAPI .exitAmmPool( { ...ammExit.request, domainSeparator: ammInfo.domainSeparator, storageId: storageId0.offchainId, validUntil: getTimestampDaysLater(DAYS), }, patch, account.apiKey, ) .finally() if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else { setToastOpen({ open: true, type: ToastType.success, content: t('labelExitAmmSuccess'), }) } if (ammExit.ammData?.__cache__) { makeCache(ammExit.ammData?.__cache__) } } else { throw new Error('api not ready') } } catch (error: any) { if (error?.message === 'api not ready') { setToastOpen({ open: true, type: ToastType.error, content: t('labelExitAmmFailed'), }) } else if ((error as sdk.RESULT_INFO)?.code) { const errorItem = SDK_ERROR_MAP_TO_UI[(error as sdk.RESULT_INFO)?.code ?? 700001] if ([102024, 102025, 114001, 114002].includes((error as sdk.RESULT_INFO)?.code || 0)) { await updateExitFee() } setToastOpen({ open: true, type: ToastType.error, content: t('labelExitAmmFailed') + ' error: ' + (errorItem ? t(errorItem.messageKey, { ns: ToastType.error }) : (error as sdk.RESULT_INFO).message), }) } else if (error?.message) { sdk.dumpError400(error) setToastOpen({ open: true, type: ToastType.error, content: t('labelExitAmmFailed'), }) } } updatePageAmmExit({ ammData: { ...ammData, ...{ coinLP: { ...ammData.coinLP, tradeValue: 0 }, }, }, }) walletLayer2Service.sendUserUpdate() // @ts-ignore refreshRef?.current?.firstElementChild.click() await sdk.sleep(SUBMIT_PANEL_QUICK_AUTO_CLOSE) setIsLoading(false) // await updateExitFee(); }, [ammData, account, ammInfo]) const walletLayer2Callback = React.useCallback(async () => { updateExitFee() }, []) useWalletLayer2Socket({ walletLayer2Callback }) return { ammCalcData, ammData: ammData, propsLPExtends: { coinPrecision: tokenMap['LP-' + ammInfo.market].precision, }, handleAmmPoolEvent, btnStatus, onAmmClick: onBtnClick, btnI18nKey: btnLabel, // updateExitFee, exitSmallOrderCloseClick: (isAgree = false) => { if (isAgree) { submitAmmExit() } }, } } ================================================ FILE: packages/core/src/hooks/useractions/hookAmmJoin.ts ================================================ import React from 'react' import { AccountStatus, AmmJoinData, getValuePrecisionThousand, IBData, myLog, SDK_ERROR_MAP_TO_UI, TradeBtnStatus, } from '@loopring-web/common-resources' import { ToastType, useOpenModals, useToggle } from '@loopring-web/component-lib' import { DAYS, getTimestampDaysLater, LoopringAPI, makeCache, store, useAccount, useAmmMap, usePageAmmPool, useSubmitBtn, useSystem, useTokenMap, useUserRewards, useWalletLayer2Socket, walletLayer2Service, } from '../../index' import * as sdk from '@loopring-web/loopring-sdk' import { useTranslation } from 'react-i18next' import _ from 'lodash' // ----------calc hook ------- export const useAmmJoin = ({ updateJoinFee, setToastOpen, market, refreshRef, }: // ammCalcDefault, // ammDataDefault, { market: string updateJoinFee: () => Promise setToastOpen: any refreshRef: React.Ref // ammCalcDefault: Partial>; // ammDataDefault: Partial, string>>; }) => { const { ammJoin: { request, ammCalcData, ammData }, updatePageAmmJoin, } = usePageAmmPool() const [[maxCoinA, maxCoinB], setMaxLp] = React.useState<[any, any]>([Infinity, Infinity]) const { t } = useTranslation(['common', 'error']) const [isLoading, setIsLoading] = React.useState(false) const { tokenMap, idIndex, marketMap } = useTokenMap() const { allowTrade } = useSystem() const { ammMap } = useAmmMap() const ammInfo = ammMap['AMM-' + market] const { setShowSupport, setShowTradeIsFrozen } = useOpenModals() const { toggle: { joinAmm }, } = useToggle() const { account } = useAccount() const [baseMinAmt, setBaseMinAmt] = React.useState() const [quoteMinAmt, setQuoteMinAmt] = React.useState() const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { if (account.readyState === AccountStatus.ACTIVATED) { const { ammData } = store.getState()._router_pageAmmPool.ammJoin const times = 10 const validAmt1 = ammData?.coinA?.tradeValue ? ammData?.coinA?.tradeValue >= times * baseMinAmt : false const validAmt2 = ammData?.coinB?.tradeValue ? ammData?.coinB?.tradeValue >= times * quoteMinAmt : false myLog('btnLabelActiveCheck ammJOin validAmt1:', validAmt1, ' validAmt2:', validAmt2) if (isLoading || !(ammInfo !== undefined && ammInfo?.market)) { return { tradeBtnStatus: TradeBtnStatus.LOADING, label: '' } } else { if (account.readyState === AccountStatus.ACTIVATED) { if ( ammData === undefined || ammData?.coinA.tradeValue === undefined || ammData?.coinB.tradeValue === undefined || ammData.coinA.tradeValue === 0 || ammData.coinB.tradeValue === 0 ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelEnterAmount', } } else if (sdk.toBig(ammData.coinA.tradeValue).gt(ammData.coinA.balance)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelAMMNoEnough|${ammData.coinA.belong}`, } } else if (sdk.toBig(ammData.coinB.tradeValue).gt(ammData.coinB.balance)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelAMMNoEnough|${ammData.coinB.belong}`, } } else if (!validAmt1 || !validAmt2) { const tokenA = tokenMap[ammInfo.coinA ?? ''] const tokenB = tokenMap[ammInfo.coinB ?? ''] const viewBaseMintAmt = getValuePrecisionThousand( times * baseMinAmt, tokenA?.precisionForOrder ?? 4, tokenA?.precisionForOrder ?? 4, tokenA?.precisionForOrder ?? 2, false, { floor: false, isAbbreviate: true }, ) const viewQuoteMintAmt = getValuePrecisionThousand( times * quoteMinAmt, tokenB?.precisionForOrder ?? 4, tokenB?.precisionForOrder ?? 4, tokenB?.precisionForOrder ?? 2, false, { floor: false, isAbbreviate: true }, ) return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelLimitMin| ${viewBaseMintAmt} ${ammData?.coinA.belong} ${t( 'labelAmmMinAnd', )} ${viewQuoteMintAmt} ${ammData?.coinB.belong}`, } } else if ( !Number.isFinite(maxCoinA) && !Number.isFinite(maxCoinB) && (sdk.toBig(ammData.coinA.tradeValue).gt(maxCoinA) || sdk.toBig(ammData.coinB.tradeValue).gt(maxCoinB)) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelAMMMax| ${t('labelAMMMaxAND', { coinA: `${maxCoinA} ${ammData.coinA.belong}`, coinB: `${maxCoinB} ${ammData.coinB.belong}`, })}`, } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '', } } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '', } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } }, [account.readyState, ammMap, ammInfo, isLoading, baseMinAmt, quoteMinAmt, maxCoinA, maxCoinB]) const onAmmClick = React.useCallback( async function (props) { setIsLoading(true) if (!allowTrade.order.enable) { setShowSupport({ isShow: true }) setIsLoading(false) } else if (ammInfo?.joinDisable) { setShowTradeIsFrozen({ isShow: true, messageKey: 'labelNoticeForMarketFrozen', type: t('labelAmmJoin') + ` ${ammInfo?.__rawConfig__.name}`, }) setIsLoading(false) } else if (!joinAmm.enable) { setShowTradeIsFrozen({ isShow: true, type: t('labelAmmJoin') + ` ${ammInfo?.__rawConfig__.name}`, }) setIsLoading(false) } else { if (!LoopringAPI.ammpoolAPI || !LoopringAPI.userAPI || !request || !account?.eddsaKey?.sk) { myLog('onAmmJoin ammpoolAPI:', LoopringAPI.ammpoolAPI, 'joinRequest:', request) setToastOpen({ open: true, type: ToastType.success, content: t('labelJoinAmmFailed'), }) setIsLoading(false) walletLayer2Service.sendUserUpdate() return } const patch: sdk.AmmPoolRequestPatch = { chainId: store.getState().system.chainId as sdk.ChainId, ammName: ammInfo?.__rawConfig__.name ?? '', poolAddress: ammInfo?.address ?? '', eddsaKey: account.eddsaKey.sk, } let req = _.cloneDeep(request) try { const request0: sdk.GetNextStorageIdRequest = { accountId: account.accountId, sellTokenId: req.joinTokens.pooled[0].tokenId as number, } const storageId0 = await LoopringAPI.userAPI.getNextStorageId(request0, account.apiKey) const request_1: sdk.GetNextStorageIdRequest = { accountId: account.accountId, sellTokenId: req.joinTokens.pooled[1].tokenId as number, } const storageId1 = await LoopringAPI.userAPI.getNextStorageId(request_1, account.apiKey) req.storageIds = [storageId0.offchainId, storageId1.offchainId] req.validUntil = getTimestampDaysLater(DAYS) if (ammInfo?.domainSeparator) { req.domainSeparator = ammInfo?.domainSeparator } myLog('join ammpool req:', req) let response if (store.getState().system.chainId === 5 && /CLRC-USDT/gi.test(ammInfo.name)) { console.log('Test case for new amm join, please do not submit request', req) } else { response = await LoopringAPI.ammpoolAPI.joinAmmPool(req, patch, account.apiKey) } myLog('join ammpool response:', response) updatePageAmmJoin({ ammData: { ...ammData, ...{ coinA: { ...ammData.coinA, tradeValue: 0 }, coinB: { ...ammData.coinB, tradeValue: 0 }, }, }, }) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] setToastOpen({ open: true, type: ToastType.error, content: t('labelJoinAmmFailed') + ' error: ' + (errorItem ? t(errorItem.messageKey, { ns: 'error' }) : (response as sdk.RESULT_INFO).message), }) } else { setToastOpen({ open: true, type: ToastType.success, content: t('labelJoinAmmSuccess'), }) } } catch (reason: any) { sdk.dumpError400(reason) setToastOpen({ open: true, type: ToastType.error, content: t('labelJoinAmmFailed'), }) } finally { setIsLoading(false) walletLayer2Service.sendUserUpdate() // @ts-ignore refreshRef?.current?.firstElementChild.click() } if (props.__cache__) { makeCache(props.__cache__) } } }, [request, ammData, account, t, allowTrade], ) const { btnStatus, onBtnClick, btnLabel } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback: onAmmClick, }) const handleAmmPoolEvent = React.useCallback( (data: AmmJoinData>, type: 'coinA' | 'coinB') => { if (!data || !tokenMap || !data.coinA.belong || !data.coinB.belong) { myLog('handleJoin return ', data) return } const { ammJoin: { ammCalcData }, } = store.getState()._router_pageAmmPool const { ammMap } = store.getState().amm.ammMap const ammInfo = ammMap['AMM-' + market] const { slippage } = data const slippageReal = sdk.toBig(slippage).div(100).toString() const isAtoB = type === 'coinA' const tokenCoinA = tokenMap[ammInfo.coinA] const tokenCoinB = tokenMap[ammInfo.coinB] const rawA = data.coinA.tradeValue ? data.coinA.tradeValue.toString() : '0' const rawB = data.coinB.tradeValue ? data.coinB.tradeValue.toString() : '0' const rawVal = isAtoB ? rawA : rawB const rawValMatchForRawVal = isAtoB ? rawB : rawA const lpToken = tokenMap['LP-' + ammInfo.market] const ammPoolSnapshot: any = { poolName: ammInfo.name, poolAddress: ammInfo.address, pooled: [ { tokenId: tokenCoinA.tokenId, volume: ammInfo.tokens.pooled[0], }, { tokenId: tokenCoinB.tokenId, volume: ammInfo.tokens.pooled[1], }, ], //[ammInfo., TokenVolumeV3]; lp: { tokenId: lpToken.tokenId, volume: ammInfo.tokens?.lp as any, }, risky: false, } const { request } = sdk.makeJoinAmmPoolRequest( rawVal, isAtoB, slippageReal, account.accAddress, ammCalcData?.fees, ammPoolSnapshot, tokenMap as any, idIndex as any, 0, 0, rawValMatchForRawVal, ) const newData = _.cloneDeep(data) if (ammInfo.tokens?.lp && sdk.toBig(ammInfo.tokens?.lp ?? 0).gt(0)) { if (isAtoB) { newData.coinB.tradeValue = rawA !== '0' ? (sdk .toBig(request.joinTokens.pooled[1].volume) .div('1e' + tokenCoinB.decimals) .toFixed(marketMap[ammInfo.market].precisionForPrice) as any) : undefined } else { newData.coinA.tradeValue = rawB !== '0' ? (sdk .toBig(request.joinTokens.pooled[0].volume) .div('1e' + tokenCoinA.decimals) .toFixed(marketMap[ammInfo.market].precisionForPrice) as any) : undefined } } if (ammCalcData?.fee) { const { request: maxResult } = sdk.makeJoinAmmPoolRequest( sdk.toBig(data.coinB.balance).minus(ammCalcData?.fee).toString(), false, slippageReal, account.accAddress, ammCalcData?.fees, ammPoolSnapshot, tokenMap as any, idIndex as any, 0, 0, undefined, ) if (ammInfo.tokens?.lp && sdk.toBig(ammInfo.tokens?.lp ?? 0).gt(0)) { setMaxLp([ sdk .toBig(maxResult.joinTokens.pooled[0].volume) .div('1e' + tokenCoinA.decimals) .toFixed(marketMap[ammInfo.market].precisionForPrice), sdk .toBig(maxResult.joinTokens.pooled[1].volume) .div('1e' + tokenCoinB.decimals) .toFixed(marketMap[ammInfo.market].precisionForPrice), ]) } } myLog('raw request:', request) updatePageAmmJoin({ request, ammData: { coinA: newData.coinA as IBData, coinB: newData.coinB as IBData, coinLP: ammData.coinLP, slippage, }, }) }, [market], ) const walletLayer2Callback = React.useCallback(async () => { updateJoinFee() // getUserRewards() const account = store.getState().account if (ammInfo?.market && account.readyState === AccountStatus.ACTIVATED) { const baseT = tokenMap[ammInfo.coinA] const quoteT = tokenMap[ammInfo.coinB] setBaseMinAmt( baseT ? sdk .toBig(baseT.orderAmounts.minimum) .div('1e' + baseT.decimals) .toNumber() : undefined, ) setQuoteMinAmt( quoteT ? sdk .toBig(quoteT.orderAmounts.minimum) .div('1e' + quoteT.decimals) .toNumber() : undefined, ) } }, []) useWalletLayer2Socket({ walletLayer2Callback }) return { ammCalcData, ammData, handleAmmPoolEvent, btnStatus, onAmmClick: onBtnClick, btnI18nKey: btnLabel, updatePageAmmJoin, propsAExtends: { coinPrecision: tokenMap[ammInfo.coinA].precision, }, propsBExtends: { coinPrecision: tokenMap[ammInfo.coinB].precision, }, } } ================================================ FILE: packages/core/src/hooks/useractions/hookSwap.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' import { BIGO, calcPriceByAmmTickMapDepth, DefaultFeeBips, getPriceImpactInfo, getTimestampDaysLater, isTradePairMarket, LoopringAPI, makeWalletLayer2, MAPFEEBIPS, MarketCalcParams, marketInitCheck, PriceLevel, reCalcStoB, store, swapDependAsync, useAccount, useAmmMap, useAmount, useBtradeMap, usePageTradeLite, usePairMatch, useSocket, useSubmitBtn, useSystem, useTicker, useToast, useTokenMap, useTokenPrices, useWalletLayer2, useWalletLayer2Socket, walletLayer2Service, } from '../../index' import { AccountStatus, CoinMap, defaultSlipage, EmptyValueTag, getShowStr, getValuePrecisionThousand, IBData, MarketType, myLog, RouterPath, SagaStatus, SDK_ERROR_MAP_TO_UI, SwapTradeCalcData, TradeBtnStatus, WalletMap, } from '@loopring-web/common-resources' import { SwapData, SwapTradeData, SwapType, ToastType, useOpenModals, useSettings, useToggle, } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import { useHistory } from 'react-router-dom' import BigNumber from 'bignumber.js' export enum ShowWitchAle3t1 { AlertImpact = 'AlertImpact', SwapSecondConfirmation = 'SwapSecondConfirmation', ConfirmImpact = 'ConfirmImpact', SmallPrice = 'SmallPrice', } export const useAlert = () => { const [confirmed, setConfirmed] = React.useState<[boolean, boolean]>([false, false]) const [showAlert, setShowWhich] = React.useState<{ isShow: boolean step: 1 | 2 showWitch: '' | ShowWitchAle3t1 }>({ isShow: false, step: 1, showWitch: '', }) return { showAlert, confirmed, setShowWhich, setConfirmed, } } const useSwapSocket = () => { const { sendSocketTopic, socketEnd } = useSocket() const { ammMap } = useAmmMap() const { pageTradeLite } = usePageTradeLite() React.useEffect(() => { if (pageTradeLite.market) { sendSocketTopic({ [sdk.WsTopicType.ammpool]: ammMap['AMM-' + pageTradeLite.market] ? [ammMap['AMM-' + pageTradeLite.market].address] : [], [sdk.WsTopicType.mixorder]: { markets: [pageTradeLite.market], level: 0, count: 50, snapshot: true, }, }) } else { socketEnd() } return () => { socketEnd() } }, [pageTradeLite?.market]) } export const useSwap = < T extends SwapTradeData>, C extends { [key: string]: any }, CAD extends SwapTradeCalcData, >({ path, }: { path: string }) => { //High: No not Move!!!!!! const { realMarket } = usePairMatch({ path }) const { t } = useTranslation(['common', 'error']) const history = useHistory() const refreshRef = React.createRef() const { toastOpen, setToastOpen, closeToast } = useToast() const { isMobile, swapSecondConfirmation } = useSettings() const { setShowSupport, setShowTradeIsFrozen } = useOpenModals() const { account, status: accountStatus } = useAccount() const { marketArray: bTradeMarketArray } = useBtradeMap() const { toggle: { order }, } = useToggle() /** loaded from loading **/ const { exchangeInfo, allowTrade } = useSystem() const { coinMap, tokenMap, marketArray, marketCoins, marketMap } = useTokenMap() const { ammMap } = useAmmMap() /** init Ticker ready from ui-backend load**/ const { tickerMap } = useTicker() /** get store value **/ /** after unlock **/ const { amountMap, getAmount, status: amountStatus } = useAmount() const { status: walletLayer2Status } = useWalletLayer2() const [sellMinAmt, setSellMinAmt] = React.useState() const [tradeData, setTradeData] = React.useState(undefined) const [tradeCalcData, setTradeCalcData] = React.useState({ coinInfoMap: marketCoins?.reduce((prev: any, item: string | number) => { return { ...prev, [item]: coinMap ? coinMap[item] : {} } }, {} as CoinMap), } as CAD) /** redux storage **/ const { pageTradeLite, updatePageTradeLite, __SUBMIT_LOCK_TIMER__, __TOAST_AUTO_CLOSE_TIMER__, __DAYS__, } = usePageTradeLite() /*** api prepare ***/ // const [pair, setPair] = React.useState(realPair); const [isSwapLoading, setIsSwapLoading] = React.useState(false) /***confirm ***/ const [storageId, setStorageId] = React.useState<{ orderId: number offchainId: number }>({} as any) const { tokenPrices } = useTokenPrices() const showSwapSecondConfirmation = swapSecondConfirmation !== false const isSmallOrder = tradeData && tradeData.buy.tradeValue ? tokenPrices[tradeData.buy.belong] * tradeData.buy.tradeValue < 100 : false const resetMarket = (_market: MarketType, type: 'sell' | 'buy') => { const { tradePair } = marketInitCheck({ market: _market, type }) const [_, sellToken, buyToken] = (tradePair ?? '').match(/(\w+)-(\w+)/i) let { market } = sdk.getExistedMarket(marketArray, sellToken, buyToken) setIsMarketStatus((state) => { return { tradePair, market, isMarketInit: state.market !== market, } }) if (coinMap && tokenMap && marketMap && marketArray) { // const { tradePair } = marketInitCheck({ market: _market, type }); // @ts-ignore const [, coinA, coinB] = tradePair.match(/([\w,#]+)-([\w,#]+)/i) let walletMap: WalletMap | undefined if ( account.readyState === AccountStatus.ACTIVATED && walletLayer2Status === SagaStatus.UNSET ) { if (!Object.keys(tradeCalcData.walletMap ?? {}).length) { walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap as WalletMap } walletMap = tradeCalcData.walletMap as WalletMap } const tradeDataTmp: any = { sell: { belong: coinA, tradeValue: 0, balance: walletMap ? walletMap[coinA]?.count : 0, }, buy: { belong: coinB, tradeValue: 0, balance: walletMap ? walletMap[coinB]?.count : 0, }, } const sellCoinInfoMap = tokenMap[coinB]?.tradePairs?.reduce( (prev: any, item: string | number) => { return { ...prev, [item]: coinMap[item] } }, {} as CoinMap, ) const buyCoinInfoMap = tokenMap[coinA].tradePairs?.reduce( (prev: any, item: string | number) => { return { ...prev, [item]: coinMap[item] } }, {} as CoinMap, ) setTradeCalcData((state) => { return { ...state, walletMap, coinSell: coinA, coinBuy: coinB, sellPrecision: tokenMap[coinA as string]?.precision, buyPrecision: tokenMap[coinB as string]?.precision, sellCoinInfoMap, buyCoinInfoMap, priceImpact: '', priceImpactColor: 'inherit', minimumReceived: undefined, StoB: undefined, BtoS: undefined, fee: undefined, feeTakerRate: undefined, tradeCost: undefined, isShowBtradeAllow: false, } }) setTradeData({ ...tradeDataTmp }) history.push(`${RouterPath.lite}/${_market}`) updatePageTradeLite({ market, tradePair }) myLog('hookSwap: Market change getAmount', market) } } const [{ market, isMarketInit }, setIsMarketStatus] = React.useState<{ market: MarketType tradePair?: MarketType isMarketInit?: boolean }>({} as any) React.useEffect(() => { resetMarket(realMarket ?? '#null-#null', 'sell') }, []) const clearData = (calcTradeParams: Partial | null | undefined) => { setTradeData((state) => { return { ...state, sell: { ...state?.sell, tradeValue: undefined }, buy: { ...state?.buy, tradeValue: undefined }, isChecked: undefined, } as T }) setTradeCalcData((state) => { return { ...state, minimumReceived: undefined, priceImpact: undefined, fee: undefined, isNotMatchMarketPrice: undefined, marketPrice: undefined, marketRatePrice: undefined, isChecked: undefined, isShowBtradeAllow: false, } }) updatePageTradeLite({ market, maxFeeBips: MAPFEEBIPS, calcTradeParams: { ...calcTradeParams, // takerRate: undefined, // feeBips: undefined, output: undefined, sellAmt: undefined, buyAmt: undefined, amountS: undefined, amountBOut: undefined, amountBOutWithoutFee: undefined, amountBOutSlip: undefined, priceImpact: undefined, }, }) } // @ts-ignore const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string | undefined } => { if (!tokenMap && !tokenPrices) { return { label: undefined, tradeBtnStatus: TradeBtnStatus.DISABLED, } } const account = store.getState().account const sellToken = tokenMap[tradeData?.sell.belong as string] const buyToken = tokenMap[tradeData?.buy.belong as string] const { calcTradeParams } = pageTradeLite if (!sellToken || !buyToken || !calcTradeParams || storageId?.orderId == undefined) { return { label: undefined, tradeBtnStatus: TradeBtnStatus.DISABLED, } } const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} let validAmt = !!( calcTradeParams?.amountS && sellMinAmt && sdk.toBig(calcTradeParams?.amountS).gte(sdk.toBig(sellMinAmt)) ) const sellExceed = sdk.toBig(sellToken?.orderAmounts?.maximum).lt(calcTradeParams.amountS ?? 0) const buyExceed = sdk .toBig(buyToken?.orderAmounts?.maximum) .lt(calcTradeParams?.amountBOutSlip?.minReceived ?? 0) if (sellExceed || buyExceed) { validAmt = false } const notEnough = sdk .toBig(walletMap[sellToken.symbol]?.count ?? 0) .lt(calcTradeParams.sellAmt ?? 0) const sellMaxVal = sdk.toBig(sellToken?.orderAmounts?.maximum).div('1e' + sellToken.decimals) const buyMaxVal = sdk.toBig(buyToken?.orderAmounts?.maximum).div('1e' + buyToken.decimals) if (isSwapLoading || isMarketInit) { return { label: undefined, tradeBtnStatus: TradeBtnStatus.LOADING, } } else { if (account.readyState === AccountStatus.ACTIVATED) { if (!calcTradeParams || !calcTradeParams.sellAmt || !calcTradeParams.buyAmt) { myLog( 'hookSwap: calcTradeParams.baseAmt:', calcTradeParams.sellAmt, ' calcTradeParams.quoteAmt:', calcTradeParams.buyAmt, ) return { label: 'labelEnterAmount', tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (notEnough) { return { label: `labelArgNoEnough| ${tradeData?.sell.belong}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (sellExceed) { const maxOrderSize = sellMaxVal + ' ' + tradeData?.sell.belong return { label: `labelLimitMax| ${maxOrderSize}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (buyExceed) { const maxOrderSize = buyMaxVal + ' ' + tradeData?.buy.belong return { label: `labelLimitMax| ${maxOrderSize}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (!validAmt) { //!validAmt) { const sellSymbol = tradeData?.sell.belong if (sellMinAmt === undefined || !sellSymbol || sellMinAmt === 'NaN') { return { label: 'labelEnterAmount', tradeBtnStatus: TradeBtnStatus.DISABLED, } } else { const sellToken = tokenMap[sellSymbol] // //VolToNumberWithPrecision(sellMinAmt ?? '', sellSymbol as any) const minOrderSize = getValuePrecisionThousand( sdk.toBig(sellMinAmt ?? 0).div('1e' + sellToken.decimals), sellToken.precision, sellToken.precision, sellToken.precision, false, { floor: false, isAbbreviate: true }, ) if (isNaN(Number(minOrderSize))) { return { label: `labelLimitMin| ${EmptyValueTag + ' ' + sellSymbol}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else { return { label: `labelLimitMin| ${minOrderSize + ' ' + sellSymbol}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } } } else { return { label: undefined, tradeBtnStatus: TradeBtnStatus.AVAILABLE, } } } else { return { label: undefined, tradeBtnStatus: TradeBtnStatus.AVAILABLE, } } } }, [ tokenMap, tradeCalcData?.isChecked, tradeCalcData?.isNotMatchMarketPrice, tradeData?.sell.belong, tradeData?.buy.belong, pageTradeLite, sellMinAmt, isMarketInit, isSwapLoading, storageId.orderId, ]) /*** Btn related function ***/ const swapFunc = React.useCallback(async () => { let { calcTradeParams, tradeChannel, orderType, maxFeeBips } = pageTradeLite if ( !LoopringAPI.userAPI || !tokenMap || !exchangeInfo || !calcTradeParams || account.readyState !== AccountStatus.ACTIVATED ) { setToastOpen({ open: true, type: ToastType.error, content: t('labelSwapFailed'), }) setIsSwapLoading(false) return } const sell = tradeData?.sell.belong as string const buy = tradeData?.buy.belong as string const sellToken = tokenMap[sell] const buyToken = tokenMap[buy] try { const request: sdk.SubmitOrderRequestV3 = { exchange: exchangeInfo.exchangeAddress, accountId: account.accountId, storageId: storageId.orderId, sellToken: { tokenId: sellToken.tokenId, volume: calcTradeParams.amountS as string, }, buyToken: { tokenId: buyToken.tokenId, volume: calcTradeParams.amountBOutSlip?.minReceived as string, }, allOrNone: false, validUntil: getTimestampDaysLater(__DAYS__), // maxFeeBips: parseInt(totalFee as string), maxFeeBips: maxFeeBips ?? MAPFEEBIPS, fillAmountBOrS: false, // amm only false orderType, tradeChannel, eddsaSignature: '', } myLog('submitOrder request', request) const response: { hash: string } | any = await LoopringAPI.userAPI.submitOrder( request, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] if ((response as sdk.RESULT_INFO).code === 114002) { getAmount({ market }) clearData(calcTradeParams) } setToastOpen({ open: true, type: ToastType.error, content: t('labelSwapFailed') + ' error: ' + (errorItem ? t(errorItem.messageKey, { ns: 'error' }) : (response as sdk.RESULT_INFO).message), }) } else { getStorageId() await sdk.sleep(__TOAST_AUTO_CLOSE_TIMER__) const resp = await LoopringAPI.userAPI.getOrderDetails( { accountId: account.accountId, orderHash: response.hash, }, account.apiKey, ) myLog('hookSwap:-----> resp:', resp) if (resp.orderDetail?.status !== undefined) { myLog('hookSwap:resp.orderDetail:', resp.orderDetail) switch (resp.orderDetail?.status) { case sdk.OrderStatus.cancelled: const baseAmount = sdk.toBig(resp.orderDetail.volumes.baseAmount) const baseFilled = sdk.toBig(resp.orderDetail.volumes.baseFilled) const quoteAmount = sdk.toBig(resp.orderDetail.volumes.quoteAmount) const quoteFilled = sdk.toBig(resp.orderDetail.volumes.quoteFilled) const percentage1 = baseAmount.eq(BIGO) ? 0 : baseFilled.div(baseAmount).toNumber() const percentage2 = quoteAmount.eq(BIGO) ? 0 : quoteFilled.div(quoteAmount).toNumber() myLog('hookSwap:percentage1:', percentage1, ' percentage2:', percentage2) if (percentage1 === 0 || percentage2 === 0) { setToastOpen({ open: true, type: ToastType.warning, content: t('labelSwapCancelled'), }) } else { setToastOpen({ open: true, type: ToastType.success, content: t('labelSwapSuccess'), }) } break case sdk.OrderStatus.processed: setToastOpen({ open: true, type: ToastType.success, content: t('labelSwapSuccess'), }) break default: setToastOpen({ open: true, type: ToastType.error, content: t('labelSwapFailed'), }) } } walletLayer2Service.sendUserUpdate() clearData(calcTradeParams) } } catch (reason: any) { sdk.dumpError400(reason) setToastOpen({ open: true, type: ToastType.error, content: t('labelSwapFailed'), }) } await sdk.sleep(__SUBMIT_LOCK_TIMER__) setIsSwapLoading(false) }, [ pageTradeLite, tokenMap, exchangeInfo, account.readyState, account.accountId, account.apiKey, account.eddsaKey.sk, tradeData?.sell?.belong, tradeData?.buy?.belong, __SUBMIT_LOCK_TIMER__, setToastOpen, t, __DAYS__, getAmount, market, __TOAST_AUTO_CLOSE_TIMER__, updatePageTradeLite, ]) const { showAlert, confirmed, setShowWhich, setConfirmed } = useAlert() const doShowAlert = () => { const { priceLevel } = getPriceImpactInfo(pageTradeLite.calcTradeParams, account.readyState) myLog('hookSwap:---- swapCalculatorCallback priceLevel:', priceLevel) setConfirmed((state) => { if (isSmallOrder) { state[1] = false } else { state[1] = true } setShowWhich(() => { if (tradeCalcData?.isNotMatchMarketPrice) { return { isShow: true, step: 1, showWitch: ShowWitchAle3t1.AlertImpact } } else if (priceLevel === PriceLevel.Lv1 || priceLevel === PriceLevel.Lv2) { return { isShow: true, step: 1, showWitch: ShowWitchAle3t1.ConfirmImpact } } else if (isSmallOrder) { state[0] = true return { isShow: true, step: 2, showWitch: ShowWitchAle3t1.SmallPrice } } else if (showSwapSecondConfirmation) { return { isShow: true, step: 1, showWitch: ShowWitchAle3t1.SwapSecondConfirmation } } else { state[0] = true return { isShow: false, step: 2, showWitch: '' } } }) return state }) } React.useEffect(() => { if (confirmed[0] === true && confirmed[1] === true) { swapFunc() setConfirmed([false, false]) } }, [confirmed[0], confirmed[1]]) const swapCalculatorCallback = React.useCallback(async () => { setIsSwapLoading(true) if (!allowTrade.order.enable) { setShowSupport({ isShow: true }) setIsSwapLoading(false) } else if (!order.enable) { setShowTradeIsFrozen({ isShow: true, type: 'Swap' }) setIsSwapLoading(false) } else { doShowAlert() } }, [ pageTradeLite.calcTradeParams, account.readyState, allowTrade.order.enable, order.enable, setShowSupport, setShowTradeIsFrozen, swapFunc, showSwapSecondConfirmation, isSmallOrder, ]) const { btnStatus: swapBtnStatus, onBtnClick: onSwapClick, btnLabel: swapBtnI18nKey, } = useSubmitBtn({ availableTradeCheck, isLoading: isSwapLoading || (isMarketInit ?? false), submitCallback: swapCalculatorCallback, }) /*** Btn related end ***/ const toPro = React.useCallback(() => { history.push({ pathname: `${RouterPath.pro}/${market}`, }) }, [market]) const should15sRefresh = React.useCallback(() => { if (market) { // updateDepth() callPairDetailInfoAPIs() // marketTradeTableCallback(); } }, [market]) /*** table related end ***/ const getStorageId = React.useCallback(async () => { if ( tradeCalcData?.coinSell && tokenMap && tokenMap[tradeCalcData.coinSell] && LoopringAPI.userAPI ) { // setStorageId({} as any) const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: account.accountId, sellTokenId: tokenMap[tradeCalcData.coinSell].tokenId, }, account.apiKey, ) if ((storageId as sdk.RESULT_INFO).code) { setToastOpen({ open: true, content: 'error: getStorageId', type: ToastType.error, }) } else { setStorageId(storageId) } } else { setStorageId({} as any) } }, [tradeCalcData?.coinSell, account, tokenMap]) React.useEffect(() => { if (accountStatus === SagaStatus.UNSET) { const account = store.getState().account // walletLayer2Callback() if (account.readyState === AccountStatus.ACTIVATED) { getAmount({ market }) } } }, [accountStatus, market]) React.useEffect(() => { if (accountStatus === SagaStatus.UNSET) { const account = store.getState().account if (account.readyState === AccountStatus.ACTIVATED && tradeCalcData?.coinSell) { getStorageId() } } }, [accountStatus, tradeCalcData?.coinSell]) const walletLayer2Callback = React.useCallback(async () => { // let walletMap: WalletMap | undefined = undefined if (account.readyState === AccountStatus.ACTIVATED) { refreshAmmPoolSnapshot() } else { if (tradeCalcData.coinSell && tradeCalcData.coinBuy) { setTradeData((state) => { return { ...state, sell: { belong: tradeCalcData.coinSell }, buy: { belong: tradeCalcData.coinBuy }, } as T }) } updatePageTradeLite({ market: market as MarketType, feeBips: 0, totalFee: 0, takerRate: 0, calcTradeParams: {}, priceImpactObj: undefined, }) setTradeCalcData((state) => { return { ...state, walletMap: {}, isShowBtradeAllow: false, minimumReceived: undefined, priceImpact: undefined, fee: undefined, } }) } }, [tradeData, market, tradeCalcData, marketArray, ammMap, account.readyState]) useSwapSocket() useWalletLayer2Socket({ walletLayer2Callback }) /*** user Action function ***/ //High: effect by wallet state update const handleSwapPanelEvent = async ( swapData: SwapData>>, swapType: any, ): Promise => { const { tradeData: _tradeData } = swapData myLog('hookSwap: handleSwapPanelEvent', swapType, _tradeData) switch (swapType) { case SwapType.SEll_CLICK: case SwapType.BUY_CLICK: return case SwapType.SELL_SELECTED: myLog('hookSwap: handleSwapPanelEvent _tradeData', _tradeData) if (_tradeData?.sell.belong !== tradeData?.sell.belong) { resetMarket( `${_tradeData?.sell?.belong ?? `#null`}-${_tradeData?.buy?.belong ?? `#null`}`, 'sell', ) } else { reCalculateDataWhenValueChange( _tradeData, `${_tradeData?.sell.belong}-${_tradeData?.buy.belong}`, 'sell', ) } // throttleSetValue('sell', _tradeData) break case SwapType.BUY_SELECTED: //type = 'buy' if (_tradeData?.buy.belong !== tradeData?.buy.belong) { resetMarket( `${_tradeData?.sell?.belong ?? `#null`}-${_tradeData?.buy?.belong ?? `#null`}`, 'buy', ) } else { reCalculateDataWhenValueChange( _tradeData, `${_tradeData?.sell.belong}-${_tradeData?.buy.belong}`, 'buy', ) } break case SwapType.EXCHANGE_CLICK: const { close } = pageTradeLite let btos: string = '0' if (close) { // @ts-ignore const [, _coinA] = market.match(/(\w+)-(\w+)/i) btos = getValuePrecisionThousand( 1 / Number(typeof close === 'number' ? close : close.replaceAll(sdk.SEP, '')), tokenMap[_coinA].precision, tokenMap[_coinA].precision, tokenMap[_coinA].precision, true, ) // .toFixed(tokenMap[idIndex[poolATokenVol.tokenId]].precision)) } const _tradeCalcData: any = { ...tradeCalcData, coinSell: tradeCalcData.coinBuy, sellPrecision: tokenMap[tradeCalcData.coinBuy as string].precision, coinBuy: tradeCalcData.coinSell, buyPrecision: tokenMap[tradeCalcData.coinSell as string].precision, sellCoinInfoMap: tradeCalcData.buyCoinInfoMap, buyCoinInfoMap: tradeCalcData.sellCoinInfoMap, StoB: market === `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}` ? close : btos, BtoS: market === `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}` ? btos : close, isShowBtradeAllow: false, priceImpact: undefined, priceImpactColor: 'inherit', minimumReceived: undefined, fee: undefined, feeTakerRate: undefined, tradeCost: undefined, isNotMatchMarketPrice: undefined, marketPrice: undefined, marketRatePrice: undefined, isChecked: undefined, } myLog('hookSwap:Exchange,tradeCalcData,_tradeCalcData', tradeCalcData, _tradeCalcData) callPairDetailInfoAPIs() updatePageTradeLite({ market, tradePair: `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}`, calcTradeParams: { ...pageTradeLite.calcTradeParams, isReverse: !pageTradeLite.calcTradeParams, amountS: undefined, output: undefined, }, }) setSellMinAmt(undefined) setTradeCalcData(_tradeCalcData) setTradeData((state) => { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap return { ...(state ?? {}), sell: { belong: _tradeCalcData.coinSell, tradeValue: undefined, balance: walletMap ? walletMap[_tradeCalcData.coinSell as string]?.count : 0, }, buy: { belong: _tradeCalcData.coinBuy, tradeValue: undefined, balance: walletMap ? walletMap[_tradeCalcData.coinBuy as string]?.count : 0, }, } as T }) break default: break } } React.useEffect(() => { myLog( 'hookSwap: pageTradeLite.deep amountStatus storageId', storageId, amountStatus, pageTradeLite?.depth?.symbol, market, ) if ( amountStatus == SagaStatus.UNSET && pageTradeLite.depth && pageTradeLite.depth.symbol === market ) { refreshAmmPoolSnapshot() setIsMarketStatus((state) => { return { ...state, isMarketInit: false, } }) } }, [ pageTradeLite.depth, tradeCalcData.coinBuy, account.readyState, storageId, amountStatus, market, ]) React.useEffect(() => { if (market) { //@ts-ignore if (refreshRef.current) { myLog('hookSwap: pageTradeLite, click', market) // @ts-ignore refreshRef.current.firstElementChild.click() should15sRefresh() } } }, [market]) const refreshAmmPoolSnapshot = React.useCallback(() => { const { ticker, ammPoolSnapshot, depth, lastStepAt, tradePair, market } = pageTradeLite if ( tradeData && lastStepAt && tradeCalcData.coinSell === tradeData['sell'].belong && tradeCalcData.coinBuy === tradeData['buy'].belong && tradeData[lastStepAt].tradeValue && tradeData[lastStepAt].tradeValue !== 0 ) { reCalculateDataWhenValueChange(tradeData, tradePair, lastStepAt) } else if ( tradeCalcData.coinSell && tradeCalcData.coinBuy && (`${tradeCalcData.coinSell}-${tradeCalcData.coinBuy}` === market || `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}` === market) ) { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap let { stob, btos, close } = calcPriceByAmmTickMapDepth({ market: market as any, tradePair: `${tradeCalcData.coinSell}-${tradeCalcData.coinBuy}`, dependencyData: { ticker, ammPoolSnapshot, depth }, noGetValuePrecisionThousand: true }) const result = reCalcStoB({ market, tradeData: tradeData as SwapTradeData>, tradePair: tradePair as any, noGetValuePrecisionThousand: true }) const reserveInfo = sdk.getReserveInfo(tradeCalcData.coinSell as string, tradeCalcData.coinBuy as string, marketArray, tokenMap, marketMap) setTradeCalcData((state) => { return { ...state, walletMap, StoB: getValuePrecisionThousand( result ? result.stob : stob, undefined, reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, true ), BtoS: getValuePrecisionThousand( result ? result.btos : btos, undefined, !reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, !reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, true, ), } }) setTradeData({ ...tradeData, sell: { ...tradeData?.sell, belong: tradeCalcData.coinSell, balance: walletMap ? walletMap[tradeCalcData.coinSell as string]?.count : 0, }, buy: { ...tradeData?.buy, belong: tradeCalcData.coinBuy, }, } as T) // } as T) updatePageTradeLite({ market, close }) } }, [market, pageTradeLite, tradeData, tradeCalcData, setTradeCalcData]) const callPairDetailInfoAPIs = React.useCallback(async () => { if (market && ammMap && LoopringAPI.exchangeAPI) { try { const { depth, ammPoolSnapshot } = await swapDependAsync(market) if (depth) { const { tickerMap } = store.getState().tickerMap myLog('hookSwap: pageTradeLite', 'depth') updatePageTradeLite({ market, depth, ammPoolSnapshot, ticker: tickerMap[market], }) } } catch (error: any) { myLog('hookSwap: error:', error, 'go to LRC-ETH') setToastOpen({ open: true, content: 'error: resetMarket', type: ToastType.error, }) } } }, [market, ammMap, tickerMap]) const reCalculateDataWhenValueChange = React.useCallback( (_tradeData, _tradePair?, type?) => { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap const { ammPoolSnapshot, depth, tradePair, close } = store.getState()._router_pageTradeLite.pageTradeLite const { amountMap } = store.getState().amountMap let calcForMinAmt, calcForMinCost, calcForPriceImpact myLog('hookSwap:reCalculateDataWhenValueChange', tradeData, _tradePair, type) if (depth && market && _tradePair && isTradePairMarket(_tradePair, market) && _tradeData) { const coinA = _tradeData.sell.belong const coinB = _tradeData.buy.belong _tradeData.sell.balance = walletMap ? walletMap[coinA]?.count : 0 const sellToken = tokenMap[coinA as string] const buyToken = tokenMap[coinB as string] const isAtoB = type === 'sell' let input: any = isAtoB ? _tradeData.sell.tradeValue : _tradeData.buy.tradeValue input = input === undefined || isNaN(Number(input)) ? 0 : Number(input) let slippage = sdk .toBig( _tradeData.slippage && !isNaN(_tradeData.slippage) ? _tradeData.slippage : defaultSlipage, ) .times(100) .toString() let totalFee: any = undefined let feeTakerRate: any = undefined let feeBips: any = undefined let takerRate: any = undefined let buyMinAmtInfo: any = undefined let sellMinAmtInfo: any = undefined let tradeCost: any = undefined let basePrice: any = undefined let tradePrice: any = undefined let maxFeeBips: any = MAPFEEBIPS if (amountMap && amountMap[market as string] && ammMap) { myLog(`hookSwap: amountMap[${market}]:`, amountMap[market as string]) const ammMarket = `AMM-${market}` // const amount = ammMap[ammMarket] // ? amountMap[ammMarket] // : amountMap[market as string]; const amountMarket = amountMap[market as string] buyMinAmtInfo = amountMarket[_tradeData['buy'].belong as string] sellMinAmtInfo = amountMarket[_tradeData['sell'].belong as string] // myLog(`buyMinAmtInfo,sellMinAmtInfo: AMM-${market}, ${_tradeData[ 'buy' ].belong}`, buyMinAmtInfo, sellMinAmtInfo) takerRate = buyMinAmtInfo ? buyMinAmtInfo.userOrderInfo.takerRate : 0 feeBips = ammMap[ammMarket] ? ammMap[ammMarket].__rawConfig__.feeBips : DefaultFeeBips feeTakerRate = amountMarket[_tradeData['buy'].belong as string] && amountMarket[_tradeData['buy'].belong as string].userOrderInfo.takerRate tradeCost = amountMarket[_tradeData['buy'].belong as string].tradeCost const minAmountInput = BigNumber.max( buyMinAmtInfo.userOrderInfo.minAmount, tokenMap[buyToken.symbol].orderAmounts.dust, ) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div('1e' + buyToken.decimals) .toString() calcForMinAmt = sdk.getOutputAmount({ input: minAmountInput, sell: coinA, buy: coinB, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: slippage, }) myLog( 'hookSwap:buyMinAmtInfo.userOrderInfo.minAmount:', buyMinAmtInfo.userOrderInfo.minAmount, `buyMinAmtInfo.userOrderInfo.minAmount, with slippage:${slippage}`, sdk .toBig(buyMinAmtInfo.userOrderInfo.minAmount) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .toString(), ) /*** calc for Price Impact ****/ const sellMinAmtInput = sdk .toBig(sellMinAmtInfo.baseOrderInfo.minAmount) .div('1e' + sellToken.decimals) .toString() calcForPriceImpact = sdk.getOutputAmount({ input: sellMinAmtInput, sell: coinA, buy: coinB, isAtoB: true, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: '10', }) basePrice = sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput) myLog( 'hookSwap:calcForPriceImpact input: ', sellMinAmtInput, ', output: ', sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput).toNumber(), ', calcForPriceImpact:', calcForPriceImpact?.amountBOutSlip?.minReceivedVal, ', calcForPriceImpact basePrice: ', basePrice.toNumber(), ) /**** calc for min Cost ****/ const dustToken = buyToken let calcForMinCostInput = BigNumber.max( sdk.toBig(tradeCost).times(2), sdk.toBig(dustToken.orderAmounts.dust), ) const tradeCostInput = calcForMinCostInput .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div('1e' + dustToken.decimals) .toString() myLog( 'hookSwap:tradeCost*2:', sdk.toBig(tradeCost).times(2).toString(), 'buyToken.orderAmounts.dust', buyToken.orderAmounts.dust, 'calcForMinCostInput', calcForMinCostInput.toString(), `calcForMinCostInput, with slippage:${slippage}`, calcForMinCostInput.div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))).toString(), 'calcForMinCost, Input', tradeCostInput, ) calcForMinCost = sdk.getOutputAmount({ input: tradeCostInput, sell: coinA, buy: coinB, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: slippage, }) const minAmt = BigNumber.max( sellToken.orderAmounts.dust, calcForMinCost?.amountS ?? 0, ).times(1.1) setSellMinAmt(minAmt.toString()) myLog( `hookSwap:calcForMinAmt.amountS`, sdk .toBig(calcForMinAmt?.amountS ?? 0) .div('1e' + tokenMap[_tradeData['sell'].belong as string].decimals) .toString(), 'calcForMinCost.amountS', sdk .toBig(calcForMinCost?.amountS ?? 0) .div('1e' + tokenMap[_tradeData['sell'].belong as string].decimals) .toString(), ) } const calcTradeParams = sdk.getOutputAmount({ input: input.toString(), sell: coinA, buy: coinB, isAtoB, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : DefaultFeeBips, takerRate: '0', slipBips: slippage, }) const minSymbol = _tradeData.buy.belong tradePrice = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(isAtoB ? input.toString() : calcTradeParams?.output) const priceImpact = sdk .toBig(1) .minus(sdk.toBig(tradePrice).div(basePrice ?? 1)) .minus(0.001) if (calcTradeParams && priceImpact.gte(0)) { calcTradeParams.priceImpact = priceImpact.toFixed(4, 1) } else { calcTradeParams && (calcTradeParams.priceImpact = '0') } if ( tradeCost && calcTradeParams && calcTradeParams.amountBOutSlip?.minReceived && feeTakerRate ) { let value = sdk .toBig(calcTradeParams.amountBOutSlip?.minReceived) .times(feeTakerRate) .div(10000) myLog( 'hookSwap:input Accounts', calcTradeParams?.amountS, '100 U calcForMinAmt:', calcForMinAmt?.amountS, ) let validAmt = !!( calcTradeParams?.amountS && calcForMinAmt?.amountS && sdk.toBig(calcTradeParams?.amountS).gte(calcForMinAmt.amountS) ) let totalFeeRaw myLog( `hookSwap:${minSymbol} tradeCost:`, tradeCost, 'useTakeRate Fee:', value.toString(), 'calcForMinAmt?.amountS:', calcForMinAmt?.amountS, `is setup minTrade amount, ${calcForMinAmt?.amountS}:`, validAmt, ) if (!validAmt) { if (sdk.toBig(tradeCost).gte(value)) { totalFeeRaw = sdk.toBig(tradeCost) } else { totalFeeRaw = value } myLog( 'hookSwap:maxFeeBips update for tradeCost before value:', maxFeeBips, 'totalFeeRaw', totalFeeRaw.toString(), ) maxFeeBips = Math.ceil( totalFeeRaw.times(10000).div(calcTradeParams.amountBOutSlip?.minReceived).toNumber(), ) myLog('hookSwap:maxFeeBips update for tradeCost after value:', maxFeeBips) } else { totalFeeRaw = sdk.toBig(value) } totalFee = getValuePrecisionThousand( totalFeeRaw.div('1e' + tokenMap[minSymbol].decimals).toString(), tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, false, { floor: true }, ) tradeCost = getValuePrecisionThousand( sdk .toBig(tradeCost) .div('1e' + tokenMap[minSymbol].decimals) .toString(), tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, false, { floor: true }, ) myLog('hookSwap:totalFee view value:', totalFee, tradeCost) } const minimumReceived = getValuePrecisionThousand( sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .minus(totalFee) .toString(), tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, false, { floor: true }, ) const minimumConverted = calcTradeParams?.amountBOut ? getValuePrecisionThousand( sdk .toBig(calcTradeParams.amountBOut) .times(sdk.toBig(1).minus(sdk.toBig(slippage).div('10000'))) .div('1e' + tokenMap[minSymbol].decimals) .toString(), tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, tokenMap[minSymbol].precision, false, { floor: true }, ) : undefined const priceImpactObj = getPriceImpactInfo(calcTradeParams, account.readyState) let _tradeCalcData: CAD & { [key: string]: any } = { priceImpact: priceImpactObj.value.toString(), priceImpactColor: priceImpactObj.priceImpactColor, minimumReceived: !minimumReceived?.toString().startsWith('-') ? minimumReceived : undefined, fee: totalFee, feeTakerRate, tradeCost, minimumConverted, } as CAD _tradeData[isAtoB ? 'buy' : 'sell'].tradeValue = getShowStr(calcTradeParams?.output) const result = reCalcStoB({ market, tradeData: _tradeData as SwapTradeData>, tradePair: tradePair as any, }) if (result && result.stob && sdk.toBig(result.stob?.replaceAll(sdk.SEP, '')).gt(0)) { _tradeCalcData.StoB = result.stob _tradeCalcData.BtoS = result.btos } else { if (close) { // @ts-ignore const [, _coinA] = market.match(/(\w+)-(\w+)/i) let _btos = getValuePrecisionThousand( 1 / Number(close.replaceAll(sdk.SEP, '')), tokenMap[_coinA].precision, tokenMap[_coinA].precision, tokenMap[_coinA].precision, true, ) // .toFixed(tokenMap[idIndex[poolATokenVol.tokenId]].precision)) if (market === tradePair) { _tradeCalcData.StoB = close _tradeCalcData.BtoS = _btos } else { _tradeCalcData.StoB = _btos _tradeCalcData.BtoS = close } } } if (tokenPrices) { const marketPrice = sdk .toBig(tokenPrices[_tradeData.sell.belong]) .div(tokenPrices[_tradeData.buy.belong]) const marketRatePrice = marketPrice.div(_tradeCalcData.StoB?.replaceAll(sdk.SEP, '') ?? 1) const isNotMatchMarketPrice = marketRatePrice.gt(1.05) _tradeCalcData.isNotMatchMarketPrice = isNotMatchMarketPrice _tradeCalcData.marketPrice = getValuePrecisionThousand( marketPrice.toString(), tokenMap[_tradeData.buy.belong].precision, tokenMap[_tradeData.buy.belong].precision, undefined, ) _tradeCalcData.marketRatePrice = marketRatePrice.minus(1).times(100).toFixed(2) myLog( 'hookSwap: stob', _tradeCalcData.StoB, marketPrice.toString(), 'marketPriceRate', _tradeCalcData.marketRatePrice, isNotMatchMarketPrice, ) } let isShowBtradeAllow = false if ( priceImpactObj.value && priceImpactObj.value > 1 && bTradeMarketArray && (bTradeMarketArray.includes(_tradeData.buy.belong + '-' + _tradeData.sell.belong) || bTradeMarketArray.includes(_tradeData.sell.belong + '-' + _tradeData.buy.belong)) ) { isShowBtradeAllow = true } updatePageTradeLite({ market, calcTradeParams: { ...calcTradeParams, feeBips: feeBips ? feeBips : DefaultFeeBips, takerRate: takerRate ? takerRate : 0, } as any, priceImpactObj, lastStepAt: type, feeBips, takerRate, sellMinAmtInfo: sellMinAmtInfo as any, buyMinAmtInfo: buyMinAmtInfo as any, totalFee, maxFeeBips, feeTakerRate, tradeCost, }) //setOutput(calcTradeParams) if (account.readyState !== AccountStatus.ACTIVATED) { // @ts-ignore _tradeCalcData.priceImpact = undefined _tradeCalcData.minimumReceived = undefined } if (_tradeData?.isChecked !== undefined) { myLog('hookSwap: tradeCalcData?.isChecked', _tradeData) _tradeCalcData.isChecked = _tradeData.isChecked } setTradeCalcData((state) => ({ ...state, ..._tradeCalcData, isShowBtradeAllow, lastStepAt: type, walletMap, })) setTradeData((state) => ({ ...state, ..._tradeData, })) } }, [ account.readyState, amountMap, pageTradeLite, tradeCalcData, tradeData, coinMap, tokenMap, marketMap, marketArray, bTradeMarketArray, ], ) return { tradeCalcData, tradeData, handleSwapPanelEvent, onSwapClick, swapBtnI18nKey, swapBtnStatus, toastOpen, closeToast, should15sRefresh, market, refreshRef, isSwapLoading, toPro, isMarketInit, isMobile, setToastOpen, showAlert, pageTradeLite, // setShowWhich, // setConfirmed, handleClose: () => { setShowWhich((state) => ({ ...state, isShow: false })) setConfirmed([false, false]) setIsSwapLoading(false) }, handleConfirm: () => { if (showAlert.step == 1 && confirmed[1] == false) { setShowWhich(() => ({ step: 2, showWitch: ShowWitchAle3t1.SmallPrice, isShow: true })) } else { setShowWhich((state) => ({ ...state, isShow: false })) } setConfirmed((state) => { state[showAlert.step - 1] = true return state }) }, priceLevel: getPriceImpactInfo(pageTradeLite.calcTradeParams, account.readyState), } } ================================================ FILE: packages/core/src/hooks/useractions/index.ts ================================================ export * from './useCheckAccStatus' export * from './useAccountModal' export * from './useActiveAccount' export * from './useDeposit' export * from './useExportAccount' export * from './useNFTDeploy' export * from './useNFTDeposit' export * from './useNFTTransfer' export * from './useNFTWithdraw' export * from './useReset' export * from './useTransfer' export * from './useUpdateAccount' export * from './useVendor' export * from './useWithdraw' export * from './useForceWithdraw' export * from './useDefiTrade' export * from './useCollectionAdvanceMeta' export * from './useNFTMintAdvance' export * from './useDualTrade' export * from './useEditCollection' export * from './useRampConfirm' export * from './useBanxaConfirm' export * from './useCreateRedPacket' export * from './useRedpacket' export * from './useStakeTrade' export * from './useStakeTradeExit' export * from './useBtrade' export * from './hookSwap' export * from './useDualEdit' export * from './useContactAdd' export * from './useContact' export * from './useStakingAprTrend' export * from './useNFTBurn' export * from './vault' export * from './useContactAdd' export * from './useContact' export {useTransferToTaikoAccount} from './useTransferToTaikoAccount' export { useTaikoLock } from './useTaikoLock' export { useCoinbaseWalletPassword } from './useCoinbaseWalletPassword' ================================================ FILE: packages/core/src/hooks/useractions/useAccountModal.ts ================================================ import { store, useAccount } from '../../index' import { AccountStep, useOpenModals } from '@loopring-web/component-lib' import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { sleep, WalletType } from '@loopring-web/loopring-sdk' import { useAccountHook } from '../../services/account/useAccountHook' import { SUBMIT_PANEL_DOUBLE_QUICK_AUTO_CLOSE } from '@loopring-web/common-resources' export function useAccountModal() { const { shouldShow, setShouldShow, statusUnset: statusAccountUnset } = useAccount() const { setShowAccount } = useOpenModals() const handleErrorAccount = React.useCallback(() => { statusAccountUnset() }, [statusAccountUnset]) const handleLockAccount = React.useCallback(() => { statusAccountUnset() }, [setShowAccount, shouldShow, statusAccountUnset]) const handleNoAccount = React.useCallback( (_data: any) => { statusAccountUnset() setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.NoAccount, }) }, [setShowAccount, shouldShow, statusAccountUnset], ) const handleDepositingAccount = React.useCallback(async () => { setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.Deposit_Submit, }) await sleep(3000) setShouldShow(false) setShowAccount({ isShow: false }) statusAccountUnset() }, [setShouldShow, setShowAccount, shouldShow, statusAccountUnset]) const handleErrorApproveToken = React.useCallback(() => { setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.Deposit_WaitForAuth, }) }, [setShowAccount, shouldShow]) const handleErrorDepositSign = React.useCallback(() => { setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.Deposit_Failed, }) }, [setShowAccount, shouldShow]) const handleProcessDeposit = React.useCallback(() => { setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.Deposit_Approve_WaitForAuth, }) }, [setShowAccount, shouldShow]) const handleSignAccount = React.useCallback(() => { statusAccountUnset() setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.UpdateAccount, }) }, [setShowAccount, shouldShow, statusAccountUnset]) const handleSignDeniedByUser = React.useCallback(() => { setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.UnlockAccount_User_Denied, }) }, [setShowAccount, shouldShow]) const handleSignError = React.useCallback( ({ error, walletType }: { error?: sdk.RESULT_INFO; walletType?: WalletType }) => { setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.UnlockAccount_Failed, error, info: { walletType }, }) }, [setShowAccount, shouldShow], ) const handleProcessSign = React.useCallback(() => { setShowAccount({ isShow: shouldShow ?? false, step: AccountStep.UnlockAccount_WaitForAuth, }) }, [setShowAccount, shouldShow]) const handleAccountActive = React.useCallback(async () => { setShouldShow(false) statusAccountUnset() await sdk.sleep(SUBMIT_PANEL_DOUBLE_QUICK_AUTO_CLOSE) if (store.getState().modals.isShowAccount.isShow) { setShowAccount({ isShow: false }) } }, [setShouldShow, setShowAccount, statusAccountUnset]) useAccountHook({ handleErrorAccount, handleLockAccount, // clear private data handleNoAccount, // handleDepositingAccount, handleErrorApproveToken, handleErrorDepositSign, handleProcessDeposit, // two or one step handleSignAccount, //unlock or update account sgin handleProcessSign, handleSignError, handleSignDeniedByUser, // handleProcessAccountCheck, handleAccountActive, }) } ================================================ FILE: packages/core/src/hooks/useractions/useActiveAccount.ts ================================================ import { AccountStep, ResetProps, useOpenModals } from '@loopring-web/component-lib' import { useBtnStatus, useUpdateAccount, useChargeFees, useModalData, makeWalletLayer2, useWalletLayer2, store, } from '../../index' import { updateActiveAccountData as updateActiveAccountDataRedux } from '@loopring-web/core' import { LIVE_FEE_TIMES, myLog, SagaStatus, WalletMap } from '@loopring-web/common-resources' import React from 'react' export const useActiveAccount = (): { activeAccountProps: ResetProps activeAccountCheckFeeIsEnough: (props?: { isRequiredAPI: true; intervalTime?: number }) => void } => { const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const { setShowActiveAccount, setShowAccount, modals: { isShowActiveAccount: { isShow, info }, }, } = useOpenModals() const { status: walletLayer2Status } = useWalletLayer2() const [walletMap, setWalletMap] = React.useState( makeWalletLayer2({ needFilterZero: true, isActive: true }).walletMap ?? ({} as WalletMap), ) const { goUpdateAccount } = useUpdateAccount() const { activeAccountValue } = useModalData() const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, checkFeeIsEnough, resetIntervalTime, } = useChargeFees({ isActiveAccount: true, requestType: 'UPDATE_ACCOUNT_BY_NEW' as any, updateData: ({ fee, chargeFeeTokenList, isFeeNotEnough }) => { const { activeAccountValue } = store.getState()._router_modalData // const { tags } = store.getState().account; myLog('activeAccountValue feeInfo', fee, isFeeNotEnough) store.dispatch( updateActiveAccountDataRedux({ ...activeAccountValue, fee, isFeeNotEnough, chargeFeeList: chargeFeeTokenList && chargeFeeTokenList.length ? chargeFeeTokenList : activeAccountValue?.chargeFeeList && activeAccountValue?.chargeFeeList.length ? activeAccountValue?.chargeFeeList : [], }), ) }, }) const walletLayer2Callback = React.useCallback(() => { const walletMap = makeWalletLayer2({ needFilterZero: true, isActive: true }).walletMap ?? {} setWalletMap(walletMap) checkFeeIsEnough() }, []) React.useEffect(() => { if (walletLayer2Callback && walletLayer2Status === SagaStatus.UNSET) { walletLayer2Callback() } }, [walletLayer2Status]) React.useEffect(() => { if (isFeeNotEnough.isFeeNotEnough) { disableBtn() } else { enableBtn() } }, [isFeeNotEnough.isFeeNotEnough]) React.useEffect(() => { if (isShow) { checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [isShow]) const activeAccountProps: ResetProps = { onResetClick: ({ isNotFirstTime = false, isReset = false, }: { isNotFirstTime?: boolean isReset?: boolean }) => { if (activeAccountValue?.fee?.belong && activeAccountValue?.fee?.__raw__) { setShowActiveAccount({ isShow: false }) goUpdateAccount({ isFirstTime: !isNotFirstTime, isReset, feeInfo: activeAccountValue.fee, }) } }, isReset: info?.isReset, isNewAccount: true, resetBtnStatus: btnStatus, goToDeposit: () => { setShowActiveAccount({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.AddAssetGateway }) // setShowDeposit({ isShow: true }); }, walletMap, chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo: activeAccountValue.fee, } return { activeAccountProps, activeAccountCheckFeeIsEnough: checkFeeIsEnough, } } ================================================ FILE: packages/core/src/hooks/useractions/useBanxaConfirm.ts ================================================ import { AccountStatus, AddressError, BANXA_URLS, BanxaOrder, CoinMap, CustomErrorWithCode, Explorer, FeeInfo, IBData, myLog, SDK_ERROR_MAP_TO_UI, TOAST_TIME, TRADE_TYPE, UIERROR_CODE, WALLET_TYPE, WalletMap, } from '@loopring-web/common-resources' import { NETWORKEXTEND, store, useAccount, useModalData, useSystem, useTokenMap } from '../../stores' import { AccountStep, useOpenModals } from '@loopring-web/component-lib' import React from 'react' import { makeWalletLayer2 } from '../help' import { OffFaitCommon, offFaitService, useChargeFees, useWalletLayer2Socket, walletLayer2Service, } from '../../services' import { useBtnStatus } from '../common' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI } from '../../api_wrapper' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { getTimestampDaysLater } from '../../utils' import { DAYS } from '../../defs' import { RAMP_SELL_PANEL } from './useVendor' import { BalanceReason, banxaApiCall, BanxaCheck, banxaService } from '../../services' import { ChainId } from '@loopring-web/loopring-sdk' import _ from 'lodash' import { useWalletInfo } from '../../stores/localStore/walletInfo' import Web3 from 'web3' import { isAccActivated } from './useCheckAccStatus' import { useRouteMatch } from 'react-router-dom' import { useLocation } from 'react-use' import { merge } from 'rxjs' import { useTranslation } from 'react-i18next' export const useBanxaConfirm = , I, _C extends FeeInfo>({ setSellPanel, }: { sellPanel: RAMP_SELL_PANEL setSellPanel: (value: RAMP_SELL_PANEL) => void }) => { const { t } = useTranslation() const match: any = useRouteMatch('/trade/fiat/:tab?') const { href } = useLocation() const search = href?.split('?')[1] ?? '' const searchParams = new URLSearchParams(search) const subject = React.useMemo(() => merge(banxaService.onSocket(), offFaitService.onSocket()), []) const nodeTimer = React.useRef(-1) const { exchangeInfo, chainId } = useSystem() const { allowTrade: { raw_data }, } = useSystem() const legalEnable = (raw_data as any)?.legal?.enable const { tokenMap, totalCoinMap } = useTokenMap() const { setShowAccount, modals: { isShowAccount: { info }, }, } = useOpenModals() const { account } = useAccount() const [balanceNotEnough, setBalanceNotEnough] = React.useState<{ isEnough: boolean reason?: BalanceReason }>({ isEnough: false }) const { offBanxaValue, updateOffBanxaData } = useModalData() const { processRequestBanxaTransfer: processRequest, chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, } = useBanxaTransPost() const [walletMap, setWalletMap] = React.useState( makeWalletLayer2({ needFilterZero: true }).walletMap ?? ({} as WalletMap), ) const walletLayer2Callback = React.useCallback(() => { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} setWalletMap(walletMap) }, []) useWalletLayer2Socket({ walletLayer2Callback }) const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const { transferBanxaValue, updateTransferBanxaData } = useModalData() React.useEffect(() => { if ( info?.transferBanxa === AccountStep.Transfer_BANXA_Failed && info?.trigger == 'checkFeeIsEnough' ) { checkFeeIsEnough() } }, [info?.transferBanxa]) const restTransfer = React.useCallback(() => { const { _router_modalData: { offBanxaValue }, } = store.getState() if (offBanxaValue && offBanxaValue.id && offBanxaValue.status === 'waitingPayment') { const memo = `banxa-off:${offBanxaValue.id}` const walletMap = makeWalletLayer2({ needFilterZero: true })?.walletMap ?? {} setShowAccount({ isShow: false }) checkFeeIsEnough({ isRequiredAPI: true }) updateTransferBanxaData({ belong: offBanxaValue.coin_code, tradeValue: offBanxaValue.coin_amount, balance: walletMap[offBanxaValue?.coin_code ?? '']?.count, address: offBanxaValue.wallet_address ?? '', memo, // fee: feeInfo,//transferBanxaValue.fee, }) setSellPanel(RAMP_SELL_PANEL.BANXA_CONFIRM) } else { setSellPanel(RAMP_SELL_PANEL.LIST) } }, [offBanxaValue]) const checkBtnStatus = React.useCallback(() => { const transferBanxaValue = store.getState()._router_modalData.transferBanxaValue const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} if ( tokenMap && chargeFeeTokenList.length && isFeeNotEnough && !isFeeNotEnough.isFeeNotEnough && transferBanxaValue.belong && tokenMap[transferBanxaValue.belong] && transferBanxaValue.fee && transferBanxaValue.fee.belong && transferBanxaValue.address ) { const sellToken = tokenMap[transferBanxaValue.belong] const feeToken = tokenMap[transferBanxaValue.fee.belong] const feeRaw = transferBanxaValue.fee.feeRaw ?? transferBanxaValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const balance = sdk .toBig(walletMap[transferBanxaValue.belong]?.count ?? 0) .times('1e' + sellToken.decimals) myLog('transferBanxaValue balance', balance) const tradeValue = sdk .toBig(transferBanxaValue.tradeValue ?? 0) .times('1e' + sellToken.decimals) const isExceedBalance = tradeValue .plus(feeToken.tokenId === sellToken.tokenId ? fee : '0') .gt(balance) myLog('isExceedBalance', isExceedBalance, fee.toString(), tradeValue.toString()) if (tradeValue && !isExceedBalance) { enableBtn() return } else { disableBtn() if (isExceedBalance && feeToken.tokenId === sellToken.tokenId) { setBalanceNotEnough({ isEnough: true, reason: BalanceReason.FeeAndBalance, }) } else if (isExceedBalance) { setBalanceNotEnough({ isEnough: true, reason: BalanceReason.Balance, }) } } } disableBtn() }, [ chargeFeeTokenList.length, disableBtn, enableBtn, isFeeNotEnough.isFeeNotEnough, tokenMap, transferBanxaValue.address, transferBanxaValue.balance, transferBanxaValue.belong, transferBanxaValue.fee?.feeRaw, transferBanxaValue.tradeValue, ]) React.useEffect(() => { const offBanxaValue = store.getState()._router_modalData.offBanxaValue if ( match?.params?.tab?.toLowerCase() === 'sell'.toLowerCase() && searchParams.get('orderId') && searchParams.get('orderId')?.toLowerCase() == offBanxaValue?.id?.toLowerCase() ) { restTransfer() } }, [match.params?.tab, offBanxaValue?.id, searchParams.get('orderId')]) React.useEffect(() => { checkBtnStatus() }, [chargeFeeTokenList, feeInfo?.belong, isFeeNotEnough?.isFeeNotEnough, transferBanxaValue]) const onTransferClick = React.useCallback( async (isFirstTime: boolean = true) => { const { transferBanxaValue, offBanxaValue } = store.getState()._router_modalData const { accountId, accAddress, readyState, apiKey, eddsaKey } = account if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && connectProvides.usedWeb3 && transferBanxaValue.address !== '*' && transferBanxaValue.address && transferBanxaValue?.fee && offBanxaValue && offBanxaValue.id && offBanxaValue.wallet_address?.toLowerCase() === transferBanxaValue.address.toLowerCase() && transferBanxaValue?.fee.belong && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_WaitForAuth, }) const sellToken = tokenMap[transferBanxaValue.belong as string] const feeToken = tokenMap[transferBanxaValue.fee.belong] const feeRaw = transferBanxaValue.fee.feeRaw ?? transferBanxaValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const tradeValue = sdk .toBig(transferBanxaValue.tradeValue ?? 0) .times('1e' + sellToken.decimals) const finalVol = tradeValue const transferVol = finalVol.toFixed(0, 0) const [storageId, { data }] = await Promise.all([ LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: sellToken.tokenId, }, apiKey, ), banxaApiCall({ chainId: chainId as ChainId, method: sdk.ReqMethod.GET, url: `/api/orders/${offBanxaValue.id}`, query: '', payload: {}, account, }), ]) if (data?.order?.status !== 'waitingPayment' || !data?.order?.wallet_address) { offFaitService.banxaCheckStatus({ data: data?.order, }) throw new CustomErrorWithCode({ ...SDK_ERROR_MAP_TO_UI[UIERROR_CODE.ERROR_OFF_RAMP_EXPIRED], code: UIERROR_CODE.ERROR_OFF_RAMP_EXPIRED, message: 'Banxa Expired', }) // throw new CustomError(ErrorMap.ERROR_OFF_RAMP_EXPIRED); } const req: sdk.OriginTransferRequestV3 = { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: transferBanxaValue.address, payeeId: 0, storageId: storageId?.offchainId, token: { tokenId: sellToken.tokenId, volume: transferVol, }, maxFee: { tokenId: feeToken.tokenId, volume: fee.toString(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), memo: transferBanxaValue.memo, } myLog('transfer req:', req) processRequest(req, isFirstTime) } catch (e: any) { if (e?.code === UIERROR_CODE.ERROR_OFF_RAMP_EXPIRED) { setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_Failed, error: { code: UIERROR_CODE.UNKNOWN, message: t( e.code && e.messageKey ? e.messageKey : SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN].messageKey, { ns: 'error' }, ), } as sdk.RESULT_INFO, }) } else { setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_Failed, error: { code: UIERROR_CODE.UNKNOWN, message: e.message, } as sdk.RESULT_INFO, }) } // transfer failed } } else { return } }, [account, tokenMap, exchangeInfo, setShowAccount, processRequest], ) const checkOrderStatus = _.debounce(async (_order: BanxaOrder) => { if (nodeTimer.current) { clearTimeout(nodeTimer.current as NodeJS.Timeout) } //@ts-ignore _order = { ..._order, // id: "54737597f1ae4daffd9ed5891cfa68dc", } myLog('banxa check Order ', _order.id) try { const { data: { order }, } = await banxaApiCall({ chainId: chainId as ChainId, method: sdk.ReqMethod.GET, url: `/api/orders/${_order.id}`, query: '', payload: {}, account, }) myLog('banxa check Order ', order) if (order.status === 'waitingPayment' && order.wallet_address && order.coin_code) { updateOffBanxaData({ order }) banxaService.KYCDone() console.log('BANXA KYC Done BANXA order Info:', order) } else { setTimeout(() => { checkOrderStatus(order) }, 1000 * 15) } } catch (e) { if (_order && _order.id && _order.status && _order.status === 'pendingPayment') { setTimeout(() => { checkOrderStatus(_order) }, 1000 * 15) } } }, 100) React.useEffect(() => { const subscription = subject.subscribe((props) => { myLog('Banxa subscription ', props) switch (props.status) { case OffFaitCommon.OffFaitCancel: setSellPanel(RAMP_SELL_PANEL.LIST) clearTimeout(nodeTimer.current as NodeJS.Timeout) break case BanxaCheck.CheckOrderStatus: myLog('Banxa checkOrderStatus') // @ts-ignore checkOrderStatus(props.data.order) break case BanxaCheck.OrderHide: myLog('Banxa Order OrderHide') clearTimeout(nodeTimer.current as NodeJS.Timeout) break case BanxaCheck.OrderEnd: clearTimeout(nodeTimer.current as NodeJS.Timeout) setSellPanel(RAMP_SELL_PANEL.LIST) break case BanxaCheck.OrderShow: if (props.data?.reason == 'transferDone') { const { _router_modalData: { offBanxaValue }, } = store.getState() setSellPanel(RAMP_SELL_PANEL.LIST) setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_Confirm, info: { orderId: offBanxaValue?.id, hash: `${BANXA_URLS[chainId]}/status/${offBanxaValue?.id}`, }, }) } break default: break } }) return () => { clearTimeout(nodeTimer.current as NodeJS.Timeout) subscription.unsubscribe() } }, [subject]) React.useEffect(() => { if ( info?.transferBanxa === AccountStep.Transfer_BANXA_Failed && info?.trigger == 'checkFeeIsEnough' ) { checkFeeIsEnough() } }, [info?.transferBanxa]) const banxaViewProps = React.useMemo(() => { const { address, memo, fee, __request__, ...tradeData } = transferBanxaValue return { type: TRADE_TYPE.TOKEN, disabled: !(legalEnable === true), addressDefault: address, realAddr: address, tradeData, coinMap: totalCoinMap as CoinMap, transferBtnStatus: btnStatus, isLoopringAddress: true, isSameAddress: false, isAddressCheckLoading: WALLET_TYPE.Loopring, feeInfo, handleFeeChange, balanceNotEnough, chargeFeeTokenList, isFeeNotEnough, handleSureItsLayer2: () => undefined, sureItsLayer2: true, onTransferClick, handlePanelEvent: () => undefined, addrStatus: AddressError.NoError, memo, walletMap, handleOnMemoChange: () => undefined, handleOnAddressChange: () => undefined, } as any }, [ balanceNotEnough, btnStatus, chargeFeeTokenList, feeInfo, handleFeeChange, isFeeNotEnough, legalEnable, onTransferClick, totalCoinMap, transferBanxaValue, walletMap, ]) return { banxaViewProps, offBanxaValue } } export const useBanxaTransPost = () => { const { account } = useAccount() const { chainId } = useSystem() const { checkHWAddr, updateHW } = useWalletInfo() const { setShowAccount } = useOpenModals() const { offBanxaValue, updateTransferBanxaData } = useModalData() const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, // setIsFeeNotEnough, } = useChargeFees({ requestType: sdk.OffchainFeeReqType.TRANSFER, updateData: ({ fee }) => { myLog('transferBanxaValue updateData', fee) const { transferBanxaValue } = store.getState()._router_modalData updateTransferBanxaData({ ...transferBanxaValue, fee }) }, }) const processRequestBanxaTransfer = React.useCallback( async (request: sdk.OriginTransferRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account let response try { if (connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } updateTransferBanxaData({ __request__: request }) response = await LoopringAPI.userAPI.submitInternalTransfer( { request: _.cloneDeep(request), web3: connectProvides.usedWeb3 as any, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) // myLog("Banxa submitInternalTransfer:", response); if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } // setIsConfirmTransfer(false); setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_In_Progress, }) setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_Success, info: { hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-transfer`, }, }) banxaService.TransferDone({ order: offBanxaValue as any }) walletLayer2Service.sendUserUpdate() if (isHWAddr) { myLog('Banxa ......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } await sdk.sleep(TOAST_TIME) } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_First_Method_Denied, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_User_Denied, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } myLog('Transfer BANXA error', e) setShowAccount({ isShow: true, step: AccountStep.Transfer_BANXA_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } try { if ( response && offBanxaValue && (response as sdk.TX_HASH_API)?.hash && offBanxaValue.wallet_address ) { banxaApiCall({ chainId: chainId as ChainId, account, method: sdk.ReqMethod.POST, url: `/api/orders/${offBanxaValue.id}/confirm`, query: '', payload: { tx_hash: (response as sdk.TX_HASH_API)?.hash, source_address: account.accAddress, destination_address: offBanxaValue.wallet_address, }, }) } } catch (error) { console.error('Banxa confirm error', error) } }, [ account, chainId, checkHWAddr, setShowAccount, updateHW, offBanxaValue, updateTransferBanxaData, ], ) return { processRequestBanxaTransfer, chargeFeeTokenList, isFeeNotEnough, // setIsFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, } } ================================================ FILE: packages/core/src/hooks/useractions/useBtrade.ts ================================================ import * as sdk from '@loopring-web/loopring-sdk' import React from 'react' import { DAYS, dexSwapDependAsync, getTimestampDaysLater, LoopringAPI, makeWalletLayer2, MAPFEEBIPS, marketInitCheck, reCalcStoB, store, useAccount, useBtradeMap, usePairMatch, useSocket, useSubmitBtn, useSystem, useToast, useTokenMap, useTokenPrices, useWalletLayer2, useWalletLayer2Socket, walletLayer2Service, } from '@loopring-web/core' import { AccountStatus, BtradeTradeCalcData, BtradeType, CoinMap, CustomErrorWithCode, defaultBlockTradeSlipage, EmptyValueTag, getValuePrecisionThousand, IBData, MarketType, myLog, SagaStatus, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, SUBMIT_PANEL_DOUBLE_QUICK_AUTO_CLOSE, TradeBtnStatus, UIERROR_CODE, WalletMap, RouterPath, globalSetup, } from '@loopring-web/common-resources' import { AccountStep, SwapData, SwapTradeData, SwapType, ToastType, useOpenModals, useSettings, useToggle, } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import { useHistory } from 'react-router-dom' import { useTradeBtrade } from '../../stores/router/tradeBtrade' import BigNumber from 'bignumber.js' import { merge } from 'rxjs' import { btradeOrderbookService } from '../../services' import _ from 'lodash' const useBtradeSocket = ({}: { updateAPICall: () => void }) => { const { sendSocketTopic, socketEnd } = useSocket() const { tradeBtrade, updateTradeBtrade } = useTradeBtrade() const { marketMap } = useBtradeMap() const subjectBtradeOrderbook = React.useMemo(() => btradeOrderbookService.onSocket(), []) React.useEffect(() => { if (tradeBtrade?.depth?.symbol) { sendSocketTopic({ [sdk.WsTopicType.btradedepth]: { showOverlap: false, markets: [tradeBtrade?.depth?.symbol], level: 0, count: 50, snapshot: false, }, }) } else { socketEnd() } return () => { socketEnd() } }, [ // account.readyState, tradeBtrade?.depth?.symbol, ]) React.useEffect(() => { const { tradeBtrade } = store.getState()._router_tradeBtrade const subscription = merge(subjectBtradeOrderbook).subscribe(({ btradeOrderbookMap }) => { // const { market } = store.getState()._router_tradeBtrade.tradeBtrade const item = marketMap[tradeBtrade.market] if (btradeOrderbookMap && item.btradeMarket && btradeOrderbookMap[item?.btradeMarket]) { updateTradeBtrade({ // @ts-ignore market: item.market, depth: btradeOrderbookMap[item?.btradeMarket], ...item, }) myLog('useBtradeSwap: depth', btradeOrderbookMap[item?.btradeMarket]) // debonceCall() } }) return () => subscription.unsubscribe() }, [tradeBtrade.market]) } export const useBtradeSwap = < T extends SwapTradeData>, C extends { [key: string]: any }, CAD extends BtradeTradeCalcData, >({ path, }: { path: string }) => { const { marketCoins, marketArray, marketMap, tradeMap, getBtradeMap } = useBtradeMap() //High: No not Move!!!!!! const { realMarket } = usePairMatch({ path, coinA: 'ETH', coinB: 'USDC', marketArray, tokenMap: tradeMap, }) const { t } = useTranslation(['common', 'error']) const history = useHistory() const refreshRef = React.createRef() const { toastOpen, setToastOpen, closeToast } = useToast() const { isMobile } = useSettings() const { setShowSupport, setShowTradeIsFrozen, setShowAccount } = useOpenModals() const { account } = useAccount() const { toggle: { BTradeInvest }, } = useToggle() /** loaded from loading **/ const { exchangeInfo, allowTrade } = useSystem() const { coinMap, tokenMap } = useTokenMap() /** init Ticker ready from ui-backend load**/ /** get store value **/ /** after unlock **/ const { status: walletLayer2Status } = useWalletLayer2() const [tradeData, setTradeData] = React.useState(undefined) const [tradeCalcData, setTradeCalcData] = React.useState>({ isBtrade: true, lockedNotification: true, coinInfoMap: marketCoins?.reduce((prev: any, item: string | number) => { return { ...prev, [item]: coinMap ? coinMap[item] : {} } }, {} as CoinMap), } as any) /** redux storage **/ const { tradeBtrade, updateTradeBtrade, __SUBMIT_LOCK_TIMER__, __TOAST_AUTO_CLOSE_TIMER__, __DAYS__, } = useTradeBtrade() const resetMarket = (_market: MarketType, type: 'sell' | 'buy') => { const { tradePair } = marketInitCheck({ market: _market, type, defaultValue: 'ETH-USDC', marketArray, marketMap, tokenMap: tradeMap, }) // @ts-ignore const [_, sellToken, buyToken] = (tradePair ?? '').match(/(\w+)-(\w+)/i) let { market } = sdk.getExistedMarket(marketArray, sellToken, buyToken) setIsMarketStatus((state) => { return { tradePair, market, isMarketInit: state.market !== market, } }) if (coinMap && tokenMap && tradeMap && marketMap && marketArray) { // @ts-ignore const [, coinA, coinB] = tradePair.match(/([\w,#]+)-([\w,#]+)/i) let walletMap: WalletMap | undefined if ( account.readyState === AccountStatus.ACTIVATED && walletLayer2Status === SagaStatus.UNSET ) { if (!Object.keys(tradeCalcData?.walletMap ?? {}).length) { walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap as WalletMap } walletMap = tradeCalcData?.walletMap as WalletMap } const tradeDataTmp: any = { sell: { belong: coinA, tradeValue: undefined, balance: walletMap ? walletMap[coinA]?.count : 0, }, buy: { belong: coinB, tradeValue: undefined, balance: walletMap ? walletMap[coinB]?.count : 0, }, } const sellCoinInfoMap = tradeMap[coinB]?.tradePairs?.reduce( (prev: any, item: string | number) => { return { ...prev, [item]: coinMap[item] } }, {} as CoinMap, ) const buyCoinInfoMap = tradeMap[coinA]?.tradePairs?.reduce( (prev: any, item: string | number) => { return { ...prev, [item]: coinMap[item] } }, {} as CoinMap, ) const btradeType = BtradeType.Quantity let _tradeCalcData = {} setTradeCalcData((state) => { _tradeCalcData = { ...state, walletMap, coinSell: coinA, coinBuy: coinB, sellPrecision: tokenMap[coinA as string]?.precision, buyPrecision: tokenMap[coinB as string]?.precision, sellCoinInfoMap, buyCoinInfoMap, StoB: undefined, BtoS: undefined, fee: undefined, tradeCost: undefined, lockedNotification: true, volumeSell: undefined, volumeBuy: undefined, sellMinAmtStr: undefined, sellMaxL2AmtStr: undefined, sellMaxAmtStr: undefined, totalQuota: undefined, l1Pool: undefined, l2Pool: undefined, btradeType, } return _tradeCalcData }) setTradeData((state) => { return { ...state, btradeType, ...tradeDataTmp, } }) history.push(`${RouterPath.btrade}/${_market}`) updateTradeBtrade({ market, tradePair, btradeType, tradeCalcData: _tradeCalcData, }) } } const [{ market, isMarketInit }, setIsMarketStatus] = React.useState<{ market: MarketType tradePair?: MarketType isMarketInit?: boolean }>({} as any) React.useEffect(() => { resetMarket(realMarket ?? '#null-#null', 'sell') }, []) React.useEffect(() => { if (marketArray?.[0]) { resetMarket(marketArray[0] as MarketType, 'sell') } }, [marketArray?.[0]]) const [isBtradeLoading, setIsBtradeLoading] = React.useState(false) const { tokenPrices } = useTokenPrices() const clearData = () => { let _tradeCalcData: any = {} let btradeType: any setTradeData((state) => { btradeType = state?.btradeType ? state.btradeType : BtradeType.Quantity return { ...state, btradeType: state?.btradeType ? state.btradeType : BtradeType.Quantity, sell: { ...state?.sell, tradeValue: undefined }, buy: { ...state?.buy, tradeValue: undefined }, } as T }) setTradeCalcData((state) => { _tradeCalcData = { ...(state ?? {}), maxFeeBips: undefined, lockedNotification: true, volumeSell: undefined, volumeBuy: undefined, btradeType, } return _tradeCalcData }) updateTradeBtrade({ market, maxFeeBips: 0, btradeType, tradeCalcData: { ..._tradeCalcData, }, }) } const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string | undefined } => { if (!tokenMap && !tokenPrices) { // setSwapBtnStatus(); // return {tradeBtnStatus:TradeBtnStatus.DISABLED}; return { label: undefined, tradeBtnStatus: TradeBtnStatus.DISABLED, } } // const account = store.getState().account; const sellToken = tokenMap[tradeData?.sell.belong as string] const buyToken = tokenMap[tradeData?.buy.belong as string] const account = store.getState().account const { tradeCalcData, sellMinAmtInfo, sellMaxAmtInfo } = tradeBtrade if (!sellToken || !buyToken || !tradeCalcData) { return { label: undefined, tradeBtnStatus: TradeBtnStatus.DISABLED, } } const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} let validAmt = !!( tradeCalcData?.volumeSell && sellMinAmtInfo && sdk .toBig(tradeCalcData?.volumeSell) .gte(sdk.toBig(sellMinAmtInfo).times('1e' + sellToken.decimals)) ) const sellExceed = sellMaxAmtInfo ? sdk .toBig(sellMaxAmtInfo) .times('1e' + sellToken.decimals) .lt(tradeCalcData.volumeSell ?? 0) : false if (sellExceed) { validAmt = false } const notEnough = sdk .toBig(walletMap[sellToken.symbol]?.count ?? 0) .lt(tradeData?.sell?.tradeValue ?? 0) if (isBtradeLoading || isMarketInit) { return { label: undefined, tradeBtnStatus: TradeBtnStatus.LOADING, } } else { if (account.readyState === AccountStatus.ACTIVATED) { if (!tradeCalcData || !tradeCalcData.volumeSell || !tradeCalcData.volumeBuy) { return { label: 'labelEnterAmount', tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (notEnough) { const sellSymbol = tradeData?.sell.belong return { label: `labelArgNoEnough| ${sellSymbol}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (sellExceed) { const maxOrderSize = tradeCalcData.sellMaxAmtStr + ' ' + tradeData?.sell.belong return { label: `labelLimitMax| ${maxOrderSize}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (!validAmt) { const sellSymbol = tradeData?.sell.belong if (sellMinAmtInfo === undefined || !sellSymbol || sellMinAmtInfo === 'NaN') { return { label: 'labelEnterAmount', tradeBtnStatus: TradeBtnStatus.DISABLED, } } else { const sellToken = tokenMap[sellSymbol] const minOrderSize = getValuePrecisionThousand( sdk.toBig(sellMinAmtInfo ?? 0), //.div("1e" + sellToken.decimals), sellToken.precision, sellToken.precision, sellToken.precision, false, { floor: false, isAbbreviate: true }, ) if (isNaN(Number(minOrderSize))) { return { label: `labelLimitMin| ${EmptyValueTag + ' ' + sellSymbol}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else { return { label: `labelLimitMin| ${minOrderSize + ' ' + sellSymbol}`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } } } else { return { label: undefined, tradeBtnStatus: TradeBtnStatus.AVAILABLE, } } } else { return { label: undefined, tradeBtnStatus: TradeBtnStatus.AVAILABLE, } } } }, [ account, tokenMap, tradeData?.sell.belong, tradeData?.buy.belong, tradeBtrade.maxFeeBips, tradeData?.sell.tradeValue, tradeData?.buy.tradeValue, isBtradeLoading, isMarketInit, ]) const sendRequest = React.useCallback(async () => { setIsBtradeLoading(true) const { tradeBtrade: { tradeCalcData, sellToken: _sellToken, buyToken: _buyToken }, } = store.getState()._router_tradeBtrade const account = store.getState().account try { if ( account.readyState == AccountStatus.ACTIVATED && _sellToken && _buyToken && tokenMap && exchangeInfo && tradeCalcData && tradeCalcData?.volumeSell && tradeCalcData?.volumeBuy && tradeCalcData.maxFeeBips && LoopringAPI.userAPI && LoopringAPI.defiAPI ) { const sellToken = tokenMap[_sellToken] const buyToken = tokenMap[_buyToken] const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: account.accountId, sellTokenId: sellToken?.tokenId ?? 0, }, account.apiKey, ) const request: sdk.OriginBTRADEV3OrderRequest = { exchange: exchangeInfo.exchangeAddress, storageId: storageId.orderId, accountId: account.accountId, sellToken: { tokenId: sellToken?.tokenId ?? 0, volume: sdk.toBig(tradeCalcData.volumeSell).toFixed(0), }, buyToken: { tokenId: buyToken?.tokenId ?? 0, volume: sdk.toBig(tradeCalcData.volumeBuy).toFixed(0), }, validUntil: getTimestampDaysLater(DAYS), maxFeeBips: tradeCalcData.maxFeeBips, fillAmountBOrS: false, allOrNone: false, eddsaSignature: '', clientOrderId: '', orderType: sdk.OrderTypeResp.TakerOnly, fastMode: tradeCalcData.btradeType === BtradeType.Speed ? true : false, } myLog('useBtradeSwap: submitOrder request', request) const response: { hash: string } | any = await LoopringAPI.defiAPI.sendBtradeOrder({ request, privateKey: account.eddsaKey.sk, apiKey: account.apiKey, }) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw new CustomErrorWithCode({ code: (response as sdk.RESULT_INFO).code, message: (response as sdk.RESULT_INFO).message, ...SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? UIERROR_CODE.UNKNOWN], }) } else { clearData() } const item = { fromSymbol: sellToken.symbol, fromAmount: sdk.toBig(tradeCalcData.volumeSell).div('1e' + sellToken.decimals), settledFromAmount: undefined, toSymbol: buyToken.symbol, feeAmount: tradeCalcData.fee, settledToAmount: undefined, } let info: any = { sellToken, buyToken, sellStr: getValuePrecisionThousand( sdk.toBig(tradeCalcData.volumeSell).div('1e' + sellToken.decimals), sellToken.precision, sellToken.precision, sellToken.precision, false, { floor: false }, ), buyStr: getValuePrecisionThousand( sdk.toBig(tradeCalcData.volumeBuy).div('1e' + buyToken.decimals), buyToken.precision, buyToken.precision, buyToken.precision, false, { floor: false }, ), sellFStr: undefined, buyFStr: undefined, convertStr: marketMap[market].baseTokenId !== sellToken.tokenId ? `1 ${buyToken.symbol} \u2248 ${ sdk.toBig(tradeCalcData.volumeSell) .multipliedBy('1e' + buyToken.decimals) .div('1e' + sellToken.decimals) .div(tradeCalcData.volumeBuy).toFixed(4) } ${sellToken.symbol}` : `1 ${sellToken.symbol} \u2248 ${ sdk.toBig(tradeCalcData.volumeBuy) .multipliedBy('1e' + sellToken.decimals) .div('1e' + buyToken.decimals) .div(tradeCalcData.volumeSell).toFixed(4) } ${buyToken.symbol}`, feeStr: tradeCalcData?.fee, time: undefined, placedAmount: tokenMap && sellToken.symbol && item.fromAmount && sdk.toBig(item.fromAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.fromAmount), undefined, undefined, tokenMap[item.fromSymbol].precision, false, { isAbbreviate: true }, )} ${item.fromSymbol}` : EmptyValueTag, executedAmount: tokenMap && item.fromSymbol && item.settledFromAmount && sdk.toBig(item.settledFromAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.settledFromAmount), undefined, undefined, tokenMap[item.fromSymbol].precision, false, { isAbbreviate: true }, )} ${item.fromSymbol}` : EmptyValueTag, executedRate: tokenMap && item.fromSymbol && item.settledFromAmount && item.fromAmount && sdk.toBig(item.fromAmount).gt(0) ? `${sdk .toBig(item.settledFromAmount) .div(item.fromAmount) .multipliedBy('100') .toFixed(2)}%` : EmptyValueTag, convertedAmount: tokenMap && item.toSymbol && item.settledToAmount && sdk.toBig(item.settledToAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.settledToAmount), undefined, undefined, tokenMap[item.toSymbol].precision, false, { isAbbreviate: true }, )} ${item.toSymbol}` : EmptyValueTag, settledAmount: tokenMap && item.toSymbol && item.settledToAmount && item.feeAmount && sdk.toBig(item.settledToAmount).gt(0) ? `${getValuePrecisionThousand( sdk.toBig(item.settledToAmount).minus(item.feeAmount), undefined, undefined, tokenMap[item.toSymbol].precision, false, { isAbbreviate: true }, )} ${item.toSymbol}` : EmptyValueTag, } setShowAccount({ isShow: true, step: AccountStep.BtradeSwap_Pending, info, }) walletLayer2Service.sendUserUpdate() await sdk.sleep(SUBMIT_PANEL_DOUBLE_QUICK_AUTO_CLOSE) if (refreshRef.current) { // @ts-ignore refreshRef.current.firstElementChild.click() } const orderConfirm: { hash: string } | any = await LoopringAPI.defiAPI.getBtradeOrders({ request: { accountId: account.accountId, // @ts-ignore hash: response.hash, }, apiKey: account.apiKey, }) if ((orderConfirm as sdk.RESULT_INFO).code || (orderConfirm as sdk.RESULT_INFO).message) { } else if (['failed', 'cancelled'].includes(orderConfirm.status)) { throw 'orderConfirm failed' } else if (store.getState().modals.isShowAccount.isShow) { setShowAccount({ isShow: true, step: AccountStep.BtradeSwap_Pending, info: { ...info, }, }) } await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if (store.getState().modals.isShowAccount.isShow) { setShowAccount({ isShow: false }) } } else { throw new Error('api not ready') } } catch (error: any) { let content: string = '' if ([102024, 102025, 114001, 114002].includes(error?.code || 0)) { content = t('labelBtradeSwapFailed') + ' error: ' + (error && error.messageKey ? t(error.messageKey, { ns: 'error' }) : (error as sdk.RESULT_INFO).message) } else { sdk.dumpError400(error) content = t('labelBtradeSwapFailed') + ' error: ' + (error && error.messageKey ? t(error.messageKey, { ns: 'error' }) : (error as sdk.RESULT_INFO).message) } setShowAccount({ isShow: false, }) setToastOpen({ open: true, type: ToastType.error, content, }) } setIsBtradeLoading(false) }, [ tradeBtrade, tradeData, tokenMap, exchangeInfo, __SUBMIT_LOCK_TIMER__, setToastOpen, t, __DAYS__, market, __TOAST_AUTO_CLOSE_TIMER__, updateTradeBtrade, ]) /*** Btn related function ***/ const btradeSwapSubmit = React.useCallback(async () => { if (!allowTrade?.order?.enable) { setShowSupport({ isShow: true }) setIsBtradeLoading(false) return } else if (!marketMap[market]?.enabled || !BTradeInvest.enable) { setShowTradeIsFrozen({ isShow: true, type: 'Btrade', }) setIsBtradeLoading(false) return } else { sendRequest() } }, [market, marketMap, BTradeInvest]) const { btnStatus: swapBtnStatus, onBtnClick: onSwapClick, btnLabel: swapBtnI18nKey, } = useSubmitBtn({ availableTradeCheck, isLoading: isBtradeLoading || (isMarketInit ?? false), submitCallback: btradeSwapSubmit, }) const should15sRefresh = React.useCallback(() => { myLog('useBtradeSwap: should15sRefresh', market) if (market) { getBtradeMap() callPairDetailInfoAPIs() } }, [market]) React.useEffect(() => { const { depth, market } = store.getState()._router_tradeBtrade.tradeBtrade if (depth && new RegExp(market).test(depth?.symbol)) { refreshWhenDepthUp() setIsMarketStatus((state) => { return { ...state, isMarketInit: false, } }) } }, [tradeBtrade.depth, account.readyState, market]) const walletLayer2Callback = React.useCallback(async () => { if (account.readyState === AccountStatus.ACTIVATED) { refreshWhenDepthUp() } else { if (tradeCalcData.coinSell && tradeCalcData.coinBuy) { setTradeData((state) => { return { ...state, btradeType: state?.btradeType ? state.btradeType : BtradeType.Quantity, sell: { belong: tradeCalcData.coinSell }, buy: { belong: tradeCalcData.coinBuy }, } as T }) } updateTradeBtrade({ market: market as MarketType, maxFeeBips: 0, totalFee: 0, tradeCalcData: {}, }) setTradeCalcData((state) => { return { ...state, walletMap: {}, minimumReceived: undefined, priceImpact: undefined, fee: undefined, } }) } }, [tradeData, market, tradeCalcData, marketArray, account.readyState]) const callPairDetailInfoAPIs = React.useCallback(async () => { if (market && LoopringAPI.defiAPI && marketMap && marketMap[market]) { try { const { depth } = await dexSwapDependAsync({ // @ts-ignore market: marketMap[market]?.btradeMarket, tokenMap, }) updateTradeBtrade({ // @ts-ignore market, depth, ...marketMap[market], }) myLog('useBtradeSwap: depth', depth) } catch (error: any) { myLog('useBtradeSwap:', error, 'go to LRC-ETH') setToastOpen({ open: true, content: 'error: resetMarket', type: ToastType.error, }) // myLog("useBtradeSwap:", error, "go to LRC-USDT"); resetMarket(market, 'sell') } } }, [market, marketMap]) useBtradeSocket({ updateAPICall: callPairDetailInfoAPIs }) useWalletLayer2Socket({ walletLayer2Callback }) /*** user Action function ***/ //High: effect by wallet state update const handleSwapPanelEvent = async ( swapData: SwapData>>, swapType: any, ): Promise => { const { tradeData: _tradeData } = swapData myLog('useBtradeSwap: resetSwap', swapType, _tradeData) const depth = store.getState()._router_tradeBtrade.tradeBtrade?.depth switch (swapType) { case SwapType.SEll_CLICK: case SwapType.BUY_CLICK: return case SwapType.SELL_SELECTED: myLog(_tradeData) if (_tradeData?.sell.belong !== tradeData?.sell.belong) { resetMarket( `${_tradeData?.sell?.belong ?? `#null`}-${_tradeData?.buy?.belong ?? `#null`}`, 'sell', ) } else { reCalculateDataWhenValueChange( _tradeData, `${_tradeData?.sell.belong}-${_tradeData?.buy.belong}`, 'sell', ) } break case SwapType.BUY_SELECTED: if (_tradeData?.buy.belong !== tradeData?.buy.belong) { resetMarket( `${_tradeData?.sell?.belong ?? `#null`}-${_tradeData?.buy?.belong ?? `#null`}`, 'buy', ) } else { reCalculateDataWhenValueChange( _tradeData, `${_tradeData?.sell.belong}-${_tradeData?.buy.belong}`, 'buy', ) } break case SwapType.EXCHANGE_CLICK: let StoB, BtoS if (depth && depth.mid_price) { const pr1 = sdk.toBig(1).div(depth.mid_price).toString() const pr2 = depth.mid_price ;[StoB, BtoS] = market === `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}` ? [pr1, pr2] : [pr2, pr1] } const sellPrecision = tokenMap[tradeCalcData.coinBuy as string].precision const buyPrecision = tokenMap[tradeCalcData.coinSell as string].precision const reserveInfo = sdk.getReserveInfo(tradeCalcData.coinSell as string, tradeCalcData.coinBuy as string, marketArray, tokenMap, marketMap as any) const _tradeCalcData = { ...tradeCalcData, coinSell: tradeCalcData.coinBuy, coinBuy: tradeCalcData.coinSell, sellPrecision, buyPrecision, sellCoinInfoMap: tradeCalcData.buyCoinInfoMap, buyCoinInfoMap: tradeCalcData.sellCoinInfoMap, StoB: getValuePrecisionThousand( StoB, undefined, reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, true, ), BtoS: getValuePrecisionThousand( BtoS, undefined, !reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, !reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, true, ), } myLog( 'useBtradeSwap:Exchange,tradeCalcData,_tradeCalcData', tradeData, tradeCalcData, _tradeCalcData, ) callPairDetailInfoAPIs() updateTradeBtrade({ market, tradePair: `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}`, tradeCalcData: _tradeCalcData, }) setTradeCalcData(_tradeCalcData) // @ts-ignore setTradeData((state) => { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap return { ...(state ?? {}), sell: { belong: _tradeCalcData.coinSell, tradeValue: undefined, balance: walletMap ? walletMap[_tradeCalcData.coinSell as string]?.count : 0, }, buy: { belong: _tradeCalcData.coinBuy, tradeValue: undefined, balance: walletMap ? walletMap[_tradeCalcData.coinBuy as string]?.count : 0, }, } }) break default: // myLog("useBtradeSwap:resetSwap default"); // resetTradeCalcData(undefined, market); break } } React.useEffect(() => { if (market) { //@ts-ignore if (refreshRef.current) { // @ts-ignore refreshRef.current.firstElementChild.click() should15sRefresh() } } }, [market]) const reCalculateDataWhenValueChange = React.useCallback( (_tradeData, _tradePair?, type?) => { const { tradeBtrade: { depth, tradePair, btradeType: _btradeType }, } = store.getState()._router_tradeBtrade const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap myLog('useBtradeSwap:reCalculateDataWhenValueChange', tradeData, _tradePair, type) if ( depth && marketMap[market] && marketMap[market]?.enabled !== 'isFormLocal' && market && _tradePair === tradePair && _tradeData?.sell ) { const btradeType = _tradeData.btradeType ? _tradeData.btradeType : _btradeType const coinA = _tradeData?.sell.belong const coinB = _tradeData?.buy.belong _tradeData.sell.balance = walletMap ? walletMap[coinA]?.count : 0 const sellToken = tokenMap[coinA as string] const buyToken = tokenMap[coinB as string] const sellBuyStr = `${sellToken.symbol}-${buyToken.symbol}` const isAtoB = type === 'sell' let input: any = isAtoB ? _tradeData.sell.tradeValue : _tradeData.buy.tradeValue input = input === undefined || isNaN(Number(input)) ? 0 : Number(input) let totalFee: any = undefined let stob: string | undefined = undefined let btos: string | undefined = undefined let minimumReceived: any let minimumConverted: string | undefined = undefined let sellMinAmtInfo: any = undefined let sellMaxAmtInfo: any = undefined let sellMaxL2AmtInfo: any = undefined let totalFeeRaw: any = undefined let totalQuote: any = undefined let poolToVol: any = undefined const info: sdk.BTRADE_MARKET = marketMap[market] as sdk.BTRADE_MARKET let maxFeeBips = info.feeBips ?? MAPFEEBIPS let slippage = sdk .toBig( _tradeData.slippage && !isNaN(_tradeData.slippage) ? _tradeData.slippage : defaultBlockTradeSlipage, ) .times(100) .toString() const { btradeAmount, minAmount, l2Amount } = info const calcDexOutput = sdk.calcDex({ info, input: input.toString(), sell: sellToken.symbol, buy: buyToken.symbol, isAtoB, marketArr: marketArray, tokenMap, marketMap: marketMap as any, depth, feeBips: maxFeeBips.toString(), slipBips: slippage, }) if ( btradeAmount && l2Amount && (sellBuyStr == market ? btradeAmount.base !== '0' : btradeAmount.quote !== '0') ) { const btradeAmountVol = sellBuyStr == market ? btradeAmount.base : btradeAmount.quote if (btradeAmountVol) { poolToVol = sdk .toBig(btradeAmountVol) .div('1e' + sellToken.decimals) .toString() ?? '0' } const sellDeepStr = sdk .toBig(sellBuyStr == market ? depth.bids_amtTotal : depth.asks_volTotal) .div('1e' + sellToken.decimals) .times(0.99) .toString() ?? '0' if (btradeType === BtradeType.Speed) { const calcDexL2Output = sdk.calcDex({ info, input: (sellBuyStr == market ? sdk.toBig(l2Amount.quote ?? 0) : sdk.toBig(l2Amount.base ?? 0) ) .div('1e' + buyToken.decimals) .toString(), //input.toString(), sell: sellToken.symbol, buy: buyToken.symbol, isAtoB: false, marketArr: marketArray, tokenMap, marketMap: marketMap as any, depth, feeBips: maxFeeBips.toString(), slipBips: slippage, }) totalQuote = poolToVol ? getValuePrecisionThousand( BigNumber.min(sellDeepStr, poolToVol, calcDexL2Output?.amountS ?? 0), sellToken.precision, sellToken.precision, undefined, false, { isAbbreviate: true }, ) : EmptyValueTag sellMaxAmtInfo = poolToVol ? BigNumber.min(sellDeepStr, poolToVol, calcDexL2Output?.amountS ?? 0) : sellDeepStr } else { sellMaxAmtInfo = poolToVol ? BigNumber.min(sellDeepStr, poolToVol) : sellDeepStr totalQuote = poolToVol ? getValuePrecisionThousand( BigNumber.min(sellDeepStr, poolToVol), sellToken.precision, sellToken.precision, undefined, false, { isAbbreviate: true }, ) : (sellBuyStr == market ? btradeAmount.base == '0' : btradeAmount.quote == '0') ? t('labelBtradeInsufficient') : EmptyValueTag } sellMinAmtInfo = BigNumber.max( sellToken.orderAmounts.dust, sellBuyStr == market ? minAmount.base : minAmount.quote, ) .div('1e' + sellToken.decimals) .toString() } if (calcDexOutput) { totalFeeRaw = sdk .toBig(calcDexOutput?.amountBSlipped?.minReceived ?? 0) .div(10000) .times(maxFeeBips) totalFee = totalFeeRaw.gt(0) ? getValuePrecisionThousand( sdk .toBig(totalFeeRaw) .div('1e' + buyToken.decimals) .toString(), buyToken.precision, buyToken.precision, undefined, ) : 0 minimumReceived = sdk.toBig(calcDexOutput?.amountBSlipped?.minReceived ?? 0).gt(0) ? getValuePrecisionThousand( sdk .toBig(calcDexOutput?.amountBSlipped?.minReceived ?? 0) .minus(totalFeeRaw ?? 0) .div('1e' + buyToken.decimals) .toString(), buyToken.precision, buyToken.precision, undefined, ) : 0 minimumConverted = calcDexOutput?.amountB ? getValuePrecisionThousand( sdk .toBig(calcDexOutput?.amountB) .times(sdk.toBig(1).minus(sdk.toBig(slippage).div('10000'))) .toString(), buyToken.precision, buyToken.precision, undefined, ) : undefined _tradeData[isAtoB ? 'buy' : 'sell'].tradeValue = !_tradeData[isAtoB ? 'sell' : 'buy'].tradeValue && _tradeData[isAtoB ? 'sell' : 'buy'].tradeValue != '0' ? (undefined as any) : getValuePrecisionThousand( calcDexOutput[`amount${isAtoB ? 'B' : 'S'}`], isAtoB ? buyToken.precision : sellToken.precision, isAtoB ? buyToken.precision : sellToken.precision, isAtoB ? buyToken.precision : sellToken.precision, ).replaceAll(sdk.SEP, '') let result = reCalcStoB({ market, tradeData: _tradeData as SwapTradeData>, tradePair: tradePair as any, marketMap, tokenMap: tradeMap, noGetValuePrecisionThousand: true }) stob = result?.stob btos = result?.btos } else { minimumReceived = undefined } let _tradeCalcData: any = { minimumReceived, maxFeeBips, btradeType, volumeSell: calcDexOutput?.sellVol as any, volumeBuy: calcDexOutput?.amountBSlipped?.minReceived, fee: totalFee, slippage: sdk.toBig(slippage).div(100).toString(), isReverse: calcDexOutput?.isReverse, lastStepAt: type, sellMinAmtStr: getValuePrecisionThousand( sdk.toBig(sellMinAmtInfo ?? 0), sellToken.precision, sellToken.precision, sellToken.precision, false, ), sellMaxL2AmtStr: getValuePrecisionThousand( sdk.toBig(sellMaxL2AmtInfo ?? 0), sellToken.precision, sellToken.precision, sellToken.precision, false, { isAbbreviate: true }, ), sellMaxAmtStr: sellMaxAmtInfo !== undefined ? getValuePrecisionThousand( sdk.toBig(sellMaxAmtInfo ?? 0), sellToken.precision, sellToken.precision, undefined, false, { isAbbreviate: true }, ) : undefined, totalQuota: totalQuote, l1Pool: poolToVol, l2Pool: getValuePrecisionThousand( sdk .toBig((sellBuyStr == market ? l2Amount.quote : l2Amount.base) ?? 0) .div('1e' + buyToken.decimals), buyToken.precision, buyToken.precision, undefined, false, ), minimumConverted, } setTradeCalcData((state) => { const [mid_price, _mid_price_convert] = calcDexOutput ? [ depth.mid_price, 1 / depth.mid_price ] : [undefined, undefined] stob = stob ? stob : calcDexOutput ? calcDexOutput.isReverse ? _mid_price_convert!.toString() : mid_price!.toString() : state.StoB ? state.StoB: undefined btos = btos ? btos : calcDexOutput ? calcDexOutput.isReverse ? mid_price!.toString() : _mid_price_convert!.toString() : state?.BtoS ? state?.BtoS : undefined _tradeCalcData = { ...state, ..._tradeCalcData, StoB: getValuePrecisionThousand( stob?.replaceAll(sdk.SEP, '') ?? 0, undefined, calcDexOutput?.isReverse ? 6 : marketMap[market].precisionForPrice, calcDexOutput?.isReverse ? 6 : marketMap[market].precisionForPrice, true ), BtoS: getValuePrecisionThousand( btos?.replaceAll(sdk.SEP, '') ?? 0, undefined, !calcDexOutput?.isReverse ? 6 : marketMap[market].precisionForPrice, !calcDexOutput?.isReverse ? 6 : marketMap[market].precisionForPrice, true ), lastStepAt: type, } return _tradeCalcData }) setTradeData((state) => ({ ...state, ..._tradeData, sell: { ...state?.sell, ..._tradeData?.sell, balance: walletMap ? walletMap[_tradeData?.sell?.belong ?? '']?.count : 0, }, })) updateTradeBtrade({ info: info, market: market as any, totalFee: totalFee, totalFeeRaw: totalFeeRaw?.toString(), lastStepAt: type, sellMinAmtInfo: sellMinAmtInfo as any, sellMaxL2AmtInfo: sellMaxL2AmtInfo as any, sellMaxAmtInfo: sellMaxAmtInfo as any, tradeCalcData: _tradeCalcData, maxFeeBips, btradeType, }) } }, [account.readyState, tradeBtrade, tradeCalcData, tradeData, coinMap, tokenMap, marketArray], ) const refreshWhenDepthUp = React.useCallback(() => { const { depth, lastStepAt, tradePair, market } = tradeBtrade if ( (depth && depth.symbol === market) || (tradeData && lastStepAt && tradeCalcData.coinSell === tradeData['sell'].belong && tradeCalcData.coinBuy === tradeData['buy'].belong && tradeData[lastStepAt].tradeValue && tradeData[lastStepAt].tradeValue !== 0) ) { reCalculateDataWhenValueChange(tradeData, tradePair, lastStepAt) } else if ( depth && tradeCalcData.coinSell && tradeCalcData.coinBuy && (`${tradeCalcData.coinSell}-${tradeCalcData.coinBuy}` === market || `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}` === market) ) { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap const result = reCalcStoB({ market, tradeData: tradeData as SwapTradeData>, tradePair: tradePair as any, marketMap, tokenMap: tradeMap, noGetValuePrecisionThousand: true }) const buyToken = tokenMap[tradeCalcData.coinBuy] const sellToken = tokenMap[tradeCalcData.coinSell] let _tradeCalcData: any = {} setTradeCalcData((state) => { const pr1 = sdk.toBig(1).div(depth.mid_price).toString() const pr2 = depth.mid_price const [StoB, BtoS] = market === `${tradeCalcData.coinBuy}-${tradeCalcData.coinSell}` ? [pr1, pr2] : [pr2, pr1] const reserveInfo = sdk.getReserveInfo(sellToken.symbol, buyToken.symbol, marketArray, tokenMap, marketMap as any) _tradeCalcData = { ...state, ...tradeCalcData, StoB: getValuePrecisionThousand( (result ? result?.stob : StoB.toString())?.replaceAll(sdk.SEP, ''), undefined, reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, true, ), BtoS: getValuePrecisionThousand( (result ? result?.btos : BtoS.toString())?.replaceAll(sdk.SEP, ''), undefined, !reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, !reserveInfo?.isReverse ? 6 : marketMap[market].precisionForPrice, true, ), } return { ..._tradeCalcData, walletMap, } }) reCalculateDataWhenValueChange( { sell: { belong: tradeCalcData.coinSell, balance: walletMap ? walletMap[tradeCalcData.coinSell]?.count : 0, }, buy: { belong: tradeCalcData.coinBuy }, }, `${tradeCalcData.coinSell}-${tradeCalcData.coinBuy}`, 'sell', ) updateTradeBtrade({ market, tradeCalcData: _tradeCalcData }) } }, [market, tradeBtrade, tradeData, tradeCalcData, setTradeCalcData]) return { isMarketInit, toastOpen, closeToast, tradeCalcData, tradeData, onSwapClick, swapBtnI18nKey, swapBtnStatus, handleSwapPanelEvent, should15sRefresh, refreshRef, btradeSwapSubmit, tradeBtrade, isSwapLoading: isBtradeLoading, market, isMobile, setToastOpen, } } ================================================ FILE: packages/core/src/hooks/useractions/useCheckAccStatus.tsx ================================================ import { AccountStatus, coinbaseSmartWalletChains, FeeInfo, MapChainId, SagaStatus, SPECIAL_ACTIVATION_NETWORKS, UIERROR_CODE, WalletMap, } from '@loopring-web/common-resources' import { isCoinbaseSmartWallet, LoopringAPI, makeWalletLayer2, store, useAccount, useSystem, useWalletLayer2 } from '../../index' import { AccountStep, CheckActiveStatusProps, TOASTOPEN, ToastType, useOpenModals, TransErrorHelp, useSettings, } from '@loopring-web/component-lib' import React from 'react' import { connectProvides } from '@loopring-web/web3-provider' import * as sdk from '@loopring-web/loopring-sdk' export function isAccActivated() { return store.getState().account.readyState === AccountStatus.ACTIVATED } export const useCheckActiveStatus = ({ // isFeeNotEnough, checkFeeIsEnough, chargeFeeTokenList, onDisconnect, isDepositing, setToastOpen, }: // isShow, { // isShow: boolean; isDepositing: boolean onDisconnect: () => void isFeeNotEnough: { isFeeNotEnough: boolean isOnLoading: boolean } setToastOpen: (state: TOASTOPEN) => void chargeFeeTokenList: C[] checkFeeIsEnough: (props?: { isRequiredAPI: true; intervalTime?: number }) => void }): { checkActiveStatusProps: CheckActiveStatusProps } => { const { account, updateAccount } = useAccount() const { status: walletLayer2Status, updateWalletLayer2 } = useWalletLayer2() // const { chainInfos } = onchainHashInfo.useOnChainInfo(); // const nodeTimer = React.useRef(-1); const { setShowAccount, setShowActiveAccount, setShowDeposit, modals: { isShowAccount }, } = useOpenModals() const [know, setKnow] = React.useState(false) const [knowDisable, setKnowDisable] = React.useState(true) const [isAddressContract, setIsAddressContract] = React.useState(undefined) const [walletMap, setWalletMap] = React.useState( makeWalletLayer2({ needFilterZero: true, isActive: true }).walletMap ?? ({} as WalletMap), ) const [isFeeNotEnough, setIsFeeNotEnough] = React.useState({ isFeeNotEnough: true, isOnLoading: false, }) const goUpdateAccount = () => { setShowAccount({ isShow: false }) setShowActiveAccount({ isShow: true }) } const onIKnowClick = async () => { const { account } = store.getState() const [walletType, coinbaseSW] = await Promise.all([ LoopringAPI?.walletAPI?.getWalletType({ wallet: account.accAddress, network: MapChainId[defaultNetwork] as sdk.NetworkWallet }), isCoinbaseSmartWallet(account.accAddress, defaultNetwork as sdk.ChainId) ]) const isActivationSupported = !walletType?.walletType?.isContract || (coinbaseSW && coinbaseSmartWalletChains.includes(defaultNetwork)) if (!isActivationSupported || isFeeNotEnough.isFeeNotEnough) { setKnow(true) } else { goUpdateAccount() } } const walletLayer2Callback = React.useCallback(() => { const walletMap = makeWalletLayer2({ needFilterZero: true, isActive: true }).walletMap ?? {} setWalletMap(walletMap) setIsFeeNotEnough((state) => ({ ...state, isFeeNotEnough: walletMap ? chargeFeeTokenList.findIndex((item) => { if (walletMap[item.belong] && walletMap[item.belong]?.count) { return sdk .toBig(walletMap[item.belong]?.count ?? 0) .gte(sdk.toBig(item.fee.toString().replaceAll(sdk.SEP, ''))) } else { return ( item?.feeRaw !== undefined && Number(item.fee.toString()) == 0 && sdk.toBig((item.feeRaw ?? '').toString().replaceAll(sdk.SEP, '')).eq(0) ) } return }) === -1 : false, })) setKnowDisable(false) }, [chargeFeeTokenList]) const checkFeeIsEnoughInterval = React.useRef(-1) React.useEffect(() => { if (walletLayer2Callback && walletLayer2Status === SagaStatus.UNSET) { walletLayer2Callback() checkFeeIsEnough() } }, [walletLayer2Status]) const init = React.useCallback(async () => { setKnowDisable(true) try { const isContract = await sdk.isContract(connectProvides.usedWeb3 as any, account.accAddress) setIsAddressContract(isContract) updateWalletLayer2() } catch (error: any) { console.log('Web3 error', error) setToastOpen({ open: true, type: ToastType.error, content: , }) } }, [account.accAddress, updateWalletLayer2]) React.useEffect(() => { if (isShowAccount.isShow && isShowAccount.step === AccountStep.CheckingActive) { init() setKnow(false) checkFeeIsEnoughInterval.current = setInterval(() => { updateWalletLayer2() walletLayer2Callback() checkFeeIsEnough() updateAccount({}) }, 10 * 1000) } return () => { if (checkFeeIsEnoughInterval.current !== -1) { clearInterval(checkFeeIsEnoughInterval.current) } } }, [isShowAccount.step, isShowAccount.isShow]) const { app } = useSystem() const { defaultNetwork } = useSettings() const isSpecialActivation = app === 'earn' && SPECIAL_ACTIVATION_NETWORKS.includes(defaultNetwork) const checkActiveStatusProps: CheckActiveStatusProps = { know, knowDisable, onIKnowClick, isDepositing, goDisconnect: onDisconnect, account: { ...account, isContract: isAddressContract }, chargeFeeTokenList, goSend: () => { if (isSpecialActivation) { setShowAccount({ isShow: false }) setShowDeposit({ isShow: true }) } else { setShowAccount({ isShow: true, step: AccountStep.AddAssetGateway }) } }, walletMap, isFeeNotEnough, depositNeeded: true // account._accountIdNotActive !== -1 } return { checkActiveStatusProps } } ================================================ FILE: packages/core/src/hooks/useractions/useClaimConfirm.ts ================================================ import { AccountStatus, CLAIM_TYPE, getValuePrecisionThousand, IBData, LIVE_FEE_TIMES, myLog, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, UIERROR_CODE, } from '@loopring-web/common-resources' import { store, useAccount, useModalData, useSystem, useTokenMap } from '../../stores' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { AccountStep, ClaimProps, useOpenModals } from '@loopring-web/component-lib' import { useBtnStatus } from '../common' import React from 'react' import { volumeToCount } from '../help' import { useChargeFees, useWalletLayer2Socket, walletLayer2Service, claimServices, } from '../../services' import * as sdk from '@loopring-web/loopring-sdk' import { ConnectProvidersSignMap, connectProvides } from '@loopring-web/web3-provider' import { LoopringAPI } from '../../api_wrapper' import { isAccActivated } from './useCheckAccStatus' import { getTimestampDaysLater } from '../../utils' import { DAYS } from '../../defs' export const useClaimConfirm = & { tradeValueView: string }, I>() => { const { exchangeInfo, chainId } = useSystem() const { account } = useAccount() const { allowTrade: { raw_data }, } = useSystem() const legalEnable = (raw_data as any)?.legal?.enable const { tokenMap, idIndex } = useTokenMap() const { checkHWAddr, updateHW } = useWalletInfo() const { setShowAccount, setShowClaimWithdraw, modals: { isShowClaimWithdraw: { claimToken, isShow, claimType }, isShowAccount: { info }, }, } = useOpenModals() const { claimValue, updateClaimData } = useModalData() const { btnStatus, enableBtn, disableBtn, btnInfo } = useBtnStatus() const feeProps = claimValue.tradeType === TRADE_TYPE.TOKEN ? claimType === CLAIM_TYPE.lrcStaking || claimType === CLAIM_TYPE.allToken ? { requestType: sdk.OffchainFeeReqType.EXTRA_TYPES, extraType: 3, } : { requestType: sdk.OffchainFeeReqType.EXTRA_TYPES, extraType: 2, } : { requestType: sdk.OffchainNFTFeeReqType.EXTRA_TYPES, tokenAddress: claimValue?.tokenAddress, extraType: 2, isNFT: true, } const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, resetIntervalTime, } = useChargeFees({ ...feeProps, intervalTime: undefined, updateData: ({ fee }) => { const claimValue = store.getState()._router_modalData.claimValue if ( (claimValue.tradeType === TRADE_TYPE.TOKEN && (claimValue.claimType === CLAIM_TYPE.lrcStaking || claimValue.claimType === CLAIM_TYPE.allToken) && feeProps.extraType === 3) || (claimValue.tradeType === TRADE_TYPE.TOKEN && claimType === CLAIM_TYPE.redPacket && feeProps.extraType === 2 && !feeProps.isNFT) || (claimValue.tradeType === TRADE_TYPE.NFT && claimType === CLAIM_TYPE.redPacket && feeProps.extraType === 2 && feeProps.isNFT) ) { updateClaimData({ ...claimValue, fee: fee }) } }, }) useWalletLayer2Socket({ walletLayer2Callback: undefined }) // calim const resetDefault = React.useCallback(() => { if (info?.isRetry) { checkFeeIsEnough() return } // claimToken if (claimToken) { if (claimToken?.isNft) { updateClaimData({ ...claimToken.nftTokenInfo, tradeType: TRADE_TYPE.NFT, // nftData: claimToken.nftData, tokenAddress: claimToken?.nftTokenInfo?.tokenAddress, tokenId: claimToken.tokenId, belong: claimToken.nftTokenInfo?.metadata?.base?.name ?? 'NFT', tradeValue: Number(claimToken.total), volume: claimToken.total, balance: Number(claimToken.total), claimType, fee: feeInfo, luckyTokenHash: claimToken.luckyTokenHash, } as any) } else { const token = tokenMap[idIndex[claimToken.tokenId]] updateClaimData({ belong: idIndex[claimToken.tokenId], tradeType: TRADE_TYPE.TOKEN, tradeValue: volumeToCount(token.symbol, claimToken.total), volume: claimToken.total, balance: volumeToCount(token.symbol, claimToken.total), claimType, fee: feeInfo, }) } } else { } }, [checkFeeIsEnough, updateClaimData, feeInfo, claimToken, info?.isRetry]) React.useEffect(() => { if (isShow) { resetDefault() walletLayer2Service.sendUserUpdate() } }, [isShow]) React.useEffect(() => { if (isShow) { checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [isShow, claimValue.claimType, claimValue.tradeType]) const processRequest = React.useCallback( async ( request: sdk.OriginLuckTokenWithdrawsRequestV3 | sdk.OriginStakeClaimRequestV3, isHardwareWallet: boolean = false, ) => { const { apiKey, connectName, eddsaKey } = account const claimValue = store.getState()._router_modalData.claimValue // const claimValue = store.getState()._router_modalData.c; try { if (connectProvides.usedWeb3 && LoopringAPI.luckTokenAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && isHardwareWallet) { isHWAddr = true } myLog('ClaimConfirm processRequest:', isHWAddr, isHardwareWallet) let response if (claimValue.claimType === CLAIM_TYPE.redPacket) { response = await LoopringAPI.luckTokenAPI.sendLuckTokenWithdraws( { request: request as sdk.OriginLuckTokenWithdrawsRequestV3, web3: connectProvides.usedWeb3 as any, chainId: chainId === 'unknown' ? 1 : chainId, walletType: (ConnectProvidersSignMap[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) } else if (claimValue.claimType === CLAIM_TYPE.allToken) { response = await LoopringAPI.userAPI?.sendTotalClaim( { request: request as sdk.OriginClaimRequestV3, web3: connectProvides.usedWeb3 as any, chainId: chainId === 'unknown' ? 1 : chainId, walletType: (ConnectProvidersSignMap[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) } else if (claimValue.claimType === CLAIM_TYPE.lrcStaking) { response = await LoopringAPI.defiAPI?.sendStakeClaim( { request: request as sdk.OriginStakeClaimRequestV3, web3: connectProvides.usedWeb3 as any, chainId: chainId === 'unknown' ? 1 : chainId, walletType: (ConnectProvidersSignMap[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) } myLog('claim submitted :', claimValue.claimType, response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_In_Progress, }) setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_Submit, info: { symbol: claimValue.belong, }, }) claimServices.claimSuccess({ type: claimValue.claimType }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } walletLayer2Service.sendUserUpdate() setShowClaimWithdraw({ isShow: false }) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.ClaimWithdraw_Submit ) { setShowAccount({ isShow: false }) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isHardwareWallet) claimServices.claimFailed({ type: claimValue.claimType, error: { ...e, code } }) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_First_Method_Denied, info: { symbol: claimValue.belong, }, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_Denied, info: { symbol: claimValue.belong, }, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_Failed, info: { symbol: claimValue.belong, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [account], ) const onClaimClick = React.useCallback( async (_data: Partial, isHardwareRetry = false) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const claimValue = store.getState()._router_modalData.claimValue if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && claimValue?.fee?.belong && claimValue.fee?.feeRaw && claimValue?.tradeValue && (claimValue?.belong || claimValue.nftData) && !isFeeNotEnough.isFeeNotEnough && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_WaitForAuth, }) const feeToken = tokenMap[claimValue.fee.belong] const feeRaw = claimValue.fee.feeRaw ?? claimValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) let token: any let amount: any = 0 if (claimValue?.nftData) { token = { tokenId: claimValue.tokenId, } amount = claimValue.volume } else { token = tokenMap[claimValue?.belong] amount = claimValue.volume } const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: Number(feeToken.tokenId), }, apiKey, ) let brokerType: any = undefined switch (claimValue.claimType) { case CLAIM_TYPE.redPacket: brokerType = 2 break case CLAIM_TYPE.lrcStaking: case CLAIM_TYPE.allToken: brokerType = 0 break } const { broker } = await LoopringAPI.userAPI?.getAvailableBroker({ type: brokerType, }) let request: | (sdk.OriginLuckTokenWithdrawsRequestV3 & { luckyTokenHash?: string }) | sdk.OriginStakeClaimRequestV3 = {} as any if (claimValue.claimType === CLAIM_TYPE.redPacket) { request = { tokenId: token.tokenId, feeTokenId: feeToken.tokenId, amount: amount.toString(), nftData: claimToken?.isNft ? claimToken.nftTokenInfo?.nftData : undefined, claimer: accAddress, transfer: { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: broker, storageId: storageId.offchainId, maxFee: { tokenId: feeToken.tokenId, volume: '0', }, token: { tokenId: feeToken.tokenId, volume: fee.toFixed(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), }, luckyTokenHash: claimToken?.isNft ? claimToken?.luckyTokenHash : undefined, } } else if (claimValue.claimType === CLAIM_TYPE.allToken) { request = { accountId: account.accountId, token: { tokenId: token.tokenId, volume: amount.toString(), }, transfer: { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: broker, storageId: storageId.offchainId, maxFee: { tokenId: feeToken.tokenId, volume: '0', }, token: { tokenId: feeToken.tokenId, volume: fee.toFixed(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), }, } } else if (claimValue.claimType === CLAIM_TYPE.lrcStaking) { request = { accountId: account.accountId, token: { tokenId: token.tokenId, volume: amount.toString(), }, transfer: { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payeeId: 0, payerId: accountId, payeeAddr: broker, storageId: storageId.offchainId, maxFee: { tokenId: feeToken.tokenId, volume: '0', }, token: { tokenId: feeToken.tokenId, volume: fee.toFixed(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), }, } } myLog('claimWithdrawals request:', request) processRequest(request, isHardwareRetry) } catch (e: any) { // sdk.dumpError400(e); setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_Failed, error: { code: e?.code ?? UIERROR_CODE.UNKNOWN, message: e.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), } as sdk.RESULT_INFO, }) } return true } else { return false } }, [ account, tokenMap, exchangeInfo, isFeeNotEnough.isFeeNotEnough, setShowAccount, processRequest, ], ) const checkBtnStatus = React.useCallback(() => { const claimValue = store.getState()._router_modalData.claimValue if ( tokenMap && chargeFeeTokenList.length && isFeeNotEnough && !isFeeNotEnough.isFeeNotEnough && (claimValue.belong || claimValue.nftData) && claimValue.tradeValue && claimValue.fee && claimValue.fee.belong ) { enableBtn() return } disableBtn() }, [ chargeFeeTokenList.length, disableBtn, enableBtn, isFeeNotEnough.isFeeNotEnough, tokenMap, claimValue?.balance, claimValue?.belong, claimValue?.fee?.feeRaw, claimValue?.tradeValue, ]) React.useEffect(() => { checkBtnStatus() }, [ chargeFeeTokenList, claimValue?.tradeValue, isFeeNotEnough?.isFeeNotEnough, claimValue?.fee?.feeRaw, ]) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.ClaimWithdraw_WaitForAuth, }) onClaimClick({}, isHardwareRetry) }, [setShowAccount], ) return { retryBtn, claimProps: { btnStatus, btnInfo, disabled: !(legalEnable === true), tradeData: { tradeValue: claimValue?.tradeValue, belong: claimValue?.belong, balance: claimValue?.belong, tradeValueView: getValuePrecisionThousand( claimValue?.tradeValue, tokenMap[claimValue?.belong?.toString() ?? '']?.precision, tokenMap[claimValue?.belong?.toString() ?? '']?.precision, tokenMap[claimValue?.belong?.toString() ?? '']?.precision, false, ), }, chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, onClaimClick, claimType, isNFT: claimToken?.isNft ? true : false, nftIMGURL: claimToken?.nftTokenInfo?.metadata?.imageSize.original, } as any as ClaimProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useCoinbaseWalletPassword.ts ================================================ import { AccountStep, useOpenModals, useSettings } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import { useUpdateAccount } from './useUpdateAccount' import { decryptAESMd5, isSameEVMAddress, offchainFeeInfoToFeeInfo } from '../../utils' import { useCoinbaseSmartWalletPersist } from '../../stores/localStore/coinbaseSmartWalletPersist' import { LoopringAPI } from '../../api_wrapper' import { accountServices } from '../../services' import { useAccount, useTokenMap, useWalletLayer2 } from '../../stores' import { DUAL_RETRY_STATUS, LABEL_INVESTMENT_STATUS, OffchainFeeReqType, SETTLEMENT_STATUS, VaultAccountStatus } from '@loopring-web/loopring-sdk' import { useEffect, useState } from 'react' import { keys } from 'lodash' import { BigNumber} from 'ethers' export const useCoinbaseWalletPassword = () => { const { t } = useTranslation('common') const { modals: {isShowAccount, isShowActiveAccount}, setShowAccount, setShowResetAccount, setShowActiveAccount } = useOpenModals() const { goUpdateAccountCoinbaseWallet } = useUpdateAccount() const { data } = useCoinbaseSmartWalletPersist() const { defaultNetwork } = useSettings() const { account } = useAccount() const {tokenMap} = useTokenMap() const {walletLayer2} = useWalletLayer2() const foundPersistData = data?.find( (item) => item.chainId === defaultNetwork && isSameEVMAddress(item.wallet, account?.accAddress), ) const [showPasswordMismatchError, setShowPasswordMismatchError] = useState(false) useEffect(() => { setShowPasswordMismatchError(false) }, [isShowAccount.step, isShowAccount.isShow]) return { introProps: { t, onClickConfirm: () => { setShowAccount({ step: AccountStep.Coinbase_Smart_Wallet_Password_Set, isShow: true, info: isShowAccount?.info }) }, }, setProps: { t, onClickConfirm: (password: string) => { setShowAccount({ step: AccountStep.Coinbase_Smart_Wallet_Password_Set_Confirm, isShow: true, info: { ...isShowAccount?.info, password } }) }, onClickBack: () => { setShowAccount({ step: AccountStep.Coinbase_Smart_Wallet_Password_Intro, isShow: true, }) }, }, setConfirmProps: { t, onClickProceed: () => { goUpdateAccountCoinbaseWallet({ password: isShowAccount?.info?.password, feeInfo: isShowAccount?.info?.feeInfo, isReset: isShowAccount?.info?.isReset, }) }, }, setProcessingProps: { step: isShowAccount?.info?.step, showResumeUpdateAccount: isShowAccount?.info?.showResumeUpdateAccount }, setErrorProps: { t, onClickConfirm: () => { setShowAccount({ isShow: false }) }, }, getErrorProps: { t, onClickConfirm: () => { setShowAccount({ isShow: false }) }, }, inputProps: { t, inputDisabled: !( foundPersistData && foundPersistData.eddsaKey.sk && foundPersistData.nonce === account?.nonce ), onClickConfirm: async (password: string) => { if (!foundPersistData || !foundPersistData?.eddsaKey.sk) return try { const sk = decryptAESMd5(password, foundPersistData.eddsaKey.sk) const accountInfoRealTime = await LoopringAPI.exchangeAPI?.getAccount({ owner: foundPersistData.wallet, }) setShowPasswordMismatchError(false) const res = await LoopringAPI.userAPI! .getUserApiKey( { accountId: accountInfoRealTime!.accInfo.accountId, }, sk, ) accountServices.sendAccountSigned({ apiKey: res.apiKey, eddsaKey: { ...foundPersistData!.eddsaKey, sk }, isInCounterFactualStatus: false, accountId: accountInfoRealTime!.accInfo.accountId, }) setShowAccount({ isShow: false, }) } catch (e) { setShowPasswordMismatchError(true) accountServices.sendCheckAccount(foundPersistData!.wallet) } }, onClickForgetPassword: async () => { const [hasDualInvest, hasVault] = await Promise.all([ LoopringAPI.defiAPI?.getDualTransactions( { accountId: account.accountId, settlementStatuses: SETTLEMENT_STATUS.UNSETTLED, investmentStatuses: [ LABEL_INVESTMENT_STATUS.CANCELLED, LABEL_INVESTMENT_STATUS.SUCCESS, LABEL_INVESTMENT_STATUS.PROCESSED, LABEL_INVESTMENT_STATUS.PROCESSING, ].join(','), retryStatuses: [DUAL_RETRY_STATUS.RETRYING], } as any, "", ).then(res => res.totalNum && res.totalNum > 0), LoopringAPI.vaultAPI?.getVaultInfoAndBalance( { accountId: account.accountId, }, "", ).then(res => { return res.accountStatus === VaultAccountStatus.IN_STAKING }) ]) const hasPortalOrDual = hasDualInvest || hasVault if (hasPortalOrDual) { setShowAccount({ step: AccountStep.Coinbase_Smart_Wallet_Password_Forget_Password_Confirm, isShow: true, }) } else { setShowAccount({ step: AccountStep.Coinbase_Smart_Wallet_Password_Forget_Password, isShow: true, }) } }, onInputPassword: () => { setShowPasswordMismatchError(false) }, showPasswordMismatchError }, forgetPasswordConfirmProps: { t, onClickConfirm: () => { setShowAccount({ isShow: false, }) }, onClickBack: () => { setShowAccount({ step: AccountStep.Coinbase_Smart_Wallet_Password_Input, isShow: true, }) }, }, forgetPasswordProps: { t, onClickConfirm: async () => { setShowAccount({ isShow: false, }) const feeInfoRes = await LoopringAPI.userAPI?.getOffchainFeeAmt({ accountId: account.accountId, requestType: OffchainFeeReqType.UPDATE_ACCOUNT, }, '') const foundFeeKey = keys(feeInfoRes?.fees).find((key) => { const bal = BigNumber.from(walletLayer2?.[key].total || 0).sub(walletLayer2?.[key].locked || 0) return feeInfoRes?.fees?.[key]?.fee && bal.gte(feeInfoRes?.fees?.[key]?.fee) }) if (foundFeeKey && walletLayer2) { const foundFeeInfo = feeInfoRes?.fees[foundFeeKey] const mapped = offchainFeeInfoToFeeInfo(foundFeeInfo!, tokenMap, walletLayer2) setShowAccount({ step: AccountStep.Coinbase_Smart_Wallet_Password_Set, isShow: true, info: { feeInfo: mapped, } }) } }, }, } } ================================================ FILE: packages/core/src/hooks/useractions/useCollectionAdvanceMeta.ts ================================================ import { CollectionMeta, CollectionMetaJSON, myLog, SDK_ERROR_MAP_TO_UI, UIERROR_CODE, } from '@loopring-web/common-resources' import { CollectionAdvanceProps, ToastType, useOpenModals } from '@loopring-web/component-lib' import React from 'react' import { useAccount, useModalData, useSystem, useWalletL2Collection } from '../../stores' import { useBtnStatus } from '../index' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI } from '../../api_wrapper' import { useTranslation } from 'react-i18next' export function useCollectionAdvanceMeta({ setCollectionToastOpen, }: { setCollectionToastOpen: any }) { const { allowTrade, chainId } = useSystem() const { setShowCollectionAdvance } = useOpenModals() const { updateWalletL2Collection } = useWalletL2Collection() const { account } = useAccount() const [metaData, setMetaData] = React.useState('') const { t } = useTranslation('common') const { btnStatus, setLabelAndParams, resetBtnInfo, enableBtn, disableBtn, btnInfo, setLoadingBtn, } = useBtnStatus() const { collectionAdvanceValue, updateCollectionAdvanceData } = useModalData() const [error, setError] = React.useState( undefined, ) const onSubmitClick = React.useCallback( async (_data: T) => { if ( !error && collectionAdvanceValue.name?.trim() && collectionAdvanceValue.tileUri?.trim() && LoopringAPI.userAPI ) { setLoadingBtn() try { const response = await LoopringAPI.userAPI.submitNFTCollection( { ...collectionAdvanceValue, name: collectionAdvanceValue.name?.trim(), tileUri: collectionAdvanceValue.tileUri?.trim(), owner: account.accAddress, nftFactory: sdk.NFTFactory_Collection[chainId], } as sdk.CollectionBasicMeta, chainId as any, account.apiKey, account.eddsaKey.sk, ) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { const _response: sdk.RESULT_INFO = response as sdk.RESULT_INFO throw new Error( t( _response.code && SDK_ERROR_MAP_TO_UI[_response.code] ? SDK_ERROR_MAP_TO_UI[_response.code].messageKey : SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN].messageKey, { ns: 'error', name: collectionAdvanceValue.name?.trim() }, ), ) } else { setCollectionToastOpen({ open: true, type: 'success', content: t('labelCreateCollectionSuccess'), }) updateWalletL2Collection({ page: 1 }) } } catch (error) { myLog('error', error) setCollectionToastOpen({ open: true, type: ToastType.error, content: t('labelCreateCollectionFailed') + `: ${(error as any)?.message ? (error as any).message : t('errorUnknown')}`, }) } setShowCollectionAdvance({ isShow: false }) } // resetBtnInfo(); // disableBtn(); setError(undefined) setMetaData('') updateCollectionAdvanceData({}) }, [collectionAdvanceValue, disableBtn, resetBtnInfo], ) const updateBtnStatus = React.useCallback(() => { resetBtnInfo() if (!error && collectionAdvanceValue?.name) { enableBtn() return } if (collectionAdvanceValue === undefined || Object.keys(collectionAdvanceValue).length === 0) { setLabelAndParams('labelEnterMeta', {}) } else if (error) { setLabelAndParams(SDK_ERROR_MAP_TO_UI[error?.code ?? 700001]?.messageKey, {}) } else if (!collectionAdvanceValue?.name) { setLabelAndParams(SDK_ERROR_MAP_TO_UI[700014].messageKey, {}) } disableBtn() myLog('try to disable collectionAdvance btn!') }, [error, resetBtnInfo, enableBtn, setLabelAndParams, collectionAdvanceValue, disableBtn]) React.useEffect(() => { updateBtnStatus() }, [collectionAdvanceValue, error]) const collectionAdvanceProps: CollectionAdvanceProps = { handleDataChange: (_data) => { setMetaData(_data) try { let _metaDataJSON: CollectionMetaJSON = JSON.parse(_data.replaceAll(/\n|\s/gi, '')) const _metaData: sdk.CollectionBasicMeta = { owner: account.accAddress, tileUri: _metaDataJSON?.tile_uri.trim() ?? undefined, name: _metaDataJSON?.name?.trim() ?? undefined, description: _metaDataJSON?.description?.trim() ?? undefined, avatar: _metaDataJSON?.avatar_uri?.trim() ?? undefined, banner: _metaDataJSON?.banner_uri?.trim() ?? undefined, } if (!_metaData.tileUri) { setError({ code: UIERROR_CODE.ERROR_COLLECTION_METADATA_NO_TILEURI, message: 'empty tileUri', }) updateCollectionAdvanceData({}) return } if (!_metaData.name) { setError({ code: UIERROR_CODE.ERROR_COLLECTION_NO_NAME, message: 'empty name', }) updateCollectionAdvanceData({}) return } setError(undefined) updateCollectionAdvanceData(_metaData) } catch (_error) { setError({ code: UIERROR_CODE.ERROR_JSON_STRINGIFY, message: (_error as any)?.message, }) return } }, onSubmitClick, allowTrade, disabled: false, btnStatus, btnInfo, metaData, } as CollectionAdvanceProps return { collectionAdvanceProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useContact.ts ================================================ import React from 'react' import { Contact, NetworkMap, SDK_ERROR_MAP_TO_UI, ToastType } from '@loopring-web/common-resources' import { store, useAccount, useContacts } from '../../stores' import { AccountStep, RawDataTransactionItem, TransactionStatus, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import { connectProvides } from '@loopring-web/web3-provider' import { useToast } from '../common' import { LoopringAPI } from '../../api_wrapper' import * as sdk from '@loopring-web/loopring-sdk' import { useLocation } from 'react-router-dom' import { volumeToCount, volumeToCountAsBigNumber } from '../help' const RowHeight = 78 export const useContact = ({ viewHeightRatio = 0.85, viewHeightOffset = 130 }) => { // const [addOpen, setAddOpen] = React.useState(false) const [deleteInfo, setDeleteInfo] = React.useState({ open: false, selected: undefined as Contact | undefined, }) const [sendInfo, setSendInfo] = React.useState({ open: false, selected: undefined as Contact | undefined, }) const [searchValue, setSearchValue] = React.useState('') const { defaultNetwork } = useSettings() const { setShowAccount, setShowEditContact } = useOpenModals() const [btnLoading, setBtnLoading] = React.useState<{ index: number; loading: boolean }>({ loading: false, index: 0, }) // const cachedForAccountId = useSelector((state: RootState) => state.contacts.currentAccountId) const { t } = useTranslation() const [tableHeight] = React.useState(window.innerHeight * viewHeightRatio - viewHeightOffset) const [page, setPage] = React.useState(1) const pageSize = Math.floor(tableHeight / RowHeight) const { contacts, errorMessage: contactsErrorMessage, updateContacts } = useContacts() const total = contacts?.length const pagination = total ? { page, pageSize, total, } : undefined // When localInit when unlock account Failed update contact once? React.useEffect(() => { if (contactsErrorMessage) { updateContacts() } }, []) // const [selectAddress, setSelectAddress] = React.useState(undefined) const onChangeSearch = React.useCallback((input: string) => { setSearchValue(input) }, []) const onClearSearch = React.useCallback(() => { setSearchValue('') }, []) const onClickDelete = React.useCallback((address: string, name: string) => { setDeleteInfo({ open: true, selected: { address, name, }, }) }, []) const onCloseDelete = React.useCallback(() => { setDeleteInfo({ open: false, selected: undefined, }) }, []) const onClickSend = React.useCallback(async (data: Contact, index: number) => { setBtnLoading({ loading: true, index }) let isENSWrong = false if (data.contactAddress && data.ens && connectProvides.usedWeb3) { try { const ensAddr = await connectProvides?.usedWeb3.eth?.ens?.getAddress(data.ens) if (ensAddr?.toLowerCase() !== data?.contactAddress?.toLowerCase()) { isENSWrong = true } } catch (e) { console.log('error: ens->address ignore', e) } } // geUpdateContact, setShowAccount({ isShow: true, step: AccountStep.SendAssetFromContact, info: { ...data, isENSWrong, }, }) setBtnLoading({ loading: false, index }) }, []) const onCloseSend = React.useCallback(() => { setSendInfo({ open: false, selected: undefined, }) }, []) const { toastOpen, setToastOpen, closeToast } = useToast() React.useEffect(() => { if (contactsErrorMessage) { updateContacts() } }, []) const [deleteLoading, setDeleteLoading] = React.useState(false) const onPageChange = React.useCallback((page: number) => { setSearchValue('') setPage(page) }, []) const showPagination = total !== undefined && searchValue === '' const submitDeleteContact = React.useCallback( async (address: string, _name: string) => { setDeleteLoading(true) const { accountId, apiKey, isContractAddress, isCFAddress } = store.getState().account const response = await LoopringAPI.contactAPI?.deleteContact( { accountId, isHebao: isContractAddress || isCFAddress ? true : false, contactAddress: address, // @ts-ignore network: NetworkMap[defaultNetwork].walletType, }, apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { setToastOpen({ open: true, type: ToastType.error, content: t('labelContactsDeleteFailed'), }) return } // setPage to page - 1 if there is only one contact on this page when page >= 1 if ( page > 1 && page === Math.ceil(contacts.length / pageSize) && contacts.length % pageSize === 1 ) { setPage(page - 1) } updateContacts() setToastOpen({ open: true, type: ToastType.success, content: t('labelContactsDeleteSuccess'), }) setDeleteLoading(false) onCloseDelete() // if(result as sdk.RESULT_INFO) }, [contacts, page, pageSize], ) return { contacts: contacts && (searchValue === '' ? contacts.slice( (page - 1) * pageSize, page * pageSize >= contacts.length ? contacts.length : page * pageSize, ) : contacts.filter((contact) => { return ( contact.contactAddress.toLowerCase().includes(searchValue.toLowerCase()) || contact.contactName.toLowerCase().includes(searchValue.toLowerCase()) ) })), // selectAddress, // setSelectAddress, onChangeSearch, onClearSearch, searchValue, toastInfo: toastOpen, setToast: setToastOpen, closeToast, onClickDelete, deleteInfo, onCloseDelete, submitDeleteContact, deleteLoading, setShowEditContact, onClickSend, onCloseSend, sendInfo, pagination, onPageChange, showPagination, btnLoading, } } type TxsFilterProps = { // accountId: number; tokenSymbol?: string start?: number end?: number offset?: number limit?: number types?: sdk.UserTxTypes[] | string } export function useContractRecord(setToastOpen: (state: any) => void) { const { account: { accountId, apiKey }, } = useAccount() const { tokenMap } = store.getState().tokenMap const { t } = useTranslation(['error']) const [txs, setTxs] = React.useState([]) const [txsTotal, setTxsTotal] = React.useState(0) const [showLoading, setShowLoading] = React.useState(false) const { search } = useLocation() const searchParams = new URLSearchParams(search) const getTxnStatus = (status: string) => { return status === '' ? TransactionStatus.processing : status === 'PROCESSED' ? TransactionStatus.processed : status === 'PROCESSING' ? TransactionStatus.processing : status === 'RECEIVED' ? TransactionStatus.received : TransactionStatus.failed } const getUserTxnList = React.useCallback( async ({ tokenSymbol, start, end, limit, offset }: TxsFilterProps) => { // const address = routeMatch.params[0]; const tokenId = tokenSymbol ? tokenMap[tokenSymbol!].tokenId : undefined const response = await LoopringAPI.userAPI!.getUserBills( { accountId, tokenSymbol, tokenId, fromAddress: searchParams?.get('contactAddress') ?? '', transferAddress: searchParams?.get('contactAddress') ?? '', start, end, limit, offset, // @ts-ignore billType: '1,2,4', }, apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] setToastOpen({ open: true, type: ToastType.error, content: 'error : ' + errorItem ? t(errorItem.messageKey) : (response as sdk.RESULT_INFO).message, }) } else { const formattedList: RawDataTransactionItem[] = (response as any).raw_data.bills.map( (o: any) => { const feePrecision = tokenMap ? tokenMap[o.tokenF].precision : undefined return { ...o, txType: o.billType, side: o.billType as any, amount: { unit: o.token || '', value: Number(volumeToCount(o.token, o.amount)), }, fee: { unit: o.tokenF || '', value: Number(volumeToCountAsBigNumber(o.tokenF, o.amountF || 0)), }, memo: o.memo || '', time: o.timestamp, txnHash: o.hash, status: getTxnStatus(o.status), feePrecision: feePrecision, receiverAddress: o.to, senderAddress: o.from, } as RawDataTransactionItem }, ) setTxs(formattedList) setTxsTotal(response.totalNum) setShowLoading(false) } }, [accountId, apiKey, t, tokenMap], ) return { txs, txsTotal, showLoading, getUserTxnList, } } ================================================ FILE: packages/core/src/hooks/useractions/useContactAdd.tsx ================================================ import React from 'react' import { AddressError, EXCHANGE_TYPE, HEBAO_CONTRACT_MAP, myLog, NetworkMap, SDK_ERROR_MAP_TO_UI, TradeBtnStatus, UIERROR_CODE, WALLET_TYPE, Contact, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import { addressToExWalletMapFn, exWalletToAddressMapFn, LoopringAPI, store, useAddressCheck, useContacts, useSubmitBtn, } from '@loopring-web/core' import { ToastType, useSettings, useOpenModals } from '@loopring-web/component-lib' import * as sdk from '@loopring-web/loopring-sdk' export const useContactAdd = () => { const { t } = useTranslation() const [loading, setLoading] = React.useState(false) const [isNameExit, setIsNameExit] = React.useState(false) const [isContactExit, setIsContactExit] = React.useState(false) const [isEdit, setIsEdit] = React.useState<{ item: Contact & { isENSWrong: boolean } } | false>( false, ) const { modals: { isShowEditContact }, setShowOtherExchange, setShowGlobalToast, setShowEditContact, } = useOpenModals() const { address, realAddr, setAddress, addrStatus, isCFAddress: addressTypeISCFAddress, isContractAddress, isAddressCheckLoading, loopringSmartWalletVersion, ens, // isENSWrong, } = useAddressCheck(false) const { contacts, updateContacts } = useContacts() const [addName, setAddName] = React.useState('') const [selectedAddressType, setSelectedAddressType] = React.useState< WALLET_TYPE | EXCHANGE_TYPE | undefined >(undefined) const allowToClickIsSure = React.useMemo(() => { return isAddressCheckLoading || addrStatus === AddressError.InvalidAddr || !realAddr }, [addrStatus, isAddressCheckLoading, realAddr]) const mapContactAddressType = React.useCallback((): | (typeof sdk.AddressType)[sdk.AddressTypeKeys] | undefined => { if (addressTypeISCFAddress) { return sdk.AddressType.LOOPRING_HEBAO_CF } else if (loopringSmartWalletVersion?.isLoopringSmartWallet) { const item = HEBAO_CONTRACT_MAP.find( (item) => item[0] === loopringSmartWalletVersion?.version, ) return ( item ? item[1] : /V2_/.test(loopringSmartWalletVersion?.version ?? '') ? 2002 : undefined ) as any } else if (isContractAddress) { return sdk.AddressType.CONTRACT } else if (selectedAddressType) { return exWalletToAddressMapFn(selectedAddressType) } }, [ addressTypeISCFAddress, isContractAddress, loopringSmartWalletVersion?.isLoopringSmartWallet, loopringSmartWalletVersion?.version, selectedAddressType, ]) const autoSetWalletType = () => { if (realAddr && selectedAddressType == undefined) { const addressType = mapContactAddressType() if (addressType) { const type = addressToExWalletMapFn(addressType) myLog('onChangeAddressType before', type) onChangeAddressType(type) } else if (isEdit && isEdit.item.addressType) { const type = addressToExWalletMapFn(isEdit.item.addressType) myLog('onChangeAddressType before isEdit', type) onChangeAddressType(type) } else { onChangeAddressType(undefined) } } } const onSubmit = React.useCallback(async () => { setLoading(true) const { accountId, apiKey, isContractAddress, isCFAddress } = store.getState().account let createContact: sdk.CreateContactRequest | sdk.UpdateContactRequest = { contactAddress: realAddr, accountId, contactName: addName, isHebao: !!(isContractAddress || isCFAddress), network: NetworkMap[defaultNetwork].walletType, ens: isEdit?.item?.isENSWrong ? '' : isEdit ? isEdit?.item.ens : ens, } as any createContact = { ...createContact, addressType: mapContactAddressType(), } if (isEdit) { try { const response = await LoopringAPI.contactAPI?.updateContact(createContact, apiKey) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { throw { code: (response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message, } } setShowGlobalToast({ isShow: true, info: { type: ToastType.success, content: t('labelContactsEditSuccess'), }, }) restData() setShowEditContact({ isShow: false, }) } catch (error) { const _error = LoopringAPI?.globalAPI?.genErr(error as unknown as any) ?? {} error = { ...((error as any) ?? {}), ..._error, } if ((error as any)?.code == sdk.LoopringErrorCode.HTTP_ERROR) { setShowGlobalToast({ isShow: true, info: { type: ToastType.error, content: t(SDK_ERROR_MAP_TO_UI[UIERROR_CODE.TIME_OUT].messageKey, { ns: 'error' }), }, }) } else if ((error as any)?.code) { setShowGlobalToast({ isShow: true, info: { type: ToastType.error, content: t( SDK_ERROR_MAP_TO_UI[(error as any)?.code]?.messageKey ?? 'labelContactsEditFailed', { ns: 'error' }, ), }, }) } else { setShowGlobalToast({ isShow: true, info: { type: ToastType.error, content: t('labelContactsEditFailed'), }, }) } } } else { try { const response = await LoopringAPI.contactAPI?.createContact(createContact as any, apiKey) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { throw { code: (response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message, } } setShowGlobalToast({ isShow: true, info: { type: ToastType.success, content: t('labelContactsAddSuccess'), }, }) restData() setShowEditContact({ isShow: false, }) } catch (error) { const _error = LoopringAPI?.globalAPI?.genErr(error as unknown as any) ?? {} error = { ...((error as any) ?? {}), ..._error, } if ((error as any)?.code == sdk.LoopringErrorCode.HTTP_ERROR) { setShowGlobalToast({ isShow: true, info: { type: ToastType.error, content: t(SDK_ERROR_MAP_TO_UI[UIERROR_CODE.TIME_OUT].messageKey, { ns: 'error' }), }, }) } else if ((error as any)?.code) { setShowGlobalToast({ isShow: true, info: { type: ToastType.error, content: t( SDK_ERROR_MAP_TO_UI[(error as any)?.code]?.messageKey ?? 'labelContactsAddFailed', { ns: 'error' }, ), }, }) } else { setShowGlobalToast({ isShow: true, info: { type: ToastType.error, content: t('labelContactsAddFailed'), }, }) } } setLoading(false) } }, [realAddr, addName, mapContactAddressType, isEdit, t]) React.useEffect(() => { if (realAddr && addName && realAddr !== '' && !isAddressCheckLoading) { autoSetWalletType() } const list = contacts?.filter((item) => { return isEdit ? isEdit?.item?.contactAddress?.toLowerCase() !== item?.contactAddress?.toLowerCase() : true }) if (addName && list?.find((item) => item.contactName === addName)) { setIsNameExit(true) } else { setIsNameExit(false) } if (list?.find((item) => item.contactAddress.toLowerCase() === realAddr.toLowerCase())) { setIsContactExit(true) } else { setIsContactExit(false) } }, [realAddr, isAddressCheckLoading, addName]) const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string | undefined } => { if (realAddr && addName) { if (isContactExit) { return { label: `labelContactAddressExisted`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else if (isNameExit) { return { label: `labelContactNameExisted`, tradeBtnStatus: TradeBtnStatus.DISABLED, } } else { return { label: undefined, tradeBtnStatus: TradeBtnStatus.AVAILABLE, } } } return { label: undefined, tradeBtnStatus: TradeBtnStatus.DISABLED, } }, [realAddr, addName, isNameExit, isContactExit]) const { btnStatus, onBtnClick, btnLabel } = useSubmitBtn({ availableTradeCheck, isLoading: isAddressCheckLoading || loading, submitCallback: onSubmit, }) React.useEffect(() => { if (isShowEditContact.isShow) { if (isShowEditContact?.info?.contactName) { setIsEdit(() => { return { item: isShowEditContact.info } }) onChangeName(isShowEditContact.info.contactName) onChangeAddressType( addressToExWalletMapFn(isShowEditContact.info?.addressType ?? undefined), ) onChangeAddress(isShowEditContact.info?.contactAddress) // if (isShowEditContact?.info?.contactAddress ) { // onChangeAddress((isEdit as EditItem)?.item.contactAddress) // } } else { setIsEdit(false) } } else { restData() } setLoading(false) }, [isShowEditContact.isShow]) const detectedWalletType = loopringSmartWalletVersion?.isLoopringSmartWallet ? WALLET_TYPE.Loopring : isContractAddress ? WALLET_TYPE.OtherSmart : WALLET_TYPE.EOA const onChangeAddress = (input: string) => { setAddress(input) setSelectedAddressType(undefined) } const onChangeName = (input: string) => { if (new TextEncoder().encode(input).length <= 48) { setAddName(input) } } const onChangeAddressType = (value: WALLET_TYPE | EXCHANGE_TYPE | undefined) => { myLog('onChangeAddressType', value) setSelectedAddressType(value) } const restData = () => { onChangeAddress('') onChangeName('') onChangeAddressType(undefined) setSelectedAddressType(undefined) setLoading(false) updateContacts() setShowOtherExchange({ agree: false, isShow: false }) } const { defaultNetwork } = useSettings() return { contactAddProps: { isEdit, contacts, restData, selectedAddressType, detectedWalletType, addressDefault: address, isAddressCheckLoading, onChangeAddress, addName, onChangeName, realAddr, addrStatus, handleOnAddressChange: onChangeAddress, allowToClickIsSure, onChangeAddressType, btnStatus, isNameExit, isContactExit, btnLabel, submitContact: onBtnClick, isENSWrong: isEdit?.item?.isENSWrong, }, } } ================================================ FILE: packages/core/src/hooks/useractions/useCreateRedPacket.ts ================================================ import { AccountStatus, AssetsRawDataItem, Explorer, FeeInfo, getValuePrecisionThousand, LIVE_FEE_TIMES, myLog, NFTWholeINFO, REDPACKET_ORDER_LIMIT, REDPACKET_ORDER_NFT_LIMIT, RedPacketOrderData, SUBMIT_PANEL_AUTO_CLOSE, TOAST_TIME, UIERROR_CODE, WalletMap, BLINDBOX_REDPACKET_LIMIT, RedPacketOrderType, EXCLUSIVE_REDPACKET_ORDER_LIMIT_WHITELIST, EXCLUSIVE_REDPACKET_ORDER_LIMIT, MapChainId, isAddress, } from '@loopring-web/common-resources' import { NETWORKEXTEND, store, useAccount, useModalData, useNotify, useSystem, useTokenMap } from '../../stores' import { AccountStep, CreateRedPacketProps, RedPacketViewStep, SwitchData, useOpenModals, useSettings, useToggle, } from '@loopring-web/component-lib' import React, { useCallback } from 'react' import { makeWalletLayer2 } from '../help' import { useChargeFees, useWalletLayer2Socket, walletLayer2Service } from '../../services' import { useBtnStatus } from '../common' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI } from '../../api_wrapper' import { ConnectProvidersSignMap, connectProvides } from '@loopring-web/web3-provider' import { getTimestampDaysLater } from '../../utils' import { DAYS } from '../../defs' import Web3 from 'web3' import { isAccActivated } from './useCheckAccStatus' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useRedPacketConfig } from '../../stores/redPacket' import { useHistory, useLocation } from 'react-router-dom' import moment from 'moment' const checkPermission = ( dexToggle: any, info: { network: string; functionName: string; accAddress: string }, ) => { const whiteList = dexToggle.whiteList const { functionName, network, accAddress } = info const toogle = dexToggle[functionName] if (toogle.enable) { return true } else if (!toogle.enable && toogle.reason === 'no view') { const found = whiteList && whiteList[network].find((item: any) => item.superUserFunction.includes('redpacket_exclusive')) return ( found && found.superUserAddress.find( (addr) => addr.toLocaleLowerCase() === accAddress.toLocaleLowerCase(), ) ) } else { return false } } export const useCreateRedPacket = < T extends RedPacketOrderData, I, F = FeeInfo, NFT = NFTWholeINFO, >({ assetsRawData, isShow = false, }: { assetsRawData: AssetsRawDataItem[] isShow?: boolean }): { createRedPacketProps: CreateRedPacketProps retryBtn: (isHardware?: boolean) => void } => { const { exchangeInfo, chainId } = useSystem() const { tokenMap, totalCoinMap, idIndex } = useTokenMap() const { notifyMap } = useNotify() // const tradeType const { allowTrade: { transfer: transferEnabale }, } = useSystem() const { setShowAccount, setShowRedPacket, modals: { isShowAccount: { info }, }, } = useOpenModals() const [selectNFT, setSelectNFT] = React.useState(undefined) const { redPacketConfigs } = useRedPacketConfig() const { redPacketOrder, updateRedPacketOrder } = useModalData() const { account, status: accountStatus } = useAccount() const { checkHWAddr, updateHW } = useWalletInfo() const isToken = redPacketOrder.tradeType === RedPacketOrderType.TOKEN || (redPacketOrder.tradeType === RedPacketOrderType.BlindBox && !redPacketOrder.nftData) const feeProps = isToken ? { requestType: sdk.OffchainFeeReqType.EXTRA_TYPES, extraType: 1, } : { requestType: sdk.OffchainNFTFeeReqType.EXTRA_TYPES, tokenAddress: redPacketOrder?.tokenAddress, extraType: 1, isNFT: true, } const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, resetIntervalTime, } = useChargeFees({ ...feeProps, intervalTime: undefined, updateData: ({ fee }) => { const redPacketOrder = store.getState()._router_modalData.redPacketOrder updateRedPacketOrder({ ...(redPacketOrder as any), fee: fee }) const isToken = redPacketOrder.tradeType === RedPacketOrderType.TOKEN || (redPacketOrder.tradeType === RedPacketOrderType.BlindBox && !redPacketOrder.nftData) if ((isToken && !feeProps.isNFT) || (!isToken && feeProps.isNFT)) { updateRedPacketOrder({ ...(redPacketOrder as any), fee: fee }) } }, }) const [walletMap, setWalletMap] = React.useState( makeWalletLayer2({ needFilterZero: true }).walletMap ?? ({} as WalletMap), ) const walletLayer2Callback = React.useCallback(() => { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} setWalletMap(walletMap) const redPacketOrder = store.getState()._router_modalData.redPacketOrder if ( RedPacketOrderType.TOKEN === redPacketOrder.tradeType && !redPacketOrder.belong && walletMap ) { resetDefault(RedPacketOrderType.TOKEN) } else if ( RedPacketOrderType.TOKEN === redPacketOrder.tradeType && walletMap && redPacketOrder.belong && walletMap[redPacketOrder.belong] ) { handleOnDataChange({ balance: walletMap[redPacketOrder.belong]?.count, } as T) } }, [redPacketOrder, accountStatus]) const handleOnDataChange = React.useCallback( (tradeData: Partial) => { const redPacketOrder = store.getState()._router_modalData.redPacketOrder updateRedPacketOrder({ ...redPacketOrder, ...tradeData }) }, [updateRedPacketOrder], ) const resetDefault = React.useCallback( (value: RedPacketOrderType) => { if (info?.isRetry) { checkFeeIsEnough() return } const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} const isToken = value === RedPacketOrderType.TOKEN || (value === RedPacketOrderType.BlindBox && !redPacketOrder.isNFT) if (isToken && !redPacketOrder.belong && walletMap) { const keys = Reflect.ownKeys(walletMap) for (let key in keys) { const keyVal = keys[key] const walletInfo = walletMap[keyVal.toString()] if (sdk.toBig(walletInfo?.count ?? '0').gt(0)) { updateRedPacketOrder({ belong: keyVal as any, tradeValue: undefined, fee: feeInfo, balance: walletInfo?.count, memo: '', numbers: undefined, giftNumbers: undefined, validUntil: moment().add('days', 1).toDate().getTime(), validSince: Date.now(), tradeType: value, isNFT: false, } as unknown as T) break } } } else if (isToken && redPacketOrder.belong && walletMap) { const walletInfo = walletMap[redPacketOrder.belong] updateRedPacketOrder({ fee: feeInfo, belong: redPacketOrder.belong as any, tradeValue: undefined as any, balance: walletInfo?.count, memo: '', numbers: undefined, giftNumbers: undefined, validSince: Date.now(), validUntil: moment().add('days', 1).toDate().getTime(), tradeType: value, isNFT: false, } as unknown as T) } else if (!isToken) { updateRedPacketOrder({ fee: feeInfo, belong: undefined, tradeValue: undefined, balance: undefined, giftNumbers: undefined, memo: '', numbers: undefined, validSince: Date.now(), validUntil: moment().add('days', 1).toDate().getTime(), tradeType: value, isNFT: true, } as unknown as T) } else { updateRedPacketOrder({ fee: feeInfo, belong: redPacketOrder.belong, tradeValue: undefined, balance: undefined, giftNumbers: undefined, memo: '', numbers: undefined, validSince: Date.now(), validUntil: moment().add('days', 1).toDate().getTime(), tradeType: 'TOKEN', isNFT: false, } as unknown as T) } }, [ checkFeeIsEnough, walletMap, handleOnDataChange, feeInfo, redPacketOrder.belong, info?.isRetry, ], ) useWalletLayer2Socket({ walletLayer2Callback }) const { btnStatus, enableBtn, disableBtn, setLabelAndParams, resetBtnInfo, btnInfo } = useBtnStatus() const calcNumberAndAmount = React.useCallback(() => { const redPacketOrder = store.getState()._router_modalData.redPacketOrder as T if (redPacketOrder.type?.partition === sdk.LuckyTokenAmountType.RANDOM) { const eachValue = sdk.toBig(redPacketOrder?.tradeValue ?? 0).div(redPacketOrder.numbers ?? 1) const isToken = redPacketOrder.tradeType === RedPacketOrderType.TOKEN || (redPacketOrder.tradeType === RedPacketOrderType.BlindBox && !redPacketOrder.nftData) return { tradeValue: redPacketOrder?.tradeValue, eachValue: isToken ? eachValue.toString() : eachValue.toFixed(), } } else { return { tradeValue: sdk.toBig(redPacketOrder?.tradeValue ?? 0).times(redPacketOrder.numbers ?? 1), eachValue: redPacketOrder?.tradeValue, } } }, []) const history = useHistory() const checkBtnStatus = React.useCallback(() => { const _tradeData = calcNumberAndAmount() resetBtnInfo() if ( tokenMap && chargeFeeTokenList.length && !isFeeNotEnough.isFeeNotEnough && redPacketOrder.fee && redPacketOrder.fee?.belong && redPacketOrder.numbers && redPacketOrder.numbers > 0 && redPacketOrder.validUntil && // redPacketOrder.numbers <= REDPACKET_ORDER_LIMIT && _tradeData.tradeValue // && // redPacketOrder.memo && // redPacketOrder.memo?.trim().length > 0 ) { let tradeToken: any = {}, balance, tradeValue, isExceedBalance, tooSmall, tooLarge const feeToken = tokenMap[redPacketOrder.fee.belong] const feeRaw = redPacketOrder.fee.feeRaw ?? redPacketOrder.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const blindBoxGiftsEqualsZero = redPacketOrder.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX && sdk.toBig(redPacketOrder.giftNumbers ?? '0').isZero() const blindBoxGiftsLargerThanPackets = redPacketOrder.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX && sdk.toBig(redPacketOrder.giftNumbers ?? '0').isGreaterThan(redPacketOrder.numbers) const blindBoxPacketsNumberTooLarge = redPacketOrder.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX && sdk.toBig(redPacketOrder.numbers).isGreaterThan(BLINDBOX_REDPACKET_LIMIT) // @ts-ignore const isToken = redPacketOrder.tradeType === RedPacketOrderType.TOKEN || (redPacketOrder.tradeType === RedPacketOrderType.BlindBox && !redPacketOrder.nftData) if (isToken && redPacketOrder.belong && tokenMap[(redPacketOrder as T).belong as any]) { tradeToken = tokenMap[(redPacketOrder as T).belong as any] balance = sdk.toBig((redPacketOrder as T).balance ?? 0).times('1e' + tradeToken.decimals) tradeValue = sdk.toBig(_tradeData.tradeValue ?? 0).times('1e' + tradeToken.decimals) isExceedBalance = tradeValue .plus(feeToken.tokenId === tradeToken.tokenId ? fee : '0') .gt(balance) const eachValue = sdk.toBig(_tradeData.eachValue ?? 0).times('1e' + tradeToken.decimals) tooSmall = eachValue.lt(tradeToken.luckyTokenAmounts?.minimum ?? 0) tooLarge = tradeValue.gt(tradeToken.luckyTokenAmounts.maximum) } else { balance = redPacketOrder.balance ?? 0 tradeValue = sdk.toBig(redPacketOrder.tradeValue ?? 0) if (redPacketOrder.type?.partition === sdk.LuckyTokenAmountType.AVERAGE) { // console.log('isExceedBalance = tradeValue.times(redPacketOrder.giftNumbers ?? 1).gt(balance)', redPacketOrder.numbers, balance, tradeValue.toString()) isExceedBalance = tradeValue.times(redPacketOrder.numbers ?? 1).gt(balance) } else { isExceedBalance = tradeValue.gt(balance) } const eachValue = redPacketOrder.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? sdk.toBig(redPacketOrder.tradeValue ?? 0).div(redPacketOrder.giftNumbers ?? 1) : sdk.toBig(_tradeData.eachValue ?? 0) tooSmall = eachValue.lt(1) tooLarge = tradeValue .div( redPacketOrder.type?.partition === sdk.LuckyTokenAmountType.AVERAGE ? 1 : redPacketOrder.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? redPacketOrder.giftNumbers ?? 1 : redPacketOrder.numbers ?? 1, ) .gt(REDPACKET_ORDER_NFT_LIMIT) } if ( tradeValue && !isExceedBalance && !tooSmall && !tooLarge && ((!isToken && redPacketOrder.nftData) || // @ts-ignore isToken) && redPacketConfigs?.luckTokenAgents && !blindBoxGiftsEqualsZero && !blindBoxGiftsLargerThanPackets && !blindBoxPacketsNumberTooLarge && redPacketOrder.numbers <= redpacketNumberLimit ) { enableBtn() return } else { disableBtn() if (blindBoxGiftsEqualsZero) { setLabelAndParams('labelRedPacketsGiftsEqualsZero', {}) } else if (!redPacketConfigs?.luckTokenAgents) { setLabelAndParams('labelRedPacketWaitingBlock', {}) } else if (isExceedBalance) { setLabelAndParams('labelRedPacketsInsufficient', { symbol: isToken ? (tradeToken.symbol as string) : 'NFT', }) } else if ( isExceedBalance && (redPacketOrder as T).tradeType === RedPacketOrderType.TOKEN && feeToken.tokenId === tradeToken.tokenId ) { setLabelAndParams('labelReserveFee', { symbol: tradeToken.symbol as string, }) } else if (isFeeNotEnough.isFeeNotEnough) { setLabelAndParams('labelRedPacketFee', {}) } else if (tooSmall) { setLabelAndParams( 'labelRedPacketsMin', isToken ? tradeToken && tradeToken.decimals && { value: getValuePrecisionThousand( sdk .toBig(tradeToken.luckyTokenAmounts?.minimum ?? '0') .div('1e' + tradeToken.decimals), tradeToken.precision, tradeToken.precision, tradeToken.precision, false, { floor: false, isAbbreviate: true }, ), symbol: tradeToken.symbol, } : { value: redPacketOrder.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? redPacketOrder.giftNumbers : redPacketOrder.type?.partition === sdk.LuckyTokenAmountType.AVERAGE ? 1 : redPacketOrder.numbers, symbol: 'NFT', }, ) } else if (redPacketOrder.numbers > redpacketNumberLimit) { setLabelAndParams('labelRedPacketsSplitNumber', { value: redpacketNumberLimit.toString(), }) } else if (tooLarge) { setLabelAndParams( 'labelRedPacketsMax', (redPacketOrder as T).tradeType === RedPacketOrderType.TOKEN && tradeToken ? { value: getValuePrecisionThousand( sdk .toBig(tradeToken.luckyTokenAmounts.maximu ?? 0) .div('1e' + tradeToken.decimals), tradeToken.precision, tradeToken.precision, tradeToken.precision, false, { floor: true, isAbbreviate: true }, ), symbol: tradeToken.symbol, } : { value: sdk .toBig(REDPACKET_ORDER_NFT_LIMIT) .times( redPacketOrder.type?.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? redPacketOrder.giftNumbers ?? 1 : redPacketOrder.type?.partition === sdk.LuckyTokenAmountType.AVERAGE ? 1 : redPacketOrder.numbers ?? 1, ), symbol: 'NFT', }, ) } else if (blindBoxGiftsLargerThanPackets) { setLabelAndParams('labelRedPacketsGiftsLargerThanPackets', {}) } else if (blindBoxPacketsNumberTooLarge) { setLabelAndParams('labelBlindBoxNumberOverMaximun', {}) } } } disableBtn() }, [ chargeFeeTokenList.length, disableBtn, enableBtn, isFeeNotEnough.isFeeNotEnough, tokenMap, redPacketOrder.balance, redPacketOrder.belong, redPacketOrder.fee, redPacketOrder.tradeValue, redPacketOrder.numbers, redPacketOrder.memo, redPacketConfigs?.luckTokenAgents, redPacketOrder.giftNumbers, redPacketOrder.validSince, redPacketOrder.validUntil, ]) React.useEffect(() => { checkBtnStatus() }, [ chargeFeeTokenList, isFeeNotEnough.isFeeNotEnough, redPacketOrder?.numbers, redPacketOrder.balance, redPacketOrder.belong, redPacketOrder.fee, redPacketOrder.tradeValue, redPacketOrder.memo, redPacketConfigs?.luckTokenAgents, redPacketOrder.validSince, redPacketOrder.validUntil, redPacketOrder?.giftNumbers, ]) const processRequest = React.useCallback( async (request: sdk.LuckyTokenItemForSendV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account const redPacketOrder = store.getState()._router_modalData.redPacketOrder try { if (connectProvides.usedWeb3 && LoopringAPI.luckTokenAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } updateRedPacketOrder({ ...redPacketOrder, __request__: request, } as any) const response = await LoopringAPI.luckTokenAPI.sendLuckTokenSend( { request, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProvidersSignMap[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submit sendLuckTokenSend:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } // setIsConfirmTransfer(false); setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_In_Progress, }) await sdk.sleep(TOAST_TIME) if (redPacketOrder.type?.scope === sdk.LuckyTokenViewType.TARGET) { setShowAccount({ isShow: false, }) handleOnDataChange({ target: { redpacketHash: (response as sdk.TX_HASH_API)?.hash, maxSendCount: request.numbers, }, } as any) getTargetRedpackets() } else { setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_Success, info: { scope: request.type.scope, hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-transfer`, shared: request.type.scope == sdk.LuckyTokenViewType.PUBLIC ? () => { LoopringAPI.luckTokenAPI ?.getLuckTokenDetail( { hash: (response as sdk.TX_HASH_API).hash!, }, apiKey, ) .then((response) => { setShowAccount({ isShow: false }) setShowRedPacket({ isShow: true, info: { ...response.detail.luckyToken, }, step: RedPacketViewStep.QRCodePanel, }) }) } : undefined, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } walletLayer2Service.sendUserUpdate() history.push(`/redpacket?redPacketHash=${(response as sdk.TX_HASH_API)?.hash}`) resetDefault(RedPacketOrderType.TOKEN) if ( request.type.scope == sdk.LuckyTokenViewType.PRIVATE && (response as sdk.TX_HASH_API)?.hash ) { setShowAccount({ isShow: false }) const blindBoxRepspnse = (await LoopringAPI.luckTokenAPI.getBlindBoxDetail( { hash: (response as sdk.TX_HASH_API).hash!, showHelper: false, }, apiKey, )) as any setShowRedPacket({ isShow: true, info: { ...blindBoxRepspnse.raw_data.luckyToken, hash: (response as sdk.TX_HASH_API).hash, }, step: RedPacketViewStep.QRCodePanel, }) } else { await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.RedPacketSend_Success ) { setShowAccount({ isShow: false }) } } } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_First_Method_Denied, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_User_Denied, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, setShowAccount, redPacketOrder.belong, checkFeeIsEnough, resetDefault, updateHW, ], ) React.useEffect(() => { if (isShow) { resetDefault(RedPacketOrderType.BlindBox) walletLayer2Service.sendUserUpdate() } }, [isShow]) React.useEffect(() => { ;(async () => { if (redPacketOrder.target?.redpacketHash) { const response = await LoopringAPI.luckTokenAPI?.getLuckTokenDetail( { hash: redPacketOrder.target?.redpacketHash, }, account.apiKey, ) const targets = (response?.detail as any).targets as string[] handleOnDataChange({ target: { ...redPacketOrder.target, sentAddresses: targets, }, } as any) } })() }, [redPacketOrder.target?.redpacketHash]) React.useEffect(() => { if (isShow) { checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [isShow, redPacketOrder.tradeType]) const onCreateRedPacketClick = React.useCallback( async (_redPacketOrder, isHardwareRetry: boolean = false) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const redPacketOrder = store.getState()._router_modalData.redPacketOrder as T const _tradeData = calcNumberAndAmount() const isToken = redPacketOrder.tradeType === RedPacketOrderType.TOKEN || (redPacketOrder.tradeType === RedPacketOrderType.BlindBox && !redPacketOrder.isNFT) if ( readyState === AccountStatus.ACTIVATED && LoopringAPI.userAPI && tokenMap && exchangeInfo && chargeFeeTokenList.length && !isFeeNotEnough.isFeeNotEnough && redPacketOrder.belong && (!isToken ? redPacketOrder.nftData : tokenMap[redPacketOrder.belong]) && redPacketOrder.fee && redPacketOrder.fee.belong && redPacketOrder.fee?.__raw__ && redPacketOrder.numbers && redPacketOrder.numbers > 0 && _tradeData.tradeValue && redPacketOrder.type && // redPacketOrder.memo && redPacketOrder?.validUntil && redPacketConfigs?.luckTokenAgents && // redPacketOrder.memo?.trim().length > 0 && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_WaitForAuth, }) let tradeToken, tradeValue if (!isToken) { tradeToken = { tokenId: redPacketOrder.tokenId, nftDta: redPacketOrder.nftData, } tradeValue = sdk.toBig(_tradeData.tradeValue) } else { //@ts-ignore tradeToken = tokenMap[redPacketOrder.belong.toString()] tradeValue = sdk.toBig(_tradeData.tradeValue ?? 0).times('1e' + tradeToken.decimals) } const feeToken = tokenMap[redPacketOrder.fee.belong] const feeRaw = redPacketOrder.fee.feeRaw ?? redPacketOrder.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId, sellTokenId: Number(tradeToken.tokenId), }, apiKey, ) // const { broker } = await LoopringAPI.userAPI.getAvailableBroker({ // type: 1, // }); myLog('memo', redPacketOrder.memo) const req: sdk.LuckyTokenItemForSendV3 = { type: { ...redPacketOrder.type, mode: redPacketOrder.tradeType === RedPacketOrderType.BlindBox ? sdk.LuckyTokenClaimType.BLIND_BOX : redPacketOrder.tradeType === RedPacketOrderType.NFT ? sdk.LuckyTokenClaimType.COMMON : // @ts-ignore redPacketOrder.type?.mode ?? sdk.LuckyTokenClaimType.COMMON, }, numbers: redPacketOrder.numbers, giftNumbers: redPacketOrder.giftNumbers!, memo: redPacketOrder.memo ? redPacketOrder.memo : 'Best wishes', signerFlag: false as any, nftData: isToken ? undefined : redPacketOrder.nftData, // @ts-ignore templateId: 0, validSince: Math.round((redPacketOrder.validSince ?? Date.now()) / 1000), validUntil: Math.round((redPacketOrder.validUntil ?? Date.now()) / 1000), luckyToken: { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: Reflect.ownKeys(redPacketConfigs.luckTokenAgents ?? {})[0], storageId: storageId?.offchainId, token: tradeToken.tokenId, amount: tradeValue.toFixed(), feeToken: feeToken.tokenId, maxFeeAmount: fee.toFixed(), validUntil: getTimestampDaysLater(DAYS - 1), } as any, } myLog('transfer req:', req) processRequest(req, !isHardwareRetry) } catch (e: any) { // transfer failed setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_Failed, error: { code: UIERROR_CODE.UNKNOWN, message: e.message, } as sdk.RESULT_INFO, }) } } else { return } }, [ account, tokenMap, exchangeInfo, setShowAccount, processRequest, redPacketConfigs?.luckTokenAgents, ], ) const onSendTargetRedpacketClick = React.useCallback(async () => { const { readyState } = account const redPacketOrder = store.getState()._router_modalData.redPacketOrder as T const getValidAddresses = (input: string) => { return input .split(';') .map((str) => str.trim()) .filter((str) => { return isAddress(str.trim()) }) } if ( readyState === AccountStatus.ACTIVATED && LoopringAPI.luckTokenAPI && redPacketOrder.target?.redpacketHash && getValidAddresses(redPacketOrder.target?.addressListString).length > 0 && redPacketOrder.target?.addressListString ) { try { setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_WaitForAuth, }) const response = await LoopringAPI.luckTokenAPI.sendLuckTokenSubmitAddTarget( { claimer: getValidAddresses(redPacketOrder.target?.addressListString), hash: redPacketOrder.target?.redpacketHash, notifyType: isWhiteListed ? redPacketOrder.target?.popupChecked === undefined || redPacketOrder.target?.popupChecked ? 1 : 0 : 0, }, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_Success, info: { scope: sdk.LuckyTokenViewType.TARGET, }, }) getTargetRedpackets() } catch (e: any) { setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_Failed, error: { code: UIERROR_CODE.UNKNOWN, message: e.message, } as sdk.RESULT_INFO, }) } } else { return } }, [ account.readyState, redPacketOrder.target?.redpacketHash, redPacketOrder.target?.addressListString, ]) const handlePanelEvent = useCallback( async (data: SwitchData>) => { return new Promise((res: any) => { if (data.to === 'button') { if (walletMap && data?.tradeData?.belong) { const walletInfo = walletMap[data?.tradeData?.belong as string] handleOnDataChange({ belong: data.tradeData?.belong, tradeValue: data.tradeData?.tradeValue, balance: walletInfo ? walletInfo.count : 0, } as T) } else { handleOnDataChange({ belong: undefined, tradeValue: undefined, balance: undefined, } as unknown as T) } } res() }) }, [walletMap], ) const [isWhiteListed, setIsWhiteListed] = React.useState(undefined as undefined | boolean) const redpacketNumberLimit = redPacketOrder.type?.scope === sdk.LuckyTokenViewType.TARGET ? isWhiteListed ? EXCLUSIVE_REDPACKET_ORDER_LIMIT_WHITELIST : EXCLUSIVE_REDPACKET_ORDER_LIMIT : isToken ? REDPACKET_ORDER_LIMIT : REDPACKET_ORDER_NFT_LIMIT const [minimum, maximum] = React.useMemo(() => { if (redPacketOrder.tradeType === RedPacketOrderType.NFT) { const minimum = sdk .toBig(redPacketOrder?.tradeValue ?? 0) .div(REDPACKET_ORDER_NFT_LIMIT) .toFixed(0, 1) const maximum = redPacketOrder?.balance && // @ts-ignore sdk.toBig(redPacketOrder.balance ?? 0).lt(redpacketNumberLimit) ? // @ts-ignore redPacketOrder?.tradeValue ?? redPacketOrder.balance : redpacketNumberLimit return [minimum ? 1 : minimum, maximum] } else { if (redPacketOrder.belong && tokenMap[redPacketOrder.belong]) { const { minimum, maximum } = tokenMap[redPacketOrder.belong].luckyTokenAmounts const decimals = tokenMap[redPacketOrder.belong].decimals return [ sdk .toBig(minimum ?? 0) .div('1e' + decimals) .toString(), sdk .toBig(maximum ?? 0) .div('1e' + decimals) .toString(), ] } else { return [undefined, undefined] } } }, [redPacketOrder?.belong, tokenMap, redPacketOrder.tradeType]) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.RedPacketSend_WaitForAuth, }) onCreateRedPacketClick({}, isHardwareRetry) }, [processRequest, setShowAccount], ) const location = useLocation() React.useEffect(() => { ;(async () => { const nftDatas = new URLSearchParams(location.search).get('nftDatas') if (nftDatas) { updateRedPacketOrder({ ...redPacketOrder, tradeType: RedPacketOrderType.FromNFT, isNFT: true, }) const info = await LoopringAPI.nftAPI?.getInfoForNFTTokens({ nftDatas: [nftDatas], }) if (info && info[nftDatas]) { const balance = await LoopringAPI.userAPI?.getUserNFTBalances( { accountId: account.accountId, nftDatas: nftDatas, metadata: true, }, account.apiKey, ) const balanceInfo = balance!.userNFTBalances[0] handleOnDataChange({ collectionInfo: balanceInfo.collectionInfo, tokenId: balanceInfo.tokenId, tradeValue: undefined, balance: balanceInfo.total, nftData: balanceInfo.nftData, belong: balanceInfo.metadata?.base.name, tokenAddress: balanceInfo.tokenAddress, image: balanceInfo?.metadata?.imageSize && balanceInfo?.metadata?.imageSize['240-240'], } as T) } } })() }, [location.search]) const [targetRedPackets, setTargetRedPackets] = React.useState( [] as sdk.LuckyTokenItemForReceive[], ) const [popRedPacket, setPopRedPacket] = React.useState( undefined as sdk.LuckTokenClaimDetail | undefined, ) const tokenInfo = popRedPacket && tokenMap[idIndex[popRedPacket.luckyToken.tokenId]] const popRedPacketAmountStr = popRedPacket ? popRedPacket.luckyToken.isNft ? `${popRedPacket.luckyToken.tokenAmount.totalAmount} NFTs` : tokenInfo && getValuePrecisionThousand( sdk .toBig(popRedPacket.luckyToken.tokenAmount.totalAmount) .div('1e' + tokenInfo!.decimals), tokenInfo!.precision, tokenInfo!.precision, tokenInfo!.precision, false, ) + ' ' + tokenInfo?.symbol : undefined const getTargetRedpackets = async () => { const response = await LoopringAPI.luckTokenAPI?.getLuckTokenLuckyTokens( { senderId: account.accountId, scopes: '2', modes: '0,1,2', partitions: '0,1', statuses: '2', official: false, offset: 0, limit: 100, isEnough: true, } as any, account.apiKey, ) setTargetRedPackets(response?.list ?? []) } React.useEffect(() => { getTargetRedpackets() ;(async () => { const response = await LoopringAPI.luckTokenAPI?.getLuckTokenAuthorizedSigners() const found = (response?.raw_data as any)?.find( (item) => item.owner.toLocaleLowerCase() === account.accAddress.toLocaleLowerCase(), ) setIsWhiteListed(found ? true : false) })() }, []) const onClickViewTargetDetail = React.useCallback(async (hash: string) => { const response = await LoopringAPI.luckTokenAPI?.getLuckTokenDetail( { hash: hash, }, account.apiKey, ) setPopRedPacket(response?.detail) }, []) const onCloseRedpacketPop = React.useCallback(async () => { setPopRedPacket(undefined) }, []) const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { toggle } = useToggle() const showExclusiveOption = checkPermission(toggle, { accAddress: account.accAddress, network, functionName: 'redpacket_exclusive', }) const createRedPacketProps: CreateRedPacketProps = { redPacketConfig: notifyMap?.redPacket ?? {}, tradeType: redPacketOrder.tradeType, chargeFeeTokenList, onCreateRedPacketClick, btnStatus, btnInfo, disabled: transferEnabale?.enable == true, assetsData: assetsRawData, walletMap, coinMap: totalCoinMap, tokenMap, idIndex, minimum, maximum, feeInfo: redPacketOrder.fee ?? feeInfo, handleFeeChange, tradeData: redPacketOrder as T, handleOnDataChange, handlePanelEvent, selectNFT, handleOnChoose: (_value: NFT) => { setSelectNFT(_value as NFT) if (_value) { const value = _value as any handleOnDataChange({ collectionInfo: value.collectionInfo, tokenId: value.tokenId, tradeValue: undefined, balance: value.nftBalance ?? value.total, nftData: value.nftData, belong: value.name, tokenAddress: value.tokenAddress, image: value?.metadata?.imageSize ? value?.metadata?.imageSize['240-240'] : value.image, } as T) } }, selectNFTDisabled: redPacketOrder.tradeType === RedPacketOrderType.FromNFT, onSendTargetRedpacketClick, targetRedPackets, popRedPacket, popRedPacketAmountStr, onClickViewTargetDetail, onCloseRedpacketPop, isWhiteListed, showExclusiveOption, } as unknown as CreateRedPacketProps return { createRedPacketProps, retryBtn } } ================================================ FILE: packages/core/src/hooks/useractions/useDefiTrade.ts ================================================ import React from 'react' import { DeFiWrapProps, ToastType, useOpenModals, useSettings, useToggle, } from '@loopring-web/component-lib' import { AccountStatus, CustomErrorWithCode, DEFI_CONFIG, LEVERAGE_ETH_CONFIG, DeFiCalcData, DeFiChgType, getValuePrecisionThousand, globalSetup, IBData, MapChainId, MarketType, myLog, SDK_ERROR_MAP_TO_UI, TradeBtnStatus, TradeDefi, } from '@loopring-web/common-resources' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import { DAYS, getTimestampDaysLater, LoopringAPI, store, useAccount, useSystem, useTokenMap, walletLayer2Service, makeWalletLayer2, useSubmitBtn, useWalletLayer2Socket, useDefiMap, useTradeDefi, } from '../../index' import { useTranslation } from 'react-i18next' export const useDefiTrade = , I, ACD extends DeFiCalcData>({ isJoin = true, isLeverageETH = false, market, setToastOpen, setServerUpdate, setConfirmShowNoBalance, confirmShowLimitBalance, setConfirmShowLimitBalance, }: { market: string isJoin: boolean isLeverageETH: boolean setServerUpdate: (state: any) => void setConfirmShowLimitBalance: (state: boolean) => void setConfirmShowNoBalance: (state: boolean) => void confirmShowLimitBalance: boolean setToastOpen: (props: { open: boolean; content: JSX.Element | string; type: ToastType }) => void }) => { const { t } = useTranslation(['common']) const refreshRef = React.createRef() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { marketMap, marketLeverageMap, updateDefiSyncMap } = useDefiMap() const [isLoading, setIsLoading] = React.useState(false) const [isStoB, setIsStoB] = React.useState(true) const { tokenMap } = useTokenMap() const { account } = useAccount() // const { status: walletLayer2Status } = useWalletLayer2(); const { exchangeInfo, allowTrade } = useSystem() const { updateTradeDefi, resetTradeDefi } = useTradeDefi() const { setShowSupport, setShowTradeIsFrozen, setShowETHStakingApr } = useOpenModals() const { toggle } = useToggle() const [{ coinSellSymbol, coinBuySymbol }, setSymbol] = React.useState(() => { if (isJoin) { const [, coinBuySymbol, coinSellSymbol] = market.match(/(\w+)-(\w+)/i) ?? [] return { coinBuySymbol, coinSellSymbol } } else { const [, coinSellSymbol, coinBuySymbol] = market.match(/(\w+)-(\w+)/i) ?? [] return { coinBuySymbol, coinSellSymbol } } }) const getFee = React.useCallback( async ( requestType: sdk.OffchainFeeReqType.DEFI_JOIN | sdk.OffchainFeeReqType.DEFI_EXIT, ): Promise<{ fee: string; feeRaw: string } | undefined> => { if ( LoopringAPI.userAPI && coinSellSymbol && account.readyState === AccountStatus.ACTIVATED && tokenMap ) { const feeToken: sdk.TokenInfo = tokenMap[coinBuySymbol] const request: sdk.GetOffchainFeeAmtRequest = { accountId: account.accountId, requestType, market, } const { fees } = await LoopringAPI.userAPI.getOffchainFeeAmt(request, account.apiKey) const feeRaw = fees[coinBuySymbol] ? fees[coinBuySymbol].fee : '0' const fee = sdk .toBig(feeRaw) .div('1e' + feeToken.decimals) .toString() myLog('new fee:', fee.toString()) return { fee: fee, feeRaw: feeRaw, // fees, } } }, [ coinSellSymbol, account.readyState, account.accountId, account.apiKey, tokenMap, coinBuySymbol, market, ], ) const handleOnchange = _.debounce( ({ tradeData, type, _tradeDefi = {}, }: { type: DeFiChgType tradeData: T _tradeDefi?: Partial> }) => { const marketInfo = (isLeverageETH ? marketLeverageMap : marketMap)[market] let calcValue, feeRaw, fee let newTradeDefi = { ...store.getState()._router_tradeDefi.tradeDefi, ..._tradeDefi, } let _deFiCalcData: DeFiCalcData = newTradeDefi.deFiCalcData as unknown as DeFiCalcData if ( tradeData && newTradeDefi.defiBalances && coinBuySymbol && newTradeDefi?.defiBalances[coinBuySymbol] ) { const inputValue = type === DeFiChgType.coinSell ? { sellAmount: tradeData?.tradeValue?.toString() ?? '0', } : { buyAmount: tradeData?.tradeValue?.toString() ?? '0', } const buyTokenBalanceVol: string = newTradeDefi?.defiBalances[coinBuySymbol] ?? '' calcValue = sdk.calcDefi({ isJoin, isInputSell: type === DeFiChgType.coinSell, ...inputValue, maxFeeBips: (isLeverageETH ? marketLeverageMap : marketMap)[market]?.extra?.maxFeeBips ?? 5, defaultFee: newTradeDefi.defaultFee, marketInfo, tokenSell: tokenMap[coinSellSymbol], tokenBuy: tokenMap[coinBuySymbol], buyTokenBalanceVol, withdrawFeeBips: (isLeverageETH ? marketLeverageMap : marketMap)[market]?.extra ?.withdrawFeeBips, }) const sellAmount = tradeData?.tradeValue === undefined ? undefined : getValuePrecisionThousand( sdk.toBig(calcValue?.sellVol ?? 0).div('1e' + tokenMap[coinSellSymbol]?.decimals), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: false }, ).replaceAll(sdk.SEP, '') const buyAmount = tradeData?.tradeValue === undefined ? undefined : getValuePrecisionThousand( sdk.toBig(calcValue?.buyVol ?? 0).div('1e' + tokenMap[coinBuySymbol]?.decimals), tokenMap[coinBuySymbol].precision, tokenMap[coinBuySymbol].precision, tokenMap[coinBuySymbol].precision, true, { floor: true }, ).replaceAll(sdk.SEP, '') if (sdk.toBig(newTradeDefi.defaultFee).gt(0)) { feeRaw = isNaN(calcValue.feeVol) ? '0' : calcValue.feeVol fee = sdk .toBig(feeRaw) .div('1e' + tokenMap[coinBuySymbol].decimals) .toString() } else { feeRaw = '0' fee = '0' } // @ts-ignore _deFiCalcData = { ..._deFiCalcData, fee, coinSell: type === DeFiChgType.coinSell ? tradeData : { ..._deFiCalcData?.coinSell, tradeValue: sellAmount }, coinBuy: type === DeFiChgType.coinBuy ? tradeData : { ..._deFiCalcData?.coinBuy, tradeValue: buyAmount }, } } updateTradeDefi({ market: newTradeDefi.market !== market ? (market as MarketType) : undefined, ...newTradeDefi, type: marketInfo.type, ...calcValue, feeRaw, fee, deFiCalcData: { ..._deFiCalcData, }, lastInput: type, }) }, globalSetup.wait, ) const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const account = store.getState().account const tradeDefi = store.getState()._router_tradeDefi.tradeDefi if (account.readyState === AccountStatus.ACTIVATED) { const sellExceed = sdk .toBig(tradeDefi.deFiCalcData?.coinSell?.tradeValue ?? 0) .gt(tradeDefi.deFiCalcData?.coinSell?.balance ?? 0) if ( tradeDefi?.sellVol === undefined || sdk.toBig(tradeDefi?.sellVol).lte(0) || tradeDefi?.buyVol === undefined || sdk.toBig(tradeDefi?.buyVol).lte(0) || tradeDefi?.maxFeeBips === undefined || tradeDefi?.maxFeeBips === 0 ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelEnterAmount', } } else if (sdk.toBig(tradeDefi?.fee ?? 0).lte(0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelFeeCalculating`, } } else if ( sdk .toBig(tradeDefi?.sellVol) .minus(tradeDefi?.miniSellVol ?? 0) .lt(0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDefiMin| ${getValuePrecisionThousand( sdk.toBig(tradeDefi?.miniSellVol ?? 0).div('1e' + tokenMap[coinSellSymbol]?.decimals), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: false, isAbbreviate: true }, )} ${coinSellSymbol}`, } } else if (sellExceed) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDefiNoEnough| ${coinSellSymbol}`, } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } // label: ''} } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } }, [market, tokenMap, coinSellSymbol]) const resetDefault = React.useCallback( async (clearTrade: boolean = false, { defaultFee }: { defaultFee?: any }) => { let walletMap: any = {} const tradeDefi = store.getState()._router_tradeDefi.tradeDefi const [, baseSymbol, quoteSymbol] = market.match(/(\w+)-(\w+)/i) ?? [] const { marketLeverageMap, marketMap } = store.getState().invest.defiMap const marketInfo = isLeverageETH ? marketLeverageMap[market] : marketMap[market] let deFiCalcDataInit: Partial> = { ...tradeDefi.deFiCalcData, coinSell: { belong: coinSellSymbol, balance: undefined, tradeValue: tradeDefi.deFiCalcData?.coinSell?.belong === coinSellSymbol ? tradeDefi.deFiCalcData?.coinSell?.tradeValue : undefined, }, coinBuy: { belong: coinBuySymbol, balance: undefined, tradeValue: tradeDefi.deFiCalcData?.coinBuy?.belong === coinBuySymbol ? tradeDefi.deFiCalcData?.coinBuy?.tradeValue : undefined, }, } if (account.readyState === AccountStatus.ACTIVATED) { if (clearTrade) { walletLayer2Service.sendUserUpdate() } walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} deFiCalcDataInit.coinSell.balance = walletMap[coinSellSymbol]?.count deFiCalcDataInit.coinBuy.balance = walletMap[coinBuySymbol]?.count } myLog( 'resetDefault defi clearTrade', deFiCalcDataInit.coinSell, tradeDefi.deFiCalcData?.coinSell?.tradeValue, clearTrade, defaultFee, ) if ( tradeDefi.market !== market || clearTrade || tradeDefi.deFiCalcData?.coinSell?.tradeValue === undefined ) { deFiCalcDataInit.coinSell.tradeValue = undefined deFiCalcDataInit.coinBuy.tradeValue = undefined const [AtoB, BtoA] = marketInfo ? isJoin ? [marketInfo.depositPrice, marketInfo.withdrawPrice] : [marketInfo.withdrawPrice, marketInfo.depositPrice] : ['0', '0'] updateTradeDefi({ market: tradeDefi.market !== market ? (market as MarketType) : undefined, type: marketInfo.type, isStoB, sellVol: '0', buyVol: '0', sellToken: tokenMap[coinSellSymbol], buyToken: tokenMap[coinBuySymbol], deFiCalcData: { ...deFiCalcDataInit, AtoB, BtoA, fee: '', // : undefined, //feeInfo?.fee?.toString() ?? '', } as DeFiCalcData, defiBalances: { [baseSymbol]: marketInfo?.baseVolume ?? '', [quoteSymbol]: marketInfo?.quoteVolume ?? '', } as any, defaultFee: defaultFee?.feeRaw, depositPrice: marketInfo?.depositPrice ?? '0', withdrawPrice: marketInfo?.withdrawPrice ?? '0', withdrawFeeBips: marketInfo?.extra?.withdrawFeeBips, }) myLog('resetDefault defi clearTrade', deFiCalcDataInit, marketInfo) } else { const type = tradeDefi.lastInput ?? DeFiChgType.coinSell const _tradeDefi = { defiBalances: { [baseSymbol]: marketInfo?.baseVolume ?? '', [quoteSymbol]: marketInfo?.quoteVolume ?? '', } as any, defaultFee: defaultFee?.feeRaw, depositPrice: marketInfo?.depositPrice ?? '0', withdrawPrice: marketInfo?.withdrawPrice ?? '0', } const tradeData = { ...deFiCalcDataInit[type], tradeValue: tradeDefi.deFiCalcData[type]?.tradeValue ?? undefined, } handleOnchange({ tradeData, type, _tradeDefi }) } setIsLoading(false) }, [ account.readyState, coinBuySymbol, coinSellSymbol, handleOnchange, isJoin, isStoB, market, tokenMap, updateTradeDefi, ], ) const should15sRefresh = _.debounce(async (clearTrade: boolean = false) => { myLog('should15sRefresh', market, clearTrade) if (market && LoopringAPI.defiAPI) { // updateDepth() // getDefiMap(); if (clearTrade) { setIsLoading(true) } Promise.all([ LoopringAPI.defiAPI?.getDefiMarkets({ defiType: (isLeverageETH ? LEVERAGE_ETH_CONFIG : DEFI_CONFIG).products[network].join(','), }), account.readyState === AccountStatus.ACTIVATED ? getFee(isJoin ? sdk.OffchainFeeReqType.DEFI_JOIN : sdk.OffchainFeeReqType.DEFI_EXIT) : Promise.resolve(undefined), ]).then(([defiMapInfo, _feeInfo]) => { if ((defiMapInfo as sdk.RESULT_INFO).code || (defiMapInfo as sdk.RESULT_INFO).message) { setServerUpdate(true) } else { let status: any = defiMapInfo.markets[market]?.status ?? 0 status = ('00000000' + status.toString(2)).split('') if ((status[status.length - 2] !== '1' && status[status.length - 4] !== '1')) { setServerUpdate(true) } else { updateDefiSyncMap({ defiMap: { marketMap: defiMapInfo.markets, marketCoins: defiMapInfo.tokenArr, marketArray: defiMapInfo.marketArr, }, }) } } resetDefault(clearTrade, { defaultFee: _feeInfo }) }) // if (account.readyState === AccountStatus.ACTIVATED) { // resetDefault(clearTrade, {}) // } } }, globalSetup.wait) const walletLayer2Callback = React.useCallback(async () => { const tradeDefi = store.getState()._router_tradeDefi.tradeDefi const type = tradeDefi.lastInput ?? DeFiChgType.coinSell let tradeValue: any = undefined let deFiCalcDataInit: Partial> = { coinSell: { belong: coinSellSymbol, balance: undefined, }, coinBuy: { belong: coinBuySymbol, balance: undefined, }, ...(tradeDefi?.deFiCalcData ?? {}), } if (tradeDefi.deFiCalcData) { tradeValue = tradeDefi?.deFiCalcData[type]?.tradeValue ?? undefined } if (deFiCalcDataInit[type]?.belong) { let walletMap: any if (account.readyState === AccountStatus.ACTIVATED) { walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap deFiCalcDataInit.coinSell = { belong: coinSellSymbol, balance: walletMap[coinSellSymbol]?.count, } deFiCalcDataInit.coinBuy = { belong: coinBuySymbol, balance: walletMap[coinBuySymbol]?.count, } } else { deFiCalcDataInit.coinSell = { belong: coinSellSymbol, balance: undefined, } deFiCalcDataInit.coinBuy = { belong: coinBuySymbol, balance: undefined, } } const tradeData = { ...deFiCalcDataInit[type], tradeValue, } myLog('resetDefault Defi walletLayer2Callback', tradeData) handleOnchange({ tradeData, type }) } }, [account.readyState, coinBuySymbol, coinSellSymbol, handleOnchange]) useWalletLayer2Socket({ walletLayer2Callback }) const sendRequest = React.useCallback(async () => { const tradeDefi = store.getState()._router_tradeDefi.tradeDefi try { setIsLoading(true) if ( LoopringAPI.userAPI && LoopringAPI.defiAPI && tradeDefi.sellToken?.symbol && tradeDefi.maxFeeBips && exchangeInfo ) { const req: sdk.GetNextStorageIdRequest = { accountId: account.accountId, sellTokenId: tradeDefi.sellToken?.tokenId ?? 0, } const storageId = await LoopringAPI.userAPI.getNextStorageId(req, account.apiKey) let fee = tradeDefi.feeRaw let maxFeeBips = tradeDefi.maxFeeBips <= 5 ? 5 : tradeDefi.maxFeeBips const request: sdk.DefiOrderRequest = { exchange: exchangeInfo.exchangeAddress, storageId: storageId.orderId, accountId: account.accountId, sellToken: { tokenId: tradeDefi.sellToken?.tokenId ?? 0, volume: tradeDefi.sellVol, }, buyToken: { tokenId: tradeDefi.buyToken?.tokenId ?? 0, volume: tradeDefi.buyVol, }, validUntil: getTimestampDaysLater(DAYS), maxFeeBips: maxFeeBips, fillAmountBOrS: false, action: isJoin ? sdk.DefiAction.Deposit : sdk.DefiAction.Withdraw, fee: fee, type: tradeDefi.type, taker: '', eddsaSignature: '', // taker: // new BN(ethUtil.toBuffer(request.taker)).toString(), } myLog('DefiTrade request:', request) const response = await LoopringAPI.defiAPI.orderDefi( request, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] throw new CustomErrorWithCode(errorItem) } else { setToastOpen({ open: true, type: ToastType.success, content: t('labelInvestSuccess', { type: isJoin ? t('labelInvestDefDeposit') : t('labelInvestDefWithdraw'), symbol: coinBuySymbol, }), }) } } else { throw new Error('api not ready') } } catch (reason) { setToastOpen({ open: true, type: ToastType.error, content: t('labelInvestFailed') + (reason as CustomErrorWithCode)?.messageKey ?? ` error: ${t((reason as CustomErrorWithCode)?.messageKey)}`, }) } finally { setConfirmShowLimitBalance(false) should15sRefresh(true) } }, [ account.accountId, account.apiKey, account.eddsaKey.sk, coinBuySymbol, exchangeInfo, isJoin, setToastOpen, should15sRefresh, t, ]) const handleSubmit = React.useCallback(async () => { const tradeDefi = store.getState()._router_tradeDefi.tradeDefi // const marketInfo = defiMarketMap[market]; if ( (account.readyState === AccountStatus.ACTIVATED && tokenMap && exchangeInfo && account.eddsaKey?.sk, tradeDefi.buyVol) ) { const [, tokenBase] = market.match(/(\w+)-(\w+)/i) ?? [] if (allowTrade && !allowTrade.defiInvest.enable) { setShowSupport({ isShow: true }) } else if (toggle && !toggle[`${tokenBase}Invest`].enable) { setShowTradeIsFrozen({ isShow: true, type: 'DefiInvest' }) } else { sendRequest() } } else { return false } }, [ market, account.readyState, account.eddsaKey?.sk, tokenMap, exchangeInfo, sendRequest, setToastOpen, t, ]) const onSubmitBtnClick = React.useCallback(async () => { const tradeDefi = store.getState()._router_tradeDefi.tradeDefi if ( tradeDefi?.maxSellVol && tradeDefi?.sellVol && sdk.toBig(tradeDefi.sellVol).gte(tradeDefi?.maxSellVol) ) { if ( sdk .toBig(tradeDefi?.maxSellVol ?? 0) .minus(tradeDefi.miniSellVol ?? 0) .toString() .startsWith('-') ) { setConfirmShowNoBalance(true) } else { setConfirmShowLimitBalance(true) const type = DeFiChgType.coinSell const tradeValue = getValuePrecisionThousand( sdk.toBig(tradeDefi?.maxSellVol).div('1e' + tokenMap[coinSellSymbol]?.decimals), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: true }, ).replaceAll(sdk.SEP, '') // @ts-ignore const oldTrade = (tradeDefi?.deFiCalcData[type] ?? {}) as unknown as T handleOnchange({ tradeData: { ...oldTrade, tradeValue, }, type, }) // handleOnchange() } } else { handleSubmit() } }, [tokenMap, coinSellSymbol, handleOnchange, handleSubmit]) const { btnStatus, onBtnClick, btnLabel: tradeMarketI18nKey, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback: onSubmitBtnClick, }) React.useEffect(() => { if ( market && market !== '' && // walletLayer2Status === SagaStatus.UNSET && isJoin !== undefined ) { setSymbol(() => { if (isJoin) { const [, coinBuySymbol, coinSellSymbol] = market.match(/(\w+)-(\w+)/i) ?? [] return { coinBuySymbol, coinSellSymbol } } else { const [, coinSellSymbol, coinBuySymbol] = market.match(/(\w+)-(\w+)/i) ?? [] return { coinBuySymbol, coinSellSymbol } } }) if (refreshRef.current) { // @ts-ignore refreshRef.current.firstElementChild.click() should15sRefresh(true) myLog('should15sRefresh refreshRef.current click only', market) } else { should15sRefresh(true) } } return () => { myLog('should15sRefresh cancel', market) resetTradeDefi() should15sRefresh.cancel() handleOnchange.cancel() } }, [isJoin, market]) const deFiWrapProps = React.useMemo(() => { const tradeDefi = store.getState()._router_tradeDefi.tradeDefi return { isStoB, refreshRef, onConfirm: sendRequest, disabled: !tradeDefi.deFiCalcData?.AtoB || (account.readyState === AccountStatus.ACTIVATED && !tradeDefi?.feeRaw), btnInfo: { label: tradeMarketI18nKey, params: {}, }, isLoading, switchStobEvent: (_isStoB: boolean | ((prevState: boolean) => boolean)) => { setIsStoB(_isStoB) }, onRefreshData: should15sRefresh, onSubmitClick: onBtnClick as () => void, onChangeEvent: handleOnchange, tokenAProps: {}, tokenBProps: {}, deFiCalcData: { ...tradeDefi.deFiCalcData, }, maxBuyVol: tradeDefi.defiBalances ? tradeDefi.defiBalances[coinBuySymbol] : undefined, maxSellVol: tradeDefi.maxSellVol, confirmShowLimitBalance, tokenSell: tokenMap[coinSellSymbol], tokenBuy: tokenMap[coinBuySymbol], btnStatus, accStatus: account.readyState, withdrawFeeBips: tradeDefi.maxFeeBips, apr: (isLeverageETH ? marketLeverageMap : marketMap)[market]?.apy, onAprDetail: () => { setShowETHStakingApr({ isShow: true, symbol: market, info: (isLeverageETH ? marketLeverageMap : marketMap)[market], }) }, } }, [ isStoB, refreshRef, sendRequest, account.readyState, tradeMarketI18nKey, isLoading, should15sRefresh, onBtnClick, handleOnchange, confirmShowLimitBalance, tokenMap, coinSellSymbol, coinBuySymbol, btnStatus, ]) // as ForceWithdrawProps; return { deFiWrapProps: deFiWrapProps as unknown as DeFiWrapProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useDeposit.ts ================================================ import React from 'react' import { AccountStep, DepositProps, SwitchData, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { AccountStatus, AddressError, coinbaseSmartWalletChains, CoinMap, IBData, L1_UPDATE, L1L2_NAME_DEFINED, MapChainId, myLog, SagaStatus, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, UIERROR_CODE, WalletMap, } from '@loopring-web/common-resources' import { connectProvides } from '@loopring-web/web3-provider' import * as sdk from '@loopring-web/loopring-sdk' import { ActionResultCode, BIGO, callSwitchChain, DepositCommands, depositServices, getContractTypeByNetwork, LoopringAPI, store, useAccount, useAddressCheck, useAllowances, useBtnStatus, useModalData, useSystem, useTokenMap, useWalletLayer1, useWalletLayer2, } from '../../index' import { useTranslation } from 'react-i18next' import { useOnChainInfo } from '../../stores/localStore/onchainHashInfo' import Web3 from 'web3' import { omit } from 'lodash' export const useDeposit = < T extends { toAddress?: string addressError?: { error: boolean; message?: string } } & IBData, I, >( isAllowInputToAddress = false, opts?: { token?: string | null; owner?: string | null }, ) => { const subject = React.useMemo(() => depositServices.onSocket(), []) const { tokenMap, totalCoinMap } = useTokenMap() const { account, status: accountStatus, updateAccount } = useAccount() const { walletLayer1, updateWalletLayer1, status: walletLayer1Status } = useWalletLayer1() const { updateWalletLayer2 } = useWalletLayer2() const { updateDepositHash } = useOnChainInfo() const { t } = useTranslation('common') const nodeTimer = React.useRef(-1) const [isToAddressEditable, setIsToAddressEditable] = React.useState(false) const { exchangeInfo, chainId, gasPrice, allowTrade, baseURL, status: systemStatus, app } = useSystem() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const [toInputAddress, setToInputAddress] = React.useState('') const { depositValue, updateDepositData, resetDepositData, activeAccountValue: { chargeFeeList }, } = useModalData() const { // address: toAddress, realAddr: realToAddress, setAddress: setToAddress, addrStatus: _toAddressStatus, isAddressCheckLoading: toIsAddressCheckLoading, } = useAddressCheck() const toAddressStatus = coinbaseSmartWalletChains.includes(defaultNetwork) && _toAddressStatus === AddressError.IsNotLoopringContract ? AddressError.NoError : _toAddressStatus const [loopringSmartWalletCheck, setLoopringSmartWalletCheck] = React.useState( { isLoopringSmartWallet: undefined as boolean | undefined, checkedAddress: undefined as string | undefined } ) React.useEffect(() => { if (realToAddress && realToAddress !== loopringSmartWalletCheck.checkedAddress) { setLoopringSmartWalletCheck({ checkedAddress: realToAddress, isLoopringSmartWallet: undefined }) getContractTypeByNetwork(realToAddress, MapChainId[defaultNetwork]).then((contractType) => { setLoopringSmartWalletCheck((state) => ({ ...state, isLoopringSmartWallet: contractType ? contractType.contractVersion !== '' : false })) }) } }, [realToAddress]) React.useEffect(() => { const depositValue = store.getState()._router_modalData.depositValue handlePanelEvent( { to: 'button', tradeData: depositValue as any, }, 'Tobutton', ) }, [realToAddress]) const { modals: { isShowDeposit: { symbol, isShow }, }, setShowDeposit, setShowAccount, } = useOpenModals() React.useEffect(() => { if(!isShow) { const oldValue = store.getState()._router_modalData.depositValue let newValue = { ...oldValue, tradeValue: undefined } updateDepositData({ ...newValue }) } }, [isShow]) const { btnStatus, btnInfo, enableBtn, // setLoadingBtn, disableBtn, setLabelAndParams, resetBtnInfo, } = useBtnStatus() const { allowanceInfo } = useAllowances({ owner: account.accAddress, symbol: depositValue.belong as string, }) const isNewAccount = [AccountStatus.NO_ACCOUNT, AccountStatus.NOT_ACTIVE].includes( account.readyState as any, ) const updateBtnStatus = React.useCallback(() => { resetBtnInfo() if ( realToAddress && (toAddressStatus as AddressError) === AddressError.NoError && depositValue?.toAddress?.trim() !== '' && depositValue.toAddress == realToAddress && depositValue.belong === allowanceInfo?.tokenInfo.symbol && depositValue?.tradeValue && allowanceInfo && sdk.toBig(walletLayer1?.ETH?.count ?? 0).gt(BIGO) && sdk.toBig(depositValue?.tradeValue).gt(BIGO) && sdk.toBig(depositValue?.tradeValue).lte(sdk.toBig(depositValue?.balance ?? '')) ) { myLog('walletLayer1?.ETH?.count', walletLayer1?.ETH?.count) const curValInWei = sdk .toBig(depositValue?.tradeValue) .times('1e' + allowanceInfo?.tokenInfo.decimals) if (allowanceInfo.needCheck && curValInWei.gt(allowanceInfo.allowance)) { myLog('!!---> set labelL1toL2NeedApprove!!!! belong:', depositValue.belong) setLabelAndParams('labelL1toL2NeedApprove', { symbol: depositValue.belong as string, }) } // NewAccountCheck const index = chargeFeeList?.findIndex(({ belong }) => belong === depositValue.belong) ?? -1 if ( isAllowInputToAddress || (isNewAccount && (index !== -1 || /dev|uat/gi.test(baseURL))) || !isNewAccount ) { enableBtn() return } } myLog('try to disable deposit btn!') if (sdk.toBig(walletLayer1?.ETH?.count ?? 0).eq(BIGO)) { setLabelAndParams('labelNOETH', {}) } disableBtn() }, [ resetBtnInfo, isAllowInputToAddress, realToAddress, toAddressStatus, depositValue.toAddress, depositValue.belong, depositValue?.tradeValue, depositValue?.balance, allowanceInfo, walletLayer1?.ETH?.count, disableBtn, chargeFeeList, isNewAccount, setLabelAndParams, enableBtn, // account?.readyState, opts?.owner, ]) React.useEffect(() => { updateBtnStatus() }, [ depositValue?.belong, depositValue?.tradeValue, depositValue?.balance, depositValue.toAddress, allowanceInfo?.tokenInfo.symbol, toAddressStatus, realToAddress, toIsAddressCheckLoading, walletLayer1?.ETH?.count, ]) const handlePanelEvent = React.useCallback( (data: SwitchData>, _switchType: 'Tomenu' | 'Tobutton') => { const oldValue = store.getState()._router_modalData.depositValue let newValue = { ...oldValue, } if (data.to === 'button') { if (walletLayer1 && data?.tradeData?.belong) { const walletInfo = walletLayer1[data.tradeData.belong] newValue = { ...newValue, ...data.tradeData, balance: walletInfo?.count, } } } // myLog('DepositData', {...newValue, toAddress: realToAddress}) updateDepositData({ ...newValue, toAddress: realToAddress }) return Promise.resolve() }, [setToAddress, updateDepositData, realToAddress, isToAddressEditable, walletLayer1], ) const handleAddressChange = (value?: string) => { const toAddress = store.getState()._router_modalData.depositValue.toAddress if (isToAddressEditable == false && opts?.owner && opts.owner !== '') { value = opts.owner } const makeForceFresh = (state: string, value: string) => { if (value === state) { sdk.sleep(0).then(() => { setToAddress(state) }) return '' } else { return value } } setToAddress((state) => { // myLog('address update setToAddress', state, value, realToAddress, toAddressStatus) if ( value && toIsAddressCheckLoading == false && realToAddress && realToAddress.startsWith('0x') && [value, toAddress].includes(realToAddress) ) { return makeForceFresh(state, value) } else if (value !== undefined) { setToInputAddress(value) return makeForceFresh(state, value) } else if (toInputAddress !== '') { return makeForceFresh(state, toInputAddress) } else if (toIsAddressCheckLoading == false) { return makeForceFresh(state, state) } else { return state } }) } const handleClear = React.useCallback(() => { if (isAllowInputToAddress && !isToAddressEditable) { return } handleAddressChange('') }, [isAllowInputToAddress, isToAddressEditable]) const walletLayer1Callback = React.useCallback(() => { // const { // _router_modalData: { depositValue }, // } = store.getState() let updateData = {} // = { ...depositValue } if (nodeTimer.current !== -1) { clearTimeout(nodeTimer.current) } if (!depositValue.belong && walletLayer1) { const keys = Reflect.ownKeys(walletLayer1) updateData = { belong: 'ETH', tradeValue: undefined, balance: 0, } for (var key in keys) { const keyVal = keys[key] as any const walletInfo = walletLayer1[keyVal] if (sdk.toBig(walletInfo?.count ?? 0).gt(0)) { updateData = { // ...updateData, belong: keyVal as any, tradeValue: undefined, balance: walletInfo?.count, } break } } } else if (depositValue.belong && walletLayer1) { updateData = { // ...updateData, belong: depositValue.belong, balance: walletLayer1[depositValue.belong]?.count ?? 0, } } if (isAllowInputToAddress && !(opts?.owner && opts?.owner?.startsWith('0x'))) { setIsToAddressEditable(true) } handlePanelEvent( { to: 'button', tradeData: updateData as any, }, 'Tobutton', ) handleAddressChange() }, [ depositValue.belong, depositValue.tradeValue, opts?.token, opts?.owner, symbol, walletLayer1, isAllowInputToAddress, handlePanelEvent, account.accAddress, handleClear, ]) React.useEffect(() => { const { walletLayer1 } = store.getState() if (walletLayer1Status == SagaStatus.UNSET) { if ( walletLayer1.error // && // ![AccountStatus.UN_CONNECT, AccountStatus.ERROR_NETWORK, 'unknown'].includes( // account.readyState, // ) ) { updateWalletLayer1() } else { walletLayer1Callback() } } }, [walletLayer1Status]) const init = () => { if (chainId === defaultNetwork && baseURL) { let tradeData: any = { belong: opts?.token?.toUpperCase() ?? symbol, tradeValue: !isShow ? undefined : depositValue.tradeValue, } updateWalletLayer1() handleAddressChange() handlePanelEvent( { to: 'button', tradeData: { ...tradeData, } as any, }, 'Tobutton', ) } } React.useEffect(() => { // myLog('accountStatus,LoopringAPI?.__chainId__', LoopringAPI?.__chainId__, accountStatus) if (accountStatus === SagaStatus.UNSET && systemStatus === SagaStatus.UNSET) { init() if (isShow || isAllowInputToAddress) { nodeTimer.current = setTimeout(() => { updateWalletLayer1() }, L1_UPDATE) } } return () => { if (nodeTimer.current !== -1) { clearTimeout(nodeTimer.current) } } }, [accountStatus, isShow, isToAddressEditable, systemStatus]) const handleDeposit = React.useCallback( async (inputValue: any) => { myLog('handleDeposit:', inputValue) const { readyState, connectName } = account let result = { code: ActionResultCode.NoError } const { toAddress } = store.getState()._router_modalData.depositValue try { if ( readyState !== AccountStatus.UN_CONNECT && inputValue.tradeValue && tokenMap && exchangeInfo?.exchangeAddress && connectProvides.usedWeb3 && LoopringAPI.exchangeAPI && toAddress ) { const tokenInfo = tokenMap[inputValue.belong] const gasLimit = '0x' + parseInt(tokenInfo.gasAmounts.deposit).toString(16) const fee = 0 const isMetaMask = true const realGasPrice = gasPrice ?? 30 const _chainId = await connectProvides?.usedWeb3?.eth?.getChainId() //chainId === 'unknown' ? sdk.ChainId.MAINNET : chainId await callSwitchChain(_chainId) let nonce = 0 let nonceInit = false if (allowanceInfo?.needCheck) { const curValInWei = sdk.toBig(inputValue.tradeValue).times('1e' + tokenInfo.decimals) if (curValInWei.gt(allowanceInfo.allowance)) { myLog(curValInWei, allowanceInfo.allowance, ' need approveMax!') setShowAccount({ isShow: true, step: AccountStep.Deposit_Approve_WaitForAuth, info: { isAllowInputToAddress, }, }) nonce = await sdk.getNonce( connectProvides.usedWeb3 as unknown as Web3, account.accAddress, ) nonceInit = true try { await sdk.approveMax( connectProvides.usedWeb3, account.accAddress, tokenInfo.address, exchangeInfo?.depositAddress, realGasPrice, gasLimit, _chainId, '0x' + nonce.toString(16), isMetaMask, ) nonce += 1 } catch (error: any) { if (error instanceof Error) { throw { // Pull all enumerable properties, supporting properties on custom Errors ...error, // Explicitly pull Error's non-enumerable properties message: error.message, stack: error.stack, type: 'ApproveToken', } } else { throw { ...(error as any), type: 'ApproveToken', } } } } else { myLog("allowance is enough! don't need approveMax!") } } setShowAccount({ isShow: true, step: AccountStep.Deposit_WaitForAuth, info: { to: isAllowInputToAddress ? realToAddress : null, isAllowInputToAddress, }, }) if (!nonceInit) { nonce = await sdk.getNonce( connectProvides.usedWeb3 as unknown as Web3, account.accAddress, ) } myLog('before deposit:', chainId, connectName, isMetaMask) // const realChainId = chainId === 'unknown' ? 1 : chainId let response try { const exchangeInfoResponse = await LoopringAPI.exchangeAPI.getExchangeInfo() // if chaindId from response is not equal to the chainId from wallet, // or exchangeAddress on redux is not same as from response, throw error if ( exchangeInfoResponse.exchangeInfo.chainId !== _chainId || exchangeInfoResponse.exchangeInfo.exchangeAddress.toLowerCase() !== exchangeInfo.exchangeAddress.toLowerCase() ) { throw { message: 'data not matched', } } response = await sdk.deposit( connectProvides.usedWeb3, account.accAddress, exchangeInfo.exchangeAddress, tokenInfo, inputValue.tradeValue, fee, realGasPrice, gasLimit, _chainId, '0x' + nonce.toString(16), isMetaMask, toAddress, ) } catch (error) { if (error instanceof Error) { throw { ...error, message: error.message, stack: error.stack, type: 'Deposit', } } else { throw { ...(error as any), type: 'Deposit', } } } myLog('response:', response) if (response) { // Close Deposit panel... setShowDeposit({ isShow: false }) resetDepositData() updateWalletLayer1() setShowAccount({ isShow: true, info: { to: isAllowInputToAddress ? realToAddress : null, symbol: tokenInfo.symbol, value: inputValue.tradeValue, hash: response.result, isAllowInputToAddress, isNewAccount, }, step: AccountStep.Deposit_Submit, }) updateDepositHash(response.result, account.accAddress, undefined, { symbol: tokenInfo.symbol, type: 'Deposit', value: inputValue.tradeValue, }) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.Deposit_Submit ) { setShowAccount({ isShow: false }) } if (account.readyState === 'NO_ACCOUNT') { const timer = setInterval(() => { LoopringAPI.exchangeAPI?.getAccount({ owner: account.accAddress, }).then(acc => { if (acc.accInfo.accountId > 0) { updateAccount({ _accountIdNotActive : acc.accInfo.accountId, readyState: 'NOT_ACTIVE', }) updateWalletLayer2() clearInterval(timer) } }) }, 5 * 1000); } } else { throw { code: UIERROR_CODE.ERROR_NO_RESPONSE } } } else { throw { code: UIERROR_CODE.DATA_NOT_READY } } } catch (e) { const { type, ..._error } = (e as any)?.message ? (e as any) : { type: '' } const error = LoopringAPI?.exchangeAPI?.genErr(_error as any) ?? { code: UIERROR_CODE.DATA_NOT_READY, } const code = sdk.checkErrorInfo(error, true) switch (code) { case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: if (type === 'ApproveToken') { setShowAccount({ isShow: true, step: AccountStep.Deposit_Approve_Denied, info: { isAllowInputToAddress, }, }) } else { setShowAccount({ isShow: true, step: AccountStep.Deposit_Denied, info: { isAllowInputToAddress, }, }) } break default: setShowAccount({ isShow: true, step: AccountStep.Deposit_Failed, info: { isAllowInputToAddress, }, error: { ..._error, ...error, code: (e as any)?.code ?? UIERROR_CODE.UNKNOWN, }, }) resetDepositData() break } updateWalletLayer1() } return result }, [ isNewAccount, account, tokenMap, exchangeInfo?.exchangeAddress, exchangeInfo?.depositAddress, isAllowInputToAddress, setShowAccount, gasPrice, chainId, allowanceInfo?.needCheck, allowanceInfo?.allowance, realToAddress, updateWalletLayer1, resetDepositData, updateDepositHash, ], ) const onDepositClick = React.useCallback(async () => { // setLoadingBtn(); const depositValue = store.getState()._router_modalData.depositValue myLog('onDepositClick depositValue:', depositValue) if (depositValue && depositValue.belong) { // enableBtn(); await handleDeposit(depositValue as T) } }, [depositValue, handleDeposit]) React.useEffect(() => { const subscription = subject.subscribe((props) => { myLog('subscription Deposit DepsitERC20') switch (props.status) { case DepositCommands.DepsitERC20: onDepositClick() break default: break } }) return () => { subscription.unsubscribe() } }, [subject, accountStatus, systemStatus]) const title = app === 'earn' ? 'labelDeposit' : account.readyState === AccountStatus.NO_ACCOUNT ? t('labelDepositTitleAndActive', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol }) : t('labelDepositTitle', { l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol }) const isTaiko = [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const depositProps: DepositProps = { btnInfo, isNewAccount, title, type: TRADE_TYPE.TOKEN, handleClear, allowTrade, isAllowInputToAddress, chargeFeeTokenList: chargeFeeList ?? [], tradeData: depositValue as T, coinMap: totalCoinMap as CoinMap, walletMap: (isTaiko ? omit(walletLayer1, ['TAIKO']) : walletLayer1) as WalletMap, depositBtnStatus: btnStatus, handlePanelEvent, handleAddressChange, onDepositClick, toAddressStatus, toIsAddressCheckLoading, toAddress: toInputAddress, realToAddress: depositValue.toAddress, isToAddressEditable, isLoopringSmartWallet: loopringSmartWalletCheck.isLoopringSmartWallet, onClose() { setShowDeposit({ isShow: false }) }, } return { depositProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useDualEdit.ts ================================================ import React from 'react' import { DualDetailType, ToastType, useOpenModals, useSettings } from '@loopring-web/component-lib' import { AccountStatus, CustomErrorWithCode, myLog, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, TradeBtnStatus, } from '@loopring-web/common-resources' import BigNumber from 'bignumber.js' import { DAYS, getTimestampDaysLater, useSubmitBtn, useToast, useTokenMap, useTradeDual, } from '@loopring-web/core' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI, store, useSystem } from '../../index' import { useTranslation } from 'react-i18next' export const useDualEdit = < T extends { isRenew: boolean renewTargetPrice?: string renewDuration?: number }, R extends DualDetailType, >({ refresh, }: { refresh?: (item: R, dontCloseModal: boolean) => void }) => { const { exchangeInfo } = useSystem() const { setShowAccount } = useOpenModals() const { t } = useTranslation() const { setShowDual } = useOpenModals() const { editDual: { dualViewInfo }, updateEditDual, resetEditDual, } = useTradeDual() const { dualAuto } = useSettings() const { toastOpen, setToastOpen, closeToast } = useToast() const [isLoading, setIsLoading] = React.useState(false) const { tokenMap, idIndex } = useTokenMap() const [tradeData, setTradeData] = React.useState(() => { // @ts-ignore return { isRenew: dualViewInfo?.__raw__?.order?.dualReinvestInfo?.isRecursive ?? false, renewDuration: dualViewInfo?.__raw__?.order?.dualReinvestInfo?.isRecursive || dualAuto.day === 'auto' ? dualViewInfo?.__raw__?.order?.dualReinvestInfo?.maxDuration / 86400000 : dualAuto.day, renewTargetPrice: dualViewInfo?.__raw__?.order.dualReinvestInfo.newStrike, } as T }) React.useEffect(() => { const { dualViewInfo } = store.getState()._router_tradeDual.editDual setTradeData({ // @ts-ignore isRenew: dualViewInfo?.__raw__?.order?.dualReinvestInfo?.isRecursive ?? false, renewDuration: dualViewInfo?.__raw__?.order?.dualReinvestInfo?.maxDuration / 86400000, renewTargetPrice: dualViewInfo?.__raw__?.order.dualReinvestInfo.newStrike, }) }, [dualViewInfo?.__raw__?.order?.hash]) const handleOnchange = ({ tradeData }: { tradeData: T }) => { setTradeData(tradeData) const editDual = store.getState()._router_tradeDual.editDual updateEditDual({ ...editDual, tradeData, }) } const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const account = store.getState().account const { maxDuration, newStrike } = dualViewInfo?.__raw__?.order?.dualReinvestInfo ?? {} if (account.readyState === AccountStatus.ACTIVATED && tradeData) { if (!tradeData.isRenew) { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: 'labelConfirm', } } else if ( tradeData.isRenew && tradeData.renewDuration && tradeData.renewTargetPrice && (tradeData.renewDuration !== (maxDuration ?? 0) / 86400000 || !sdk.toBig(tradeData.renewTargetPrice).eq(newStrike)) ) { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '', } } else { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: '' } // label: ''} } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } }, [dualViewInfo, tradeData.isRenew, tradeData.renewDuration, tradeData.renewTargetPrice]) const onSubmitBtnClick = React.useCallback( async (props?: any) => { const editDual = store.getState()._router_tradeDual.editDual const account = store.getState().account let { tradeData: _tradeData } = editDual _tradeData = { ...tradeData, ..._tradeData } const tradeDual = editDual?.dualViewInfo?.__raw__?.order const dualViewInfo = editDual?.dualViewInfo try { setIsLoading(true) if ( LoopringAPI.userAPI && LoopringAPI.defiAPI && tradeDual && dualViewInfo && exchangeInfo ) { let request: any = { currentDualHash: tradeDual.hash, isRecursive: tradeDual.dualReinvestInfo.isRecursive, maxDuration: tradeDual.dualReinvestInfo.maxDuration, newStrike: tradeDual.dualReinvestInfo.newStrike, accountId: account.accountId, } if (!_tradeData.isRenew) { request.isRecursive = false } else { request.isRecursive = true request.maxDuration = tradeDual.dualReinvestInfo.maxDuration && tradeDual.dualReinvestInfo.maxDuration !== 0 ? tradeDual.dualReinvestInfo.maxDuration : 60*60*1000*24 if ( _tradeData.renewDuration && _tradeData.renewDuration !== (request.maxDuration ?? 0) / 86400000 ) { request.maxDuration = Number(_tradeData.renewDuration) * 86400000 } if ( _tradeData.renewTargetPrice && tradeDual?.tokenInfoOrigin?.storageId !== undefined && !sdk.toBig(_tradeData.renewTargetPrice).eq(tradeDual.dualReinvestInfo.newStrike) ) { const req: sdk.GetNextStorageIdRequest = { accountId: account.accountId, sellTokenId: tradeDual.tokenInfoOrigin.tokenIn ?? 0, } const storageId = await LoopringAPI.userAPI.getNextStorageId(req, account.apiKey) request.newStrike = _tradeData.renewTargetPrice const [, , base, quote] = (tradeDual.tokenInfoOrigin.market ?? 'dual-').match(/(dual-)?(\w+)-(\w+)/i) ?? [] const buyToken = tradeDual.dualType === sdk.DUAL_TYPE.DUAL_BASE ? tokenMap[quote] : tokenMap[base] //tokenMap[idIndex[tradeDual.tokenInfoOrigin.tokenOut]] const sellToken = tokenMap[idIndex[tradeDual.tokenInfoOrigin.tokenIn]] request.newOrder = { exchange: exchangeInfo.exchangeAddress, storageId: storageId.orderId, accountId: account.accountId, sellToken: { tokenId: tradeDual.tokenInfoOrigin.tokenIn, //tradeDual.tokenInfoOrigin.tokenIn ?? 0, volume: tradeDual.tokenInfoOrigin.amountIn, }, buyToken: { tokenId: buyToken.tokenId, //tradeDual.tokenInfoOrigin.tokenOut ?? 0, ...(tradeDual.dualType === sdk.DUAL_TYPE.DUAL_BASE ? { volume: sdk .toBig( sdk .toBig(tradeDual.tokenInfoOrigin.amountIn) .div('1e' + sellToken.decimals) .times(request.newStrike) .toFixed(buyToken.precision, BigNumber.ROUND_CEIL), ) .times('1e' + buyToken.decimals) .toString(), } : { volume: sdk .toBig( sdk .toBig( sdk .toBig(tradeDual.tokenInfoOrigin.amountIn) .div('1e' + sellToken.decimals), ) .div(request.newStrike) .toFixed(buyToken.precision, BigNumber.ROUND_CEIL), ) .times('1e' + buyToken.decimals) .toFixed(0, BigNumber.ROUND_FLOOR), }), }, validUntil: getTimestampDaysLater(DAYS * 12), maxFeeBips: 5, fillAmountBOrS: false, fee: tradeDual.feeVol ?? '0', baseProfit: tradeDual.dualReinvestInfo.profit, settleRatio: tradeDual?.settleRatio?.toString()?.replaceAll(sdk.SEP, ''), expireTime: tradeDual.expireTime, } } } myLog('DualTrade request:', request) const response = await LoopringAPI.defiAPI.editDual( request, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = /DUAL_PRODUCT_STOPPED/gi.test( (response as sdk.RESULT_INFO).message ?? '', ) ? SDK_ERROR_MAP_TO_UI[115003] : SDK_ERROR_MAP_TO_UI[700001] throw new CustomErrorWithCode({ ...response, ...errorItem, } as any) } else { setShowDual({ isShow: false, dualInfo: undefined }) setToastOpen({ open: true, type: ToastType.success, content: t('labelDualEditSuccess'), }) // await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) } refresh && refresh( { ...dualViewInfo, ...dualViewInfo.__raw__.order, __raw__: { order: { ...dualViewInfo.__raw__.order, dualReinvestInfo: { ...dualViewInfo?.__raw__?.dualReinvestInfo, newStrike: request.newStrike, maxDuration: request.maxDuration, isRecursive: request.isRecursive, }, }, }, } as any, props && props.dontCloseModal, ) } else { throw new Error('api not ready') } } catch (reason) { if (!_tradeData.isRenew) { resetEditDual() } setToastOpen({ open: true, type: ToastType.error, content: t('labelDualEditFailed'), }) } setIsLoading(false) }, [ exchangeInfo, setShowAccount, setShowDual, tradeData.isRenew, tradeData.renewDuration, tradeData.renewTargetPrice, ], ) const { btnStatus, onBtnClick, btnLabel: tradeMarketI18nKey, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback: onSubmitBtnClick, }) return { editDualTrade: tradeData, editDualBtnInfo: { label: tradeMarketI18nKey, params: {}, }, onEditDualClick: onBtnClick, handleOnchange, editDualBtnStatus: btnStatus, dualToastOpen: toastOpen, closeDualToast: closeToast, setDualTradeData: setTradeData, } } ================================================ FILE: packages/core/src/hooks/useractions/useDualTrade.ts ================================================ import React from 'react' import { AccountStep, DualChgData, DualWrapProps, useOpenModals, useSettings, useToggle, } from '@loopring-web/component-lib' import { AccountStatus, CustomErrorWithCode, DualCalcData, DualTrade, DualViewInfo, getValuePrecisionThousand, globalSetup, myLog, SagaStatus, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, TradeBtnStatus, } from '@loopring-web/common-resources' import { DAYS, getTimestampDaysLater, makeDualViewItem, makeWalletLayer2, TradeDual, useDualMap, useSubmitBtn, useToast, useWalletLayer2Socket, walletLayer2Service, } from '@loopring-web/core' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI, store, useAccount, useSystem, useTokenMap } from '../../index' import { confirmation, useTradeDual } from '../../stores' export const useDualTrade = < T extends DualTrade, I, ACD extends DualCalcData, R extends DualViewInfo, >() => { const refreshRef = React.useRef() const { dualAuto } = useSettings() const { confirmation: { showAutoDefault }, setShowAutoDefault } = confirmation.useConfirmation() const { exchangeInfo, allowTrade } = useSystem() const { tokenMap, marketArray } = useTokenMap() const { setShowAccount } = useOpenModals() const { marketMap: dualMarketMap } = useDualMap() const { account, status: accountStatus } = useAccount() const { setShowDual } = useOpenModals() const { toastOpen, closeToast } = useToast() const { modals: { isShowDual }, setShowSupport, setShowTradeIsFrozen, } = useOpenModals() const { tradeDual, updateTradeDual, resetTradeDual } = useTradeDual() const [serverUpdate, setServerUpdate] = React.useState(false) const { toggle: { dualInvest, dual_reinvest }, } = useToggle() const [[coinSellSymbol, coinBuySymbol], setSellBuySymbol] = React.useState< [string | undefined, string | undefined] >([undefined, undefined]) // }); const [isLoading, setIsLoading] = React.useState(false) const [productInfo, setProductInfo] = React.useState(undefined as any) const refreshDual = React.useCallback( ({ dualInfo = productInfo, tradeData, balance, index = productInfo?.__raw__.index, }: { dualInfo?: R tradeData?: T balance?: { [key: string]: sdk.DualBalance } index?: sdk.DualIndex }) => { let walletMap: any = {} let { sellSymbol, buySymbol } = isShowDual.dualInfo as R // feeVol: string | undefined = undefined; let { info } = dualInfo.__raw__ const { tradeDual: { coinSell: _coinSell }, } = store.getState()._router_tradeDual let _updateInfo: Partial> = { dualViewInfo: _.cloneDeep(dualInfo), } if (productInfo?.productId === dualInfo.productId) { _updateInfo = { ...(tradeDual as TradeDual), ..._updateInfo, } } else { // resetTradeDual(); // info = _updateInfo.dualViewInfo.__raw__.info; } if (index && _updateInfo.dualViewInfo) { _updateInfo.dualViewInfo.__raw__.index = index } else if (!_updateInfo.dualViewInfo) { return } if (balance) { _updateInfo.balance = balance } const [baseSymbol, quoteSymbol] = [sellSymbol, buySymbol] const dualMarket = dualMarketMap[`DUAL-${sellSymbol}-${buySymbol}`] ?? dualMarketMap[`DUAL-${buySymbol}-${sellSymbol}`] setSellBuySymbol([baseSymbol, quoteSymbol]) let coinSell: any = tradeData ?? {} if (tradeData && tradeData.belong === baseSymbol) { // coinSell= tradeData; } else if (_coinSell?.belong === baseSymbol) { coinSell = { ..._coinSell, } as unknown as T } else { const isRenew = _updateInfo?.coinSell?.isRenew ?? (dual_reinvest?.enable && dualAuto.auto) ? true : false let calc = (_updateInfo.dualViewInfo.expireTime - Date.now()) / 86400000 calc = calc < 1 ? Math.ceil(calc) : Math.floor(calc) const renewDuration = Number( tradeDual?.coinSell?.renewDuration ?? (dualAuto.day === 'auto' ? calc : dualAuto.day), ) coinSell = { balance: _updateInfo?.coinSell?.balance ?? 0, tradeValue: _updateInfo?.coinSell?.tradeValue ?? undefined, belong: baseSymbol, isRenew, renewDuration, } as unknown as T } const existedMarket = sdk.getExistedMarket(marketArray, baseSymbol, quoteSymbol) if (account.readyState == AccountStatus.ACTIVATED && existedMarket) { walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap coinSell.balance = walletMap[baseSymbol]?.count } const dualViewInfo = makeDualViewItem( info, index ?? ({} as any), dualInfo.__raw__.rule, sellSymbol, buySymbol, dualMarket, ) if (_updateInfo.balance) { const calDualValue: sdk.CalDualResult = sdk.calcDual({ ...dualInfo.__raw__, balance: _updateInfo.balance, // feeVol, sellToken: tokenMap[baseSymbol], buyToken: tokenMap[quoteSymbol], sellAmount: coinSell.tradeValue?.toString() ?? undefined, dualMarket, }) _updateInfo = { ..._updateInfo, ...(calDualValue as TradeDual), } } updateTradeDual({ ..._updateInfo, dualViewInfo, coinSell }) }, [ account.readyState, dualMarketMap, isShowDual.dualInfo, marketArray, productInfo, tokenMap, tradeDual, updateTradeDual, dualAuto, ], ) const handleOnchange = ({ tradeData }: DualChgData) => { refreshDual({ tradeData }) } const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const account = store.getState().account const tradeDual = store.getState()._router_tradeDual.tradeDual if (account.readyState === AccountStatus.ACTIVATED && coinSellSymbol && tradeDual?.coinSell) { const sellToken = tokenMap[tradeDual?.coinSell.belong] const sellExceed = sdk .toBig(tradeDual.coinSell?.tradeValue ?? 0) .gt(tradeDual?.coinSell?.balance ?? 0) // myLog("sellExceed", sellExceed, tradeDual.sellVol, tradeDual); if ( tradeDual?.sellVol === undefined || sdk.toBig(tradeDual?.sellVol).lte(0) || tradeDual?.maxFeeBips === undefined || tradeDual?.maxFeeBips === 0 ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelEnterAmount', } } else if ( sdk .toBig(tradeDual?.sellVol ?? 0) .minus(tradeDual?.miniSellVol ?? 0) .lt(0) ) { const sellMinVal = getValuePrecisionThousand( sdk.toBig(tradeDual?.miniSellVol).div('1e' + sellToken?.decimals), sellToken.precision, sellToken.precision, sellToken.precision, false, { floor: false, isAbbreviate: true }, ) const mimOrderSize = sellMinVal + ' ' + sellToken.symbol return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDualMin| ${mimOrderSize}`, } } else if (sellExceed) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDualNoEnough| ${coinSellSymbol}`, } } else if ( tradeDual?.maxSellAmount && tradeDual?.sellVol && sellToken && sdk.toBig(tradeDual?.coinSell?.tradeValue ?? 0).gte(tradeDual?.maxSellAmount) ) { const sellMaxVal = getValuePrecisionThousand( tradeDual?.maxSellAmount, sellToken.precision, sellToken.precision, sellToken.precision, false, { floor: false, isAbbreviate: true }, ) const maxOrderSize = sellMaxVal + ' ' + sellToken.symbol return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDualMax| ${maxOrderSize}`, } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } // label: ''} } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } }, [coinSellSymbol, tokenMap]) const should15sRefresh = _.debounce(async (clearTrade: boolean = false) => { if (productInfo && coinSellSymbol && coinBuySymbol && LoopringAPI.defiAPI) { if (clearTrade) { setIsLoading(true) } const dualMarket = dualMarketMap[`DUAL-${coinSellSymbol}-${coinBuySymbol}`] ?? dualMarketMap[`DUAL-${coinBuySymbol}-${coinSellSymbol}`] Promise.all([ LoopringAPI.defiAPI?.getDualIndex({ baseSymbol: productInfo.__raw__.info.base, quoteSymbol: dualMarket.quoteAlias, }), LoopringAPI.defiAPI?.getDualPrices({ baseSymbol: productInfo.__raw__.info.base, productIds: productInfo.productId, }), LoopringAPI.defiAPI?.getDualBalance(), ]) .then(([dualIndexResponse, dualPriceResponse, dualBalanceResponse]) => { const { tradeDual: { dualViewInfo }, } = store.getState()._router_tradeDual let dualInfo: R = _.cloneDeep(dualViewInfo) as R let balance = undefined, index = undefined if ( (dualIndexResponse as sdk.RESULT_INFO).code || (dualIndexResponse as sdk.RESULT_INFO).message ) { } else { index = dualIndexResponse.dualPrice } if ( (dualPriceResponse as sdk.RESULT_INFO).code || (dualPriceResponse as sdk.RESULT_INFO).message ) { } if (dualInfo?.__raw__?.info && dualPriceResponse.infos) { dualInfo.__raw__.info = { ...dualInfo.__raw__.info, ...dualPriceResponse.infos[0], } } if ( (dualBalanceResponse as sdk.RESULT_INFO).code || (dualBalanceResponse as sdk.RESULT_INFO).message ) { } else { balance = dualBalanceResponse.raw_data.reduce((prev: any, item: any) => { prev[item.coin] = item return prev }, {} as any) } refreshDual({ dualInfo, balance, index }) setIsLoading(false) }) .catch((error) => { console.log(error) }) } }, globalSetup.wait) const refreshClick = React.useCallback(() => { if (refreshRef.current && tradeDual) { //@ts-ignore refreshRef.current.firstElementChild.click() myLog('should15sRefresh refreshRef.current click only') should15sRefresh(true) } }, [tradeDual]) React.useEffect(() => { if (isShowDual?.isShow && isShowDual?.dualInfo?.__raw__) { setProductInfo(isShowDual.dualInfo as R) refreshDual({ dualInfo: isShowDual.dualInfo as R }) } else { resetTradeDual() } refreshClick() return () => { myLog('should15sRefresh cancel') resetTradeDual() should15sRefresh.cancel() } }, [isShowDual, refreshRef.current]) const walletLayer2Callback = React.useCallback(async () => { const { tradeDual: { coinSell }, } = store.getState()._router_tradeDual if (coinSell) { if (account.readyState === AccountStatus.ACTIVATED) { refreshDual({ tradeData: { ...coinSell } as T }) } else { refreshDual({ tradeData: { ...coinSell, tradeValue: undefined } as T }) } } }, [account.readyState, refreshDual]) useWalletLayer2Socket({ walletLayer2Callback }) const sendRequest = React.useCallback(async () => { const tradeDual = store.getState()._router_tradeDual.tradeDual const account = store.getState().account try { setIsLoading(true) if ( LoopringAPI.userAPI && LoopringAPI.defiAPI && tradeDual && tradeDual.coinSell?.belong && tradeDual.maxFeeBips && exchangeInfo ) { const sellToken = tokenMap[tradeDual.coinSell?.belong] const req: sdk.GetNextStorageIdRequest = { accountId: account.accountId, sellTokenId: sellToken?.tokenId ?? 0, } const storageId = await LoopringAPI.userAPI.getNextStorageId(req, account.apiKey) if ((storageId as sdk.RESULT_INFO).code || (storageId as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[700001] throw new CustomErrorWithCode({ ...storageId, ...errorItem, } as any) } const { dualType, productId, profit, // dualPrice: { dualBid }, } = tradeDual.dualViewInfo.__raw__.info const isRenew = tradeDual.coinSell?.isRenew ? { isRecursive: true, maxRecurseProductDuration: Number(tradeDual?.coinSell?.renewDuration ?? 7) * 86400000, } : {} // myLog("fee", tradeDual.feeVol); const request: sdk.DualOrderRequest = { clientOrderId: '', exchange: exchangeInfo.exchangeAddress, storageId: storageId.orderId, accountId: account.accountId, sellToken: { tokenId: sellToken?.tokenId ?? 0, volume: tradeDual.sellVol, }, buyToken: dualType === sdk.DUAL_TYPE.DUAL_BASE ? { tokenId: tokenMap[tradeDual.greaterEarnTokenSymbol]?.tokenId ?? 0, volume: tradeDual.greaterEarnVol, } : { tokenId: tokenMap[tradeDual.lessEarnTokenSymbol]?.tokenId ?? 0, volume: tradeDual.lessEarnVol, }, validUntil: getTimestampDaysLater(DAYS * 12), maxFeeBips: tradeDual.maxFeeBips, fillAmountBOrS: false, fee: tradeDual.feeVol ?? '0', baseProfit: profit, productId, settleRatio: tradeDual.dualViewInfo.settleRatio.replaceAll(sdk.SEP, ''), //sdk.toBig(tradeDual.dualViewInfo.settleRatio).f, expireTime: tradeDual.dualViewInfo.expireTime, ...isRenew, } myLog('DualTrade request:', request) const response = await LoopringAPI.defiAPI.orderDual( request, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = /DUAL_PRODUCT_STOPPED/gi.test( (response as sdk.RESULT_INFO).message ?? '', ) ? SDK_ERROR_MAP_TO_UI[115003] : SDK_ERROR_MAP_TO_UI[700001] throw new CustomErrorWithCode({ ...response, ...errorItem, } as any) } else { setShowDual({ isShow: false, dualInfo: undefined }) setShowAccount({ isShow: true, step: AccountStep.Dual_Success, info: { symbol: sellToken.symbol, value: tradeDual.coinSell.tradeValue, }, }) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.Dual_Success ) { setShowAccount({ isShow: false }) } } } else { throw new Error('api not ready') } walletLayer2Service.sendUserUpdate() } catch (reason) { setShowAccount({ isShow: true, step: AccountStep.Dual_Failed, error: reason as sdk.RESULT_INFO, info: { symbol: tradeDual?.coinSell?.belong, }, }) } finally { should15sRefresh(true) } }, [exchangeInfo, setShowAccount, setShowDual, tokenMap]) // const isNoBalance = ; const onSubmitBtnClick = React.useCallback(async () => { const { tradeDual } = store.getState()._router_tradeDual if ( (account.readyState === AccountStatus.ACTIVATED && tokenMap && exchangeInfo && account.eddsaKey?.sk, tradeDual.sellVol) ) { if (!allowTrade.defiInvest.enable) { setShowSupport({ isShow: true }) } else if (!dualInvest.enable) { setShowTradeIsFrozen({ isShow: true, type: 'DualInvest' }) } else { sendRequest() } } else { return false } }, [tokenMap, coinSellSymbol]) React.useEffect(() => { if (accountStatus === SagaStatus.UNSET) { walletLayer2Callback() } }, [accountStatus]) const { btnStatus, onBtnClick, btnLabel: tradeMarketI18nKey, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback: onSubmitBtnClick, }) const dualTradeProps: DualWrapProps = { refreshRef, disabled: false, btnInfo: { label: tradeMarketI18nKey, params: {}, }, toggle: dual_reinvest, isLoading, tokenMap: tokenMap as any, onRefreshData: should15sRefresh, onSubmitClick: onBtnClick as () => void, onChangeEvent: handleOnchange, tokenSellProps: {}, dualCalcData: tradeDual as ACD, // maxSellVol: tradeDual.maxSellVol, tokenSell: tokenMap[coinSellSymbol ?? ''], btnStatus, accStatus: account.readyState as AccountStatus, showAutoDefault, setShowAutoDefault, } // as ForceWithdrawProps; return { dualTradeProps, serverUpdate, setServerUpdate, dualToastOpen: toastOpen, closeDualToast: closeToast, } } ================================================ FILE: packages/core/src/hooks/useractions/useEditCollection.tsx ================================================ import { CollectionMeta, ErrorType, IPFS_HEAD_URL, myLog, NFTSubRouter, RouterPath, SDK_ERROR_MAP_TO_UI, UIERROR_CODE, } from '@loopring-web/common-resources' import { BigNumber } from 'bignumber.js' import React from 'react' import { getIPFSString, ipfsService, LoopringAPI, store, useAccount, useBtnStatus, useIPFS, useModalData, useSystem, useToast, useWalletL2Collection, } from '../../index' import { IpfsFile, ToastType, useToggle } from '@loopring-web/component-lib' import { useHistory, useRouteMatch } from 'react-router-dom' import * as sdk from '@loopring-web/loopring-sdk' import { AddResult } from 'ipfs-core-types/src/root' import { useTranslation } from 'react-i18next' // const enum MINT_VIEW_STEP { // METADATA, // MINT_CONFIRM, // } BigNumber.config({ EXPONENTIAL_AT: 100 }) export const useEditCollection = ({ isEdit = false, type, }: { isEdit?: boolean type: 'addCollection' | 'editCollection' | 'addLegacyCollection' }) => { const { toggle: { collectionNFT }, } = useToggle() const { toastOpen: collectionToastOpen, setToastOpen: setCollectionToastOpen, closeToast: collectionToastClose, } = useToast() let match: any = useRouteMatch('/nft/:type?/:tokenAddress?') const { t } = useTranslation('common') const [disabled, _setDisabled] = React.useState(!collectionNFT.enable) const { collectionValue, updateCollectionData, resetCollectionData } = useModalData() const [collectionOldValue] = React.useState( isEdit ? ({ ...collectionValue, } as T) : undefined, ) const { baseURL, chainId } = useSystem() const { updateWalletL2Collection } = useWalletL2Collection() const history = useHistory() const keysEditInit = (collection: Partial = {}) => { // const req = Promise.all([ // fetch(getIPFSString(collection.banner, baseURL), { // method: "HEAD", // }), // fetch(getIPFSString(collection.tileUri, baseURL), { // method: "HEAD", // }), // fetch(getIPFSString(collection.avatar, baseURL), { // method: "HEAD", // }), // ]).then([]); // tokenInfo.__mediaType__ = getMediaType( // req?.headers?.get("content-type") ?? "" // ); return { banner: collection.banner ? ({ error: undefined, file: { type: 'image/*', }, fullSrc: getIPFSString(collection.banner, baseURL), localSrc: getIPFSString(collection.banner, baseURL), isProcessing: false, isUpdateIPFS: false, uniqueId: '', } as unknown as IpfsFile) : undefined, tileUri: collection.tileUri ? ({ error: undefined, file: { type: 'image/*', }, fullSrc: getIPFSString(collection.tileUri, baseURL), localSrc: getIPFSString(collection.tileUri, baseURL), isProcessing: false, isUpdateIPFS: false, uniqueId: '', } as unknown as IpfsFile) : undefined, avatar: collection.avatar ? ({ error: undefined, file: { type: 'image/*', }, fullSrc: getIPFSString(collection.avatar, baseURL), localSrc: getIPFSString(collection.avatar, baseURL), isProcessing: false, isUpdateIPFS: false, uniqueId: '', } as unknown as IpfsFile) : undefined, } } const [keys, setKeys] = React.useState<{ [key: string]: undefined | IpfsFile }>(() => { return isEdit ? keysEditInit(collectionOldValue) : { banner: undefined, // name: undefined, tileUri: undefined, avatar: undefined, // thumbnail: undefined, } }) const { account } = useAccount() const { btnStatus, btnInfo, enableBtn, disableBtn, setLabelAndParams, resetBtnInfo, setLoadingBtn, } = useBtnStatus() const updateBtnStatus = React.useCallback( (error?: ErrorType & any) => { resetBtnInfo() const ipfsProcessing = Reflect.ownKeys(keys).find( (key) => keys[key as string]?.isProcessing === true, ) if ( !error && collectionValue && collectionValue.name && collectionValue.tileUri && ipfsProcessing === undefined ) { enableBtn() return } if (!collectionValue?.name) { setLabelAndParams('labelCollectionRequiredName', {}) } if (!collectionValue?.tileUri) { setLabelAndParams('labelCollectionRequiredTileUri', {}) } if (ipfsProcessing) { setLoadingBtn() return } disableBtn() myLog('try to disable nftMint btn!') }, [resetBtnInfo, keys, collectionValue, disableBtn, enableBtn, setLabelAndParams, setLoadingBtn], ) React.useEffect(() => { updateBtnStatus() }, [collectionValue, updateBtnStatus, keys]) React.useEffect(() => { return () => { resetCollectionData() } }, []) const onSubmitClick = React.useCallback(async () => { if (collectionValue.name?.trim() && collectionValue.tileUri?.trim() && LoopringAPI.userAPI) { setLoadingBtn() if (isEdit && type === 'editCollection' && collectionOldValue?.id) { try { const response = await LoopringAPI.userAPI.submitEditNFTCollection( { name: collectionValue.name?.trim(), tileUri: collectionValue.tileUri?.trim(), accountId: account.accountId, banner: collectionValue.banner?.trim() ?? '', avatar: collectionValue.avatar?.trim() ?? '', description: collectionValue.description?.trim() ?? '', collectionId: collectionOldValue.id ?? '', // @ts-ignore thumbnail: '', }, chainId as any, account.apiKey, account.eddsaKey.sk, ) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { const _response: sdk.RESULT_INFO = response as sdk.RESULT_INFO throw new Error( t( _response.code && SDK_ERROR_MAP_TO_UI[_response.code] ? SDK_ERROR_MAP_TO_UI[_response.code].messageKey : SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN].messageKey, { ns: 'error', name: collectionValue.name?.trim() }, ), ) } else { setCollectionToastOpen({ open: true, type: ToastType.success, content: t('labelEditCollectionSuccess'), }) updateWalletL2Collection({ page: 1 }) history.push(`${RouterPath.nft}/${NFTSubRouter.myCollection}`) } updateCollectionData({ ...collectionOldValue }) setKeys({ banner: undefined, name: undefined, tileUri: undefined, avatar: undefined, thumbnail: undefined, }) } catch (error) { setCollectionToastOpen({ open: true, type: ToastType.error, content: t('labelEditCollectionFailed') + `: ${(error as any)?.message ? (error as any).message : t('errorUnknown')}`, }) resetBtnInfo() } } else if (type === 'addLegacyCollection' && account.accountId && match.params.tokenAddress) { try { const response = await LoopringAPI.userAPI.submitNFTLegacyCollection( { ...collectionValue, tokenAddress: match.params.tokenAddress, accountId: account.accountId, name: collectionValue.name?.trim(), tileUri: collectionValue.tileUri?.trim(), }, chainId as any, account.apiKey, account.eddsaKey.sk, ) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { const _response: sdk.RESULT_INFO = response as sdk.RESULT_INFO throw new Error( t( _response.code && SDK_ERROR_MAP_TO_UI[_response.code] ? SDK_ERROR_MAP_TO_UI[_response.code].messageKey : SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN].messageKey, { ns: 'error', name: collectionValue.name?.trim() }, ), ) } else { setCollectionToastOpen({ open: true, type: ToastType.success, content: t('labelCreateCollectionSuccess'), }) updateWalletL2Collection({ page: 1 }) history.push( `${RouterPath.nft}/${NFTSubRouter.importLegacyCollection}/${match.params.tokenAddress}`, ) } updateCollectionData({}) setKeys({ banner: undefined, name: undefined, tileUri: undefined, avatar: undefined, thumbnail: undefined, }) } catch (error) { setCollectionToastOpen({ open: true, type: ToastType.error, content: t('labelCreateCollectionFailed') + `: ${(error as any)?.message ? (error as any).message : t('errorUnknown')}`, }) resetBtnInfo() } } else { try { const response = await LoopringAPI.userAPI.submitNFTCollection( { ...collectionValue, name: collectionValue.name?.trim(), tileUri: collectionValue.tileUri?.trim(), owner: account.accAddress, nftFactory: sdk.NFTFactory_Collection[chainId], } as sdk.CollectionMeta, chainId as any, account.apiKey, account.eddsaKey.sk, ) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { const _response: sdk.RESULT_INFO = response as sdk.RESULT_INFO throw new Error( t( _response.code && SDK_ERROR_MAP_TO_UI[_response.code] ? SDK_ERROR_MAP_TO_UI[_response.code].messageKey : SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN].messageKey, { ns: 'error', name: collectionValue.name?.trim() }, ), ) } else { setCollectionToastOpen({ open: true, type: ToastType.success, content: t('labelCreateCollectionSuccess'), }) updateWalletL2Collection({ page: 1 }) history.push(`${RouterPath.nft}/${NFTSubRouter.myCollection}`) } updateCollectionData({}) setKeys({ banner: undefined, name: undefined, tileUri: undefined, avatar: undefined, thumbnail: undefined, }) } catch (error) { setCollectionToastOpen({ open: true, type: ToastType.error, content: t('labelCreateCollectionFailed') + `: ${(error as any)?.message ? (error as any).message : t('errorUnknown')}`, }) resetBtnInfo() } } } }, [ account, chainId, collectionOldValue, collectionValue, history, isEdit, resetBtnInfo, setCollectionToastOpen, setLoadingBtn, t, updateCollectionData, updateWalletL2Collection, match.params.tokenAddress, ]) const handleOnDataChange = React.useCallback( (key: string, value: any) => { const collectionValue = store.getState()._router_modalData.collectionValue myLog('collectionValue', collectionValue) updateCollectionData({ ...collectionValue, [key]: value }) }, [updateCollectionData], ) const onDelete = React.useCallback( (key: string) => { setKeys((state) => { return { ...state, [key]: undefined, } }) handleOnDataChange(key, undefined) }, [handleOnDataChange], ) const handleFailedUpload = React.useCallback( (data: { uniqueId: string; error: sdk.RESULT_INFO }) => { setKeys((state) => { const key: string = Reflect.ownKeys(state).find((key) => { return state[key as any]?.uniqueId === data.uniqueId }) as string if (key) { handleOnDataChange(key, undefined) return { ...state, [key]: { ...state[key], isProcessing: false, ...{ error: data.error ? data.error : { code: UIERROR_CODE.UNKNOWN, message: `Ipfs Error ${data}`, }, }, } as IpfsFile, } } else { return state } }) }, [handleOnDataChange], ) const handleSuccessUpload = React.useCallback( (data: AddResult & { uniqueId: string }) => { setKeys((state) => { const key: string = Reflect.ownKeys(state).find((key) => { return state[key as any]?.uniqueId === data.uniqueId }) as string if (key) { const cid = data.cid.toString() handleOnDataChange(key, `${IPFS_HEAD_URL}${data.path}`) return { ...state, [key as any]: { ...state[key as any], ...{ cid, fullSrc: getIPFSString(`${IPFS_HEAD_URL}${data.path}`, baseURL), isProcessing: false, }, }, } } else { return state } }) }, [baseURL, handleOnDataChange], ) const { ipfsProvides } = useIPFS({ handleSuccessUpload, handleFailedUpload, }) const onFilesLoad = React.useCallback( (key: string, value: IpfsFile) => { let uniqueId = key + '|' + Date.now() value.isUpdateIPFS = true ipfsService.addFile({ ipfs: ipfsProvides.ipfs, file: value.file, uniqueId: uniqueId, }) setKeys((state) => { return { ...state, [key]: { ...value, file: value.file, uniqueId: uniqueId, cid: '', }, } }) }, [ipfsProvides.ipfs], ) return { keys, isEdit, collectionToastOpen, collectionToastClose, onFilesLoad, onDelete, btnStatus, btnInfo, disabled, handleOnDataChange, collectionValue, resetEdit: isEdit ? () => { updateCollectionData({ ...collectionOldValue }) setKeys(keysEditInit(collectionOldValue)) } : undefined, onSubmitClick, } } ================================================ FILE: packages/core/src/hooks/useractions/useExportAccount.ts ================================================ import React from 'react' import { AccountStatus } from '@loopring-web/common-resources' import { useAccount } from '../../index' import { useTranslation } from 'react-i18next' export const useExportAccount = <_T>() => { const { t } = useTranslation('common') const { account } = useAccount() const [accountInfo, setAccountInfo] = React.useState({ address: '', accountId: 0, level: '', nonce: 0 as number | undefined, apiKey: '', publicX: '', publicY: '', privateKey: '', }) const [exportAccountToastOpen, setExportAccountToastOpen] = React.useState(false) const exportAccountAlertText = t('labelCopyClipBoard') React.useEffect(() => { if (account.readyState !== AccountStatus.ACTIVATED) { return undefined } const accInfo = { address: account.accAddress, accountId: account.accountId, level: account.level, nonce: account.nonce, apiKey: account.apiKey, publicX: account.eddsaKey.formatedPx, publicY: account.eddsaKey.formatedPy, privateKey: account.eddsaKey.sk, } setAccountInfo(accInfo) }, [account.readyState, account.nonce]) const exportAccountProps = { accountInfo, } return { exportAccountProps: exportAccountProps, exportAccountAlertText, exportAccountToastOpen, setExportAccountToastOpen, } } ================================================ FILE: packages/core/src/hooks/useractions/useForceWithdraw.ts ================================================ import React from 'react' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { AccountStep, ForceWithdrawProps, SwitchData, TransactionTradeViews, useOpenModals, } from '@loopring-web/component-lib' import { AccountStatus, CoinMap, IBData, myLog, UIERROR_CODE, WalletMap, LIVE_FEE_TIMES, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, RecordTabIndex, } from '@loopring-web/common-resources' import { LAST_STEP, updateForceWithdrawData as updateForceWithdrawDataStore, } from '@loopring-web/core' import * as sdk from '@loopring-web/loopring-sdk' import { useTokenMap, useAccount, BIGO, DAYS, getTimestampDaysLater, LoopringAPI, store, useAddressCheck, useBtnStatus, walletLayer2Service, useModalData, isAccActivated, useChargeFees, useSystem, WalletMapExtend, WalletLayer2Map, } from '../../index' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useHistory, useRouteMatch } from 'react-router-dom' import Web3 from 'web3' export const useForceWithdraw = , T>() => { const { tokenMap, totalCoinMap, idIndex } = useTokenMap() const { account } = useAccount() const { exchangeInfo, chainId } = useSystem() const { setShowAccount } = useOpenModals() const history = useHistory() const match = useRouteMatch('/layer2/:forceWithdraw') const { forceWithdrawValue, updateForceWithdrawData, resetForceWithdrawData } = useModalData() const { modals: { isShowAccount }, } = useOpenModals() const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, resetIntervalTime, } = useChargeFees({ requestType: sdk.OffchainFeeReqType.FORCE_WITHDRAWAL, updateData: ({ fee }) => { const { forceWithdrawValue } = store.getState()._router_modalData store.dispatch(updateForceWithdrawDataStore({ ...forceWithdrawValue, fee })) }, }) const { checkHWAddr, updateHW } = useWalletInfo() // const [forceWithDrawAccount, setForceWithDrawAccount] = // React.useState(); const [walletItsMap, setWalletItsMap] = React.useState>({}) const { address, realAddr, setAddress, checkAddAccountId, addrStatus, isLoopringAddress, isActiveAccount, isAddressCheckLoading, } = useAddressCheck() const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const checkBtnStatus = React.useCallback(() => { if ( tokenMap && forceWithdrawValue?.fee?.belong && forceWithdrawValue.fee?.feeRaw && forceWithdrawValue.belong && forceWithdrawValue.balance && !!forceWithdrawValue?.withdrawAddress && !isFeeNotEnough.isFeeNotEnough ) { enableBtn() myLog('enableBtn') return } disableBtn() }, [ tokenMap, forceWithdrawValue.fee?.belong, forceWithdrawValue.fee?.feeRaw, forceWithdrawValue.belong, forceWithdrawValue.balance, forceWithdrawValue?.withdrawAddress, isFeeNotEnough.isFeeNotEnough, disableBtn, enableBtn, ]) React.useEffect(() => { checkBtnStatus() }, [ address, addrStatus, isFeeNotEnough.isFeeNotEnough, forceWithdrawValue.fee?.belong, forceWithdrawValue.fee?.feeRaw, forceWithdrawValue.belong, isLoopringAddress, isActiveAccount, ]) const walletLayer2Callback = React.useCallback(async () => { if ( LoopringAPI.userAPI && realAddr && checkAddAccountId && isLoopringAddress && !isActiveAccount ) { const { userBalances } = await LoopringAPI.userAPI?.getUserBalances( { accountId: checkAddAccountId, tokens: '' }, account.apiKey, ) let walletMap: WalletMap = {} if (userBalances) { const walletLayer2 = Reflect.ownKeys(userBalances).reduce((prev, item) => { // @ts-ignore return { ...prev, [idIndex[item]]: userBalances[Number(item)] } }, {} as WalletLayer2Map) walletMap = Reflect.ownKeys(walletLayer2).reduce((prev, item) => { const { total, locked, // pending: { withdraw }, } = walletLayer2[item as string] const countBig = sdk.toBig(total).minus(sdk.toBig(locked)) if (countBig.eq(BIGO)) { return prev } return { ...prev, [item]: { belong: item, count: sdk.fromWEI(tokenMap, item, countBig.toString()), detail: walletLayer2[item as string], }, } }, {} as WalletMapExtend) } const key = Reflect.ownKeys(walletMap).length ? Reflect.ownKeys(walletMap)[0] : undefined const _value = key ? walletMap[key] : undefined updateForceWithdrawData({ withdrawAddress: realAddr, belong: _value?.belong ?? '', tradeValue: _value?.count, balance: _value?.count, }) setWalletItsMap({ ...walletMap }) } else { setWalletItsMap({}) } }, [ account.apiKey, checkAddAccountId, idIndex, isActiveAccount, isLoopringAddress, realAddr, tokenMap, updateForceWithdrawData, ]) React.useEffect(() => { if (checkAddAccountId && isLoopringAddress && !isActiveAccount && !!realAddr) { walletLayer2Callback() } else { setWalletItsMap({}) updateForceWithdrawData({ withdrawAddress: '', belong: '', tradeValue: undefined, balance: undefined, }) } }, [isLoopringAddress, isActiveAccount, checkAddAccountId, realAddr]) // useWalletLayer2Socket({ walletLayer2Callback }); const resetDefault = React.useCallback(() => { checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) resetForceWithdrawData() setWalletItsMap({}) setAddress('') }, [checkFeeIsEnough, resetForceWithdrawData, setAddress]) React.useEffect(() => { // @ts-ignore if (match?.params?.forcewithdraw?.toLowerCase() === 'forcewithdraw') { resetDefault() } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [match?.params]) const processRequest = React.useCallback( async (request: sdk.OriginForcesWithdrawalsV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account try { if (connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } myLog('force Withdraw processRequest:', isHWAddr, isNotHardwareWallet) const response = await LoopringAPI.userAPI.submitForceWithdrawals( { request, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === 'unknown' ? 1 : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submitNFTWithdraw:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_In_Progress, }) setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_Submit, info: { symbol: forceWithdrawValue.belong, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } walletLayer2Service.sendUserUpdate() resetDefault() history.push( `/l2assets/history/${RecordTabIndex.Transactions}?types=${TransactionTradeViews.forceWithdraw}`, ) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.ForceWithdraw_Submit ) { setShowAccount({ isShow: false }) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_First_Method_Denied, info: { symbol: forceWithdrawValue.belong, }, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_Denied, info: { symbol: forceWithdrawValue.belong, }, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_Failed, info: { symbol: forceWithdrawValue.belong, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, setShowAccount, forceWithdrawValue.belong, checkFeeIsEnough, resetDefault, updateHW, ], ) const handleForceWithdraw = React.useCallback( async (_inputValue: R, isFirstTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const forceWithdrawValue = store.getState()._router_modalData.forceWithdrawValue if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && forceWithdrawValue?.fee?.belong && forceWithdrawValue.fee?.feeRaw && forceWithdrawValue?.belong && forceWithdrawValue?.withdrawAddress && !isFeeNotEnough.isFeeNotEnough && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_WaitForAuth, }) const feeToken = tokenMap[forceWithdrawValue.fee.belong] const feeRaw = forceWithdrawValue.fee.feeRaw ?? forceWithdrawValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) // const fee = sdk.toBig(forceWithdrawValue.fee.__raw__?.feeRaw ?? 0); const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: Number(feeToken.tokenId), }, apiKey, ) const { broker } = await LoopringAPI.userAPI?.getAvailableBroker({ type: 1, }) const request: sdk.OriginForcesWithdrawalsV3 = { transfer: { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: broker, storageId: storageId.offchainId, token: { tokenId: feeToken.tokenId, volume: fee.toFixed(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), }, requesterAddress: accAddress, tokenId: tokenMap[forceWithdrawValue.belong].tokenId, withdrawAddress: forceWithdrawValue.withdrawAddress ?? realAddr, } myLog('ForcesWithdrawals request:', request) processRequest(request, isFirstTime) } catch (e: any) { sdk.dumpError400(e) setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, }, }) } return true } else { return false } }, [ account, tokenMap, exchangeInfo, isFeeNotEnough.isFeeNotEnough, setShowAccount, realAddr, processRequest, ], ) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.ForceWithdraw_WaitForAuth, }) handleForceWithdraw( { belong: forceWithdrawValue.belong, balance: forceWithdrawValue.balance, tradeValue: forceWithdrawValue.tradeValue, } as R, !isHardwareRetry, ) }, [forceWithdrawValue, handleForceWithdraw, setShowAccount], ) // myLog("walletItsMap", walletItsMap); // @ts-ignore const forceWithdrawProps: ForceWithdrawProps = React.useMemo(() => { return { disabled: false, // onChangeEvent: undefined, type: TRADE_TYPE.TOKEN, addressDefault: address, handleOnAddressChange: (value: any) => { setAddress(value) updateForceWithdrawData({ belong: '', tradeValue: undefined, balance: undefined, }) }, lastFailed: isShowAccount?.info?.lastFailed === LAST_STEP.forceWithdraw, isActiveAccount, isNotAvailableAddress: !(isLoopringAddress && !isActiveAccount), realAddr, isAddressCheckLoading, isLoopringAddress, tradeData: forceWithdrawValue as any, coinMap: totalCoinMap as CoinMap, walletMap: walletItsMap, addrStatus, withdrawBtnStatus: btnStatus, onWithdrawClick: handleForceWithdraw, handlePanelEvent: async (data: SwitchData) => { return new Promise((res: any) => { if (data.to === 'button') { if (data.tradeData.belong) { const walletInfo = walletItsMap[data.tradeData.belong] updateForceWithdrawData({ ...forceWithdrawValue, belong: data.tradeData.belong, tradeValue: walletInfo?.count, //data.tradeData?.tradeValue, balance: walletInfo?.count, // withdrawAddress: realAddr, }) } else { updateForceWithdrawData({ belong: '', tradeValue: undefined, balance: undefined, // withdrawAddress: realAddr, }) } } res() }) }, handleFeeChange, feeInfo, chargeFeeTokenList, isFeeNotEnough, } }, [ addrStatus, address, btnStatus, isShowAccount, chargeFeeTokenList, feeInfo, isShowAccount?.info?.lastFailed, forceWithdrawValue, handleFeeChange, handleForceWithdraw, isActiveAccount, isAddressCheckLoading, isFeeNotEnough, isLoopringAddress, realAddr, setAddress, totalCoinMap, updateForceWithdrawData, walletItsMap, ]) // as ForceWithdrawProps; return { forceWithdrawProps, retryBtn, } } // ErrorCode.ERR_REST_ACCOUNT_NOT_EXIST, //104003 // message =s"payer hasn't completed opening an payerAccount, payerAccount id // // ErrorCode.ERR_ACCOUNT_FREEZED, //121001 // message = "payer is freeze" // // // ErrorCode.ERR_INVALID_ARGUMENT, //100001 // "payer address and payer id is not match" // // ErrorCode.ERR_INVALID_SIGNATURE, //100206 // "inner transfer eddsa sig invalid" // // ErrorCode.ERR_WALLET_BALANCE_NOT_ENOUGH, //109143 // s"balance of ${transfer.tokenId} in payerAccount ${transfer.payerId} is not enough" // // ErrorCode.ERR_NO_ACCOUNT, //113001 // s"withdraw address payerAccount info not found ${targetAccount.error}" ================================================ FILE: packages/core/src/hooks/useractions/useNFTBurn.ts ================================================ import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { AccountStep, SwitchData, TransferProps, useOpenModals, } from '@loopring-web/component-lib' import { AccountStatus, CoinMap, Explorer, LIVE_FEE_TIMES, myLog, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, TradeNFT, UIERROR_CODE, } from '@loopring-web/common-resources' import { BIGO, DAYS, getIPFSString, getTimestampDaysLater, isAccActivated, LAST_STEP, LoopringAPI, store, useAccount, useBtnStatus, useChargeFees, useModalData, useSystem, useTokenMap, useWalletLayer2, useWalletLayer2WithNFTSocket, walletLayer2Service, } from '../../index' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useHistory, useLocation } from 'react-router-dom' import { NETWORKEXTEND, useContacts } from '../../stores' import Web3 from 'web3' import {useTranslation} from "react-i18next"; export const useNFTBurn = , T>() => { const { setShowAccount, setShowNFTTransfer, setShowNFTDetail, modals: { isShowNFTDetail, isShowNFTTransfer: { isShow, info, address: contactAddress, name: contactName, addressType: contactAddressType, }, }, } = useOpenModals() const { t } = useTranslation() const { tokenMap, totalCoinMap } = useTokenMap() const { account } = useAccount() const { exchangeInfo, chainId, baseURL } = useSystem() const { updateWalletLayer2 } = useWalletLayer2() const { nftTransferValue, updateNFTWithdrawData, updateNFTTransferData } = useModalData() const history = useHistory() const { search, pathname } = useLocation() const searchParams = new URLSearchParams(search) const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, resetIntervalTime, checkFeeIsEnough, } = useChargeFees({ tokenAddress: nftTransferValue.tokenAddress, // requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, updateData: // React.useCallback( ({ fee, requestType }) => { let _requestType = sdk.OffchainNFTFeeReqType.NFT_TRANSFER if (_requestType === requestType) { const nftTransferValue = store.getState()._router_modalData.nftTransferValue updateNFTTransferData({ ...nftTransferValue, balance: sdk .toBig(nftTransferValue.total ?? 0) .minus(nftTransferValue.locked ?? 0) .toNumber(), fee, }) } }, // [feeWithActive] // ), }) const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const checkBtnStatus = React.useCallback(() => { if ( tokenMap && nftTransferValue.fee?.belong && nftTransferValue?.tradeValue && chargeFeeTokenList.length && !isFeeNotEnough.isFeeNotEnough && sdk.toBig(nftTransferValue.tradeValue).gt(BIGO) && sdk.toBig(nftTransferValue.tradeValue).lte(Number(nftTransferValue.balance) ?? 0) ) { enableBtn() myLog('enableBtn') return } disableBtn() }, [ tokenMap, nftTransferValue.fee?.belong, nftTransferValue.tradeValue, nftTransferValue.balance, chargeFeeTokenList.length, isFeeNotEnough?.isFeeNotEnough, disableBtn, enableBtn, ]) React.useEffect(() => { checkBtnStatus() }, [ isFeeNotEnough.isFeeNotEnough, nftTransferValue.tradeValue, nftTransferValue.fee, ]) const walletLayer2Callback = React.useCallback(() => { checkFeeIsEnough() }, [checkFeeIsEnough]) const { contacts, updateContacts, errorMessage: contactsErrorMessage } = useContacts() useWalletLayer2WithNFTSocket({ walletLayer2Callback }) const resetDefault = React.useCallback(() => { if (info?.isRetry) { checkFeeIsEnough() return } checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) if (nftTransferValue.nftData) { updateNFTTransferData({ balance: sdk .toBig(nftTransferValue.total ?? 0) .minus(nftTransferValue.locked ?? 0) .toNumber(), belong: nftTransferValue.name as any, tradeValue: undefined, fee: feeInfo, }) } else { updateNFTTransferData({ fee: feeInfo, belong: '', balance: 0, tradeValue: 0, address: '*', }) } }, [ info?.isRetry, contactsErrorMessage, checkFeeIsEnough, nftTransferValue.nftData, nftTransferValue.total, nftTransferValue.locked, nftTransferValue.name, contactAddress, updateContacts, updateNFTTransferData, feeInfo, ]) React.useEffect(() => { if (isShow || info?.isBurn) { updateWalletLayer2() resetDefault() } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [isShow, info?.isShowLocal]) const { checkHWAddr, updateHW } = useWalletInfo() const [lastRequest, setLastRequest] = React.useState({}) const processRequest = React.useCallback( async (request: sdk.OriginNFTTransferRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account try { if (connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } setLastRequest({ request }) const response = await LoopringAPI.userAPI?.submitNFTInTransfer( { request, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[ connectName ] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submitInternalTransfer:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowNFTTransfer({ isShow: false }) // setIsConfirmTransfer(false); setShowAccount({ isShow: true, step: AccountStep.NFTBurn_In_Progress, }) setShowAccount({ isShow: true, step: AccountStep.NFTBurn_Success, info: { symbol: nftTransferValue.name, hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-nftTransfer-${account.accountId}-${ request.token.tokenId }-${request.storageId}`, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } setShowNFTDetail({ ...isShowNFTDetail, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) updateNFTWithdrawData({ ...isShowNFTDetail, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) updateNFTTransferData({ ...isShowNFTDetail, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) walletLayer2Service.sendUserUpdate() // resetNFTTransferData(); await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.NFTBurn_Success ) { setShowAccount({ isShow: false }) searchParams.delete('detail') history.push(pathname + '?' + searchParams.toString()) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.NFTBurn_First_Method_Denied, info: { symbol: nftTransferValue.name, }, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.NFTBurn_User_Denied, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, }) } setShowAccount({ isShow: true, step: AccountStep.NFTBurn_Failed, info: { symbol: nftTransferValue.name, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, setShowNFTTransfer, setShowAccount, nftTransferValue.name, setShowNFTDetail, isShowNFTDetail, updateNFTWithdrawData, updateNFTTransferData, updateHW, searchParams, history, pathname, checkFeeIsEnough, ], ) const onTransferClick = React.useCallback( async (_nftTransferValue, isFirstTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const nftTransferValue = { ...store.getState()._router_modalData.nftTransferValue, // tradeValue: _nftTransferValue.tradeValue, // balance: _nftTransferValue.balance, } if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && connectProvides.usedWeb3 && nftTransferValue?.nftData && nftTransferValue?.fee?.belong && nftTransferValue?.fee?.feeRaw && nftTransferValue.tradeValue && nftTransferValue.tokenId && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.NFTBurn_WaitForAuth, }) const feeToken = tokenMap[nftTransferValue.fee.belong] const feeRaw = nftTransferValue.fee.feeRaw ?? nftTransferValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const tradeValue = nftTransferValue.tradeValue ?? _nftTransferValue.tradeValue const balance = nftTransferValue.balance ?? _nftTransferValue.balance const isExceedBalance = sdk.toBig(tradeValue).gt(balance) if (isExceedBalance) { throw Error('overflow balance') } const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: nftTransferValue.tokenId, }, apiKey, ) const {result:realAddr} = await LoopringAPI.nftAPI?.getUserNFTBurnAddress({accountId,tokenId:nftTransferValue.tokenId}); if(!realAddr){ throw {code:UIERROR_CODE.ERROR_ADDRESS_BURN_NFT} } const req: sdk.OriginNFTTransferRequestV3 = { exchange: exchangeInfo.exchangeAddress, fromAccountId: accountId, fromAddress: accAddress, toAccountId: 0, toAddress: realAddr, storageId: storageId?.offchainId, token: { tokenId: nftTransferValue.tokenId, nftData: nftTransferValue.nftData, amount: tradeValue.toString(), }, payPayeeUpdateAccount: false, maxFee: { tokenId: feeToken.tokenId, amount: fee.toString(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), memo: 'Burn', } myLog('nftBurn req:', req) processRequest(req, isFirstTime) } catch (e: any) { const errorItem = SDK_ERROR_MAP_TO_UI[(e)?.code ?? UIERROR_CODE.UNKNOWN]?? SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN] setShowAccount({ isShow: true, step: AccountStep.NFTBurn_Failed, info: { symbol: nftTransferValue?.name, }, error: { code:(e)?.code ?? UIERROR_CODE.UNKNOWN, message: t(errorItem.messageKey,{ ns: 'error' },), }, }) } } else { return } }, [ account, tokenMap, exchangeInfo, setShowAccount, processRequest, ], ) // const activeFee = React.useMemo(() => { // // return activeAccountFeeList?.find( // // (item: any) => item.belong == feeInfo.belong // // ); // }, [feeInfo, activeAccountFeeList]); const handlePanelEvent = React.useCallback( async (data: SwitchData, _switchType: 'Tomenu' | 'Tobutton') => { return new Promise((res: any) => { if (data.to === 'button') { if (data.tradeData.belong) { updateNFTTransferData({ belong: data.tradeData.belong, tradeValue: data.tradeData?.tradeValue, balance: data.tradeData.balance, address: '*', }) } else { updateNFTTransferData({ belong: undefined, tradeValue: undefined, balance: undefined, address: '*', }) } } res() }) }, [updateNFTTransferData], ) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.NFTBurn_WaitForAuth, }) processRequest(lastRequest, !isHardwareRetry) }, [lastRequest, processRequest, setShowAccount], ) const nftBurnProps: TransferProps = { handleOnMemoChange:()=>{}, memo: nftTransferValue.memo ?? '', type: TRADE_TYPE.NFT, lastFailed: store.getState().modals.isShowAccount.info?.lastFailed === LAST_STEP.nftTransfer, tradeData: { ...nftTransferValue } as unknown as R, coinMap: totalCoinMap as CoinMap, walletMap: {}, transferBtnStatus: btnStatus, transferI18nKey: t('labelL2NFTBurnBtn'), onTransferClick, handleFeeChange, feeInfo, chargeFeeTokenList, isFeeNotEnough, handlePanelEvent, baseURL, getIPFSString, isFromContact: contactAddress ? true : false, contact: contactAddress ? { address: contactAddress, name: contactName!, addressType: contactAddressType!, } : undefined, contacts, } as unknown as TransferProps return { nftBurnProps, retryBtn, // cancelNFTTransfer, } } ================================================ FILE: packages/core/src/hooks/useractions/useNFTDeploy.ts ================================================ import { useAccount, useTokenMap, useModalData, useWalletLayer2Socket, walletLayer2Service, DAYS, getTimestampDaysLater, LoopringAPI, store, useSystem, isAccActivated, useChargeFees, useWalletLayer2NFT, useWalletL2Collection, NETWORKEXTEND, } from '../../index' import { AccountStep, NFTDeployProps, useOpenModals } from '@loopring-web/component-lib' import React from 'react' import { AccountStatus, Layer1Action, myLog, TradeNFT, UIERROR_CODE, LIVE_FEE_TIMES, SUBMIT_PANEL_QUICK_AUTO_CLOSE, } from '@loopring-web/common-resources' import { useBtnStatus } from '../common/useBtnStatus' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import * as sdk from '@loopring-web/loopring-sdk' import { useLayer1Store } from '../../stores/localStore/layer1Store' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useHistory, useLocation, // useLocation } from 'react-router-dom' import Web3 from 'web3' export function useNFTDeploy & { broker: string }, I>() { const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const { tokenMap } = useTokenMap() const { account } = useAccount() const { exchangeInfo, chainId } = useSystem() const { nftDeployValue, updateNFTDeployData, resetNFTDeployData } = useModalData() const { page, updateWalletLayer2NFT } = useWalletLayer2NFT() const { page: collectionPage, updateWalletL2Collection } = useWalletL2Collection() const { setShowAccount, setShowNFTDetail, setShowNFTDeploy, modals: { isShowNFTDeploy, isShowNFTDetail }, } = useOpenModals() const { setOneItem } = useLayer1Store() const { checkHWAddr, updateHW } = useWalletInfo() const history = useHistory() const { search, pathname } = useLocation() const searchParams = new URLSearchParams(search) const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, resetIntervalTime, } = useChargeFees({ tokenAddress: nftDeployValue.tokenAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_DEPLOY, updateData: ({ fee }) => { updateNFTDeployData({ ...nftDeployValue, fee }) }, }) const processRequestNFT = React.useCallback( async ( request: sdk.OriginDeployNFTRequestV3 | sdk.OriginDeployCollectionRequestV3, isFirstTime: boolean, ) => { const { apiKey, connectName, eddsaKey } = account try { if (connectProvides.usedWeb3 && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) isHWAddr = !isFirstTime ? !isHWAddr : isHWAddr let response if (request.hasOwnProperty('nftData')) { response = await LoopringAPI.userAPI?.submitDeployNFT( { request: request as sdk.OriginDeployNFTRequestV3, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) } else { response = await LoopringAPI.userAPI?.submitDeployCollection( { request: request as sdk.OriginDeployCollectionRequestV3, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) } if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setOneItem({ chainId: chainId as sdk.ChainId, uniqueId: request.tokenAddress.toLowerCase(), domain: Layer1Action.NFTDeploy, }) // setShowAccount({ // isShow: true, // step: AccountStep.NFTDeploy_In_Progress, // }); setShowAccount({ isShow: true, step: AccountStep.NFTDeploy_Submit, info: { symbol: nftDeployValue?.tokenAddress, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } walletLayer2Service.sendUserUpdate() setShowNFTDeploy({ isShow: false }) resetNFTDeployData() // searchParams.delete("detail"); if (nftDeployValue.nftData) { updateWalletLayer2NFT({ page: Number(searchParams.get('collectionPage')) ?? 1, collectionId: nftDeployValue?.collectionMeta?.id, collectionContractAddress: nftDeployValue?.collectionMeta?.contractAddress, }) setShowNFTDetail({ ...isShowNFTDetail, deploymentStatus: sdk.DEPLOYMENT_STATUS.DEPLOYING, }) } else { updateWalletL2Collection({ page: collectionPage, }) history.push(pathname + '?' + searchParams.toString()) } await sdk.sleep(SUBMIT_PANEL_QUICK_AUTO_CLOSE) if (store.getState().modals.isShowAccount.isShow) { setShowAccount({ isShow: false }) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isFirstTime) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.NFTDeploy_First_Method_Denied, info: { symbol: nftDeployValue?.tokenAddress, }, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.NFTDeploy_Denied, info: { symbol: nftDeployValue?.tokenAddress, }, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } setShowAccount({ isShow: true, step: AccountStep.NFTDeploy_Failed, info: { symbol: nftDeployValue?.tokenAddress, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, setShowAccount, nftDeployValue?.tokenAddress, checkFeeIsEnough, setOneItem, updateWalletLayer2NFT, page, resetNFTDeployData, updateHW, ], ) React.useEffect(() => { if (isShowNFTDeploy.isShow) { checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [isShowNFTDeploy]) const checkBtnStatus = React.useCallback(() => { if (tokenMap && !isFeeNotEnough.isFeeNotEnough) { enableBtn() return } disableBtn() }, [disableBtn, enableBtn, isFeeNotEnough.isFeeNotEnough, tokenMap]) React.useEffect(() => { checkBtnStatus() }, [checkBtnStatus, nftDeployValue, isFeeNotEnough.isFeeNotEnough]) const onNFTDeployClick = async (_nftDeployValue: T, isFirsTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const nftDeployValue = store.getState()._router_modalData.nftDeployValue if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && !isFeeNotEnough.isFeeNotEnough && nftDeployValue && nftDeployValue.broker && nftDeployValue.tokenAddress && connectProvides.usedWeb3 && (nftDeployValue?.nftData || nftDeployValue?.collectionMeta?.owner?.toLowerCase() === account.accAddress?.toLowerCase()) && nftDeployValue.fee && nftDeployValue?.fee.belong && nftDeployValue?.fee.fee && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.NFTDeploy_WaitForAuth, }) const feeToken = tokenMap[nftDeployValue.fee.belong] const feeRaw = nftDeployValue.fee.feeRaw ?? nftDeployValue.fee.__raw__?.feeRaw ?? 0 const _fee = sdk.toBig(feeRaw) // const _fee = sdk.toBig(nftDeployValue.fee.__raw__?.feeRaw ?? 0); myLog('fee.__raw__', _fee) const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: Number(feeToken.tokenId), }, apiKey, ) let req: sdk.OriginDeployNFTRequestV3 | sdk.OriginDeployCollectionRequestV3 if ( nftDeployValue.collectionMeta && nftDeployValue.collectionMeta.owner?.toLowerCase() === account.accAddress?.toLowerCase() ) { req = { nftOwner: account.accAddress?.toLowerCase(), nftBaseUri: nftDeployValue.collectionMeta.baseUri ?? '', tokenAddress: nftDeployValue.tokenAddress, nftFactory: nftDeployValue.collectionMeta?.nftFactory ?? '', transfer: { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: nftDeployValue.broker, storageId: storageId.offchainId, token: { tokenId: feeToken.tokenId, volume: _fee.toFixed(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), }, } } else { req = { nftData: nftDeployValue.nftData ?? '', tokenAddress: nftDeployValue.tokenAddress, transfer: { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: nftDeployValue.broker, storageId: storageId.offchainId, token: { tokenId: feeToken.tokenId, volume: _fee.toFixed(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), }, } } myLog('nftDeploy req:', req) processRequestNFT(req, isFirsTime) } catch (e: unknown) { // nftTransfer failed setShowAccount({ isShow: true, step: AccountStep.NFTDeploy_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: (e as any).message }, }) } } else { return false } } useWalletLayer2Socket({}) const nftDeployProps = { coinMap: {}, handleOnNFTDataChange(_data: T): void {}, tradeData: nftDeployValue as T, walletMap: {}, onNFTDeployClick: (trade: T) => { onNFTDeployClick(trade) }, nftDeployBtnStatus: btnStatus, chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, } as NFTDeployProps return { nftDeployProps, updateNFTDeployData, } } ================================================ FILE: packages/core/src/hooks/useractions/useNFTDeposit.ts ================================================ import React from 'react' import { AccountStep, NFTDepositProps, useOpenModals } from '@loopring-web/component-lib' import { AccountStatus, CoinMap, DEAULT_NFTID_STRING, ErrorMap, ErrorType, globalSetup, IPFS_LOOPRING_SITE, myLog, NFTSubRouter, RouterPath, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, TradeNFT, UIERROR_CODE, WalletMap, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { useModalData, useSystem, useTokenMap, useAccount, useWalletLayer1, ActionResultCode, LoopringAPI, store, useBtnStatus, getIPFSString, depositServices, DepositCommands, BIGO, waitForTx, } from '../../index' import { connectProvides } from '@loopring-web/web3-provider' import _ from 'lodash' import { useOnChainInfo } from '../../stores/localStore/onchainHashInfo' import { useHistory } from 'react-router-dom' import Web3 from 'web3' export const useNFTDeposit = , I>(): { nftDepositProps: NFTDepositProps } => { const subject = React.useMemo(() => depositServices.onSocket(), []) const { tokenMap, totalCoinMap } = useTokenMap() const { account } = useAccount() const { exchangeInfo, chainId, gasPrice, baseURL } = useSystem() const [isNFTCheckLoading, setIsNFTCheckLoading] = React.useState(false) const { nftDepositValue, updateNFTDepositData, resetNFTDepositData } = useModalData() const { walletLayer1, updateWalletLayer1 } = useWalletLayer1() const { updateDepositHash } = useOnChainInfo() const history = useHistory() const { btnStatus, btnInfo, enableBtn, disableBtn, setLoadingBtn, setLabelAndParams, resetBtnInfo, } = useBtnStatus() const { setShowAccount, setShowNFTDeposit } = useOpenModals() const debounceCheck = _.debounce( async (data) => { const web3 = connectProvides.usedWeb3 as unknown as Web3 if (LoopringAPI.nftAPI && exchangeInfo && web3) { setIsNFTCheckLoading(true) let [balance, meta, isApproved] = await Promise.all([ LoopringAPI.nftAPI.getNFTBalance({ account: account.accAddress, nftId: data.nftId, nftType: data.nftType as unknown as sdk.NFTType, web3, tokenAddress: data.tokenAddress, }), LoopringAPI.nftAPI.getContractNFTMeta( { _id: data.nftId, nftId: data.nftId, nftType: data.nftType as unknown as sdk.NFTType, web3, tokenAddress: data.tokenAddress, }, IPFS_LOOPRING_SITE as any, ), LoopringAPI.nftAPI.isApprovedForAll({ web3, from: account.accAddress, exchangeAddress: exchangeInfo.exchangeAddress, tokenAddress: data.tokenAddress, nftType: data.nftType as unknown as sdk.NFTType, }), ]).finally(() => { setIsNFTCheckLoading(() => false) }) myLog('setIsNFTCheckLoading done', balance, meta, isApproved) const shouldUpdate = { ...data, nftId: data.nftId, name: meta.name ?? 'unknown NFT', image: meta?.image ?? '', description: meta.description ?? '', balance: Number(balance.count ?? 0), isApproved, } myLog('debounceCheck', shouldUpdate) updateNFTDepositData(shouldUpdate) } }, globalSetup.wait, { trailing: true }, ) { } const handleOnNFTDataChange = async (data: T) => { const web3 = connectProvides.usedWeb3 const nftDepositValue = store.getState()._router_modalData.nftDepositValue let shouldUpdate: any = { nftType: nftDepositValue.nftType ?? 0, } if (data.hasOwnProperty('tokenAddress')) { shouldUpdate = { ...shouldUpdate, tokenAddress: data.tokenAddress, } } if (data.hasOwnProperty('nftIdView') && web3) { let _nftId = nftDepositValue.nftId if ( (data.nftId !== '' && (!data.nftIdView || !data.nftIdView?.trim())) || (data.nftIdView && data.nftIdView.toLowerCase().startsWith('0x')) ) { _nftId = data.nftIdView ?? '' } else if (data.nftIdView !== undefined) { try { if (data.nftIdView === '') { _nftId = '' shouldUpdate.balance = '' shouldUpdate.tradeValue = undefined } else { _nftId = web3.utils.toHex(sdk.toBN(data.nftIdView) as any).replace('0x', '') const prev = DEAULT_NFTID_STRING.substring( 0, DEAULT_NFTID_STRING.length - _nftId.toString().length, ) _nftId = prev + _nftId.toString() } } catch (error: any) { const errorView: ErrorType = ErrorMap.NTF_ID_ENCODE_ERROR updateBtnStatus({ errorView, ...(error as any) }) return } } shouldUpdate = { ...shouldUpdate, nftIdView: data.nftIdView, nftId: _nftId, } } if (data.hasOwnProperty('nftType')) { shouldUpdate = { ...shouldUpdate, nftType: data.nftType, } } if (data.hasOwnProperty('tradeValue')) { shouldUpdate = { ...shouldUpdate, tradeValue: data.tradeValue, } } if ( (shouldUpdate.tokenAddress && shouldUpdate.tokenAddress !== nftDepositValue.tokenAddress) || (shouldUpdate.nftIdView && shouldUpdate.nftIdView != nftDepositValue.nftIdView) || (shouldUpdate.nftType !== undefined && shouldUpdate.nftType != nftDepositValue.nftType) ) { myLog('debounceCheck', debounceCheck) const obj = { ...nftDepositValue, ...shouldUpdate } if ( obj.tokenAddress && obj.nftId !== undefined && obj.nftId !== '' && obj.nftType !== undefined ) { debounceCheck(obj) } } else if ( nftDepositValue.balance !== 0 && (!nftDepositValue.tokenAddress || !nftDepositValue.nftIdView || nftDepositValue.nftType === undefined) ) { shouldUpdate = { ...shouldUpdate, description: '', image: '', name: '', balance: 0, isApproved: undefined, } } myLog('nftDepositValue', nftDepositValue, shouldUpdate) updateNFTDepositData({ ...nftDepositValue, ...shouldUpdate, }) } const onNFTDepositClick = React.useCallback(async () => { let result = { code: ActionResultCode.NoError } const nftDepositValue = store.getState()._router_modalData.nftDepositValue setLoadingBtn() try { if ( account.readyState !== AccountStatus.UN_CONNECT && nftDepositValue.tradeValue && nftDepositValue.tokenAddress && nftDepositValue.nftId && tokenMap && exchangeInfo?.exchangeAddress && connectProvides.usedWeb3 && LoopringAPI.nftAPI ) { const web3 = connectProvides.usedWeb3 as unknown as Web3 const gasLimit = undefined //parseInt(NFTGasAmounts.deposit) ?? undefined; const realGasPrice = gasPrice ?? 30 enableBtn() setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_WaitForAuth, }) let nonce = (await sdk.getNonce(connectProvides.usedWeb3 as unknown as Web3, account.accAddress)) ?? 0 if (!nftDepositValue.isApproved) { setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_Approve_WaitForAuth, }) try { const tx = await LoopringAPI.nftAPI.approveNFT({ web3, from: account.accAddress, depositAddress: exchangeInfo?.exchangeAddress, tokenAddress: nftDepositValue.tokenAddress, nftId: nftDepositValue.nftId, gasPrice: realGasPrice, gasLimit, chainId: chainId as sdk.ChainId, nonce, nftType: nftDepositValue.nftType as unknown as sdk.NFTType, sendByMetaMask: true, }) await waitForTx(web3, tx.result); } catch (error: any) { if (error instanceof Error) { throw { // Pull all enumerable properties, supporting properties on custom Errors ...error, // Explicitly pull Error's non-enumerable properties message: error.message, stack: error.stack, type: 'ApproveToken', } } else { throw { ...(error as any), type: 'ApproveToken', } } } nonce += 1 } else { myLog('NFT is Approved ALL at History') } setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_WaitForAuth, }) try { const response = await LoopringAPI.nftAPI.depositNFT({ web3, from: account.accAddress, exchangeAddress: exchangeInfo?.exchangeAddress, tokenAddress: nftDepositValue.tokenAddress, nftId: nftDepositValue.nftId, amount: nftDepositValue.tradeValue, gasPrice: realGasPrice, gasLimit, chainId: chainId as sdk.ChainId, nonce, nftType: nftDepositValue.nftType as unknown as sdk.NFTType, sendByMetaMask: true, }) handleOnNFTDataChange({ nftIdView: '', tokenAddress: '' } as T) updateWalletLayer1() resetNFTDepositData() setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_Submit, info: { symbol: nftDepositValue?.name, value: nftDepositValue.tradeValue, hash: response.result, }, }) updateDepositHash(response.result, account.accAddress, undefined, { symbol: nftDepositValue.name, type: 'Deposit NFT', value: nftDepositValue.tradeValue, }) myLog('response:', response) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.NFTDeposit_Submit ) { setShowAccount({ isShow: false }) history.push(`${RouterPath.nft}/${NFTSubRouter.assetsNFT}`) } } catch (error) { if (error instanceof Error) { throw { // Pull all enumerable properties, supporting properties on custom Errors ...error, // Explicitly pull Error's non-enumerable properties message: error.message, stack: error.stack, type: 'Deposit', } } else { throw { ...(error as any), type: 'Deposit', } } } } else { throw { code: UIERROR_CODE.DATA_NOT_READY } } } catch (e) { //deposit failed enableBtn() const { type, ..._error } = (e as any).message ? (e as any) : { type: '' } const error = LoopringAPI?.exchangeAPI?.genErr(_error as any) ?? { code: UIERROR_CODE.DATA_NOT_READY, } const code = sdk.checkErrorInfo(error, true) setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_Approve_Denied, }) myLog('---- deposit NFT ERROR reason:', _error?.message, code) switch (code) { case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: if (type === 'ApproveToken') { setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_Approve_Denied, }) } else { setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_Denied, info: { symbol: nftDepositValue?.name, }, }) } break default: setShowAccount({ isShow: true, step: AccountStep.NFTDeposit_Failed, info: { symbol: nftDepositValue?.name, }, error: { ..._error, ...error, code: (e as any)?.code ?? UIERROR_CODE.UNKNOWN, }, }) break } updateWalletLayer1() } return result }, [ account.accAddress, account.readyState, chainId, exchangeInfo?.exchangeAddress, gasPrice, nftDepositValue.isApproved, nftDepositValue.name, nftDepositValue.nftId, nftDepositValue.nftType, nftDepositValue.tokenAddress, nftDepositValue.tradeValue, resetNFTDepositData, setShowAccount, setShowNFTDeposit, tokenMap, updateDepositHash, ]) const nftDepositProps: NFTDepositProps = { type: TRADE_TYPE.NFT, handleOnNFTDataChange, getIPFSString, onNFTDepositClick, walletMap: walletLayer1 as WalletMap, coinMap: totalCoinMap as CoinMap, tradeData: nftDepositValue as T, nftDepositBtnStatus: btnStatus, isNFTCheckLoading, baseURL, btnInfo, } const updateBtnStatus = React.useCallback( (error?: ErrorType & any) => { resetBtnInfo() myLog('updateBtnStatus', nftDepositValue) if ( !error && walletLayer1 && nftDepositValue && nftDepositValue.balance && nftDepositValue.tradeValue && sdk.toBig(walletLayer1?.ETH?.count ?? 0).gt(BIGO) && sdk.toBig(nftDepositValue.tradeValue).gt(sdk.toBig(0)) && sdk.toBig(nftDepositValue.tradeValue).lte(sdk.toBig(nftDepositValue?.balance ?? '')) ) { myLog('try to enable nftDeposit btn!', walletLayer1?.ETH?.count) enableBtn() if (!nftDepositValue.isApproved) { myLog('!!---> set labelNFTDepositNeedApprove!!!! belong:', nftDepositValue.tokenAddress) setLabelAndParams('labelNFTDepositNeedApprove', { symbol: nftDepositValue.name ?? 'unknown NFT', }) } } else { if (sdk.toBig(walletLayer1?.ETH?.count ?? 0).eq(BIGO)) { setLabelAndParams('labelNOETH', {}) } myLog('try to disable nftDeposit btn!') disableBtn() } }, [resetBtnInfo, nftDepositValue, walletLayer1, enableBtn, setLabelAndParams, disableBtn], ) React.useEffect(() => { updateBtnStatus() }, [ nftDepositValue?.tokenAddress, nftDepositValue?.nftId, nftDepositValue?.nftType, nftDepositValue?.tradeValue, nftDepositValue?.balance, walletLayer1?.ETH?.count, ]) React.useEffect(() => { updateWalletLayer1() const subscription = subject.subscribe((props) => { myLog('subscription Deposit DepsitNFT') switch (props.status) { case DepositCommands.DepsitNFT: onNFTDepositClick() break default: break } }) return () => { subscription.unsubscribe() } }, [subject]) return { nftDepositProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useNFTMintAdvance.ts ================================================ import React from 'react' import { AccountStep, NFTMintAdvanceProps, useOpenModals } from '@loopring-web/component-lib' import { AccountStatus, CollectionMeta, CustomError, EmptyValueTag, ErrorMap, ErrorType, Explorer, IPFS_HEAD_URL, LIVE_FEE_TIMES, MINT_LIMIT, myLog, NFTSubRouter, RouterPath, SUBMIT_PANEL_QUICK_AUTO_CLOSE, TradeNFT, UIERROR_CODE, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { LAST_STEP, NETWORKEXTEND, store, useAccount, useModalData, useSystem, useTokenMap, useWalletLayer2NFT, } from '../../stores' import { useBtnStatus, useMyCollection } from '../common' import { LoopringAPI } from '../../api_wrapper' import { useChargeFees, useWalletLayer2Socket, walletLayer2Service } from '../../services' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useTranslation } from 'react-i18next' import { getIPFSString, getTimestampDaysLater, makeMeta } from '../../utils' import { ActionResultCode, DAYS } from '../../defs' import { useHistory } from 'react-router-dom' import Web3 from 'web3' import { isAccActivated } from './useCheckAccStatus' const CID = require('cids') export const useNFTMintAdvance = , Co extends CollectionMeta, I>() => { const { tokenMap, totalCoinMap } = useTokenMap() const { account } = useAccount() const { exchangeInfo, chainId } = useSystem() const collectionListProps = useMyCollection() const { nftMintAdvanceValue, updateNFTMintAdvanceData, resetNFTMintAdvanceData } = useModalData() const { btnStatus, btnInfo, enableBtn, disableBtn, setLabelAndParams, resetBtnInfo } = useBtnStatus() const { t } = useTranslation('common') const { checkHWAddr, updateHW } = useWalletInfo() const { page, updateWalletLayer2NFT } = useWalletLayer2NFT() const [isNotAvailableCID, setIsNotAvailableCID] = React.useState( undefined, ) const [isNotAvailableTokenAddress, setIsNotAvailableTokenAddress] = React.useState< undefined | { reason: string } >(undefined) const [isNFTCheckLoading, setIsNFTCheckLoading] = React.useState(false) const { setShowAccount, setShowNFTMintAdvance } = useOpenModals() const { baseURL, etherscanBaseUrl } = useSystem() const history = useHistory() const { chargeFeeTokenList, isFeeNotEnough, resetIntervalTime, checkFeeIsEnough, handleFeeChange, feeInfo, } = useChargeFees({ tokenAddress: nftMintAdvanceValue?.collectionMeta?.contractAddress, //tokenAddress?.toLowerCase(), requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, updateData: ({ fee }) => { updateNFTMintAdvanceData({ ...nftMintAdvanceValue, fee, }) }, }) const checkAvailable = ({ nftMintAdvanceValue, isFeeNotEnough, isNotAvailableCID, }: { nftMintAdvanceValue: Partial isFeeNotEnough: any isNotAvailableCID: undefined | { reason: string } }) => { return ( nftMintAdvanceValue && nftMintAdvanceValue.royaltyPercentage !== undefined && Number.isInteger(Number(nftMintAdvanceValue.royaltyPercentage)) && nftMintAdvanceValue.royaltyPercentage >= 0 && nftMintAdvanceValue.royaltyPercentage <= 10 && nftMintAdvanceValue.tradeValue && Number(nftMintAdvanceValue.tradeValue) > 0 && Number(nftMintAdvanceValue.tradeValue) <= MINT_LIMIT && (nftMintAdvanceValue.image !== undefined || nftMintAdvanceValue.name !== undefined) && nftMintAdvanceValue.nftId && nftMintAdvanceValue.fee && nftMintAdvanceValue.fee.belong && nftMintAdvanceValue.fee.feeRaw && !isFeeNotEnough.isFeeNotEnough && !isNotAvailableCID ) } const updateBtnStatus = React.useCallback( (error?: ErrorType & any) => { resetBtnInfo() if ( !error && checkAvailable({ nftMintAdvanceValue: nftMintAdvanceValue as any, isFeeNotEnough, isNotAvailableCID, }) ) { enableBtn() return } if (isNotAvailableCID) { setLabelAndParams('labelNFTMintWrongCIDBtn', {}) } else if ( (!nftMintAdvanceValue.image && !nftMintAdvanceValue.name) || !( nftMintAdvanceValue.royaltyPercentage !== undefined && Number.isInteger(nftMintAdvanceValue.royaltyPercentage / 1) && nftMintAdvanceValue.royaltyPercentage >= 0 && nftMintAdvanceValue.royaltyPercentage <= 10 ) ) { setLabelAndParams('labelNFTMintNoMetaBtn', {}) } disableBtn() myLog('try to disable nftMintAdvance btn!') }, [ isNotAvailableCID, isFeeNotEnough, resetBtnInfo, nftMintAdvanceValue, enableBtn, setLabelAndParams, disableBtn, ], ) useWalletLayer2Socket({}) React.useEffect(() => { updateBtnStatus() }, [isFeeNotEnough.isFeeNotEnough, isNotAvailableCID, nftMintAdvanceValue, feeInfo]) const resetDefault = React.useCallback(() => { checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) resetNFTMintAdvanceData() }, [checkFeeIsEnough, updateNFTMintAdvanceData]) const processRequest = React.useCallback( async (request: sdk.NFTMintRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account try { if (connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } const response = await LoopringAPI.userAPI?.submitNFTMint( { request, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, } as any, ) myLog('submitNFTMintAdvance:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowAccount({ isShow: true, step: AccountStep.NFTMint_In_Progress, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, }, }) setShowAccount({ isShow: true, step: AccountStep.NFTMint_Success, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, lastStep: LAST_STEP.nftMintAdv, hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-nftMintAdvance`, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } walletLayer2Service.sendUserUpdate() history.push({ pathname: `${RouterPath.nft}/${NFTSubRouter.assetsNFT}/byCollection/${nftMintAdvanceValue?.collectionMeta?.contractAddress}--${nftMintAdvanceValue?.collectionMeta?.id}`, }) resetDefault() await sdk.sleep(SUBMIT_PANEL_QUICK_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.NFTMint_Success ) { setShowAccount({ isShow: false }) } } } catch (e: any) { myLog('useADNFTMint', e) const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.NFTMint_First_Method_Denied, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, isAdvanceMint: true, }, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.NFTMint_Denied, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, isAdvanceMint: true, }, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } else if ([102040].includes((e as sdk.RESULT_INFO)?.code || 0)) { walletLayer2Service.sendUserUpdate() history.push({ pathname: `${RouterPath.nft}/${NFTSubRouter.assetsNFT}/byCollection/${nftMintAdvanceValue?.collectionMeta?.contractAddress}--${nftMintAdvanceValue?.collectionMeta?.id}`, }) resetDefault() } setShowAccount({ isShow: true, step: AccountStep.NFTMint_Failed, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, setShowAccount, nftMintAdvanceValue.name, nftMintAdvanceValue.tradeValue, resetDefault, checkFeeIsEnough, updateWalletLayer2NFT, page, updateHW, ], ) const handleOnNFTDataChange = React.useCallback( async (data: Partial) => { let shouldUpdate: any = { balance: MINT_LIMIT, } if (data.hasOwnProperty('tokenAddress')) { let collectionMeta: any = undefined setIsNFTCheckLoading(true) setIsNotAvailableTokenAddress(undefined) if (data.tokenAddress === '') { shouldUpdate = { tokenAddress: undefined, collectionMeta: undefined, } } else { try { const response = await LoopringAPI.userAPI ?.getUserOwenCollection( { owner: account.accAddress, tokenAddress: data.tokenAddress, // @ts-ignore isMintable: true, }, account.apiKey, ) .catch((_error) => { throw new CustomError(ErrorMap.TIME_OUT) }) if ( (response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message)) || !response.collections.length ) { throw new CustomError(ErrorMap.ERROR_COLLECTION_INFO) } collectionMeta = (response as any).collections[0] as CollectionMeta shouldUpdate = { tokenAddress: collectionMeta?.contractAddress, collectionMeta: collectionMeta, } } catch (error) { shouldUpdate = { tokenAddress: undefined, collectionMeta: undefined, } setIsNotAvailableTokenAddress({ reason: ErrorMap.ERROR_COLLECTION_INFO.messageKey, }) } } setIsNFTCheckLoading(false) } else if (!nftMintAdvanceValue.tokenAddress) { resetNFTMintAdvanceData() } else if ( nftMintAdvanceValue.tokenAddress && data.hasOwnProperty('nftIdView') && LoopringAPI.nftAPI && nftMintAdvanceValue.nftIdView !== data.nftIdView ) { setIsNFTCheckLoading(true) if (!data.nftIdView) { shouldUpdate = { nftIdView: undefined, nftId: undefined, } } else { let nftId: string = '' try { let cid: string if (/^Qm[a-zA-Z0-9]{44}$/.test(data.nftIdView)) { cid = data.nftIdView } else { cid = new CID(data.nftIdView).toV0() } nftId = LoopringAPI.nftAPI.ipfsCid0ToNftID(cid) shouldUpdate = { nftId, ...shouldUpdate, } } catch (error: any) { myLog('handleOnNFTDataChange -> data.nftId', error) // setIsAvailableId(false); setIsNotAvailableCID({ reason: ErrorMap.IPFS_CID_TO_NFTID_ERROR.messageKey, }) shouldUpdate = { nftId: undefined, nftIdView: undefined, } } if (nftId && nftId !== '') { try { const value = await fetch(getIPFSString(`${IPFS_HEAD_URL}${data.nftIdView}`, baseURL)) .then((response) => response.json()) .catch((_error) => { setIsNotAvailableCID({ reason: ErrorMap.IPFS_TIME_OUT.messageKey, }) throw ErrorMap.IPFS_TIME_OUT }) if (value) { shouldUpdate = { nftId: nftId, name: value.name ?? t('labelUnknown'), image: value.image, collection_metadata: value.collection_metadata, description: value.description ?? EmptyValueTag, royaltyPercentage: isNaN(value.royalty_percentage) ? undefined : Number(value.royalty_percentage), ...shouldUpdate, } setIsNotAvailableCID(undefined) } else { setIsNotAvailableCID({ reason: ErrorMap.ERROR_COLLECTION_EMPTY.messageKey, }) throw ErrorMap.ERROR_COLLECTION_EMPTY } } catch (error: any) { console.log('Mint NFT read resource error:', error) shouldUpdate = { nftId: undefined, nftIdView: undefined, name: undefined, image: undefined, description: undefined, balance: undefined, ...shouldUpdate, } setIsNotAvailableCID({ reason: error.messageKey }) } } } } else { shouldUpdate = { ...shouldUpdate, ...data, } } setIsNFTCheckLoading(false) updateNFTMintAdvanceData({ ...shouldUpdate, }) }, [nftMintAdvanceValue, t, updateNFTMintAdvanceData], ) const onNFTMintAdvanceClick = React.useCallback( async (_nftMintAdvanceValue, isHardwareRetry: boolean = false) => { let result = { code: ActionResultCode.NoError } // pattern="^Qm[a-zA-Z0-9]{44}$" if ( LoopringAPI.userAPI && LoopringAPI.nftAPI && exchangeInfo && account.readyState === AccountStatus.ACTIVATED && nftMintAdvanceValue && nftMintAdvanceValue.collectionMeta?.contractAddress && checkAvailable({ nftMintAdvanceValue: nftMintAdvanceValue as any, isFeeNotEnough, isNotAvailableCID, }) ) { setShowNFTMintAdvance({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.NFTMint_WaitForAuth, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, }, }) try { const { accountId, accAddress, apiKey } = account const fee = sdk.toBig( nftMintAdvanceValue?.fee?.feeRaw ?? nftMintAdvanceValue?.fee?.__raw__?.feeRaw ?? 0, ) const feeToken = tokenMap[nftMintAdvanceValue?.fee?.belong ?? ''] const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId, sellTokenId: feeToken.tokenId, }, apiKey, ) const req: sdk.NFTMintRequestV3 = { exchange: exchangeInfo.exchangeAddress, minterId: accountId, minterAddress: accAddress, toAccountId: accountId, toAddress: accAddress, nftType: 0, tokenAddress: nftMintAdvanceValue.collectionMeta.contractAddress, nftId: nftMintAdvanceValue.nftId ?? '', amount: nftMintAdvanceValue.tradeValue?.toString() ?? '', validUntil: getTimestampDaysLater(DAYS), storageId: storageId?.offchainId, maxFee: { tokenId: feeToken.tokenId, amount: fee.toString(), // TEST: fee.toString(), }, counterFactualNftInfo: { nftOwner: account.accAddress, nftFactory: nftMintAdvanceValue.collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[chainId], nftBaseUri: nftMintAdvanceValue.collectionMeta?.baseUri ?? '', }, royaltyPercentage: Math.floor(nftMintAdvanceValue?.royaltyPercentage ?? 0), forceToMint: false, } myLog('onNFTMintAdvanceClick req:', req) processRequest(req, !isHardwareRetry) } catch (e: any) { setShowAccount({ isShow: true, step: AccountStep.NFTMint_Failed, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) } return } else { result.code = ActionResultCode.DataNotReady } }, [ account, chainId, checkAvailable, exchangeInfo, isNotAvailableCID, isFeeNotEnough, nftMintAdvanceValue, processRequest, setShowAccount, setShowNFTMintAdvance, tokenMap, ], ) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.NFTMint_WaitForAuth, info: { symbol: nftMintAdvanceValue.name, value: nftMintAdvanceValue.tradeValue, }, }) onNFTMintAdvanceClick({}, isHardwareRetry) }, [nftMintAdvanceValue.name, nftMintAdvanceValue.tradeValue, processRequest, setShowAccount], ) const nftMintAdvanceProps: NFTMintAdvanceProps = { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, isNFTCheckLoading, isNotAvailableTokenAddress, isNotAvailableCID, collectionInputProps: { collectionListProps: { ...collectionListProps, size: 'small', }, collection: nftMintAdvanceValue.collectionMeta as Co, onSelected: (item) => { handleOnNFTDataChange({ collectionMeta: item } as unknown as T) }, domain: LoopringAPI.delegate?.getCollectionDomain() ?? '', makeMeta, }, etherscanBaseUrl, baseURL, getIPFSString, handleOnNFTDataChange, onNFTMintClick: onNFTMintAdvanceClick, walletMap: {} as any, coinMap: totalCoinMap as any, tradeData: { ...nftMintAdvanceValue } as any, nftMintBtnStatus: btnStatus, btnInfo, } return { nftMintAdvanceProps, retryBtn, resetIntervalTime, resetDefault, } } ================================================ FILE: packages/core/src/hooks/useractions/useNFTTransfer.ts ================================================ import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { AccountStep, SwitchData, TransferProps, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { AccountStatus, AddressError, CoinMap, CurrencyToTag, EmptyValueTag, EXCHANGE_TYPE, Explorer, FeeInfo, getValuePrecisionThousand, LIVE_FEE_TIMES, myLog, PriceTag, SagaStatus, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, TradeNFT, UIERROR_CODE, WALLET_TYPE, } from '@loopring-web/common-resources' import { BIGO, DAYS, getIPFSString, getTimestampDaysLater, isAccActivated, LAST_STEP, LoopringAPI, store, useAccount, useAddressCheck, useBtnStatus, useChargeFees, useModalData, useSystem, useTokenMap, useTokenPrices, useWalletLayer2, useWalletLayer2WithNFTSocket, volumeToCountAsBigNumber, walletLayer2Service, } from '../../index' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useHistory, useLocation } from 'react-router-dom' import { addressToExWalletMapFn, exWalletToAddressMapFn } from '@loopring-web/core' import { NETWORKEXTEND, useContacts } from '../../stores' import Web3 from 'web3' export const useNFTTransfer = , T>() => { const { setShowAccount, setShowNFTTransfer, setShowNFTDetail, setShowEditContact, modals: { isShowNFTDetail, isShowNFTTransfer: { isShow, info, address: contactAddress, name: contactName, addressType: contactAddressType, }, }, } = useOpenModals() const { tokenMap, totalCoinMap } = useTokenMap() const { account, status: accountStatus } = useAccount() const { exchangeInfo, chainId, baseURL, forexMap } = useSystem() const { currency } = useSettings() const { tokenPrices } = useTokenPrices() const { contacts, updateContacts, errorMessage: contactsErrorMessage, status: contactStatus, } = useContacts() const { updateWalletLayer2 } = useWalletLayer2() const { nftTransferValue, updateNFTWithdrawData, updateNFTTransferData } = useModalData() const history = useHistory() const { search, pathname } = useLocation() const searchParams = new URLSearchParams(search) const [sureItsLayer2, setSureItsLayer2] = React.useState( undefined, ) const [feeWithActive, setFeeWithActive] = React.useState(false) // const [chargeFeeTransferList, setChargeFeeTransferList] = React.useState([ // false, // ]); const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, resetIntervalTime, checkFeeIsEnough, } = useChargeFees({ tokenAddress: nftTransferValue.tokenAddress, // requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, requestType: feeWithActive ? sdk.OffchainNFTFeeReqType.NFT_TRANSFER_AND_UPDATE_ACCOUNT : sdk.OffchainNFTFeeReqType.NFT_TRANSFER, updateData: // React.useCallback( ({ fee, requestType }) => { let _requestType = feeWithActive ? sdk.OffchainNFTFeeReqType.NFT_TRANSFER_AND_UPDATE_ACCOUNT : sdk.OffchainNFTFeeReqType.NFT_TRANSFER if (_requestType === requestType) { const nftTransferValue = store.getState()._router_modalData.nftTransferValue updateNFTTransferData({ ...nftTransferValue, balance: sdk .toBig(nftTransferValue.total ?? 0) .minus(nftTransferValue.locked ?? 0) .toNumber(), fee, }) } }, // [feeWithActive] // ), }) const { chargeFeeTokenList: activeAccountFeeList, checkFeeIsEnough: checkActiveFeeIsEnough, // resetIntervalTime: resetActiveIntervalTime, } = useChargeFees({ isActiveAccount: true, requestType: undefined as any, }) const { address, realAddr, setAddress, addrStatus, isActiveAccount, isActiveAccountFee, isLoopringAddress, isAddressCheckLoading, isSameAddress, isContractAddress, loopringSmartWalletVersion, reCheck, isENSWrong, ens, } = useAddressCheck() React.useEffect(() => { if (loopringSmartWalletVersion?.isLoopringSmartWallet && sureItsLayer2 === undefined) { setSureItsLayer2(WALLET_TYPE.Loopring) } }, [loopringSmartWalletVersion?.isLoopringSmartWallet]) const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const handleOnMemoChange = React.useCallback( (e: React.ChangeEvent) => { const nftTransferValue = store.getState()._router_modalData.nftTransferValue updateNFTTransferData({ ...nftTransferValue, memo: e.target.value, }) }, [updateNFTTransferData], ) const checkBtnStatus = React.useCallback(() => { const contact = contacts?.find((x) => x.contactAddress === realAddr) const ensHasCheck = (contact?.ens || ens) ? !isENSWrong : true if ( tokenMap && nftTransferValue.fee?.belong && nftTransferValue?.tradeValue && chargeFeeTokenList.length && !isFeeNotEnough.isFeeNotEnough && !isSameAddress && ensHasCheck && sureItsLayer2 && sdk.toBig(nftTransferValue.tradeValue).gt(BIGO) && sdk.toBig(nftTransferValue.tradeValue).lte(Number(nftTransferValue.balance) ?? 0) && (addrStatus as AddressError) === AddressError.NoError && realAddr ) { enableBtn() myLog('enableBtn') return } disableBtn() }, [ tokenMap, nftTransferValue.fee?.belong, nftTransferValue.tradeValue, nftTransferValue.balance, chargeFeeTokenList.length, isFeeNotEnough?.isFeeNotEnough, isSameAddress, sureItsLayer2, addrStatus, realAddr, disableBtn, enableBtn, contacts, ]) React.useEffect(() => { checkBtnStatus() }, [ address, addrStatus, sureItsLayer2, isFeeNotEnough.isFeeNotEnough, isSameAddress, nftTransferValue.tradeValue, nftTransferValue.fee, ]) const walletLayer2Callback = React.useCallback(() => { checkFeeIsEnough() }, [checkFeeIsEnough]) useWalletLayer2WithNFTSocket({ walletLayer2Callback }) const resetDefault = React.useCallback(() => { if (info?.isRetry) { checkFeeIsEnough() return } if (contactsErrorMessage) { updateContacts() } checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) // checkActiveFeeIsEnough({ // isRequiredAPI: true, // // intervalTime: LIVE_FEE_TIMES, // }); if (nftTransferValue.nftData) { updateNFTTransferData({ balance: sdk .toBig(nftTransferValue.total ?? 0) .minus(nftTransferValue.locked ?? 0) .toNumber(), belong: nftTransferValue.name as any, tradeValue: undefined, fee: feeInfo, address: address ? address : '*', }) } else { updateNFTTransferData({ fee: feeInfo, belong: '', balance: 0, tradeValue: 0, address: '*', }) } if (contactAddress) { setAddress(contactAddress) } else { setAddress('') } }, [ info?.isRetry, contactsErrorMessage, checkFeeIsEnough, nftTransferValue.nftData, nftTransferValue.total, nftTransferValue.locked, nftTransferValue.name, contactAddress, updateContacts, updateNFTTransferData, feeInfo, address, setAddress, ]) React.useEffect(() => { if (isShow || info?.isShowLocal) { updateWalletLayer2() resetDefault() } else { resetIntervalTime() checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: undefined as any, }) } return () => { resetIntervalTime() setAddress('') checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: undefined as any, }) } }, [isShow, info?.isShowLocal]) React.useEffect(() => { if (accountStatus === SagaStatus.UNSET && account.readyState === AccountStatus.ACTIVATED) { // myLog('useEffect nftTransferValue.address:', nftTransferValue.address) setAddress(nftTransferValue.address ? nftTransferValue.address : '') } }, [setAddress, nftTransferValue.address, accountStatus, account.readyState]) const { checkHWAddr, updateHW } = useWalletInfo() const [lastRequest, setLastRequest] = React.useState({}) const processRequest = React.useCallback( async (request: sdk.OriginNFTTransferRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account try { if (connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } setLastRequest({ request }) const response = await LoopringAPI.userAPI?.submitNFTInTransfer( { request, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submitInternalTransfer:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowNFTTransfer({ isShow: false }) // setIsConfirmTransfer(false); setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_In_Progress, }) setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_Success, info: { symbol: nftTransferValue.name, hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-nftTransfer-${account.accountId}-${ request.token.tokenId }-${request.storageId}`, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } setShowNFTDetail({ ...isShowNFTDetail, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) updateNFTWithdrawData({ ...isShowNFTDetail, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) updateNFTTransferData({ ...isShowNFTDetail, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) walletLayer2Service.sendUserUpdate() // resetNFTTransferData(); await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.NFTTransfer_Success ) { setShowAccount({ isShow: false }) searchParams.delete('detail') history.push(pathname + '?' + searchParams.toString()) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_First_Method_Denied, info: { symbol: nftTransferValue.name, }, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_User_Denied, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true, requestType: feeWithActive ? sdk.OffchainNFTFeeReqType.NFT_TRANSFER_AND_UPDATE_ACCOUNT : sdk.OffchainNFTFeeReqType.NFT_TRANSFER, }) } setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_Failed, info: { symbol: nftTransferValue.name, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, setShowNFTTransfer, setShowAccount, nftTransferValue.name, setShowNFTDetail, isShowNFTDetail, updateNFTWithdrawData, updateNFTTransferData, updateHW, searchParams, history, pathname, checkFeeIsEnough, feeWithActive, ], ) const onTransferClick = React.useCallback( async (_nftTransferValue, isFirstTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const nftTransferValue = { ...store.getState()._router_modalData.nftTransferValue, // tradeValue: _nftTransferValue.tradeValue, // balance: _nftTransferValue.balance, } if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && connectProvides.usedWeb3 && nftTransferValue?.nftData && nftTransferValue?.fee?.belong && nftTransferValue?.fee?.feeRaw && nftTransferValue.tradeValue && nftTransferValue.tokenId && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_WaitForAuth, }) const feeToken = tokenMap[nftTransferValue.fee.belong] const feeRaw = nftTransferValue.fee.feeRaw ?? nftTransferValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const tradeValue = nftTransferValue.tradeValue ?? _nftTransferValue.tradeValue const balance = nftTransferValue.balance ?? _nftTransferValue.balance const isExceedBalance = sdk.toBig(tradeValue).gt(balance) if (isExceedBalance) { throw Error('overflow balance') } const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: nftTransferValue.tokenId, }, apiKey, ) const req: sdk.OriginNFTTransferRequestV3 = { exchange: exchangeInfo.exchangeAddress, fromAccountId: accountId, fromAddress: accAddress, toAccountId: 0, toAddress: realAddr ? realAddr : address, storageId: storageId?.offchainId, token: { tokenId: nftTransferValue.tokenId, nftData: nftTransferValue.nftData, amount: tradeValue.toString(), }, payPayeeUpdateAccount: !isActiveAccountFee && feeWithActive, maxFee: { tokenId: feeToken.tokenId, amount: fee.toString(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), memo: nftTransferValue.memo, } myLog('nftTransfer req:', req) processRequest(req, isFirstTime) } catch (e: any) { setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_Failed, info: { symbol: nftTransferValue?.name, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, }, }) } } else { return } }, [ account, tokenMap, exchangeInfo, setShowAccount, realAddr, address, processRequest, isActiveAccountFee, feeWithActive, ], ) // const activeFee = React.useMemo(() => { // // return activeAccountFeeList?.find( // // (item: any) => item.belong == feeInfo.belong // // ); // }, [feeInfo, activeAccountFeeList]); const activeAccountPrice = React.useMemo(() => { if ( realAddr !== '' && !isActiveAccount && activeAccountFeeList.length && activeAccountFeeList[0] && tokenPrices && activeAccountFeeList[0].feeRaw ) { const feeInfo: FeeInfo = activeAccountFeeList[0] const feeU: any = volumeToCountAsBigNumber(feeInfo.belong, feeInfo.feeRaw ?? 0)?.times( tokenPrices[feeInfo.belong], ) ?? undefined return feeU && currency && forexMap[currency] ? '~' + PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( // @ts-ignore feeU * forexMap[currency], 2, 2, 2, true, { floor: true }, ) : EmptyValueTag } else { return } // return activeAccountFeeList?.find( // (item: any) => item.belong == feeInfo.belong // ); }, [realAddr, isActiveAccount, activeAccountFeeList, tokenPrices, currency, forexMap]) const handlePanelEvent = React.useCallback( async (data: SwitchData, _switchType: 'Tomenu' | 'Tobutton') => { return new Promise((res: any) => { if (data.to === 'button') { if (data.tradeData.belong) { updateNFTTransferData({ belong: data.tradeData.belong, tradeValue: data.tradeData?.tradeValue, balance: data.tradeData.balance, address: '*', }) } else { updateNFTTransferData({ belong: undefined, tradeValue: undefined, balance: undefined, address: '*', }) } } res() }) }, [updateNFTTransferData], ) React.useEffect(() => { if (realAddr !== '' && isActiveAccount === false) { checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: 'TRANSFER_ACTIVE', }) } }, [isActiveAccount, realAddr]) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.NFTTransfer_WaitForAuth, }) processRequest(lastRequest, !isHardwareRetry) }, [lastRequest, processRequest, setShowAccount], ) React.useEffect(() => { const { contacts } = store.getState().contacts const contact = contacts?.find( (x) => x.contactAddress?.toLowerCase() === realAddr?.toLowerCase(), ) if (isShow === false) { setSureItsLayer2(undefined) } else if (contact?.addressType !== undefined) { const found = contact.addressType ? addressToExWalletMapFn(contact.addressType) : undefined setSureItsLayer2(found) } if ( isShow && contactStatus == SagaStatus.UNSET && contact && realAddr?.toLowerCase() == contact?.contactAddress?.toLowerCase() ) { reCheck() } }, [realAddr, isShow, contactStatus]) const nftTransferProps: TransferProps = { handleOnMemoChange, memo: nftTransferValue.memo ?? '', type: TRADE_TYPE.NFT, addressDefault: address, realAddr, lastFailed: store.getState().modals.isShowAccount.info?.lastFailed === LAST_STEP.nftTransfer, handleSureItsLayer2: (sure: WALLET_TYPE | EXCHANGE_TYPE) => { const found = exWalletToAddressMapFn(sure)! const contact = contacts?.find((x) => x.contactAddress === realAddr) if (!account.isContractAddress && contact) { LoopringAPI.contactAPI?.updateContact( { ...contact, isHebao: !!(account.isContractAddress || account.isCFAddress), accountId: account.accountId, addressType: found, }, account.apiKey, ) updateContacts() } setSureItsLayer2(sure) }, // isConfirmTransfer, sureItsLayer2, tradeData: { ...nftTransferValue } as unknown as R, coinMap: totalCoinMap as CoinMap, walletMap: {}, transferBtnStatus: btnStatus, onTransferClick, handleFeeChange, addrStatus, feeInfo, chargeFeeTokenList, activeAccountPrice, // activeAccountFeeList, // chargeFeeTransferList, isFeeNotEnough, handlePanelEvent, isLoopringAddress, baseURL, getIPFSString, isSameAddress, isAddressCheckLoading, handleOnAddressChange: (value: any) => { checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: undefined as any, }) setAddress((state) => { if (state !== value || '') { // flag = true; setFeeWithActive((state) => { if (state !== false) { checkFeeIsEnough({ isRequiredAPI: true, requestType: sdk.OffchainFeeReqType.TRANSFER, }) } return false }) } return value || '' }) }, feeWithActive, isActiveAccount, isActiveAccountFee, handleOnFeeWithActive: (value: boolean) => { setFeeWithActive(value) if (value && !isActiveAccountFee) { checkFeeIsEnough({ isRequiredAPI: true, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER_AND_UPDATE_ACCOUNT, }) } else { checkFeeIsEnough({ isRequiredAPI: true, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, }) } }, isSmartContractAddress: isContractAddress, isFromContact: contactAddress ? true : false, contact: contactAddress ? { address: contactAddress, name: contactName!, addressType: contactAddressType!, } : undefined, loopringSmartWalletVersion, contacts, isENSWrong, geUpdateContact: () => { if (isENSWrong) { const contact = contacts?.find((x) => x.contactAddress === realAddr) setShowEditContact({ isShow: true, info: { ...contact, isENSWrong, }, }) } }, ens, } // const cancelNFTTransfer = () => { // resetDefault(); // }; return { nftTransferProps, retryBtn, // cancelNFTTransfer, } } ================================================ FILE: packages/core/src/hooks/useractions/useNFTWithdraw.ts ================================================ import React from 'react' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { AccountStep, SwitchData, useOpenModals, useSettings, WithdrawProps } from '@loopring-web/component-lib' import { AccountStatus, AddressError, CoinMap, EXCHANGE_TYPE, Explorer, LIVE_FEE_TIMES, myLog, SagaStatus, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, TradeNFT, UIERROR_CODE, WALLET_TYPE, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { BIGO, DAYS, fiatNumberDisplaySafe, getIPFSString, getTimestampDaysLater, isAccActivated, LAST_STEP, LoopringAPI, store, tryFn, useAccount, useAddressCheck, useBtnStatus, useChargeFees, useModalData, useSystem, useTokenMap, useWalletLayer2NFT, useWalletLayer2WithNFTSocket, walletLayer2Service, } from '../../index' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useHistory, useLocation } from 'react-router-dom' import { addressToExWalletMapFn, exWalletToAddressMapFn } from '@loopring-web/core' import { useContacts, useTokenPrices } from '../../stores' import Web3 from 'web3' import Decimal from 'decimal.js' import { ethers } from 'ethers' export const useNFTWithdraw = , T>() => { const { modals: { isShowNFTDetail, isShowNFTWithdraw: { isShow, info, address: contactAddress }, }, setShowNFTWithdraw, setShowNFTDetail, setShowAccount, setShowEditContact, } = useOpenModals() const { updateContacts, contacts, errorMessage: contactsErrorMessage, status: contactStatus, } = useContacts() const { tokenMap, totalCoinMap, disableWithdrawList } = useTokenMap() const { account } = useAccount() const { exchangeInfo, chainId, baseURL } = useSystem() const { page, updateWalletLayer2NFT } = useWalletLayer2NFT() const history = useHistory() const { search, pathname } = useLocation() const searchParams = new URLSearchParams(search) const { nftWithdrawValue, updateNFTTransferData, updateNFTWithdrawData, resetNFTWithdrawData } = useModalData() const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, resetIntervalTime, checkFeeIsEnough, } = useChargeFees({ requestType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, tokenAddress: nftWithdrawValue.tokenAddress, deployInWithdraw: nftWithdrawValue.isCounterFactualNFT && nftWithdrawValue.deploymentStatus === 'NOT_DEPLOYED', updateData: ({ fee }) => { const nftWithdrawValue = store.getState()._router_modalData.nftWithdrawValue updateNFTWithdrawData({ ...nftWithdrawValue, balance: sdk .toBig(nftWithdrawValue.total ?? 0) .minus(nftWithdrawValue.locked ?? 0) .toNumber(), fee, }) }, }) const { checkHWAddr, updateHW } = useWalletInfo() const [sureIsAllowAddress, setSureIsAllowAddress] = React.useState< EXCHANGE_TYPE | WALLET_TYPE | undefined >(undefined) const [lastRequest, setLastRequest] = React.useState({}) const { address, realAddr, setAddress, addrStatus, isCFAddress, isContract1XAddress, isAddressCheckLoading, loopringSmartWalletVersion, reCheck, isENSWrong, ens, } = useAddressCheck() React.useEffect(() => { if (loopringSmartWalletVersion?.isLoopringSmartWallet && sureIsAllowAddress === undefined) { setSureIsAllowAddress(WALLET_TYPE.Loopring) } }, [loopringSmartWalletVersion?.isLoopringSmartWallet]) const isNotAvailableAddress = isContract1XAddress ? 'isContract1XAddress' : undefined const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const checkBtnStatus = React.useCallback(() => { const contact = contacts?.find((x) => x.contactAddress === realAddr) const ensHasCheck = (contact?.ens || ens) ? !isENSWrong : true if ( tokenMap && nftWithdrawValue?.fee?.belong && nftWithdrawValue.fee?.feeRaw && nftWithdrawValue?.tradeValue && sdk.toBig(nftWithdrawValue.tradeValue).gt(BIGO) && sdk.toBig(nftWithdrawValue.tradeValue).lte(Number(nftWithdrawValue.balance) ?? 0) && (addrStatus as AddressError) === AddressError.NoError && !isFeeNotEnough.isFeeNotEnough && !isNotAvailableAddress && ensHasCheck && (info?.isToMyself || sureIsAllowAddress) && realAddr ) { enableBtn() myLog('enableBtn') return } disableBtn() }, [ contacts, tokenMap, nftWithdrawValue.fee?.belong, nftWithdrawValue.fee?.feeRaw, nftWithdrawValue.tradeValue, nftWithdrawValue.balance, addrStatus, isFeeNotEnough, isNotAvailableAddress, info?.isToMyself, sureIsAllowAddress, realAddr, disableBtn, enableBtn, ]) React.useEffect(() => { checkBtnStatus() }, [ address, addrStatus, isFeeNotEnough.isFeeNotEnough, nftWithdrawValue.fee, nftWithdrawValue.tradeValue, isNotAvailableAddress, sureIsAllowAddress, ]) const walletLayer2Callback = React.useCallback(() => { checkFeeIsEnough() }, []) useWalletLayer2WithNFTSocket({ walletLayer2Callback }) const resetDefault = React.useCallback(() => { if (info?.isRetry) { checkFeeIsEnough() return } if (contactsErrorMessage) { updateContacts() } checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) if (nftWithdrawValue.nftData) { updateNFTWithdrawData({ // ...nftWithdrawValue, balance: sdk .toBig(nftWithdrawValue.total ?? 0) .minus(nftWithdrawValue.locked ?? 0) .toNumber(), belong: nftWithdrawValue.name, tradeValue: undefined, fee: feeInfo, address: info?.isToMyself ? account.accAddress : '*', }) } else { updateNFTWithdrawData({ fee: feeInfo, belong: '', balance: 0, tradeValue: undefined, address: info?.isToMyself ? account.accAddress : '*', }) } if (info?.isToMyself) { setAddress(account.accAddress) } else if (contactAddress) { setAddress(contactAddress) } else { setAddress('') } }, [ checkFeeIsEnough, nftWithdrawValue, info?.isRetry, info?.isToMyself, updateNFTWithdrawData, feeInfo, account.accAddress, setAddress, contactAddress, contactsErrorMessage, ]) React.useEffect(() => { if (isShow || info?.isShowLocal) { resetDefault() } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [isShow, info?.isShowLocal]) const processRequest = React.useCallback( async (request: sdk.NFTWithdrawRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account try { if (connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } myLog('nftWithdraw processRequest:', isHWAddr, isNotHardwareWallet) const response = await LoopringAPI.userAPI.submitNFTWithdraw( { request, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === 'unknown' ? 1 : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submitNFTWithdraw:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowNFTWithdraw({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_In_Progress, }) setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_Success, info: { symbol: nftWithdrawValue.name, hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-nftWithdraw${account.accountId}-${ request.token.tokenId }-${request.storageId}`, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } setShowNFTDetail({ ...isShowNFTDetail, deploymentStatus: isShowNFTDetail.deploymentStatus === sdk.DEPLOYMENT_STATUS.NOT_DEPLOYED ? sdk.DEPLOYMENT_STATUS.DEPLOYING : isShowNFTDetail.deploymentStatus, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) updateNFTWithdrawData({ ...isShowNFTDetail, deploymentStatus: isShowNFTDetail.deploymentStatus === sdk.DEPLOYMENT_STATUS.NOT_DEPLOYED ? sdk.DEPLOYMENT_STATUS.DEPLOYING : isShowNFTDetail.deploymentStatus, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) updateNFTTransferData({ ...isShowNFTDetail, deploymentStatus: isShowNFTDetail.deploymentStatus === sdk.DEPLOYMENT_STATUS.NOT_DEPLOYED ? sdk.DEPLOYMENT_STATUS.DEPLOYING : isShowNFTDetail.deploymentStatus, locked: ( Number(isShowNFTDetail?.locked ?? 0) + Number(request?.token?.amount) ).toString(), }) walletLayer2Service.sendUserUpdate() await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.NFTWithdraw_Success ) { setShowAccount({ isShow: false }) searchParams.delete('detail') history.push(pathname + '?' + searchParams.toString()) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_First_Method_Denied, info: { symbol: nftWithdrawValue.name, }, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_User_Denied, info: { symbol: nftWithdrawValue.name, }, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_Failed, info: { symbol: nftWithdrawValue.name, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, setShowAccount, nftWithdrawValue.name, checkFeeIsEnough, updateWalletLayer2NFT, page, setShowNFTDetail, resetNFTWithdrawData, updateHW, ], ) const handleNFTWithdraw = React.useCallback( async (_nftWithdrawToken: any, address, isFirstTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const nftWithdrawToken = { ...store.getState()._router_modalData.nftWithdrawValue, ..._nftWithdrawToken, } if ( readyState === AccountStatus.ACTIVATED && tokenMap && exchangeInfo && connectProvides.usedWeb3 && address && LoopringAPI.userAPI && nftWithdrawValue.fee?.belong && nftWithdrawValue?.fee?.feeRaw && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_WaitForAuth, }) const feeToken = tokenMap[nftWithdrawValue.fee.belong] const feeRaw = nftWithdrawValue.fee.feeRaw ?? nftWithdrawValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) // const fee = sdk.toBig(nftWithdrawValue.fee.__raw__?.feeRaw ?? 0); const tradeValue = nftWithdrawToken.tradeValue const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accountId, sellTokenId: nftWithdrawToken.tokenId, }, apiKey, ) const request: sdk.NFTWithdrawRequestV3 = { exchange: exchangeInfo.exchangeAddress, owner: accAddress, to: address, accountId: account.accountId, storageId: storageId?.offchainId, token: { tokenId: nftWithdrawToken.tokenId, nftData: nftWithdrawToken.nftData, amount: tradeValue.toString(), }, maxFee: { tokenId: feeToken.tokenId, amount: fee.toString(), // TEST: fee.toString(), }, // fastWithdrawalMode: nftWithdrawType2 === WithdrawType.Fast, extraData: '', minGas: 0, validUntil: getTimestampDaysLater(DAYS), } myLog('submitNFTWithdraw:', request) processRequest(request, isFirstTime) } catch (e: any) { sdk.dumpError400(e) setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, }, }) } return true } else { return false } }, [ account, tokenMap, exchangeInfo, nftWithdrawValue.fee?.belong, nftWithdrawValue.fee?.__raw__, nftWithdrawValue.fee?.feeRaw, setShowAccount, processRequest, ], ) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_WaitForAuth, }) processRequest(lastRequest, !isHardwareRetry) }, [lastRequest, processRequest, setShowAccount], ) React.useEffect(() => { const { contacts } = store.getState().contacts const contact = contacts?.find( (x) => x.contactAddress?.toLowerCase() === realAddr?.toLowerCase(), ) if (!isShow) { setSureIsAllowAddress(undefined) } else if (contact?.addressType !== undefined) { const found = contact.addressType ? addressToExWalletMapFn(contact.addressType) : undefined setSureIsAllowAddress(found) } if ( isShow && contactStatus == SagaStatus.UNSET && contact && realAddr?.toLowerCase() == contact?.contactAddress?.toLowerCase() ) { reCheck() } }, [realAddr, isShow, contacts]) const { tokenPrices } = useTokenPrices() const { getValueInCurrency } = useSystem() const { currency } = useSettings() const feeTokenInfo = tokenMap ? tokenMap[feeInfo.belong] : undefined const feeNormalInCurrency = tryFn(() => { return feeTokenInfo && feeInfo.feeRaw && tokenPrices && fiatNumberDisplaySafe( getValueInCurrency( new Decimal(ethers.utils.formatUnits(feeInfo.feeRaw, feeTokenInfo.decimals)) .mul(tokenPrices[feeTokenInfo.symbol]) .toFixed(2), ), currency, ) }, () => undefined) const nftWithdrawProps: WithdrawProps = { handleOnAddressChange: (value: any) => { setAddress(value) }, sureIsAllowAddress, handleSureIsAllowAddress: (value) => { const found = exWalletToAddressMapFn(value) // const found = map.find(x => x[0] === value)![1] const contact = contacts?.find((x) => x.contactAddress === realAddr) if (!account?.isContractAddress && contact) { LoopringAPI.contactAPI ?.updateContact( { ...contact, isHebao: !!(account.isContractAddress || account.isCFAddress), accountId: account.accountId, addressType: found, }, account.apiKey, ) .then(() => { updateContacts() }) } setSureIsAllowAddress(value) }, type: TRADE_TYPE.NFT, addressDefault: address, accAddr: account.accAddress, isNotAvailableAddress, realAddr, isToMyself: info?.isToMyself, disableWithdrawList, tradeData: nftWithdrawValue as any, coinMap: totalCoinMap as CoinMap, walletMap: {}, isCFAddress, getIPFSString, baseURL, lastFailed: store.getState().modals.isShowAccount.info?.lastFailed === LAST_STEP.nftWithdraw, isContractAddress: isContract1XAddress, isAddressCheckLoading, addrStatus, withdrawBtnStatus: btnStatus, withdrawType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, withdrawTypes: { [sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL]: 'Standard', } as any, onWithdrawClick: (tradeData: R) => { if (nftWithdrawValue && nftWithdrawValue.tradeValue) { handleNFTWithdraw(tradeData, realAddr ? realAddr : address) } }, handleWithdrawTypeChange: () => {}, handlePanelEvent: async (data: SwitchData) => { return new Promise((res: any) => { if (data.to === 'button') { if (data.tradeData.belong) { updateNFTWithdrawData({ belong: data.tradeData.belong, tradeValue: data.tradeData?.tradeValue, balance: data.tradeData.balance, address: info?.isToMyself ? account.accAddress : '*', }) } else { updateNFTWithdrawData({ belong: undefined, tradeValue: undefined, balance: undefined, address: info?.isToMyself ? account.accAddress : '*', }) } } res() }) }, handleFeeChange, feeInfo, chargeFeeTokenList, isFeeNotEnough, isLoopringAddress: true, contacts, loopringSmartWalletVersion, isENSWrong, geUpdateContact: () => { if (isENSWrong) { const contact = contacts?.find((x) => x.contactAddress === realAddr) setShowEditContact({ isShow: true, info: { ...contact, isENSWrong, }, }) } }, ens, withdrawMode: { mode: 'normal', showFastMode: false, fastMode: undefined, fastMaxAlert: { show: false, message: '', }, normalMode: { fee: feeNormalInCurrency ? '~' + feeNormalInCurrency : '--', time: '~25 minutes', }, onChange: () => {}, showTrustUI: false }, } as unknown as WithdrawProps return { nftWithdrawProps, retryBtn, } } ================================================ FILE: packages/core/src/hooks/useractions/useRampConfirm.ts ================================================ import { AccountStatus, AddressError, CoinMap, Explorer, FeeInfo, IBData, myLog, TOAST_TIME, TRADE_TYPE, UIERROR_CODE, WALLET_TYPE, WalletMap, } from '@loopring-web/common-resources' import { NETWORKEXTEND, useAccount, useModalData, useSystem, useTokenMap } from '../../stores' import { AccountStep, useOpenModals } from '@loopring-web/component-lib' import React from 'react' import { makeWalletLayer2 } from '../help' import { useChargeFees, useWalletLayer2Socket, walletLayer2Service } from '../../services' import { useBtnStatus } from '../common' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI } from '../../api_wrapper' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { getTimestampDaysLater } from '../../utils' import { DAYS } from '../../defs' import { RAMP_SELL_PANEL } from './useVendor' import { useWalletInfo } from '../../stores/localStore/walletInfo' import Web3 from 'web3' import { isAccActivated } from './useCheckAccStatus' export const useRampConfirm = , I, _C extends FeeInfo>({ sellPanel, setSellPanel, }: { sellPanel: RAMP_SELL_PANEL setSellPanel: (value: RAMP_SELL_PANEL) => void }) => { const { exchangeInfo } = useSystem() const { allowTrade: { raw_data }, } = useSystem() const legalEnable = (raw_data as any)?.legal?.enable const { tokenMap, totalCoinMap } = useTokenMap() const { setShowAccount, modals: { isShowAccount: { info }, }, } = useOpenModals() const { account } = useAccount() const [balanceNotEnough, setBalanceNotEnough] = React.useState(false) const { offRampValue } = useModalData() const { processRequestRampTransfer: processRequest, chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, } = useRampTransPost() const [walletMap, setWalletMap] = React.useState( makeWalletLayer2({ needFilterZero: true }).walletMap ?? ({} as WalletMap), ) const walletLayer2Callback = React.useCallback(() => { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} setWalletMap(walletMap) }, []) useWalletLayer2Socket({ walletLayer2Callback }) const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const { transferRampValue, updateTransferRampData, resetOffRampData } = useModalData() React.useEffect(() => { if ( info?.transferRamp === AccountStep.Transfer_RAMP_Failed && info?.trigger == 'checkFeeIsEnough' ) { checkFeeIsEnough() } }, [info?.transferRamp]) const checkBtnStatus = React.useCallback(() => { if ( tokenMap && chargeFeeTokenList.length && !isFeeNotEnough.isFeeNotEnough && transferRampValue.belong && tokenMap[transferRampValue.belong] && transferRampValue.fee && transferRampValue.fee.belong && transferRampValue.address ) { const sellToken = tokenMap[transferRampValue.belong] const feeToken = tokenMap[transferRampValue.fee.belong] const feeRaw = transferRampValue.fee.feeRaw ?? transferRampValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const balance = sdk.toBig(transferRampValue.balance ?? 0).times('1e' + sellToken.decimals) const tradeValue = sdk .toBig(transferRampValue.tradeValue ?? 0) .times('1e' + sellToken.decimals) const isExceedBalance = tradeValue .plus(feeToken.tokenId === sellToken.tokenId ? fee : '0') .gt(balance) myLog('isExceedBalance', isExceedBalance, fee.toString(), tradeValue.toString()) if (tradeValue && !isExceedBalance) { enableBtn() return } else { disableBtn() // if (isExceedBalance && feeToken.tokenId === sellToken.tokenId) { // // setIsFeeEnough(isFeeNotEnoughtrue); // // setIsFeeNotEnough({ // // isFeeNotEnough: true, // // isOnLoading: false, // // }); // setBalanceNotEnough(true); // } else if (isExceedBalance) { setBalanceNotEnough(true) } // else { // // } } } disableBtn() }, [ chargeFeeTokenList.length, disableBtn, enableBtn, isFeeNotEnough.isFeeNotEnough, tokenMap, transferRampValue.address, transferRampValue.balance, transferRampValue.belong, transferRampValue.fee, transferRampValue.tradeValue, ]) React.useEffect(() => { checkBtnStatus() }, [chargeFeeTokenList, isFeeNotEnough.isFeeNotEnough, transferRampValue]) const onTransferClick = React.useCallback( async (transferRampValue, isFirstTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && connectProvides.usedWeb3 && transferRampValue.address !== '*' && transferRampValue?.fee && transferRampValue?.fee.belong && transferRampValue.fee?.__raw__ && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_WaitForAuth, }) const sellToken = tokenMap[transferRampValue.belong as string] const feeToken = tokenMap[transferRampValue.fee.belong] const feeRaw = transferRampValue.fee.feeRaw ?? transferRampValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) // const balance = sdk // .toBig(transferRampValue.balance ?? 0) // .times("1e" + sellToken.decimals); const tradeValue = sdk .toBig(transferRampValue.tradeValue ?? 0) .times('1e' + sellToken.decimals) // const isExceedBalance = // feeToken.tokenId === sellToken.tokenId && // tradeValue.plus(fee).gt(balance); const finalVol = tradeValue const transferVol = finalVol.toFixed(0, 0) const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: sellToken.tokenId, }, apiKey, ) const req: sdk.OriginTransferRequestV3 = { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: transferRampValue.address, payeeId: 0, storageId: storageId?.offchainId, token: { tokenId: sellToken.tokenId, volume: transferVol, }, maxFee: { tokenId: feeToken.tokenId, volume: fee.toString(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), memo: transferRampValue.memo, } myLog('transfer req:', req) processRequest(req, isFirstTime) } catch (e: any) { // transfer failed setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_Failed, error: { code: UIERROR_CODE.UNKNOWN, message: e.message, } as sdk.RESULT_INFO, }) } } else { return } }, [account, tokenMap, exchangeInfo, setShowAccount, processRequest], ) // const [rampViewProps, setRampViewProps] = // React.useState | undefined>(undefined); const initRampViewProps = React.useCallback(() => { if (offRampValue?.send && window.rampInstance) { const { amount, assetSymbol, destinationAddress } = offRampValue?.send const memo = 'OFF-RAMP Transfer' updateTransferRampData({ belong: assetSymbol, tradeValue: Number(amount), balance: walletMap[assetSymbol]?.count, fee: feeInfo, memo, address: destinationAddress as string, }) return } if (window.rampInstance) { window.rampInstance.close() } else { // setSellPanel(RAMP_SELL_PANEL.LIST); resetOffRampData() } }, [ btnStatus, chargeFeeTokenList, feeInfo, handleFeeChange, isFeeNotEnough, legalEnable, onTransferClick, setSellPanel, totalCoinMap, updateTransferRampData, ]) React.useEffect(() => { if (sellPanel === RAMP_SELL_PANEL.RAMP_CONFIRM) { initRampViewProps() } else { } }, [sellPanel]) const rampViewProps = React.useMemo(() => { const { address, memo, fee, __request__, ...tradeData } = transferRampValue return { type: TRADE_TYPE.TOKEN, disabled: !(legalEnable === true), addressDefault: address, realAddr: address, tradeData, coinMap: totalCoinMap as CoinMap, transferBtnStatus: btnStatus, isLoopringAddress: true, isSameAddress: false, isAddressCheckLoading: WALLET_TYPE.Loopring, feeInfo, handleFeeChange, balanceNotEnough, chargeFeeTokenList, isFeeNotEnough, handleSureItsLayer2: () => undefined, sureItsLayer2: true, onTransferClick, handlePanelEvent: () => undefined, addrStatus: AddressError.NoError, memo, walletMap, handleOnMemoChange: () => undefined, handleOnAddressChange: () => undefined, } as any }, [ balanceNotEnough, btnStatus, chargeFeeTokenList, feeInfo, handleFeeChange, isFeeNotEnough, legalEnable, onTransferClick, totalCoinMap, transferRampValue, walletMap, ]) return { rampViewProps } } export const useRampTransPost = () => { const { account } = useAccount() const { chainId } = useSystem() const { checkHWAddr, updateHW } = useWalletInfo() const { setShowAccount } = useOpenModals() const { updateTransferRampData, resetTransferRampData } = useModalData() const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, // setIsFeeNotEnough, } = useChargeFees({ requestType: sdk.OffchainFeeReqType.TRANSFER, // updateData: ({ fee }) => {}, }) const processRequestRampTransfer = React.useCallback( async (request: sdk.OriginTransferRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account try { if ( connectProvides.usedWeb3 && LoopringAPI.userAPI && window.rampInstance && isAccActivated() ) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } updateTransferRampData({ __request__: request }) const response = await LoopringAPI.userAPI.submitInternalTransfer( { request, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submitInternalTransfer:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } // setIsConfirmTransfer(false); setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_In_Progress, }) await sdk.sleep(TOAST_TIME) setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_Success, info: { hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-transfer`, }, }) if (window.rampInstance) { try { console.log('RAMP WEIGHT display on transfer done') // @ts-ignore window.rampInstance.domNodes.overlay.style.display = '' } catch (e) { console.log('RAMP WEIGHT hidden failed') } } if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } walletLayer2Service.sendUserUpdate() resetTransferRampData() } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_First_Method_Denied, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_User_Denied, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true }) } setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) setShowAccount({ isShow: true, step: AccountStep.Transfer_Failed, }) break } } }, [ account, chainId, checkHWAddr, resetTransferRampData, setShowAccount, updateHW, updateTransferRampData, ], ) return { processRequestRampTransfer, chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, } } ================================================ FILE: packages/core/src/hooks/useractions/useRedpacket.ts ================================================ import React from 'react' import { LoopringAPI } from '../../api_wrapper' import { AccountStep, RedPacketViewStep, useOpenModals } from '@loopring-web/component-lib' import * as sdk from '@loopring-web/loopring-sdk' import { LuckyTokenItemStatus } from '@loopring-web/loopring-sdk' import { store, useAccount, useSystem, useTargetRedPackets } from '../../stores' import { CustomError, ErrorMap, UIERROR_CODE } from '@loopring-web/common-resources' export function useOpenRedpacket() { const { setShowRedPacket, setShowAccount } = useOpenModals() const { chainId } = useSystem() const { account } = useAccount() const { getExclusiveRedpacket } = useTargetRedPackets() const callOpen = React.useCallback(async () => { // setShowAccount({ // isShow: true, // step: AccountStep.RedPacketOpen_Claim_In_Progress, // }); const _info = store.getState().modals.isShowRedPacket.info as sdk.LuckyTokenItemForReceive & { referrer?: string isShouldSharedRely: boolean } // let difference = new Date(_info.validSince).getTime() - Date.now(); if ( _info.status == LuckyTokenItemStatus.COMPLETED || _info.status == LuckyTokenItemStatus.OVER_DUE || _info.tokenAmount.remainCount === 0 ) { setShowRedPacket({ isShow: true, step: RedPacketViewStep.TimeOutPanel, info: { ..._info, }, }) } else { try { if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { let response = await LoopringAPI.luckTokenAPI?.sendLuckTokenClaimBlindBox({ request: { hash: _info?.hash, claimer: account.accAddress, referrer: _info.isShouldSharedRely && _info?.referrer ? _info?.referrer : '', serialNo: _info.serialNo }, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, } as any) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } // setShowAccount({ // isShow: false, // }); setShowRedPacket({ isShow: true, step: RedPacketViewStep.BlindBoxDetail, info: { ..._info, }, }) } else { let response = await LoopringAPI.luckTokenAPI?.sendLuckTokenClaimLuckyToken({ request: { hash: _info?.hash, claimer: account.accAddress, referrer: _info.isShouldSharedRely && _info?.referrer ? _info?.referrer : '', serialNo: _info.serialNo }, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, } as any) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } // setShowAccount({ // isShow: false, // }); setShowRedPacket({ isShow: true, step: RedPacketViewStep.DetailPanel, info: { ..._info, response, claimAmount: (response as any).amount, }, }) } getExclusiveRedpacket() } catch (error: any) { if (error?.code === UIERROR_CODE.ERROR_REDPACKET_CLAIMED) { // setShowAccount({ // isShow: false, // }); if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, step: RedPacketViewStep.BlindBoxDetail, info: { ..._info, }, }) } else { setShowRedPacket({ isShow: true, step: RedPacketViewStep.DetailPanel, info: { ..._info, }, }) } } else if ( [ UIERROR_CODE.ERROR_REDPACKET_CLAIM_TIMEOUT, UIERROR_CODE.ERROR_REDPACKET_CLAIM_OUT, ].includes(error?.code) ) { // setShowAccount({ // isShow: false, // }); setShowRedPacket({ isShow: true, step: RedPacketViewStep.TimeOutPanel, info: { ..._info, }, }) } else { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: error?.message, // @ts-ignore ...(error instanceof Error ? { message: error?.message, stack: error?.stack, } : error ?? {}), }, }) // await sdk.sleep(SUBMIT_PANEL_QUICK_AUTO_CLOSE); // setShowAccount({ // isShow: false, // }); } } } }, [chainId, account]) return { callOpen, } } export const useRedPacketScanQrcodeSuccess = () => { const { setShowAccount, setShowRedPacket, // modals: { isShowAccount }, } = useOpenModals() const { account: { apiKey, accountId }, } = useAccount() const [redPacketInfo, setRedPacketInfo] = React.useState< { hash: string; referrer: string } | undefined >(undefined) const getLuckTokenDetail = React.useCallback(async () => { if (LoopringAPI.luckTokenAPI && redPacketInfo) { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_In_Progress, }) const response = await LoopringAPI.luckTokenAPI.getLuckTokenDetail( { accountId: accountId, hash: redPacketInfo.hash, fromId: 0, showHelper: true, } as any, apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: (response as sdk.RESULT_INFO)?.code, msg: (response as sdk.RESULT_INFO)?.message, ...(response instanceof Error ? { message: response?.message, stack: response?.stack, } : response ?? {}), }, }) } else { const detail = (response as any).detail const luckTokenInfo: sdk.LuckyTokenItemForReceive = detail.luckyToken let difference = new Date(luckTokenInfo.validSince).getTime() - Date.now() if (luckTokenInfo) { setShowAccount({ isShow: false }) if (response.detail?.claimAmount.toString() !== '0') { if (response.detail?.luckyToken.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, step: RedPacketViewStep.BlindBoxDetail, info: { ...luckTokenInfo, }, }) } else { setShowRedPacket({ isShow: true, step: RedPacketViewStep.DetailPanel, info: { ...luckTokenInfo, }, }) } } else if (difference > 0) { // change here if (luckTokenInfo.sender.accountId === accountId) { if (luckTokenInfo.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, referrer: redPacketInfo.referrer, }, step: RedPacketViewStep.BlindBoxDetail, }) } else { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, referrer: redPacketInfo.referrer, }, step: RedPacketViewStep.DetailPanel, }) } } else { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, referrer: redPacketInfo.referrer, }, step: RedPacketViewStep.RedPacketClock, }) } } else if ( luckTokenInfo.status == LuckyTokenItemStatus.COMPLETED || luckTokenInfo.status == LuckyTokenItemStatus.OVER_DUE || // difference + 86400000 < 0 || luckTokenInfo.tokenAmount.remainCount === 0 ) { if (luckTokenInfo.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, }, step: RedPacketViewStep.BlindBoxDetail, }) } else { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, }, step: RedPacketViewStep.TimeOutPanel, }) } } else { const canOpenBlindbox = luckTokenInfo.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX && luckTokenInfo.status === sdk.LuckyTokenItemStatus.PENDING && detail.blindBoxStatus === '' const canOpenLuckyToken = luckTokenInfo.type.mode !== sdk.LuckyTokenClaimType.BLIND_BOX && luckTokenInfo.status === sdk.LuckyTokenItemStatus.PENDING && !detail.claimStatus if (canOpenBlindbox || canOpenLuckyToken) { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, referrer: redPacketInfo.referrer, hideViewDetail: accountId !== luckTokenInfo.sender.accountId, }, step: RedPacketViewStep.OpenPanel, }) } else { if (luckTokenInfo.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, referrer: redPacketInfo.referrer, }, step: RedPacketViewStep.BlindBoxDetail, }) } else { setShowRedPacket({ isShow: true, info: { ...luckTokenInfo, referrer: redPacketInfo.referrer, }, step: RedPacketViewStep.DetailPanel, }) } } } setShowAccount({ isShow: false, }) } else { const error = new CustomError(ErrorMap.ERROR_REDPACKET_EMPTY) setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: UIERROR_CODE.ERROR_REDPACKET_EMPTY, msg: error.message, }, }) } // const luckTokenInfo: sdk.LuckyTokenItemForReceive = (response as any) // .detail.luckyToken; // if (luckTokenInfo) { // setShowAccount({ isShow: false }); // setShowRedPacket({ // isShow: true, // info: { // ...luckTokenInfo, // referrer: redPacketInfo.referrer, // }, // step: RedPacketViewStep.OpenPanel, // }); // } else { // const error = new CustomError(ErrorMap.ERROR_REDPACKET_EMPTY); // setShowAccount({ // isShow: true, // step: AccountStep.RedPacketOpen_Failed, // error: { // code: UIERROR_CODE.ERROR_REDPACKET_EMPTY, // msg: error.message, // }, // }); // } } } }, [redPacketInfo, accountId]) React.useEffect(() => { if (redPacketInfo) { getLuckTokenDetail() } }, [redPacketInfo]) const handleSuccess = React.useCallback( async (data: string) => { const url = new URL(data) const searchParams = new URLSearchParams(url.hash.replace('#/wallet', '')) if (searchParams.has('redpacket') && searchParams.get('id')) { setRedPacketInfo({ hash: searchParams.get('id')?.toString() ?? '', referrer: searchParams.get('referrer')?.toString() ?? '', }) } else { setRedPacketInfo({ hash: '', referrer: '', }) const error = new CustomError(ErrorMap.ERROR_REDPACKET_EMPTY) setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: UIERROR_CODE.ERROR_REDPACKET_EMPTY, msg: error.message, }, }) } }, [apiKey], ) return { handleSuccess, } } ================================================ FILE: packages/core/src/hooks/useractions/useReset.ts ================================================ import React from 'react' import { ResetProps, useOpenModals } from '@loopring-web/component-lib' import { FeeInfo, LIVE_FEE_TIMES } from '@loopring-web/common-resources' import { useBtnStatus, useChargeFees } from '../../index' import * as sdk from '@loopring-web/loopring-sdk' import { useUpdateAccount } from './useUpdateAccount' export const useReset = (): { resetProps: ResetProps } => { const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const { setShowResetAccount, modals: { isShowResetAccount: { isShow }, }, } = useOpenModals() const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, resetIntervalTime, } = useChargeFees({ requestType: sdk.OffchainFeeReqType.UPDATE_ACCOUNT, updateData: () => {}, }) React.useEffect(() => { if (isFeeNotEnough.isFeeNotEnough) { disableBtn() } else { enableBtn() } }, [isFeeNotEnough.isFeeNotEnough]) React.useEffect(() => { if (isShow) { checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) return } else { resetIntervalTime() } return () => { resetIntervalTime() } }, [isShow]) const { goUpdateAccount } = useUpdateAccount() const onResetClick = React.useCallback(() => { setShowResetAccount({ isShow: false }) goUpdateAccount({ isReset: true, feeInfo: feeInfo }) }, [goUpdateAccount, feeInfo]) const resetProps: ResetProps = { isFeeNotEnough, onResetClick, resetBtnStatus: btnStatus, feeInfo, chargeFeeTokenList, handleFeeChange, } return { resetProps: resetProps as ResetProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useStakeTrade.ts ================================================ import React from 'react' import { AccountStep, DeFiSideWrapProps, ToastType, useOpenModals, useToggle, } from '@loopring-web/component-lib' import { AccountStatus, CustomErrorWithCode, DeFiSideCalcData, getValuePrecisionThousand, globalSetup, IBData, myLog, SagaStatus, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_QUICK_AUTO_CLOSE, TradeBtnStatus, TradeStake, } from '@loopring-web/common-resources' import { calcSideStaking, makeWalletLayer2, useStakingMap, useSubmitBtn, useWalletLayer2Socket, } from '@loopring-web/core' import _ from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI, store, useAccount, useSystem, useTokenMap, walletLayer2Service, } from '../../index' import { useTranslation } from 'react-i18next' import { useTradeStake } from '../../stores' export const useStakeTradeJOIN = , I, ACD extends DeFiSideCalcData>({ setToastOpen, symbol: coinSellSymbol, }: { symbol: string setToastOpen: (props: { open: boolean; content: JSX.Element | string; type: ToastType }) => void }) => { const { t } = useTranslation(['common']) const refreshRef = React.createRef() const [isLoading, setIsLoading] = React.useState(false) const { tokenMap } = useTokenMap() const { account } = useAccount() const { setShowAccount } = useOpenModals() const { status: stakingMapStatus, marketMap: stakingMap, getStakingMap } = useStakingMap() const { setShowSupport, setShowTradeIsFrozen } = useOpenModals() const { tradeStake, updateTradeStake, resetTradeStake } = useTradeStake() const { exchangeInfo, allowTrade } = useSystem() const { toggle } = useToggle() const handleOnchange = _.debounce( ({ tradeData, _tradeStake = {} }: { tradeData: T; _tradeStake?: Partial> }) => { const tradeStake = store.getState()._router_tradeStake.tradeStake let _deFiSideCalcData: DeFiSideCalcData = { ...tradeStake.deFiSideCalcData, } as unknown as DeFiSideCalcData let _oldTradeStake = { ...tradeStake, ..._tradeStake, } //_.cloneDeep({ ...tradeStake, ..._tradeStake }); myLog('defi handleOnchange', _oldTradeStake) if (tradeData && coinSellSymbol) { const inputValue = tradeData?.tradeValue?.toString() ?? '0' const tokenSell = tokenMap[coinSellSymbol] const { sellVol, deFiSideCalcData } = calcSideStaking({ inputValue, isJoin: true, deFiSideCalcData: _deFiSideCalcData, tokenSell, }) // @ts-ignore _deFiSideCalcData = { ...deFiSideCalcData, coinSell: { ...tradeData, tradeValue: tradeData?.tradeValue?.toString(), }, } updateTradeStake({ sellToken: tokenSell, sellVol, deFiSideCalcData: { ..._deFiSideCalcData, }, }) } }, globalSetup.wait, ) const resetDefault = React.useCallback( async (clearTrade: boolean = false) => { let walletMap: any = {} let deFiSideCalcDataInit: Partial> = { ...tradeStake.deFiSideCalcData, coinSell: { belong: coinSellSymbol, balance: undefined, tradeValue: tradeStake.deFiSideCalcData?.coinSell?.belong === coinSellSymbol ? tradeStake.deFiSideCalcData?.coinSell?.tradeValue : undefined, }, } try { let item = stakingMap[coinSellSymbol] if (item && stakingMap) { deFiSideCalcDataInit.stakeViewInfo = { ...item } } else { throw new Error('no product') } if (account.readyState === AccountStatus.ACTIVATED) { if (clearTrade === true) { walletLayer2Service.sendUserUpdate() } walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} deFiSideCalcDataInit.coinSell.balance = walletMap[coinSellSymbol]?.count } if (clearTrade || tradeStake.deFiSideCalcData?.coinSell?.tradeValue === undefined) { deFiSideCalcDataInit.coinSell.tradeValue = undefined updateTradeStake({ sellVol: '0', sellToken: tokenMap[coinSellSymbol], deFiSideCalcData: { ...deFiSideCalcDataInit, coinSell: { ...deFiSideCalcDataInit.coinSell, tradeValue: undefined, }, } as DeFiSideCalcData, }) myLog('resetDefault defi clearTrade', deFiSideCalcDataInit) } else { const tradeData = { ...deFiSideCalcDataInit.coinSell, tradeValue: tradeStake.deFiSideCalcData?.coinSell?.tradeValue ?? undefined, } handleOnchange({ tradeData }) } } catch (error) { setToastOpen({ open: true, type: ToastType.error, content: t( SDK_ERROR_MAP_TO_UI[(error as sdk.RESULT_INFO).code ?? 700001]?.messageKey ?? (error as sdk.RESULT_INFO).message, ), }) } setIsLoading(false) }, [ account.readyState, coinSellSymbol, handleOnchange, coinSellSymbol, tokenMap, tradeStake.deFiSideCalcData, updateTradeStake, ], ) const walletLayer2Callback = React.useCallback(async () => { let tradeValue: any = undefined let deFiSideCalcDataInit: Partial> = { coinSell: { belong: coinSellSymbol, balance: undefined, }, ...(tradeStake?.deFiSideCalcData ?? {}), } if (tradeStake.deFiSideCalcData) { tradeValue = tradeStake?.deFiSideCalcData?.coinSell?.tradeValue ?? undefined } if (deFiSideCalcDataInit?.coinSell?.belong) { let walletMap: any if (account.readyState === AccountStatus.ACTIVATED) { walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap deFiSideCalcDataInit.coinSell = { belong: coinSellSymbol, balance: walletMap[coinSellSymbol]?.count, } } else { deFiSideCalcDataInit.coinSell = { belong: coinSellSymbol, balance: undefined, } } const tradeData = { ...deFiSideCalcDataInit.coinSell, tradeValue, } myLog('resetDefault Defi walletLayer2Callback', tradeData) handleOnchange({ tradeData }) } }, [account.readyState, coinSellSymbol, handleOnchange, tradeStake.deFiSideCalcData]) useWalletLayer2Socket({ walletLayer2Callback }) const sendRequest = React.useCallback(async () => { const tradeStake = store.getState()._router_tradeStake.tradeStake try { setIsLoading(true) if ( LoopringAPI.userAPI && LoopringAPI.defiAPI && tradeStake.deFiSideCalcData?.coinSell && tradeStake.sellToken?.tokenId !== undefined && exchangeInfo ) { const request = { accountId: account.accountId, timestamp: Date.now(), token: { tokenId: tradeStake.sellToken?.tokenId, volume: tradeStake.sellVol, }, } myLog('Stake Trade request:', request) const response = await LoopringAPI.defiAPI.sendStake( request, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] throw new CustomErrorWithCode(errorItem) } else { const response1 = await LoopringAPI.defiAPI.getStakeSummary( { accountId: account.accountId, hashes: response.hash, tokenId: tradeStake.sellToken.tokenId, }, account.apiKey, ) let item: any if ((response1 as sdk.RESULT_INFO).code || (response1 as sdk.RESULT_INFO).message) { } else { item = (response1 as any).list[0] } setShowAccount({ isShow: true, step: AccountStep.Staking_Success, info: { symbol: tradeStake.sellToken.symbol, amount: tradeStake.deFiSideCalcData.coinSell.tradeValue, daysDuration: Math.ceil( Number(tradeStake?.deFiSideCalcData?.stakeViewInfo?.rewardPeriod ?? 0) / 86400000, ), ...item, }, }) await sdk.sleep(SUBMIT_PANEL_QUICK_AUTO_CLOSE) walletLayer2Service.sendUserUpdate() if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.Staking_Success ) { setShowAccount({ isShow: false }) } } } else { throw new Error('api not ready') } } catch (reason) { setToastOpen({ open: true, type: ToastType.error, content: t('labelInvestFailed') + ' ' + (reason as CustomErrorWithCode)?.messageKey ?? ` error: ${t((reason as CustomErrorWithCode)?.messageKey)}`, }) } finally { resetDefault(true) } }, [ account.accountId, account.apiKey, account.eddsaKey.sk, exchangeInfo, resetDefault, setToastOpen, t, ]) const onSubmitBtnClick = React.useCallback(async () => { // const tradeStake = store.getState().router_tradeStake.tradeStake; if ( account.readyState === AccountStatus.ACTIVATED && tokenMap && exchangeInfo && account.eddsaKey?.sk ) { if (allowTrade && !allowTrade.defiInvest.enable) { setShowSupport({ isShow: true }) } else if (toggle && !toggle[`${coinSellSymbol}StackInvest`].enable) { setShowTradeIsFrozen({ isShow: true, type: 'StakingInvest' }) } else { sendRequest() } } else { return false } }, [ account.readyState, account.eddsaKey?.sk, tokenMap, exchangeInfo, sendRequest, setToastOpen, t, ]) const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const account = store.getState().account const tradeStake = store.getState()._router_tradeStake.tradeStake myLog('tradeStake', tradeStake) if (account.readyState === AccountStatus.ACTIVATED) { if (tradeStake?.sellVol === undefined || sdk.toBig(tradeStake?.sellVol).lte(0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelEnterAmount', } } else if ( sdk .toBig(tradeStake?.sellVol) .minus(tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellVol ?? 0) .lt(0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDefiMin| ${getValuePrecisionThousand( sdk.toBig(tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellAmount ?? 0), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: false, isAbbreviate: true }, )} ${coinSellSymbol}`, } } else if ( sdk .toBig(tradeStake?.sellVol) .gt(tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellVol ?? 0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDefiMax| ${getValuePrecisionThousand( sdk.toBig(tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellAmount ?? 0), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: false, isAbbreviate: true }, )} ${coinSellSymbol}`, } // return { // tradeBtnStatus: TradeBtnStatus.DISABLED, // label: `labelDefiNoEnough| ${coinSellSymbol}`, // }; } else if ( tradeStake?.deFiSideCalcData?.coinSell?.tradeValue && sdk .toBig(tradeStake.deFiSideCalcData.coinSell.tradeValue) .gt(tradeStake.deFiSideCalcData?.coinSell.balance ?? 0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelStakeNoEnough| ${coinSellSymbol}`, } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } // label: ''} } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } }, [ tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellVol, tradeStake.sellVol, tradeStake.sellToken, tradeStake.deFiSideCalcData, tokenMap, coinSellSymbol, ]) const { btnStatus, onBtnClick, btnLabel: tradeMarketI18nKey, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback: onSubmitBtnClick, }) React.useEffect(() => { getStakingMap() walletLayer2Service.sendUserUpdate() }, []) React.useEffect(() => { const { _router_tradeStake: { tradeStake }, invest: { stakingMap: { marketMap: stakingMap }, }, } = store.getState() if ( stakingMapStatus === SagaStatus.UNSET && stakingMap && stakingMap[coinSellSymbol]?.symbol == coinSellSymbol && !(tradeStake?.deFiSideCalcData?.coinSell?.belong == coinSellSymbol) ) { resetDefault(true) } else if (stakingMapStatus === SagaStatus.UNSET && stakingMap && !stakingMap[coinSellSymbol]) { // setToastOpen({ // // }) } return () => { resetTradeStake() handleOnchange.cancel() } }, [stakingMapStatus]) const stakeWrapProps = React.useMemo(() => { return { disabled: false, btnInfo: { label: tradeMarketI18nKey, params: {}, }, isJoin: true, isLoading, switchStobEvent: (_isStoB: boolean | ((prevState: boolean) => boolean)) => {}, onSubmitClick: onBtnClick as () => void, onChangeEvent: handleOnchange, deFiSideCalcData: { ...tradeStake.deFiSideCalcData, }, minSellAmount: tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellAmount, maxSellAmount: tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellAmount, tokenSell: { ...tokenMap[coinSellSymbol], decimals: tradeStake?.deFiSideCalcData?.stakeViewInfo?.decimals, precision: tradeStake?.deFiSideCalcData?.stakeViewInfo?.precision, }, btnStatus, accStatus: account.readyState, } }, [ refreshRef, sendRequest, tradeStake.deFiSideCalcData, tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellVol, account.readyState, tradeMarketI18nKey, isLoading, onBtnClick, handleOnchange, tokenMap, coinSellSymbol, btnStatus, ]) // as ForceWithdrawProps; return { stakeWrapProps: stakeWrapProps as unknown as DeFiSideWrapProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useStakeTradeExit.ts ================================================ import { AccountStatus, CustomErrorWithCode, DeFiSideRedeemCalcData, getValuePrecisionThousand, globalSetup, IBData, myLog, RedeemStake, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_QUICK_AUTO_CLOSE, TradeBtnStatus, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import { AccountStep, DeFiStakeRedeemWrapProps, RawDataDefiSideStakingItem, ToastType, useOpenModals, useToggle, } from '@loopring-web/component-lib' import { store, useAccount, useRedeemStake, useSystem, useTokenMap } from '../../stores' import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import _ from 'lodash' import { calcRedeemStaking } from '../help' import { LoopringAPI } from '../../api_wrapper' import { useSubmitBtn } from '../common' import { useHistory } from 'react-router-dom' import moment from 'moment' import { walletLayer2Service } from '../../services' export const useStakeRedeemClick = () => { const { tokenMap, idIndex } = useTokenMap() const { updateRedeemStake } = useRedeemStake() const { setShowSideStakingRedeem } = useOpenModals() const redeemItemClick = (item: RawDataDefiSideStakingItem) => { const tokenInfo = tokenMap[idIndex[item.tokenId]] updateRedeemStake({ sellToken: tokenInfo, deFiSideRedeemCalcData: { coinSell: { belong: tokenInfo.symbol, balance: sdk .toBig(item.remainAmount) .div('1e' + tokenInfo.decimals) .toString(), tradeValue: undefined, }, stakeViewInfo: { ...item } as never, }, }) setShowSideStakingRedeem({ isShow: true, symbol: tokenInfo.symbol }) } return { redeemItemClick } } export const useStakeTradeExit = , I, ACD extends DeFiSideRedeemCalcData>({ setToastOpen, }: // symbol: coinSellSymbol, { setToastOpen: (props: { open: boolean; content: JSX.Element | string; type: ToastType }) => void }) => { const { t } = useTranslation() const { setShowSupport, setShowTradeIsFrozen, setShowSideStakingRedeem } = useOpenModals() const history = useHistory() const { redeemStake, updateRedeemStake } = useRedeemStake() const { tokenMap } = useTokenMap() const { account } = useAccount() const { exchangeInfo, allowTrade } = useSystem() const { toggle } = useToggle() const [isLoading, setIsLoading] = React.useState(false) const { setShowAccount } = useOpenModals() const coinSellSymbol = redeemStake?.sellToken?.symbol const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const account = store.getState().account const redeemStake = store.getState()._router_redeemStake.redeemStake if (redeemStake?.sellToken && account.readyState === AccountStatus.ACTIVATED) { if (redeemStake?.sellVol === undefined || sdk.toBig(redeemStake?.sellVol).lte(0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelEnterAmount', } } else if ( sdk .toBig(redeemStake?.sellVol) .minus((redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.minSellVol ?? 0) .lt(0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDefiMin| ${getValuePrecisionThousand( sdk.toBig( (redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.minSellAmount ?? 0, ), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: false, isAbbreviate: true }, )} ${coinSellSymbol}`, } } else if ( sdk .toBig(redeemStake?.sellVol) .gt((redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.remainAmount) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelStakeNoEnough| ${redeemStake?.sellToken.symbol}`, } } else if ( !sdk .toBig(redeemStake?.sellVol) .eq((redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.remainAmount) && sdk .toBig((redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.remainAmount ?? 0) .minus(redeemStake?.sellVol) .minus((redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.minSellVol ?? 0) .lt(0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelRemainingBtnAmount', } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } // label: ''} } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } }, [redeemStake?.deFiSideRedeemCalcData, tokenMap, coinSellSymbol]) const handleOnchange = _.debounce( ({ tradeData, _redeemStake = {}, }: { tradeData: T _redeemStake?: Partial> }) => { const redeemStake = store.getState()._router_redeemStake.redeemStake let _deFiSideRedeemCalcData: DeFiSideRedeemCalcData = { ...redeemStake.deFiSideRedeemCalcData, } as unknown as DeFiSideRedeemCalcData let _oldTradeStake = { ...redeemStake, ..._redeemStake, } //_.cloneDeep({ ...tradeStake, ..._tradeStake }); myLog('defi handleOnchange', _oldTradeStake) if (tradeData && coinSellSymbol) { const inputValue = tradeData?.tradeValue?.toString() ?? '0' const tokenSell = tokenMap[coinSellSymbol] const { sellVol, deFiSideRedeemCalcData } = calcRedeemStaking({ inputValue, isJoin: false, deFiSideRedeemCalcData: _deFiSideRedeemCalcData, tokenSell, }) // @ts-ignore _deFiSideRedeemCalcData = { ...deFiSideRedeemCalcData, coinSell: { ...tradeData, tradeValue: tradeData?.tradeValue?.toString(), }, } updateRedeemStake({ sellToken: tokenSell, sellVol, deFiSideRedeemCalcData: { ..._deFiSideRedeemCalcData, }, }) } }, globalSetup.wait, ) const sendRequest = React.useCallback(async () => { const redeemStake = store.getState()._router_redeemStake.redeemStake try { setIsLoading(true) if ( LoopringAPI.userAPI && LoopringAPI.defiAPI && redeemStake.sellToken?.symbol && redeemStake.sellVol && exchangeInfo ) { const request = { accountId: account.accountId, token: { tokenId: redeemStake.sellToken?.tokenId ?? 0, volume: redeemStake.sellVol, }, hash: (redeemStake.deFiSideRedeemCalcData.stakeViewInfo as any)?.hash, } myLog('DefiTrade request:', request) const response = await LoopringAPI.defiAPI.sendStakeRedeem( request, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] throw new CustomErrorWithCode(errorItem) } else { const searchParams = new URLSearchParams() searchParams.set( 'refreshStake', (redeemStake.deFiSideRedeemCalcData.stakeViewInfo as any).hash, ) history.replace({ search: searchParams.toString() }) setShowSideStakingRedeem({ isShow: false }) const remainAmount = sdk .toBig(redeemStake?.deFiSideRedeemCalcData?.coinSell?.balance) .minus(redeemStake?.deFiSideRedeemCalcData?.coinSell?.tradeValue ?? 0) .toString() setShowAccount({ isShow: true, step: AccountStep.Staking_Redeem_Success, info: { productId: redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo?.productId, symbol: redeemStake.sellToken.symbol, amount: redeemStake?.deFiSideRedeemCalcData?.coinSell?.tradeValue, remainAmount, }, }) await sdk.sleep(SUBMIT_PANEL_QUICK_AUTO_CLOSE) walletLayer2Service.sendUserUpdate() if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.Staking_Redeem_Success ) { setShowAccount({ isShow: false }) } } } else { throw new Error('api not ready') } } catch (reason) { setToastOpen({ open: true, type: ToastType.error, content: t('labelInvestFailed') + (reason as CustomErrorWithCode)?.messageKey ?? ` error: ${t((reason as CustomErrorWithCode)?.messageKey)}`, }) } finally { setIsLoading(false) } }, [ account.accountId, account.apiKey, account.eddsaKey.sk, exchangeInfo, setToastOpen, t, redeemStake?.sellToken?.symbol, redeemStake?.sellToken?.tokenId, redeemStake?.sellVol, ]) const handleSubmit = React.useCallback(async () => { if ( account.readyState === AccountStatus.ACTIVATED && tokenMap && exchangeInfo && account.eddsaKey?.sk ) { if (allowTrade && !allowTrade.defiInvest.enable) { setShowSupport({ isShow: true }) } else if (toggle && !toggle[`${coinSellSymbol}StackInvest`].enable) { setShowTradeIsFrozen({ isShow: true, type: coinSellSymbol + 'StakingRedeemInvest', }) } else { sendRequest() } } else { return false } }, [ account.readyState, account.eddsaKey?.sk, tokenMap, redeemStake?.sellToken?.symbol, exchangeInfo, sendRequest, setToastOpen, t, ]) const onSubmitBtnClick = React.useCallback(async () => { const tradeStake = store.getState()._router_tradeStake.tradeStake if ( tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellVol && tradeStake?.sellVol && sdk.toBig(tradeStake.sellVol).gte(tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellVol) ) { if ( sdk .toBig(tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellVol ?? 0) .minus(tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellVol ?? 0) .toString() .startsWith('-') ) { } else { const tradeValue = getValuePrecisionThousand( sdk .toBig(tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellVol) .div('1e' + tokenMap[coinSellSymbol]?.decimals), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: true }, ).replaceAll(sdk.SEP, '') // @ts-ignore const oldTrade = (tradeStake?.deFiSideCalcData ?? {}) as unknown as T handleOnchange({ tradeData: { ...oldTrade, tradeValue, }, }) // handleOnchange() } } else { handleSubmit() } }, [tokenMap, coinSellSymbol, handleOnchange, handleSubmit]) const { btnStatus, onBtnClick, btnLabel: tradeMarketI18nKey, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback: onSubmitBtnClick, }) const stakeWrapProps = React.useMemo(() => { const stakeViewInfo = redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo const requiredHoldDay = (stakeViewInfo?.claimableTime - stakeViewInfo?.stakeAt) / 86400000 const holdDay = moment(Date.now()).diff( moment(new Date(stakeViewInfo?.stakeAt ?? '')) .utc() .startOf('days'), 'days', false, ) return { isJoin: false, isFullTime: holdDay >= requiredHoldDay, disabled: account.readyState !== AccountStatus.ACTIVATED, btnInfo: { label: tradeMarketI18nKey, params: {}, }, isLoading, minSellAmount: (redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.minSellAmount, maxSellAmount: (redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.maxSellAmount, onSubmitClick: onBtnClick as () => void, switchStobEvent: (_isStoB: boolean | ((prevState: boolean) => boolean)) => {}, onChangeEvent: handleOnchange, deFiSideRedeemCalcData: redeemStake?.deFiSideRedeemCalcData as any, tokenSell: tokenMap[coinSellSymbol], btnStatus, accStatus: account.readyState, } as DeFiStakeRedeemWrapProps }, [ sendRequest, redeemStake?.deFiSideRedeemCalcData, (redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.claimableTime, (redeemStake?.deFiSideRedeemCalcData?.stakeViewInfo as any)?.maxSellVol, account.readyState, tradeMarketI18nKey, onBtnClick, handleOnchange, tokenMap, coinSellSymbol, btnStatus, ]) // as ForceWithdrawProps; return { stakeWrapProps: stakeWrapProps as unknown as DeFiStakeRedeemWrapProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useStakingAprTrend.ts ================================================ import React from 'react' import { useOpenModals } from '@loopring-web/component-lib' import { CustomErrorWithCode, myLog, SDK_ERROR_MAP_TO_UI } from '@loopring-web/common-resources' import { useDefiMap } from '../../stores' import { LoopringAPI } from '../../api_wrapper' import * as sdk from '@loopring-web/loopring-sdk' export const useStakingAprTrend = () => { const { marketMap: defiMarketMap, marketLeverageMap } = useDefiMap() const [isLoading, setLoading] = React.useState(false) const [{ trends, defiInfo }, setTrends] = React.useState({ trends: [], defiInfo: undefined, }) const { modals: { isShowETHStakingApr }, } = useOpenModals() React.useEffect(() => { if (isShowETHStakingApr.isShow && isShowETHStakingApr.symbol) { setLoading(true) // isShowETHStakingApr.info LoopringAPI.defiAPI .getDefiApys({ request: { defiType: isShowETHStakingApr.info?.type, product: isShowETHStakingApr.symbol, limit: 90, }, }) .then((response) => { if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] throw new CustomErrorWithCode(errorItem) } else { const _defiMarketMap = { ...defiMarketMap, ...marketLeverageMap, } setTrends({ defiInfo: _defiMarketMap[isShowETHStakingApr.symbol], trends: response.apys, }) setLoading(false) } }) .finally(() => { setLoading(false) }) } return () => {} }, [isShowETHStakingApr.isShow]) return { isLoading, trends, defiInfo, } } ================================================ FILE: packages/core/src/hooks/useractions/useTaikoLock.ts ================================================ import React, { useEffect, useState } from 'react' import { AccountStep, ToastType, useOpenModals, useSettings, useToggle, } from '@loopring-web/component-lib' import { AccountStatus, CustomErrorWithCode, DeFiSideCalcData, FeeInfo, getValuePrecisionThousand, globalSetup, IBData, L1L2_NAME_DEFINED, MapChainId, myLog, SagaStatus, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_QUICK_AUTO_CLOSE, TradeBtnStatus, TradeStake, } from '@loopring-web/common-resources' import { accountServices, calcSideStaking, DAYS, erc20ABI, fiatNumberDisplay, getStateFnState, getTimestampDaysLater, hasLrTAIKODust, isNumberStr, numberFormat, numberFormatThousandthPlace, resetlrTAIKOIfNeeded, strNumDecimalPlacesLessThan, taikoDepositABI, unlockAccount, useChargeFees, useStakingMap, useUpdateAccount, useWalletLayer2Socket, } from '@loopring-web/core' import _, { last } from 'lodash' import * as sdk from '@loopring-web/loopring-sdk' import Web3 from 'web3' import { LoopringAPI, store, useAccount, useSystem, useTokenMap, walletLayer2Service, } from '../../index' import { useTranslation } from 'react-i18next' import { useTokenPrices, useTradeStake, useVaultLayer2, useWalletLayer2, WalletLayer1Map } from '../../stores' import Decimal from 'decimal.js' import { BigNumber, Contract, ethers, providers, utils } from 'ethers' import moment from 'moment' import { useAppKitProvider } from '@reown/appkit/react' import { useDispatch } from 'react-redux' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { mapObject } from 'react-financial-charts' const depositContractAddrTAIKOHEKLA = '0x40aCCf1a13f4960AC00800Dd6A4afE82509C2fD2' const depositContractAddrTAIKO = '0xaD32A362645Ac9139CFb5Ba3A2A46fC4c378812B' const depositTaikoWithDurationApprove = async (input: { provider: providers.Web3Provider amount: BigNumber duration: BigNumber taikoAddress: string from: string to: string chainId: sdk.ChainId approveToAddress: string }) => { const { provider, amount, duration, taikoAddress, from, to, chainId, approveToAddress } = input const signer = provider.getSigner() const tokenContract = new Contract(taikoAddress, erc20ABI, signer) const allowance = await tokenContract.allowance(from, approveToAddress) if (allowance.lt(amount)) { const approveTx = await tokenContract.approve(approveToAddress, amount) await approveTx.wait() } } const depositTaikoWithDuration = async (input: { provider: providers.Web3Provider amount: BigNumber duration: BigNumber taikoAddress: string from: string to: string chainId: sdk.ChainId approveToAddress: string }) => { const { provider, amount, duration, taikoAddress, from, to, chainId, approveToAddress } = input const depositContractAddr = chainId === sdk.ChainId.TAIKO ? depositContractAddrTAIKO : depositContractAddrTAIKOHEKLA const signer = provider.getSigner() const contract = new Contract(depositContractAddr, taikoDepositABI, signer) const tx = await contract.deposit(from, to, taikoAddress, amount, duration, '0x') return tx.wait() } const depositTaikoWithDurationTx = async (input: { provider: providers.Web3Provider amount: BigNumber duration: BigNumber taikoAddress: string from: string to: string chainId: sdk.ChainId approveToAddress: string }) => { const { provider, amount, duration, taikoAddress, from, to, chainId, approveToAddress } = input const depositContractAddr = chainId === sdk.ChainId.TAIKO ? depositContractAddrTAIKO : depositContractAddrTAIKOHEKLA const signer = provider.getSigner() const contract = new Contract(depositContractAddr, taikoDepositABI, signer) return contract.functions['deposit'](from, to, taikoAddress, amount, duration, '0x') // return tx.wait() } const submitTaikoFarmingMint = async (info: { amount: BigNumber accountId: number apiKey: string exchangeAddress: string tokenId: number eddsaSk: string }) => { const storageId = await LoopringAPI?.userAPI?.getNextStorageId( { accountId: info.accountId, sellTokenId: info.tokenId, }, info.apiKey, ) const avaiableNFT = await LoopringAPI?.defiAPI?.getTaikoFarmingAvailableNft( { accountId: info.accountId, }, info.apiKey, ) const positionsInfo = await LoopringAPI?.defiAPI?.getTaikoFarmingPositionInfo( { accountId: info.accountId, } ) const positions = positionsInfo?.data const claimedTotal = positions && positions[0] && positions[0].claimedTotal ? BigNumber.from(positions[0].claimedTotal) : BigNumber.from('0') const preorderHash = positions && positions?.length > 0 ? ((positions[0] as any).orderHash as string) : '' const taikoFarmingSubmit: sdk.TaikoFarmingSubmitRequest = { exchange: info.exchangeAddress, accountId: info.accountId, storageId: storageId!.orderId, sellToken: { tokenId: info.tokenId, amount: claimedTotal.add(info.amount).toString(), }, buyToken: { tokenId: avaiableNFT!.tokenId, nftData: avaiableNFT!.nftData, amount: '1', }, allOrNone: false, fillAmountBOrS: true, validUntil: getTimestampDaysLater(365 * 10), maxFeeBips: 100, preOrderHash: preorderHash, } return LoopringAPI?.defiAPI?.submitTaikoFarmingClaim({ request: taikoFarmingSubmit, apiKey: info.apiKey, eddsaKey: info.eddsaSk, }) } export const useTaikoLock = , I>({ setToastOpen, symbol: coinSellSymbol, }: { symbol: string setToastOpen: (props: { open: boolean; content: JSX.Element | string; type: ToastType }) => void }) => { const { t } = useTranslation(['common']) const refreshRef = React.createRef() const [isLoading, setIsLoading] = React.useState(false) const [taikoFarmingChecked, setTaikoFarmingChecked] = React.useState(false) const onCheckBoxChange = React.useCallback(() => { setTaikoFarmingChecked(!taikoFarmingChecked) }, [taikoFarmingChecked]) const { tokenMap } = useTokenMap() const { tokenPrices } = useTokenPrices() const { account } = useAccount() const dispatch = useDispatch() const { setShowAccount } = useOpenModals() const { status: stakingMapStatus, marketMap: stakingMap, getStakingMap } = useStakingMap() const { setShowSupport, setShowTradeIsFrozen } = useOpenModals() const { tradeStake, updateTradeStake, resetTradeStake } = useTradeStake() const { exchangeInfo, allowTrade, getValueInCurrency } = useSystem() const { toggle } = useToggle() const { defaultNetwork, currency, coinJson } = useSettings() const sellToken = tokenMap[coinSellSymbol] ? tokenMap[coinSellSymbol] : undefined const taikoFarmingPrecision = 2 const { walletLayer2, updateWalletLayer2 } = useWalletLayer2() const [mintedLRTAIKO, setMintedLRTAIKO] = useState(undefined as string | undefined) const holdingTAIKO = walletLayer2 && walletLayer2['TAIKO'] ? BigNumber.from(walletLayer2['TAIKO'].total).sub(walletLayer2['TAIKO'].locked).toString() : undefined const holdingLRTAIKO = walletLayer2 && walletLayer2['LRTAIKO']?.total && sellToken ? utils.formatUnits( BigNumber.from(walletLayer2['LRTAIKO'].total).sub(walletLayer2['LRTAIKO'].locked), sellToken.decimals, ) : undefined const handleOnchange = _.debounce( ({ tradeData, _tradeStake = {} }: { tradeData: T; _tradeStake?: Partial> }) => { const tradeStake = store.getState()._router_tradeStake.tradeStake let _deFiSideCalcData: DeFiSideCalcData = { ...tradeStake.deFiSideCalcData, } as unknown as DeFiSideCalcData let _oldTradeStake = { ...tradeStake, ..._tradeStake, } myLog('defi handleOnchange', _oldTradeStake) if (tradeData && coinSellSymbol) { const inputValue = tradeData?.tradeValue?.toString() ?? '' const tokenSell = tokenMap[coinSellSymbol] const { sellVol, deFiSideCalcData } = calcSideStaking({ inputValue, isJoin: true, deFiSideCalcData: _deFiSideCalcData, tokenSell, }) // @ts-ignore _deFiSideCalcData = { ...deFiSideCalcData, coinSell: { ...tradeData, tradeValue: inputValue, }, stakeViewInfo: { ...deFiSideCalcData.stakeViewInfo, } } updateTradeStake({ sellToken: tokenSell, sellVol, deFiSideCalcData: { ..._deFiSideCalcData, }, }) } }, globalSetup.wait, ) const resetDefault = React.useCallback( async (clearTrade: boolean = false) => { let deFiSideCalcDataInit: Partial> = { ...tradeStake.deFiSideCalcData, coinSell: { belong: coinSellSymbol, balance: undefined, tradeValue: tradeStake.deFiSideCalcData?.coinSell?.belong === coinSellSymbol ? tradeStake.deFiSideCalcData?.coinSell?.tradeValue : undefined, }, } try { let item = stakingMap[coinSellSymbol] if (item && stakingMap) { deFiSideCalcDataInit.stakeViewInfo = { ...item, // minSellAmount: '1', // minSellVol: utils.parseEther('1').toString(), } } else { throw new Error('no product') } // if (account.readyState === AccountStatus.ACTIVATED) { if (clearTrade === true) { walletLayer2Service.sendUserUpdate() } const walletLayer1 = store.getState().walletLayer1.walletLayer1 as WalletLayer1Map deFiSideCalcDataInit.coinSell.balance = walletLayer1[coinSellSymbol]?.count ? numberFormat(walletLayer1[coinSellSymbol]?.count, { fixed: taikoFarmingPrecision, removeTrailingZero: true, }) : undefined if (clearTrade || tradeStake.deFiSideCalcData?.coinSell?.tradeValue === undefined) { deFiSideCalcDataInit.coinSell.tradeValue = undefined updateTradeStake({ sellVol: '0', sellToken: tokenMap[coinSellSymbol], deFiSideCalcData: { ...deFiSideCalcDataInit, coinSell: { ...deFiSideCalcDataInit.coinSell, tradeValue: undefined, }, } as DeFiSideCalcData, }) myLog('resetDefault defi clearTrade', deFiSideCalcDataInit) } else { const tradeData = { ...deFiSideCalcDataInit.coinSell, tradeValue: tradeStake.deFiSideCalcData?.coinSell?.tradeValue ?? undefined, } handleOnchange({ tradeData }) } } catch (error) { setToastOpen({ open: true, type: ToastType.error, content: t( SDK_ERROR_MAP_TO_UI[(error as sdk.RESULT_INFO).code ?? 700001]?.messageKey ?? (error as sdk.RESULT_INFO).message, ), }) } setIsLoading(false) setDaysInput('') }, [ account.readyState, coinSellSymbol, handleOnchange, coinSellSymbol, tokenMap, tradeStake.deFiSideCalcData, updateTradeStake, ], ) const walletLayer1Callback = React.useCallback(async () => { let tradeValue: any = undefined let deFiSideCalcDataInit: Partial> = { coinSell: { belong: coinSellSymbol, balance: undefined, }, ...(tradeStake?.deFiSideCalcData ?? {}), } if (tradeStake.deFiSideCalcData) { tradeValue = tradeStake?.deFiSideCalcData?.coinSell?.tradeValue ?? undefined } if (deFiSideCalcDataInit?.coinSell?.belong) { const walletLayer1 = store.getState().walletLayer1.walletLayer1 as WalletLayer1Map deFiSideCalcDataInit.coinSell = { belong: coinSellSymbol, balance: numberFormat(walletLayer1[coinSellSymbol]?.count, { fixed: taikoFarmingPrecision, removeTrailingZero: true, }), } const tradeData = { ...deFiSideCalcDataInit.coinSell, tradeValue, } myLog('resetDefault Defi walletLayer2Callback', tradeData) handleOnchange({ tradeData }) } }, [account.readyState, coinSellSymbol, handleOnchange, tradeStake.deFiSideCalcData]) useWalletLayer2Socket({ walletLayer1Callback }) // useWalletLayer({ walletLayer1Callback }) const sendRequest = React.useCallback(async () => { const tradeStake = store.getState()._router_tradeStake.tradeStake try { setIsLoading(true) if ( LoopringAPI.userAPI && LoopringAPI.defiAPI && tradeStake.deFiSideCalcData?.coinSell && tradeStake.sellToken?.tokenId !== undefined && exchangeInfo ) { const request = { accountId: account.accountId, timestamp: Date.now(), token: { tokenId: tradeStake.sellToken?.tokenId, volume: tradeStake.sellVol, }, } myLog('Stake Trade request:', request) const response = await LoopringAPI.defiAPI.sendStake( request, account.eddsaKey.sk, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { const errorItem = SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? 700001] throw new CustomErrorWithCode(errorItem) } else { const response1 = await LoopringAPI.defiAPI.getStakeSummary( { accountId: account.accountId, hashes: response.hash, tokenId: tradeStake.sellToken.tokenId, }, account.apiKey, ) let item: any if ((response1 as sdk.RESULT_INFO).code || (response1 as sdk.RESULT_INFO).message) { } else { item = (response1 as any).list[0] } setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Lock_Success, info: { symbol: tradeStake.sellToken.symbol, amount: tradeStake.deFiSideCalcData.coinSell.tradeValue, daysDuration: Math.ceil( Number(tradeStake?.deFiSideCalcData?.stakeViewInfo?.rewardPeriod ?? 0) / 86400000, ), ...item, }, }) await sdk.sleep(SUBMIT_PANEL_QUICK_AUTO_CLOSE) walletLayer2Service.sendUserUpdate() refreshData() } } else { throw new Error('api not ready') } } catch (reason) { setToastOpen({ open: true, type: ToastType.error, content: t('labelInvestFailed') + ' ' + (reason as CustomErrorWithCode)?.messageKey ?? ` error: ${t((reason as CustomErrorWithCode)?.messageKey)}`, }) } finally { resetDefault(true) } }, [ account.accountId, account.apiKey, account.eddsaKey.sk, exchangeInfo, resetDefault, setToastOpen, t, ]) const provider = useAppKitProvider('eip155') const [daysInput, setDaysInput] = React.useState('') const [txSubmitModalState, setTxSubmitModalState] = React.useState({ open: false, status: 'init' as 'init' | 'tokenApproving' | 'depositing' | 'depositCompleted', }) const [pendingTxsModalOpen, setPendingTxsModalOpen] = React.useState(false) const [localPendingTx, setLocalPendingTx] = useState<{ txHash: string lockDuration: number stakeAt: number amount: string } | undefined>(undefined) const daysInputValid = Number.isInteger(Number(daysInput)) && Number(daysInput) >= 15 && Number(daysInput) <= 60 const [stakeInfo, setStakeInfo] = React.useState( undefined as | undefined | { totalStaked: string stakingReceivedLocked: { accountId: number tokenId: number stakeAt: number initialAmount: string remainAmount: string totalRewards: string productId: string hash: string status: string createdAt: number updatedAt: number claimableTime: number lastDayPendingRewards: string apr: string }[] }, ) const [realizedAndUnrealized, setRealizedAndUnrealized] = React.useState( undefined as | undefined | { redeemAmount: string realizedUSDT: string unrealizedTaiko: string }, ) const [pendingDeposits, setPendingDeposits] = React.useState( undefined as | undefined | { accountId: number tokenId: number stakeAt: number txHash: string eventIndex: number lockDuration: number hash: string status: string createdAt: number updatedAt: number }[], ) const pendingDepositsMergeLocal = [ ...(localPendingTx ? [ {...localPendingTx, isLocal: true} ] : []), ...(pendingDeposits ? pendingDeposits.map(tx => ({...tx, isLocal: false})) : []), ] const firstLockingPos: { claimableTime: number } | undefined = stakeInfo?.stakingReceivedLocked && last(stakeInfo.stakingReceivedLocked) ? { claimableTime: last(stakeInfo.stakingReceivedLocked)!.claimableTime } : pendingDepositsMergeLocal.length > 0 ? { claimableTime: last(pendingDepositsMergeLocal)!.lockDuration + last(pendingDepositsMergeLocal)!.stakeAt, } : undefined const hasNoLockingPos = !firstLockingPos const [hasLockingTxNotOnChain, setHasLockingTxNotOnChain] = React.useState(false) const onSubmitBtnClick = React.useCallback(async () => { if (tokenMap && exchangeInfo) { if (allowTrade && !allowTrade.defiInvest.enable) { setShowSupport({ isShow: true }) } else if (toggle && !toggle['taikoFarming'].enable) { setShowTradeIsFrozen({ isShow: true, type: 'StakingInvest' }) } else if (hasLrTAIKODust() && account.readyState === 'LOCKED') { setShowLogInToCleanLrTaiko(true) } else { new Promise((res) => { setIsLoading(true) setTxSubmitModalState({ open: true, status: 'tokenApproving', }) res(null) }) .then(() => { if (account.accountId && account.accountId !== -1) { return resetlrTAIKOIfNeeded(account, defaultNetwork, exchangeInfo, 5) } }) .then(() => { const oneDay = BigNumber.from('60').mul('60').mul('24') const duration = stakeInfo && !firstLockingPos ? BigNumber.from(daysInput).mul(oneDay) : firstLockingPos ? BigNumber.from(firstLockingPos.claimableTime) .sub(Date.now()) .div('1000') .div(oneDay) .mul(oneDay) : undefined if (tradeStake && tradeStake.deFiSideCalcData && duration && sellToken) { return depositTaikoWithDurationApprove({ provider: new providers.Web3Provider(provider.walletProvider!), amount: BigNumber.from(tradeStake!.sellVol), duration: duration, taikoAddress: sellToken.address, from: account.accAddress, to: account.accAddress, chainId: defaultNetwork, approveToAddress: exchangeInfo!.depositAddress, }).then(() => { setTxSubmitModalState({ open: true, status: 'depositing', }) setHasLockingTxNotOnChain(true) return depositTaikoWithDurationTx({ provider: new providers.Web3Provider(provider.walletProvider!), amount: BigNumber.from(tradeStake!.sellVol), duration: duration, taikoAddress: sellToken.address, from: account.accAddress, to: account.accAddress, chainId: defaultNetwork, approveToAddress: exchangeInfo!.depositAddress, }) }) } else { throw new Error('depositTaikoWithDurationApprove error') } }) .then((tx) => { setDaysInput('') setIsLoading(false) resetDefault(true) setLocalPendingTx({ txHash: tx.hash, amount: tradeStake!.sellVol, stakeAt: Date.now(), lockDuration: firstLockingPos ? firstLockingPos.claimableTime - Date.now() : Number(daysInput) * 24 * 60 * 60 * 1000, }) return tx.wait() }) .then((txReceipt) => { setHasLockingTxNotOnChain(false) const recursiveCheck = async ( hash: string, env: { addr: string; chainId: sdk.ChainId }, ) => { const account = store.getState().account const defaultNetwork = store.getState().settings.defaultNetwork if ( env.addr.toLowerCase() !== account.accAddress.toLowerCase() || defaultNetwork !== env.chainId ) { // stop if address, chain changed return } const accountId = account.accountId === -1 ? account._accountIdNotActive ?? -1 : account.accountId if (!accountId || accountId === -1) { accountServices.sendCheckAccount(account.accAddress) await sdk.sleep(10 * 1000) return recursiveCheck(hash, env) } else { const res = await LoopringAPI?.defiAPI?.getTaikoFarmingDepositDurationList({ accountId, tokenId: sellToken!.tokenId, statuses: 'locked', // @ts-ignore txHashes: hash, }) if (res?.data && res.data.length > 0) { setTxSubmitModalState((state) => ({ ...state, status: 'depositCompleted', })) return } else { await sdk.sleep(10 * 1000) return recursiveCheck(hash, env) } } } recursiveCheck(txReceipt.transactionHash, { addr: account.accAddress, chainId: defaultNetwork, }) }) .catch((e) => { setTxSubmitModalState({ open: false, status: 'init', }) setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Lock_Failed, info: { error: { msg: e.toString(), }, }, }) }) .finally(() => { setHasLockingTxNotOnChain(false) resetDefault(true) }) } } else { return false } }, [ account.readyState, account.eddsaKey?.sk, tokenMap, exchangeInfo, sendRequest, setToastOpen, daysInput, tradeStake, tradeStake.deFiSideCalcData, t, ]) const [taikoFarmingAccountStatus, setTaikoFarmingAccountStatus] = useState(undefined as number | undefined) const chargeFee = useChargeFees({ requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, amount: holdingTAIKO ? Number(holdingTAIKO) : 0, needAmountRefresh: true, tokenSymbol: sellToken?.symbol, }) const { idIndex }=useTokenMap() useEffect(() => { const feeTokenBalance = walletLayer2 && chargeFee.feeInfo.__raw__ ? walletLayer2[idIndex[chargeFee.feeInfo.__raw__.tokenId]] : undefined const isFeeNotEnough = feeTokenBalance ? BigNumber.from(feeTokenBalance.total).sub(feeTokenBalance.locked).lt(chargeFee.feeInfo.__raw__.feeRaw) : false if (isFeeNotEnough) { const foundFee = chargeFee.chargeFeeTokenList.find((feeInfo) => { const balance = walletLayer2 && walletLayer2[idIndex[feeInfo.__raw__.tokenId] ] return balance && BigNumber.from(balance.total).sub(balance.locked).gte(feeInfo.__raw__.feeRaw) }) foundFee && chargeFee.handleFeeChange(foundFee) } }, [chargeFee, walletLayer2]) const taikoFee = chargeFee?.chargeFeeTokenList?.find(fee => fee.belong === 'TAIKO') const taikoBalanceGreaterThanFee = holdingTAIKO && taikoFee ? BigNumber.from(holdingTAIKO).gt(taikoFee.__raw__.feeRaw) : undefined const settlementStatus: 'init' | 'minting' | 'notSettled' | 'settled' | 'noPosition' = taikoFarmingAccountStatus === undefined ? 'init' : (taikoFarmingAccountStatus === 1 || taikoFarmingAccountStatus === 2) ? 'minting' : taikoFarmingAccountStatus === 3 ? 'notSettled' : taikoFarmingAccountStatus === 0 && taikoBalanceGreaterThanFee ? 'settled' : 'noPosition' const availableTradeCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const account = store.getState().account const tradeStake = store.getState()._router_tradeStake.tradeStake myLog('tradeStake', tradeStake) if (tradeStake?.sellVol === undefined || sdk.toBig(tradeStake?.sellVol).lte(0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelEnterAmount', } } else if ( sdk .toBig(tradeStake?.sellVol) .minus(tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellVol ?? 0) .lt(0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDefiMin| ${getValuePrecisionThousand( sdk.toBig(tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellAmount ?? 0), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: false, isAbbreviate: true }, )} ${coinSellSymbol}`, } } else if ( sdk .toBig(tradeStake?.sellVol) .gt(tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellVol ?? 0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelDefiMax| ${getValuePrecisionThousand( sdk.toBig(tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellAmount ?? 0), tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, tokenMap[coinSellSymbol].precision, false, { floor: false, isAbbreviate: true }, )} ${coinSellSymbol}`, } } else if ( tradeStake?.deFiSideCalcData?.coinSell?.tradeValue && sdk .toBig(tradeStake.deFiSideCalcData.coinSell.tradeValue) .gt(tradeStake.deFiSideCalcData?.coinSell.balance ?? 0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelStakeNoEnough| ${coinSellSymbol}`, } } else if (stakeInfo && hasNoLockingPos && !daysInput) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'input days please', } } else if (stakeInfo && hasNoLockingPos && !daysInputValid) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'Days should be between 15 and 60', } } else if ( firstLockingPos && firstLockingPos.claimableTime < Date.now() ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'posiotions should be redeemed to lock again', } } else if ( firstLockingPos && moment(firstLockingPos.claimableTime).diff(moment(), 'days') < 1 ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'Less than 1 day to unlcok, locking Disabled', } } else if (settlementStatus === 'notSettled') { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'Settlement is in progress', } } else if (settlementStatus === 'settled') { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'Plese reddem first', } } else if (hasLockingTxNotOnChain) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'There is a pending transaction, please wait', } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } // label: ''} } }, [tokenMap, coinSellSymbol, daysInput, stakeInfo, hasNoLockingPos, daysInputValid, settlementStatus, firstLockingPos, hasLockingTxNotOnChain]) const checked = availableTradeCheck() const btnStatus = isLoading ? TradeBtnStatus.LOADING : checked.tradeBtnStatus const onBtnClick = onSubmitBtnClick const [stakingTotal, setStakingTotal] = React.useState(undefined) const stakingAmountRaw = stakeInfo && sellToken ? utils.formatUnits(stakeInfo.totalStaked, sellToken.decimals) : undefined const stakingAmount = stakingAmountRaw && sellToken ? numberFormatThousandthPlace(stakingAmountRaw, { tokenSymbol: sellToken.symbol, fixed: sellToken.precision, removeTrailingZero: true, }) : undefined const stakingAmountWithNoSymbol = stakingAmountRaw && sellToken ? numberFormatThousandthPlace(stakingAmountRaw, { fixed: sellToken.precision, removeTrailingZero: true, }) : undefined const stakingAmountInCurrency = stakingAmountRaw && sellToken && tokenPrices[sellToken.symbol] && getValueInCurrency(new Decimal(stakingAmountRaw).mul(tokenPrices[sellToken.symbol]).toString()) ? fiatNumberDisplay( getValueInCurrency( new Decimal(stakingAmountRaw).mul(tokenPrices[sellToken.symbol]).toString(), ), currency, ) : undefined const [mintRedeemModalState, setMintRedeemModalState] = React.useState({ open: false, mint: { inputValue: '', warningChecked: false, availableToMint: '', minInputAmount: undefined as Decimal | undefined, maxInputAmount: undefined as Decimal | undefined, }, redeemErrorMsg: undefined as string | undefined, status: 'notSignedIn' as 'notSignedIn' | 'signingIn' | 'signedIn' | 'minting' | 'redeeming' | 'redeemError', }) useEffect(() => { if (defaultNetwork && ![sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork)) { new providers.Web3Provider(provider.walletProvider!).send('wallet_switchEthereumChain', [ { chainId: sdk.toHex(sdk.ChainId.TAIKO) }, ]) } }, [defaultNetwork]) const [previousLockRecord, setPreviousLockRecord] = useState({ status: 'init' } as | { status: 'init' } | { status: 'notFound' } | { TAIKOProfit: string USDTProfit: string redeemAmount: string expirationTime: number status: 'found' }) const { vaultAccountInfo, updateVaultLayer2 } = useVaultLayer2() const refreshData = async () => { const account = store.getState().account const accountId = account.accountId === -1 ? account._accountIdNotActive ?? -1 : account.accountId if (!accountId || accountId === -1) { setStakeInfo({ totalStaked: '0', stakingReceivedLocked: [], }) // refresh account accountServices.sendCheckAccount(account.accAddress) return } LoopringAPI?.defiAPI ?.getTaikoFarmingDepositDurationList({ accountId, tokenId: sellToken?.tokenId, statuses: 'received', }) .then((res) => { getStateFnState(setLocalPendingTx).then(localPendingTx => { const found = res.data.find((item) => { return item.txHash === localPendingTx?.txHash }) if (found) { setLocalPendingTx(undefined) } setPendingDeposits(res.data) }) }) LoopringAPI?.defiAPI ?.getTaikoFarmingPositionInfo({ accountId: accountId, }) .then((res) => { const data = res.data setTaikoFarmingAccountStatus(res.account.status) const availableToMint = (data && data[0] && data[0].claimableTotal) ?? '0' const minClaimAmount = (data[0] as any).minClaimAmount as string const maxClaimAmount = (data[0] as any).maxClaimAmount as string setMintRedeemModalState((mintModalState) => ({ ...mintModalState, mint: { ...mintModalState.mint, availableToMint: availableToMint, minInputAmount: new Decimal(utils.formatUnits(minClaimAmount, sellToken?.decimals)), maxInputAmount: new Decimal(utils.formatUnits(maxClaimAmount, sellToken?.decimals)), }, })) setMintedLRTAIKO(utils.formatUnits(data[0].claimedTotal, sellToken?.decimals)) setStakingTotal(data[0].stakedTotal) }) LoopringAPI?.defiAPI ?.getTaikoFarmingUserSummary({ accountId: accountId, tokenId: sellToken?.tokenId, statuses: 'received,locked', }) .then((res) => { setStakeInfo({ stakingReceivedLocked: res.staking, totalStaked: res.totalStaked, }) }) account.apiKey && LoopringAPI.defiAPI?.getTaikoFarmingTransactions( { accountId, tokenId: sellToken?.tokenId, limit: 1, types: 'redeem', } as any, account.apiKey, ).then(res => { if (res.transactions.length === 0) { setPreviousLockRecord({ status: 'notFound' }) } else if (res.transactions.length > 0){ const record = res.transactions[0] as any setPreviousLockRecord({ status: 'found', TAIKOProfit: record.amount ? record.amount : '0', USDTProfit: record.portalOfU ? record.portalOfU : '0', redeemAmount: record.amountOut ? record.amountOut : '0', expirationTime: record.lockDuration, }) } }) LoopringAPI?.defiAPI ?.getTaikoFarmingGetRedeem({ accountId: accountId, tokenId: sellToken?.tokenId, }, '') .then((res) => { setRealizedAndUnrealized({ realizedUSDT: res.profitOfU, unrealizedTaiko: res.profit, redeemAmount: res.redeemAmount }) }) updateWalletLayer2() account.apiKey && updateVaultLayer2({}) } const [showLogInToCleanLrTaiko, setShowLogInToCleanLrTaiko] = useState(false) const clearState = () => { setShowLogInToCleanLrTaiko(false) setStakingTotal(undefined) setStakeInfo(undefined) setMintRedeemModalState({ open: false, mint: { inputValue: '', warningChecked: false, availableToMint: '', minInputAmount: undefined, maxInputAmount: undefined, }, status: 'notSignedIn', redeemErrorMsg: undefined, }) setLocalPendingTx(undefined) setPendingDeposits(undefined) setRealizedAndUnrealized(undefined) setPreviousLockRecord({ status: 'init' }) chargeFee.handleFeeChange({ belong: 'ETH', fee: 0, feeRaw: undefined, } as FeeInfo) } React.useEffect(() => { getStakingMap() walletLayer2Service.sendUserUpdate() }, [account.readyState]) React.useEffect(() => { clearState() const isTaiko = [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const timer = isTaiko ? setInterval(() => { refreshData() }, 10 * 1000) : undefined isTaiko && refreshData() return () => { timer && clearInterval(timer) } }, [account.accAddress, defaultNetwork, sellToken]) React.useEffect(() => { const { _router_tradeStake: { tradeStake }, invest: { stakingMap: { marketMap: stakingMap }, }, } = store.getState() if ( stakingMapStatus === SagaStatus.UNSET && stakingMap && stakingMap[coinSellSymbol]?.symbol == coinSellSymbol && !(tradeStake?.deFiSideCalcData?.coinSell?.belong == coinSellSymbol) ) { resetDefault(true) } else if (stakingMapStatus === SagaStatus.UNSET && stakingMap && !stakingMap[coinSellSymbol]) { } return () => { resetTradeStake() handleOnchange.cancel() } }, [stakingMapStatus]) // React.useEffect(() => { // // if (walletLayer2Status === SagaStatus.UNSET) { // // chargeFee. // // } // // if (reddemAmount) { // // chargeFee.checkFeeIsEnough({ // // isRequiredAPI: true, // // intervalTime: LIVE_FEE_TIMES, // // tokenSymbol: sellToken.symbol, // // requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, // // needAmountRefresh: true, // // amount: Number(reddemAmount), // // }) // // } // }, [reddemAmount, sellToken.symbol]) const network = MapChainId[defaultNetwork] ?? MapChainId[1] const btnLabel = checked.tradeBtnStatus === TradeBtnStatus.DISABLED ? t( checked.label.split('|')[0], checked.label.split('|') && checked.label.split('|')[1] ? { arg: checked.label.split('|')[1], layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, } : { layer2: L1L2_NAME_DEFINED[network].layer2, l1ChainName: L1L2_NAME_DEFINED[network].l1ChainName, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }, ) : t('labelLockTAIKO') const availableToMintFormatted = mintRedeemModalState.mint.availableToMint && sellToken ? utils.formatUnits(mintRedeemModalState.mint.availableToMint, sellToken.decimals) : undefined const isInputInvalid = mintRedeemModalState.mint.inputValue && mintRedeemModalState.mint.maxInputAmount && mintRedeemModalState.mint.minInputAmount && (new Decimal(mintRedeemModalState.mint.inputValue).lessThan(mintRedeemModalState.mint.minInputAmount) || new Decimal(mintRedeemModalState.mint.inputValue).greaterThan(Decimal.min(availableToMintFormatted ?? '0', mintRedeemModalState.mint.maxInputAmount))) const { goUpdateAccount } = useUpdateAccount() const [feeModalState, setFeeModalState] = useState({ open: false, }) // const expirationTime = stakeInfo && last(stakeInfo.stakingReceivedLocked) // ? last(stakeInfo.stakingReceivedLocked)?.claimableTime // : undefined // const expireStatus: 'expired' | 'notExpired' | 'noPosition' = // taikoFarmingAccountStatus === 0 // ? walletLayer2 && walletLayer2['TAIKO'] && new Decimal(walletLayer2['TAIKO'].total).gt(0) // ? 'expired' : 'noPosition' // : 'notExpired' const redeemAmount = settlementStatus === 'settled' && previousLockRecord && previousLockRecord.status === 'found' ? previousLockRecord.redeemAmount : realizedAndUnrealized?.redeemAmount const unrealizedTAIKOBN = settlementStatus === 'settled' ? previousLockRecord.status === 'found' ? BigNumber.from(previousLockRecord.TAIKOProfit) : undefined : realizedAndUnrealized && realizedAndUnrealized.unrealizedTaiko ? BigNumber.from(realizedAndUnrealized.unrealizedTaiko) : undefined const realizedUSDTBN = settlementStatus === 'settled' ? previousLockRecord.status === 'found' ? BigNumber.from(previousLockRecord.USDTProfit) : undefined : realizedAndUnrealized && realizedAndUnrealized.realizedUSDT ? BigNumber.from(realizedAndUnrealized.realizedUSDT) : undefined // expirationTime !== undefined // ? expirationTime < Date.now() // ? 'expired' // : 'notExpired' // : 'noPosition' // taikoFarmingAccountStatus const daysInputInfo = (stakeInfo && hasNoLockingPos) ? { value: daysInput, onInput: (input) => { if (Number.isInteger(Number(input)) || input === '') { setDaysInput(input) } }, disabled: false, unlockTime: moment().add(Number(daysInput), 'days').format('YYYY-MM-DD') } : { value: firstLockingPos ? Math.max(0, moment(firstLockingPos!.claimableTime).diff(moment(), 'days')).toString() : '', onInput: (input) => { if (Number.isInteger(Number(input)) || input === '') { setDaysInput(input) } }, disabled: true, unlockTime: stakeInfo?.stakingReceivedLocked && last(stakeInfo!.stakingReceivedLocked)?.claimableTime ? moment(last(stakeInfo!.stakingReceivedLocked)!.claimableTime).format('YYYY-MM-DD') : '' } const { walletProvider } = useAppKitProvider('eip155') const { checkHWAddr} = useWalletInfo() const accountReadyStateCheck = async (activatedCallBack: () => void) => { if (account.readyState === AccountStatus.ACTIVATED) { activatedCallBack() } else if (account.readyState === AccountStatus.LOCKED) { unlockAccount() } else if (account.readyState === AccountStatus.NOT_ACTIVE) { setMintRedeemModalState({ ...mintRedeemModalState, open: true, status: 'notSignedIn', }) } } const expirationTime = settlementStatus === 'settled' ? previousLockRecord && previousLockRecord.status === 'found' ? previousLockRecord.expirationTime : 0 : stakeInfo ? last(stakeInfo.stakingReceivedLocked)?.claimableTime : 0 const lrTaikoInUse = tokenMap && tokenMap['LRTAIKO'] && vaultAccountInfo?.collateralInfo?.collateralTokenId === tokenMap['LRTAIKO'].tokenId const output = { stakeWrapProps: { disabled: false, buttonDisabled: btnStatus !== TradeBtnStatus.AVAILABLE || !stakeInfo, // || (stakeInfo && hasNoLockingPos && (!daysInputValid || !daysInput)), isJoin: true, isLoading, switchStobEvent: (_isStoB: boolean | ((prevState: boolean) => boolean)) => {}, onSubmitClick: onBtnClick as () => void, onChangeEvent: handleOnchange, deFiSideCalcData: tradeStake.deFiSideCalcData, minSellAmount: tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellAmount, maxSellAmount: tradeStake?.deFiSideCalcData?.stakeViewInfo?.maxSellAmount, tokenSell: { ...tokenMap[coinSellSymbol], decimals: tradeStake?.deFiSideCalcData?.stakeViewInfo?.decimals, precision: tradeStake?.deFiSideCalcData?.stakeViewInfo?.precision, }, btnStatus, accStatus: account.readyState, btnLabel: btnLabel, lockedPosition: stakingTotal && new Decimal(stakingTotal).gt(0) ? { amount: stakingAmount, amountInCurrency: stakingAmountInCurrency, trailblazerBooster: '60x', } : undefined, taikoFarmingChecked, showMultiplier: stakeInfo && hasNoLockingPos, onCheckBoxChange, lockTaikoPlaceholder: tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellAmount ? `≥ ${tradeStake?.deFiSideCalcData?.stakeViewInfo?.minSellAmount}` : '', daysInput: daysInputInfo, myPosition: { totalAmount: (() => { const amountBN = settlementStatus === 'settled' ? previousLockRecord && previousLockRecord.status === 'found' && previousLockRecord.redeemAmount : stakingTotal return amountBN && sellToken ? numberFormatThousandthPlace(utils.formatUnits(amountBN, sellToken.decimals), { fixed: sellToken.precision, removeTrailingZero: true, fixedRound: Decimal.ROUND_DOWN, }) : '--' })(), totalAmountInCurrency: stakingAmountInCurrency, positions: stakeInfo && stakeInfo.stakingReceivedLocked.map((stake) => { const tokenInfo = tokenMap[coinSellSymbol] const lockingDays = Math.floor( (stake.claimableTime - stake.stakeAt) / (1000 * 60 * 60 * 24), ) return { amount: numberFormatThousandthPlace( utils.formatUnits(stake.initialAmount, tokenInfo.decimals), { fixed: tokenInfo.precision, removeTrailingZero: true, }, ), unlocked: stake.status !== 'locked', lockingDays, unlockTime: stake.claimableTime ? moment(stake.claimableTime).format('YYYY-MM-DD') : '', multiplier: lockingDays + 'x', } }), expirationTime, totalAmountWithNoSymbol: stakingAmountWithNoSymbol, realizedUSDT: realizedUSDTBN ? numberFormatThousandthPlace(utils.formatUnits(realizedUSDTBN, 6), { fixed: 2, removeTrailingZero: true, fixedRound: Decimal.ROUND_DOWN, }) + ' USDT' : '--', unrealizedTAIKO: unrealizedTAIKOBN && sellToken ? numberFormatThousandthPlace(utils.formatUnits(unrealizedTAIKOBN, sellToken.decimals), { fixed: sellToken.precision, removeTrailingZero: true, fixedRound: Decimal.ROUND_UP, }) + ' TAIKO' : '--', settlementStatus, showMyPosition: !( (taikoFarmingAccountStatus === undefined || taikoFarmingAccountStatus === 0) && account.readyState !== AccountStatus.ACTIVATED ), }, mintButton: { onClick: async () => { accountReadyStateCheck(() => { setMintRedeemModalState({ ...mintRedeemModalState, open: true, status: 'minting', }) }) }, disabled: !( settlementStatus === 'minting' && expirationTime && expirationTime > Date.now() ), }, redeemButton: { onClick: () => { accountReadyStateCheck(() => { if ( previousLockRecord && previousLockRecord.status === 'found' && previousLockRecord.redeemAmount && ethers.BigNumber.from(previousLockRecord.redeemAmount).gt('0') ) { setMintRedeemModalState({ ...mintRedeemModalState, open: true, status: 'redeeming', }) } else if (lrTaikoInUse) { setMintRedeemModalState({ ...mintRedeemModalState, open: true, status: 'redeeming', }) } else { setMintRedeemModalState({ ...mintRedeemModalState, open: true, status: 'redeemError', redeemErrorMsg: 'No TAIKO to Redeem', }) } }) }, disabled: !( ['settled', 'notSettled', 'minting'].includes(settlementStatus) && expirationTime && expirationTime < Date.now() ), }, taikoCoinJSON: coinJson['TAIKO'], mintRedeemModal: { redeem: { redeemAmount: holdingTAIKO && sellToken ? numberFormatThousandthPlace(utils.formatUnits(holdingTAIKO, sellToken.decimals), { fixed: sellToken.precision, removeTrailingZero: true, }) + ' ' + sellToken.symbol : '--', lrTaikoInUse, lockedTaikoAmount: previousLockRecord && previousLockRecord.status === 'found' && sellToken ? numberFormatThousandthPlace( utils.formatUnits( ethers.BigNumber.from(previousLockRecord.redeemAmount).sub( previousLockRecord.TAIKOProfit, ), sellToken.decimals, ), { fixed: sellToken.precision, removeTrailingZero: true, }, ) + ' TAIKO' : '--', pnlAmount: holdingLRTAIKO && mintedLRTAIKO && sellToken ? `${ new Decimal(holdingLRTAIKO).sub(mintedLRTAIKO).isPos() ? '+' : '-' }${numberFormatThousandthPlace( new Decimal(holdingLRTAIKO).sub(mintedLRTAIKO).abs().toString(), { fixed: sellToken.precision, removeTrailingZero: true }, )}` : '--', onClickConfirm: async () => { Promise.resolve('') .then(async () => { setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Redeem_In_Progress, info: { amount: numberFormatThousandthPlace( utils.formatUnits(holdingTAIKO!, sellToken!.decimals), { fixed: sellToken!.precision, removeTrailingZero: true, }, ), }, }) if (!exchangeInfo) { throw new Error('No exchangeInfo') } const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: sellToken!.tokenId, }, account.apiKey, ) if (!storageId) { throw new Error('No storageId') } let isHWAddr = checkHWAddr(account.accAddress) const tokenVolume = chargeFee.feeInfo.__raw__.tokenId === sellToken!.tokenId ? ethers.BigNumber.from(holdingTAIKO!) .sub(chargeFee.feeInfo.__raw__.feeRaw) .toString() : holdingTAIKO!.toString() const request: sdk.OffChainWithdrawalRequestV3 = { exchange: exchangeInfo.exchangeAddress, owner: account.accAddress, to: account.accAddress, accountId: account.accountId, storageId: storageId?.offchainId, token: { tokenId: sellToken!.tokenId, volume: tokenVolume, }, maxFee: { tokenId: chargeFee.feeInfo.__raw__.tokenId, volume: chargeFee.feeInfo.__raw__.feeRaw, }, fastWithdrawalMode: false, extraData: '', minGas: 0, validUntil: getTimestampDaysLater(DAYS), } return LoopringAPI.userAPI?.submitOffchainWithdraw( { request: { ...request, }, web3: new Web3(walletProvider as any), chainId: defaultNetwork, walletType: sdk.ConnectorNames.Unknown, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: account.eddsaKey.counterFactualInfo, }, ) }) .then((res) => { if (res?.code) { throw new Error(res.message) } setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Redeem_Success, info: { amount: numberFormatThousandthPlace( utils.formatUnits(holdingTAIKO!, sellToken!.decimals), { fixed: sellToken!.precision, removeTrailingZero: true, }, ), redeemAt: Date.now(), }, }) updateWalletLayer2() setMintRedeemModalState((state) => ({ ...state, open: false, })) }) .catch((e) => { setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Redeem_Failed, error: e, }) }) }, fee: chargeFee.feeInfo.fee + ' ' + chargeFee.feeInfo.belong, onClickFee: () => { setFeeModalState({ ...feeModalState, open: true, }) }, readlizedUSDT: realizedUSDTBN ? numberFormatThousandthPlace(utils.formatUnits(realizedUSDTBN, 6), { fixed: 2, removeTrailingZero: true, }) + ' USDT' : '--', unrealizedTAIKO: unrealizedTAIKOBN && sellToken ? numberFormatThousandthPlace( utils.formatUnits(unrealizedTAIKOBN, sellToken.decimals), { fixed: sellToken!.precision, removeTrailingZero: true }, ) + ' TAIKO' : '--', }, onClickSignIn: async () => { setMintRedeemModalState({ ...mintRedeemModalState, status: 'signingIn', }) const feeInfo = await LoopringAPI?.globalAPI?.getActiveFeeInfo({ accountId: account._accountIdNotActive, }) const { userBalances } = await LoopringAPI?.globalAPI?.getUserBalanceForFee({ accountId: account._accountIdNotActive!, tokens: '', }) const found = Object.keys(feeInfo.fees).find((key) => { const fee = feeInfo.fees[key].fee const foundBalance = userBalances[feeInfo.fees[key].tokenId] return ( (foundBalance && sdk.toBig(foundBalance.total).gte(fee)) || sdk.toBig(fee).eq('0') ) }) await goUpdateAccount({ isFirstTime: true, isReset: false, // @ts-ignore feeInfo: { token: feeInfo.fees[found!].fee, belong: found!, fee: feeInfo.fees[found!].fee, feeRaw: feeInfo.fees[found!].fee, }, }) .then(() => { setMintRedeemModalState({ ...mintRedeemModalState, status: 'signedIn', }) }) .catch(() => { setMintRedeemModalState({ ...mintRedeemModalState, status: 'notSignedIn', }) }) }, onClickMint: () => { setMintRedeemModalState({ ...mintRedeemModalState, status: 'minting', }) }, open: mintRedeemModalState.open, onClose: () => { setMintRedeemModalState({ ...mintRedeemModalState, open: false, }) }, onClickMax: () => { setMintRedeemModalState({ ...mintRedeemModalState, mint: { ...mintRedeemModalState.mint, inputValue: utils.formatUnits( mintRedeemModalState.mint.availableToMint, sellToken!.decimals, ), }, }) }, mintWarningChecked: mintRedeemModalState.mint.warningChecked, // mintWarningText: t('labelTaikoFarmingMintWarningText'), onWarningCheckBoxChange: () => { setMintRedeemModalState({ ...mintRedeemModalState, mint: { ...mintRedeemModalState.mint, warningChecked: !mintRedeemModalState.mint.warningChecked, }, }) }, onConfirmBtnClicked: async () => { setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Mint_In_Progress, info: { symbol: 'lrTAIKO', amount: numberFormatThousandthPlace(mintRedeemModalState.mint.inputValue, { fixed: 18, removeTrailingZero: true, }), mintAt: Date.now(), }, }) const res = await submitTaikoFarmingMint({ amount: utils.parseUnits(mintRedeemModalState.mint.inputValue, sellToken!.decimals), accountId: account.accountId, apiKey: account.apiKey, exchangeAddress: exchangeInfo!.exchangeAddress, tokenId: sellToken!.tokenId, eddsaSk: account.eddsaKey.sk, }) .then((res) => { const checkStatus = (hash: string) => { return LoopringAPI.defiAPI ?.getTaikoFarmingTransactionByHash( { accountId: account.accountId, hash: res!.hash, }, account.apiKey, ) .then(async (res2) => { if (res2!.operation.status === 7) { throw { msg: 'error', } } else if (res2.operation.status < 7) { setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Mint_In_Progress, info: { symbol: sellToken!.symbol, amount: numberFormatThousandthPlace( mintRedeemModalState.mint.inputValue, { fixed: sellToken!.precision, removeTrailingZero: true, }, ), }, }) await sdk.sleep(5 * 1000) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step === AccountStep.Taiko_Farming_Mint_In_Progress ) { return checkStatus(hash) } } else { setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Mint_Success, info: { symbol: 'lrTAIKO', amount: numberFormatThousandthPlace( mintRedeemModalState.mint.inputValue, { fixed: 18, removeTrailingZero: true, }, ), mintAt: Date.now(), }, }) setMintRedeemModalState({ ...mintRedeemModalState, mint: { ...mintRedeemModalState.mint, inputValue: '', warningChecked: false, }, }) } }) } return checkStatus(res!.hash) }) .catch((e) => { setShowAccount({ isShow: true, step: AccountStep.Taiko_Farming_Mint_Failed, info: { error: e, }, }) }) .finally(() => { refreshData() }) }, onInput: (input: string) => { if ( stakingMap && stakingMap[coinSellSymbol] && (isNumberStr(input) && strNumDecimalPlacesLessThan(input, stakingMap[coinSellSymbol].precision + 1)) || input === '' ) { setMintRedeemModalState({ ...mintRedeemModalState, mint: { ...mintRedeemModalState.mint, inputValue: input, }, }) } }, inputValue: mintRedeemModalState.mint.inputValue, confirmBtnDisabled: !isNumberStr(mintRedeemModalState.mint.inputValue) || !mintRedeemModalState.mint.warningChecked || !mintRedeemModalState.mint.availableToMint || new Decimal(availableToMintFormatted!).eq('0') || isInputInvalid ? true : false, confirmBtnWording: isInputInvalid || !mintRedeemModalState.mint.inputValue ? availableToMintFormatted && mintRedeemModalState.mint.minInputAmount && mintRedeemModalState.mint.maxInputAmount && Decimal.min(mintRedeemModalState.mint.maxInputAmount, availableToMintFormatted).gte( mintRedeemModalState.mint.minInputAmount, ) ? `Please input between ${mintRedeemModalState.mint.minInputAmount.toString()} - ${Decimal.min( mintRedeemModalState.mint.maxInputAmount, availableToMintFormatted, ).toString()}` : 'Invalid amount' : !mintRedeemModalState.mint.warningChecked ? 'Please check checkbox' : 'Confirm', tokenAvailableAmount: availableToMintFormatted ? availableToMintFormatted : '--', inputPlaceholder: mintRedeemModalState.mint.minInputAmount && mintRedeemModalState.mint.maxInputAmount ? availableToMintFormatted && Decimal.min(mintRedeemModalState.mint.maxInputAmount, availableToMintFormatted).gte( mintRedeemModalState.mint.minInputAmount, ) ? `${mintRedeemModalState.mint.minInputAmount.toString()} - ${Decimal.min( mintRedeemModalState.mint.maxInputAmount, availableToMintFormatted, ).toString()}` : `≥ ${mintRedeemModalState.mint.minInputAmount.toString()}` : '', status: mintRedeemModalState.status, redeemErrorMsg: mintRedeemModalState.redeemErrorMsg, }, feeModal: { ...chargeFee, isFeeNotEnough: chargeFee.isFeeNotEnough.isFeeNotEnough, open: feeModalState.open, onClose: () => { setFeeModalState({ ...feeModalState, open: false, }) }, onClickFee: () => { setFeeModalState({ ...feeModalState, open: true, }) }, handleToggleChange: (fee) => { chargeFee.handleFeeChange(fee) }, feeLoading: false, }, txSubmitModal: { open: txSubmitModalState.open, onClose: () => { setTxSubmitModalState({ open: false, status: 'init', }) }, status: txSubmitModalState.status, }, hasPendingDeposits: pendingDepositsMergeLocal && pendingDepositsMergeLocal.length > 0, onClickPendingDeposits: () => { setPendingTxsModalOpen(true) }, pendingTxsModal: { open: pendingTxsModalOpen, onClose: () => { setPendingTxsModalOpen(false) }, pendingTxs: pendingDepositsMergeLocal?.map((tx) => { return { hash: tx.txHash, amount: numberFormatThousandthPlace( utils.formatUnits((tx as any).amount as string, sellToken!.decimals), { fixed: sellToken!.precision, removeTrailingZero: true, }, ), symbol: sellToken!.symbol, isLocal: tx.isLocal, } }), onClickLocking: () => { setTxSubmitModalState({ open: true, status: 'depositing', }) }, }, lrTAIKOTradeEarnSummary: mintedLRTAIKO && holdingLRTAIKO && sellToken && { holdingAmount: numberFormatThousandthPlace(holdingLRTAIKO, { fixed: sellToken.precision, removeTrailingZero: true, }), mintedAmount: numberFormatThousandthPlace(mintedLRTAIKO, { fixed: sellToken.precision, removeTrailingZero: true, }), pnl: `${ new Decimal(holdingLRTAIKO).sub(mintedLRTAIKO).isPos() ? '+' : '-' }${numberFormatThousandthPlace( new Decimal(holdingLRTAIKO).sub(mintedLRTAIKO).abs().toString(), { fixed: sellToken.precision, removeTrailingZero: true }, )} TAIKO`, // pnl: `--`, }, logInToCleanLrTaikoModal: { open: showLogInToCleanLrTaiko, onClose: () => { setShowLogInToCleanLrTaiko(false) }, onClickSignIn: () => { unlockAccount() setShowLogInToCleanLrTaiko(false) }, } }, } return output } ================================================ FILE: packages/core/src/hooks/useractions/useTransfer.ts ================================================ import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { AccountStep, SwitchData, TransferProps, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { AccountStatus, AddressError, CoinMap, CurrencyToTag, EmptyValueTag, EXCHANGE_TYPE, Explorer, FeeInfo, getValuePrecisionThousand, IBData, LIVE_FEE_TIMES, myLog, PriceTag, SagaStatus, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, UIERROR_CODE, WALLET_TYPE, WalletMap, } from '@loopring-web/common-resources' import { BIGO, DAYS, getTimestampDaysLater, isAccActivated, LAST_STEP, LoopringAPI, makeWalletLayer2, store, useAccount, useAddressCheck, useBtnStatus, useChargeFees, useModalData, useSystem, useTokenMap, useTokenPrices, useWalletLayer2Socket, volumeToCountAsBigNumber, walletLayer2Service, } from '../../index' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { addressToExWalletMapFn, exWalletToAddressMapFn } from '@loopring-web/core' import { NETWORKEXTEND, useContacts } from '../../stores' export const useTransfer = , T>() => { const { setShowAccount, setShowTransfer } = useOpenModals() const { setShowEditContact, modals: { isShowTransfer: { symbol, isShow, info, address: contactAddress, name: contactName, addressType: contactAddressType, }, }, } = useOpenModals() const { tokenMap, totalCoinMap } = useTokenMap() const { account, status: accountStatus } = useAccount() const { exchangeInfo, chainId, forexMap } = useSystem() const { currency } = useSettings() const { tokenPrices } = useTokenPrices() const { contacts, status: contactStatus, errorMessage: contactsErrorMessage, updateContacts, } = useContacts() const { transferValue, updateTransferData, resetTransferData } = useModalData() const [walletMap, setWalletMap] = React.useState( makeWalletLayer2({ needFilterZero: true }).walletMap ?? ({} as WalletMap), ) const [sureItsLayer2, setSureItsLayer2] = React.useState( undefined, ) const { btnStatus, enableBtn, disableBtn } = useBtnStatus() const [feeWithActive, setFeeWithActive] = React.useState(false) const { chargeFeeTokenList, isFeeNotEnough, handleFeeChange, feeInfo, checkFeeIsEnough, resetIntervalTime, resetFee, } = useChargeFees({ requestType: feeWithActive ? sdk.OffchainFeeReqType.TRANSFER_AND_UPDATE_ACCOUNT : sdk.OffchainFeeReqType.TRANSFER, updateData: ({ fee, requestType }) => { let _requestType = feeWithActive ? sdk.OffchainFeeReqType.TRANSFER_AND_UPDATE_ACCOUNT : sdk.OffchainFeeReqType.TRANSFER if (_requestType === requestType) { const transferValue = store.getState()._router_modalData.transferValue updateTransferData({ ...transferValue, fee }) } }, // [feeWithActive] // ), }) const { chargeFeeTokenList: activeAccountFeeList, checkFeeIsEnough: checkActiveFeeIsEnough, // resetIntervalTime: resetActiveIntervalTime, } = useChargeFees({ isActiveAccount: true, requestType: undefined as any, }) const handleOnMemoChange = React.useCallback( (e: React.ChangeEvent) => { const transferValue = store.getState()._router_modalData.transferValue updateTransferData({ ...transferValue, memo: e.target.value, }) }, [updateTransferData], ) const { address, realAddr, setAddress, addrStatus, isLoopringAddress, isActiveAccount, isAddressCheckLoading, isActiveAccountFee, isSameAddress, isContractAddress, loopringSmartWalletVersion, reCheck, isENSWrong, ens, } = useAddressCheck(true) React.useEffect(() => { if (loopringSmartWalletVersion?.isLoopringSmartWallet && sureItsLayer2 === undefined) { setSureItsLayer2(WALLET_TYPE.Loopring) } }, [loopringSmartWalletVersion?.isLoopringSmartWallet]) const checkBtnStatus = React.useCallback(() => { if (tokenMap && transferValue.belong && tokenMap[transferValue.belong]) { const sellToken = tokenMap[transferValue.belong] const tradeValue = sdk.toBig(transferValue.tradeValue ?? 0).times('1e' + sellToken.decimals) const isEnough = tradeValue.lte( sdk.toBig(transferValue.balance ?? 0).times('1e' + sellToken.decimals), ) const contact = contacts?.find((x) => x.contactAddress === realAddr) const ensHasCheck = (contact?.ens || ens) ? !isENSWrong : true if ( tradeValue && chargeFeeTokenList.length && !isFeeNotEnough.isFeeNotEnough && !isSameAddress && ensHasCheck && isEnough && sureItsLayer2 && transferValue.fee?.belong && tradeValue.gt(BIGO) && ((address && address.startsWith('0x')) || realAddr) && (addrStatus as AddressError) === AddressError.NoError ) { enableBtn() return } } disableBtn() }, [ realAddr, tokenMap, transferValue, disableBtn, sureItsLayer2, chargeFeeTokenList.length, isFeeNotEnough, isSameAddress, address, addrStatus, enableBtn, contacts, ]) React.useEffect(() => { checkBtnStatus() }, [ realAddr, chargeFeeTokenList, address, sureItsLayer2, isFeeNotEnough.isFeeNotEnough, isAddressCheckLoading, transferValue, addrStatus, ]) const walletLayer2Callback = React.useCallback(() => { const walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} setWalletMap(walletMap) }, []) useWalletLayer2Socket({ walletLayer2Callback }) const resetDefault = React.useCallback(() => { if (info?.isRetry) { checkFeeIsEnough() return } checkFeeIsEnough({ isRequiredAPI: true, intervalTime: LIVE_FEE_TIMES }) if (contactsErrorMessage) { updateContacts() } if (symbol && walletMap) { myLog('resetDefault symbol:', symbol) updateTransferData({ fee: feeInfo, belong: symbol as any, balance: walletMap[symbol]?.count, tradeValue: undefined, address: '*', memo: '', }) } else { if (!transferValue.belong && walletMap) { const keys = Reflect.ownKeys(walletMap) for (let key in keys) { const keyVal = keys[key] const walletInfo = walletMap[keyVal] if (sdk.toBig(walletInfo.count).gt(0)) { updateTransferData({ belong: keyVal as any, tradeValue: undefined, fee: feeInfo, balance: walletInfo?.count, address: '*', memo: '', }) break } } } else if (transferValue.belong && walletMap) { const walletInfo = walletMap[transferValue.belong] updateTransferData({ fee: feeInfo, belong: transferValue.belong, tradeValue: undefined, balance: walletInfo?.count, address: info?.isToMyself ? account.accAddress : '*', memo: '', }) } else { updateTransferData({ fee: feeInfo, belong: transferValue.belong, tradeValue: undefined, balance: undefined, address: info?.isToMyself ? account.accAddress : '*', memo: '', }) } } if (contactAddress) { setAddress(contactAddress) } else { setAddress('') } }, [ info?.isRetry, info?.isToMyself, checkFeeIsEnough, contactsErrorMessage, symbol, walletMap, contactAddress, updateContacts, updateTransferData, feeInfo, transferValue.belong, account.accAddress, setAddress, ]) React.useEffect(() => { if ( isShow && accountStatus === SagaStatus.UNSET && account.readyState === AccountStatus.ACTIVATED ) { resetDefault() resetFee() } else { resetIntervalTime() checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: undefined as any, }) } return () => { resetIntervalTime() setAddress('') checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: undefined as any, }) } }, [isShow]) const { checkHWAddr, updateHW } = useWalletInfo() const [lastRequest, setLastRequest] = React.useState({}) const processRequest = React.useCallback( async (request: sdk.OriginTransferRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account try { if (connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated()) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } setLastRequest({ request }) const response = await LoopringAPI.userAPI.submitInternalTransfer( { request, web3: connectProvides.usedWeb3 as any, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submitInternalTransfer:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } info?.onCloseCallBack && info?.onCloseCallBack() setShowTransfer({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.Transfer_In_Progress, }) setShowAccount({ isShow: true, step: AccountStep.Transfer_Success, info: { hash: Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-transfer`, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } resetTransferData() walletLayer2Service.sendUserUpdate() await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.Transfer_Success ) { setShowAccount({ isShow: false }) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.Transfer_First_Method_Denied, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.Transfer_User_Denied, }) break default: if ([102024, 102025, 114001, 114002].includes((e as sdk.RESULT_INFO)?.code || 0)) { checkFeeIsEnough({ isRequiredAPI: true, requestType: feeWithActive ? sdk.OffchainFeeReqType.TRANSFER_AND_UPDATE_ACCOUNT : sdk.OffchainFeeReqType.TRANSFER, }) } setShowAccount({ isShow: true, step: AccountStep.Transfer_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, checkHWAddr, chainId, info, setShowTransfer, setShowAccount, resetTransferData, updateHW, checkFeeIsEnough, feeWithActive, ], ) const onTransferClick = React.useCallback( async (_transferValue, isFirstTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account const transferValue = store.getState()._router_modalData.transferValue if ( readyState === AccountStatus.ACTIVATED && tokenMap && LoopringAPI.userAPI && exchangeInfo && connectProvides.usedWeb3 && transferValue.address !== '*' && transferValue?.fee && transferValue?.fee.belong && transferValue.fee?.__raw__ && eddsaKey?.sk ) { try { setShowAccount({ isShow: true, step: AccountStep.Transfer_WaitForAuth, }) const sellToken = tokenMap[transferValue.belong as string] const feeToken = tokenMap[transferValue.fee.belong] const feeRaw = transferValue.fee.feeRaw ?? transferValue.fee.__raw__?.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const balance = sdk.toBig(transferValue.balance ?? 0).times('1e' + sellToken.decimals) const tradeValue = sdk .toBig(transferValue.tradeValue ?? 0) .times('1e' + sellToken.decimals) const isExceedBalance = feeToken.tokenId === sellToken.tokenId && tradeValue.plus(fee).gt(balance) const finalVol = isExceedBalance ? balance.minus(fee) : tradeValue const transferVol = finalVol.toFixed(0, 0) const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId, sellTokenId: sellToken.tokenId, }, apiKey, ) const req: sdk.OriginTransferRequestV3 = { exchange: exchangeInfo.exchangeAddress, payerAddr: accAddress, payerId: accountId, payeeAddr: realAddr ? realAddr : address, payeeId: 0, storageId: storageId?.offchainId, payPayeeUpdateAccount: !isActiveAccountFee && feeWithActive, token: { tokenId: sellToken.tokenId, volume: transferVol, }, maxFee: { tokenId: feeToken.tokenId, volume: fee.toString(), // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), memo: transferValue.memo, } myLog('transfer req:', req) processRequest(req, isFirstTime) } catch (e: any) { // transfer failed setShowAccount({ isShow: true, step: AccountStep.Transfer_Failed, error: { code: UIERROR_CODE.UNKNOWN, message: e.message, } as sdk.RESULT_INFO, }) } } else { return } }, [ account, tokenMap, exchangeInfo, setShowAccount, realAddr, address, processRequest, isActiveAccountFee, feeWithActive, ], ) const handlePanelEvent = React.useCallback( async (data: SwitchData) => { return new Promise((res: any) => { if (data.to === 'button') { if (walletMap && data?.tradeData?.belong) { const walletInfo = walletMap[data?.tradeData?.belong as string] updateTransferData({ belong: data.tradeData?.belong, tradeValue: data.tradeData?.tradeValue, balance: walletInfo ? walletInfo.count : 0, address: '*', }) } else { updateTransferData({ belong: undefined, tradeValue: undefined, balance: undefined, address: '*', }) } } res() }) }, [walletMap, updateTransferData], ) const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.Transfer_WaitForAuth, }) processRequest(lastRequest, !isHardwareRetry) }, [lastRequest, processRequest, setShowAccount], ) const activeAccountPrice = React.useMemo(() => { if ( realAddr !== '' && isActiveAccount == false && activeAccountFeeList.length && activeAccountFeeList[0] && tokenPrices && activeAccountFeeList[0].feeRaw ) { const feeInfo: FeeInfo = activeAccountFeeList[0] const feeU: any = volumeToCountAsBigNumber(feeInfo.belong, feeInfo.feeRaw ?? 0)?.times( tokenPrices[feeInfo.belong], ) ?? undefined return feeU && currency && forexMap[currency] ? '~' + PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( // @ts-ignore feeU * forexMap[currency], 2, 2, 2, true, { floor: true }, ) : EmptyValueTag } else { return } }, [realAddr, isActiveAccount, activeAccountFeeList, tokenPrices, currency, forexMap]) React.useEffect(() => { if (realAddr !== '' && isActiveAccount === false) { checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: 'TRANSFER_ACTIVE', }) } }, [isActiveAccount, realAddr]) const handleSureItsLayer2 = (sure) => { const found = exWalletToAddressMapFn(sure)! const contact = contacts?.find((x) => x.contactAddress === realAddr) if (!account.isContractAddress && contact && contact.addressType !== sure) { LoopringAPI.contactAPI ?.updateContact( { ...contact, isHebao: !!(account.isContractAddress || account.isCFAddress), accountId: account.accountId, addressType: found, }, account.apiKey, ) .then(() => { updateContacts() reCheck() }) } setSureItsLayer2(sure) } React.useEffect(() => { const { contacts } = store.getState().contacts const contact = contacts?.find( (x) => x.contactAddress?.toLowerCase() === realAddr?.toLowerCase(), ) if (isShow === false) { setSureItsLayer2(undefined) } else if (contact?.addressType !== undefined) { const found = contact.addressType ? addressToExWalletMapFn(contact.addressType) : undefined setSureItsLayer2((state) => { if (state !== found) { return found } else { return state } }) } if ( isShow && contactStatus == SagaStatus.UNSET && contact && realAddr?.toLowerCase() == contact?.contactAddress?.toLowerCase() ) { reCheck() } }, [realAddr, isShow, contactStatus]) const transferProps: TransferProps = { contacts, type: TRADE_TYPE.TOKEN, addressDefault: address, realAddr, tradeData: transferValue as any, coinMap: totalCoinMap as CoinMap, walletMap: walletMap as WalletMap, transferBtnStatus: btnStatus, onTransferClick, handlePanelEvent, handleFeeChange, feeInfo, handleSureItsLayer2, lastFailed: store.getState().modals.isShowAccount.info?.lastFailed === LAST_STEP.transfer, // isConfirmTransfer, sureItsLayer2, chargeFeeTokenList, activeAccountPrice, isFeeNotEnough, isLoopringAddress, isSameAddress, isAddressCheckLoading, addrStatus, memo: transferValue.memo ?? '', handleOnMemoChange, handleOnAddressChange: (value: any, isContactSelection?: boolean) => { checkActiveFeeIsEnough({ isRequiredAPI: true, requestType: undefined as any, }) setAddress((state) => { if (isContactSelection) { const contact = contacts?.find((x) => x.contactAddress === value) const v = contact && addressToExWalletMapFn(contact.addressType) v && setSureItsLayer2(v) } else { setSureItsLayer2(undefined) } if (state !== value || '') { // flag = true; setFeeWithActive((state) => { if (state !== false) { checkFeeIsEnough({ isRequiredAPI: true, requestType: sdk.OffchainFeeReqType.TRANSFER, }) } return false }) } return value || '' }) }, isActiveAccount, isActiveAccountFee, feeWithActive, handleOnFeeWithActive: (value: boolean) => { setFeeWithActive(value) if (value && !isActiveAccountFee) { checkFeeIsEnough({ isRequiredAPI: true, requestType: sdk.OffchainFeeReqType.TRANSFER_AND_UPDATE_ACCOUNT, }) } else { checkFeeIsEnough({ isRequiredAPI: true, requestType: sdk.OffchainFeeReqType.TRANSFER, }) } }, isSmartContractAddress: isContractAddress, isFromContact: contactAddress ? true : false, contact: contactAddress ? contacts?.find((x) => x.contactAddress === contactAddress) : undefined, loopringSmartWalletVersion, isENSWrong, geUpdateContact: () => { if (isENSWrong) { const contact = contacts?.find((x) => x.contactAddress === realAddr) setShowEditContact({ isShow: true, info: { ...contact, isENSWrong, }, }) } }, ens, // contacts, } return { retryBtn, transferProps, } } ================================================ FILE: packages/core/src/hooks/useractions/useTransferToTaikoAccount.ts ================================================ import { AccountStep, TransferToTaikoAccountProps, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { store, TokenMap, useAccount, useConfig, useContacts, useSystem, useTokenMap, useWalletLayer2 } from '../../stores' import { useEffect } from 'react' import { useGetSet } from 'react-use' import { LoopringAPI } from '../../api_wrapper' import { Account, MapChainId, NetworkMap, UIERROR_CODE, WalletMap } from '@loopring-web/common-resources' import { ChainId, checkErrorInfo, ConnectorError, ExchangeAPI, OffchainFeeInfo, OffchainFeeReqType, RabbitWithdrawRequest, RESULT_INFO, sleep, TokenInfo } from '@loopring-web/loopring-sdk' import { ethers, utils } from 'ethers' import { getTimestampDaysLater, isValidateNumberStr, numberFormat } from '../../utils' import { makeWalletLayer2, parseRabbitConfig } from '../../hooks/help' import Decimal from 'decimal.js' import { DAYS } from '../../defs' import { useAppKitProvider } from '@reown/appkit/react' import { useDebouncedCallback } from '../../hooks/common' import _ from 'lodash' import { parseRabbitConfig2 } from '../../hooks/help/parseRabbitConfig' const offchainFeeInfoToFeeInfo = (offchainFeeInfo: OffchainFeeInfo, tokenMap: TokenMap<{ [key: string]: any; }>, walletMap: WalletMap) => { return { belong: offchainFeeInfo.token, fee: tokenMap[offchainFeeInfo.token] ? ethers.utils.formatUnits(offchainFeeInfo.fee, tokenMap[offchainFeeInfo.token].decimals) : '', feeRaw: offchainFeeInfo.fee, token: offchainFeeInfo.token, hasToken: !!offchainFeeInfo.token, count: walletMap[offchainFeeInfo.token]?.count, discount: offchainFeeInfo.discount ? offchainFeeInfo.discount : undefined, __raw__: { fastWithDraw: '', tokenId: tokenMap[offchainFeeInfo.token]?.tokenId, feeRaw: offchainFeeInfo.fee, } } } const networkById = (id: ChainId) => { return MapChainId[id] ? (167009 === id ? 'TAIKO' : MapChainId[id]) : undefined } const transferToOtherNetwork = async ({ account, transferToken, feeToken, state, configJSON, feeRaw, walletProvider, chainId: defaultNetwork, balance }: { account: Account transferToken: TokenInfo feeToken: TokenInfo state: any configJSON: { toOtherNetworks: { network: string supportedTokens: string[] }[] agentId?: number agentAddr?: string exchange?: string } feeRaw: string walletProvider: any chainId: number balance: string }) => { const network = networkById(defaultNetwork)! const toNetwork = configJSON.toOtherNetworks[0]?.network // Get next storage Id for the transfer const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: transferToken.tokenId, }, account.apiKey, ) // Get agent and exchange information from config // Prepare the transfer request if ( transferToken.tokenId === feeToken?.tokenId && utils .parseUnits(state.amount, transferToken.decimals) .add(feeRaw!) .gt(utils.parseUnits(balance, transferToken.decimals)) ) { var transferVolume = utils .parseUnits(state.amount, transferToken.decimals) .sub(ethers.BigNumber.from(feeRaw!)) .toString() } else { transferVolume = utils.parseUnits(state.amount, transferToken.decimals).toString() } const request: RabbitWithdrawRequest = { fromNetwork: network, toNetwork: toNetwork, toAddress: state.receipt, transfer: { exchange: configJSON.exchange!, payerId: account.accountId, payerAddr: account.accAddress, payeeId: configJSON.agentId!, payeeAddr: configJSON.agentAddr!, token: { tokenId: transferToken.tokenId, volume: transferVolume, }, maxFee: { // @ts-ignore tokenId: feeToken.tokenId, volume: ethers.BigNumber.from(feeRaw).toString(), }, storageId: storageId!.offchainId, validUntil: getTimestampDaysLater(DAYS), counterFactualInfo: account.eddsaKey.counterFactualInfo, }, } // Setup provider for transaction signing const provider = new ethers.providers.Web3Provider(walletProvider as any) // Submit the withdraw request return LoopringAPI.rabbitWithdrawAPI!.submitRabitWithdraw(request, { exchangeAddr: configJSON.exchange!, signer: provider.getSigner(), eddsaSignKey: account.eddsaKey.sk, chainId: defaultNetwork as number, }) } export const useTransferToTaikoAccount = (): TransferToTaikoAccountProps => { const { setShowAccount, setShowTransferToTaikoAccount, setShowBridge, modals } = useOpenModals() const { coinJson, defaultNetwork, feeChargeOrder } = useSettings() const { app } = useSystem() const { tokenMap, idIndex } = useTokenMap() const { updateWalletLayer2 } = useWalletLayer2() const { fastWithdrawConfig } = useConfig() const { account } = useAccount() const initialState = { transferToken: undefined as string | undefined, feeList: undefined as OffchainFeeInfo[] | undefined, feeToken: undefined as string | undefined, panel: 'main' as 'main' | 'contacts' | 'tokenSelection' | 'confirm', showFeeModal: false, amount: '', receipt: '', tokenFilterInput: '', maxTransferAmount: undefined as ethers.BigNumber | undefined, feeLoading: false } const [getState, setState] = useGetSet(initialState) const state = getState() const transferTokenSymbol = state.transferToken ? state.transferToken : modals.isShowTransferToTaikoAccount.info?.initSymbol ? modals.isShowTransferToTaikoAccount.info?.initSymbol : 'ETH' const transferToken = tokenMap[transferTokenSymbol] const isOverMax = state.amount && ethers.utils .parseUnits(state.amount, transferToken.decimals) .gt(state.maxTransferAmount ?? '0') const { contacts, updateContacts } = useContacts() const fromNetwork = networkById(defaultNetwork) const parsed = fastWithdrawConfig && idIndex && fromNetwork ? parseRabbitConfig2(fastWithdrawConfig, fromNetwork, idIndex) : undefined const transferTokenList = parsed?.toOtherNetworks[0]?.supportedTokens || [] // const toTaikoNetwork = parsed?.toOtherNetworks[0].network const refreshData = async () => { const globalState = store.getState() const account = globalState.account const idIndex = globalState.tokenMap.idIndex const defaultNetwork = globalState.settings.defaultNetwork const fastWithdrawConfig = globalState.config.fastWithdrawConfig const fromNetwork = networkById(defaultNetwork) const parsed = fastWithdrawConfig && idIndex && fromNetwork ? parseRabbitConfig2(fastWithdrawConfig, fromNetwork, idIndex) : undefined const destinationNetwork = parsed?.toOtherNetworks[0]?.network const state = getState() const modals = globalState.modals const transferTokenSymbol = state.transferToken ? state.transferToken : modals.isShowTransferToTaikoAccount.info?.initSymbol ? modals.isShowTransferToTaikoAccount.info?.initSymbol : 'ETH' const transferToken = tokenMap[transferTokenSymbol] setState((state) => ({ ...state, feeLoading: true })) const feeRes = await LoopringAPI.rabbitWithdrawAPI ?.getUserCrossChainFee( { receiveFeeNetwork: fromNetwork!, requestType: OffchainFeeReqType.RABBIT_OFFCHAIN_WITHDRAWAL, calFeeNetwork: destinationNetwork!, tokenSymbol: transferTokenSymbol, amount: state.amount ? utils.parseUnits(state.amount, transferToken.decimals).toString() : '0', }, account.apiKey, ).finally(() => { setState(state => ({ ...state, feeLoading: false })) }) const destinationNetworkId = _.toPairs(MapChainId).find(([_, v]) => v === destinationNetwork)?.[0] const desExchangeAPI = new ExchangeAPI({baseUrl: `https://${process.env[`REACT_APP_API_URL_${destinationNetworkId}`]}`}) const desTokens = await desExchangeAPI.getTokens() transferToken && LoopringAPI.rabbitWithdrawAPI?.getNetworkWithdrawalAgents({ tokenId: desTokens.tokensMap[transferToken.symbol].tokenId, network: destinationNetwork!, amount: '0' }).then(res => { const amounts = res.map(agent => { return ethers.BigNumber.from(agent.totalAmount).sub(agent.freezeAmount).toString() }) const sorted = amounts.concat('0').sort((a, b) => ethers.BigNumber.from(b).gte(a) ? 1 : -1) const amount = sorted[0] ? sorted[0] : '0' setState((state) => ({ ...state, maxTransferAmount: ethers.BigNumber.from(amount), })) }) setState((state) => ({ ...state, feeList: feeRes?.fees || [], })) } useEffect(() => { var timer: NodeJS.Timeout if (modals.isShowTransferToTaikoAccount.isShow && fastWithdrawConfig) { timer = setInterval(() => { refreshData() }, 10 * 1000) refreshData() } return () => { timer && clearInterval(timer) } }, [modals.isShowTransferToTaikoAccount.isShow, fastWithdrawConfig]) const { walletMap } = makeWalletLayer2({ needFilterZero: true }) const enoughFeeList = walletMap && tokenMap ? state.feeList?.filter(fee => { return ethers.utils.parseUnits(walletMap[fee.token]?.count.toString() ?? '0', tokenMap[fee.token].decimals).gte(fee.fee) }) : [] const _feeInfo = [state.feeToken, ...feeChargeOrder] .map((order) => enoughFeeList?.find((fee) => fee.token === order)) .filter((fee) => fee)[0] const feeInfo = _feeInfo ? _feeInfo : state.feeList?.find(feeInfo => feeInfo.token === 'ETH') const feeTokenSymbol = feeInfo?.token const feeToken = (tokenMap && feeTokenSymbol) ? tokenMap[feeTokenSymbol] : undefined const feeRaw = feeInfo?.fee const transferTokenWallet = walletMap ? walletMap[transferTokenSymbol] : undefined const isInvalidAddress = state.receipt && !ethers.utils.isAddress(state.receipt) const balance = transferTokenWallet && transferToken ? transferTokenWallet.count : undefined; const isOverBalance = state.amount && new Decimal(state.amount) .gt(balance ?? '0') const isFeeNotEnough = (() => { if (!tokenMap) return false if (!feeInfo || !walletMap || !feeTokenSymbol || !walletMap[feeTokenSymbol]?.count || !feeToken) return true const count = walletMap[feeTokenSymbol]!.count return new Decimal(count).lt(utils.formatUnits(feeInfo.fee, feeToken.decimals)) })() const sendBtnDisabled = state.feeLoading || !state.amount || new Decimal(state.amount).lessThanOrEqualTo('0') || !state.receipt || isOverMax || isOverBalance || isFeeNotEnough || isInvalidAddress const {walletProvider} = useAppKitProvider('eip155') const sendBtn = { onClick: async () => { setState({ ...state, panel: 'confirm', }) }, disabled: sendBtnDisabled, text: 'Send' } const debouncedRefreshData = useDebouncedCallback(refreshData, 500) const isToEthereum = parsed?.toOtherNetworks[0]?.network && ['SEPOLIA', 'ETHEREUM'].includes(parsed?.toOtherNetworks[0]?.network) const confirmSend = async () => { setShowAccount({ step: AccountStep.Transfer_To_Taiko_In_Progress, isShow: true, info: { isToEthereum } }) return transferToOtherNetwork({ account, chainId: defaultNetwork, transferToken, feeToken: feeToken!, state, configJSON: parsed!, feeRaw: feeRaw!, walletProvider: walletProvider, balance: balance?.toString() ?? '0', }).then((response) => { if ((response as any)?.resultInfo?.code || (response as any)?.resultInfo?.message) { throw (response as any).resultInfo } else if (response.status === 'failed') { throw new Error('withdraw filed') } setShowTransferToTaikoAccount({ isShow: false }) setState(initialState) setShowAccount({ step: AccountStep.Transfer_To_Taiko_Success, isShow: true, info: { isToEthereum } }) sleep(1000).then(() => { updateWalletLayer2() }) }) .catch(e => { const msg = checkErrorInfo(e, false) const userDenied = [ConnectorError.USER_DENIED, ConnectorError.USER_DENIED_2].includes( msg as ConnectorError, ) || 'User rejected the request.' === msg if (userDenied) { setShowAccount({ step: AccountStep.Transfer_To_Taiko_User_Denied, isShow: true, info: { isToEthereum } }) } else { setShowAccount({ step: AccountStep.Transfer_To_Taiko_Failed, isShow: true, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, info: { isToEthereum } }) } }) } const output = { onClickContact: () => { setState({ ...state, panel: 'contacts', }) }, onClickFee: () => { setState({ ...state, showFeeModal: true, }) }, onClickConfirm: confirmSend, onInputAmount: (str) => { if (isValidateNumberStr(str, transferToken.precision) || str === '') { setState({ ...state, amount: str, }) debouncedRefreshData() } }, onInputAddress: (addr) => { setState({ ...state, receipt: addr, }) }, onClickToken: () => { setState({ ...state, panel: 'tokenSelection', }) }, onClickBalance: () => { if (transferTokenWallet?.count.toString()) { setState({ ...state, amount: transferTokenWallet.count.toString(), }) } }, fee: feeToken && feeRaw ? numberFormat(ethers.utils.formatUnits(feeRaw, feeToken.decimals), { fixed: feeToken.precision, removeTrailingZero: true, }) + ' ' + feeToken.symbol : '--', balance: balance ? numberFormat(balance, { fixed: transferToken.precision, removeTrailingZero: true, }) + ' ' + transferTokenSymbol : '--', token: { coinJSON: coinJson[transferTokenSymbol], symbol: transferTokenSymbol, }, feeSelect: { open: state.showFeeModal, onClose: () => { setState({ ...state, showFeeModal: false, }) }, feeInfo: feeInfo && walletMap && tokenMap ? offchainFeeInfoToFeeInfo(feeInfo, tokenMap, walletMap as any) : undefined, chargeFeeTokenList: walletMap && tokenMap ? state.feeList?.map((item) => offchainFeeInfoToFeeInfo(item, tokenMap, walletMap as any)) : [], handleToggleChange: (feeInfo) => { setState({ ...state, feeToken: feeInfo.token!, }) }, disableNoToken: false, onClickFee: () => {}, feeLoading: state.feeLoading || state.feeList === undefined, // isFeeNotEnough: true, isFeeNotEnough, isFastWithdrawAmountLimit: false, }, panel: state.panel, contacts: { onSelect: (address) => { setState({ ...state, panel: 'main', receipt: address, }) }, scrollHeight: '200px', contacts: contacts as any, }, tokenSelection: { filter: state.tokenFilterInput, tokens: coinJson ? transferTokenList .filter((token) => { if (state.tokenFilterInput) { return token.toLowerCase().includes(state.tokenFilterInput.toLowerCase()) } return true }) .map((token) => ({ symbol: token, coinJSON: [coinJson[token]], amount: walletMap && walletMap[token] ? walletMap[token].count.toString() : '0', })) : [], onChangeFilter: (inputValue: string) => { setState({ ...state, tokenFilterInput: inputValue, }) }, onClickClearFilter: () => { setState({ ...state, tokenFilterInput: '', }) }, onClickCancel: () => { setState({ ...state, panel: 'main', }) }, onClickToken: (symbol) => { setState({ ...state, transferToken: symbol, panel: 'main', amount: '', }) refreshData() }, }, receiptInput: state.receipt, amountInput: state.amount, onClickBack() { if (state.panel === 'main') { if (modals.isShowTransferToTaikoAccount.from === 'bridge') { setShowBridge({ isShow: true }) } else { setShowAccount({ isShow: true, step: AccountStep.SendAssetGateway, info: { symbol: modals.isShowTransferToTaikoAccount.info?.initSymbol }, }) } setShowTransferToTaikoAccount({ isShow: false }) setState(initialState) } else { setState({ ...state, panel: 'main', }) } }, onClickClose() { setShowTransferToTaikoAccount({ isShow: false }) setState(initialState) }, open: modals.isShowTransferToTaikoAccount.isShow, supportedTokens: transferTokenList, sendBtn: sendBtn, maxAlert: { show: isOverMax || isOverBalance, message: isOverMax && state.maxTransferAmount ? `Quota: ${numberFormat( utils.formatUnits(state.maxTransferAmount!, transferToken.decimals), { fixed: transferToken.precision, removeTrailingZero: true }, )} ${transferToken.symbol}` : isOverBalance ? `Insufficient ${transferToken.symbol} balance` : '', }, receiptError: { show: isInvalidAddress, message: 'invalid address', }, receiptClear: { show: !!state.receipt, onClick: () => { setState({ ...state, receipt: '', }) }, }, showReceiptWarning: state.receipt && !isInvalidAddress, retrySend: () => { confirmSend() }, title: isToEthereum ? 'Send to Ethereum' : 'Send to Taiko', hideContactBtn: app === 'earn' } as TransferToTaikoAccountProps return output } ================================================ FILE: packages/core/src/hooks/useractions/useUpdateAccount.ts ================================================ import React from 'react' import { FeeInfo, MapChainId, myLog, UIERROR_CODE } from '@loopring-web/common-resources' import { AccountStep, setShowAccount as _setShowAccount, useOpenModals, useSettings } from '@loopring-web/component-lib' import { activateAccount, useAccount, LoopringAPI, accountServices, activateAccountSmartWallet, updateAccountRecursively, isCoinbaseSmartWallet, encryptAESMd5, isSameEVMAddress, withRetry, getAndSaveEncryptedSKFromServer } from '../../index' import * as sdk from '@loopring-web/loopring-sdk' import { useWalletInfo } from '../../stores/localStore/walletInfo' import { useLocation } from 'react-router-dom'; import { coinbaseSmartWalletPersist, store } from '../../stores' import { persistStoreCoinbaseSmartWalletData } from 'stores/localStore/coinbaseSmartWalletPersist' const handleError = (e: any, isReset: boolean) => { const setShowAccount = (args: any) => store.dispatch(_setShowAccount(args)) if (e.message === 'submitEncryptedEcdsaKey failed') { setShowAccount({ isShow: true, step: AccountStep.Coinbase_Smart_Wallet_Password_Set_Error, }) return } const error = LoopringAPI?.exchangeAPI?.genErr(e as any) ?? { code: UIERROR_CODE.DATA_NOT_READY, } const code = sdk.checkErrorInfo(error, true) myLog('unlock', error, e, code) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: myLog('activateAccount UpdateAccount: NOT_SUPPORT_ERROR') setShowAccount({ isShow: true, step: isReset ? AccountStep.ResetAccount_First_Method_Denied : AccountStep.UpdateAccount_First_Method_Denied, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: myLog('activateAccount: USER_DENIED') setShowAccount({ isShow: true, step: isReset ? AccountStep.ResetAccount_User_Denied : AccountStep.UpdateAccount_User_Denied, }) break default: setShowAccount({ isShow: true, step: isReset ? AccountStep.ResetAccount_Failed : AccountStep.UpdateAccount_Failed, error: { ...((e as any) ?? {}), ...error, code: (e as any)?.code ?? UIERROR_CODE.UNKNOWN, }, }) break } throw error } export const goUpdateAccountCoinbaseWalletBackupKeyOnlyFn = async ({ isReset = false, backupKeyJSON, }: { isFirstTime?: boolean isReset?: boolean feeInfo?: FeeInfo backupKeyJSON: string }) => { const { settings: { defaultNetwork }, account: { accAddress }, } = store.getState() const setShowAccount = (args: any) => store.dispatch(_setShowAccount(args)) const { eddsaKey, request } = JSON.parse(backupKeyJSON) try { setShowAccount({ isShow: true, step: AccountStep.Coinbase_Smart_Wallet_Password_Set_Processing, info: { step: 'updatingAccount', showResumeUpdateAccount: true, }, }) const [{ apiKey }, { walletType }] = await Promise.all([ LoopringAPI?.userAPI?.getUserApiKey( { accountId: request.accountId, }, eddsaKey.sk, ), LoopringAPI?.walletAPI?.getWalletType({ wallet: accAddress, network: MapChainId[defaultNetwork] as sdk.NetworkWallet, }), ]).then((response) => { if ((response[0] as sdk.RESULT_INFO)?.code) { throw response[0] } return response as any }) await withRetry( () => LoopringAPI!.userAPI!.submitEncryptedEcdsaKey(request, eddsaKey.sk, apiKey).then((res) => { if (res.code) { throw res } return res }), 3, 1000, )() .catch((e) => { throw new Error('submitEncryptedEcdsaKey failed') }) const foundData = store.getState().localStore.coinbaseSmartWalletPersist.data!.find((item) => item.chainId === defaultNetwork && isSameEVMAddress(item.wallet, accAddress))! store.dispatch( coinbaseSmartWalletPersist.persistStoreCoinbaseSmartWalletData({ ...foundData, eddsaKeyBackup: { backupNotFinished: false, json: '' } }) ) accountServices.sendAccountSigned({ apiKey, eddsaKey, isInCounterFactualStatus: walletType?.isInCounterFactualStatus, isContract: walletType?.isContract, }) setShowAccount({ isShow: true, step: AccountStep.Coinbase_Smart_Wallet_Password_Set_Processing, info: { step: 'completed', showResumeUpdateAccount: true, }, }) await sdk.sleep(2 * 1000) setShowAccount({ isShow: false }) } catch (e) { handleError(e, isReset) } } export const goUpdateAccountCoinbaseWalletUpdateAccountFn = async ({ isFirstTime = false, isReset = false, feeInfo, updateAccountJSON, }: { isFirstTime?: boolean isReset?: boolean feeInfo?: FeeInfo updateAccountJSON: string }) => { const { settings: { defaultNetwork }, } = store.getState() const setShowAccount = (args: any) => store.dispatch(_setShowAccount(args)) const { eddsaKey, request } = JSON.parse(updateAccountJSON) try { setShowAccount({ isShow: true, step: AccountStep.Coinbase_Smart_Wallet_Password_Set_Processing, info: { step: 'updatingAccount', showResumeUpdateAccount: true, }, }) await updateAccountRecursively({ request, eddsaKey: { eddsaKey }, }) const foundData = store.getState().localStore.coinbaseSmartWalletPersist.data!.find((item) => item.chainId === defaultNetwork && isSameEVMAddress(item.wallet, request.owner))! const submitEncryptedEcdsaKeyReq = { accountId: request.accountId, eddsaEncryptedPrivateKey: foundData.eddsaKey.sk, nonce: request.nonce + 1, } const backupKeyJSON = JSON.stringify({ request: submitEncryptedEcdsaKeyReq, eddsaKey: eddsaKey }) store.dispatch( coinbaseSmartWalletPersist.persistStoreCoinbaseSmartWalletData({ ...foundData, nonce: request.nonce + 1, updateAccountData: { updateAccountNotFinished: false, json: '', }, eddsaKeyBackup: { backupNotFinished: true, json: backupKeyJSON } }), ) await goUpdateAccountCoinbaseWalletBackupKeyOnlyFn({ isReset, backupKeyJSON, }) } catch (e) { handleError(e, isReset) } } const checkBeforeGoUpdateAccount = async (isReset: boolean, feeInfo?: FeeInfo) => { const { account: { accAddress, nonce, accountId }, settings: { defaultNetwork }, localStore: { coinbaseSmartWalletPersist }, system: { exchangeInfo }, } = store.getState() if (!exchangeInfo) { return false } if (await isCoinbaseSmartWallet(accAddress, defaultNetwork)) { const foundPersistData = coinbaseSmartWalletPersist?.data.find( (item) => item.chainId === defaultNetwork && isSameEVMAddress(item.wallet, accAddress) && item.nonce === nonce, ) if ( foundPersistData && !!foundPersistData.eddsaKeyBackup?.backupNotFinished && foundPersistData.eddsaKeyBackup?.json ) { goUpdateAccountCoinbaseWalletBackupKeyOnlyFn({ isReset: false, backupKeyJSON: foundPersistData.eddsaKeyBackup?.json!, }) } else if ( foundPersistData && !!foundPersistData.updateAccountData?.updateAccountNotFinished && foundPersistData.updateAccountData?.json ) { goUpdateAccountCoinbaseWalletUpdateAccountFn({ isReset: false, updateAccountJSON: foundPersistData.updateAccountData?.json!, }) } else { store.dispatch( _setShowAccount({ isShow: true, step: AccountStep.Coinbase_Smart_Wallet_Password_Set, info: { feeInfo, } }), ) } return false } return true } export function useUpdateAccount() { const { updateHW, checkHWAddr } = useWalletInfo() const { setShowAccount } = useOpenModals() const { account } = useAccount() const { search } = useLocation() const { referralCode, setReferralCode, defaultNetwork } = useSettings() const { persistStoreCoinbaseSmartWalletData } = coinbaseSmartWalletPersist.useCoinbaseSmartWalletPersist() const goUpdateAccount = React.useCallback( async ({ isFirstTime = false, isReset = false, feeInfo, }: { isFirstTime?: boolean isReset?: boolean feeInfo?: FeeInfo }) => { const shouldContinue = await checkBeforeGoUpdateAccount(isReset, feeInfo) if (!shouldContinue) { return } setShowAccount({ isShow: true, step: isReset ? AccountStep.ResetAccount_Approve_WaitForAuth : AccountStep.UpdateAccount_Approve_WaitForAuth, }) const isHWAddr = !isFirstTime ? true : checkHWAddr(account.accAddress) myLog( 'goUpdateAccount: isFirstTime:', isFirstTime, ' isReset:', isReset, ' isHWAddr:', isHWAddr, ) let walletType, apiKey try { const { eddsaKey, accInfo } = await activateAccount({ isHWAddr, feeInfo, isReset, referral: referralCode, }) if (!isFirstTime && isHWAddr) { updateHW({ wallet: account.accAddress, isHWAddr }) } if (LoopringAPI.userAPI && LoopringAPI.walletAPI && accInfo && accInfo?.accountId !== -1) { ;[{ apiKey }, { walletType }] = await Promise.all([ LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk, ), LoopringAPI.walletAPI.getWalletType({ wallet: account.accAddress, network: MapChainId[defaultNetwork] as sdk.NetworkWallet }), ]) .then((response) => { if ((response[0] as sdk.RESULT_INFO)?.code) { throw response[0] } return response as any }) .catch((error) => { throw error }) accountServices.sendAccountSigned({ apiKey, eddsaKey, isInCounterFactualStatus: walletType?.isInCounterFactualStatus, isContract: walletType?.isContract, }) setShowAccount({ isShow: true, step: isReset ? AccountStep.ResetAccount_Success : AccountStep.UpdateAccount_Success, }) await sdk.sleep(1000) setShowAccount({ isShow: false }) } else { throw { code: UIERROR_CODE.DATA_NOT_READY } } } catch (e) { handleError(e, isReset) } setReferralCode('') }, [account.accAddress, search, checkHWAddr, setShowAccount, updateHW, referralCode], ) const goUpdateAccountCoinbaseWallet = React.useCallback( async ({ isFirstTime = false, isReset = false, feeInfo, password, }: { password: string isFirstTime?: boolean isReset?: boolean feeInfo?: FeeInfo }) => { try { setShowAccount({ isShow: true, step: AccountStep.Coinbase_Smart_Wallet_Password_Set_Processing, info: { step: 'keyGenerating', showResumeUpdateAccount: false } }) const { eddsaKey, request, approveFn } = await activateAccountSmartWallet({ feeInfo, isReset, referral: referralCode, }) setShowAccount({ isShow: true, step: AccountStep.Coinbase_Smart_Wallet_Password_Set_Processing, info: { step: 'blockConfirming', showResumeUpdateAccount: false } }) await approveFn() const encryptedSk = encryptAESMd5(password, eddsaKey.sk) const updateAccountJSON = JSON.stringify({ request, eddsaKey }) persistStoreCoinbaseSmartWalletData({ eddsaKey: { ...eddsaKey, sk: encryptedSk }, wallet: request.owner, nonce: request.nonce, chainId: defaultNetwork, updateAccountData: { updateAccountNotFinished: true, json: updateAccountJSON }, eddsaKeyBackup: { backupNotFinished: true, json: '' } }) await goUpdateAccountCoinbaseWalletUpdateAccountFn({ isFirstTime, isReset, feeInfo, updateAccountJSON, }) } catch (e) { handleError(e, isReset) } setReferralCode('') }, [account.accAddress, search, checkHWAddr, setShowAccount, updateHW, referralCode], ) return { goUpdateAccount, goUpdateAccountCoinbaseWallet, } } ================================================ FILE: packages/core/src/hooks/useractions/useVendor.ts ================================================ import { AccountStatus, L1L2_NAME_DEFINED, MapChainId, myLog, TradeBtnStatus, VendorItem, VendorList, } from '@loopring-web/common-resources' import { store, useAccount, useModalData, useSystem } from '../../index' import { RampInstantEventTypes, RampInstantSDK } from '@ramp-network/ramp-instant-sdk' import { AccountStep, useOpenModals, useSettings } from '@loopring-web/component-lib' import React from 'react' import { useTranslation } from 'react-i18next' import { BanxaCheck, banxaService, OrderENDReason } from '../../services' import _ from 'lodash' import { useHistory, useRouteMatch } from 'react-router-dom' import { useLocation } from 'react-use' export enum RAMP_SELL_PANEL { LIST, RAMP_CONFIRM, BANXA_CONFIRM, } export const useVendor = () => { const { account } = useAccount() const { t } = useTranslation() const { setShowTradeIsFrozen } = useOpenModals() const match: any = useRouteMatch('/trade/fiat/:tab?') const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const banxaRef = React.useRef() const subject = React.useMemo(() => banxaService.onSocket(), []) const { allowTrade: { raw_data }, } = useSystem() const legalEnable = (raw_data as any)?.legal?.enable const legalShow = (raw_data as any)?.legal?.show const { setShowAccount } = useOpenModals() const nodeTimer = React.useRef(-1) const history = useHistory() const { href } = useLocation() const search = href?.split('?')[1] ?? '' const searchParams = new URLSearchParams(search) const { // updateOffRampData, resetOffRampData, resetOffBanxaData, } = useModalData() const [sellPanel, setSellPanel] = React.useState( RAMP_SELL_PANEL.LIST, // RAMP_SELL_PANEL.BANXA_CONFIRM ) const [banxaBtnStatus, setBanxaBtnStatus] = React.useState( TradeBtnStatus.AVAILABLE, ) const _banxaClick = _.debounce(() => { resetOffBanxaData() banxaService.banxaStart() }, 500) const vendorListBuy: VendorItem[] = legalShow ? [ { // key: VendorProviders.Ramp, // svgIcon: "RampIcon", ...VendorList.Ramp, handleSelect: () => { setShowAccount({ isShow: false }) if (legalEnable) { let config: any = { hostAppName: 'Loopring', hostLogoUrl: 'https://static.loopring.io/assets/svg/logo.svg', userAddress: account.accAddress, defaultFlow: 'ONRAMP', enabledFlows: ['ONRAMP'], } if (account && account.accountId && account.accountId !== -1) { config = { ...config, swapAsset: 'LOOPRING_*', // enabledFlows: ["ONRAMP"], hostApiKey: 'r6e232on45rt3ukdb7zbcvh3avdwbqpore5rbht7', } } else { config = { ...config, swapAsset: 'LOOPRING_ETH,LOOPRING_USDC,LOOPRING_LRC', hostApiKey: 'xqh8ej6ye2rpoj528xd6rkghsgmyrk4hxb7kxarz', } } window.rampInstance = new RampInstantSDK({ ...config, }).show() window.rampInstance.on(RampInstantEventTypes.WIDGET_CLOSE, () => { resetOffRampData() setSellPanel(RAMP_SELL_PANEL.LIST) if (window.rampInstance) { window.rampInstance.unsubscribe('*', () => undefined) window.rampInstance = undefined } let dex = 'labelAddAssetTitleCardDes' if ( account.readyState && [ AccountStatus.DEPOSITING, AccountStatus.NOT_ACTIVE, AccountStatus.NO_ACCOUNT, ].includes( // @ts-ignore account?.readyState, ) ) { dex = 'labelAddAssetTitleCardDesActive' } setShowAccount({ isShow: true, step: AccountStep.ThirdPanelReturn, info: { title: t('labelAddAssetTitleCard'), description: t(dex, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, }) }) } }, }, { ...VendorList.Banxa, handleSelect: () => { let dex = 'labelAddAssetTitleCardDes' if ( account.readyState && [ AccountStatus.DEPOSITING, AccountStatus.NOT_ACTIVE, AccountStatus.NO_ACCOUNT, ].includes( // @ts-ignore account?.readyState, ) ) { dex = 'labelAddAssetTitleCardDesActive' } setShowAccount({ isShow: true, step: AccountStep.ThirdPanelReturn, info: { title: t('labelAddAssetTitleCard'), description: t(dex, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, }) if (legalEnable) { window.open( 'https://loopring.banxa.com/?code=1fe263e17175561954c6&buyMode&walletAddress=' + account.accAddress, '_blank', ) window.opener = null } }, }, ] : [] const vendorListSell: VendorItem[] = legalShow ? [ // { // ...VendorList.Ramp, // handleSelect: () => { // setShowAccount({ isShow: false }); // if (legalEnable) { // let config: any = { // hostAppName: "Loopring", // hostLogoUrl: "https://static.loopring.io/assets/svg/logo.svg", // userAddress: account.accAddress, // defaultFlow: "OFFRAMP", // enabledFlows: ["OFFRAMP"], // url: "https://ramp.network/sell-beta", // }; // config = { // ...config, // hostApiKey: "qjkymvqp2q7uvvrf7x6fb93pxn4aqc5tb7xheg8u", // }; // window.rampInstance = new RampInstantSDK({ // ...config, // }); // window.rampInstance.onSendCrypto( // ( // assetSymbol: string, // amount: string, // destinationAddress: string // ) => { // if (window.rampInstance) { // try { // updateOffRampData({ // send: { assetSymbol, amount, destinationAddress }, // }); // setSellPanel(RAMP_SELL_PANEL.RAMP_CONFIRM); // console.log( // "onSendCrypto", // assetSymbol, // destinationAddress // ); // //@ts-ignore // window.rampInstance.domNodes.overlay.style.display = // "none"; // console.log("RAMP WEIGHT hidden on send Crypto"); // } catch (e) { // console.log("RAMP WEIGHT hidden failed"); // } // } else { // resetOffRampData(); // setSellPanel(RAMP_SELL_PANEL.LIST); // } // return new Promise(() => {}); // } // ); // window.rampInstance.on(RampInstantEventTypes.WIDGET_CLOSE, () => { // console.log("RAMP WEIGHT close"); // resetOffRampData(); // setSellPanel(RAMP_SELL_PANEL.LIST); // if (window.rampInstance) { // window.rampInstance.unsubscribe("*", () => undefined); // window.rampInstance = undefined; // } // }); // console.log("RAMP WEIGHT display on send user selected"); // window.rampInstance.show(); // } // }, // }, { ...VendorList.Banxa, btnStatus: banxaBtnStatus, handleSelect: async (_event) => { setBanxaBtnStatus(TradeBtnStatus.LOADING) setShowAccount({ isShow: false }) _banxaClick() // @ts-ignore }, }, ] : [] const closeBanxa = () => { // @ts-ignore var parentsNode: undefined | HTMLElement = window.document.getElementById('iframeBanxaTarget') if (parentsNode && parentsNode.style) { // @ts-ignore parentsNode.style.display = 'none' } } const enterCheck = React.useCallback(async () => { const data = await banxaService.banxaCheckHavePending() if (data?.order?.status === 'waitingPayment') { myLog('banxa Check Have waitingPayment', data.order) banxaService.KYCDone() searchParams.set('orderId', data?.order.id) history.replace({ pathname: '/trade/fiat/sell', search: searchParams.toString(), }) store.dispatch( setShowAccount({ isShow: true, step: AccountStep.ContinuousBanxaOrder, info: { orderId: data?.order.id }, }), ) // closeBanxa() return } setBanxaBtnStatus(TradeBtnStatus.AVAILABLE) }, []) React.useEffect(() => { const offBanxaValue = store.getState()._router_modalData.offBanxaValue if ( match?.params?.tab?.toLowerCase() === 'sell'.toLowerCase() && searchParams.get('orderId') && searchParams.get('orderId')?.toLowerCase() !== offBanxaValue?.id?.toLowerCase() ) { banxaService.banxaCheckHavePending() } else if ( match?.params?.tab?.toLowerCase() === 'sell'.toLowerCase() && !searchParams.has('orderId') ) { setBanxaBtnStatus(TradeBtnStatus.LOADING) setSellPanel(RAMP_SELL_PANEL.LIST) enterCheck() } }, [match?.params?.tab, searchParams?.get('orderId')]) const clickEvent = () => banxaService.banxaEnd({ reason: OrderENDReason.UserCancel, data: { resource: 'on close' }, }) React.useEffect(() => { const close = window.document.querySelector('#iframeBanxaClose') const parentsNode = window.document.querySelector('#iframeBanxaTarget') if (close && parentsNode) { parentsNode.addEventListener('click', clickEvent) } const subscription = subject.subscribe((props) => { switch (props.status) { // case BanxaCheck.CheckOrderStatus: // checkOrderStatus(props.data); // break; case BanxaCheck.OrderHide: setBanxaBtnStatus(TradeBtnStatus.AVAILABLE) // hideBanxa(); closeBanxa() break case BanxaCheck.OrderShow: setBanxaBtnStatus(TradeBtnStatus.AVAILABLE) // showBanxa(); break case BanxaCheck.OrderEnd: // myLog("subscription Banxa", props. status:t(status), props.data); if (props?.data?.reason === OrderENDReason.BanxaNotReady) { setShowTradeIsFrozen({ isShow: true, messageKey: 'labelBanxaNotReady', }) } if (props?.data?.reason === OrderENDReason.CreateOrderFailed) { setShowTradeIsFrozen({ isShow: true, messageKey: 'labelBanxaFailedForAPI', }) } closeBanxa() setBanxaBtnStatus(TradeBtnStatus.AVAILABLE) // clearTimeout(nodeTimer.current as NodeJS.Timeout); break default: break } }) return () => { if (close && parentsNode) { parentsNode?.removeEventListener('click', clickEvent) } clearTimeout(nodeTimer.current as NodeJS.Timeout) subscription.unsubscribe() closeBanxa() } }, []) return { banxaRef, vendorListBuy, vendorListSell, vendorForce: undefined, sellPanel, setSellPanel, // setSellPanel, } } ================================================ FILE: packages/core/src/hooks/useractions/useWithdraw.ts ================================================ import React from 'react' import Web3 from 'web3' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { AccountStep, SwitchData, useOpenModals, useSettings, useToggle, WithdrawProps } from '@loopring-web/component-lib' import { AccountStatus, AddressError, CoinMap, EXCHANGE_TYPE, Explorer, FeeInfo, getValuePrecisionThousand, IBData, MapChainId, myLog, SagaStatus, SUBMIT_PANEL_AUTO_CLOSE, TRADE_TYPE, TradeBtnStatus, UIERROR_CODE, WALLET_TYPE, WalletMap, WITHDRAW_TOKEN_FILTER_LIST, WithdrawType, WithdrawTypes, } from '@loopring-web/common-resources' import * as sdk from '@loopring-web/loopring-sdk' import { BIGO, DAYS, getTimestampDaysLater, isAccActivated, LAST_STEP, LoopringAPI, makeWalletLayer2, store, TokenMap, useAccount, useAddressCheck, useContacts, useDebouncedCallback, useModalData, useSystem, useTokenMap, useTokenPrices, useWalletLayer2Socket, walletLayer2Service, fiatNumberDisplaySafe, parseRabbitConfig, useConfig, numberFormat, tryFn, } from '../../index' import { useWalletInfo } from '../../stores/localStore/walletInfo' import _, { values, omit } from 'lodash' import { addressToExWalletMapFn, exWalletToAddressMapFn } from '@loopring-web/core' import { useGetSet } from 'react-use' import { ethers } from 'ethers' import Decimal from 'decimal.js' import { useAppKitProvider } from '@reown/appkit/react' const offchainFeeInfoToFeeInfo = (offchainFeeInfo: sdk.OffchainFeeInfo, tokenMap: TokenMap<{ [key: string]: any; }>, walletMap: WalletMap) => { return { belong: offchainFeeInfo.token, fee: tokenMap[offchainFeeInfo.token] ? ethers.utils.formatUnits(offchainFeeInfo.fee, tokenMap[offchainFeeInfo.token].decimals) : '', feeRaw: offchainFeeInfo.fee, token: offchainFeeInfo.token, hasToken: !!offchainFeeInfo.token, count: walletMap[offchainFeeInfo.token]?.count, discount: offchainFeeInfo.discount, __raw__: { fastWithDraw: '', tokenId: tokenMap[offchainFeeInfo.token]?.tokenId, feeRaw: offchainFeeInfo.fee, } } } export const useWithdraw = , T>() => { const { modals: { isShowWithdraw: { symbol, isShow, info, address: contactAddress }, }, setShowAccount, setShowWithdraw, setShowEditContact, } = useOpenModals() const { tokenMap, totalCoinMap, disableWithdrawList, idIndex } = useTokenMap() const { tokenPrices } = useTokenPrices() const { currency, defaultNetwork, feeChargeOrder } = useSettings() const { account, status: accountStatus } = useAccount() const { exchangeInfo, chainId, getValueInCurrency, app } = useSystem() const { contacts, errorMessage: contactsErrorMessage, updateContacts, status: contactStatus, } = useContacts() const { withdrawValue, updateWithdrawData, resetWithdrawData } = useModalData() const [walletMap2, setWalletMap2] = React.useState( makeWalletLayer2({ needFilterZero: true, _isToL1: true }).walletMap ?? ({} as WalletMap), ) const [sureIsAllowAddress, setSureIsAllowAddress] = React.useState< WALLET_TYPE | EXCHANGE_TYPE | undefined >(undefined) const initState = { fee: { chargeFeeTokenListNormal: [] as sdk.OffchainFeeInfo[], chargeFeeTokenListFast: [] as sdk.OffchainFeeInfo[], isOnLoading: false, symbol: undefined as string | undefined, }, withdrawMode: { fastInfo: undefined as undefined | { fee: string time: string }, maxFastWithdrawAmountBN: undefined as undefined | string, normalInfo: undefined as undefined | { fee: string time: string }, mode: 'fast' as 'fast' | 'normal', } } const {fastWithdrawConfig} = useConfig() const parsed = fastWithdrawConfig && idIndex && MapChainId[defaultNetwork] ? parseRabbitConfig(fastWithdrawConfig, MapChainId[defaultNetwork], idIndex) : undefined const fastModeTokens = parsed?.toL1SupportedTokens const [getState, setState] = useGetSet(initState) const state = getState() const { fee: { symbol: feeSymbol, chargeFeeTokenListFast, chargeFeeTokenListNormal }, withdrawMode } = state const withdrawToken = tokenMap[withdrawValue.belong as string] const {toggle} = useToggle() const tradeValueBN = withdrawToken && withdrawValue.tradeValue ? ethers.utils.parseUnits(withdrawValue.tradeValue.toString(), withdrawToken.decimals) : ethers.BigNumber.from('0') const fastWithdrawOverflow = state.withdrawMode.maxFastWithdrawAmountBN && tradeValueBN ? tradeValueBN.gte(state.withdrawMode.maxFastWithdrawAmountBN) : undefined const fastModeSupportted = toggle.rabbitWithdraw.enable && fastModeTokens?.includes(withdrawValue.belong as string) const isFastMode = fastWithdrawOverflow === false && fastModeSupportted ? withdrawMode.mode === 'fast' : false const chargeFeeTokenList = isFastMode ? chargeFeeTokenListFast : chargeFeeTokenListNormal const enoughFeeList = tryFn(() => { return chargeFeeTokenList.filter(fee => { return ethers.utils.parseUnits(walletMap2[fee.token]?.count ?? '0', tokenMap[fee.token].decimals).gte(fee.fee) }) }, () => []) const _feeInfo = [feeSymbol, ...feeChargeOrder] .map((order) => enoughFeeList?.find((fee) => fee.token === order)) .filter((fee) => fee)[0] const feeInfo = _feeInfo ? _feeInfo : chargeFeeTokenList?.find(feeInfo => feeInfo.token === 'ETH') const feeInfo2 = feeInfo && walletMap2 && tokenMap ? offchainFeeInfoToFeeInfo(feeInfo, tokenMap, walletMap2 as any) : undefined; const isFeeNotEnough = { isFeeNotEnough: feeInfo2 && feeInfo2.fee && feeInfo2.count ? new Decimal(feeInfo2.fee).gt(feeInfo2.count) : true, isOnLoading: getState().fee.isOnLoading, } const handleFeeChange = (feeInfo: FeeInfo) => { setState({ ...getState(), fee: { ...getState().fee, symbol: feeInfo.token, }, }) } const { checkHWAddr, updateHW } = useWalletInfo() const [lastRequest, setLastRequest] = React.useState({}) const { address, realAddr, isCFAddress, isContractAddress, setAddress, addrStatus, isLoopringAddress, isAddressCheckLoading, loopringSmartWalletVersion, reCheck, isENSWrong, ens, } = useAddressCheck(false) React.useEffect(() => { if (loopringSmartWalletVersion?.isLoopringSmartWallet && sureIsAllowAddress === undefined) { setSureIsAllowAddress(WALLET_TYPE.Loopring) } }, [loopringSmartWalletVersion?.isLoopringSmartWallet]) const isNotAvailableAddress = // isCFAddress // ? "isCFAddress" // : isContractAddress && disableWithdrawList.includes(withdrawValue?.belong ?? '') ? `isContractAddress` : undefined const checkBtnStatus = () => { const withdrawValue = store.getState()._router_modalData.withdrawValue if (tokenMap && withdrawValue.belong && tokenMap[withdrawValue.belong]) { const withdrawT = tokenMap[withdrawValue.belong] const tradeValue = sdk.toBig(withdrawValue.tradeValue ?? 0).times('1e' + withdrawT.decimals) const exceedPoolLimit = withdrawValue.withdrawType == sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL && tradeValue.gt(0) && withdrawT.fastWithdrawLimit && tradeValue.gte(withdrawT.fastWithdrawLimit) const isEnough = tradeValue.lte( sdk.toBig(withdrawValue.balance ?? 0).times('1e' + withdrawT.decimals), ) const contact = contacts?.find((x) => x.contactAddress === realAddr) const ensHasCheck = (contact?.ens || ens) ? !isENSWrong : true if (exceedPoolLimit) { const amt = getValuePrecisionThousand( sdk.toBig(withdrawT.fastWithdrawLimit ?? 0).div('1e' + withdrawT.decimals), withdrawT.precision, withdrawT.precision, withdrawT.precision, false, { floor: true }, ).toString() return { label: `labelL2toL1BtnExceed|${amt}`, enable: false, isFastWithdrawAmountLimit: true, } } else if (!info?.isToMyself && realAddr && withdrawValue.tradeValue && sureIsAllowAddress === undefined) { return { label: 'Please input address type', enable: false, isFastWithdrawAmountLimit: false, } } if ( tradeValue && !exceedPoolLimit && !isNotAvailableAddress && chargeFeeTokenList.length && !isFeeNotEnough.isFeeNotEnough && feeInfo2?.belong && feeInfo2?.feeRaw && tradeValue.gt(BIGO) && !isFeeNotEnough.isOnLoading && withdrawValue.tradeValue && realAddr && isEnough && ensHasCheck && (info?.isToMyself || sureIsAllowAddress) && [AddressError.NoError, AddressError.IsNotLoopringContract].includes(addrStatus) ) { return { enable: true, isFastWithdrawAmountLimit: false, label: undefined } } } return { label: undefined, enable: false, isFastWithdrawAmountLimit: false } } const checkResult = checkBtnStatus() const btnStatus: TradeBtnStatus = checkResult.enable ? TradeBtnStatus.AVAILABLE : TradeBtnStatus.DISABLED const withdrawI18nKey = checkResult.label const isFastWithdrawAmountLimit=checkResult.isFastWithdrawAmountLimit const walletLayer2Callback = () => { const walletMap = makeWalletLayer2({ needFilterZero: true, _isToL1: true }).walletMap ?? ({} as WalletMap) setWalletMap2( WITHDRAW_TOKEN_FILTER_LIST.reduce((pre, cur) => { return omit(pre, cur) as WalletMap }, walletMap), ) } const resetDefault = React.useCallback(() => { if (info?.isRetry) { return } if (contactsErrorMessage) { updateContacts() } if (symbol) { if (walletMap2) { updateWithdrawData({ belong: symbol as any, balance: walletMap2[symbol]?.count, tradeValue: undefined, address: info?.isToMyself ? account.accAddress : '*', withdrawType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, }) } } else { if (!withdrawValue.belong && walletMap2) { const keys = Reflect.ownKeys(walletMap2) let objInit = { belong: 'LRC', tradeValue: undefined, balance: 0, address: info?.isToMyself ? account.accAddress : '*', withdrawType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL as WithdrawType, } for (let key in keys) { const keyVal = keys[key] const walletInfo = walletMap2[keyVal] if (sdk.toBig(walletInfo.count).gt(0)) { objInit = { belong: keyVal as any, tradeValue: undefined, balance: walletInfo?.count, address: info?.isToMyself ? account.accAddress : '*', withdrawType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, } break } } updateWithdrawData(objInit as any) } else if (withdrawValue.belong && walletMap2) { const walletInfo = walletMap2[withdrawValue.belong] updateWithdrawData({ belong: withdrawValue.belong, tradeValue: undefined, balance: walletInfo?.count, address: info?.isToMyself ? account.accAddress : '*', withdrawType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, }) } else { updateWithdrawData({ belong: withdrawValue.belong, tradeValue: undefined, balance: undefined, address: info?.isToMyself ? account.accAddress : '*', withdrawType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, }) } } if (info?.isToMyself) { setAddress(account.accAddress) } else if (contactAddress) { setAddress(contactAddress) } else { setAddress('') } }, [ account.accAddress, setAddress, info?.isToMyself, symbol, walletMap2, updateWithdrawData, feeInfo, withdrawValue.belong, info?.isRetry, contactAddress, ]) const refreshFee = async () => { setState((state) => ({ ...state, fee: { ...state.fee, isOnLoading: true, }, })) try { const globalState = store.getState() const account = globalState.account const network = MapChainId[globalState.settings.defaultNetwork] const symbol = globalState._router_modalData.withdrawValue.belong as string const withdrawValue = globalState._router_modalData.withdrawValue.tradeValue ? globalState._router_modalData.withdrawValue.tradeValue.toString() : '0' const withdrawToken = symbol ? tokenMap[symbol] : undefined const feeResNormal = await LoopringAPI.userAPI?.getOffchainFeeAmt( { accountId: account.accountId, requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, tokenSymbol: symbol, amount: ethers.utils.parseUnits(withdrawValue ? withdrawValue : '0', withdrawToken?.decimals).toString(), }, account.apiKey, ) const feeResFast = await LoopringAPI.rabbitWithdrawAPI?.getUserCrossChainFee( { receiveFeeNetwork: network, requestType: sdk.OffchainFeeReqType.RABBIT_OFFCHAIN_WITHDRAWAL, calFeeNetwork: network, tokenSymbol: symbol, amount: ethers.utils.parseUnits(withdrawValue ? withdrawValue : '0', withdrawToken?.decimals).toString(), }, account.apiKey, ) .catch(e => { return {fees: []} }) withdrawToken && LoopringAPI.rabbitWithdrawAPI?.getNetworkWithdrawalAgents({ tokenId: withdrawToken.tokenId, network: network, amount: '0' }).then(res => { const amounts = res.map(agent => { return ethers.BigNumber.from(agent.totalAmount).sub(agent.freezeAmount).toString() }) const sorted = amounts.concat('0').sort((a, b) => ethers.BigNumber.from(b).gte(a) ? 1 : -1) const amount = sorted[0] ? sorted[0] : '0' setState((state) => ({ ...state, withdrawMode: { ...state.withdrawMode, maxFastWithdrawAmountBN: amount, } })) }) const chargeFeeTokenListNormal = feeResNormal?.fees ? values(feeResNormal.fees) : [] const chargeFeeTokenListFast = feeResFast?.fees ?? [] setState((state) => ({ ...state, fee: { ...state.fee, chargeFeeTokenListNormal, chargeFeeTokenListFast, } })) } finally { setState((state) => ({ ...state, fee: { ...state.fee, isOnLoading: false, }, })) } } const debouncedRefresshFee = useDebouncedCallback(refreshFee, 100) const onChangeWithdrawMode = (mode: 'fast' | 'normal') => { setState((state) => ({ ...state, withdrawMode: { ...state.withdrawMode, mode, }, })) refreshFee() } React.useEffect(() => { const account = store.getState().account var refreshTimer: NodeJS.Timeout | undefined = undefined if ( isShow && accountStatus === SagaStatus.UNSET && account.readyState === AccountStatus.ACTIVATED ) { resetDefault() refreshTimer = setInterval(() => { refreshFee() }, 20 * 1000) refreshFee() } if (isShow) { setState(initState) } return () => { setAddress('') refreshTimer && clearInterval(refreshTimer) } }, [isShow, accountStatus]) useWalletLayer2Socket({ walletLayer2Callback }) const processRequest = React.useCallback( async (request: sdk.OffChainWithdrawalRequestV3, isNotHardwareWallet: boolean) => { const { apiKey, connectName, eddsaKey } = account const withdrawValue = store.getState()._router_modalData.withdrawValue try { if ( connectProvides.usedWeb3 && LoopringAPI.userAPI && isAccActivated() && feeInfo2?.belong ) { let isHWAddr = checkHWAddr(account.accAddress) if (!isHWAddr && !isNotHardwareWallet) { isHWAddr = true } myLog('withdraw processRequest:', isHWAddr, isNotHardwareWallet) const feeToken = tokenMap[feeInfo2.belong] const feeRaw = feeInfo2.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const response = await LoopringAPI.userAPI.submitOffchainWithdraw( { request: { ...request, maxFee: { tokenId: feeToken?.tokenId ?? request.maxFee.tokenId, volume: fee?.toString() ?? request.maxFee.volume, // TEST: fee.toString(), }, }, web3: connectProvides.usedWeb3 as unknown as Web3, chainId: chainId === 'unknown' ? 1 : chainId, walletType: (ConnectProviders[connectName] ?? connectName) as unknown as sdk.ConnectorNames, eddsaKey: eddsaKey.sk, apiKey, isHWAddr, }, { accountId: account.accountId, counterFactualInfo: eddsaKey.counterFactualInfo, }, ) myLog('submitOffchainWithdraw:', response) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } info?.onCloseCallBack && info?.onCloseCallBack() setShowWithdraw({ isShow: false, info }) setShowAccount({ isShow: true, step: AccountStep.Withdraw_In_Progress, }) let hash = Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-withdraw` setShowAccount({ isShow: true, step: AccountStep.Withdraw_Success, info: { symbol: withdrawValue.belong, hash, isToMyself: info?.isToMyself, }, }) if (isHWAddr) { myLog('......try to set isHWAddr', isHWAddr) updateHW({ wallet: account.accAddress, isHWAddr }) } resetWithdrawData() walletLayer2Service.sendUserUpdate() await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step == AccountStep.Withdraw_Success ) { setShowAccount({ isShow: false }) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, isNotHardwareWallet) myLog('checkErrorInfo', code, e) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.Withdraw_First_Method_Denied, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setLastRequest({ request }) setShowAccount({ isShow: true, step: AccountStep.Withdraw_User_Denied, }) break default: setShowAccount({ isShow: true, step: AccountStep.Withdraw_Failed, info: { symbol: withdrawValue.belong, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } } }, [ account, withdrawValue, checkHWAddr, chainId, setShowAccount, resetWithdrawData, updateHW, ], ) const { walletProvider } = useAppKitProvider('eip155') const handleWithdraw = React.useCallback( async (inputValue: any, address, isFirstTime: boolean = true) => { const { accountId, accAddress, readyState, apiKey, eddsaKey } = account if ( readyState === AccountStatus.ACTIVATED && tokenMap && exchangeInfo && connectProvides.usedWeb3 && address && LoopringAPI.userAPI && feeInfo2?.belong && feeInfo2?.feeRaw && eddsaKey?.sk && (info?.isToMyself || sureIsAllowAddress) ) { try { setShowAccount({ isShow: true, step: AccountStep.Withdraw_WaitForAuth, }) const withdrawToken = tokenMap[withdrawValue.belong as string] const feeToken = tokenMap[feeInfo2.belong] // const fee = sdk.toBig(withdrawValue.fee?.feeRaw ?? 0); const feeRaw = feeInfo2.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const balance = sdk.toBig(inputValue.balance ?? 0).times('1e' + withdrawToken.decimals) const tradeValue = sdk .toBig(inputValue.tradeValue ?? 0) .times('1e' + withdrawToken.decimals) const isExceedBalance = feeToken.tokenId === withdrawToken.tokenId && tradeValue.plus(fee).gt(balance) const finalVol = isExceedBalance ? balance.minus(fee) : tradeValue const withdrawVol = finalVol.toFixed(0, 0) const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accountId, sellTokenId: withdrawToken.tokenId, }, apiKey, ) const request: sdk.OffChainWithdrawalRequestV3 = { exchange: exchangeInfo.exchangeAddress, owner: accAddress, to: address, accountId: account.accountId, storageId: storageId?.offchainId, token: { tokenId: withdrawToken.tokenId, volume: withdrawVol, }, maxFee: { tokenId: feeToken.tokenId, volume: fee.toString(), // TEST: fee.toString(), }, fastWithdrawalMode: withdrawValue.withdrawType == sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL, // WithdrawType.Fast, extraData: '', minGas: 0, validUntil: getTimestampDaysLater(DAYS), } myLog('submitOffchainWithdraw:', request) processRequest(request, isFirstTime) } catch (e: any) { sdk.dumpError400(e) setShowAccount({ isShow: true, step: AccountStep.Withdraw_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, }, }) } return true } else { return false } }, [ account, tokenMap, exchangeInfo, feeInfo2?.belong, feeInfo2?.feeRaw, info, sureIsAllowAddress, setShowAccount, processRequest, ], ) const handleRabbitWithdraw = async (inputValue: any, toAddress: string) => { const { readyState, eddsaKey } = account const ok = readyState === AccountStatus.ACTIVATED && tokenMap && exchangeInfo && connectProvides.usedWeb3 && LoopringAPI.userAPI && feeInfo2?.belong && feeInfo2?.feeRaw && eddsaKey?.sk && (info?.isToMyself || sureIsAllowAddress) && toAddress if (!ok) return false try { setShowAccount({ isShow: true, step: AccountStep.Withdraw_WaitForAuth, }) const withdrawToken = tokenMap[withdrawValue.belong as string] const feeToken = tokenMap[feeInfo2.belong] const feeRaw = feeInfo2!.feeRaw ?? 0 const fee = sdk.toBig(feeRaw) const balance = sdk.toBig(inputValue.balance ?? 0).times('1e' + withdrawToken.decimals) const tradeValue = sdk.toBig(inputValue.tradeValue ?? 0).times('1e' + withdrawToken.decimals) const isExceedBalance = feeToken.tokenId === withdrawToken.tokenId && tradeValue.plus(fee).gt(balance) const finalVol = isExceedBalance ? balance.minus(fee) : tradeValue const withdrawVol = finalVol.toFixed(0, 0) const network = MapChainId[chainId] const configiJSON = fastWithdrawConfig! const storageId = await LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: withdrawToken.tokenId, }, account.apiKey, ) const agentId = configiJSON.networkL2AgentAccountIds[network] const agentAddr = configiJSON.networkL2AgentAddresses[network] const exchange = configiJSON.networkExchanges[network] const request: sdk.RabbitWithdrawRequest = { fromNetwork: network, toNetwork: network, toAddress: toAddress, transfer: { exchange: exchange, payerId: account.accountId, payerAddr: account.accAddress, payeeId: agentId, payeeAddr: agentAddr, token: { tokenId: withdrawToken.tokenId, volume: withdrawVol, }, maxFee: { // @ts-ignore tokenId: feeToken.tokenId, volume: feeRaw }, storageId: storageId!.offchainId, validUntil: getTimestampDaysLater(DAYS), counterFactualInfo: eddsaKey.counterFactualInfo }, } const provider = new ethers.providers.Web3Provider(walletProvider as any) const response = await LoopringAPI.rabbitWithdrawAPI?.submitRabitWithdraw(request, { exchangeAddr: exchange, signer: provider.getSigner(), eddsaSignKey: account.eddsaKey.sk, chainId: chainId as number, }) if ((response as any).resultInfo && (response as any).resultInfo.code > 0) { throw (response as any).resultInfo } else if (!response || response.status === 'failed') { throw new Error('withdraw failed') } else { info?.onCloseCallBack && info?.onCloseCallBack() setShowWithdraw({ isShow: false, contactName: info?.contactName, }) setShowAccount({ isShow: true, step: AccountStep.Withdraw_In_Progress, }) let hash = Explorer + `tx/${(response as sdk.TX_HASH_API)?.hash}-withdraw` // todo setShowAccount({ isShow: true, step: AccountStep.Withdraw_Success, info: { symbol: withdrawValue.belong, hash, isToMyself: info?.isToMyself, }, }) resetWithdrawData() walletLayer2Service.sendUserUpdate() await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) if ( store.getState().modals.isShowAccount.isShow && store.getState().modals.isShowAccount.step === AccountStep.Withdraw_Success ) { setShowAccount({ isShow: false }) } } } catch (e: any) { const code = sdk.checkErrorInfo(e, true) myLog('checkErrorInfo', code, e) switch (code) { case sdk.ConnectorError.NOT_SUPPORT_ERROR: setShowAccount({ isShow: true, step: AccountStep.Withdraw_First_Method_Denied, }) break case sdk.ConnectorError.USER_DENIED: case sdk.ConnectorError.USER_DENIED_2: setShowAccount({ isShow: true, step: AccountStep.Withdraw_User_Denied, }) break default: setShowAccount({ isShow: true, step: AccountStep.Withdraw_Failed, info: { symbol: withdrawValue.belong, }, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, ...(e instanceof Error ? { message: e?.message, stack: e?.stack, } : e ?? {}), }, }) break } sdk.dumpError400(e) setShowAccount({ isShow: true, step: AccountStep.Withdraw_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: e?.message, }, }) } } const retryBtn = React.useCallback( (isHardwareRetry: boolean = false) => { setShowAccount({ isShow: true, step: AccountStep.NFTWithdraw_WaitForAuth, }) if (isFastMode) { return } else { processRequest(lastRequest, !isHardwareRetry) } }, [lastRequest, processRequest, setShowAccount, isFastMode], ) React.useEffect(() => { const { contacts } = store.getState().contacts const contact = contacts?.find( (x) => x.contactAddress?.toLowerCase() === realAddr?.toLowerCase(), ) if (contact?.addressType !== undefined) { const found = contact.addressType ? addressToExWalletMapFn(contact.addressType) : undefined setSureIsAllowAddress(found) } else { setSureIsAllowAddress(undefined) } if ( isShow && contactStatus == SagaStatus.UNSET && contact && realAddr?.toLowerCase() == contact?.contactAddress?.toLowerCase() ) { reCheck() } }, [realAddr, isShow, contactStatus]) const isTaiko = [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const withdrawProps: WithdrawProps = { type: TRADE_TYPE.TOKEN, isLoopringAddress, isAddressCheckLoading, isCFAddress, isToMyself: info?.isToMyself, isContractAddress, withdrawI18nKey, accAddr: account.accAddress, isNotAvailableAddress, addressDefault: address, realAddr, disableWithdrawList, tradeData: withdrawValue as any, coinMap: totalCoinMap as CoinMap, walletMap: walletMap2 as WalletMap, withdrawBtnStatus: btnStatus, withdrawType: withdrawValue.withdrawType as any, isFastWithdrawAmountLimit, sureIsAllowAddress, lastFailed: store.getState().modals.isShowAccount.info?.lastFailed === LAST_STEP.withdraw, handleSureIsAllowAddress: (value: WALLET_TYPE | EXCHANGE_TYPE) => { const found = exWalletToAddressMapFn(value) // const found = map.find(x => x[0] === value)![1] const contact = contacts?.find((x) => x.contactAddress === realAddr) if (!account?.isContractAddress && contact) { LoopringAPI.contactAPI ?.updateContact( { ...contact, isHebao: !!(account.isContractAddress || account.isCFAddress), accountId: account.accountId, addressType: found, }, account.apiKey, ) .then(() => { updateContacts() }) } setSureIsAllowAddress(value) }, onWithdrawClick: () => { if (withdrawValue && withdrawValue.belong) { return isFastMode ? handleRabbitWithdraw(withdrawValue, realAddr ? realAddr : address) : handleWithdraw(withdrawValue, realAddr ? realAddr : address) } }, handleWithdrawTypeChange: (value) => { // setWithdrawType(value); const _withdrawValue = store.getState()._router_modalData.withdrawValue updateWithdrawData({ ..._withdrawValue, withdrawType: value as any, }) }, handlePanelEvent: async (data: SwitchData, _switchType: 'Tomenu' | 'Tobutton') => { if (data.to === 'button') { if (walletMap2 && data?.tradeData?.belong) { setState({ ...state, withdrawMode: { ...state.withdrawMode, maxFastWithdrawAmountBN: undefined, } }) debouncedRefresshFee() const walletInfo = walletMap2[data?.tradeData?.belong as string] updateWithdrawData({ ...withdrawValue, belong: data.tradeData?.belong, tradeValue: data.tradeData?.tradeValue, balance: walletInfo?.count, address: '*', }) } else { updateWithdrawData({ withdrawType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, belong: undefined, tradeValue: undefined, balance: undefined, address: '*', }) } } }, handleFeeChange, feeInfo: feeInfo2, addrStatus, chargeFeeTokenList: chargeFeeTokenList.map(feeInfo => { return feeInfo && walletMap2 && tokenMap ? offchainFeeInfoToFeeInfo(feeInfo, tokenMap, walletMap2 as any) : undefined }).filter(feeInfo => feeInfo !== undefined), isFeeNotEnough, handleOnAddressChange: (value: any) => { setAddress(value) }, isFromContact: contactAddress ? true : false, contact: contactAddress ? contacts?.find((x) => x.contactAddress === contactAddress) : undefined, loopringSmartWalletVersion, contacts, isENSWrong, geUpdateContact: () => { if (isENSWrong) { const contact = contacts?.find((x) => x.contactAddress === realAddr) setShowEditContact({ isShow: true, info: { ...contact, isENSWrong, }, }) } }, ens, withdrawMode: (() => { const state = getState() const feeSymbol = state.fee.symbol ?? 'ETH' const feeFast = state.fee.chargeFeeTokenListFast.find((item) => item.token === feeSymbol) const feeNormal = state.fee.chargeFeeTokenListNormal.find((item) => item.token === feeSymbol) const feeTokenInfo = tokenMap[feeSymbol] const feeFastInCurrency = tryFn( () => { return feeFast && fiatNumberDisplaySafe( getValueInCurrency( new Decimal(ethers.utils.formatUnits(feeFast.fee, feeTokenInfo.decimals)) .mul(tokenPrices[feeSymbol]) .toFixed(2), ), currency, ) }, () => undefined ) const feeNormalInCurrency = tryFn( () => fiatNumberDisplaySafe( getValueInCurrency( new Decimal(ethers.utils.formatUnits(feeNormal!.fee, feeTokenInfo.decimals)) .mul(tokenPrices[feeSymbol]) .toFixed(2), ), currency, ), () => undefined, ) return { mode: isFastMode ? 'fast' : 'normal', showTrustUI: !isTaiko, showFastMode: fastModeSupportted, fastMode: { fee: feeFastInCurrency ? '~' + feeFastInCurrency : '--', time: '~3 minutes', }, fastMaxAlert: { show: fastWithdrawOverflow === true, message: state.withdrawMode.maxFastWithdrawAmountBN && withdrawToken ? `Max ${numberFormat( ethers.utils.formatUnits( state.withdrawMode.maxFastWithdrawAmountBN, withdrawToken.decimals, ), { fixed: withdrawToken.precision, removeTrailingZero: true }, )} ${withdrawToken?.symbol}` : '--', }, normalMode: { fee: feeNormalInCurrency ? '~' + feeNormalInCurrency : '--', time: '~25 minutes', }, onChange: onChangeWithdrawMode, fastModeSupportedTokens: parsed?.toL1SupportedTokens ? parsed.toL1SupportedTokens : [] } })(), } myLog('withdrawProps', getState(), withdrawValue, withdrawProps) return { withdrawProps, retryBtn, } } ================================================ FILE: packages/core/src/hooks/useractions/vault/index.ts ================================================ export * from './useVaultJoin' export * from './useAccountInfo' export * from './useVaultRedeem' export * from './useVaultLoan' export * from './useVaultRepay' export * from './useVaultBorrow' ================================================ FILE: packages/core/src/hooks/useractions/vault/useAccountInfo.ts ================================================ import { store, useAccount, useSubmitBtn, useTokenMap, useVaultLayer2, useVaultMap, VaultLayer2States, } from '@loopring-web/core' import React from 'react' import { L1L2_NAME_DEFINED, MapChainId, RouterPath, SagaStatus, TradeBtnStatus, VaultKey, VaultLoanType, } from '@loopring-web/common-resources' import { useOpenModals, useSettings } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import * as sdk from '@loopring-web/loopring-sdk' import { useConfirmation } from '../../../stores/localStore/confirmation' import { useHistory } from 'react-router' export type VaultAccountInfoStatus = VaultLayer2States & { joinBtnStatus: TradeBtnStatus joinBtnLabel: string onJoinPop: (props: any) => void swapBtnStatus: TradeBtnStatus swapBtnLabel: string onSwapPop: (props: any & { symbol: string, isSell?: boolean }) => void redeemBtnStatus: TradeBtnStatus onRedeemPop: (props: any) => void redeemBtnLabel: string borrowBtnStatus: TradeBtnStatus onBorrowPop: (props: any) => void borrowBtnLabel: string repayBtnStatus: TradeBtnStatus onRepayPop: (props: any) => void repayBtnLabel: string vaultAccountInfoStatus: SagaStatus onGoToSwap: ({ symbol, isSell }: { symbol?: string; isSell?: boolean }) => void // isShowFeathure: vaultAccountInfo?.accountStatus } export const useVaultAccountInfo = () => { const { vaultAccountInfo, status: vaultAccountInfoStatus, updateVaultLayer2, tokenFactors, collateralTokens, maxLeverage, activeInfo, } = useVaultLayer2() const { erc20Map } = useVaultMap() const { idIndex } = useTokenMap() const { setShowVaultJoin, setShowVaultSwap, setShowVaultExit, setShowVaultLoan, setShowConfirmedVault, } = useOpenModals() const { t } = useTranslation() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { confirmation: { confirmedOpenVaultPosition }, } = useConfirmation() const availableJoinCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const { vaultAccountInfo } = store.getState().vaultLayer2 switch (vaultAccountInfo?.accountStatus) { // @ts-ignore case sdk.VaultAccountStatus.IN_REDEEM: //sdk.VaultAccountStatus.IN_REDEEM: return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultREDEEMPendingBtn|` } // @ts-ignore case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: `labelVaultAddBtn|` } case sdk.VaultAccountStatus.FREE: // sdk.VaultAccountStatus.FREE: default: if (activeInfo?.hash) { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: `labelVaultJoinBtn|` } } return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: `labelVaultJoinBtn|` } } }, [vaultAccountInfoStatus, activeInfo]) const { btnStatus: joinBtnStatus, onBtnClick: onJoinPop, btnLabel: joinBtnLabel, } = useSubmitBtn({ availableTradeCheck: availableJoinCheck, isLoading: false, submitCallback: async () => { const { vaultAccountInfo } = store.getState().vaultLayer2 switch (vaultAccountInfo?.accountStatus) { // @ts-ignore case sdk.VaultAccountStatus.IN_REDEEM: // sdk.VaultAccountStatus.IN_REDEEM: break // @ts-ignore case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: setShowVaultJoin({ isShow: true, info: { isActiveAccount: false } }) break // @ts-ignore case sdk.VaultAccountStatus.FREE: //sdk.VaultAccountStatus.FREE default: { if (!confirmedOpenVaultPosition) { setShowConfirmedVault({ isShow: true }) } else { history.push(`${RouterPath.vault}/${VaultKey.VAULT_DASHBOARD}`) setShowVaultJoin({ isShow: true, info: { isActiveAccount: true } }) setShowConfirmedVault({ isShow: false }) } } } }, }) const availableSwapCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const { vaultAccountInfo } = store.getState().vaultLayer2 if (vaultAccountInfoStatus === SagaStatus.UNSET && vaultAccountInfo) { // setIsLoading(false) switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: `labelVaultTradeBtn|` } default: return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultTradeBtn|` } } } else { // setIsLoading(true) return { tradeBtnStatus: TradeBtnStatus.LOADING, label: `abelVaultTradeBtn|` } } }, [vaultAccountInfoStatus]) const { btnStatus: swapBtnStatus, onBtnClick: onSwapPop, btnLabel: swapBtnLabel, } = useSubmitBtn({ availableTradeCheck: availableSwapCheck, isLoading: false, submitCallback: async ({ symbol, isSell }: { symbol?: string, isSell?: boolean }) => { updateVaultLayer2({}) const { vaultAccountInfo } = store.getState().vaultLayer2 switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: setShowVaultSwap({ isShow: true, symbol: symbol ?? 'ETH', isSell }) break } }, }) const availableRedeemCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const { vaultAccountInfo } = store.getState().vaultLayer2 if (vaultAccountInfoStatus === SagaStatus.UNSET && vaultAccountInfo) { // setIsLoading(false) switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: `labelVaultRedeemBtn|` } default: return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultRedeemBtn|` } } } else { // setIsLoading(true) return { tradeBtnStatus: TradeBtnStatus.LOADING, label: `abelVaultRedeemBtn|` } } }, [vaultAccountInfoStatus]) const { btnStatus: redeemBtnStatus, onBtnClick: onRedeemPop, btnLabel: redeemBtnLabel, } = useSubmitBtn({ availableTradeCheck: availableRedeemCheck, isLoading: false, submitCallback: async (key?: string) => { const { vaultAccountInfo } = store.getState().vaultLayer2 switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: setShowVaultExit({ isShow: true, symbol: key ?? '' }) break } }, }) const availableBorrowCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const { vaultAccountInfo } = store.getState().vaultLayer2 if (vaultAccountInfoStatus === SagaStatus.UNSET && vaultAccountInfo) { // setIsLoading(false) switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: `labelVaultBorrowBtn|` } default: return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultBorrowBtn|` } } } else { // setIsLoading(true) return { tradeBtnStatus: TradeBtnStatus.LOADING, label: `abelVaultBorrowBtn|` } } }, [vaultAccountInfoStatus]) const { btnStatus: borrowBtnStatus, onBtnClick: onBorrowPop, btnLabel: borrowBtnLabel, } = useSubmitBtn({ availableTradeCheck: availableBorrowCheck, isLoading: false, submitCallback: async ({symbol}: {isShow: boolean,symbol: string}) => { const { vaultAccountInfo } = store.getState().vaultLayer2 switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: setShowVaultLoan({ isShow: true, info: {symbol: symbol ?? ''}, type: VaultLoanType.Borrow }) break } }, }) const availableRepayCheck = React.useCallback((): { tradeBtnStatus: TradeBtnStatus label: string } => { const { vaultAccountInfo } = store.getState().vaultLayer2 if (vaultAccountInfoStatus === SagaStatus.UNSET && vaultAccountInfo) { // setIsLoading(false) switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: `labelVaultRepayBtn|` } default: return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultRepayBtn|` } } } else { // setIsLoading(true) return { tradeBtnStatus: TradeBtnStatus.LOADING, label: `abelVaultRepayBtn|` } } }, [vaultAccountInfoStatus]) const { btnStatus: repayBtnStatus, onBtnClick: onRepayPop, btnLabel: repayBtnLabel, } = useSubmitBtn({ availableTradeCheck: availableRepayCheck, isLoading: false, submitCallback: async (key?: string) => { const { vaultAccountInfo } = store.getState().vaultLayer2 switch (vaultAccountInfo?.accountStatus) { case sdk.VaultAccountStatus.IN_STAKING: //sdk.VaultAccountStatus.IN_STAKING: setShowVaultLoan({ isShow: true, symbol: key ?? '', type: VaultLoanType.Repay }) break } }, }) const history = useHistory() const onGoToSwap = ({ symbol, isSell }: { symbol?: string; isSell?: boolean }) => { const searchParams = new URLSearchParams() symbol !== undefined && searchParams.append('symbol', symbol) isSell !== undefined && searchParams.append('isSell', isSell ? 'true' : 'false') history.push(RouterPath.vault + '/' + VaultKey.VAULT_TRADE + `?${searchParams.toString()}`) } const label = React.useCallback((btnLabel) => { const key = btnLabel.split('|') return t(key[0], { arg: key[1], // symbol: tradeCalcProData.coinBase, layer2: L1L2_NAME_DEFINED[network].layer2, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, }) }, []) const { account } = useAccount() React.useEffect(() => { if (account.apiKey) updateVaultLayer2({}) }, [account.apiKey]) return { joinBtnStatus, joinBtnLabel: label(joinBtnLabel), onJoinPop, vaultAccountInfo, swapBtnStatus, swapBtnLabel: label(swapBtnLabel), onSwapPop, redeemBtnStatus, onRedeemPop, redeemBtnLabel: label(redeemBtnLabel), borrowBtnStatus, onBorrowPop, borrowBtnLabel: label(borrowBtnLabel), repayBtnStatus, onRepayPop, repayBtnLabel: label(repayBtnLabel), vaultAccountInfoStatus, tokenFactors, maxLeverage, collateralTokens, onGoToSwap, } as VaultAccountInfoStatus } ================================================ FILE: packages/core/src/hooks/useractions/vault/useVaultBorrow.ts ================================================ import { CustomErrorWithCode, EmptyValueTag, getValuePrecisionThousand, IBData, myLog, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, SUBMIT_PANEL_CHECK, TradeBtnStatus, UIERROR_CODE, VaultBorrowData, } from '@loopring-web/common-resources' import { AccountStep, SwitchData, useOpenModals, VaultBorrowProps, } from '@loopring-web/component-lib' import { useTranslation } from 'react-i18next' import { onchainHashInfo, store, useAccount, useSystem, useTokenMap, useTokenPrices, useTradeVault, useVaultLayer2, useVaultMap, } from '../../../stores' import React, { useRef } from 'react' import { makeVaultAvaiable2 } from '../../help' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI } from '../../../api_wrapper' import { useSubmitBtn, useUserWallets } from '../../common' import BigNumber from 'bignumber.js' import { l2CommonService } from '../../../services' import { keys } from 'lodash' import Decimal from 'decimal.js' import { calcMarinLevel, marginLevelType } from './utils' import { numberFormat } from '../../../utils' import { utils } from 'ethers' export type VaultBorrowTradeData = IBData & { erc20Symbol: string borrowed: string count: string } export const calcSupportBorrowData = ( tradeData: T, // Omit & { count: string }, ) => { const { invest: { vaultMap: { tokenMap: vaultTokenMap, coinMap: vaultCoinMap }, }, } = store.getState() let supportData: any = { maxBorrowAmount: undefined, maxBorrowStr: undefined, minBorrowAmount: undefined, minBorrowStr: undefined, maxBorrowVol: undefined, minBorrowVol: undefined, maxQuote: undefined, borrowVol: undefined, borrowAmt: undefined, totalQuote: undefined, } if (tradeData?.belong && vaultTokenMap) { const borrowToken = vaultTokenMap[tradeData.belong] const orderAmounts = borrowToken.orderAmounts const minBorrowVol = BigNumber.max( // orderAmounts.dust, //@ts-ignore borrowToken?.vaultTokenAmounts?.minLoanAmount, ) const minBorrowAmt = minBorrowVol.div('1e' + borrowToken.decimals) const totalQuote = sdk.toBig(orderAmounts.maximum ?? 0).div('1e' + borrowToken.decimals) const maxBorrowAmt = sdk .toBig(BigNumber.min(totalQuote, tradeData.count)) .toFixed(borrowToken?.vaultTokenAmounts?.qtyStepScale, BigNumber.ROUND_DOWN) const tradeValue = tradeData.tradeValue supportData = { minBorrowAmount: minBorrowAmt?.toString(), minBorrowStr: getValuePrecisionThousand( minBorrowAmt ?? 0, borrowToken.precision, borrowToken.precision, undefined, ), minBorrowVol: minBorrowVol.toString(), maxQuote: orderAmounts.maximum, borrowVol: sdk .toBig(tradeValue ?? 0) .times('1e' + borrowToken.decimals) .toString(), borrowAmtStr: getValuePrecisionThousand( tradeValue ?? 0, borrowToken.precision, borrowToken.precision, undefined, ), borrowedStr: getValuePrecisionThousand( tradeData.borrowed ?? 0, borrowToken.precision, borrowToken.precision, undefined, ), balance: maxBorrowAmt, borrowAmt: tradeValue ?? 0, totalQuote: totalQuote.toString(), coinInfoMap: vaultCoinMap, hourlyRateInPercent: sdk.toFixed(sdk.toNumber(borrowToken.interestRate) * 100, 6, false), yearlyRateInPercent: sdk.toFixed(sdk.toNumber(borrowToken.interestRate) * 100 * 24 * 365, 2, false) } } return { ...supportData, } } export const useVaultBorrow = < T extends VaultBorrowTradeData, V extends VaultBorrowData, I, >(): Partial> => { const { t } = useTranslation() const { modals: { isShowVaultLoan }, setShowAccount, setShowVaultLoan, } = useOpenModals() const [isLoading, setIsLoading] = React.useState(false) const { exchangeInfo, forexMap } = useSystem() const { tokenMap: vaultTokenMap, coinMap: vaultCoinMap, marketCoins, getVaultMap, tokenPrices: vaultTokenPrices } = useVaultMap() const [walletMap, setWalletMap] = React.useState(() => { const { vaultAvaiable2Map } = makeVaultAvaiable2({}) return vaultAvaiable2Map }) const { updateVaultBorrowHash } = onchainHashInfo.useOnChainInfo() // chainInfos[defaultNetwork].vaultBorrowHashes const { vaultAccountInfo, status: vaultAccountInfoStatus, updateVaultLayer2 } = useVaultLayer2() const { vaultBorrowData, updateVaultBorrow, resetVaultBorrow } = useTradeVault() const { account } = useAccount() // const [tradeData, setTradeData] = React.useState(undefined) const timerRef = useRef(null) const updateVaultBorrowDataRepeatly = async () => { if (timerRef.current) { clearInterval(timerRef.current) } const fn = async () => { getVaultMap() const vaultBorrowData = store.getState()._router_tradeVault.vaultBorrowData let symbol = vaultBorrowData.belong as string | undefined if (!symbol) { return } const maxBorrowable = await LoopringAPI.vaultAPI?.getMaxBorrowable( { accountId: account.accountId, symbol: symbol.slice(2), }, account.apiKey, '1', ) const vaultBorrowData2 = store.getState()._router_tradeVault.vaultBorrowData let symbol2 = vaultBorrowData2.belong as string | undefined if (symbol2 !== symbol) { return } const tokenPrice = vaultTokenPrices[symbol] const vToken = vaultTokenMap[symbol] const balance = numberFormat( new Decimal(maxBorrowable!.maxBorrowableOfUsdt).div(tokenPrice).toString(), { fixed: vToken.vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, }, ) const maxBorrowVol = utils.parseUnits(balance, vToken.decimals).toString() updateVaultBorrow({ ...vaultBorrowData2, balance: Number(balance), tradeData: { ...vaultBorrowData2.tradeData, balance: Number(balance) }, maxBorrowAmount: balance, maxBorrowStr: balance, maxBorrowVol: maxBorrowVol.toString(), }) } timerRef.current = setInterval(fn, 10 * 1000) fn() } const initData = async () => { let vaultBorrowData: any = {} let initSymbol = marketCoins[0] if (isShowVaultLoan.info?.symbol) { initSymbol = isShowVaultLoan.info?.symbol } let { vaultAvaiable2Map } = makeVaultAvaiable2({}) setWalletMap(vaultAvaiable2Map) vaultBorrowData = { ...vaultBorrowData, vaultAvaiable2Map, coinMap: vaultCoinMap, walletMap: vaultAvaiable2Map, } let walletInfo walletInfo = { belong: initSymbol, ...((vaultAvaiable2Map && vaultAvaiable2Map[initSymbol.toString()]) ?? {}), // balance: (vaultAvaiable2Map && vaultAvaiable2Map[initSymbol.toString()]?.count) ?? 0, tradeValue: undefined, erc20Symbol: initSymbol.slice(2) } const supportdata = calcSupportBorrowData(walletInfo) walletInfo = { ...walletInfo, borrowedAmt: walletInfo.borrowed, } vaultBorrowData = { ...vaultBorrowData, ...walletInfo, ...supportdata, walletMap: vaultAvaiable2Map, tradeData: walletInfo, } updateVaultBorrow({ ...walletInfo, ...vaultBorrowData, ...calcSupportBorrowData(walletInfo), }) } const onRefreshData = React.useCallback(() => { getVaultMap() }, []) React.useEffect(() => { if (isShowVaultLoan.isShow) { initData() } else { if (timerRef.current) { clearInterval(timerRef.current) } resetVaultBorrow() } // @ts-ignore }, [isShowVaultLoan.isShow]) React.useEffect(() => { isShowVaultLoan.isShow && updateVaultBorrowDataRepeatly() }, [isShowVaultLoan.isShow, vaultAccountInfo?.leverage, vaultBorrowData?.tradeData?.belong]) const handlePanelEvent = React.useCallback( (data: SwitchData) => { if (data.to === 'button') { let { vaultAvaiable2Map } = makeVaultAvaiable2({}) setWalletMap(vaultAvaiable2Map) if (vaultAvaiable2Map && data?.tradeData?.belong) { let walletInfo: any = vaultAvaiable2Map[data?.tradeData?.belong as string] walletInfo = { ...walletInfo, tradeValue: data?.tradeData?.belong !== store.getState()._router_tradeVault.vaultBorrowData.tradeData.belong ? undefined : data.tradeData?.tradeValue, borrowedAmt: walletInfo.borrowed, } const supportdata = calcSupportBorrowData(walletInfo) updateVaultBorrow({ ...store.getState()._router_tradeVault.vaultBorrowData, ...vaultBorrowData, ...walletInfo, ...supportdata, walletMap: vaultAvaiable2Map, tradeData: { ...store.getState()._router_tradeVault.vaultBorrowData.tradeData, ...walletInfo, }, }) } else { updateVaultBorrow({ belong: undefined, tradeValue: undefined, balance: undefined, }) } } }, [account, updateVaultBorrow], ) const availableTradeCheck = React.useCallback(() => { const vaultBorrowData = store.getState()._router_tradeVault.vaultBorrowData if ( !vaultBorrowData?.tradeValue || !vaultBorrowData?.belong || sdk.toBig(vaultBorrowData?.tradeValue ?? 0).lte(0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: '' } } else if (sdk.toBig(vaultBorrowData?.tradeValue ?? 0).lt(vaultBorrowData.minBorrowAmount)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultBorrowMini|${vaultBorrowData.minBorrowStr} ${vaultBorrowData.belong.slice(2)}`, } } else if (sdk.toBig(vaultBorrowData.tradeData.tradeValue ?? 0).gt(BigNumber.min(vaultBorrowData.totalQuote, vaultBorrowData.tradeData.balance))) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultBorrowNotEnough|${vaultBorrowData.belong}`, } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } } }, [ vaultAccountInfoStatus, vaultTokenMap, vaultBorrowData, vaultBorrowData.belong, vaultBorrowData.tradeValue, vaultBorrowData.balance, vaultBorrowData.maxBorrowAmount, vaultBorrowData.minBorrowAmount, vaultBorrowData.tradeData ]) const processRequest = async (request?: sdk.VaultBorrowRequest) => { const vaultBorrowData = store.getState()._router_tradeVault.vaultBorrowData const vaultToken = vaultTokenMap[vaultBorrowData.belong] const { account: { eddsaKey, apiKey, accountId, accAddress }, } = store.getState() const erc20Symbol = vaultBorrowData.erc20Symbol try { if ((LoopringAPI.vaultAPI && request) || (vaultBorrowData.request && accountId)) { let response = await LoopringAPI.vaultAPI.submitVaultBorrow({ request: request ?? vaultBorrowData.request, privateKey: eddsaKey?.sk, apiKey: apiKey, }, '1') if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } setShowVaultLoan({ isShow: false, }) setIsLoading(false) l2CommonService.sendUserUpdate() updateVaultBorrowHash((response as any).hash, accAddress) await sdk.sleep(SUBMIT_PANEL_CHECK) const response2 = await LoopringAPI?.vaultAPI.getVaultGetOperationByHash( { accountId: accountId?.toString(), hash: (response as any).hash, }, apiKey, '1' ) let status = '' if ( response2?.raw_data?.operation?.status == sdk.VaultOperationStatus.VAULT_STATUS_FAILED ) { updateVaultBorrowHash((response as any).hash, accAddress, 'failed') throw sdk.VaultOperationStatus.VAULT_STATUS_FAILED } else if ( [sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED].includes( response2?.raw_data?.operation?.status, ) ) { updateVaultBorrowHash((response as any).hash, accAddress, 'success') status = 'labelSuccessfully' } else { status = 'labelPending' } setShowAccount({ isShow: store.getState().modals.isShowAccount.isShow, step: status == 'labelSuccessfully' ? AccountStep.VaultBorrow_Success : AccountStep.VaultBorrow_In_Progress, info: { amount: sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED ? vaultBorrowData.borrowAmtStr : 0, sum: vaultBorrowData.borrowAmtStr, status: t(status), forexMap, symbol: erc20Symbol, vSymbol: vaultToken.symbol, time: response2?.raw_data?.order?.createdAt, }, }) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) l2CommonService.sendUserUpdate() if ( store.getState().modals.isShowAccount.isShow && [AccountStep.VaultBorrow_Success, AccountStep.VaultBorrow_In_Progress].includes( store.getState().modals.isShowAccount.step, ) ) { setShowAccount({ isShow: false }) } } else { throw new Error('api not ready') } } catch (e) { setIsLoading(false) const code = (e as any)?.message === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? UIERROR_CODE.ERROR_ORDER_FAILED : (e as sdk.RESULT_INFO)?.code ?? UIERROR_CODE.UNKNOWN const error = new CustomErrorWithCode({ code, message: (e as sdk.RESULT_INFO)?.message, ...SDK_ERROR_MAP_TO_UI[code], }) setShowAccount({ isShow: true, step: AccountStep.VaultBorrow_Failed, info: { amount: EmptyValueTag, sum: vaultBorrowData.borrowAmtStr, status: t('labelFailed'), forexMap, symbol: erc20Symbol, vSymbol: vaultBorrowData.belong, time: Date.now(), title: t('labelVaultBorrowTitle'), }, error, }) } } const submitCallback = async () => { const vaultBorrowData = store.getState()._router_tradeVault.vaultBorrowData const account = store.getState().account const erc20Symbol = vaultBorrowData.erc20Symbol setIsLoading(true) try { if ( vaultBorrowData && exchangeInfo && vaultBorrowData.belong && LoopringAPI.vaultAPI && LoopringAPI.userAPI && vaultAccountInfo && sdk.toBig(vaultBorrowData.borrowVol).gte(vaultBorrowData.minBorrowVol ?? 0) && sdk.toBig(vaultBorrowData.borrowVol).lte(vaultBorrowData.maxBorrowVol ?? 0) ) { setShowAccount({ isShow: true, step: AccountStep.VaultBorrow_In_Progress, info: { amount: EmptyValueTag, sum: vaultBorrowData.borrowAmtStr, status: t('labelPending'), forexMap, symbol: erc20Symbol, vSymbol: vaultBorrowData.belong, time: Date.now(), title: t('labelVaultBorrowTitle'), }, }) const vaultBorrowRequest: sdk.VaultBorrowRequest = { accountId: account.accountId, token: { tokenId: vaultTokenMap[vaultBorrowData.belong].vaultTokenId as unknown as number, volume: vaultBorrowData.borrowVol, }, timestamp: Date.now(), } updateVaultBorrow({ ...vaultBorrowData, request: vaultBorrowRequest, }) processRequest(vaultBorrowRequest) } } catch (e) { setIsLoading(false) setShowAccount({ isShow: true, step: AccountStep.VaultBorrow_Failed, info: { amount: EmptyValueTag, sum: vaultBorrowData.borrowAmtStr, status: t('labelFailed'), forexMap, symbol: erc20Symbol, vSymbol: vaultBorrowData.belong, time: Date.now(), title: t('labelVaultBorrowTitle'), }, error: { ...(e as any), }, }) } } const { btnStatus, onBtnClick, btnLabel, // btnStyle: tradeLimitBtnStyle, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback, }) const moreToBorrowInUSD = (vaultBorrowData.tradeData && vaultTokenPrices[vaultBorrowData.tradeData.belong as string]) ? new Decimal(vaultBorrowData.tradeData.tradeValue ?? '0') .mul(vaultTokenPrices[vaultBorrowData.tradeData.belong as string]) .toString() : undefined const nextMarginLevel = vaultAccountInfo && moreToBorrowInUSD ? calcMarinLevel( vaultAccountInfo.totalCollateralOfUsdt, vaultAccountInfo.totalDebtOfUsdt, vaultAccountInfo.totalBalanceOfUsdt, moreToBorrowInUSD, '0' ) : vaultAccountInfo?.marginLevel return { handlePanelEvent, vaultBorrowBtnStatus: btnStatus, vaultBorrowBtnI18nKey: btnLabel, onVaultBorrowClick: onBtnClick, walletMap: walletMap as unknown as any, coinMap: keys(walletMap ?? {}).reduce((prev, key) => { return { ...prev, [key]: { ...vaultCoinMap[key?.toString() ?? ''], erc20Symbol: vaultCoinMap[key?.toString() ?? '']?.simpleName.slice(2), belongAlice: vaultCoinMap[key?.toString() ?? '']?.simpleName.slice(2), }, } }, {}), tradeData: vaultBorrowData.tradeData as any, vaultBorrowData: vaultBorrowData as V, onRefreshData, tokenProps: { decimalsLimit: vaultTokenMap[vaultBorrowData?.tradeData?.belong]?.vaultTokenAmounts?.qtyStepScale, allowDecimals: vaultTokenMap[vaultBorrowData?.tradeData?.belong]?.vaultTokenAmounts ?.qtyStepScale ? true : false, }, marginLevelChange: vaultAccountInfo?.marginLevel ? nextMarginLevel && vaultBorrowData.tradeValue ? { from: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, to: { marginLevel: nextMarginLevel, type: marginLevelType(nextMarginLevel), }, } : { from: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, to: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, } : undefined, userLeverage: vaultAccountInfo?.leverage, hideLeverage: (vaultAccountInfo as any)?.accountType === 0, } } ================================================ FILE: packages/core/src/hooks/useractions/vault/useVaultJoin.ts ================================================ import { AccountStep, SwitchData, useOpenModals, VaultJoinProps } from '@loopring-web/component-lib' import { AccountStatus, CoinInfo, CoinMap, EmptyValueTag, getValuePrecisionThousand, IBData, SUBMIT_PANEL_AUTO_CLOSE, SUBMIT_PANEL_CHECK, TRADE_TYPE, TradeBtnStatus, WalletMap, myLog, mapSpecialTokenName, } from '@loopring-web/common-resources' import React from 'react' import { DAYS, getTimestampDaysLater, LoopringAPI, makeVaultLayer2, makeWalletLayer2, store, useAccount, useSubmitBtn, useSystem, useTokenMap, useTradeVault, useVaultLayer2, useVaultMap, useL2CommonSocket, l2CommonService, NETWORKEXTEND, useTokenPrices, numberFormat, useUserWallets, } from '@loopring-web/core' import * as sdk from '@loopring-web/loopring-sdk' import { ConnectProviders, ConnectProvidersSignMap, connectProvides, } from '@loopring-web/web3-provider' import { useTranslation } from 'react-i18next' import BigNumber from 'bignumber.js' import { VaultAccountStatus } from '@loopring-web/loopring-sdk' import { keys } from 'lodash' import { calcMarinLevel, marginLevelType } from './utils' import Decimal from 'decimal.js' import { utils } from 'ethers' import { useBasicTrade } from '@loopring-web/component-lib/src/components/tradePanel/components' const DATE_IN_TEN_YEARS = 2027988026 export const useVaultJoin = , I>() => { const { t } = useTranslation() const { tokenMap: vaultTokenMap, joinTokenMap, erc20Map, getVaultMap } = useVaultMap() const { tokenMap, coinMap, idIndex } = useTokenMap() const { status: vaultAccountInfoStatus, vaultAccountInfo, updateVaultLayer2 } = useVaultLayer2() const { updateUserWallets } = useUserWallets() const { exchangeInfo, chainId, baseURL } = useSystem() const { account } = useAccount() const { updateVaultJoin, vaultJoinData, resetVaultJoin } = useTradeVault() const [isLoading, setIsLoading] = React.useState(false) const [isAddOrRedeem, setIsAddOrRedeem] = React.useState<'Add' | 'Redeem'>('Add') const { tokenPrices } = useTokenPrices() const isActiveAccount = [sdk.VaultAccountStatus.FREE, sdk.VaultAccountStatus.UNDEFINED].includes( vaultAccountInfo?.accountStatus ?? ('' as any), ) || vaultAccountInfo == undefined || vaultAccountInfo?.accountStatus == undefined const calcSupportData = (tradeData: T) => { let supportData = {} if (tradeData?.belong && walletAllowMap && walletAllowMap[tradeData.belong as any]) { const vaultTokenSymbol = walletAllowMap[tradeData.belong as any]?.vaultToken const vaultTokenInfo = vaultTokenMap[vaultTokenSymbol] const ercToken = tokenMap[tradeData.belong] const minAmount = vaultAccountInfo?.accountStatus === VaultAccountStatus.IN_STAKING ? sdk .toBig('10') .exponentiatedBy(-1 * vaultTokenInfo?.vaultTokenAmounts?.qtyStepScale) .toString() : sdk .toBig(vaultTokenInfo?.vaultTokenAmounts?.minAmount) .div('1e' + vaultTokenInfo.decimals) .toString() supportData = { maxShowVal: getValuePrecisionThousand( sdk //@ts-ignore .toBig(vaultTokenInfo?.vaultTokenAmounts?.maxAmount ?? 0) .div('1e' + ercToken.decimals), vaultTokenInfo?.vaultTokenAmounts?.qtyStepScale, vaultTokenInfo?.vaultTokenAmounts?.qtyStepScale, undefined, ), minShowVal: getValuePrecisionThousand( minAmount, vaultTokenInfo?.vaultTokenAmounts?.qtyStepScale, vaultTokenInfo?.vaultTokenAmounts?.qtyStepScale, undefined, ), maxAmount: vaultTokenInfo?.vaultTokenAmounts?.maxAmount, minAmount: sdk.toBig(minAmount).times('1e' + vaultTokenInfo.decimals), vaultSymbol: vaultTokenSymbol, vaultTokenInfo, } } return { ...supportData, } } const availableTradeCheck = React.useCallback(() => { const vaultAccountInfoSymbol = idIndex[vaultAccountInfo?.collateralInfo?.collateralTokenId ?? ''] ?? '' const vaultJoinData = store.getState()._router_tradeVault.vaultJoinData if (!vaultJoinData?.amount && sdk.toBig(vaultJoinData?.amount ?? 0).lte(0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: '' } } else if ( !isActiveAccount && vaultAccountInfoSymbol && vaultJoinData.belong !== vaultAccountInfoSymbol ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultJoinSymbolNotSame|${vaultJoinData.belong}`, } } else if (sdk.toBig(vaultJoinData.amount).lt(vaultJoinData.minAmount)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultJoinMini|${vaultJoinData.minShowVal} ${mapSpecialTokenName(vaultJoinData.belong as string)}`, } } else if (sdk.toBig(vaultJoinData.tradeValue ?? 0).gt(vaultJoinData.balance ?? 0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: isAddOrRedeem === 'Add' ? `labelVaultJoinNotEnough|${vaultJoinData.belong}` : `labelVaultRedeemNotEnough|${vaultJoinData.belong}`, } } else if (sdk.toBig(vaultJoinData.amount ?? 0).gt(vaultJoinData.maxAmount ?? 0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultJoinMax|${vaultJoinData.maxShowVal} ${vaultJoinData.belong}`, } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } } }, [ vaultAccountInfoStatus, tokenMap, vaultJoinData, vaultJoinData.tradeValue, vaultJoinData.balance, vaultJoinData.amount, vaultJoinData.maxAmount, vaultJoinData.minAmount, vaultJoinData.belong, isAddOrRedeem, ]) const processRequest = async (request?: sdk.VaultJoinRequest) => { const vaultJoinData = store.getState()._router_tradeVault.vaultJoinData ?? {} const ercToken = tokenMap[vaultJoinData?.belong?.toString() ?? ''] try { if (LoopringAPI.vaultAPI && (request || vaultJoinData.request) && ercToken) { let response = await LoopringAPI.vaultAPI.submitVaultJoin( { // @ts-ignore request: request ?? vaultJoinData.request, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, web3: connectProvides.usedWeb3 as any, chainId: chainId === 'unknown' ? 1 : chainId, walletType: (ConnectProvidersSignMap[account.connectName] ?? account.connectName) as unknown as sdk.ConnectorNames, }, '1', ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else { updateUserWallets() updateVaultLayer2( isActiveAccount ? { activeInfo: { hash: (response as any).hash, isInActive: true, }, } : {}, ) setShowVaultJoin({ isShow: false }) setIsLoading(false) await sdk.sleep(SUBMIT_PANEL_CHECK) const response2 = await LoopringAPI.vaultAPI.getVaultGetOperationByHash( { accountId: account?.accountId?.toString(), hash: (response as any).hash, }, account.apiKey, '1', ) let status = '' if ( response2?.raw_data?.operation?.status == sdk.VaultOperationStatus.VAULT_STATUS_FAILED ) { throw sdk.VaultOperationStatus.VAULT_STATUS_FAILED } else if ( [ sdk.VaultOperationStatus.VAULT_STATUS_EARNING, sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED, ].includes(response2?.raw_data?.operation?.status) ) { status = 'labelSuccessfully' } else { status = 'labelPending' } setShowAccount({ isShow: store.getState().modals.isShowAccount.isShow, step: status == 'labelSuccessfully' ? AccountStep.VaultJoin_Success : AccountStep.VaultJoin_In_Progress, info: { title: isActiveAccount ? t('labelVaultJoinTitle') : isAddOrRedeem === 'Add' ? t('labelVaultJoinAdd') : t('labelVaultRedeem'), type: isActiveAccount ? t('labelVaultJoin') : isAddOrRedeem === 'Redeem' ? t('labelVaultRedeem') : t('labelVaultMarginCall'), status: t(status), amount: sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED ? getValuePrecisionThousand( vaultJoinData.tradeValue, ercToken.precision, ercToken.precision, undefined, ) : 0, sum: getValuePrecisionThousand( vaultJoinData.tradeValue, ercToken.precision, ercToken.precision, undefined, ), symbol: ercToken.symbol, vSymbol: vaultJoinData.vaultSymbol, time: response2?.raw_data?.order?.createdAt, }, }) } await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) l2CommonService.sendUserUpdate() if ( store.getState().modals.isShowAccount.isShow && [AccountStep.VaultJoin_Success, AccountStep.VaultJoin_In_Progress].includes( store.getState().modals.isShowAccount.step, ) ) { setShowAccount({ isShow: false }) } } else { throw new Error('api not ready') } } catch (e) { let error setIsLoading(false) setShowAccount({ isShow: true, step: AccountStep.VaultJoin_Failed, info: { title: isActiveAccount ? t('labelVaultJoinTitle') : isAddOrRedeem === 'Add' ? t('labelVaultJoinAdd') : t('labelVaultRedeem'), type: isActiveAccount ? t('labelVaultJoin') : isAddOrRedeem === 'Redeem' ? t('labelVaultRedeem') : t('labelVaultMarginCall'), status: t('labelFailed'), percentage: '0', amount: EmptyValueTag, sum: getValuePrecisionThousand( vaultJoinData.tradeValue, ercToken.precision, ercToken.precision, undefined, ), symbol: ercToken.symbol, vSymbol: vaultJoinData.vaultSymbol, time: Date.now(), }, error, }) } } const submitCallback = async () => { const vaultJoinData = store.getState()._router_tradeVault.vaultJoinData const ercToken = tokenMap[vaultJoinData?.belong ?? ''] ?? {} try { if ( vaultJoinData && exchangeInfo && vaultJoinData.belong && LoopringAPI.vaultAPI && LoopringAPI.userAPI && LoopringAPI.defiAPI && vaultAccountInfo && sdk.toBig(vaultJoinData.amount).gte(vaultJoinData.minAmount ?? 0) && sdk.toBig(vaultJoinData.amount).lte(vaultJoinData.maxAmount ?? 0) ) { setIsLoading(true) const taikoFarmingPositionInfo = await LoopringAPI.defiAPI.getTaikoFarmingPositionInfo({accountId: account.accountId}); if ( [0, 3].includes(taikoFarmingPositionInfo.account.status) && vaultJoinData.tradeData.belong === 'LRTAIKO' ) { throw { message: 'Cannot use lrTAIKO as collateral under the current conditions.', } } setShowAccount({ isShow: true, step: AccountStep.VaultJoin_In_Progress, info: { title: isActiveAccount ? t('labelVaultJoinTitle') : isAddOrRedeem === 'Add' ? t('labelVaultJoinAdd') : t('labelVaultRedeem'), type: isActiveAccount ? t('labelVaultJoin') : isAddOrRedeem === 'Redeem' ? t('labelVaultRedeem') : t('labelVaultJoinAdd'), status: t('labelPending'), percentage: '0', amount: EmptyValueTag, sum: getValuePrecisionThousand( vaultJoinData.tradeValue, ercToken?.precision ?? 6, ercToken?.precision ?? 6, undefined, ), symbol: ercToken.symbol, vSymbol: vaultJoinData.vaultSymbol, time: Date.now(), }, }) let number = 3 // Step 1: check rest balance (loop three times for dust) while (number--) { if (isActiveAccount) { const response = await LoopringAPI.vaultAPI.getVaultBalance( { accountId: account.accountId, tokens: '', }, account.apiKey, '1', ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else if (response.raw_data?.length) { const tokenListIgnoreZero: any = [] const promiseAllStorageId = response?.raw_data?.reduce((prev, item) => { if (sdk.toBig(item?.total).gt(0)) { tokenListIgnoreZero.push(item) prev.push( //@ts-ignore LoopringAPI.userAPI.getNextStorageId( { accountId: account.accountId, sellTokenId: item.tokenId, }, account.apiKey, ), ) } return prev }, [] as Array>) ?? [] if (tokenListIgnoreZero.length === 0) { break } const { broker } = await LoopringAPI.userAPI?.getAvailableBroker({ type: 4, }) await Promise.all([...promiseAllStorageId]).then((result) => { return Promise.all( result.map((item, index) => { return ( item && LoopringAPI.vaultAPI?.sendVaultResetToken( { request: { exchange: exchangeInfo.exchangeAddress, payerAddr: account.accAddress, payerId: account.accountId, payeeId: 0, payeeAddr: broker, storageId: item.offchainId, token: { tokenId: tokenListIgnoreZero[index].tokenId, volume: tokenListIgnoreZero[index].total, }, maxFee: { tokenId: tokenListIgnoreZero[index].tokenId, volume: '0', }, validUntil: getTimestampDaysLater(DAYS), memo: '', } as any, web3: connectProvides.usedWeb3 as any, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[account.connectName] ?? account.connectName) as unknown as sdk.ConnectorNames, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, }, { accountId: account.accountId, counterFactualInfo: account.eddsaKey.counterFactualInfo, }, '1', ) ) }), ) }) } } } // Step 2: get a NFT const [avaiableNFT, storageId] = await Promise.all([ isActiveAccount ? LoopringAPI.vaultAPI .getVaultGetAvailableNFT( { accountId: account.accountId, }, account.apiKey, ) .then((avaiableNFT) => { if ( avaiableNFT && ((avaiableNFT as sdk.RESULT_INFO).code || (avaiableNFT as sdk.RESULT_INFO).message) ) { throw avaiableNFT } return { ...avaiableNFT.raw_data, } }) : Promise.resolve({ ...vaultAccountInfo.collateralInfo, tokenId: vaultAccountInfo?.collateralInfo?.nftTokenId, }), LoopringAPI.userAPI.getNextStorageId( { accountId: account.accountId, sellTokenId: tokenMap[vaultJoinData.belong].tokenId, }, account.apiKey, ), ]) if ( storageId && ((storageId as sdk.RESULT_INFO).code || (storageId as sdk.RESULT_INFO).message) ) { throw storageId } const amount = isActiveAccount ? sdk.toBig(vaultJoinData.amount) : isAddOrRedeem === 'Add' ? sdk .toBig(vaultAccountInfo?.collateralInfo?.collateralTokenAmount) .plus(vaultJoinData.amount) : sdk .toBig(vaultAccountInfo?.collateralInfo?.collateralTokenAmount) .minus(vaultJoinData.amount) const takerOrder: sdk.VaultJoinRequest = { exchange: exchangeInfo.exchangeAddress, accountId: account.accountId, storageId: storageId.orderId, sellToken: { tokenId: tokenMap[vaultJoinData.belong].tokenId, amount: amount.toString(), }, buyToken: { //@ts-ignore tokenId: avaiableNFT.tokenId, //@ts-ignore nftData: avaiableNFT.nftData, amount: '1', }, allOrNone: false, fillAmountBOrS: true, validUntil: DATE_IN_TEN_YEARS, maxFeeBips: 100, joinHash: isActiveAccount ? '' : (avaiableNFT as any)?.orderHash, } updateVaultJoin({ ...vaultJoinData, __request__: takerOrder, request: takerOrder, }) processRequest(takerOrder) } } catch (e) { setIsLoading(false) setShowAccount({ isShow: true, step: AccountStep.VaultJoin_Failed, info: { title: isActiveAccount ? t('labelVaultJoinTitle') : isAddOrRedeem === 'Add' ? t('labelVaultJoinAdd') : t('labelVaultRedeem'), type: isActiveAccount ? t('labelVaultJoin') : isAddOrRedeem === 'Redeem' ? t('labelVaultRedeem') : t('labelVaultMarginCall'), status: t('labelFailed'), percentage: '0', amount: EmptyValueTag, sum: EmptyValueTag, symbol: ercToken.symbol, vSymbol: vaultJoinData.vaultSymbol, time: Date.now(), }, error: { ...(e as any), }, }) } } const { btnStatus, onBtnClick, btnLabel, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback, }) const { modals: { isShowVaultJoin: { isShow, symbol }, }, setShowVaultJoin, setShowAccount, } = useOpenModals() const makeWalletLayer2ForVault = () => { let walletMap = makeWalletLayer2({ needFilterZero: true }).walletMap ?? {} Reflect.ownKeys(walletMap ?? {}).forEach((symbol) => { walletMap[symbol.toString()] = { ...walletMap[symbol.toString()], count: sdk .toBig((walletMap && walletMap[symbol.toString()]?.count) ?? 0) .toFixed(erc20Map[symbol]?.vaultTokenAmounts?.qtyStepScale, BigNumber.ROUND_DOWN), } as any }) return walletMap } const walletAllowMap = joinTokenMap && (keys(joinTokenMap) .filter((key) => { return joinTokenMap[key].vaultTokenAmounts.status & 2 }) .reduce((pre, cur) => { const value = joinTokenMap[cur] const symbolWithLV = cur.slice(2) return { ...pre, [symbolWithLV]: { vaultToken: cur, vaultId: value.vaultTokenId, name: symbolWithLV, simpleName: symbolWithLV, }, } }, {}) as { name: string; simpleName: string; vaultToken: string; vaultId: number }) const initData = () => { let vaultJoinData: any = {} let walletMap = makeWalletLayer2ForVault() let vaultMap = makeVaultLayer2({ needFilterZero: true }).vaultLayer2Map ?? {} vaultJoinData = { ...vaultJoinData, walletMap, vaultMap, coinMap: walletAllowMap, } let walletInfo, isActiveAccount = !vaultAccountInfo?.accountStatus || [VaultAccountStatus.FREE, VaultAccountStatus.UNDEFINED].includes( vaultAccountInfo?.accountStatus, ) const initSymbol = (() => { const availableCollaterals = Object.keys(walletAllowMap).filter(key => joinTokenMap[key]?.vaultTokenAmounts.status & 2 ) if (symbol && availableCollaterals.includes(`LV${symbol}`)) { return symbol } else if (availableCollaterals.length > 0) { return availableCollaterals[0] } if ( account && account.readyState === AccountStatus.ACTIVATED && !isActiveAccount && vaultAccountInfo?.collateralInfo?.collateralTokenId !== undefined ) { return idIndex[vaultAccountInfo?.collateralInfo.collateralTokenId] } else if (account.readyState === AccountStatus.ACTIVATED && !symbol) { const key = Reflect.ownKeys(joinTokenMap).find((keyVal) => { if (!(joinTokenMap[keyVal.toString()]?.vaultTokenAmounts.status & 2)) { return false; } const erc20Symbol = idIndex[joinTokenMap[keyVal.toString()]?.tokenId] const walletInfo = walletMap[erc20Symbol] ?? { count: 0 } return sdk.toBig(walletInfo?.count ?? 0).gt(0); }) if (key) { return idIndex[joinTokenMap[key.toString()]?.tokenId].toString() } } return 'ETH' })() const maxRedeemCollateral = vaultAccountInfo && (vaultAccountInfo as any).maxRedeemCollateral ? Decimal.max( numberFormat( utils.formatUnits( (vaultAccountInfo as any).maxRedeemCollateral as string, tokenMap[initSymbol].decimals, ), { fixed: vaultTokenMap['LV' + initSymbol].vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }, ), '0', ).toString() : undefined walletInfo = { belong: initSymbol, balance: isAddOrRedeem === 'Add' ? walletMap[initSymbol]?.count ?? 0 : maxRedeemCollateral, tradeValue: undefined, } updateVaultJoin({ ...walletInfo, ...vaultJoinData, ...calcSupportData(walletInfo), tradeData: walletInfo, }) } React.useEffect(() => { if (isAddOrRedeem === 'Redeem') { const symbol = vaultJoinData.belong as string const maxRedeemCollateral = vaultAccountInfo && (vaultAccountInfo as any).maxRedeemCollateral && tokenMap[symbol] ? Decimal.max( numberFormat( utils.formatUnits( (vaultAccountInfo as any).maxRedeemCollateral as string, tokenMap[symbol].decimals, ), { fixed: vaultTokenMap['LV' + symbol].vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }, ), '0', ).toString() : undefined updateVaultJoin({ ...vaultJoinData, tradeData: { ...vaultJoinData.tradeData, // @ts-ignore balance: maxRedeemCollateral, }, }) } }, [(vaultAccountInfo as any)?.maxRedeemCollateral, isAddOrRedeem]) const vaultLayer2Callback = React.useCallback(() => { const vaultJoinData = store.getState()._router_tradeVault.vaultJoinData let walletMap = makeWalletLayer2ForVault() updateVaultJoin({ ...vaultJoinData, walletMap, vaultLayer2Map: makeVaultLayer2({ needFilterZero: true }).vaultLayer2Map, }) }, []) const walletLayer2Callback = React.useCallback(() => { const vaultJoinData = store.getState()._router_tradeVault.vaultJoinData updateVaultJoin({ ...vaultJoinData, walletMap: makeWalletLayer2ForVault(), }) }, []) useL2CommonSocket({ walletLayer2Callback, vaultLayer2Callback }) React.useEffect(() => { if (isShow) { onRefreshData() initData() } else { resetVaultJoin() } }, [isShow, isAddOrRedeem]) const onRefreshData = React.useCallback(() => { getVaultMap() updateUserWallets() }, []) const refreshRef = React.createRef() const handlePanelEvent = async (data: SwitchData, _switchType: 'Tomenu' | 'Tobutton') => { const walletMap = makeWalletLayer2ForVault() const tokenSymbol = data.tradeData.belong const maxRedeemCollateral = vaultAccountInfo && (vaultAccountInfo as any).maxRedeemCollateral && tokenMap[tokenSymbol as string] ? Decimal.max( numberFormat( utils.formatUnits( (vaultAccountInfo as any).maxRedeemCollateral as string, tokenMap[tokenSymbol as string].decimals, ), { fixed: vaultTokenMap['LV' + (tokenSymbol as string)].vaultTokenAmounts.qtyStepScale, removeTrailingZero: true, fixedRound: Decimal.ROUND_FLOOR, }, ), '0', ).toString() : undefined let walletInfo: any = { ...walletMap[tokenSymbol], balance: isAddOrRedeem === 'Add' ? walletMap[tokenSymbol]?.count ?? 0 : maxRedeemCollateral, tradeValue: data.tradeData?.tradeValue, belong: tokenSymbol, } myLog('walletInfo', walletInfo) if (tokenSymbol) { updateVaultJoin({ ...vaultJoinData, amount: sdk .toBig(walletInfo.tradeValue ?? 0) .times('1e' + tokenMap[tokenSymbol as string].decimals) .toString(), tradeData: walletInfo, ...walletInfo, ...calcSupportData(walletInfo), }) } } const walletAllowCoin = React.useMemo(() => { const vaultTokenSymbol = idIndex[vaultAccountInfo?.collateralInfo?.collateralTokenId ?? ''] return { [vaultTokenSymbol]: coinMap[vaultTokenSymbol] } }, [vaultAccountInfo?.collateralInfo]) const moreToCollateralizeInUSD = vaultJoinData.tradeData && vaultJoinData.tradeData.tradeValue && new Decimal(vaultJoinData.tradeData.tradeValue).gt(0) && tokenPrices[vaultJoinData.tradeData.belong as string] ? new Decimal(vaultJoinData.tradeData.tradeValue ?? '0') .mul(tokenPrices[vaultJoinData.tradeData.belong as string]) .mul(isAddOrRedeem === 'Add' ? 1 : -1) .toString() : undefined const nextMarginLevel = vaultAccountInfo && moreToCollateralizeInUSD ? calcMarinLevel( vaultAccountInfo.totalCollateralOfUsdt, vaultAccountInfo.totalDebtOfUsdt, vaultAccountInfo.totalBalanceOfUsdt, '0', moreToCollateralizeInUSD, ) : undefined const ercToken = vaultJoinData.tradeData && tokenMap[vaultJoinData.tradeData.belong as string] const props = { handleError: undefined, type: TRADE_TYPE.TOKEN, baseURL, btnI18nKey: btnLabel, btnStatus, isActiveAccount, disabled: false, onSubmitClick: (_data: T) => onBtnClick(), propsExtends: {}, tradeData: vaultJoinData.tradeData as unknown as T, handlePanelEvent, onRefreshData, refreshRef, walletMap: vaultJoinData.walletMap as WalletMap, vaultJoinData, coinMap: isActiveAccount ? walletAllowMap : walletAllowCoin, tokenProps: { decimalsLimit: erc20Map && erc20Map[vaultJoinData?.tradeData?.belong as string]?.vaultTokenAmounts?.qtyStepScale, allowDecimals: erc20Map && erc20Map[vaultJoinData?.tradeData?.belong as string]?.vaultTokenAmounts?.qtyStepScale ? true : false, }, onToggleAddRedeem: (value: 'Add' | 'Redeem') => { setIsAddOrRedeem(value) }, isAddOrRedeem, marginLevelChange: vaultAccountInfo?.marginLevel ? nextMarginLevel ? { from: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, to: { marginLevel: nextMarginLevel, type: marginLevelType(nextMarginLevel), }, } : { from: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, to: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, } : undefined, holdingCollateral: vaultAccountInfo && vaultAccountInfo?.collateralInfo && ercToken ? numberFormat( utils.formatUnits( vaultAccountInfo?.collateralInfo.collateralTokenAmount, ercToken.decimals, ), { fixed: ercToken.precision, removeTrailingZero: true, }, ) : undefined, } const basicTrade = useBasicTrade(props as any) const [panelIndex, setPanelIndex] = React.useState(basicTrade.index) React.useEffect(() => { setPanelIndex(basicTrade.index) }, [basicTrade.index]) const output= { ...props, basicTrade, panelIndex, handleConfirm: (index: number) => { setPanelIndex(index) }, modalOpen: isShow, onCloseModal: () => { setShowVaultJoin({ isShow: false }) } } return output } ================================================ FILE: packages/core/src/hooks/useractions/vault/useVaultLoan.ts ================================================ import { useOpenModals } from '@loopring-web/component-lib' import { store } from '../../../stores' import React from 'react' import { VaultLoanType } from '@loopring-web/common-resources' import { useVaultRepay } from './useVaultRepay' import { useVaultBorrow } from './useVaultBorrow' import { useL2CommonSocket } from '../../../services' export const useVaultLoan = () => { const { modals: { isShowVaultLoan: { type, isShow, info }, }, } = useOpenModals() const [vaultLoanType, setVaultLoanType] = React.useState(type ?? VaultLoanType.Borrow) const handleTabChange = (index: VaultLoanType) => { setVaultLoanType(index) } React.useEffect(() => { if (isShow) { setVaultLoanType(() => { return store.getState().modals?.isShowVaultLoan?.type ?? VaultLoanType.Borrow }) // const withdrawValue = } }, [isShow]) useL2CommonSocket({}) return { vaultRepayProps: useVaultRepay(info?.symbol), vaultBorrowProps: useVaultBorrow(), vaultLoanType, handleTabChange, } } ================================================ FILE: packages/core/src/hooks/useractions/vault/useVaultRedeem.ts ================================================ import { AccountStep, useOpenModals, useSettings } from '@loopring-web/component-lib' import { CurrencyToTag, CustomErrorWithCode, EmptyValueTag, getValuePrecisionThousand, PriceTag, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, SUBMIT_PANEL_CHECK, TradeBtnStatus, UIERROR_CODE, } from '@loopring-web/common-resources' import React from 'react' import { l2CommonService, LoopringAPI, store, useL2CommonSocket, useSubmitBtn, useSystem, useVaultLayer2, } from '@loopring-web/core' import * as sdk from '@loopring-web/loopring-sdk' import { useTranslation } from 'react-i18next' export const useVaultRedeem = () => { const { t } = useTranslation('common') const { status: vaultAccountInfoStatus, vaultAccountInfo } = useVaultLayer2() const [isLoading, setIsLoading] = React.useState(false) const { setShowVaultExit, setShowAccount, setShowNoVaultAccount } = useOpenModals() const { forexMap } = useSystem() const { currency } = useSettings() const [info, setInfo] = React.useState< | { profit: any usdValue: any usdDebt: any usdEquity: any forexMap: any profitPercent: any } | undefined >(undefined) const vaultLayer2Callback = React.useCallback(() => { const { vaultLayer2: { vaultAccountInfo }, } = store.getState() if (vaultAccountInfo?.accountStatus == sdk.VaultAccountStatus.IN_STAKING) { setInfo(() => { const profit = (vaultAccountInfo as any)?.accountType === 0 ? vaultAccountInfo?.totalCollateralOfUsdt && vaultAccountInfo?.totalCollateralOfUsdt ? sdk .toBig(vaultAccountInfo?.totalEquityOfUsdt ?? 0) .minus(vaultAccountInfo?.totalCollateralOfUsdt ?? 0) : undefined : vaultAccountInfo?.totalCollateralOfUsdt && vaultAccountInfo?.totalCollateralOfUsdt ? sdk .toBig(vaultAccountInfo?.totalBalanceOfUsdt ?? 0) .minus(vaultAccountInfo?.totalDebtOfUsdt ?? 0) : undefined return { profit: profit ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(profit).times(forexMap[currency] ?? 0), 2, 2, 2, true, { floor: true }, ) : EmptyValueTag, profitPercent: profit && Number(vaultAccountInfo?.totalCollateralOfUsdt ?? 0) ? getValuePrecisionThousand( profit.div(vaultAccountInfo?.totalCollateralOfUsdt).times(100) ?? 0, 2, 2, undefined, false, { isFait: false, floor: true, }, ) + '%' : EmptyValueTag, usdValue: vaultAccountInfo?.totalBalanceOfUsdt ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(vaultAccountInfo?.totalBalanceOfUsdt ?? 0).times(forexMap[currency] ?? 0), 2, 2, 2, true, { floor: true }, ) : EmptyValueTag, usdDebt: vaultAccountInfo?.totalDebtOfUsdt ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(vaultAccountInfo?.totalDebtOfUsdt ?? 0).times(forexMap[currency] ?? 0), 2, 2, 2, true, { floor: true }, ) : EmptyValueTag, usdEquity: vaultAccountInfo?.totalEquityOfUsdt ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(vaultAccountInfo?.totalEquityOfUsdt ?? 0).times(forexMap[currency] ?? 0), 2, 2, 2, true, { floor: true }, ) : EmptyValueTag, forexMap, } }) } }, [currency]) React.useEffect(() => { vaultLayer2Callback() }, [currency]) useL2CommonSocket({ vaultLayer2Callback }) const availableTradeCheck = React.useCallback(() => { if ( vaultAccountInfo?.accountStatus == sdk.VaultAccountStatus.IN_STAKING && vaultAccountInfo?.collateralInfo?.orderHash ) { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: 'labelVaultConfirm' } } else { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: 'labelVaultConfirm' } } }, [vaultAccountInfoStatus, vaultAccountInfo?.collateralInfo?.orderHash]) const processRequest = async (request: sdk.VaultExitRequest) => { try { const { account: { apiKey, eddsaKey, accountId }, } = store.getState() if (request && LoopringAPI.vaultAPI) { let response = await LoopringAPI.vaultAPI.submitVaultExit({ // @ts-ignore request: request, privateKey: eddsaKey.sk, apiKey, }, '1') if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } // submit success setShowVaultExit({ isShow: false }) await sdk.sleep(SUBMIT_PANEL_CHECK) const response2 = await LoopringAPI.vaultAPI.getVaultGetOperationByHash( { accountId: accountId?.toString(), hash: (response as any).hash, }, apiKey, '1' ) let status = '' if ( response2?.raw_data?.operation?.status == sdk.VaultOperationStatus.VAULT_STATUS_FAILED ) { throw sdk.VaultOperationStatus.VAULT_STATUS_FAILED } else if ( [sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED].includes( response2?.raw_data?.operation?.status, ) ) { status = 'labelSuccessfully' } else { status = 'labelPending' } setShowAccount({ isShow: store.getState().modals.isShowAccount.isShow, step: status == 'labelSuccessfully' ? AccountStep.VaultRedeem_Success : AccountStep.VaultRedeem_In_Progress, info: { ...info, status: t(status), }, }) setIsLoading(false) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) l2CommonService.sendUserUpdate() if ( store.getState().modals.isShowAccount.isShow && [AccountStep.VaultRedeem_Success, AccountStep.VaultRedeem_In_Progress].includes( store.getState().modals.isShowAccount.step, ) ) { setShowAccount({ isShow: false }) } var timer = setInterval(() => { LoopringAPI.vaultAPI?.getVaultGetOperationByHash( { accountId: accountId?.toString(), hash: (response as any).hash, }, apiKey, '1' ).then(x => { if (x.operation.status === sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED) { setShowNoVaultAccount({ isShow: false }) clearInterval(timer) } }) }, 2000) } else { throw new Error('api not ready') } } catch (e) { const code = (e as any)?.message === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? UIERROR_CODE.ERROR_ORDER_FAILED : (e as sdk.RESULT_INFO)?.code ?? UIERROR_CODE.UNKNOWN const error = new CustomErrorWithCode({ code, message: (e as sdk.RESULT_INFO)?.message, ...SDK_ERROR_MAP_TO_UI[code], }) setShowAccount({ isShow: true, step: AccountStep.VaultRedeem_Failed, info: { ...info, status: t('labelFailed'), }, error, }) } setIsLoading(false) } const submitCallback = async () => { try { const { accountId } = store.getState().account if ( vaultAccountInfo?.accountStatus == sdk.VaultAccountStatus.IN_STAKING && vaultAccountInfo?.collateralInfo?.orderHash ) { setIsLoading(true) setShowAccount({ isShow: true, step: AccountStep.VaultRedeem_In_Progress, }) processRequest({ accountId, joinHash: vaultAccountInfo?.collateralInfo?.orderHash, timestamp: Date.now(), }) } else { throw 'accountStatus is not in staking' } } catch (e) { if (e as any) { setIsLoading(false) setShowAccount({ isShow: true, step: AccountStep.VaultRedeem_Failed, info: { ...info, status: t('labelFailed'), }, error: { ...(e as any), }, }) } } } const { btnStatus, onBtnClick, btnLabel, // btnStyle: tradeLimitBtnStyle, } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback, }) return { btnStatus, onClose: () => { setShowVaultExit({ isShow: false }) }, confirmLabel: btnLabel, onSubmitClick: () => onBtnClick(), } } ================================================ FILE: packages/core/src/hooks/useractions/vault/useVaultRepay.ts ================================================ import { CustomErrorWithCode, EmptyValueTag, getValuePrecisionThousand, IBData, SDK_ERROR_MAP_TO_UI, SUBMIT_PANEL_AUTO_CLOSE, SUBMIT_PANEL_CHECK, TradeBtnStatus, UIERROR_CODE, VaultRepayData, } from '@loopring-web/common-resources' import { AccountStep, SwitchData, useOpenModals } from '@loopring-web/component-lib' import { NETWORKEXTEND, store, useAccount, useSystem, useTokenMap, useTradeVault, useVaultLayer2, useVaultMap, } from '../../../stores' import { useTranslation } from 'react-i18next' import React from 'react' import * as sdk from '@loopring-web/loopring-sdk' import { makeVaultRepay } from '../../help' import { LoopringAPI } from '../../../api_wrapper' import { useSubmitBtn } from '../../common' import BigNumber from 'bignumber.js' import { getTimestampDaysLater } from '../../../utils' import { DAYS } from '../../../defs' import { ConnectProviders, connectProvides } from '@loopring-web/web3-provider' import { mapValues } from 'lodash' import Decimal from 'decimal.js' import { calcMarinLevel, marginLevelType } from './utils' export const useVaultRepay = < T extends IBData & { borrowed: string max: string }, V extends VaultRepayData, I, >(initialSymbol: string | undefined) => { const { modals: { isShowVaultLoan }, setShowAccount, setShowVaultLoan, } = useOpenModals() const { vaultAccountInfo, status: vaultAccountInfoStatus, updateVaultLayer2 } = useVaultLayer2() const { account } = useAccount() const { idIndex: erc20IdIndex } = useTokenMap() const { tokenMap: vaultTokenMap, idIndex: vaultIdIndex, coinMap: vaultCoinMap, tokenPrices } = useVaultMap() const { t } = useTranslation() const { vaultRepayData, updateVaultRepay, resetVaultRepay } = useTradeVault() const { exchangeInfo, chainId } = useSystem() const [isLoading, setIsLoading] = React.useState(false) const [walletMap, setWalletMap] = React.useState(() => { return makeVaultRepay({ needFilterZero: true }).vaultAvaiable2Map }) const [tradeData, setTradeData] = React.useState(undefined) const calcSupportData = (tradeData: T) => { let supportData = {} if (tradeData?.belong) { const vaultToken = vaultTokenMap[tradeData.belong as any] if(!vaultToken) { return {} } const borrowed = tradeData.borrowed let minRepayVol = BigNumber.max( // orderAmounts.dust, //@ts-ignore vaultToken?.vaultTokenAmounts?.minLoanAmount, ) let minRepayAmt = minRepayVol.div('1e' + vaultToken.decimals) const tradeValue = tradeData.tradeValue if (sdk.toBig(tradeData.borrowed).minus(tradeData.tradeValue).lt(minRepayAmt)) { minRepayVol = sdk .toBig(tradeData.borrowed) .times('1e' + vaultToken.decimals) .toString() minRepayAmt = sdk.toBig(tradeData.borrowed).toString() } supportData = { maxRepayAmount: borrowed, maxRepayStr: getValuePrecisionThousand( borrowed, // sdk.toBig(vaultToken.btradeAmount).div('1e' + vaultToken.decimals), vaultToken?.vaultTokenAmounts?.qtyStepScale, vaultToken?.vaultTokenAmounts?.qtyStepScale, undefined, ), minRepayAmount: minRepayAmt, minRepayStr: getValuePrecisionThousand( minRepayAmt, vaultToken?.vaultTokenAmounts?.qtyStepScale, vaultToken?.vaultTokenAmounts?.qtyStepScale, undefined, ), maxRepayVol: sdk .toBig(borrowed) .times('1e' + vaultToken.decimals) .toString(), minRepayVol: minRepayVol.toString(), repayVol: sdk .toBig(tradeValue ?? 0) .times('1e' + vaultToken.decimals) .toString(), repayAmtStr: getValuePrecisionThousand( tradeValue ?? 0, vaultToken?.vaultTokenAmounts?.qtyStepScale, vaultToken?.vaultTokenAmounts?.qtyStepScale, undefined, ), repayAmt: tradeValue ?? 0, coinInfoMap: vaultCoinMap, } } return { ...supportData, } } // useVaultSocket() const initData = () => { let vaultRepayData: any = {} let initSymbol = vaultIdIndex[vaultAccountInfo?.collateralInfo?.collateralTokenId ?? ''] ?? '' const walletMap = makeVaultRepay({ needFilterZero: true }).vaultAvaiable2Map ?? {} setWalletMap(walletMap) let walletInfo: any = walletMap[initSymbol.toString()] walletInfo = { ...walletInfo, belong: initSymbol.toString(), balance: walletInfo?.count ?? 0, tradeValue: undefined, max: BigNumber.min(walletInfo?.borrowed, walletInfo?.count) ?? 0, } vaultRepayData = { ...vaultRepayData, walletMap, coinMap: vaultCoinMap, } setTradeData({ ...walletInfo, }) updateVaultRepay({ ...walletInfo, ...vaultRepayData, ...calcSupportData(walletInfo), }) } React.useEffect(() => { if (isShowVaultLoan.isShow) { initData() } else { resetVaultRepay() } }, [isShowVaultLoan.isShow]) const availableTradeCheck = React.useCallback(() => { const vaultRepayData = store.getState()._router_tradeVault.vaultRepayData if ( !vaultRepayData?.tradeValue || !vaultRepayData?.belong || sdk.toBig(vaultRepayData?.tradeValue ?? 0).lte(0) ) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: '' } } else if (sdk.toBig(vaultRepayData?.tradeValue ?? 0).lt(vaultRepayData.minRepayAmount)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultRepayMini|${vaultRepayData.minRepayStr} ${vaultRepayData.belong.slice(2)}`, } } else if (sdk.toBig(vaultRepayData.tradeValue ?? 0).gt(vaultRepayData.balance ?? 0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultRepayNotEnough|${vaultRepayData.belong}`, } } else if (sdk.toBig(vaultRepayData.tradeValue ?? 0).gt(vaultRepayData.maxRepayAmount ?? 0)) { return { tradeBtnStatus: TradeBtnStatus.DISABLED, label: `labelVaultRepayMax|${vaultRepayData.maxRepayStr} ${vaultRepayData.belong.slice(2)}`, } } else { return { tradeBtnStatus: TradeBtnStatus.AVAILABLE, label: '' } } }, [ vaultAccountInfoStatus, vaultTokenMap, vaultRepayData, vaultRepayData.belong, vaultRepayData.tradeValue, vaultRepayData.balance, vaultRepayData.maxRepayAmount, vaultRepayData.minRepayAmount, ]) const handlePanelEvent = React.useCallback( async (data: SwitchData) => { let vaultRepayData = store.getState()._router_tradeVault.vaultRepayData return new Promise((res: any) => { if (data.to === 'button') { const walletMap = makeVaultRepay({ needFilterZero: true }).vaultAvaiable2Map ?? {} setWalletMap(walletMap) if (walletMap && data?.tradeData?.belong) { const vaultToken = vaultTokenMap[data.tradeData.belong] let walletInfo: any = walletMap[data?.tradeData?.belong as string] walletInfo = { ...walletInfo, balance: walletInfo ? walletInfo.count : 0, tradeValue: data.tradeData?.tradeValue, max: BigNumber.min( walletInfo.borrowed, sdk.toFixed(sdk.toBig(walletInfo.count), vaultToken.vaultTokenAmounts.qtyStepScale, false) ) } setTradeData(walletInfo) vaultRepayData = { ...vaultRepayData, ...walletInfo, ...calcSupportData(walletInfo), walletMap, tradeData: walletInfo, } updateVaultRepay({ ...vaultRepayData, }) } else { updateVaultRepay({ belong: undefined, tradeValue: undefined, balance: undefined, }) } } res() }) }, [tradeData, account.readyState], ) const processRequest = async (request?: sdk.VaultRepayRequestV3WithPatch['request']) => { const account = store.getState().account const vaultRepayData = store.getState()._router_tradeVault.vaultRepayData const vaultToken = vaultTokenMap[vaultRepayData.belong] const erc20Symbol = vaultRepayData.belong.slice(2) try { if ((request || vaultRepayData.request) && LoopringAPI.vaultAPI) { let response = await LoopringAPI.vaultAPI?.submitVaultRepay( { // @ts-ignore request: request ?? vaultRepayData.request, web3: connectProvides.usedWeb3 as any, chainId: chainId === NETWORKEXTEND.NONETWORK ? sdk.ChainId.MAINNET : chainId, walletType: (ConnectProviders[account.connectName] ?? account.connectName) as unknown as sdk.ConnectorNames, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, }, { accountId: account.accountId, counterFactualInfo: account.eddsaKey.counterFactualInfo, }, '1' ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { throw response } else { setShowVaultLoan({ isShow: false }) setIsLoading(false) updateVaultLayer2({}) await sdk.sleep(SUBMIT_PANEL_CHECK) const response2 = await LoopringAPI.vaultAPI.getVaultGetOperationByHash( { accountId: account?.accountId?.toString(), hash: (response as any).hash, }, account.apiKey, '1' ) let status = '' if ( response2?.raw_data?.operation?.status == sdk.VaultOperationStatus.VAULT_STATUS_FAILED ) { throw sdk.VaultOperationStatus.VAULT_STATUS_FAILED } else if ( [sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED].includes( response2?.raw_data?.operation?.status, ) ) { status = 'labelSuccessfully' } else { status = 'labelPending' } setShowAccount({ isShow: store.getState().modals.isShowAccount.isShow, step: status == 'labelSuccessfully' ? AccountStep.VaultRepay_Success : AccountStep.VaultRepay_In_Progress, info: { status: t(status), amount: sdk.VaultOperationStatus.VAULT_STATUS_SUCCEED ? vaultRepayData.repayAmtStr : 0, sum: vaultRepayData.repayAmtStr, symbol: erc20Symbol, vSymbol: vaultToken.symbol, time: response2?.raw_data?.order?.createdAt, }, }) await sdk.sleep(SUBMIT_PANEL_AUTO_CLOSE) updateVaultLayer2({}) if ( store.getState().modals.isShowAccount.isShow && [AccountStep.VaultRepay_Success, AccountStep.VaultRepay_In_Progress].includes( store.getState().modals.isShowAccount.step, ) ) { setShowAccount({ isShow: false }) } } } else { throw new Error('api not ready') } setIsLoading(false) } catch (e) { const code = (e as any)?.message === sdk.VaultOperationStatus.VAULT_STATUS_FAILED ? UIERROR_CODE.ERROR_ORDER_FAILED : (e as sdk.RESULT_INFO)?.code ?? UIERROR_CODE.UNKNOWN const error = new CustomErrorWithCode({ code, message: (e as sdk.RESULT_INFO)?.message, ...SDK_ERROR_MAP_TO_UI[code], }) setIsLoading(false) setShowAccount({ isShow: true, step: AccountStep.VaultRepay_Failed, info: { status: t('labelFailed'), amount: EmptyValueTag, sum: vaultRepayData.repayAmtStr, symbol: erc20Symbol, vSymbol: vaultToken.symbol, time: Date.now(), error, }, }) } } const submitCallback = async () => { const vaultRepayData = store.getState()._router_tradeVault.vaultRepayData const erc20Symbol = vaultRepayData.belong.slice(2) try { if ( vaultRepayData && exchangeInfo && vaultRepayData.belong && LoopringAPI.vaultAPI && LoopringAPI.userAPI && vaultAccountInfo && sdk.toBig(vaultRepayData?.repayVol).gte(vaultRepayData.minRepayVol ?? 0) && sdk.toBig(vaultRepayData?.maxRepayAmount).lte(vaultRepayData.maxRepayVol ?? 0) ) { setIsLoading(true) setShowAccount({ isShow: true, step: AccountStep.VaultRepay_In_Progress, info: { status: t('labelPending'), amount: EmptyValueTag, sum: vaultRepayData.repayAmtStr, symbol: erc20Symbol, vSymbol: vaultRepayData.belong, time: Date.now(), }, }) const tokenInfo = vaultTokenMap[vaultRepayData.belong] const [{ broker }, { offchainId }] = await Promise.all([ LoopringAPI.userAPI?.getAvailableBroker({ type: 4, }), LoopringAPI.userAPI?.getNextStorageId( { accountId: account.accountId, sellTokenId: tokenInfo.vaultTokenId, }, account.apiKey, ), ]) const vaultRepayRequest = { exchange: exchangeInfo.exchangeAddress, payerAddr: account.accAddress, payerId: account.accountId, payeeId: 0, payeeAddr: broker, storageId: offchainId, token: { tokenId: tokenInfo.vaultTokenId, volume: vaultRepayData?.repayVol, }, maxFee: { tokenId: tokenInfo.vaultTokenId, volume: '0', // TEST: fee.toString(), }, validUntil: getTimestampDaysLater(DAYS), memo: '', } updateVaultRepay({ ...vaultRepayData, request: vaultRepayRequest, }) processRequest(vaultRepayRequest) } } catch (e) { setIsLoading(false) setShowAccount({ isShow: true, step: AccountStep.VaultRepay_Failed, error: { ...(e as any), }, }) } } const { btnStatus, onBtnClick, btnLabel } = useSubmitBtn({ availableTradeCheck, isLoading, submitCallback, }) const moreToBorrowInUSD = vaultRepayData.tradeData && tokenPrices[vaultRepayData.tradeData.belong as string] ? new Decimal(vaultRepayData.tradeData.tradeValue?.toString() ?? '0') .mul(tokenPrices[vaultRepayData.tradeData.belong as string]) .mul('-1') .toString() : undefined const nextMarginLevel = vaultAccountInfo?.marginLevel && moreToBorrowInUSD ? calcMarinLevel( vaultAccountInfo.totalCollateralOfUsdt, vaultAccountInfo.totalDebtOfUsdt, vaultAccountInfo.totalBalanceOfUsdt, moreToBorrowInUSD, '0', ) : vaultAccountInfo?.marginLevel return { handlePanelEvent, vaultRepayBtnStatus: btnStatus, vaultRepayBtnI18nKey: btnLabel, onVaultRepayClick: onBtnClick, walletMap: mapValues(walletMap, (value, key) => { return { ...value, erc20Symbol: key.slice(2), belongAlice: key.slice(2), } }) as unknown as any, coinMap: mapValues(vaultCoinMap, (value) => { return { ...value, erc20Symbol: value?.simpleName.slice(2), belongAlice: value?.simpleName.slice(2), } }), tradeData: { ...vaultRepayData.tradeData, erc20Symbol: vaultRepayData.tradeData?.belong.slice(2), belongAlice: vaultRepayData.tradeData?.belong.slice(2), }, vaultRepayData: { ...vaultRepayData, erc20Symbol: vaultRepayData.tradeData?.belong.slice(2), belongAlice: vaultRepayData.tradeData?.belong.slice(2), tradeData: { ...vaultRepayData?.tradeData, erc20Symbol: vaultRepayData.tradeData?.belong.slice(2), belongAlice: vaultRepayData.tradeData?.belong.slice(2), }, } as unknown as V, tokenInfo: vaultTokenMap[vaultRepayData?.belong], tokenProps: { decimalsLimit: vaultTokenMap[vaultRepayData?.belong]?.vaultTokenAmounts?.qtyStepScale, allowDecimals: vaultTokenMap[vaultRepayData?.tradeData?.belong]?.vaultTokenAmounts ?.qtyStepScale ? true : false, }, marginLevelChange: vaultAccountInfo?.marginLevel ? nextMarginLevel && vaultRepayData.tradeValue ? { from: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, to: { marginLevel: nextMarginLevel, type: marginLevelType(nextMarginLevel), }, } : { from: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, to: { marginLevel: vaultAccountInfo.marginLevel, type: marginLevelType(vaultAccountInfo.marginLevel), }, } : undefined, initialSymbol } } ================================================ FILE: packages/core/src/hooks/useractions/vault/utils.ts ================================================ import Decimal from 'decimal.js' export const calcMarinLevel = ( originMarginInUSD: string, originDebtInUSD: string, totalBalanceInUSD: string, moreToBorrowInUSD: string, moreToCollateralizeInUSD: string, ) => { const originMarginDecimal = new Decimal(originMarginInUSD) const totalBalanceInUSDDecimal = new Decimal(totalBalanceInUSD) const moreToBorrowDecimal = new Decimal(moreToBorrowInUSD ?? '0') const moreToCollateralizeDecimal = new Decimal(moreToCollateralizeInUSD ?? '0') const originDebtDecimal = new Decimal(originDebtInUSD ?? '0') if (originDebtDecimal.add(moreToBorrowDecimal).eq('0')) { return '999' } else { const calculated = originMarginDecimal .add(moreToCollateralizeDecimal) .add(totalBalanceInUSDDecimal) .add(moreToBorrowDecimal) .div(originDebtDecimal.add(moreToBorrowDecimal)) if (calculated.gte('999')) { return '999' } else if (calculated.lt('0')) { return undefined } else { return calculated.toString() } } } export const marginLevelType: (marginLevel: string) => 'danger' | 'safe' | 'warning' = ( marginLevel: string, ) => { const marginLevelDecimal = new Decimal(marginLevel) if (marginLevelDecimal.gte('1.5')) { return 'safe' } else if (marginLevelDecimal.gte('1.15')) { return 'warning' } else { return 'danger' } } ================================================ FILE: packages/core/src/index.ts ================================================ export * from './component' export * from './stores/index' export * from './stores/rootSaga' export * from './hookConnect' export * from './TimeoutCheckProvider' export * from './storage' export * from './services' export * from './defs' export * from './api_wrapper' export * from './utils' export * from './hooks' export * from './modal' export { erc20ABI, taikoDepositABI } from './abi' ================================================ FILE: packages/core/src/modal/AccountL1Modal/hook.tsx ================================================ /* eslint-disable react/jsx-pascal-case */ import { AccountStep, Button, Deposit_Approve_Denied, Deposit_Approve_WaitForAuth, Deposit_Denied, Deposit_Failed, Deposit_Sign_WaitForRefer, Deposit_Submit, Deposit_WaitForAuth, DepositProps, HadAccount, NoAccount, QRAddressPanel, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { ConnectProviders, walletServices } from '@loopring-web/web3-provider' import React, { useState } from 'react' import { Account, AccountStatus, AssetsRawDataItem, copyToClipBoard, L1L2_NAME_DEFINED, MapChainId, } from '@loopring-web/common-resources' import { depositServices, goActiveAccount, LAST_STEP, lockAccount, onchainHashInfo, unlockAccount, useAccount, useNotify, useRampTransPost, useSystem, useToast, } from '@loopring-web/core' export function useAccountModalForL1UI({ t, assetsRawData, depositProps, ...rest }: { t: any etherscanBaseUrl: string depositProps: DepositProps account: Account assetsRawData: AssetsRawDataItem[] // onClose?: any; }) { const { chainInfos, updateDepositHash } = onchainHashInfo.useOnChainInfo() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { processRequestRampTransfer } = useRampTransPost() const { campaignTagConfig } = useNotify().notifyMap ?? {} const { modals: { isShowAccount }, setShowConnect, setShowAccount, setShowDeposit, } = useOpenModals() rest = { ...rest, ...isShowAccount.info } const { allowTrade } = useSystem() const { account, addressShort, shouldShow, setShouldShow } = useAccount() const { toastOpen, setToastOpen, closeToast } = useToast() // const { nftDepositProps } = useNFTDeposit(); const [openQRCode, setOpenQRCode] = useState(false) const [copyToastOpen, setCopyToastOpen] = useState(false) const onSwitch = React.useCallback(() => { setShowAccount({ isShow: false }) setShouldShow(true) setShowConnect({ isShow: shouldShow ?? false }) }, [setShowAccount, setShouldShow, setShowConnect, shouldShow]) const onCopy = React.useCallback(async () => { copyToClipBoard(account.accAddress) setCopyToastOpen(true) }, [account, setCopyToastOpen]) const onViewQRCode = React.useCallback(() => { setOpenQRCode(true) }, [setOpenQRCode]) const onDisconnect = React.useCallback(async () => { walletServices.sendDisconnect('', 'customer click disconnect') setShowAccount({ isShow: false }) }, [setShowAccount]) const onQRClick = React.useCallback(() => { setShowAccount({ isShow: true, step: AccountStep.QRCode }) }, [setShowAccount]) const unlockBtn = React.useMemo(() => { return ( ) }, [t, setShouldShow]) const lockBtn = React.useMemo(() => { return ( ) }, [t]) const onQRBack = React.useCallback(() => { if (Number.isInteger(isShowAccount.info?.backTo)) { setShowAccount({ isShow: true, step: isShowAccount.info?.backTo }) } else { switch (account.readyState) { case AccountStatus.NO_ACCOUNT: case AccountStatus.DEPOSITING: setShowAccount({ isShow: true, step: AccountStep.NoAccount }) break case AccountStatus.LOCKED: case AccountStatus.ACTIVATED: break default: setShowAccount({ isShow: false }) } } }, [account.readyState, isShowAccount, setShowAccount]) const closeBtnInfo = React.useCallback( (props?: { closeExtend?: (e?: any) => void }) => { return { btnTxt: 'labelClose', callback: (e: any) => { setShouldShow(false) setShowAccount({ isShow: false }) if (props?.closeExtend) { props?.closeExtend(e) } // if (onClose) { // onClose(e); // } }, } }, [setShouldShow, setShowAccount], ) const accountList = React.useMemo(() => { // const isShowAccount?.info. return Object.values({ [AccountStep.NoAccount]: { view: ( { setShouldShow(false) setShowAccount({ isShow: false }) }, clearDepositHash: () => undefined, updateDepositHash, ...account, etherscanUrl: rest.etherscanBaseUrl, onSwitch, onCopy, onViewQRCode, onDisconnect, addressShort, }} /> ), onQRClick, height: 'auto', }, [AccountStep.QRCode]: { view: ( ), onBack: onQRBack, noClose: true, height: 'auto', }, [AccountStep.HadAccount]: { view: ( undefined, chainInfos, noButton: true, onSwitch, onCopy, onClose: (_e: any) => { setShouldShow(false) setShowAccount({ isShow: false }) }, etherscanUrl: rest.etherscanBaseUrl, onViewQRCode, onDisconnect, addressShort, etherscanLink: rest.etherscanBaseUrl + 'address/' + account.accAddress, mainBtn: account.readyState === AccountStatus.ACTIVATED ? lockBtn : unlockBtn, }} /> ), onQRClick, height: 'auto', }, [AccountStep.Deposit_Sign_WaitForRefer]: { view: ( ), }, [AccountStep.Deposit_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.Deposit_Approve_Denied]: { view: ( { depositServices.depositERC20() }, }} {...{ ...rest, account, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.Deposit_WaitForAuth]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.Deposit_Denied]: { view: ( { depositServices.depositERC20() }, }} {...{ ...rest, account, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.Deposit_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.deposit, }, }) }, })} {...{ ...rest, account, error: isShowAccount.error, t, }} /> ), onBack: !depositProps.isAllowInputToAddress ? () => { setShowAccount({ isShow: false }) setShowDeposit({ isShow: true }) } : undefined, }, [AccountStep.Deposit_Submit]: { view: ( { setShowAccount({ isShow: false }) setShowDeposit({ isShow: true, symbol: (rest as any)?.symbol ?? isShowAccount?.info?.symbol ?? 'LRC', }) }, }} {...{ ...rest, account, t, }} /> ), }, }) }, [ network, account, isShowAccount.info, isShowAccount.error, allowTrade, depositProps.isNewAccount, depositProps.isAllowInputToAddress, depositProps.tradeData.belong, depositProps.tradeData.tradeValue, campaignTagConfig, chainInfos, updateDepositHash, rest, onSwitch, onCopy, onViewQRCode, onDisconnect, addressShort, onQRClick, lockBtn, unlockBtn, t, onQRBack, closeBtnInfo, setShowAccount, setShowDeposit, processRequestRampTransfer, ]) const currentModal = accountList[isShowAccount.step] return { depositProps, copyToastOpen, setCopyToastOpen, setToastOpen, openQRCode, setOpenQRCode, isShowAccount, account, closeBtnInfo, accountList, currentModal, toastOpen, closeToast, // checkActiveStatusProps, // dualToastOpen, } } ================================================ FILE: packages/core/src/modal/AccountL1Modal/index.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { DepositProps, ModalAccount, ModalQRCode, Toast, ToastType, useOpenModals, } from '@loopring-web/component-lib' import { useAccountModalForL1UI } from './hook' import { Account, AssetsRawDataItem, TOAST_TIME } from '@loopring-web/common-resources' export const ModalAccountL1Info = withTranslation('common')( ({ // onClose, etherscanBaseUrl, open, assetsRawData, depositProps, t, ...rest }: { open: boolean account: Account depositProps: DepositProps // onClose?: (e: MouseEvent) => void; etherscanBaseUrl: string assetsRawData: AssetsRawDataItem[] } & WithTranslation) => { // const { isMobile } = useSettings(); const { modals: { // isShowNFTDetail, isShowAccount, }, // setShowNFTDetail, setShowAccount, // setShowDeposit, // setShowTransfer, // setShowWithdraw, } = useOpenModals() const { setCopyToastOpen, setOpenQRCode, account, copyToastOpen, openQRCode, accountList, currentModal, toastOpen, closeToast, } = useAccountModalForL1UI({ t, assetsRawData, depositProps, etherscanBaseUrl, ...rest, }) // myLog( // "resetProps.chargeFeeTokenList", // activeAccountProps.chargeFeeTokenList // ); return ( <> { setCopyToastOpen(false) }} severity={ToastType.success} /> setOpenQRCode(false)} title={'ETH Address'} description={account?.accAddress} url={account?.accAddress} /> { setShowAccount({ isShow: false }) // currentModal?.onClose && currentModal?.onClose(); }} panelList={accountList} onBack={currentModal?.onBack} onQRClick={currentModal?.onQRClick} step={isShowAccount.step} etherscanBaseUrl={etherscanBaseUrl} isLayer2Only={true} noClose={currentModal?.noClose} /> ) }, ) ================================================ FILE: packages/core/src/modal/AccountModal/components/NFTDetail.tsx ================================================ import styled from '@emotion/styled' import { Avatar, Box, BoxProps, Divider, Grid, IconButton, Link, Tab, Tabs, Tooltip, Typography, } from '@mui/material' import { AssetsRawDataItem, EmptyValueTag, Explorer, getShortAddr, ImageIcon, IPFS_LOOPRING_SITE, LinkIcon, myLog, NFT_TYPE_STRING, NFTWholeINFO, RefreshIPFSIcon, type, ZoomIcon, TOAST_TIME, htmlDecode, HideIcon, ViewIcon, SDK_ERROR_MAP_TO_UI, UIERROR_CODE, FavSolidIcon, FavHollowIcon, BurnIcon, RedPacketIcon, RouterPath, } from '@loopring-web/common-resources' import { WithTranslation, withTranslation } from 'react-i18next' import { Button, TextareaAutosizeStyled, useOpenModals, useSettings, useToggle, NFTMedia, ZoomMedia, AccountStep, EmptyDefault, Toast, ToastType, } from '@loopring-web/component-lib' import { nftRefresh, store, useAccount, useSystem } from '../../../stores' import React from 'react' import { getIPFSString } from '../../../utils' import { LoopringAPI } from '../../../api_wrapper' import { useToast } from '../../../hooks' import { sanitize } from 'dompurify' import { StylePaper } from '../../../component' import { DEPLOYMENT_STATUS, NFTType } from '@loopring-web/loopring-sdk' import * as sdk from '@loopring-web/loopring-sdk' import { useHistory } from 'react-router-dom' import { useTheme } from '@emotion/react' enum NFTDetailTab { Detail = 'Detail', Property = 'Property', } const BoxNFT = styled(Box)` background: var(--color-global-bg-opacity); img { object-fit: contain; } ` as typeof Box const FavoriteBoxStyle = styled(Box)` .favHollow { &:hover { color: var(--color-error); } } .favSolid { &:hover { color: var(--color-text-secondary); } } ` const BoxStyle = styled(Box)< { isMobile: boolean; baseURL: string } & BoxProps & Partial >` .objectFit { img { object-fit: contain; } } .line-clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } ` as ( props: { isMobile?: boolean; baseURL?: string } & BoxProps & Partial, ) => JSX.Element export const NFTDetail = withTranslation('common')( ({ popItem, etherscanBaseUrl, baseURL, setNFTMetaNotReady, t, }: { popItem: Partial etherscanBaseUrl: string baseURL: string setNFTMetaNotReady: (props: any) => void assetsRawData: AssetsRawDataItem[] } & WithTranslation) => { const { isMobile } = useSettings() const { chainId } = useSystem() const theme = useTheme() const { account } = useAccount() const [iconLoading, setIconLoading] = React.useState(false) const { nftDataHashes: { nftDataHashes }, updateNFTRefreshHash, } = nftRefresh.useNFTRefresh() const nodeTimer = React.useRef(-1) const { toastOpen, setToastOpen, closeToast } = useToast() const { toggle: { deployNFT }, } = useToggle() const { setShowNFTDetail, setShowAccount, setShowNFTTransfer, setShowNFTDeploy, setShowTradeIsFrozen, modals: { isShowNFTDetail }, } = useOpenModals() const [zoom, setZoom] = React.useState(false) const [tabValue, setTabValue] = React.useState(NFTDetailTab.Detail) const [showFresh, setShowFresh] = React.useState( popItem?.nftData && nftDataHashes[popItem.nftData?.toLowerCase()] ? 'loading' : 'click', ) const history = useHistory() myLog('showFresh', showFresh) const onFavoriteClick = React.useCallback(async () => { if (LoopringAPI.userAPI) { try { setIconLoading(true) const response = await LoopringAPI.userAPI.submitUpdateNFTGroup( { accountId: account.accountId, nftHashes: [popItem.nftData ?? ''], preferenceType: sdk.NFT_PREFERENCE_TYPE.fav, statusToUpdate: !popItem?.preference?.favourite, }, chainId as any, account.apiKey, account.eddsaKey.sk, ) setIconLoading(false) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { const _response: sdk.RESULT_INFO = response as sdk.RESULT_INFO throw new Error( t( _response.code && SDK_ERROR_MAP_TO_UI[_response.code] ? SDK_ERROR_MAP_TO_UI[_response.code].messageKey : SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN].messageKey, { ns: 'error', name: popItem.name?.trim() }, ), ) } else { setToastOpen({ open: true, type: ToastType.success, content: t(`labelFavouriteSuccess`, { favorite: !popItem?.preference?.favourite ? t('labelfavourite') : t('labelunfavourite'), }), }) setShowNFTDetail({ ...isShowNFTDetail, preference: { ...isShowNFTDetail.preference, favourite: !popItem?.preference?.favourite ?? false, } as any, }) } } catch (error) { setToastOpen({ open: true, type: ToastType.error, content: t(`labelFavouriteFailed`, { favorite: !popItem?.preference?.favourite ? t('labelfavourite') : t('labelunfavourite'), }) + `: ${(error as any)?.message ? (error as any).message : t('errorUnknown')}`, }) } } }, [popItem]) const onHideClick = React.useCallback(async () => { if (LoopringAPI.userAPI) { try { setIconLoading(true) const response = await LoopringAPI.userAPI.submitUpdateNFTGroup( { accountId: account.accountId, nftHashes: [popItem.nftData ?? ''], preferenceType: sdk.NFT_PREFERENCE_TYPE.hide, statusToUpdate: !popItem?.preference?.hide, }, chainId as any, account.apiKey, account.eddsaKey.sk, ) setIconLoading(false) if ( response && ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) ) { const _response: sdk.RESULT_INFO = response as sdk.RESULT_INFO throw new Error( t( _response.code && SDK_ERROR_MAP_TO_UI[_response.code] ? SDK_ERROR_MAP_TO_UI[_response.code].messageKey : SDK_ERROR_MAP_TO_UI[UIERROR_CODE.UNKNOWN].messageKey, { ns: 'error', name: popItem.name?.trim() }, ), ) } else { setToastOpen({ open: true, type: ToastType.success, content: t(`labelHideSuccess`, { hide: !popItem?.preference?.hide ? t('labelhide') : t('labelunhide'), }), }) setShowNFTDetail({ ...isShowNFTDetail, preference: { ...isShowNFTDetail.preference, hide: !popItem?.preference?.hide ?? false, } as any, }) } } catch (error) { setToastOpen({ open: true, type: ToastType.error, content: t(`labelHideFailed`, { hide: !popItem?.preference?.hide ? t('labelhide') : t('labelunhide'), }) + `: ${(error as any)?.message ? (error as any).message : t('errorUnknown')}`, }) } } }, [popItem]) const handleRefresh = React.useCallback(async () => { setShowFresh('loading') setToastOpen({ open: true, type: ToastType.success, content: t('labelNFTServerRefreshSubmit'), }) if (popItem && popItem.nftData) { updateNFTRefreshHash(popItem.nftData) await LoopringAPI.nftAPI?.callRefreshNFT({ network: 'ETHEREUM', tokenAddress: popItem.tokenAddress ?? '', nftId: popItem?.nftId?.toString() ?? '', nftType: (popItem?.nftType?.toString() ?? '') as NFT_TYPE_STRING, }) setToastOpen({ open: true, type: ToastType.success, content: t('labelNFTServerRefreshSubmit'), }) } }, []) // const [showDialog, setShowDialog] = // React.useState(undefined); const [isKnowNFTNoMeta, setIsKnowNFTNoMeta] = React.useState( !!(popItem?.name !== '' && popItem.image && popItem.image !== ''), ) let properties = popItem.properties ? typeof popItem.properties === 'string' ? JSON.parse(popItem.properties) : popItem.properties : undefined React.useEffect(() => { setIsKnowNFTNoMeta((_state) => { return !!(popItem.name !== '' && popItem.image && popItem.image !== '') }) }, [popItem.name, popItem.image]) const updateNFTStatus = React.useCallback(async () => { const nftDataHashes = store.getState().localStore.nftHashInfos[chainId]?.nftDataHashes clearTimeout(nodeTimer.current as NodeJS.Timeout) if (popItem.nftData && nftDataHashes && nftDataHashes[popItem.nftData.toLowerCase()]) { updateNFTRefreshHash(popItem.nftData) nodeTimer.current = setTimeout(() => { updateNFTStatus() // updateNFTRefreshHash(popItem.nftData); }, 180000) } else { setShowFresh('click') } return () => { clearTimeout(nodeTimer.current as NodeJS.Timeout) } }, [nodeTimer]) React.useEffect(() => { if (popItem?.nftData && nftDataHashes[popItem.nftData]) { updateNFTStatus() } }, [nftDataHashes, popItem.nftData]) const compileString = (str: any) => { const _type = type(str) let _str if (['string', 'number', 'symbol'].includes(_type)) { _str = str } else if (['array', 'object'].includes(_type)) { _str = JSON.stringify(str, undefined, 2) } return sanitize(_str ?? EmptyValueTag) } const ref = React.useRef() const cid = LoopringAPI?.nftAPI?.ipfsNftIDToCid(popItem?.nftId ?? '') return ( <> } shouldPlay={true} onNFTError={() => undefined} isOrigin={true} getIPFSString={getIPFSString} baseURL={baseURL} /> {popItem.preference && ( { onFavoriteClick() }} sx={{ minWidth: 'initial', padding: '4px', marginRight: 1, }} > {popItem.preference.favourite ? ( ) : ( )} )} { handleRefresh() }} sx={{ minWidth: 'initial', padding: '4px', marginRight: 1 }} > { setZoom(true) }} sx={{ minWidth: 'initial', padding: '4px', marginRight: 1 }} > {popItem.preference && ( { onHideClick() }} sx={{ minWidth: 'initial', padding: '4px', marginRight: 1, }} > {popItem.preference.hide ? : } )} {popItem?.collectionInfo && ( <> {( popItem?.collectionInfo?.cached?.tileUri ?? getIPFSString(popItem?.collectionInfo?.tileUri ?? '', baseURL) ).startsWith('http') ? ( ) : ( )} {popItem?.collectionInfo ? popItem?.collectionInfo?.name ? htmlDecode(popItem.collectionInfo.name) : t('labelUnknown') + ' - ' + getShortAddr(popItem?.collectionInfo?.contractAddress ?? '') : EmptyValueTag} )} {!!( popItem.isCounterFactualNFT && popItem.deploymentStatus === DEPLOYMENT_STATUS.NOT_DEPLOYED && popItem.minter?.toLowerCase() === account.accAddress.toLowerCase() ) && ( )} {/* { history.push(`${RouterPath.redPacket}/create?nftDatas=${popItem.nftData}`) }} style={{ height: 40, width: 40, background: '#F26666', borderRadius: theme.unit, }} > */} { setShowNFTTransfer({ isShow: true, info: { isBurn: true, ...popItem } }) }} > setTabValue(value)} aria-label='NFT Detail Tab' > {tabValue === NFTDetailTab.Detail && ( <> {t('labelNFTTOTAL')} {Number(popItem.total) - Number(popItem.locked ?? 0)} {t('labelNFTID')} {'# ' + getShortAddr(popItem?.nftIdView ? popItem.nftIdView : popItem.nftId ?? '')} {t('labelNFTContractAddress')} {popItem.tokenAddress} {t('labelNFTRoyaltyPercentage')} {popItem?.royaltyPercentage ?? EmptyValueTag + '%'} {t('labelNFTTYPE')} {popItem?.nftType} {t('labelNFTMinter')} { window.open(`${etherscanBaseUrl}address/${popItem.minter}`, 'blank') window.opener = null }} > {popItem.minter} {t('labelNFTMetadata')} { window.open(IPFS_LOOPRING_SITE + cid, 'blank') window.opener = null }} > {cid} )} {tabValue === NFTDetailTab.Property && (!!properties ? ( typeof properties === 'string' ? ( {properties.toString()} ) : ( [...(Array.isArray(properties) ? properties : Object.keys(properties))].map( (key, index) => { // @ts-ignore return Array.isArray(properties) ? ( ) : ( // JSON.stringify(key.toString()) ) }, ) ) ) : ( ( {t('labelNoContent')} )} /> ))} { setZoom(false) }} getIPFSString={getIPFSString} baseURL={baseURL} open={zoom} nftItem={popItem as Partial} /> ) }, ) ================================================ FILE: packages/core/src/modal/AccountModal/components/QRCodeScanner.tsx ================================================ import { ImportRedPacketWrap } from '@loopring-web/component-lib' import { useQrcodeScan } from '../../../services' import React from 'react' import { myLog } from '@loopring-web/common-resources' // import { useLocation } from 'react-use'; export const ImportRedPacket = ({ handleSuccess, }: { handleSuccess: (value: string) => Promise }) => { const ref = React.useRef() // const { search } = useLocation(); const handleFailedUpload = React.useCallback((data: any) => { myLog('handleFailedUpload', data) }, []) const handleSuccessUpload = React.useCallback((result: any) => { myLog('handleSuccessUpload', result?.data?.decodedText) if (result?.data?.decodedText) { handleSuccess(result?.data?.decodedText) } }, []) const { h5QrcodeScannerProvides } = useQrcodeScan({ handleFailedUpload, handleSuccessUpload, }) React.useEffect(() => { if (ref.current) { h5QrcodeScannerProvides.render() } else { h5QrcodeScannerProvides.clear() } return () => { h5QrcodeScannerProvides.clear() } }, [ref.current]) return } ================================================ FILE: packages/core/src/modal/AccountModal/hook.tsx ================================================ /* eslint-disable react/jsx-pascal-case */ import { AccountStep, AddAsset, AddAssetItem, Button, CheckActiveStatus, ClaimWithdraw_Denied, ClaimWithdraw_Failed, ClaimWithdraw_First_Method_Denied, ClaimWithdraw_In_Progress, ClaimWithdraw_Submit, ClaimWithdraw_WaitForAuth, ContinuousBanxaOrder, CreateAccount_Approve_Denied, CreateAccount_Approve_Submit, CreateAccount_Approve_WaitForAuth, CreateAccount_Denied, CreateAccount_Failed, CreateAccount_Submit, CreateAccount_WaitForAuth, Deposit_Approve_Denied, Deposit_Approve_WaitForAuth, Deposit_Denied, Deposit_Failed, Deposit_Sign_WaitForRefer, Deposit_Submit, Deposit_WaitForAuth, DepositProps, Dual_Failed, Dual_Success, Staking_Failed, Staking_Success, ExportAccount_Approve_WaitForAuth, ExportAccount_Failed, ExportAccount_Success, ExportAccount_User_Denied, ForceWithdraw_Denied, ForceWithdraw_Failed, ForceWithdraw_First_Method_Denied, ForceWithdraw_In_Progress, ForceWithdraw_Submit, ForceWithdraw_WaitForAuth, HadAccount, NFTDeploy_Denied, NFTDeploy_Failed, NFTDeploy_First_Method_Denied, NFTDeploy_In_Progress, NFTDeploy_Submit, NFTDeploy_WaitForAuth, NFTDeposit_Approve_Denied, NFTDeposit_Approve_WaitForAuth, NFTDeposit_Denied, NFTDeposit_Failed, NFTDeposit_Submit, NFTDeposit_WaitForAuth, NFTMint_Denied, NFTMint_Failed, NFTMint_First_Method_Denied, NFTMint_In_Progress, NFTMint_Success, NFTMint_WaitForAuth, NFTTransfer_Failed, NFTTransfer_First_Method_Denied, NFTTransfer_In_Progress, NFTTransfer_Success, NFTTransfer_User_Denied, NFTTransfer_WaitForAuth, NFTWithdraw_Failed, NFTWithdraw_First_Method_Denied, NFTWithdraw_In_Progress, NFTWithdraw_Success, NFTWithdraw_User_Denied, NFTWithdraw_WaitForAuth, NoAccount, QRAddressPanel, RedPacketOpen_Claim_Failed, RedPacketOpen_Claim_In_Progress, RedPacketOpen_Failed, RedPacketOpen_In_Progress, RedPacketSend_Claim_Success, RedPacketSend_Failed, RedPacketSend_First_Method_Denied, RedPacketSend_In_Progress, RedPacketSend_Success, RedPacketSend_User_Denied, RedPacketSend_WaitForAuth, SendAsset, SendAssetItem, SendNFTAsset, ThirdPanelReturn, Transfer_banxa_confirm, Transfer_Failed, Transfer_First_Method_Denied, Transfer_In_Progress, Transfer_Success, Transfer_User_Denied, Transfer_WaitForAuth, UnlockAccount_Failed, UnlockAccount_Success, UnlockAccount_User_Denied, UnlockAccount_WaitForAuth, UpdateAccount, UpdateAccount_Approve_WaitForAuth, UpdateAccount_Failed, UpdateAccount_First_Method_Denied, UpdateAccount_Success, UpdateAccount_User_Denied, useOpenModals, VendorMenu, Withdraw_Failed, Withdraw_First_Method_Denied, Withdraw_In_Progress, Withdraw_Success, Withdraw_User_Denied, Withdraw_WaitForAuth, Staking_Redeem_Success, Staking_Redeem_Failed, BtradeSwap_Settled, BtradeSwap_Delivering, BtradeSwap_Failed, BtradeSwap_Pending, AMM_Pending, useSettings, TOASTOPEN, setShowGlobalToast, SendFromContact, NFTBurn_Failed, NFTBurn_First_Method_Denied, NFTBurn_In_Progress, NFTBurn_Success, NFTBurn_User_Denied, NFTBurn_WaitForAuth, // NFTBurn_Failed, // NFTBurn_First_Method_Denied, // NFTBurn_In_Progress, // NFTBurn_Success, // NFTBurn_User_Denied, // NFTBurn_WaitForAuth, // SendFromContact, VaultTrade_Success, VaultTrade_Failed, VaultTrade_In_Progress, VaultJoin_Success, VaultJoin_Failed, VaultJoin_In_Progress, VaultRedeem_Success, VaultRedeem_Failed, VaultRedeem_In_Progress, VaultBorrow_Success, VaultBorrow_Failed, VaultBorrow_In_Progress, VaultRepay_Success, VaultRepay_Failed, VaultRepay_In_Progress, VaultDustCollector_Success, VaultDustCollector_Failed, VaultDustCollector_In_Progress, General_Failed, Taiko_Farming_Lock_Success, Taiko_Farming_Redeem_Failed, Taiko_Farming_Redeem_Success, Taiko_Farming_Lock_Failed, Taiko_Farming_Mint_Success, Taiko_Farming_Mint_Failed, Taiko_Farming_Mint_In_Progress, Taiko_Farming_Redeem_In_Progress, useToggle, Transfer_To_TaikoUser_Denied, Transfer_To_TaikoIn_Progress, Transfer_To_Taiko_User_Denied, Transfer_To_Taiko_In_Progress, Transfer_To_Taiko_Success, Transfer_To_Taiko_Failed, UnlockAccount_Reset_Key_Confirm, Coinbase_Smart_Wallet_Password_Intro, Coinbase_Smart_Wallet_Password_Set, Coinbase_Smart_Wallet_Password_Input, Coinbase_Smart_Wallet_Password_Forget_Password_Confirm, Coinbase_Smart_Wallet_Password_Forget_Password, UpdateAccount_SmartWallet_NotSupported_Alert, CreateAccount_EOA_Only_Alert, } from '@loopring-web/component-lib' import { ConnectProviders, connectProvides, walletServices } from '@loopring-web/web3-provider' import React, { useState } from 'react' import { Account, AccountStatus, AddAssetList, AddAssetListMap, AssetsRawDataItem, Bridge, copyToClipBoard, FeeInfo, InvestRouter, InvestType, L1L2_NAME_DEFINED, MapChainId, NFTSubRouter, NFTWholeINFO, RouterPath, SendAssetList, SendAssetListMap, SendNFTAssetList, SPECIAL_ACTIVATION_NETWORKS, TradeTypes, VendorProviders, } from '@loopring-web/common-resources' import { banxaService, depositServices, goActiveAccount, LAST_STEP, lockAccount, mintService, offFaitService, onchainHashInfo, OrderENDReason, parseRabbitConfig, store, unlockAccount, useAccount, useActiveAccount, useCheckActiveStatus, useCoinbaseWalletPassword, useCollectionAdvanceMeta, useConfig, useContacts, useCreateRedPacket, useExportAccount, useForceWithdraw, useModalData, useNFTBurn, useNFTDeploy, useNFTMintAdvance, useNFTTransfer, useNFTWithdraw, useNotify, useRampTransPost, useRedPacketScanQrcodeSuccess, useReset, useStakeTradeExit, useSystem, useTokenMap, useTransfer, useTransferToTaikoAccount, useVendor, useWalletLayer2, useWithdraw, } from '@loopring-web/core' import * as sdk from '@loopring-web/loopring-sdk' import { useHistory } from 'react-router-dom' import { ImportRedPacket } from './components/QRCodeScanner' import { useClaimConfirm } from '../../hooks/useractions/useClaimConfirm' import { useContactAdd } from '../../hooks/useractions/useContactAdd' import { Coinbase_Smart_Wallet_Password_Get_Error, Coinbase_Smart_Wallet_Password_Set_Confirm, Coinbase_Smart_Wallet_Password_Set_Error, Coinbase_Smart_Wallet_Password_Set_Processing } from '@loopring-web/component-lib/src/components/modal/ModalPanels/CoinbaseSmartWalletModal' export function useAccountModalForUI({ t, assetsRawData, isLayer1Only = false, depositProps, isWebEarn, ...rest }: { t: any etherscanBaseUrl: string isLayer1Only?: boolean depositProps: DepositProps account: Account assetsRawData: AssetsRawDataItem[] isWebEarn?: boolean // onClose?: any; }) { const { chainInfos, updateDepositHash, clearDepositHash } = onchainHashInfo.useOnChainInfo() const { updateWalletLayer2 } = useWalletLayer2() const { processRequestRampTransfer } = useRampTransPost() const { campaignTagConfig } = useNotify().notifyMap ?? {} const history = useHistory() const { modals: { isShowAccount }, setShowConnect, setShowAccount, setShowDeposit, setShowTransfer, setShowWithdraw, setShowResetAccount, setShowActiveAccount, setShowNFTTransfer, setShowNFTWithdraw, setShowTransferToTaikoAccount } = useOpenModals() rest = { ...rest, ...isShowAccount.info } const { nftDepositValue, nftTransferValue, nftWithdrawValue, nftDeployValue, transferValue, withdrawValue, resetTransferData, resetWithdrawData, resetDepositData, forceWithdrawValue, claimValue, } = useModalData() const { chainId, allowTrade, app } = useSystem() const { defaultNetwork } = useSettings() const network = MapChainId[defaultNetwork] ?? MapChainId[1] const { account, addressShort, shouldShow, setShouldShow } = useAccount() const redPacketScanQrcodeSuccessProps = useRedPacketScanQrcodeSuccess() const { exportAccountAlertText, exportAccountToastOpen, setExportAccountToastOpen } = useExportAccount() const { retryBtn: nftMintAdvanceRetryBtn } = useNFTMintAdvance() const { retryBtn: creatRedPacketRetryBtn } = useCreateRedPacket({ assetsRawData, isShow: false, }) // const [toastOpen, setToastOpen] = React.useState({ // open: false, // content: '', // type: ToastType.info, // }) const { collectionAdvanceProps } = useCollectionAdvanceMeta({ setCollectionToastOpen: (info: TOASTOPEN) => { setShowGlobalToast({ isShow: info.open, info: { ...info } }) }, }) const { vendorListBuy, banxaRef } = useVendor() // const { nftMintProps } = useNFTMint(); const { withdrawProps } = useWithdraw() const transferToTaikoProps = useTransferToTaikoAccount() const { transferProps } = useTransfer() const { nftWithdrawProps } = useNFTWithdraw() const { nftTransferProps } = useNFTTransfer() const { nftBurnProps } = useNFTBurn() const { nftDeployProps } = useNFTDeploy() const { contactAddProps } = useContactAdd() const { stakeWrapProps } = useStakeTradeExit({ setToastOpen: (info: TOASTOPEN) => setShowGlobalToast({ isShow: info.open, info: { ...info } }), }) const { retryBtn: forceWithdrawRetry } = useForceWithdraw() const { claimProps, retryBtn: claimRetryBtn } = useClaimConfirm() const coinbaseWalletPassword = useCoinbaseWalletPassword() const { resetProps } = useReset() const { activeAccountProps, activeAccountCheckFeeIsEnough } = useActiveAccount() const [tryCheckL2BalanceTimes, setTryCheckL2BalanceTimes] = React.useState(5) // const { nftDepositProps } = useNFTDeposit(); const { exportAccountProps } = useExportAccount() const [openQRCode, setOpenQRCode] = useState(false) const [copyToastOpen, setCopyToastOpen] = useState(false) const onSwitch = React.useCallback(() => { setShowAccount({ isShow: false }) setShouldShow(true) setShowConnect({ isShow: shouldShow ?? false }) }, [setShowAccount, setShouldShow, setShowConnect, shouldShow]) const onCopy = React.useCallback(async () => { copyToClipBoard(account.accAddress) setCopyToastOpen(true) }, [account, setCopyToastOpen]) const onViewQRCode = React.useCallback(() => { setOpenQRCode(true) }, [setOpenQRCode]) const onDisconnect = React.useCallback(async () => { walletServices.sendDisconnect('', 'customer click disconnect') setShowAccount({ isShow: false }) }, [setShowAccount]) const onQRClick = React.useCallback(() => { setShowAccount({ isShow: true, step: AccountStep.QRCode }) }, [setShowAccount]) const unlockBtn = React.useMemo(() => { return ( ) }, [t, setShouldShow]) const lockBtn = React.useMemo(() => { return ( ) }, [t]) const onQRBack = React.useCallback(() => { if (Number.isInteger(isShowAccount.info?.backTo)) { setShowAccount({ isShow: true, step: isShowAccount.info?.backTo }) } else { switch (account.readyState) { case AccountStatus.NO_ACCOUNT: case AccountStatus.DEPOSITING: setShowAccount({ isShow: true, step: AccountStep.NoAccount }) break case AccountStatus.LOCKED: case AccountStatus.ACTIVATED: // setShowAccount({ isShow: true, step: AccountStep.HadAccount }) break default: setShowAccount({ isShow: false }) } } }, [account.readyState, isShowAccount, setShowAccount]) const closeBtnInfo = React.useCallback( (props?: { closeExtend?: (e?: any) => void }) => { return { btnTxt: 'labelClose', callback: (e: any) => { setShouldShow(false) setShowAccount({ isShow: false }) if (props?.closeExtend) { props?.closeExtend(e) } }, } }, [setShouldShow, setShowAccount], ) const nodeTimer = React.useRef(-1) const clearDeposit = React.useCallback(() => { clearDepositHash(account.accAddress) }, [clearDepositHash, account]) const updateDepositStatus = React.useCallback(async () => { const chainInfos = store.getState().localStore.chainHashInfos[chainId] const { accAddress } = account clearTimeout(nodeTimer.current as NodeJS.Timeout) if ( chainInfos && chainInfos.depositHashes && chainInfos.depositHashes[accAddress] && connectProvides ) { const depositList = chainInfos.depositHashes[accAddress] let flag = false depositList.forEach((txInfo) => { if (txInfo.status === 'pending' && connectProvides?.usedWeb3?.eth?.getTransactionReceipt) { connectProvides.usedWeb3.eth.getTransactionReceipt(txInfo.hash).then((result) => { if (result) { updateDepositHash(txInfo.hash, accAddress, result.status ? 'success' : 'failed') } }) flag = true } }) if (flag) { setTryCheckL2BalanceTimes(20) let wait = 60000 if ( account.readyState && [AccountStatus.DEPOSITING, AccountStatus.NOT_ACTIVE].includes( // @ts-ignore account?.readyState, ) ) { wait = 30000 } nodeTimer.current = setTimeout(() => { updateDepositStatus() }, wait) updateWalletLayer2() } else { setTryCheckL2BalanceTimes((state) => { if (state > 0) { updateWalletLayer2() nodeTimer.current = setTimeout(() => { updateDepositStatus() }, 10000) } return state - 1 }) } } }, [account, chainId, updateDepositHash, updateWalletLayer2, nodeTimer, tryCheckL2BalanceTimes]) React.useEffect(() => { if (chainInfos?.depositHashes && chainInfos?.depositHashes[account.accAddress]) { updateDepositStatus() } return () => { clearTimeout(nodeTimer.current as NodeJS.Timeout) } }, [account.accAddress, chainInfos?.depositHashes]) const { setShowLayerSwapNotice, setShowAnotherNetworkNotice } = useOpenModals() const disbaleList = account.isInCounterFactualStatus ? [AddAssetList.FromMyL1.key] : undefined const addAssetList: AddAssetItem[] = React.useMemo( () => AddAssetListMap[network].map((item: string) => { switch (item) { case AddAssetList.BuyWithCard.key: return { ...AddAssetList.BuyWithCard, handleSelect: () => { setShowAccount({ isShow: true, step: AccountStep.PayWithCard }) }, } case AddAssetList.FromMyL1.key: return { ...AddAssetList.FromMyL1, handleSelect: () => { setShowAccount({ isShow: false, info: { lastFailed: undefined }, }) if (isShowAccount?.info?.symbol) { resetDepositData() } setShowDeposit({ isShow: true, symbol: isShowAccount?.info?.symbol, }) }, } case AddAssetList.FromOtherL1.key: return { ...AddAssetList.FromOtherL1, handleSelect: () => { let dex = 'labelAddAssetTitleBridgeDes' if ( account.readyState && [ AccountStatus.DEPOSITING, AccountStatus.NOT_ACTIVE, AccountStatus.NO_ACCOUNT, ].includes( // @ts-ignore account?.readyState, ) ) { dex = 'labelAddAssetTitleBridgeDesActive' } setShowAccount({ isShow: true, step: AccountStep.ThirdPanelReturn, info: { title: t('labelAddAssetTitleBridge', { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), description: t(dex, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, }) window.open( Bridge + `?l2account=${account.accAddress}&token=${ isShowAccount?.info?.symbol ?? '' }&__trace_isSharedBy=loopringExchange`, ) window.opener = null }, } case AddAssetList.FromOtherL2.key: return { ...AddAssetList.FromOtherL2, handleSelect: () => { setShowAccount({ isShow: true, step: AccountStep.QRCode, info: { backTo: AccountStep.AddAssetGateway }, }) }, } case AddAssetList.FromExchange.key: return { ...AddAssetList.FromExchange, handleSelect: () => { let dex = 'labelAddAssetTitleExchangeDes' if ( account.readyState && [ AccountStatus.DEPOSITING, AccountStatus.NOT_ACTIVE, AccountStatus.NO_ACCOUNT, ].includes( // @ts-ignore account?.readyState, ) ) { dex = 'labelAddAssetTitleExchangeDesActive' } setShowAccount({ isShow: true, step: AccountStep.ThirdPanelReturn, info: { title: t('labelAddAssetTitleExchange'), description: t(dex, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, }) setShowLayerSwapNotice({ isShow: true }) }, } case AddAssetList.FromAnotherNet.key: return { ...AddAssetList.FromAnotherNet, handleSelect: () => { let dex = 'labelAddAssetTitleAnotherNetDes' if ( account.readyState && [ AccountStatus.DEPOSITING, AccountStatus.NOT_ACTIVE, AccountStatus.NO_ACCOUNT, ].includes( // @ts-ignore account?.readyState, ) ) { dex = 'labelAddAssetTitleAnotherNetDesActive' } setShowAccount({ isShow: true, step: AccountStep.ThirdPanelReturn, info: { title: t('labelFromAnotherNet'), description: t(dex, { loopringL2: L1L2_NAME_DEFINED[network].loopringL2, l2Symbol: L1L2_NAME_DEFINED[network].l2Symbol, l1Symbol: L1L2_NAME_DEFINED[network].l1Symbol, ethereumL1: L1L2_NAME_DEFINED[network].ethereumL1, loopringLayer2: L1L2_NAME_DEFINED[network].loopringLayer2, }), }, }) setShowAnotherNetworkNotice({ isShow: true, info: { url: 'https://www.orbiter.finance/?source=Ethereum&dest=Loopring' }, }) }, } } // . }), [ network, account.accAddress, isShowAccount?.info?.symbol, setShowAccount, setShowDeposit, setShowLayerSwapNotice, ], ) const { toggle } = useToggle() const { fastWithdrawConfig } = useConfig() const { idIndex } = useTokenMap() const sendAssetList: SendAssetItem[] = React.useMemo( () => SendAssetListMap[network].filter(item => { if (item === SendAssetList.SendAssetToTaikoAccount.key) { // console.log('isShowAccount?.info', isShowAccount?) const parsed = parseRabbitConfig(fastWithdrawConfig, network, idIndex) return ( !isShowAccount?.info?.hideSendToTaiko && toggle.rabbitWithdraw.enable && parsed.toTaikoNetwork === 'TAIKO' && (isShowAccount?.info?.symbol ? parsed.toTaikoNetworkSupportedTokens?.includes(isShowAccount.info.symbol) : true) ) } else { return true } }).map((item: string) => { switch (item) { case SendAssetList.SendAssetToL2.key: return { ...SendAssetList.SendAssetToL2, handleSelect: () => { setShowAccount({ isShow: false, info: { lastFailed: undefined }, }) if (isShowAccount?.info?.symbol) { resetTransferData() } setShowTransfer({ isShow: true, symbol: isShowAccount?.info?.symbol, }) }, } case SendAssetList.SendAssetToMyL1.key: return { ...SendAssetList.SendAssetToMyL1, handleSelect: () => { setShowAccount({ isShow: false, info: { lastFailed: undefined }, }) if (isShowAccount?.info?.symbol) { resetWithdrawData() } setShowWithdraw({ isShow: true, info: { isToMyself: true }, symbol: isShowAccount?.info?.symbol, }) }, } case SendAssetList.SendAssetToOtherL1.key: return { ...SendAssetList.SendAssetToOtherL1, handleSelect: () => { setShowAccount({ isShow: false, info: { lastFailed: undefined }, }) if (isShowAccount?.info?.symbol) { resetWithdrawData() } setShowWithdraw({ isShow: true, info: { isToMyself: false }, symbol: isShowAccount?.info?.symbol, }) }, } case SendAssetList.SendAssetToAnotherNet.key: return { ...SendAssetList.SendAssetToAnotherNet, handleSelect: () => { setShowAccount({ isShow: false, }) setShowAnotherNetworkNotice({ isShow: true, info: { url: 'https://www.orbiter.finance/?source=Loopring&dest=Ethereum' }, }) // window.open('https://www.orbiter.finance/?source=Loopring&dest=Ethereum') // window.opener = null }, } case SendAssetList.SendAssetToTaikoAccount.key: return { ...SendAssetList.SendAssetToTaikoAccount, handleSelect: () => { setShowAccount({ isShow: false, info: { lastFailed: undefined }, }) setShowTransferToTaikoAccount({ isShow: true, from: 'send' }) }, } } }), [network, isShowAccount?.info?.symbol, setShowAccount, setShowTransfer, setShowWithdraw, toggle.rabbitWithdraw.enable, fastWithdrawConfig], ) const sendNFTAssetList: SendAssetItem[] = React.useMemo( () => [ { ...SendNFTAssetList.SendAssetToL2, handleSelect: (_e) => { setShowAccount({ isShow: false }) setShowNFTTransfer({ isShow: true, }) }, }, { ...SendNFTAssetList.SendAssetToMyL1, handleSelect: () => { setShowAccount({ isShow: false }) setShowNFTWithdraw({ isShow: true, info: { isToMyself: true, lastFailed: undefined }, }) }, }, { ...SendNFTAssetList.SendAssetToOtherL1, handleSelect: () => { setShowAccount({ isShow: false, }) setShowNFTWithdraw({ isShow: true, info: { isToMyself: false, lastFailed: undefined }, }) }, }, ], [setShowAccount, setShowNFTTransfer, setShowNFTWithdraw], ) const onBackReceive = React.useCallback(() => { setShowAccount({ isShow: true, step: AccountStep.AddAssetGateway, info: { ...isShowAccount?.info }, }) }, [isShowAccount?.info, setShowAccount]) const onBackSend = React.useCallback(() => { setShowAccount({ isShow: true, step: AccountStep.SendAssetGateway, info: { ...isShowAccount?.info }, }) }, [isShowAccount?.info, setShowAccount]) const { checkActiveStatusProps } = useCheckActiveStatus({ setToastOpen: (info: TOASTOPEN) => setShowGlobalToast({ isShow: info.open, info: { ...info } }), onDisconnect, isDepositing: !!chainInfos?.depositHashes[account?.accAddress]?.length, chargeFeeTokenList: activeAccountProps.chargeFeeTokenList as FeeInfo[], checkFeeIsEnough: activeAccountCheckFeeIsEnough, isFeeNotEnough: activeAccountProps.isFeeNotEnough, }) const isEarn = app === 'earn' const isTaiko = [sdk.ChainId.TAIKO, sdk.ChainId.TAIKOHEKLA].includes(defaultNetwork) const accountList = React.useMemo(() => { // const isShowAccount?.info. return Object.values({ [AccountStep.ContinuousBanxaOrder]: { view: ( { setShouldShow(false) setShowAccount({ isShow: false }) }, btnTxt: t('labelBanxaContinuous'), }} orderId={isShowAccount?.info?.orderId} chainId={chainId as any} btnInfo2={{ isLoading: isShowAccount?.info?.isBanxaLaunchLoading, callback: () => { setShowAccount({ isShow: false }) offFaitService.offRampCancel({ data: { product: VendorProviders.Banxa, orderId: isShowAccount?.info?.orderId, }, }) banxaService.banxaStart(true) }, btnTxt: t('labelBanxaCreate'), }} /> ), height: 'auto', onClose: () => { banxaService.banxaEnd({ reason: OrderENDReason.UserCancel, data: { resource: 'on close' }, }) }, }, [AccountStep.ThirdPanelReturn]: { view: ( ), height: 'auto', }, [AccountStep.CheckingActive]: { view: ( ), height: 'auto', }, [AccountStep.AddAssetGateway]: { height: 'auto', view: ( ), }, [AccountStep.SendAssetGateway]: { view: ( item.type === 'sameLayer')} crossLayerAssetList={sendAssetList.filter((item) => item.type === 'crossLayer')} crossChainAssetList={sendAssetList.filter((item) => item.type === 'crossChain')} toL1Title={isTaiko ? 'To Taiko' : 'To Ethereum'} /> ), height: 'auto', }, [AccountStep.SendAssetFromContact]: { view: , }, [AccountStep.SendNFTGateway]: { view: ( } sendAssetList={sendNFTAssetList} allowTrade={allowTrade} isNotAllowToL1={account.isContract1XAddress} /> ), }, [AccountStep.PayWithCard]: { view: ( ), onBack: onBackReceive, }, [AccountStep.NoAccount]: { view: ( { setShouldShow(false) setShowAccount({ isShow: false }) }, updateDepositHash, clearDepositHash: clearDeposit, ...account, etherscanUrl: rest.etherscanBaseUrl, onSwitch, onCopy, onViewQRCode, onDisconnect, addressShort, }} /> ), onQRClick, height: isLayer1Only ? 'auto' : null, }, [AccountStep.HadAccount]: { view: ( { setShouldShow(false) setShowAccount({ isShow: false }) }, etherscanUrl: rest.etherscanBaseUrl, onViewQRCode, onDisconnect, addressShort, etherscanLink: rest.etherscanBaseUrl + 'address/' + account.accAddress, mainBtn: account.readyState === AccountStatus.ACTIVATED ? lockBtn : unlockBtn, hideVIPlevel: isWebEarn ? true : false, }} /> ), onQRClick, height: isLayer1Only ? 'auto' : null, }, [AccountStep.QRCodeScanner]: { view: , onBack: () => { setShowAccount({ isShow: false }) }, height: 'auto', }, [AccountStep.QRCode]: { view: ( ), onBack: onQRBack, noClose: true, height: 'auto', }, [AccountStep.Deposit_Sign_WaitForRefer]: { view: ( ), }, [AccountStep.Deposit_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.Deposit_Approve_Denied]: { view: ( { depositServices.depositERC20() }, }} {...{ ...rest, account, t, }} title={isEarn ? 'labelDeposit' : 'labelL1toL2'} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.Deposit_WaitForAuth]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.Deposit_Denied]: { view: ( { depositServices.depositERC20() }, }} {...{ ...rest, account, t, }} title={isEarn ? 'labelDeposit' : 'labelL1toL2'} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.Deposit_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.deposit, }, }) }, })} {...{ ...rest, account, error: isShowAccount.error, t, }} title={isEarn ? 'labelDeposit' : 'labelL1toL2'} /> ), onBack: !depositProps.isAllowInputToAddress ? () => { setShowAccount({ isShow: false }) setShowDeposit({ isShow: true }) } : undefined, }, [AccountStep.Deposit_Submit]: { view: ( { setShowAccount({ isShow: false }) setShowDeposit({ isShow: true, symbol: (rest as any)?.symbol ?? isShowAccount?.info?.symbol ?? 'LRC', }) }, }} {...{ ...rest, account, t, }} title={isEarn ? 'labelDeposit' : 'labelL1toL2'} /> ), }, [AccountStep.NFTDeposit_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.NFTDeposit_Approve_Denied]: { view: ( { depositServices.depositNFT() // setShowAccount({ isShow: false }); }, }} {...{ ...rest, account, ...nftDepositValue, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeposit_WaitForAuth]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeposit_Denied]: { view: ( { depositServices.depositNFT() }, }} {...{ ...rest, account, ...nftDepositValue, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeposit_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.nftDeposit, }, }) }, })} {...{ ...rest, account, ...nftDepositValue, error: isShowAccount.error, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeposit_Submit]: { view: ( { setShowAccount({ isShow: false }) history.push(`${RouterPath.nft}/${NFTSubRouter.depositNFT}`) }, }} {...{ ...rest, account, ...nftDepositValue, t, }} /> ), }, [AccountStep.NFTMint_WaitForAuth]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTMint_Denied]: { view: ( { if (isShowAccount.info?.isAdvanceMint) { nftMintAdvanceRetryBtn() } else { mintService.goMintConfirm() } }, }} {...{ ...rest, account, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTMint_First_Method_Denied]: { view: ( { if (isShowAccount.info?.isAdvanceMint) { nftMintAdvanceRetryBtn(true) } else { mintService.goMintConfirm(true) } }, }} symbol={isShowAccount.info?.name} value={isShowAccount.info?.value} {...{ ...rest, account, t, }} /> ), }, [AccountStep.NFTMint_In_Progress]: { view: ( ), }, [AccountStep.NFTMint_Failed]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTMint_Success]: { view: ( { setShowAccount({ isShow: false }) if (isShowAccount.info?.lastStep === LAST_STEP.nftMint) { history.push( `${RouterPath.nft}/${NFTSubRouter.mintNFT}/${isShowAccount.info?.collection?.contractAddress}`, ) } else { history.push(`${RouterPath.nft}/${NFTSubRouter.mintNFTAdvance}`) } }, }} symbol={isShowAccount.info?.name} value={isShowAccount.info?.value} {...{ t, ...rest, account, link: isShowAccount?.info?.hash ? { name: 'Txn Hash', url: isShowAccount?.info?.hash, } : undefined, }} /> ), }, [AccountStep.RedPacketSend_WaitForAuth]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.RedPacketSend_First_Method_Denied]: { view: ( { creatRedPacketRetryBtn(true) }, }} {...{ ...rest, account, ...nftDeployValue, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.RedPacketSend_In_Progress]: { view: ( ), }, [AccountStep.RedPacketSend_User_Denied]: { view: ( { creatRedPacketRetryBtn() }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.RedPacketSend_Failed]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.RedPacketSend_Success]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.RedPacketOpen_In_Progress]: { view: ( ), }, [AccountStep.RedPacketOpen_Failed]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.RedPacketOpen_Claim_In_Progress]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.RedPacketSend_Claim_Success]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.RedPacketOpen_Claim_Failed]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeploy_WaitForAuth]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeploy_Denied]: { view: ( { nftDeployProps.onNFTDeployClick(nftDeployValue as any) }, }} {...{ ...rest, account, ...nftDeployValue, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeploy_First_Method_Denied]: { view: ( { nftDeployProps.onNFTDeployClick(nftDeployValue as any, false) }, }} {...{ ...rest, account, ...nftDeployValue, t, }} /> ), }, [AccountStep.NFTDeploy_In_Progress]: { view: ( ), }, [AccountStep.NFTDeploy_Failed]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.NFTDeploy_Submit]: { view: ( ), onBack: () => { setShowAccount({ isShow: false }) }, }, [AccountStep.ForceWithdraw_WaitForAuth]: { view: ( ), // onBack: () => { // setShowAccount({ isShow: false }); // }, }, [AccountStep.ForceWithdraw_Denied]: { view: ( { forceWithdrawRetry() }, }} {...{ ...rest, account, ...forceWithdrawValue, t, }} /> ), // onBack: () => { // setShowAccount({ isShow: false }); // }, }, [AccountStep.ForceWithdraw_First_Method_Denied]: { view: ( { // setShowAccount({ isShow: false }); forceWithdrawRetry(true) }, }} {...{ ...rest, account, ...forceWithdrawValue, t, }} /> ), }, [AccountStep.ForceWithdraw_In_Progress]: { view: ( ), }, [AccountStep.ForceWithdraw_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.forceWithdraw, }, }) }, })} {...{ ...rest, account, ...forceWithdrawValue, error: isShowAccount.error, t, }} /> ), // onBack: () => { // setShowAccount({ isShow: false }); // }, }, [AccountStep.ForceWithdraw_Submit]: { view: ( ), }, // ClaimWithdraw [AccountStep.ClaimWithdraw_WaitForAuth]: { view: ( ), }, [AccountStep.ClaimWithdraw_Denied]: { view: ( { claimRetryBtn() }, }} {...{ ...rest, account, ...claimValue, t, }} /> ), }, [AccountStep.ClaimWithdraw_First_Method_Denied]: { view: ( { claimRetryBtn(true) }, }} {...{ ...rest, account, ...claimValue, t, }} /> ), }, [AccountStep.ClaimWithdraw_In_Progress]: { view: ( ), }, [AccountStep.ClaimWithdraw_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.claim, }, }) }, })} {...{ ...rest, account, ...claimValue, error: isShowAccount.error, t, }} /> ), }, [AccountStep.ClaimWithdraw_Submit]: { view: ( ), }, [AccountStep.General_Failed]: { view: , }, // transfer [AccountStep.Transfer_WaitForAuth]: { view: ( ), }, [AccountStep.Transfer_First_Method_Denied]: { view: ( { transferProps.onTransferClick(transferValue as any, false) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Transfer_User_Denied]: { view: ( { transferProps.onTransferClick(transferValue as any) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Transfer_In_Progress]: { view: ( ), }, [AccountStep.Transfer_Success]: { view: ( { setShowAccount({ isShow: false }) setShowTransfer({ isShow: true, symbol: isShowAccount?.info?.symbol, }) }, }} {...{ ...rest, account, link: isShowAccount?.info?.hash ? { name: 'Txn Hash', url: isShowAccount?.info?.hash, } : undefined, t, }} /> ), }, [AccountStep.Transfer_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.transfer, }, }) }, })} {...{ ...rest, account, error: isShowAccount.error, t, }} /> ), }, [AccountStep.Transfer_RAMP_WaitForAuth]: { view: ( ), }, [AccountStep.Transfer_RAMP_First_Method_Denied]: { view: ( { const { __request__ } = store.getState()._router_modalData.transferRampValue if (__request__) { processRequestRampTransfer(__request__, false) } else { setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_Failed, }) } }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Transfer_RAMP_User_Denied]: { view: ( { const { __request__ } = store.getState()._router_modalData.transferRampValue if (__request__) { processRequestRampTransfer(__request__, true) } else { setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_Failed, }) } }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Transfer_RAMP_In_Progress]: { view: ( ), }, [AccountStep.Transfer_RAMP_Success]: { view: ( ), }, [AccountStep.Transfer_RAMP_Failed]: { view: ( ), }, // transferBanxa [AccountStep.Transfer_BANXA_WaitForAuth]: { view: ( ), }, [AccountStep.Transfer_BANXA_First_Method_Denied]: { view: ( { const { __request__ } = store.getState()._router_modalData.transferBanxaValue if (__request__) { processRequestRampTransfer(__request__, false) } else { setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_Failed, }) } }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Transfer_BANXA_User_Denied]: { view: ( { const { __request__ } = store.getState()._router_modalData.transferBanxaValue if (__request__) { processRequestRampTransfer(__request__, true) } else { setShowAccount({ isShow: true, step: AccountStep.Transfer_RAMP_Failed, }) } }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Transfer_BANXA_In_Progress]: { view: ( ), }, [AccountStep.Transfer_BANXA_Success]: { view: ( ), }, [AccountStep.Transfer_BANXA_Confirm]: { view: ( ), }, [AccountStep.Transfer_BANXA_Failed]: { view: ( ), }, [AccountStep.NFTBurn_WaitForAuth]: { view: ( ), }, [AccountStep.NFTBurn_First_Method_Denied]: { view: ( { nftTransferProps.onTransferClick(nftTransferValue as any, false) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.NFTBurn_User_Denied]: { view: ( { nftTransferProps.onTransferClick(nftTransferValue as any) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.NFTBurn_In_Progress]: { view: ( ), }, [AccountStep.NFTBurn_Success]: { view: ( ), }, [AccountStep.NFTBurn_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.nftTransfer, }, }) }, })} {...{ ...rest, account, error: isShowAccount.error, t, }} /> ), }, // withdraw [AccountStep.Withdraw_WaitForAuth]: { view: ( ), }, [AccountStep.Withdraw_First_Method_Denied]: { view: ( { withdrawProps.onWithdrawClick(withdrawValue as any, false) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Withdraw_User_Denied]: { view: ( { withdrawProps.onWithdrawClick(withdrawValue as any) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Withdraw_In_Progress]: { view: ( ), }, [AccountStep.Withdraw_Success]: { view: ( { setShowAccount({ isShow: false }) setShowWithdraw({ isShow: true, info: { isToMyself: isShowAccount?.info?.isToMyself ?? false, symbol: isShowAccount?.info?.symbol, }, symbol: isShowAccount?.info?.symbol, }) }, }} {...{ ...rest, account, symbol: isShowAccount?.info?.symbol, // value:isShowAccount?.info?.value, link: isShowAccount?.info?.hash ? { name: 'Txn Hash', url: isShowAccount?.info?.hash, } : undefined, t, }} /> ), }, [AccountStep.Withdraw_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.withdraw, }, }) }, })} {...{ ...rest, symbol: isShowAccount?.info?.symbol, // value:isShowAccount?.info?.value, account, error: isShowAccount.error, t, }} /> ), }, // transfer [AccountStep.NFTTransfer_WaitForAuth]: { view: ( ), }, [AccountStep.NFTTransfer_First_Method_Denied]: { view: ( { nftTransferProps.onTransferClick(nftTransferValue as any, false) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.NFTTransfer_User_Denied]: { view: ( { nftTransferProps.onTransferClick(nftTransferValue as any) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.NFTTransfer_In_Progress]: { view: ( ), }, [AccountStep.NFTTransfer_Success]: { view: ( ), }, [AccountStep.NFTTransfer_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.nftTransfer, }, }) }, })} {...{ ...rest, account, error: isShowAccount.error, t, }} /> ), }, //Burn // withdraw [AccountStep.NFTWithdraw_WaitForAuth]: { view: ( ), }, [AccountStep.NFTWithdraw_First_Method_Denied]: { view: ( { nftWithdrawProps.onWithdrawClick(nftWithdrawValue as any, false) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.NFTWithdraw_User_Denied]: { view: ( { nftWithdrawProps.onWithdrawClick(nftWithdrawValue as any) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.NFTWithdraw_In_Progress]: { view: ( ), }, [AccountStep.NFTWithdraw_Success]: { view: ( ), }, [AccountStep.NFTWithdraw_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.nftWithdraw, }, }) }, })} {...{ ...rest, account, error: isShowAccount.error, t, }} /> ), }, //create account [AccountStep.CreateAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.CreateAccount_EOA_Only_Alert]: { view: ( setShowAccount({ isShow: false })} onConfirm={() => { setShowAccount({ isShow: true, step: AccountStep.CheckingActive, }) }} /> ), height: '450px', }, [AccountStep.CreateAccount_Approve_Denied]: { view: ( ), }, [AccountStep.CreateAccount_Approve_Submit]: { view: ( ), }, [AccountStep.CreateAccount_WaitForAuth]: { view: ( ), }, [AccountStep.CreateAccount_Denied]: { view: ( ), }, [AccountStep.CreateAccount_Failed]: { view: ( ), }, [AccountStep.CreateAccount_Submit]: { view: ( ), }, [AccountStep.UnlockAccount_WaitForAuth]: { view: ( ), }, [AccountStep.UnlockAccount_User_Denied]: { view: ( { unlockAccount() }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.UnlockAccount_Success]: { view: ( ), }, [AccountStep.UnlockAccount_Failed]: { view: ( { setShowAccount({ isShow: true, step: AccountStep.UnlockAccount_Reset_Key_Confirm }) }} {...{ ...rest, account, error: isShowAccount.error, walletType: isShowAccount?.info?.walletType, t, }} /> ), }, [AccountStep.UnlockAccount_Reset_Key_Confirm]: { view: ( { if (walletServices) if (isShowAccount.info && isShowAccount.info.walletType) { const walletType = isShowAccount.info.walletType as sdk.WalletType if (walletType.isContract || walletType.isInCounterFactualStatus) { return } } setShowAccount({ isShow: false }) setShowActiveAccount({ isShow: true, info: { isReset: true, confirmationType: undefined }, }) }} /> ), height: 510, }, [AccountStep.ResetAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.ResetAccount_First_Method_Denied]: { view: ( { activeAccountProps.onResetClick({ isReset: true, isNotFirstTime: false, }) }, }} {...{ ...rest, account, t, }} /> ), onBack: () => { setShowAccount({ isShow: false }) setShowResetAccount({ isShow: true }) }, }, [AccountStep.ResetAccount_User_Denied]: { view: ( { activeAccountProps.onResetClick({ isReset: true, }) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.ResetAccount_Success]: { view: ( ), }, [AccountStep.ResetAccount_Failed]: { view: ( { setShouldShow(false) setShowAccount({ isShow: false }) setShowActiveAccount({ isShow: false }) }, }} {...{ ...rest, account, error: isShowAccount.error, t, }} /> ), }, [AccountStep.UpdateAccount_SmartWallet_NotSupported_Alert]: { view: ( { setShowAccount({ isShow: false }) setShowActiveAccount({ isShow: false }) }, }} /> ), }, //update account [AccountStep.UpdateAccount]: { view: ( { setShowAccount({ isShow: false }) setShowActiveAccount({ isShow: true }) // goUpdateAccount({}); }} {...{ ...rest, account, t }} /> ), onQRClick, }, [AccountStep.UpdateAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.UpdateAccount_First_Method_Denied]: { view: ( { activeAccountProps.onResetClick({ isNotFirstTime: true }) // goUpdateAccount({ isFirstTime: false }); }, }} {...{ ...rest, account, t, }} /> ), onBack: () => { setShowAccount({ isShow: true, step: AccountStep.CreateAccount_EOA_Only_Alert }) // backToUpdateAccountBtnInfo.callback(); }, }, [AccountStep.UpdateAccount_User_Denied]: { view: ( { activeAccountProps.onResetClick({}) }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.UpdateAccount_Success]: { view: ( ), }, [AccountStep.UpdateAccount_Failed]: { view: ( { setShouldShow(false) const isSpecialActivation = isEarn && SPECIAL_ACTIVATION_NETWORKS.includes(defaultNetwork) if (isSpecialActivation) { setShowAccount({ isShow: false }) } else { setShowAccount({ isShow: false }) setShowActiveAccount({ isShow: false }) } }, }} {...{ ...rest, account, error: isShowAccount.error, t, }} /> ), }, [AccountStep.ExportAccount_Approve_WaitForAuth]: { view: ( ), }, [AccountStep.ExportAccount_User_Denied]: { view: ( ), }, [AccountStep.ExportAccount_Success]: { view: ( ), }, [AccountStep.ExportAccount_Failed]: { view: ( ), }, [AccountStep.Dual_Success]: { view: ( { if (isWebEarn) { setShowAccount({ isShow: false }) history.push('/l2assets/assets/Invests') } else { setShowAccount({ isShow: false }) history.push(`${RouterPath.invest}/${InvestRouter[InvestType.MyBalance]}`) } }, }} {...{ ...rest, account, t, }} /> ), }, [AccountStep.Dual_Failed]: { view: ( ), }, [AccountStep.Staking_Redeem_Failed]: { view: ( ), }, [AccountStep.Staking_Redeem_Success]: { view: ( ), }, [AccountStep.Staking_Success]: { view: ( ), }, [AccountStep.Staking_Failed]: { view: ( ), }, [AccountStep.BtradeSwap_Delivering]: { view: ( ), height: 'auto', }, [AccountStep.BtradeSwap_Pending]: { view: ( ), height: 'auto', }, [AccountStep.BtradeSwap_Settled]: { view: ( ), height: 'auto', }, [AccountStep.BtradeSwap_Failed]: { view: ( ), height: 'auto', }, [AccountStep.AMM_Pending]: { view: ( ), height: 'auto', }, [AccountStep.VaultTrade_Success]: { view: ( ), }, [AccountStep.VaultTrade_Failed]: { view: ( ), }, [AccountStep.VaultTrade_In_Progress]: { view: ( ), }, [AccountStep.VaultJoin_Success]: { view: ( ), }, [AccountStep.VaultJoin_Failed]: { view: ( ), }, [AccountStep.VaultJoin_In_Progress]: { view: ( ), }, [AccountStep.VaultRedeem_Success]: { view: ( ), }, [AccountStep.VaultRedeem_Failed]: { view: ( ), }, [AccountStep.VaultRedeem_In_Progress]: { view: ( ), }, [AccountStep.VaultBorrow_Success]: { view: ( ), }, [AccountStep.VaultBorrow_Failed]: { view: ( ), }, [AccountStep.VaultBorrow_In_Progress]: { view: ( ), }, [AccountStep.VaultRepay_Success]: { view: ( ), }, [AccountStep.VaultRepay_Failed]: { view: ( ), }, [AccountStep.VaultRepay_In_Progress]: { view: ( ), }, [AccountStep.VaultDustCollector_Success]: { view: ( ), }, [AccountStep.VaultDustCollector_Failed]: { view: ( ), }, [AccountStep.VaultDustCollector_In_Progress]: { view: ( ), }, [AccountStep.Taiko_Farming_Lock_Success]: { view: ( ), }, [AccountStep.Taiko_Farming_Lock_Failed]: { view: ( ), }, [AccountStep.Taiko_Farming_Redeem_In_Progress]: { view: ( ), }, [AccountStep.Taiko_Farming_Redeem_Success]: { view: ( ), }, [AccountStep.Taiko_Farming_Redeem_Failed]: { view: ( ), }, [AccountStep.Taiko_Farming_Mint_Success]: { view: ( ), }, [AccountStep.Taiko_Farming_Mint_Failed]: { view: ( ), }, [AccountStep.Taiko_Farming_Mint_In_Progress]: { view: ( ), }, [AccountStep.Transfer_To_Taiko_User_Denied]: { view: ( { transferToTaikoProps.retrySend() }, }} {...{ ...rest, account, t, title: isShowAccount.info?.isToEthereum ? 'Send to Ethereum' : 'Send to Taiko', }} /> ), }, [AccountStep.Transfer_To_Taiko_In_Progress]: { view: ( ), }, [AccountStep.Transfer_To_Taiko_Success]: { view: ( { setShowAccount({ isShow: false }) setShowTransferToTaikoAccount({ isShow: true, from: 'send', }) }, }} {...{ ...rest, account, link: isShowAccount?.info?.hash ? { name: 'Txn Hash', url: isShowAccount?.info?.hash, } : undefined, t, title: isShowAccount.info?.isToEthereum ? 'Send to Ethereum' : 'Send to Taiko', }} /> ), }, [AccountStep.Transfer_To_Taiko_Failed]: { view: ( { setShowAccount({ ...isShowAccount, isShow: false, info: { ...isShowAccount.info, lastFailed: LAST_STEP.transfer, }, }) }, })} {...{ ...rest, account, error: isShowAccount.error, t, title: isShowAccount.info?.isEthereum ? 'Send to Ethereum' : 'Send to Taiko', }} /> ), }, [AccountStep.Coinbase_Smart_Wallet_Password_Intro]: { view: , }, [AccountStep.Coinbase_Smart_Wallet_Password_Set]: { view: , height: '440px', }, [AccountStep.Coinbase_Smart_Wallet_Password_Set_Confirm]: { view: ( ), noClose: true, }, [AccountStep.Coinbase_Smart_Wallet_Password_Set_Processing]: { view: ( ), height: '450px', }, [AccountStep.Coinbase_Smart_Wallet_Password_Set_Error]: { view: ( ), }, [AccountStep.Coinbase_Smart_Wallet_Password_Get_Error]: { view: ( ), }, [AccountStep.Coinbase_Smart_Wallet_Password_Input]: { view: , }, [AccountStep.Coinbase_Smart_Wallet_Password_Forget_Password_Confirm]: { view: ( ), height: '520px', }, [AccountStep.Coinbase_Smart_Wallet_Password_Forget_Password]: { view: ( ), height: '450px', }, }) }, [ activeAccountProps, resetProps, checkActiveStatusProps, account, isShowAccount.info, isShowAccount.error, addAssetList, allowTrade, depositProps.isNewAccount, depositProps.isAllowInputToAddress, depositProps.tradeData.belong, depositProps.tradeData.tradeValue, sendAssetList, sendNFTAssetList, vendorListBuy, campaignTagConfig, onBackReceive, chainInfos, isLayer1Only, updateDepositHash, clearDeposit, rest, onSwitch, onCopy, onViewQRCode, onDisconnect, addressShort, onQRClick, lockBtn, unlockBtn, t, onQRBack, closeBtnInfo, nftDepositValue, nftDeployValue, setShowAccount, setShowDeposit, creatRedPacketRetryBtn, nftMintAdvanceRetryBtn, nftDeployProps, forceWithdrawRetry, transferProps, transferValue, processRequestRampTransfer, withdrawProps, withdrawValue, nftTransferProps, nftTransferValue, nftWithdrawProps, nftWithdrawValue, setShowActiveAccount, disbaleList, ]) const currentModal = accountList[isShowAccount.step] return { nftDeployProps, nftTransferProps, nftWithdrawProps, transferProps, withdrawProps, nftBurnProps, claimProps, depositProps, resetProps, collectionAdvanceProps, sideStackRedeemProps: stakeWrapProps, activeAccountProps, exportAccountProps, exportAccountAlertText, exportAccountToastOpen, setExportAccountToastOpen, copyToastOpen, setCopyToastOpen, openQRCode, setOpenQRCode, isShowAccount, account, closeBtnInfo, accountList, currentModal, onBackReceive, onBackSend, contactAddProps, transferToTaikoProps } } ================================================ FILE: packages/core/src/modal/AccountModal/index.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { DepositProps, Modal, ModalAccount, ModalPanel, ModalQRCode, Toast, ToastType, useOpenModals, ETHStakingDetail, AccountStep, } from '@loopring-web/component-lib' import { useStakingAprTrend, useSystem } from '@loopring-web/core' import { useAccountModalForUI } from './hook' import { Account, AssetsRawDataItem, SendAssetList, TOAST_TIME, } from '@loopring-web/common-resources' export const ModalAccountInfo = withTranslation('common')( ({ // onClose, etherscanBaseUrl, open, assetsRawData, isLayer1Only, depositProps, hideDepositWithdrawBack, isWebEarn, t, ...rest }: { open: boolean isLayer1Only?: boolean account: Account depositProps: DepositProps etherscanBaseUrl: string assetsRawData: AssetsRawDataItem[] hideDepositWithdrawBack?: boolean isWebEarn?: boolean } & WithTranslation) => { const { baseURL } = useSystem() const { modals: { isShowAccount, isShowGlobalToast, isShowETHStakingApr }, setShowAccount, setShowDeposit, setShowTransfer, setShowWithdraw, setShowGlobalToast, setShowETHStakingApr, } = useOpenModals() const stakingAprProps = useStakingAprTrend() const { exportAccountAlertText, exportAccountToastOpen, setExportAccountToastOpen, setCopyToastOpen, setOpenQRCode, account, collectionAdvanceProps, nftBurnProps, transferProps, withdrawProps, nftTransferProps, nftWithdrawProps, nftDeployProps, resetProps, claimProps, activeAccountProps, exportAccountProps, // dualTradeProps, sideStackRedeemProps, copyToastOpen, openQRCode, accountList, currentModal, onBackReceive, onBackSend, contactAddProps, transferToTaikoProps, // toastOpen, // closeToast, } = useAccountModalForUI({ t, assetsRawData, depositProps, etherscanBaseUrl, isLayer1Only, isWebEarn, ...rest, }) return ( <> { setExportAccountToastOpen(false) }} severity={ToastType.success} /> setShowGlobalToast({ isShow: false, info: { content: '', messageKey: '', type: ToastType.info, }, }) } /> { if (transferProps.isFromContact) { setShowTransfer({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.SendAssetFromContact, info: { ...transferProps.contact, isENSWrong: false, select: SendAssetList.SendAssetToL2.key, }, }) } else { setShowTransfer({ isShow: false }) onBackSend() } }, }} contactAddProps={contactAddProps} withdrawProps={{ ...withdrawProps, onBack: hideDepositWithdrawBack ? undefined : () => { if (withdrawProps.isFromContact) { setShowWithdraw({ isShow: false }) setShowAccount({ isShow: true, step: AccountStep.SendAssetFromContact, info: { ...withdrawProps.contact, isENSWrong: false, select: SendAssetList.SendAssetToOtherL1.key, }, }) } else { setShowWithdraw({ isShow: false }) onBackSend() } }, }} depositProps={{ ...depositProps, onBack: hideDepositWithdrawBack ? undefined : () => { setShowDeposit({ isShow: false }) onBackReceive() }, }} collectionAdvanceProps={collectionAdvanceProps as any} nftTransferProps={ { ...nftTransferProps, } as any } nftBurnProps={nftBurnProps} nftWithdrawProps={nftWithdrawProps as any} nftDeployProps={nftDeployProps as any} claimProps={claimProps as any} resetProps={resetProps as any} activeAccountProps={activeAccountProps as any} exportAccountProps={exportAccountProps} assetsData={assetsRawData} setExportAccountToastOpen={setExportAccountToastOpen} account={account} sideStackRedeemProps={sideStackRedeemProps as any} transferToTaikoProps={transferToTaikoProps} {...{ _height: 'var(--modal-height)', _width: 'var(--modal-width)' }} /> { setCopyToastOpen(false) }} severity={ToastType.success} /> setOpenQRCode(false)} title={'ETH Address'} description={account?.accAddress} url={account?.accAddress} /> { setShowAccount({ isShow: false }) currentModal?.onClose && currentModal?.onClose() }} panelList={accountList} onBack={currentModal?.onBack} onQRClick={currentModal?.onQRClick} step={isShowAccount.step} etherscanBaseUrl={etherscanBaseUrl} isLayer2Only={isLayer1Only} noClose={currentModal?.noClose} /> setShowETHStakingApr({ ...isShowETHStakingApr, isShow: false })} content={} /> ) }, ) ================================================ FILE: packages/core/src/modal/AmmPoolModal/components/ammPanel.tsx ================================================ import React from 'react' import { AmmPanel, ConfirmAmmExitMiniOrder, TOASTOPEN } from '@loopring-web/component-lib' import { Grid } from '@mui/material' import { useAccount, useAmmMap, usePageAmmPool, walletLayer2Service } from '../../../index' import styled from '@emotion/styled' import { useAmmJoin } from '../../../hooks/useractions/hookAmmJoin' import { useAmmExit } from '../../../hooks/useractions/hookAmmExit' import { SagaStatus, AmmPanelType } from '@loopring-web/common-resources' export const BoxWrapperStyled = styled(Grid)` background: var(--color-box); border-radius: ${({ theme }) => theme.unit}px; .divider-item { border-right: 0; } @media only screen and (min-width: 900px) { .divider-item { height: 0; padding-top: 42%; margin-left: 8px; border-right: 1px solid var(--color-divide); } } ` as typeof Grid export const AmmPanelView = ({ market, ammType, getRecentAmmPoolTxs, updateAmmPoolSnapshot, refreshRef, setToastOpen, // getFee, updateExitFee, updateJoinFee, // ammExit, // ammJoin, ...rest }: { market: string refreshRef: React.Ref updateAmmPoolSnapshot: () => void setToastOpen: (state: TOASTOPEN) => void // getFee: () => void; ammType?: keyof typeof AmmPanelType getRecentAmmPoolTxs?: (props: { limit?: number; offset?: number }) => void updateExitFee: () => Promise updateJoinFee: () => Promise // ammExit: any; // ammJoin: any; } & any) => { const [confirmExitSmallOrder, setConfirmExitSmallOrder] = React.useState<{ open: boolean type: 'Disabled' | 'Mini' }>({ open: false, type: 'Disabled' }) const { ammMap } = useAmmMap() const [index, setIndex] = React.useState(ammType == 1 ? AmmPanelType.Exit : AmmPanelType.Join) const handleTabChange = React.useCallback( (newValue: any) => { if (index !== newValue) { setIndex(newValue) } }, [index], ) const { ammCalcData: ammCalcDataDeposit, ammData: ammJoinData, handleAmmPoolEvent: handleJoinAmmPoolEvent, onAmmClick: onAmmAddClick, btnStatus: addBtnStatus, btnI18nKey: ammDepositBtnI18nKey, propsAExtends, propsBExtends, } = useAmmJoin({ updateJoinFee, setToastOpen, market, refreshRef, }) const { ammCalcData: ammCalcDataWithdraw, ammData: ammExitData, handleAmmPoolEvent: handleExitAmmPoolEvent, onAmmClick: onAmmRemoveClick, btnStatus: removeBtnStatus, btnI18nKey: ammWithdrawBtnI18nKey, exitSmallOrderCloseClick, propsLPExtends, } = useAmmExit({ updateExitFee, setToastOpen, market, refreshRef, // ammCalcDefault: ammExit.ammCalcData, // ammDataDefault: ammExit.ammData, setConfirmExitSmallOrder, }) const { resetAmmPool } = usePageAmmPool() const { status: accountStatus } = useAccount() React.useEffect(() => { if (refreshRef.current) { // @ts-ignore refreshRef.current.firstElementChild.click() } return () => { resetAmmPool() } }, []) React.useEffect(() => { if (accountStatus === SagaStatus.UNSET) { walletLayer2Service.sendUserUpdate() } }, [accountStatus]) return ( <> { setConfirmExitSmallOrder({ open: false, type: 'Disabled' }) exitSmallOrderCloseClick(isAgree) }} open={confirmExitSmallOrder.open} /> { updateAmmPoolSnapshot() }} tabSelected={ammType ? ammType : AmmPanelType.Join} ammInfo={ammMap['AMM-' + market]} refreshRef={refreshRef} ammType={index} handleTabChange={handleTabChange} ammDepositData={ammJoinData} ammCalcDataDeposit={ammCalcDataDeposit} handleAmmAddChangeEvent={handleJoinAmmPoolEvent} onAmmAddClick={onAmmAddClick} ammDepositBtnI18nKey={ammDepositBtnI18nKey} ammDepositBtnStatus={addBtnStatus} propsAExtends={propsAExtends} propsBExtends={propsBExtends} ammWithdrawData={ammExitData} ammCalcDataWithDraw={ammCalcDataWithdraw} handleAmmRemoveChangeEvent={handleExitAmmPoolEvent} onAmmRemoveClick={onAmmRemoveClick} ammWithdrawBtnI18nKey={ammWithdrawBtnI18nKey} ammWithdrawBtnStatus={removeBtnStatus} propsLPExtends={propsLPExtends} /> ) } ================================================ FILE: packages/core/src/modal/AmmPoolModal/components/ammRecordPanel.tsx ================================================ import React from 'react' import { StylePaper } from '../../../component' import { BoxProps, Divider, Tab, Tabs } from '@mui/material' import { AmmRecordTable, useSettings } from '@loopring-web/component-lib' import { AccountStatus, RowConfig } from '@loopring-web/common-resources' import styled from '@emotion/styled' import { useTranslation } from 'react-i18next' import { store, useSystem } from '../../../stores' import { useAmmRecord } from '../hooks' const TabsStyled = styled(Tabs)` padding-left: ${({ theme }) => theme.unit}px; ` const AMMPanelStyled = styled(StylePaper)` background: initial; && { margin-bottom: 0; } .amm-record-table { .rdg { ${({ isMobile }) => !isMobile ? `--template-columns: 50% 30% 20% !important;` : `--template-columns: 86% 14% !important;`} } } ` as (props: BoxProps & { isMobile: boolean }) => JSX.Element const applyProps = (index: number) => { return { id: `simple-tab-${index}`, 'aria-controls': `tabpanel-${index}`, } } export const AmmRecordPanel = ({ market }: { market: string }) => { const { isMyAmmLoading, isRecentLoading, ammMarketArray, container, myAmmMarketArray, ammUserTotal, getUserAmmPoolTxs, getRecentAmmPoolTxs, pageSize, } = useAmmRecord({ market }) const [tabIndex, setTabIndex] = React.useState<0 | 1>(0) const { t } = useTranslation('common') const { currency, isMobile } = useSettings() const { forexMap } = useSystem() const tableHeight = RowConfig.rowHeaderHeight + (tabIndex === 0 ? 15 : 14) * RowConfig.rowHeight const handleTabsChange = React.useCallback((_: any, value: 0 | 1) => { setTabIndex(value) }, []) React.useEffect(() => { if (container.current) { if (tabIndex == 0) { getRecentAmmPoolTxs({}) } else if (store.getState().account.readyState === AccountStatus.ACTIVATED && tabIndex == 1) { getUserAmmPoolTxs({}) } } }, [tabIndex, container]) return ( {/*ammRecordArray*/} {tabIndex === 0 ? ( ) : ( )} ) } ================================================ FILE: packages/core/src/modal/AmmPoolModal/components/chartAndInfo.tsx ================================================ import { Box, BoxProps, Divider, Grid, Typography } from '@mui/material' import { ChartType, floatTag, PopoverPure, ScaleAreaChart, StyledProps, useSettings, AmmPairDetail, CoinIcons, LoadingBlock, ToastType, EmptyDefault, } from '@loopring-web/component-lib' import { AmmHistoryItem, CurrencyToTag, CustomError, EmptyValueTag, FloatTag, getValuePrecisionThousand, myLog, PriceTag, SDK_ERROR_MAP_TO_UI, SoursURL, TokenType, UIERROR_CODE, UpColor, } from '@loopring-web/common-resources' import { useTranslation } from 'react-i18next' import { useAmmMap, useSystem, useTicker, useTokenMap, useUserRewards } from '../../../stores' import { BoxWrapperStyled } from './ammPanel' import { bindHover, bindPopper } from 'material-ui-popup-state' import { usePopupState } from 'material-ui-popup-state/hooks' import styled from '@emotion/styled' import React from 'react' import { LoopringAPI } from '../../../api_wrapper' import { TradingInterval } from '@loopring-web/loopring-sdk' import * as sdk from '@loopring-web/loopring-sdk' import moment from 'moment' import _ from 'lodash' const BoxStyle = styled(Box)` ${({ theme, custom }) => floatTag({ theme, custom })}; ` as (props: StyledProps & BoxProps) => JSX.Element export const ChartAndInfoPanel = ({ setToastOpen, market, }: // myAmm, { market: string setToastOpen: (props: { open: boolean; content: JSX.Element | string; type: ToastType }) => void }) => { const { t } = useTranslation('common') const { ammMap } = useAmmMap() const { tokenMap } = useTokenMap() const { tickerMap } = useTicker() const ammInfo = ammMap['AMM-' + market] const { myAmmLPMap } = useUserRewards() const [pairHistory, setPairHistory] = React.useState([]) const getPairList = React.useCallback(async () => { if (LoopringAPI.exchangeAPI) { try { const response = await LoopringAPI.exchangeAPI.getMixCandlestick({ market, interval: TradingInterval.d1, limit: 30, }) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { // myLog("getMixCandlestick", response); throw new CustomError( // @ts-ignore SDK_ERROR_MAP_TO_UI[(response as sdk.RESULT_INFO)?.code ?? UIERROR_CODE.UNKNOWN], ) } const formattedPairHistory = response.candlesticks .map((o) => ({ ...o, timeStamp: o.timestamp, date: moment(o.timestamp).format('MMM DD'), })) .sort((a, b) => a.timeStamp - b.timeStamp) setPairHistory(formattedPairHistory) } catch (error: any) { setPairHistory(undefined) setToastOpen({ open: true, type: ToastType.error, content: t('labelAMMChartFailed') + error?.message, }) myLog('getMixCandlestick', 'timeout', error) } } }, [market]) const myAmm = myAmmLPMap && myAmmLPMap[market ?? ''] const popState = usePopupState({ variant: 'popover', popupId: `popup-My-LP`, }) const { upColor, coinJson, currency } = useSettings() const { forexMap } = useSystem() const precisionA = tokenMap[ammInfo?.coinA ?? '']?.precision const precisionB = tokenMap[ammInfo?.coinB ?? '']?.precision const ticker = tickerMap[market] const { balanceAStr, balanceBStr, balanceU: myBalanceDollar } = myAmm ?? {} const tradeFloatType = ticker?.changeU === 0 || !ticker?.changeU ? FloatTag.none : ticker.changeU < 0 ? FloatTag.decrease : FloatTag.increase React.useEffect(() => { const timer = _.delay(getPairList, 0) return () => { clearTimeout(timer) } }, []) return ( {ammInfo.market ? ( <> {pairHistory?.length ? ( ) : pairHistory == undefined || pairHistory?.length == 0 ? ( { return t('labelAMMChartFailed') }} /> ) : ( {'loading'} )} {ticker?.change === 0 || typeof ticker?.change === 'undefined' ? EmptyValueTag : (tradeFloatType === FloatTag.increase ? '+' : '') + getValuePrecisionThousand(ticker?.change, 2, 2, 2, true) + '%'} {getValuePrecisionThousand(ammInfo.totalA, precisionA, precisionA)} {ammInfo.coinA} {getValuePrecisionThousand(ammInfo.totalB, precisionB, precisionB)} {ammInfo.coinB} {t('labelTVL')} {typeof ammInfo.amountU === 'undefined' ? EmptyValueTag : PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(ammInfo.amountU ?? 0).times(forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: true, isAbbreviate: true }, )} {t('label24Volume')} {ticker?.priceU ? PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( sdk.toBig(ticker?.priceU ?? 0).times(forexMap[currency] ?? 0), undefined, undefined, undefined, true, { isFait: true, floor: false, isAbbreviate: true, abbreviate: 6, }, ) : EmptyValueTag} {t('labelAPR')} {ammInfo.APR ? getValuePrecisionThousand(ammInfo.APR, 2, 2, undefined, true) + '%' : EmptyValueTag} {t('labelMe')} {typeof myBalanceDollar === 'undefined' ? ( {EmptyValueTag} ) : ( {PriceTag[CurrencyToTag[currency]] + getValuePrecisionThousand( (myBalanceDollar || 0) * (forexMap[currency] ?? 0), undefined, undefined, 2, true, { isFait: true, floor: true }, )} )} ) : ( )} ) } ================================================ FILE: packages/core/src/modal/AmmPoolModal/hooks.ts ================================================ import React from 'react' import { useTokenPrices, useAmmMap, getRecentAmmTransaction, makeMyAmmMarketArray, volumeToCount, getUserAmmTransaction, } from '../../index' // import _ from "lodash"; import { AmmRecordRow, // useOpenModals } from '@loopring-web/component-lib' // import * as sdk from "@loopring-web/loopring-sdk"; // import { useAmmCommon } from "../../hooks/useractions/hookAmmCommon"; import { AmmPoolTx, UserAmmPoolTx } from '@loopring-web/loopring-sdk' import { RowConfig } from '@loopring-web/common-resources' // import moment from "moment"; export type AwardItme = { start: string end: string market: string accountId: number awardList: { token?: string volume?: number }[] } export const useAmmRecord = ({ market }: { market: string }) => { const { ammMap } = useAmmMap() const container = React.useRef(null) const [isMyAmmLoading, setIsMyAmmLoading] = React.useState(false) const [isRecentLoading, setIsRecentLoading] = React.useState(false) const [ammMarketArray, setAmmMarketArray] = React.useState[]>([]) const [ammTotal, setAmmTotal] = React.useState(0) const [ammUserTotal, setAmmUserTotal] = React.useState(0) const [pageSize, setPageSize] = React.useState(14) const { tokenPrices } = useTokenPrices() const [myAmmMarketArray, setMyAmmMarketArray] = React.useState[]>([]) React.useEffect(() => { // @ts-ignore let height = container?.current?.offsetHeight if (height) { // const pageSize = setPageSize(Math.floor((height - RowConfig.rowHeight * 2) / RowConfig.rowHeight) - 1) // getUserAmmPoolTxs() } }, [container]) const getUserAmmPoolTxs = React.useCallback( ({ limit = pageSize, offset = 0 }) => { // limit = pageSize; if (ammMap) { const addr = ammMap['AMM-' + market]?.address if (addr) { setIsMyAmmLoading(true) getUserAmmTransaction({ address: addr, limit: limit, offset, txStatus: 'processed', })?.then( (res: { userAmmPoolTxs: UserAmmPoolTx[]; totalNum: React.SetStateAction }) => { let _myTradeArray = makeMyAmmMarketArray(market, res.userAmmPoolTxs) const formattedArray = _myTradeArray.map((o: any) => { const market = `LP-${o.coinA.simpleName}-${o.coinB.simpleName}` const formattedBalance = Number(volumeToCount(market, o.totalBalance)) const price = tokenPrices && tokenPrices[market] const totalDollar = ((formattedBalance || 0) * (price || 0)) as any return { ...o, totalDollar: totalDollar, } }) setMyAmmMarketArray(formattedArray || []) setAmmUserTotal(res.totalNum) setIsMyAmmLoading(false) }, ) } } }, [ammMap, market, tokenPrices, pageSize], ) const getRecentAmmPoolTxs = React.useCallback( ({ limit = 15, offset = 0 }) => { if (ammMap) { // const market = list[list.length - 1]; const addr = ammMap['AMM-' + market]?.address if (addr) { setIsRecentLoading(true) getRecentAmmTransaction({ address: addr, limit: limit, offset, })?.then( ({ ammPoolTrades, totalNum }: { ammPoolTrades: AmmPoolTx[]; totalNum: number }) => { let _tradeArray = makeMyAmmMarketArray(market, ammPoolTrades) const formattedArray = _tradeArray.map((o: any) => { const market = `LP-${o.coinA.simpleName}-${o.coinB.simpleName}` const formattedBalance = Number(volumeToCount(market, o.totalBalance)) const price = tokenPrices && tokenPrices[market] const totalDollar = ((formattedBalance || 0) * (price || 0)) as any return { ...o, totalDollar: totalDollar, } }) setAmmMarketArray(formattedArray || []) setAmmTotal(totalNum) setIsRecentLoading(false) }, ) } } }, [ammMap, market, tokenPrices], ) return { container, isMyAmmLoading, isRecentLoading, ammMarketArray, ammTotal, myAmmMarketArray, ammUserTotal, getUserAmmPoolTxs, //handle page change used getRecentAmmPoolTxs, pageSize, setPageSize, } } ================================================ FILE: packages/core/src/modal/AmmPoolModal/index.tsx ================================================ import React from 'react' import { WithTranslation, withTranslation } from 'react-i18next' import { boxLiner, ModalBackButton, ModalCloseButton, SwitchPanelStyled, Toast, ToastType, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { myLog, TOAST_TIME } from '@loopring-web/common-resources' import { Box, Link, Modal as MuiModal } from '@mui/material' import styled from '@emotion/styled' import { store, useAmmMap } from '../../index' import { AmmPanelView } from './components/ammPanel' import { useTheme } from '@emotion/react' import { useAmmCommon } from '../../hooks/useractions/hookAmmCommon' import { ChartAndInfoPanel } from './components/chartAndInfo' import { AmmRecordPanel } from './components/ammRecordPanel' const BoxStyle = styled(Box)` .rdg { background: var(--color-box); border-bottom-left-radius: ${({ theme }) => theme.unit}px; border-bottom-right-radius: ${({ theme }) => theme.unit}px; } ` const BoxLinear = styled(SwitchPanelStyled)` && { ${({ theme }) => boxLiner({ theme })}; .trade-panel { background: initial; .react-swipeable-view-container > div { height: initial; } } @media only screen and (max-height: 680px) { height: 100vh; overflow: scroll; } @media only screen and (max-width: 768px) { height: 86%; overflow: scroll; } } ` const Content = withTranslation('common')( ({ t, market, setPanelIndex, panelIndex, ...rest }: WithTranslation & { market: string } & any) => { const { modals: { isShowAmm: { type }, }, setShowAmm, } = useOpenModals() const { isMobile } = useSettings() const theme = useTheme() const { toastOpen, setToastOpen, closeToast, refreshRef, updateAmmPoolSnapshot, updateExitFee, updateJoinFee, } = useAmmCommon({ market }) myLog('amm type', type) return ( <> {panelIndex === 1 ? ( { setPanelIndex(0) }} /> ) : ( <> {isMobile && ( { setPanelIndex(1) }} > {t('labelAMMTransactionsLink')} )} )} { setShowAmm({ isShow: false }) }} t={t} {...rest} /> {panelIndex === 0 && ( {!isMobile && ( { setPanelIndex(1) }} > {t('labelAMMTransactionsLink')} )} )} {panelIndex === 1 && ( {market && } )} ) }, ) export const ModalCoinPairPanel = () => { const { modals: { isShowAmm: { isShow }, }, setShowAmm, } = useOpenModals() const [panelIndex, setPanelIndex] = React.useState<0 | 1>(0) const { ammMap } = useAmmMap() const [market, setMarket] = React.useState('') React.useEffect(() => { if (isShow) { const { symbol } = store.getState().modals.isShowAmm setMarket((market) => { if (symbol !== market) { return ammMap[`AMM-${symbol}`] ? ammMap[`AMM-${symbol}`].market : 'LRC-ETH' } else { return market == '' ? 'LRC-ETH' : market } }) } return () => { setPanelIndex(0) setMarket('') } }, [isShow]) const { isMobile } = useSettings() return ( { setShowAmm({ isShow: false }) }} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description' > {market && ammMap['AMM-' + market] ? ( ) : ( <> )} ) } ================================================ FILE: packages/core/src/modal/DualModal/index.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { boxLiner, CoinIcons, ConfirmInvestDualAutoRisk, ConfirmInvestDualDipRisk, ConfirmInvestDualGainRisk, ConfirmInvestDualRisk, CountDownIcon, DualWrap, DualWrapProps, ModalCloseButton, SwitchPanelStyled, Toast, TOASTOPEN, ToastType, useOpenModals, useSettings, } from '@loopring-web/component-lib' import { useHistory, useLocation } from 'react-router-dom' import { Box, Divider, Modal as MuiModal, Typography } from '@mui/material' import styled from '@emotion/styled' import { DualInvestConfirmType, DualViewType, TOAST_TIME, TokenType, } from '@loopring-web/common-resources' import { DUAL_TYPE } from '@loopring-web/loopring-sdk' import { confirmation } from '../../stores' import React from 'react' const BoxLinear = styled(SwitchPanelStyled)` && { ${({ theme }) => boxLiner({ theme })}; .trade-panel { background: initial; .react-swipeable-view-container > div { height: initial; } } @media only screen and (max-height: 680px) { height: 100vh; overflow: scroll; } @media only screen and (max-width: 768px) { height: 86%; overflow: scroll; } } ` export const ModalDualPanel = withTranslation('common')( ({ t, viewType, dualTradeProps, dualToastOpen, closeDualToast, isBeginnerMode, confirmDualAutoInvest, setConfirmDualAutoInvest, ...rest }: WithTranslation & { viewType: DualViewType | undefined dualTradeProps: DualWrapProps dualToastOpen?: TOASTOPEN closeDualToast?: (state: boolean) => void isBeginnerMode: boolean confirmDualAutoInvest: boolean setConfirmDualAutoInvest: (state: boolean) => void }) => { const history = useHistory() const { search, pathname } = useLocation() const searchParams = new URLSearchParams(search) const { modals: { isShowDual }, setShowDual, } = useOpenModals() const { isShow, dualInfo } = isShowDual ?? {} const { isMobile, coinJson } = useSettings() const [showDualAlert, setShowAlert] = React.useState(false) React.useEffect(() => { // @ts-ignore if (viewType && viewType !== '') { setShowAlert(true) } }, [viewType]) // const [confirmDualInvest, setConfirmDualInvest] = React.useState( // undefined, // ) // const { confirmation: { confirmDualAutoInvest: _confirmDualAutoInvest, confirmedDualInvestV2, confirmDualDipInvest, confirmDualGainInvest, showAutoDefault }, confirmDualAutoInvest: confirmDualAutoInvestFun, confirmDualInvest: confirmDualInvestFun, confirmDualDipInvest: confirmDualDipInvestFun, confirmDualGainInvest: confirmDualGainInvestFun, setShowAutoDefault, } = confirmation.useConfirmation() return ( <> { setShowDual({ isShow: false, dualInfo: undefined }) }} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description' > { setShowDual({ isShow: false, dualInfo: undefined }) }} t={t} {...rest} /> {dualInfo?.productId && ( <> {/* eslint-disable-next-line react/jsx-no-undef */} {t( dualInfo.__raw__.info.dualType === DUAL_TYPE.DUAL_BASE ? 'labelDualInvestBaseTitle' : 'labelDualInvestQuoteTitle', { symbolA: dualInfo.sellSymbol, symbolB: dualInfo.buySymbol, }, )} )} {/*sx={{ display: "none" }}*/} { if (closeDualToast) { closeDualToast(false) } }} /> { if (!isAgree) { dualTradeProps.onChangeEvent({ tradeData: { ...dualTradeProps.dualCalcData?.coinSell, isRenew: false, } as any, }) } else { dualTradeProps.onChangeEvent({ tradeData: { ...dualTradeProps.dualCalcData?.coinSell, isRenew: true, } as any, }) confirmDualAutoInvestFun() } setConfirmDualAutoInvest(false) }} /> { if (!isAgree) { setShowAlert(false) searchParams.set('viewType', '') history.goBack() history.push(pathname + '?' + searchParams.toString()) } else { confirmDualInvestFun(isAgree) setShowAlert(false) } }} /> { if (!isAgree) { setShowAlert(false) // history.goBack() searchParams.set('viewType', '') history.push(pathname + '?' + searchParams.toString()) } else { confirmDualGainInvestFun() setShowAlert(false) } }} /> { if (!isAgree) { setShowAlert(false) searchParams.set('viewType', '') // history.goBack() history.push(pathname + '?' + searchParams.toString()) } else { confirmDualDipInvestFun() setShowAlert(false) } }} /> ) }, ) ================================================ FILE: packages/core/src/modal/RedPacketModal/hook.tsx ================================================ /* eslint-disable react/jsx-pascal-case */ import { AccountStep, RedPacketDetailProps, RedPacketOpenedProps, RedPacketOpenProps, RedPacketQRCodeProps, RedPacketTimeoutProps, RedPacketClockProps, RedPacketViewStep, useOpenModals, RedPacketDetailLimit, NFTMedia, BoxNFT, RedPacketBlindBoxDetailProps, RedPacketBlindBoxDetailTypes, RedPacketNFTDetailLimit, RedPacketBlindBoxLimit, } from '@loopring-web/component-lib' import React, { useState } from 'react' import { AssetTabIndex, CLAIM_TYPE, CustomError, EmptyValueTag, ErrorMap, Exchange, getShortAddr, getValuePrecisionThousand, myLog, NFTWholeINFO, RouterPath, UIERROR_CODE, YEAR_DAY_MINUTE_FORMAT, } from '@loopring-web/common-resources' import { store, useAccount, useSystem, useTokenMap } from '../../stores' import { amountStrCallback, amountStrNFTCallback, getUserNFTReceiveList, getUserReceiveList, useOpenRedpacket, volumeToCountAsBigNumber, } from '../../hooks' import { useTranslation } from 'react-i18next' import moment from 'moment' import * as sdk from '@loopring-web/loopring-sdk' import { LoopringAPI } from '../../api_wrapper' import { useRedPacketHistory } from '../../stores/localStore/redPacket' import { Box } from '@mui/material' import { getIPFSString } from '../../utils' import { NFT_IMAGE_SIZES, toBig } from '@loopring-web/loopring-sdk' import { useHistory } from 'react-router-dom' import { ClaimCommands, claimServices, redpacketService } from '../../services' export function useRedPacketModal() { const ref = React.createRef() const { modals: { // isShowNFTDetail, isShowRedPacket: { info, isShow, step }, }, setShowRedPacket, setShowAccount, } = useOpenModals() const { account } = useAccount() const { updateRedpacketHash } = useRedPacketHistory() const { chainId, baseURL } = useSystem() const { tokenMap, idIndex, coinMap } = useTokenMap() const { t } = useTranslation('common') const { callOpen } = useOpenRedpacket() const subject = React.useMemo(() => claimServices.onSocket(), []) const [detail, setDetail] = React.useState(undefined) const [blinBoxDetail, setBlindBoxDetail] = React.useState(undefined) const [qrcode, setQrcode] = React.useState(undefined) const ImageEle = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive if (isShow && _info && _info.isNft && _info.nftTokenInfo) { return ( } shouldPlay={true} onNFTError={() => undefined} isOrigin={true} getIPFSString={getIPFSString} baseURL={baseURL} /> ) } }, [info?.isNft, info?.nftDta, info]) const amountStr = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive let symbol if ( isShow && _info && _info.tokenAmount && _info.type.mode !== sdk.LuckyTokenClaimType.BLIND_BOX ) { if (_info.isNft) { symbol = 'NFT' // @ ts-ignore // symbol = (_info.nftTokenInfo as any)?.metadata?.base?.name ?? "NFT"; const amount = getValuePrecisionThousand( _info.tokenAmount.totalAmount, 0, 0, undefined, false, { floor: false, // isTrade: true, }, ) return amount + ' ' + symbol } else { const token = tokenMap[idIndex[_info?.tokenId] ?? ''] const symbol = token.symbol const amount = getValuePrecisionThousand( volumeToCountAsBigNumber(symbol, _info.tokenAmount.totalAmount as any), token.precision, token.precision, undefined, false, { floor: false, // isTrade: true, }, ) return amount + ' ' + symbol } } return '' }, [info?.tokenId, info?.tokenAmount, isShow, info]) const amountClaimStr = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive & { claimAmount: string } let symbol if (isShow && _info && _info.claimAmount) { if (_info.isNft) { symbol = 'NFT' // (_info.nftTokenInfo as any)?.metadata?.base?.name ?? "NFT"; const amount = getValuePrecisionThousand(_info.claimAmount, 0, 0, undefined, false, { floor: false, // isTrade: true, }) return amount + ' ' + symbol } else { const token = tokenMap[idIndex[_info?.tokenId] ?? ''] symbol = token.symbol const amount = getValuePrecisionThousand( volumeToCountAsBigNumber(symbol, _info.claimAmount as any), token.precision, token.precision, undefined, false, { floor: false, // isTrade: true, }, ) return amount + ' ' + symbol } } return '' // tokenMap[] }, [info?.tokenId, info?.claimAmount]) const textSendBy = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive if (isShow && _info && _info.validSince > Date.now()) { const date = moment(new Date(_info.validSince)).format(YEAR_DAY_MINUTE_FORMAT) return t('labelRedPacketStartWithTime', { time: date, }) } else { if (_info && _info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { return '' } else { return t('labelLuckyRedPacketStarted') } } }, [info?.validSince, info?.createdAt]) const redPacketTimeoutProps: RedPacketTimeoutProps | undefined = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive if ( isShow && info && step === RedPacketViewStep.TimeOutPanel // && // (_info.status === sdk.LuckyTokenItemStatus.COMPLETED || // _info.status === sdk.LuckyTokenItemStatus.OVER_DUE) ) { return { ImageEle, memo: _info.info.memo, sender: _info.sender?.ens ? _info.sender?.ens : getShortAddr(_info.sender?.address), viewDetail: () => { if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { LoopringAPI.luckTokenAPI ?.getLuckTokenDetail( { hash: _info.hash, serialNo: _info.serialNo, }, account.apiKey, ) .then((response) => { setShowRedPacket({ isShow, step: RedPacketViewStep.BlindBoxDetail, info: { ...response.detail.luckyToken, }, }) }) } else { LoopringAPI.luckTokenAPI ?.getLuckTokenDetail( { hash: _info.hash, serialNo: _info.serialNo, }, account.apiKey, ) .then((response) => { setShowRedPacket({ isShow, step: RedPacketViewStep.DetailPanel, info: { ...response.detail.luckyToken, }, }) }) } }, } } return undefined }, [info, account.accAddress, isShow, step]) const redPacketOpenProps: RedPacketOpenProps | undefined = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive & { referrer?: string } if (isShow && info && step === RedPacketViewStep.OpenPanel) { if (_info?.status == sdk.LuckyTokenItemStatus.COMPLETED) { } else if (_info.status === sdk.LuckyTokenItemStatus.OVER_DUE) { setShowRedPacket({ isShow, step: RedPacketViewStep.TimeOutPanel, info: _info, }) } else if (_info?.hash) { return { ImageEle, memo: _info.info.memo, amountStr, sender: _info.sender?.ens ? _info.sender?.ens : getShortAddr(_info.sender?.address), viewDetail: info['hideViewDetail'] ? undefined : () => { if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow, step: RedPacketViewStep.BlindBoxDetail, info: _info, }) } else { setShowRedPacket({ isShow, step: RedPacketViewStep.DetailPanel, info: _info, }) } }, onOpen: callOpen, } } } return undefined }, [info, amountStr, account.accAddress, isShow, step]) const redPacketOpenedProps: RedPacketOpenedProps | undefined = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive & { claimAmount?: string } if (isShow && info && step === RedPacketViewStep.OpenedPanel && _info?.hash) { let myAmountStr: string | undefined = undefined let symbol: string if (_info?.claimAmount) { if (_info.isNft) { symbol = 'NFT' // @ts-ignore // symbol = _info.nftTokenInfo?.metadata?.base?.name ?? "NFT"; myAmountStr = getValuePrecisionThousand( volumeToCountAsBigNumber(symbol, _info.claimAmount as any), 0, 0, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + symbol } else { let tokenInfo = tokenMap[idIndex[_info?.tokenId] ?? ''] symbol = tokenInfo.symbol myAmountStr = getValuePrecisionThousand( volumeToCountAsBigNumber(symbol, _info.claimAmount as any), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + symbol } } return { ImageEle, memo: _info.info.memo, amountStr, myAmountStr, sender: _info.sender?.ens ? _info.sender?.ens : getShortAddr(_info.sender?.address), viewDetail: () => { if (_info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX) { setShowRedPacket({ isShow, step: RedPacketViewStep.BlindBoxDetail, info: _info, }) } else { setShowRedPacket({ isShow, step: RedPacketViewStep.DetailPanel, info: _info, }) } }, isBlindBox: _info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX, } } return undefined }, [info, amountClaimStr, amountStr, account.accAddress, isShow, step]) let redPacketClockProps: RedPacketClockProps | undefined = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive & { claimAmount?: string } if (isShow && info && step === RedPacketViewStep.RedPacketClock && _info?.hash) { return { memo: _info.info.memo, amountStr, amountClaimStr, sender: _info.sender?.ens ? _info.sender?.ens : getShortAddr(_info.sender?.address), validSince: _info.validSince, showRedPacket: () => { setShowRedPacket({ isShow: true, step: RedPacketViewStep.OpenPanel, info: _info, }) }, ImageEle, } } return undefined }, [info, amountClaimStr, amountStr, account.accAddress, isShow, step]) const [opendBlindBoxCount, setOpendBlindBoxCount] = React.useState(0) React.useState(undefined) const redPacketDetailCall = React.useCallback( async ({ limit = detail?.luckyToken.isNft ? RedPacketNFTDetailLimit : RedPacketDetailLimit, offset = 0, }: { limit?: number offset?: number }) => { setDetail(undefined) const _info = info as sdk.LuckyTokenItemForReceive & { claimAmount?: string } if (_info?.hash && LoopringAPI.luckTokenAPI) { try { const response = await LoopringAPI.luckTokenAPI.getLuckTokenDetail( { accountId: account.accountId, hash: _info.hash, serialNo: _info.serialNo, limit, offset, // fromId: 0, showHelper: true, } as any, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: (response as sdk.RESULT_INFO)?.code, msg: (response as sdk.RESULT_INFO)?.message, ...(response instanceof Error ? { message: response?.message, stack: response?.stack, } : response ?? {}), }, }) } else { if ( response.detail?.claimAmount?.toString() !== '0' && _info?.type.scope === sdk.LuckyTokenViewType.PUBLIC ) { updateRedpacketHash({ hash: _info?.hash, chainId: chainId as any, luckToken: _info, claimAmount: response.detail.claimAmount.toString(), address: account.accAddress, }) } const detail = (response as any).detail const luckTokenInfo: sdk.LuckyTokenItemForReceive = detail.luckyToken if (luckTokenInfo) { setDetail(detail) // setShowRedPacket({ // isShow: true, // step: RedPacketViewStep.DetailPanel, // }); } else { const error = new CustomError(ErrorMap.ERROR_REDPACKET_EMPTY) setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: UIERROR_CODE.ERROR_REDPACKET_EMPTY, msg: error.message, }, }) } } } catch (error: any) { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: error?.message, // @ts-ignore ...(error instanceof Error ? { message: error?.message, stack: error?.stack, } : error ?? {}), }, }) setShowRedPacket({ isShow: false }) } } }, [isShow, step, info], ) const [blindBoxType, setBlindBoxType] = React.useState( undefined as RedPacketBlindBoxDetailTypes | undefined, ) const [viewDetailFrom, setViewDetailFrom] = React.useState( undefined as RedPacketBlindBoxDetailTypes | undefined, ) const [wonPrizeInfo, setWonPrizeInfo] = React.useState( undefined as | { name: string url: string isNFT: true } | { amountStr: string tokenURL: string tokenName: string isNFT: false } | undefined, ) const redPacketBlindBoxDetailCall = React.useCallback( async ({ limit = RedPacketBlindBoxLimit, offset = 0 }: { limit?: number; offset?: number }) => { setDetail(undefined) setBlindBoxDetail(undefined) setWonPrizeInfo(undefined) const _info = info as sdk.LuckyTokenItemForReceive & { claimAmount?: string } setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Claim_In_Progress, }) if (_info?.hash && LoopringAPI.luckTokenAPI) { try { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_In_Progress, }) const responseTemp = await LoopringAPI.luckTokenAPI.getBlindBoxDetail( { accountId: account.accountId, hash: _info.hash, serialNo: _info.serialNo, limit: 500, offset: 0, showHelper: true, } as any, account.apiKey, ) setOpendBlindBoxCount((responseTemp.raw_data as any).claims.length) let response = await LoopringAPI.luckTokenAPI.getLuckTokenDetail( { accountId: account.accountId, hash: _info.hash, serialNo: _info.serialNo, limit: RedPacketNFTDetailLimit, offset, // fromId: 0, showHelper: true, } as any, account.apiKey, ) if ((response as any).detail.blindBoxStatus !== '') { updateRedpacketHash({ hash: _info?.hash, chainId: chainId as any, luckToken: _info, claimAmount: response.detail.claimAmount.toString(), address: account.accAddress, blindboxClaimed: true, }) } const response2 = await LoopringAPI.luckTokenAPI.getBlindBoxDetail( { accountId: account.accountId, hash: _info.hash, serialNo: _info.serialNo, limit: RedPacketBlindBoxLimit, offset, } as any, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: (response as sdk.RESULT_INFO)?.code, msg: (response as sdk.RESULT_INFO)?.message, ...(response instanceof Error ? { message: response?.message, stack: response?.stack, } : response ?? {}), }, }) setShowRedPacket({ isShow: false }) } else if ( (response2 as sdk.RESULT_INFO).code || (response2 as sdk.RESULT_INFO).message ) { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: (response2 as sdk.RESULT_INFO)?.code, msg: (response2 as sdk.RESULT_INFO)?.message, ...(response2 instanceof Error ? { message: response2?.message, stack: response2?.stack, } : response2 ?? {}), }, }) setShowRedPacket({ isShow: false }) } else { const now = new Date().getTime() if (now < response.detail.luckyToken.validSince) { setBlindBoxType('Not Started') } else if ( now >= response.detail.luckyToken.validSince && now < response.detail.luckyToken.validUntil ) { setBlindBoxType('Blind Box Started') } else if ( now > response.detail.luckyToken.validUntil || response.detail.luckyToken.status === sdk.LuckyTokenItemStatus.COMPLETED ) { if ( (response2.raw_data as any).blindBoxStatus === sdk.BlindBoxStatus.NOT_OPENED && Date.now() > (response2.raw_data as any).luckyToken.validUntil ) { const claimLuckyTokenResponse = await LoopringAPI.luckTokenAPI?.sendLuckTokenClaimLuckyToken({ request: { hash: _info.hash, claimer: account.accAddress, referrer: '', serialNo: _info.serialNo }, eddsaKey: account.eddsaKey.sk, apiKey: account.apiKey, } as any) if ( (claimLuckyTokenResponse as sdk.RESULT_INFO).code || (claimLuckyTokenResponse as sdk.RESULT_INFO).message || (claimLuckyTokenResponse as any).amount === '0' ) { setBlindBoxType('Lottery Started and Not Win Lottery') } else { setBlindBoxType('Lottery Started and Win Lottery') if (response.detail.luckyToken.isNft) { setWonPrizeInfo({ name: response.detail.luckyToken.nftTokenInfo?.metadata?.base.name ?? '', url: response.detail.luckyToken.nftTokenInfo?.metadata?.imageSize.original ?? '', isNFT: true, }) } else { const token = tokenMap[idIndex[response.detail.tokenId]] const coin = coinMap[idIndex[response.detail.tokenId]] const amount = getValuePrecisionThousand( sdk.toBig((claimLuckyTokenResponse as any).amount).div('1e' + token.decimals), token.precision, token.precision, undefined, false, { floor: false, }, ) setWonPrizeInfo({ tokenURL: 'tokenIcon', tokenName: coin?.simpleName ?? '', amountStr: amount + ' ' + (coin?.simpleName ?? ''), isNFT: false, }) } } // refetch response = await LoopringAPI.luckTokenAPI.getLuckTokenDetail( { accountId: account.accountId, hash: _info.hash, serialNo: _info.serialNo, limit, offset, // fromId: 0, showHelper: true, } as any, account.apiKey, ) redpacketService.refresh() } else { setBlindBoxType('Lottery Started') } } if ( response.detail?.claimAmount?.toString() !== '0' && _info?.type.scope === sdk.LuckyTokenViewType.PUBLIC ) { updateRedpacketHash({ hash: _info?.hash, chainId: chainId as any, luckToken: _info, claimAmount: response.detail.claimAmount.toString(), address: account.accAddress, }) } const detail = (response as any).detail const luckTokenInfo: sdk.LuckyTokenItemForReceive = detail.luckyToken if (luckTokenInfo && response2.raw_data) { setDetail(detail) setBlindBoxDetail(response2.raw_data) setShowAccount({ isShow: false, }) } else { const error = new CustomError(ErrorMap.ERROR_REDPACKET_EMPTY) setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: UIERROR_CODE.ERROR_REDPACKET_EMPTY, msg: error.message, }, }) } } } catch (error: any) { setShowAccount({ isShow: true, step: AccountStep.RedPacketOpen_Failed, error: { code: UIERROR_CODE.UNKNOWN, msg: error?.message, // @ts-ignore ...(error instanceof Error ? { message: error?.message, stack: error?.stack, } : error ?? {}), }, }) setShowRedPacket({ isShow: false }) } } }, [isShow, step, info], ) const redPacketQrCodeCall = React.useCallback(async () => { setQrcode(undefined) if (info?.hash && LoopringAPI.luckTokenAPI) { const response = await LoopringAPI.luckTokenAPI.getLuckTokenDetail( { account: account.accountId, hash: info.hash, serialNo: info.serialNo, fromId: 0, showHelper: true, } as any, account.apiKey, ) const luckTokenInfo = response.detail.luckyToken as sdk.LuckyTokenItemForReceive setQrcode(luckTokenInfo) } }, [isShow, step, info]) const [page, setPage] = useState(1) const [pageForBlindbox, setPageForBlindbox] = useState(1) const history = useHistory() React.useEffect(() => { if (isShow) { const info = store.getState().modals.isShowRedPacket.info if (step === RedPacketViewStep.DetailPanel) { setPage(1) redPacketDetailCall({}) } else if (step === RedPacketViewStep.BlindBoxDetail) { setPage(1) redPacketBlindBoxDetailCall({}) } else if (step === RedPacketViewStep.QRCodePanel && info?.hash) { if (info?.id) { setQrcode(info as any) } else { redPacketQrCodeCall() } } } }, [step, isShow]) const redPacketDetailProps = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive & { claimAmount?: string } const redPacketType = _info && _info.type ? _info.type.mode === sdk.LuckyTokenClaimType.RELAY ? 'relay' : _info.type.partition === sdk.LuckyTokenAmountType.RANDOM ? 'lucky' : 'normal' : 'normal' // _info && _info.type && console.log('_info.type.partition', _info.type.partition) if ( isShow && info && step === RedPacketViewStep.DetailPanel && _info?.hash && LoopringAPI.luckTokenAPI && detail && detail.luckyToken ) { const isShouldSharedRely = detail.luckyToken.type.mode === sdk.LuckyTokenClaimType.RELAY && // detail.luckyToken.type.scope === sdk.LuckyTokenViewType.PRIVATE && ![ sdk.LuckyTokenItemStatus.OVER_DUE, sdk.LuckyTokenItemStatus.FAILED, sdk.LuckyTokenItemStatus.COMPLETED, ].includes(detail.luckyToken.status) const showRelayText = detail.luckyToken.type.mode === sdk.LuckyTokenClaimType.RELAY && account.accountId !== _info.sender.accountId let myAmountStr: string | undefined = undefined const relyNumber = detail.helpers?.length const value = detail.helpers?.reduce((prev, item) => { // @ts-ignore return prev.plus(item.amount) }, sdk.toBig(0)) ?? 0 let relyAmount: string | undefined = undefined let symbol, list // if (detail.claimAmount.toString() !== "0") { if (_info.isNft) { symbol = detail.claimAmount == 1 ? 'NFT' : 'NFTs' // @ts-ignore // const symbol = _info.nftTokenInfo?.metadata?.base?.name ?? "NFT"; myAmountStr = sdk.toBig(detail.claimAmount).isZero() ? EmptyValueTag : getValuePrecisionThousand(detail.claimAmount, 0, 0, undefined, false, { floor: false, // isTrade: true, }) + ' ' + symbol relyAmount = getValuePrecisionThousand(value, 0, 0, undefined, false, { floor: false, // isTrade: true, }) list = getUserNFTReceiveList( detail.claims as any, _info.nftTokenInfo as any, detail.champion, ).list } else { let tokenInfo = tokenMap[idIndex[_info?.tokenId] ?? ''] symbol = tokenInfo.symbol myAmountStr = myAmountStr = sdk.toBig(detail.claimAmount).isZero() ? EmptyValueTag : getValuePrecisionThousand( volumeToCountAsBigNumber(symbol, detail.claimAmount as any), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) + ' ' + symbol relyAmount = getValuePrecisionThousand( volumeToCountAsBigNumber(symbol, value), tokenInfo.precision, tokenInfo.precision, undefined, false, { floor: false, // isTrade: true, }, ) list = getUserReceiveList(detail.claims as any, tokenInfo, detail.champion).list } // } const claimButton: 'claimed' | 'claim' | 'claiming' | 'expired' | 'hidden' = detail.luckyToken .isNft ? detail.claimStatus === sdk.ClaimRecordStatus.WAITING_CLAIM ? 'claim' : detail.claimStatus === sdk.ClaimRecordStatus.CLAIMED ? 'claimed' : detail.claimStatus === sdk.ClaimRecordStatus.CLAIMING ? 'claiming' : detail.claimStatus === sdk.ClaimRecordStatus.EXPIRED ? 'expired' : 'hidden' : 'hidden' const bottomButton: 'ended' | 'share' | 'hidden' = [ sdk.LuckyTokenItemStatus.OVER_DUE, sdk.LuckyTokenItemStatus.FAILED, sdk.LuckyTokenItemStatus.COMPLETED, ].includes(detail.luckyToken.status) ? claimButton === 'hidden' ? 'ended' : 'hidden' : detail.luckyToken.type.scope === sdk.LuckyTokenViewType.TARGET ? 'hidden' : 'share' return { redPacketType, ImageEle, totalCount: detail.luckyToken.tokenAmount.totalCount, remainCount: detail.luckyToken.tokenAmount.remainCount, memo: _info.info.memo, amountStr, amountClaimStr, sender: _info.sender?.ens ? _info.sender?.ens : getShortAddr(_info.sender?.address), claimList: list, detail, myAmountStr, relyAmount: relyAmount ? relyAmount?.toString() : undefined, relyNumber: relyNumber ? relyNumber?.toString() : undefined, isShouldSharedRely, handlePageChange: (page: number = 1) => { setPage(page) redPacketDetailCall({ offset: (detail.luckyToken.isNft ? RedPacketNFTDetailLimit : RedPacketDetailLimit) * (page - 1), }) }, onShared: () => { setShowRedPacket({ isShow: true, step: RedPacketViewStep.QRCodePanel, info: { ...detail.luckyToken, referrer: account.accountId, isShouldSharedRely, }, }) }, tokenSymbol: _info.isNft ? undefined : tokenMap[idIndex[_info?.tokenId] ?? ''].symbol, showRelayText, bottomButton, page, claimButton, onClickClaim: () => { LoopringAPI.luckTokenAPI ?.getLuckTokenBalances( { accountId: account.accountId, isNft: detail.luckyToken.isNft, tokens: [detail.luckyToken.tokenId], }, account.apiKey, ) .then((response) => { if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { } else { setShowClaimWithdraw({ isShow: true, claimToken: { tokenId: detail.luckyToken.tokenId, // response!.tokenBalance[0].tokenId, total: detail.claimAmount.toString(), locked: response!.tokenBalance[0].locked, pending: response!.tokenBalance[0].pending, nftTokenInfo: detail.luckyToken.nftTokenInfo, isNft: detail.luckyToken.isNft, luckyTokenHash: detail.luckyToken.hash, }, claimType: CLAIM_TYPE.redPacket, }) } }) }, totalNumber: (detail as any).totalNum, showReceiptListBtn: account.accountId === detail.luckyToken.sender.accountId && detail.luckyToken.type.scope === sdk.LuckyTokenViewType.TARGET, showShareBtn: false, ended: false } as RedPacketDetailProps } else { return undefined } }, [info, detail, amountClaimStr, amountStr, account.accAddress, isShow, step]) const { setShowClaimWithdraw } = useOpenModals() const successCallback = React.useCallback(async () => { if (isShow) { switch (step) { case RedPacketViewStep.DetailPanel: redPacketDetailCall({ offset: 0 }) redpacketService.refresh() break case RedPacketViewStep.BlindBoxDetail: redPacketBlindBoxDetailCall({ offset: 0 }) redpacketService.refresh() if (wonPrizeInfo && wonPrizeInfo.isNFT) { setShowRedPacket({ isShow: false }) } break } } }, [step, isShow]) React.useEffect(() => { const subscription = subject.subscribe((props) => { switch (props.status) { case ClaimCommands.Success: if (props?.data?.type == CLAIM_TYPE.redPacket) { successCallback() } break default: break } }) return () => { subscription.unsubscribe() } }, [subject, successCallback]) const redPacketBlindBoxDetailProps = React.useMemo(() => { const _info = info as sdk.LuckyTokenItemForReceive & { claimAmount?: string } if ( isShow && info && step === RedPacketViewStep.BlindBoxDetail && _info?.hash && LoopringAPI.luckTokenAPI && detail && detail.luckyToken && blinBoxDetail && blindBoxType ) { const shareButton: 'hidden' | 'share' = (blindBoxType === 'Not Started' || blindBoxType === 'Blind Box Started') && detail.luckyToken.status !== sdk.LuckyTokenItemStatus.COMPLETED && detail.luckyToken.type.scope !== sdk.LuckyTokenViewType.TARGET ? 'share' : 'hidden' const claimButton: 'claimed' | 'claim' | 'claiming' | 'expired' | 'hidden' | 'ended' = detail .luckyToken.isNft ? Date.now() > detail.luckyToken.validUntil ? detail.claimStatus === sdk.ClaimRecordStatus.WAITING_CLAIM ? 'claim' : detail.claimStatus === sdk.ClaimRecordStatus.CLAIMED ? 'claimed' : detail.claimStatus === sdk.ClaimRecordStatus.CLAIMING ? 'claiming' : detail.claimStatus === sdk.ClaimRecordStatus.EXPIRED ? 'expired' : detail.luckyToken.status !== sdk.LuckyTokenItemStatus.COMPLETED ? 'ended' : 'hidden' : 'hidden' : [ sdk.LuckyTokenItemStatus.COMPLETED, sdk.LuckyTokenItemStatus.FAILED, sdk.LuckyTokenItemStatus.OVER_DUE, ].includes(detail.luckyToken.status) ? 'ended' : 'hidden' const tokenInfo = !detail.luckyToken.isNft ? tokenMap[idIndex[detail.luckyToken.tokenId]] : undefined return { sender: _info.sender?.ens ? _info.sender?.ens : getShortAddr(_info.sender?.address), memo: _info.info.memo, type: blindBoxType, blindBoxStartTime: detail!.luckyToken.validSince, lotteryStartTime: detail!.luckyToken.validUntil, lotteryEndTime: moment(detail!.luckyToken.nftExpireTime).toDate().getTime(), opendBlindBoxAmount: detail!.luckyToken.tokenAmount.claimedBoxCount, totalBlindBoxAmount: detail!.luckyToken.tokenAmount.totalCount, deliverdGiftsAmount: (detail as any).totalNum, totalGiftsAmount: Number(detail!.luckyToken.tokenAmount.giftCount), // imageEle?: JSX.Element | undefined; onShared: () => { setShowRedPacket({ isShow: true, step: RedPacketViewStep.QRCodePanel, info: { ...detail.luckyToken, referrer: account.accountId, }, }) }, onClickViewDetail: () => { redPacketBlindBoxDetailCall({}).then(() => { setViewDetailFrom(blindBoxType) setBlindBoxType('BlindBox Claime Detail') }) }, NFTClaimList: detail!.claims.map((claim) => { return { isMe: claim.claimer.accountId === account.accountId, who: claim.claimer?.ens ? claim.claimer?.ens : getShortAddr(claim.claimer?.address), when: claim.createdAt, amount: detail.luckyToken.isNft ? claim.amount : getValuePrecisionThousand( sdk.toBig(claim.amount).div('1e' + tokenInfo!.decimals), tokenInfo!.precision, tokenInfo!.precision, tokenInfo!.precision, ) + ' ' + tokenInfo!.symbol, showMultiplier: detail.luckyToken.isNft, showLuckiest: detail.luckyToken.tokenAmount.remainAmount == '0' && detail.champion?.accountId === claim.claimer.accountId && detail.champion?.amount === claim.amount, } }), // to change BlindBoxClaimList: blinBoxDetail.claims.map((x: any) => { return { who: x.claimer?.ens ? x.claimer?.ens : getShortAddr(x.claimer?.address), when: x.createdAt, amount: x.amount ? x.amount : 0, isMe: x.claimer.accountId === account.accountId, } }), showOpenLottery: blindBoxType === 'Lottery Started and Win Lottery' || blindBoxType === 'Lottery Started and Not Win Lottery', wonPrizeInfo: wonPrizeInfo, onCloseOpenModal: () => { setShowRedPacket({ isShow: false }) }, onClickClaimDetailBack: () => { setBlindBoxType(viewDetailFrom) }, onClickClaim: async () => { if (!detail.luckyToken.isNft) { setShowRedPacket({ isShow: false, }) history.push(`${RouterPath.l2assetsDetail}/${AssetTabIndex.RedPacket}`) return } const response = await LoopringAPI.luckTokenAPI?.getLuckTokenBalances( { accountId: account.accountId, isNft: detail.luckyToken.isNft, tokens: [detail.luckyToken.tokenId], }, account.apiKey, ) if ((response as sdk.RESULT_INFO).code || (response as sdk.RESULT_INFO).message) { } else { setShowClaimWithdraw({ isShow: true, claimToken: { tokenId: detail.luckyToken.tokenId, // response!.tokenBalance[0].tokenId, total: detail.claimAmount.toString(), locked: response!.tokenBalance[0].locked, pending: response!.tokenBalance[0].pending, nftTokenInfo: detail.luckyToken.nftTokenInfo, isNft: detail.luckyToken.isNft, luckyTokenHash: detail.luckyToken.hash, }, claimType: CLAIM_TYPE.redPacket, }) } }, NFTURL: Date.now() > detail!.luckyToken.validUntil ? detail.luckyToken.nftTokenInfo?.metadata?.imageSize.original : undefined, description: '', // Date.now() > detail!.luckyToken.validUntil // ? t("labelBlindBoxExplainationEnded") // : t("labelBlindBoxExplainationNotEnded"), claimButton, shareButton, didClaimABlindBox: blinBoxDetail.blindBoxStatus !== '' && blinBoxDetail.blindBoxStatus !== 'EXPIRED', wonInfo: blinBoxDetail!.luckyToken.isNft ? { participated: blinBoxDetail.blindBoxStatus !== '' && blinBoxDetail.blindBoxStatus !== 'EXPIRED', won: blinBoxDetail.claimAmount && toBig(blinBoxDetail.claimAmount).isGreaterThan(0), amount: detail.claimAmount, isNFT: true, } : { participated: blinBoxDetail.blindBoxStatus !== '' && blinBoxDetail.blindBoxStatus !== 'EXPIRED', won: blinBoxDetail.claimAmount && toBig(blinBoxDetail.claimAmount).isGreaterThan(0), amount: blinBoxDetail.claimAmount && tokenInfo && getValuePrecisionThousand( sdk.toBig(blinBoxDetail.claimAmount ?? '0').div('1e' + tokenInfo.decimals), tokenInfo.precision, tokenInfo.precision, tokenInfo.precision, false, ), total: tokenInfo && getValuePrecisionThousand( sdk .toBig(blinBoxDetail.luckyToken.tokenAmount.totalAmount) .div('1e' + tokenInfo!.decimals), tokenInfo!.precision, tokenInfo!.precision, tokenInfo!.precision, false, ), symbol: tokenInfo?.symbol, isNFT: false, }, handlePageChange: (page: number = 1) => { setPage(page) // redPacketBlindBoxDetailCall({ offset: (detail.luckyToken.isNft ? RedPacketNFTDetailLimit : RedPacketDetailLimit) * (page - 1) }); redPacketBlindBoxDetailCall({ offset: (detail.luckyToken.isNft ? RedPacketNFTDetailLimit : RedPacketDetailLimit) * (page - 1), }) }, totalCount: detail.luckyToken.tokenAmount.giftCount, remainCount: detail.luckyToken.tokenAmount.remainCount, page, totalClaimedNFTsCount: (detail as any).totalNum, totalBlindboxCount: opendBlindBoxCount, handlePageChange_BlindBox: (page: number = 1) => { setPageForBlindbox(page) // setPage(page) LoopringAPI.luckTokenAPI ?.getBlindBoxDetail( { accountId: account.accountId, hash: _info.hash, serialNo: _info.serialNo, limit: RedPacketBlindBoxLimit, offset: (page - 1) * RedPacketBlindBoxLimit, } as any, account.apiKey, ) .then((response2) => { setBlindBoxDetail(response2.raw_data) }) }, pageForBlindbox, onClickClaimPopViewDetail: () => { redPacketBlindBoxDetailCall({}).then(() => { setBlindBoxType('Lottery Started') }) }, expired: Date.now() > detail!.luckyToken.nftExpireTime, isTokenBlindbox: detail!.luckyToken.isNft ? false : true, remainGiftsAmount: detail!.luckyToken.isNft ? detail!.luckyToken.tokenAmount.remainAmount : getValuePrecisionThousand( sdk .toBig(detail!.luckyToken.tokenAmount.remainAmount) .div('1e' + tokenInfo!.decimals), tokenInfo?.precision, undefined, undefined, false, ) + ' ' + tokenInfo?.symbol, showReceiptListBtn: account.accountId === detail.luckyToken.sender.accountId && detail.luckyToken.type.scope === sdk.LuckyTokenViewType.TARGET, targets: (detail as any).targets, } as RedPacketBlindBoxDetailProps } else { return undefined } }, [ info, detail, amountClaimStr, amountStr, account.accAddress, isShow, step, blinBoxDetail, blindBoxType, wonPrizeInfo, ]) const redPacketQRCodeProps: RedPacketQRCodeProps | undefined = React.useMemo(() => { if (isShow && info && step === RedPacketViewStep.QRCodePanel && qrcode && qrcode.hash) { if ( qrcode.status === sdk.LuckyTokenItemStatus.COMPLETED || qrcode.status === sdk.LuckyTokenItemStatus.OVER_DUE ) { setShowRedPacket({ isShow, step: RedPacketViewStep.TimeOutPanel, info: qrcode, }) } else if (qrcode?.hash) { const url = `${Exchange}wallet?redpacket&id=${qrcode?.hash}&referrer=${account.accAddress}` return { url, imageEleUrl: qrcode.nftTokenInfo?.metadata?.imageSize[NFT_IMAGE_SIZES.large] ?? undefined, textAddress: qrcode.sender?.ens ? qrcode.sender?.ens : getShortAddr(qrcode.sender?.address), textContent: qrcode.info.memo, amountStr: qrcode.isNft ? amountStrNFTCallback(qrcode.nftTokenInfo as any, qrcode.tokenAmount.totalAmount) .amount === '1' ? t('labelNFTs_one', { count: 1, }) : t('labelNFTs_other', { count: amountStrNFTCallback( qrcode.nftTokenInfo as any, qrcode.tokenAmount.totalAmount, ).amount, }) : amountStrCallback(tokenMap, idIndex, qrcode.tokenId, qrcode.tokenAmount.totalAmount) .amountStr, textSendBy, textType: info && info.type ? info.type.mode === sdk.LuckyTokenClaimType.BLIND_BOX ? t('labelLuckyBlindBox') : info.type.mode === sdk.LuckyTokenClaimType.RELAY ? t('labelRelayRedPacket') : info.type.partition === sdk.LuckyTokenAmountType.RANDOM ? t('labelLuckyRedPacket') : t('labelNormalRedPacket') : t('labelNormalRedPacket'), textShared: t('labelShare'), textDes: t('labelRedpacketScanDes'), isShouldSharedRely: qrcode.type.mode == sdk.LuckyTokenClaimType.RELAY, textNo: t('labelRedPacketNo', { value: qrcode?.hash.slice(-8) }), } as RedPacketQRCodeProps } } return undefined }, [info, qrcode, account.accAddress, isShow, textSendBy, amountStr, step]) return { redPacketQRCodeProps, redPacketTimeoutProps, redPacketOpenProps, redPacketOpenedProps, redPacketDetailProps, redPacketClockProps, redPacketBlindBoxDetailProps, } } ================================================ FILE: packages/core/src/modal/RedPacketModal/index.tsx ================================================ import { WithTranslation, withTranslation } from 'react-i18next' import { ModalRedPacket, RedPacketBlindBoxDetail, RedPacketClock, RedPacketDetail, RedPacketOpen, RedPacketOpened, RedPacketQRCode, RedPacketSize, RedPacketTimeout, RedPacketViewStep, useOpenModals, } from '@loopring-web/component-lib' import React from 'react' import { useRedPacketModal } from './hook' import { myLog, SoursURL } from '@loopring-web/common-resources' import { Box } from '@mui/material' export const ModalRedPacketPanel = withTranslation('common')( ({ etherscanBaseUrl, }: WithTranslation & { etherscanBaseUrl: string }) => { const { modals: { isShowRedPacket }, setShowRedPacket, } = useOpenModals() const { redPacketQRCodeProps, redPacketTimeoutProps, redPacketOpenProps, redPacketOpenedProps, redPacketDetailProps, redPacketClockProps, redPacketBlindBoxDetailProps, } = useRedPacketModal() // const { redPacketProps } = useRedPacketDetail(); // const theme = useTheme(); const redPacketList = React.useMemo(() => { myLog(redPacketOpenProps) return Object.values({ [RedPacketViewStep.QRCodePanel]: { view: redPacketQRCodeProps ? ( ) : ( <> ), }, [RedPacketViewStep.OpenPanel]: { view: redPacketOpenProps ? ( ) : ( <> ), }, [RedPacketViewStep.RedPacketClock]: { view: redPacketClockProps ? ( ) : ( <> ), }, [RedPacketViewStep.OpenedPanel]: { view: redPacketOpenedProps ? ( ) : ( <> ), }, [RedPacketViewStep.TimeOutPanel]: { view: redPacketTimeoutProps ? ( ) : ( <> ), }, [RedPacketViewStep.DetailPanel]: { view: redPacketDetailProps ? ( ) : ( <> ), }, [RedPacketViewStep.PreparePanel]: { view: <> }, [RedPacketViewStep.BlindBoxDetail]: { view: redPacketBlindBoxDetailProps ? ( ) : ( <> ), }, [RedPacketViewStep.Loading]: { view: ( {'loading'} ), }, }) }, [ redPacketQRCodeProps, redPacketOpenProps, redPacketDetailProps, redPacketTimeoutProps, redPacketOpenedProps, redPacketClockProps, redPacketBlindBoxDetailProps, ]) return ( { setShowRedPacket({ isShow: false }) }} etherscanBaseUrl={etherscanBaseUrl} step={isShowRedPacket.step} open={isShowRedPacket.isShow} panelList={redPacketList} /> ) }, ) ================================================ FILE: packages/core/src/modal/WalletModal/index.tsx ================================================ // @ts-nocheck import { useTranslation, WithTranslation, withTranslation } from 'react-i18next' import { AccountStep, CommonConnectInProgress, ConfirmLinkCopy, ConnectFailed, ConnectReject, ConnectRejectSwitchNetwork, ConnectSuccess, InformationForCoinBase, ModalWalletConnect, ProviderMenu, Toast, ToastType, useOpenModals, useSettings, WalletConnectConnectInProgress, WalletConnectQRCode, WalletConnectStep, WrongNetworkGuide, } from '@loopring-web/component-lib' import React from 'react' import { AccountStatus, Bridge, copyToClipBoard, GatewayItem, gatewayList as DefaultGatewayList, globalSetup, myLog, NetworkMap, SagaStatus, SoursURL, TOAST_TIME, } from '@loopring-web/common-resources' import { walletServices } from '@loopring-web/web3-provider' import { CoinbaseCallback, gameStopCallback, metaMaskCallback, RootState, useAccount, walletConnectCallback, } from '@loopring-web/core' import { useSelector } from 'react-redux' import { useLocation } from 'react-router-dom' export const useGatewayList = ({ setIsOpenUnknownProvider, setConnectProvider, setIsConfirmLinkCopy, }: { setIsOpenUnknownProvider?: any setConnectProvider?: any setIsConfirmLinkCopy: (boolean) => void }) => { const { search } = useLocation() const { t } = useTranslation() const searchParams = new URLSearchParams(search) const { isMobile } = useSettings() const { setShowConnect } = useOpenModals() const { account, status: accountStatus } = useAccount() const [stateCheck, setStateCheck] = React.useState(false) const [processingCallback, setProcessingCallback] = React.useState< { callback: () => Promise } | undefined >(undefined) React.useEffect(() => { if (stateCheck && [SagaStatus.UNSET].findIndex((ele: string) => ele === accountStatus) !== -1) { myLog('clear cache connect done') setStateCheck(false) if (processingCallback !== undefined) { processingCallback.callback() } } }, [accountStatus, stateCheck]) const gatewayList: GatewayItem[] = !isMobile ? [ { ...DefaultGatewayList[0], handleSelect: React.useCallback( async (event, flag?) => { if (!flag && account.connectName === DefaultGatewayList[0].key) { setShowConnect({ isShow: false }) } else { const isKnow = localStorage.getItem('useKnowCoinBaseWalletInstall') if ( !(isKnow === 'true') && !(window?.ethereum?._metamask && window?.ethereum?._metamask.requestBatch) ) { setIsOpenUnknownProvider && setIsOpenUnknownProvider(true) } walletServices.sendDisconnect('', 'should new provider') setConnectProvider && setConnectProvider(DefaultGatewayList[0].key) setShowConnect({ isShow: true, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) setProcessingCallback({ callback: metaMaskCallback }) setStateCheck(true) } }, [account.connectName, setShowConnect], ), }, { ...DefaultGatewayList[1], handleSelect: React.useCallback( async (event, flag?) => { if (!flag && account.connectName === DefaultGatewayList[1].key) { setShowConnect({ isShow: false }) } else { walletServices.sendDisconnect('', 'should new provider') setConnectProvider(DefaultGatewayList[1].key) setShowConnect({ isShow: true, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) setProcessingCallback({ callback: walletConnectCallback }) setStateCheck(true) } }, [account.connectName, setShowConnect], ), }, { ...DefaultGatewayList[2], handleSelect: React.useCallback( async (event, flag?) => { walletServices.sendDisconnect('', 'should new provider') setConnectProvider(DefaultGatewayList[2].key) setShowConnect({ isShow: true, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) setProcessingCallback({ callback: gameStopCallback }) setStateCheck(true) }, [setShowConnect], ), }, { ...DefaultGatewayList[3], handleSelect: React.useCallback( async (event, flag?) => { walletServices.sendDisconnect('', 'should new provider') setConnectProvider(DefaultGatewayList[3].key) if ( window.ethereum && 'isCoinbaseBrowser' in ethereum && ethereum.isCoinbaseBrowser ) { setShowConnect({ isShow: true, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) } else { setShowConnect({ isShow: false, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) } setProcessingCallback({ callback: CoinbaseCallback }) setStateCheck(true) }, [setShowConnect], ), }, ] : [ ...(window.ethereum ? [ { ...DefaultGatewayList[0], key: t('labelConnectWithDapp'), imgSrc: SoursURL + 'svg/loopring.svg', handleSelect: React.useCallback( async (event, flag?) => { if (!flag && account.connectName === DefaultGatewayList[0].key) { setShowConnect({ isShow: false }) } else { walletServices.sendDisconnect('', 'should new provider') setConnectProvider && setConnectProvider(DefaultGatewayList[0].key) setShowConnect({ isShow: true, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) setProcessingCallback({ callback: metaMaskCallback }) setStateCheck(true) } }, [account.connectName, setShowConnect], ), }, ] : [ { key: t('labelOpenInWalletApp'), keyi18n: 'labelOpenInWalletApp', imgSrc: SoursURL + 'svg/loopring.svg', handleSelect: React.useCallback( async (event, flag?) => { // setShowConnect({ isShow: false }); const token = searchParams.get('token') const l2account = searchParams.get('l2account') || searchParams.get('owner') copyToClipBoard( Bridge + `?${l2account ? `l2account=` + l2account : ''}&${ token ? `token=` + token : '' }`, ) setIsConfirmLinkCopy(true) }, [searchParams], ), }, ]), { ...DefaultGatewayList[1], handleSelect: React.useCallback( async (event, flag?) => { if (!flag && account.connectName === DefaultGatewayList[1].key) { setShowConnect({ isShow: false }) } else { walletServices.sendDisconnect('', 'should new provider') setConnectProvider && setConnectProvider(DefaultGatewayList[1].key) setShowConnect({ isShow: true, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) setProcessingCallback({ callback: walletConnectCallback }) setStateCheck(true) } }, [account.connectName, setShowConnect], ), }, { ...DefaultGatewayList[3], handleSelect: React.useCallback( async (event, flag?) => { walletServices.sendDisconnect('', 'should new provider') setConnectProvider && setConnectProvider(DefaultGatewayList[3].key) setShowConnect({ isShow: false, step: WalletConnectStep.Provider, info: { status: 'processing', }, }) setProcessingCallback({ callback: CoinbaseCallback }) setStateCheck(true) }, [setShowConnect], ), }, ] return { gatewayList } } export const ModalWalletConnectPanel = withTranslation('common')( ({ onClose, open, wait = globalSetup.wait, t, ...rest }: { open: boolean wait?: number onClose?: (e: MouseEvent) => void } & WithTranslation) => { const { account, setShouldShow } = useAccount() const { defaultNetwork } = useSettings() const { modals: { isShowConnect, isWrongNetworkGuide }, setShowConnect, setShowAccount, setShowWrongNetworkGuide, } = useOpenModals() const qrCodeUrl = useSelector((state: RootState) => state.account.qrCodeUrl) const [connectProvider, setConnectProvider] = React.useState(() => { return account?.connectName ?? false }) React.useEffect(() => { if (account?.connectName !== connectProvider) { setConnectProvider(account?.connectName) } }, [account?.connectName]) const _onClose = React.useCallback( async (e: any) => { setShouldShow(false) setShowConnect({ isShow: false }) if (account.readyState === 'UN_CONNECT') { walletServices.sendDisconnect('', 'should new provider') } if (onClose) { onClose(e) } }, [account.readyState, onClose, setShouldShow, setShowConnect], ) const [isOpenUnknownProvider, setIsOpenUnknownProvider] = React.useState(false) const [isConfirmLinkCopy, setIsConfirmLinkCopy] = React.useState(false) const { gatewayList } = useGatewayList({ setConnectProvider, setIsOpenUnknownProvider, setIsConfirmLinkCopy, }) const handleCloseDialog = React.useCallback((_event: any, state?: boolean) => { setIsOpenUnknownProvider(false) localStorage.setItem('useKnowCoinBaseWalletInstall', String(!!state)) }, []) const [copyToastOpen, setCopyToastOpen] = React.useState(false) const onRetry = React.useCallback(async () => { const index = gatewayList.findIndex((item) => { return item.key === account.connectName }) if (index !== -1 && gatewayList) { //@ts-ignore gatewayList[index].handleSelect(null, true) } else { walletServices.sendDisconnect('', 'should new provider') setShowConnect({ isShow: true, step: WalletConnectStep.Provider }) } }, [gatewayList, account.connectName, setShowConnect]) const providerBack = React.useMemo(() => { return ['UN_CONNECT', 'ERROR_NETWORK'].includes(account.readyState) ? undefined : () => { setShowConnect({ isShow: false }) switch (account.readyState) { case AccountStatus.ACTIVATED: case AccountStatus.LOCKED: break case AccountStatus.DEPOSITING: setShowAccount({ isShow: true, step: AccountStep.Deposit_Submit, }) break case AccountStatus.NO_ACCOUNT: setShowAccount({ isShow: true, step: AccountStep.NoAccount }) break } } }, [account.readyState, setShowAccount, setShowConnect]) const walletList = React.useMemo(() => { return Object.values({ [WalletConnectStep.Provider]: { view: ( ), height: 'auto', onBack: providerBack, }, [WalletConnectStep.CommonProcessing]: { view: ( ), }, [WalletConnectStep.WalletConnectProcessing]: { view: , }, [WalletConnectStep.WalletConnectQRCode]: { view: ( { copyToClipBoard(qrCodeUrl) setCopyToastOpen(true) }} url={qrCodeUrl} {...{ t, ...rest }} /> ), onBack: () => { setShowConnect({ isShow: true, step: WalletConnectStep.Provider }) }, }, [WalletConnectStep.SuccessConnect]: { view: , }, [WalletConnectStep.RejectConnect]: { view: ( ), onBack: () => { walletServices.sendDisconnect('', 'should new provider') setShowConnect({ isShow: true, step: WalletConnectStep.Provider }) }, }, [WalletConnectStep.RejectSwitchNetwork]: { view: ( ), }, [WalletConnectStep.FailedConnect]: { view: ( ), onBack: () => { walletServices.sendDisconnect('', 'should new provider') setShowConnect({ isShow: true, step: WalletConnectStep.Provider }) }, }, }) }, [ gatewayList, account.connectName, t, rest, providerBack, connectProvider, qrCodeUrl, isShowConnect.error, onRetry, setShowConnect, ]) return ( <> setIsConfirmLinkCopy(false)} /> { setShowWrongNetworkGuide({ isShow: false }) }} /> { setCopyToastOpen(false) }} severity={ToastType.success} /> ) }, ) ================================================ FILE: packages/core/src/modal/index.tsx ================================================ import { AccountStep, AlertNotSupport, DepositProps, InformationForNoMetaNFT, ModalCloseButton, ModalSettingFee, OtherExchangeDialog, SwitchPanelStyled, useOpenModals, } from '@loopring-web/component-lib' import { ClosureAnnouncementModal } from '@loopring-web/component-lib/src/components/modal/ClosureAnnouncementModal' import { isClosureAnnouncementDismissed, setClosureAnnouncementDismissed } from '@loopring-web/component-lib/src/utils/closureAnnouncementUtils' import { ModalWalletConnectPanel } from './WalletModal' import { ModalAccountInfo } from './AccountModal' import { withTranslation, WithTranslation } from 'react-i18next' import { useSystem, useAccountModal, useAccount } from '@loopring-web/core' import React from 'react' import { AccountStatus, AssetsRawDataItem } from '@loopring-web/common-resources' import { Box, Modal as MuiModal } from '@mui/material' import { ModalAccountL1Info } from './AccountL1Modal' export const ModalGroup = withTranslation('common')( ({ isLayer1Only, onWalletConnectPanelClose, depositProps, assetsRawData, hideDepositWithdrawBack, isWebEarn, ...rest }: WithTranslation & { isLayer1Only?: boolean depositProps: DepositProps assetsRawData: AssetsRawDataItem[] onWalletConnectPanelClose?: (event: MouseEvent) => void hideDepositWithdrawBack?: boolean isWebEarn?: boolean }) => { const { etherscanBaseUrl } = useSystem() const { modals: { isShowFeeSetting, isShowIFrame, isShowClosureAnnouncement }, setShowFeeSetting, setShowIFrame, setShowOtherExchange, setShowClosureAnnouncement, } = useOpenModals() useAccountModal() const { modals: { isShowAccount, isShowConnect, isShowSupport, isShowOtherExchange, isShowNFTMetaNotReady, }, setShowAccount, setShowSupport, setShowDeposit, setShowTransfer, setShowWithdraw, setShowResetAccount, setNFTMetaNotReady, } = useOpenModals() const { account } = useAccount() React.useEffect(() => { if (account.readyState !== AccountStatus.ACTIVATED) { setShowDeposit({ isShow: false }) setShowTransfer({ isShow: false }) setShowWithdraw({ isShow: false }) setShowResetAccount({ isShow: false }) } }, [account.readyState]) React.useEffect(() => { if (!isClosureAnnouncementDismissed()) { setShowClosureAnnouncement({ isShow: true }) } }, [setShowClosureAnnouncement]) return ( <> { setClosureAnnouncementDismissed() setShowClosureAnnouncement({ isShow: false }) }} /> { setShowSupport({ isShow: false }) }} /> {/**/} { setShowOtherExchange({ isShow: false, agree }) }} /> { setNFTMetaNotReady({ isShow: false }) if (isAgree) { setShowAccount({ isShow: true, step: AccountStep.SendNFTGateway, }) } }} /> setShowFeeSetting({ isShow: false })} /> setShowIFrame({ isShow: false, url: '' })} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description' > setShowIFrame({ isShow: false, url: '' })} {...rest} /> {/*{onBack ? : <>}*/}