Repository: Loopring/loopring_sdk Branch: master Commit: 852e9f6a127e Files: 165 Total size: 1.1 MB Directory structure: gitextract_m37m1v1z/ ├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .gitpod.yml ├── .prettierignore ├── .prettierrc.json ├── Changelog.md ├── LICENSE ├── README.md ├── book.json ├── docs/ │ ├── .bookignore │ ├── Changelog.md │ ├── README.md │ ├── SUMMARY.md │ ├── about_us.md │ └── js_sdk/ │ ├── Deposit.md │ ├── NFTAction/ │ │ ├── collectionNFT.md │ │ ├── deployNFT.md │ │ ├── metaNFT.md │ │ ├── mintNFT.md │ │ ├── tradeNFT.md │ │ └── validateNFTOrder.md │ ├── README.md │ ├── account/ │ │ ├── activeAccount.md │ │ ├── fee.md │ │ ├── historyRecord.md │ │ ├── signature.md │ │ ├── wallet_api.md │ │ └── whitelisted_user_api.md │ ├── deposit/ │ │ ├── depositERC20.md │ │ └── depositNFT.md │ ├── erc20Trade/ │ │ └── orderERC20.md │ ├── exchange/ │ │ ├── ammpool_api.md │ │ ├── exchange.md │ │ └── webSocket.md │ ├── transfer/ │ │ ├── transferERC20.md │ │ └── transferNFT.md │ └── withdraw/ │ ├── withdrawERC20.md │ └── withdrawNFT.md ├── jest.config.cjs ├── package.json ├── rollup.config.mjs ├── src/ │ ├── api/ │ │ ├── ammpool_api.ts │ │ ├── base_api.ts │ │ ├── config/ │ │ │ ├── abis/ │ │ │ │ ├── contractWallet.ts │ │ │ │ ├── erc1155.ts │ │ │ │ ├── erc20.ts │ │ │ │ ├── erc721.ts │ │ │ │ ├── exchange_3_6.ts │ │ │ │ ├── hebao.ts │ │ │ │ ├── index.ts │ │ │ │ └── smartWallet.ts │ │ │ ├── guardianTypeData.ts │ │ │ └── index.ts │ │ ├── contacts_api.ts │ │ ├── contract_api.ts │ │ ├── defi_api.ts │ │ ├── delegate_api.ts │ │ ├── ethereum/ │ │ │ └── contracts/ │ │ │ ├── AbiFunction.ts │ │ │ ├── Contract.ts │ │ │ ├── Contracts.ts │ │ │ └── index.ts │ │ ├── exchange_api.ts │ │ ├── global_api.ts │ │ ├── index.ts │ │ ├── luckToken_api.ts │ │ ├── nft_api.ts │ │ ├── rabbitWithdraw_api.ts │ │ ├── request.ts │ │ ├── sign/ │ │ │ ├── poseidon/ │ │ │ │ ├── EDDSAUtil.ts │ │ │ │ ├── TestsEDDSAUtil_test.ts │ │ │ │ ├── babyJub.ts │ │ │ │ ├── eddsa.ts │ │ │ │ ├── eddsa_test.ts │ │ │ │ ├── field.ts │ │ │ │ ├── field_test.ts │ │ │ │ ├── jubjub.ts │ │ │ │ ├── jubjub_test.ts │ │ │ │ ├── permutation.ts │ │ │ │ └── permutation_test.ts │ │ │ └── sign_tools.ts │ │ ├── user_api.ts │ │ ├── vault_api.ts │ │ ├── wallet_api.ts │ │ ├── whitelisted_user_api.ts │ │ └── ws_api.ts │ ├── defs/ │ │ ├── account_defs.ts │ │ ├── error_codes.ts │ │ ├── index.ts │ │ ├── loopring_constants.ts │ │ ├── loopring_defs.ts │ │ ├── loopring_enums.ts │ │ ├── nft_defs.ts │ │ ├── url_defs.ts │ │ ├── web3_defs.ts │ │ └── ws_defs.ts │ ├── index.ts │ ├── tests/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── MockData.ts │ │ ├── MockSwapData.ts │ │ ├── README.md │ │ ├── UTC--2021-02-04T02-55-36.490219109Z--ef439044717c3af35f4f46e52aa99280217a7114 │ │ ├── demo/ │ │ │ ├── NFTAction/ │ │ │ │ ├── collectionNFT.md │ │ │ │ ├── collectionNFT.test.ts │ │ │ │ ├── deployNFT.md │ │ │ │ ├── deployNFT.test.ts │ │ │ │ ├── metaNFT.md │ │ │ │ ├── metaNFT.test.ts │ │ │ │ ├── mintNFT.md │ │ │ │ ├── mintNFT.test.ts │ │ │ │ ├── tradeNFT.md │ │ │ │ ├── tradeNFT.test.ts │ │ │ │ ├── validateNFTOrder.md │ │ │ │ └── validateNFTOrder.test.ts │ │ │ ├── account/ │ │ │ │ ├── account.test.ts │ │ │ │ ├── activeAccount.md │ │ │ │ ├── activeAccount.test.ts │ │ │ │ ├── fee.md │ │ │ │ ├── fee.test.ts │ │ │ │ ├── historyRecord.md │ │ │ │ ├── historyRecord.test.ts │ │ │ │ ├── signature.md │ │ │ │ └── signature.test.ts │ │ │ ├── deposit/ │ │ │ │ ├── deposit.md │ │ │ │ ├── deposit.test.ts │ │ │ │ ├── depositNFT.md │ │ │ │ └── depositNFT.test.ts │ │ │ ├── erc20Trade/ │ │ │ │ ├── orderERC20.md │ │ │ │ └── orderERC20.test.ts │ │ │ ├── exchange/ │ │ │ │ ├── exchange.md │ │ │ │ ├── exchange.test.ts │ │ │ │ ├── webSocket.md │ │ │ │ └── webSocket.test.ts │ │ │ ├── transfer/ │ │ │ │ ├── transferERC20.md │ │ │ │ ├── transferERC20.test.ts │ │ │ │ ├── transferNFT.md │ │ │ │ └── transferNFT.test.ts │ │ │ └── withdraw/ │ │ │ ├── withdrawERC20.md │ │ │ ├── withdrawERC20.test.ts │ │ │ ├── withdrawNFT.md │ │ │ └── withdrawNFT.test.ts │ │ ├── formatter.test.ts │ │ └── unitTest/ │ │ ├── account/ │ │ │ ├── account.test.ts │ │ │ └── sign_tools.test.ts │ │ ├── appWallet/ │ │ │ └── wallet.test.ts │ │ ├── erc20Trade/ │ │ │ ├── amm.test.ts │ │ │ ├── amm_calc.test.ts │ │ │ └── defi.test.ts │ │ ├── exchange/ │ │ │ └── exchange.test.ts │ │ ├── transfer/ │ │ │ └── transferUT.test.ts │ │ └── withdraw/ │ │ ├── forceWithdrawls.test.ts │ │ └── withdrawUT.test.ts │ ├── types/ │ │ └── eddsa.d.ts │ ├── types.d.ts │ └── utils/ │ ├── formatter.ts │ ├── index.ts │ ├── log_tools.ts │ ├── network_tools.ts │ ├── obj_tools.ts │ ├── swap_calc_utils.ts │ ├── symbol_tools.ts │ └── window_utils.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "env": { "esmBundled": { "presets": [ ["@babel/env", { "targets": "> 0.25%, not dead" }], "@babel/typescript"] }, "cjs": { "presets": [ ["@babel/env", { "modules":"commonjs" }], "@babel/typescript"] }, "prod": { "presets": [ [ "@babel/env", { "targets": { "node": "18" }, "useBuiltIns": "usage", "corejs": 3 } ], "@babel/typescript" ] } }, "plugins": [ "@babel/plugin-transform-optional-chaining", "@babel/plugin-transform-typescript", "transform-class-properties" ] } ================================================ FILE: .eslintignore ================================================ # Ignore artifacts: build coverage _book dist node_modules dist ================================================ FILE: .eslintrc ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": [ "@typescript-eslint" ], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-console": 2 , // Remember, this means error! "camelcase": ["warn"] } } ================================================ FILE: .gitignore ================================================ yarn-error.log *.tgz *testfile* .idea _book dist node_modules coverage ./docs .DS_Store ================================================ FILE: .gitpod.yml ================================================ # This configuration file was automatically generated by Gitpod. # Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) # and commit this file to your remote git repository to share the goodness with others. tasks: - init: yarn install && yarn run build command: yarn run start ================================================ FILE: .prettierignore ================================================ # Ignore artifacts: build coverage _book dist node_modules ================================================ FILE: .prettierrc.json ================================================ {} ================================================ FILE: Changelog.md ================================================ # CHANGELOG ## JS SDK Version ChangeLog #### 3.6.14 1) Guardian approve #### v3.5.0 1)ecdsaSignature update to `eth_signTypedData_v4` use method `sendAsync` #### v3.4.15 1)Internal browser contract signarue support 2)referral signature at active account 3)referral reword 4)Explorer Collection related Entry and feature optimization 5)insufficient quota reminder for Dual Invest 6)Add tutorial on Block trade #### v3.3.16 1)Change Ecrecove function 2)Stop limit 3) Red Packet 4) Block trade #### v3.3.0 1) GLOBAL information update #### v3.2.6 1) BlindBox Redpacket #### v3.2.1 1) LRC staking 2) signature bug fix #### v3.1.5 1) hardware wallet last digital match / hard-code generateKeyPair(_, publicKey: { x: string; y: string } | undefined = undefined) #### v3.1.0 1) dual API update (support for Partial order) 2) red Packet feature 3) amm redeem mini order #### v3.0.1 - v3.0.8 1) reformate Error throw code states 2) new feature for collection group 3) method split for localStorage #### v2.0.24 1)collection feature 2)investment feature #### v2.0.17 1) submitForceWithdrawals 2) return a result hash 3) order/swap demo #### v2.0.12 1) ipfs cid to nftId startWith 0 2) server-side retturn nftId startWith 0 #### v2.0.11 1) add deposit to 2) get Account by account id 3) add HEBAO_META_TYPE deposit_wallet #### v2.0.7 1) Fix isMobile #### v2.0.6 1) mintNFT nftData #### v2.0.5 (If you use v2 please move to >= v2.0.4 ) 1) payPayeeUpdateAccount 2) fix generateApiKey error #### v2.0.0 1) update poseidon *** #### v1.10.3 1) Demo update #### v1.10.1 1) getAllowances formatter update 2) getTokenBalances formatter update 3) unit test update #### v1.9.6 1) add is Mobile in personalSign #### v1.9.4 1) remove Authereum & walletLink 2) DEPLOYMENT_STATUS #### v1.8.13 1)Update web3_defs.ts CREATION_CODE 2)submitDeployNFT transfer.maxFee tokenId: transfer.token.tokenId, #### v1.8.7 1)getAmmPoolTxs V3 #### v1.8.5 1)validateNftOrder #### v1.8.1 1)validateNftOrder 2)NFTCounterFactualInfo #### v1.7.9 1) nftMint NFTCounterFactualInfo ####v1.7.6 1) Doc update,UT update ####v1.7.2 1) NFT ACTION 2) AMM Bug ####v1.6.6 1) NFT MINT IPFS ####v1.6.5 1) The eddsaKey is generated by a keyseed, default format is 'Sign this message to access Loopring ####v1.6.3 1) Error format bug fix {code:number,message:string} ####v1.6.0 1) Error format `{code:number, msg|message: string}` ####v1.5.7 1) 721 NFT URI function #### v1.5.6 1) submitDeployNFT #### v1.5.2 1) NFT deposit #### v1.4.9 1) Counter Factual check 2) updateAccount #### v1.4.7 1) NFT META 2) HEBAO #### v1.4.5 1) CF wallet action sign update #### v1.4.4 (alpha) 1) fix unlock FC wallet #### v1.4.3 (alpha) 1) bug fix for NFT get contractMeta 2) FC wallet connect signature 3) Error code #### v1.4.2 1) update activity #### v1.3.10 1) Add recognizing header for web #### v1.3.9 1) Update getLatestTokenPrice url #### v1.3.8 1) SDK update for error type #### v1.3.4 - v1.3.7 destroy #### v1.3.3 1) add computeNFTAddress 2) add eslint and prettie for the code format #### v1.3.2 1) Fix some bug 2) Update withdraw NFT unit test #### v1.3.0 && v1.3.1 -- !!! revert formatter Uint8Array support # !!! revert ================================================ 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 SDK [![license](https://img.shields.io/badge/license-Loopring-blue)](https://raw.githubusercontent.com/Loopring/loopring_sdk/master/LICENSE) [![type-badge](https://img.shields.io/npm/types/react-data-grid)](https://www.npmjs.com/package/react-data-grid) [![Discord](https://img.shields.io/discord/687207715902193673)](https://discord.com/invite/KkYccYp) ## 🚀 Quick Start ```shell # Using npm npm i @loopring-web/loopring-sdk --save # Using yarn yarn add @loopring-web/loopring-sdk ``` when using for browser make sure set up `NODE_ENV=production|development` > Make sure you are using the original npm registry. > `npm config set registry http://registry.npmjs.org` ## 📒 Documentation Please see the [documentation page](https://loopring.github.io/loopring_sdk/) for information about getting started and developing with the Loopring SDK. - [JS SDK](https://loopring.github.io/loopring_sdk) - [APIs](https://docs-protocol.loopring.io) ## ✨ Changelog - [Changelog](https://loopring.github.io/loopring_sdk/ChangeLog.html) ## 🫂 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) ## 🎒 Getting Started Please see our [introduction page](https://loopring.github.io/loopring_sdk/) for details on integrating the SDK into your application. ## 🙋 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) ## 👉 [What is Loopring?](https://loopring.org/#/) ## 🔑 Security - [Wallet](https://security.loopring.io/) - [Protocol Audit](https://loopring.org/resources/loopring1.0_audit.pdf) ================================================ FILE: book.json ================================================ { "root": "./docs", "description": "loopring sdk", "author": "Loopring Dev Team", "title": "Loopring SDK", "gitbook": ">=3.0.0", "links": { "sidebar": { "Home": "https://loopring.io" } } } ================================================ FILE: docs/.bookignore ================================================ docs/detail docs/README.md docs/Changelog.mdv ================================================ FILE: docs/Changelog.md ================================================ # CHANGELOG ## JS SDK Version ChangeLog #### v3.6.0 1) dual Invest autoReinvest 2) guardina singature typeData 3) remove signature 02/03 ended #### v3.2.1 1) LRC staking 2) signature bug fix #### v3.1.5 1) hardware wallet last digital match / hard-code generateKeyPair(_, publicKey: { x: string; y: string } | undefined = undefined) #### v3.1.0 1) dual API update (support for Partial order) 2) red Packet feature 3) amm redeem mini order #### v3.0.1 - v3.0.8 1) reformate Error throw code states 2) new feature for collection group 3) method split for localStorage #### v2.0.24 1)collection feature 2)investment feature #### v2.0.17 1) submitForceWithdrawals 2) return a result hash 3) order/swap demo #### v2.0.12 1) ipfs cid to nftId startWith 0 2) server-side retturn nftId startWith 0 #### v2.0.11 1) add deposit to 2) get Account by account id 3) add HEBAO_META_TYPE deposit_wallet #### v2.0.7 1) Fix isMobile #### v2.0.6 1) mintNFT nftData #### v2.0.5 (If you use v2 please move to >= v2.0.4 ) 1) payPayeeUpdateAccount 2) fix generateApiKey error #### v2.0.0 1) update poseidon *** #### v1.10.3 1) Demo update #### v1.10.1 1) getAllowances formatter update 2) getTokenBalances formatter update 3) unit test update #### v1.9.6 1) add is Mobile in personalSign #### v1.9.4 1) remove Authereum & walletLink 2) DEPLOYMENT_STATUS #### v1.8.13 1)Update web3_defs.ts CREATION_CODE 2)submitDeployNFT transfer.maxFee tokenId: transfer.token.tokenId, #### v1.8.7 1)getAmmPoolTxs V3 #### v1.8.5 1)validateNftOrder #### v1.8.1 1)validateNftOrder 2)NFTCounterFactualInfo #### v1.7.9 1) nftMint NFTCounterFactualInfo ####v1.7.6 1) Doc update,UT update ####v1.7.2 1) NFT ACTION 2) AMM Bug ####v1.6.6 1) NFT MINT IPFS ####v1.6.5 1) The eddsaKey is generated by a keyseed, default format is 'Sign this message to access Loopring ####v1.6.3 1) Error format bug fix {code:number,message:string} ####v1.6.0 1) Error format `{code:number, msg|message: string}` ####v1.5.7 1) 721 NFT URI function #### v1.5.6 1) submitDeployNFT #### v1.5.2 1) NFT deposit #### v1.4.9 1) Counter Factual check 2) updateAccount #### v1.4.7 1) NFT META 2) HEBAO #### v1.4.5 1) CF wallet action sign update #### v1.4.4 (alpha) 1) fix unlock FC wallet #### v1.4.3 (alpha) 1) bug fix for NFT get contractMeta 2) FC wallet connect signature 3) Error code #### v1.4.2 1) update activity #### v1.3.10 1) Add recognizing header for web #### v1.3.9 1) Update getLatestTokenPrice url #### v1.3.8 1) SDK update for error type #### v1.3.4 - v1.3.7 destroy #### v1.3.3 1) add computeNFTAddress 2) add eslint and prettie for the code format #### v1.3.2 1) Fix some bug 2) Update withdraw NFT unit test #### v1.3.0 && v1.3.1 -- !!! revert formatter Uint8Array support # !!! revert ================================================ FILE: docs/README.md ================================================ # Loopring SDK [![license](https://img.shields.io/badge/license-Loopring-blue)](https://raw.githubusercontent.com/Loopring/loopring_sdk/master/LICENSE) [![type-badge](https://img.shields.io/npm/types/react-data-grid)](https://www.npmjs.com/package/react-data-grid) [![Discord](https://img.shields.io/discord/687207715902193673)](https://discord.com/invite/KkYccYp) ## 🚀 Quick Start ```shell # Using npm npm i @loopring-web/loopring-sdk --save # Using yarn yarn add @loopring-web/loopring-sdk ``` > Make sure you are using the original npm registry. > `npm config set registry http://registry.npmjs.org` ## 📒 Documentation Please see the [documentation page](https://loopring.github.io/loopring_sdk/) for information about getting started and developing with the Loopring SDK. - [JS SDK](https://loopring.github.io/loopring_sdk) - [Python](https://github.com/Loopring/hello_loopring) - [APIs](https://docs.loopring.io/en/) ## ✨ Changelog - [Changelog](https://loopring.github.io/loopring_sdk/ChangeLog.html) ## 🫂 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) ## 🎒 Getting Started Please see our [introduction page](https://loopring.github.io/loopring_sdk/) for details on integrating the SDK into your application. ## 🙋 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) ## 👉 [What is Loopring?](https://loopring.org/#/) ## 🔑 Security - [Wallet](https://security.loopring.io/) - [Protocol Audit](https://loopring.org/resources/loopring1.0_audit.pdf) ================================================ FILE: docs/SUMMARY.md ================================================ # Summary ### Overview - [Introduction](README.md) - [Changelog](Changelog.md) - [GitHub](https://github.com/Loopring/loopring_sdk) ### SDKs demo - [JS SDK](./js_sdk/README.md) - [Exchange](./js_sdk/exchange/exchange.md) - [Web Socket](./js_sdk/exchange/webSocket.md) - [Amm](./js_sdk/exchange/ammpool_api.md) - [Account](./js_sdk/README.md#mock-account) - [Active Account](./js_sdk/account/activeAccount.md) - [Fees](./js_sdk/account/fee.md) - [Signature](./js_sdk/account/signature.md) - [Transaction Recorder](./js_sdk/account/historyRecord.md) - [ERC20](./js_sdk/README.md#mock-erc20-token-map) - [Deposit](./js_sdk/deposit/depositERC20.md) - [Transfer](./js_sdk/transfer/transferERC20.md) - [Withdraw](./js_sdk/withdraw/withdrawERC20.md) - [Order](./js_sdk/erc20Trade/orderERC20.md) - [NFT](./js_sdk/README.md#mock-account) - [Deposit](./js_sdk/deposit/depositNFT.md) - [Transfer](./js_sdk/transfer/transferNFT.md) - [Withdraw](./js_sdk/withdraw/withdrawNFT.md) - [Mint](./js_sdk/NFTAction/mintNFT.md) - [MetaData](./js_sdk/NFTAction/metaNFT.md) - [Collection](./js_sdk/NFTAction/collectionNFT.md) - [Deploy](./js_sdk/NFTAction/deployNFT.md) - [Trade](./js_sdk/NFTAction/tradeNFT.md) - [Validate Order](./js_sdk/NFTAction/validateNFTOrder.md) ### Links - [Python](https://github.com/Loopring/hello_loopring) - [APIs](https://docs.loopring.io/en/) - [About us](https://loopring.org/#/) - [Submit a Request](https://desk.zoho.com/portal/loopring/en/newticket) ================================================ FILE: docs/about_us.md ================================================ ##### written by loopring dev team. ================================================ FILE: docs/js_sdk/Deposit.md ================================================ # Deposit from Ethereum L1 to Loopring, First Step for Start L2 - [Step1: getUser Layer1 ETH balance](#step1️⃣-getuser-layer1-eth-balance) - [ETH](#1-let-start-from-eth) - [ERC20](#2-erc20-token-such-as-lrc) - [NFT](#3-nft) - [Step2: Allow and Approve Loopring to get transaction those Token access](#step2️⃣-allow-and-approve-loopring-to-get-transaction-those-token-access) - [ETH](#1-eth-skip-this-step) - [ERC20](#2-erc20-token-such-as-lrc-1) - [NFT](#3-nft-1) - [Step3: Deposit](#step3️⃣-deposit) - [ETH](#1-eth-same-as-erc20-only-can-not-deposit-all-for-gas-cost) - [ERC20](#2-erc20-token-such-as-lrc-2) - [NFT](#3-nft-2) - [Additional & Reference](#additional--reference) - [Approve Simple Signature Demo](#approve-simple-signature-demo) - [Deposit Signature Demo](#deposit-signature-demo-fee-is-pay-by-eth-only-current-fee-is-0) >**All Deposit Method, User should have enough `ETH` pay for the >Ethereum Gas (Loopring have no charge, no fee for Deposit).** Before start read this Doc, make sure you understand how to create an We3 with an Ethereum provider Let's start step by step how to Deposit from Ethereum L1 to Loopring ( Demo as [Loopring JS SDK](https://loopring.github.io/loopring_sdk) ): Connect Wallet processing... When you connect with Wallet (EOA or Loopring Wallet), you will know your `accAddress = ${Account_Address}` and have a `web3` instance User has Three chooses Deposit `ETH`; `ERC20 Token (Such as LRC )`; `NFT (ERC721 & ERC1155)`; *** ### Step1️⃣: getUser Layer1 ETH balance #### 1. Let start from ETH ```ts const { ethBalance } = await LoopringAPI.exchangeAPI.getEthBalances({owner: accAddress}); ``` SDK: [getEthBalances](https://github.com/Loopring/loopring_sdk/blob/master/src/api/exchange_api.ts#L514) API: [/api/v3/eth/balances](https://uat2.loopring.io/api/v3/eth/balances?owner=0xfF7d59D9316EBA168837E3eF924BCDFd64b237D8) Choose deposit amount from UI >tips: user should keep some ETH pay for Ethereum Gas #### 2. ERC20 Token (Such as LRC) ```ts const { ethBalance } = await LoopringAPI.exchangeAPI.getEthBalances({owner: accAddress}); //tokenArr is Loopring supprot ERC20 TokenId Array.jion(',') const { tokenBalances } = await LoopringAPI.exchangeAPI.getTokenBalances({owner: accAddress, token: tokenArr.join()}) ``` API: [/api/v3/eth/tokenBalances](https://uat2.loopring.io/api/v3/eth/tokenBalances?owner=0xfF7d59D9316EBA168837E3eF924BCDFd64b237D8&token=0xfc28028d9b1f6966fe74710653232972f50673be%2C0x0000000000000000000000000000000000000000%2C0xd4e71c4bb48850f5971ce40aa428b09f242d3e8a%2C0xcd2c81b322a5b530b5fa3432e57da6803b0317f7%2C0x47525e6a5def04c9a56706e93f54cc70c2e8f165) > {tokenArr} = getMixMarkets() > SDK:[getMixMarkets](https://github.com/Loopring/loopring_sdk/blob/master/src/api/exchange_api.ts#L409) > API:[/api/v3/mix/markets](https://api.loopring.network/api/v3/mix/markets) Market pair reduce to Unique Token Name ['ETH','LRC','USDT',...] #### 3. NFT - Prepare Token Address `nftTokenAddress` - NFT ID `nftId` - Know NFT Type `ERC721` or `ERC1155` ```ts const response = await nft.getNFTBalance({ web3, account: accAddress, tokenAddress: nftTokenAddress, nftId: nftId, nftType: NFTType.ERC1155, }); ``` SDK: [getNFTBalance](https://github.com/Loopring/loopring_sdk/blob/master/src/api/nft_api.ts#L100) *** ### Step2️⃣: Allow and Approve Loopring to get transaction those Token access #### 1. ETH Skip this step #### 2. ERC20 Token (Such as LRC ) - check Allowances - getNonce web3.eth.getTransactionCount - contract.approveMax ```ts import {getTradeArg} from "./ws_defs"; const {tokenAllowances} = await exchangeAPI.getAllowances({ owner: accAddress, token: "LRC", }) if (tokenAllowances["LRC"] === undefined || tokenAllowances["LRC"] < getTradeArgValue){ const nonce = await web3.eth.getTransactionCount(accAddress); const response = await contract.approveMax( web3, accAddress, tokenAddress, // LRC address {tokenIdMap} = getTokens(); tokenIdMap['LRC'] depositAddress, //{exchangeInfo} = getExchangeInfo() exchangeInfo.depositAddress gasPrice, gasLimit, ChainId.GOERLI, nonce, true ); } ``` SDK:[getAllowances](https://github.com/Loopring/loopring_sdk/blob/master/src/api/exchange_api.ts#L583) API:[/api/v3/eth/allowances](https://uat2.loopring.io/api/v3/eth/allowances?owner=0xfF7d59D9316EBA168837E3eF924BCDFd64b237D8&token=0xfc28028d9b1f6966fe74710653232972f50673be) SDK:[getExchangeInfo](https://github.com/Loopring/loopring_sdk/blob/master/src/api/exchange_api.ts#L624) API:[/api/v3/exchange/info](https://uat2.loopring.io/api/v3/exchange/info) SDK:[getTokens](https://github.com/Loopring/loopring_sdk/blob/master/src/api/exchange_api.ts#L416) API:[/apiv3/exchange/tokens](https://uat2.loopring.io/api/v3/exchange/tokens) SDK:[approveMax](https://github.com/Loopring/loopring_sdk/blob/master/src/api/contract_api.ts#L271) [Approve Signature Demo](#approve-simple-signature-demo) #### 3. NFT - check nft isApprovedForAll - getNonce web3.eth.getTransactionCount - contract.approveNFT (ALL) by nftTokenAddress ```ts const isApproved = await nft.isApprovedForAll({ web3, from: accAddress, exchangeAddress: exchangeAddr, //{exchangeInfo} = getExchangeInfo() exchangeInfo.exchangeAddr nftType: NFTType.ERC1155, tokenAddress: nftTokenAddress, }); if(!isApproved){ const nonce = await web3.eth.getTransactionCount(accAddress); const response = await nft.approveNFT({ web3, from: accAddress, depositAddress, tokenAddress: nftTokenAddress, tokenId: nftId, nftType: NFTType.ERC1155, gasPrice, gasLimit, chainId: ChainId.GOERLI, nonce, approved: true, sendByMetaMask: true, }); } ``` SDK:[isApprovedForAll](https://github.com/Loopring/loopring_sdk/blob/master/src/api/nft_api.ts#L312) SDK:[approveNFT](https://github.com/Loopring/loopring_sdk/blob/master/src/api/nft_api.ts#L235) [Approve Signature Demo](#approve-simple-signature-demo) *** ### Step3️⃣: Deposit #### 1. ETH (Same as ERC20, only can not deposit all for Gas cost) #### 2. ERC20 Token (Such as LRC ) ```ts const nonce = await web3.eth.getTransactionCount(accAddress); const response = await contract.deposit( web3, accAddress, exchangeAddr, // {exchangeInfo} = getExchangeInfo() exchangeInfo.exchangeAddr tokenInfo, 10, // tradeValue 0, // fee 0 gasPrice, gasLimit, ChainId.GOERLI, nonce, true ); ``` SDK:[deposit](https://github.com/Loopring/loopring_sdk/blob/master/src/api/contract.ts#L300) [Deposit Signature Demo](#deposit-signature-demo-fee-is-pay-by-eth-only-current-fee-is-0) #### 3. NFT ```ts const nonce = await web3.eth.getTransactionCount(accAddress); const response = await nft.depositNFT({ web3, from:accAddress, exchangeAddress::exchangeAddr, //{exchangeInfo} = getExchangeInfo() exchangeInfo.exchangeAddr nftType: NFTType.ERC1155, tokenAddress: nftTokenAddress, amount: 1, gasPrice, gasLimit, chainId: ChainId.GOERLI, nonce, sendByMetaMask:true} ); ``` SDK:[depositNFT](https://github.com/Loopring/loopring_sdk/blob/master/src/api/nft_api.ts#L354) [Deposit Signature Demo](#deposit-signature-demo-fee-is-pay-by-eth-only-current-fee-is-0) *** ### Additional & Reference #### Approve Simple Signature Demo For more detail about genERC{XXX}Data please read [Contract ABI Specification](//https://docs.soliditylang.org/en/develop/abi-spec.html#) ```ts /* ERC20 Approve Data structure */ const data = genERC20Data(ERC20Method.Approve, { _spender: depositAddress, _value:"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", }); /* NFT setApprovalForAll Data structure */ // if (nftType === NFTType.ERC1155) { // data = this._genERC1155Data(NFTMethod.setApprovalForAll, { // operator: depositAddress, // approved, // }); // } else if (nftType === NFTType.ERC721) { // //TODO list not support now // data = this._genERC721Data(NFTMethod.setApprovalForAll, { // operator: depositAddress, // approved, // }); // } const gasPrice = fm.fromGWEI(gasPrice).toNumber(); web3.eth.sendTransaction({ from:accAddress, to: depositAddress, //{exchangeInfo} = getExchangeInfo() exchangeInfo.depositAddress value:"0", data, chainId, nonce, gasPrice, gasLimit, }).then((_error,transactionHash: string) =>{ if (!err) { resolve({ result: transactionHash }); } else { resolve({ error: { message: err.message } }); } }); ``` ### Deposit Signature Demo (fee is pay by ETH only current fee is 0) - tokenSymbol is ETH, sendTransaction value should be `amount + fee` - Other tokenSymbol as LRC sendTransaction value is `fee` - `fee` is pay by ETH only (current fee is 0) ```ts const tokenAddress = "0x?????????"; const tokenSymbol = "LRC"; const fee = 0; /* tokenSymbol is ETH, sendTransaction value should be `amount + fee` (current fee is 0) * other tokenSymbol as LRC addresss valueC is fee (current fee is 0) */ const value = tokenSymbol === "ETH"? amount + fee : fee; const data = genExchangeData(ERC20Method.Deposit, { tokenAddress, amount, from, to: from, extraData: "", }); /* NFT deposit Data structure */ // const data = genExchangeData(NFTMethod.depositNFT, { // from, // to: from, // nftType, // tokenAddress, // nftId, // amount, // extraData: extraData ? extraData : "", // }); web3.eth.sendTransaction({ from:accAddress, to:exchangeAddress, //{exchangeInfo} = getExchangeInfo() exchangeInfo.depositAddress value, data, chainId, nonce, gasPrice, gasLimit, }).then((_error,transactionHash: string) =>{ if (!err) { resolve({ result: transactionHash }); } else { resolve({ error: { message: err.message } }); } }); return await sendRawTx( web3, from, exchangeAddress, valueC.toFixed(), data, chainId, nonce, gasPrice, gasLimit, sendByMetaMask ); ``` last but not least, waiting and check your layer 2 account Balance... start Layer2 #### Reference - @Loopring-web/web3-provider Package & [DEMO](https://github.com/Loopring/web3-provider) - Loopring allow you directory dev with our [APIs](https://docs.loopring.io/en/) - For your Web-app & Dapp welcome use our [JS SDK](https://loopring.github.io/loopring_sdk) ================================================ FILE: docs/js_sdk/NFTAction/collectionNFT.md ================================================ # Collection NFT Definition: allow user create a NFT Collection with different Contract(token) address. - allow user mint the NFTs with collection - when collection deployed, NFTs will have different Contract(token) address in layer1 - Loopring own collection on L2, allow user to view/edit their Collection information. - will soon enable the "Import Collection" to manage legacy NFTs *** ## Step 1. get Account ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, // same as maxFee tokenId }, apiKey ); ``` *** ## Step 5. get collection Information(tokenAddress) ```ts const collectionRes = await LoopringAPI.userAPI .getUserOwenCollection({ owner: accInfo.owner, tokenAddress: mockData.nftTokenAddress, isMintable: true }, apiKey ) if ((collectionRes && ((collectionRes as sdk.RESULT_INFO).code || (collectionRes as sdk.RESULT_INFO).message)) || !collectionRes.collections.length ) { console.log("Collection is disable to mint "); throw "Collection is disable to mint "; } const collectionMeta = (collectionRes as any).collections[ 0 ] as CollectionMeta; const counterFactualNftInfo: NFTCounterFactualInfo = { nftOwner: accInfo.owner, nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[ sdk.ChainId.GOERLI ], nftBaseUri: collectionMeta.baseUri, }; ``` *** ## Step 6. fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: collectionMeta.contractAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey ); ``` *** ## Step7. Mint ```ts const response = await LoopringAPI.userAPI.submitNFTMint({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: collectionMeta.contractAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: "1", validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, amount: fee.fees[ "LRC" ].fee ?? "9400000000000000000", }, counterFactualNftInfo, royaltyPercentage: 5, forceToMint: true, // suggest use as false, for here is just for run test }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ____ ### ps: Mint with legacy nftFactory ! Mint NFT from this way has no collection information at deploy contract(tokenAdress is unique) *** #### Step 1,2,3,4 is same logic #### Step 5. get tokenAddress ```ts const counterFactualNftInfo = { nftOwner: accInfo.owner, nftFactory: sdk.NFTFactory[ sdk.ChainId.GOERLI ], nftBaseUri: "", }; const nftTokenAddress = LoopringAPI.nftAPI.computeNFTAddress(counterFactualNftInfo) .tokenAddress || ""; console.log("nftTokenAddress", nftTokenAddress); ``` *** #### Step 6. get fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: nftTokenAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey ); ``` #### Step 7. Mint ```ts const response = await LoopringAPI.userAPI.submitNFTMint({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: "1", validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, amount: fee.fees[ "LRC" ].fee ?? "9400000000000000000", }, royaltyPercentage: 5, counterFactualNftInfo, forceToMint: true, // suggest use as false, for here is just for run test }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ================================================ FILE: docs/js_sdk/NFTAction/deployNFT.md ================================================ # Deploy NFT Definition: Only nft minter can deploy NFT *** ## Step 1. get Account ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. get fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_DEPLOY, amount: "0", }, apiKey ); console.log(fee); ``` *** ## Step 5. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, // same as Step 7. transfer->token->tokenId }, apiKey ); ``` *** ## Step 6. broker ```ts const {broker} = await LoopringAPI.exchangeAPI.getAvailableBroker(); ``` *** ## Step 7. Build transfer & Deploy ```ts const transfer = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: LOOPRING_EXPORTED_ACCOUNT.address, payerId: LOOPRING_EXPORTED_ACCOUNT.accountId, payeeAddr: broker, // payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }; const response = await LoopringAPI.userAPI.submitDeployNFT({ request: { transfer, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log(response); ``` ================================================ FILE: docs/js_sdk/NFTAction/metaNFT.md ================================================ # NFT META METHODS *** ## getContractNFTMeta ```ts const result = await LoopringAPI.nftAPI.getContractNFTMeta({ web3, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); console.log(result); ``` *** ## getInfoForNFTTokens ```ts const response = await LoopringAPI.nftAPI.getInfoForNFTTokens({ nftDatas: [LOOPRING_EXPORTED_ACCOUNT.nftData], }); console.log(`getInfoForNFTTokens: response: `, JSON.stringify(response)); ``` *** ## computeNFTAddress ```ts const response = LoopringAPI.nftAPI.computeNFTAddress({ nftOwner: "0xE20cF871f1646d8651ee9dC95AAB1d93160b3467", nftFactory: "0x40F2C1770E11c5bbA3A26aEeF89616D209705C5D", }); console.log( `computeNFTAddress:`, response, "0xee354d81778a4c5a08fd9dbeb5cfd01a840a746d" ); ``` *** ## ipfsCid0ToNftID ```ts const ipfs = "QmNuqdeWUJ9iEiw5qZfJ2pJ9onqAS45ZffvV8JQSUzp7DQ"; const nftID = "0x0880847b7587968f32ba6c741f9d797d9dc64971979922a80c4e590453b8dc2f"; console.log( `ipfsCid0ToNftID: ipfs: `, ipfs, LoopringAPI.nftAPI.ipfsCid0ToNftID(ipfs) ); ``` *** ## ipfsNftIDToCid ```ts const ipfs = "QmNuqdeWUJ9iEiw5qZfJ2pJ9onqAS45ZffvV8JQSUzp7DQ"; const nftID = "0x0880847b7587968f32ba6c741f9d797d9dc64971979922a80c4e590453b8dc2f"; console.log( `ipfsCid0ToNftID: nftID: `, nftID, LoopringAPI.nftAPI.ipfsNftIDToCid(nftID) ); ``` ================================================ FILE: docs/js_sdk/NFTAction/mintNFT.md ================================================ # Mint NFT Definition: Mint Layer2 NFT, Loopring follow the ipfs NFT format, IPFS CID will convert to nftId, please view MetaNFT.md >

!!! important describe

>

Follow mehod is the simple way for mint NTF, but this kind of NFT will using the same contact & with no Contract metadata forever on L1

>

New Version of NFT will has it isolate Contract/colletion with metadata inforamtion

>

From Step 3. nftTokenAddress please follow create `collectionNFT` step create collection(contract), the api will return follow info for mint NFT

> ```ts tokenAddress: collectionMeta.contractAddress counterFactualNftInfo: { nftOwner: ccInfo.owner, nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[chainId], nftBaseUri: collectionMeta?.baseUri ?? "", }, ``` ## Step 1. get Account ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey({ accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, // same as maxFee tokenId }, apiKey ); ``` *** ## Step 5. get collection Information(tokenAddress) ```ts const collectionRes = await LoopringAPI.userAPI .getUserOwenCollection({ owner: accInfo.owner, tokenAddress: mockData.nftTokenAddress, isMintable: true }, apiKey ) if ((collectionRes && ((collectionRes as sdk.RESULT_INFO).code || (collectionRes as sdk.RESULT_INFO).message)) || !collectionRes.collections.length ) { console.log("Collection is disable to mint "); throw "Collection is disable to mint "; } const collectionMeta = (collectionRes as any).collections[ 0 ] as CollectionMeta; const counterFactualNftInfo: NFTCounterFactualInfo = { nftOwner: accInfo.owner, nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[ sdk.ChainId.GOERLI ], nftBaseUri: collectionMeta.baseUri, }; ``` *** ## Step 6. fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: collectionMeta.contractAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey ); ``` *** ## Step7. Mint ```ts const response = await LoopringAPI.userAPI.submitNFTMint({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: nftTokenAddress, // please read the description -> tokenAddress: collectionMeta.contractAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: "1", validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, amount: fee.fees[ "LRC" ].fee ?? "9400000000000000000", }, counterFactualNftInfo, royaltyPercentage: 5, forceToMint: true, // suggest use as false, for here is just for run test // please read the description // counterFactualNftInfo: { // nftOwner: ccInfo.owner, // nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[chainId], // nftBaseUri: collectionMeta?.baseUri ?? "", // }, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ____ ### ps: Mint with legacy nftFactory ! Mint NFT from this way has no collection information at deploy contract(tokenAdress is unique) *** #### Step 1,2,3,4 is same logic #### Step 5. get tokenAddress ```ts const counterFactualNftInfo = { nftOwner: accInfo.owner, nftFactory: sdk.NFTFactory[ sdk.ChainId.GOERLI ], nftBaseUri: "", }; const nftTokenAddress = LoopringAPI.nftAPI.computeNFTAddress(counterFactualNftInfo) .tokenAddress || ""; console.log("nftTokenAddress", nftTokenAddress); ``` *** #### Step 6. get fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: nftTokenAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey ); ``` #### Step 7. Mint ```ts const response = await LoopringAPI.userAPI.submitNFTMint({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: "1", validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, amount: fee.fees[ "LRC" ].fee ?? "9400000000000000000", }, royaltyPercentage: 5, counterFactualNftInfo, forceToMint: true, // suggest use as false, for here is just for run test }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ================================================ FILE: docs/js_sdk/NFTAction/tradeNFT.md ================================================ # Trade NFT Definition: This method is help for understand how to match a maker with a taker order *** ## tradeNFT > Private or third account can signature and approve this order [mock order](#MockOrder) ```ts // Step 1. getAccount const accInfoC = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) ).accInfo; // Step 1. eddsaKeyC const eddsaKeyC = await signatureKeyPairMock(accInfoC); // Step 3. apiKey const apiKeyC = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfoC.accountId, }, eddsaKeyC.sk ) ).apiKey; // NFT Trade const response = await LoopringAPI.userAPI.submitNFTTrade({ request: { maker: { ...mockData.makerOrder, eddsaSignature: mockData.makerOrderEddsaSignature, }, makerFeeBips: 1000, taker: { ...mockData.takerOrder, eddsaSignature: mockData.takerOrderEddsaSignature, }, takerFeeBips: 100, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, apiKey: apiKeyC, eddsaKey: eddsaKeyC.sk, }); console.log(response); ``` *** ## MockOrder > Validate NFT Order please reader @Validate NFT Order ```ts // Step 1. getAccount const accInfo = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) ).accInfo; const accInfo2 = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address2, }) ).accInfo; // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); const eddsaKey2 = await signatureKeyPairMock(accInfo2, web3_2); // Step 3. apiKey const apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; const apiKey2 = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo2.accountId, }, eddsaKey2.sk ) ).apiKey; // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); const storageId2 = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo2.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey2 ); // Step 5. generate Order, please read validateNFTOrder const makerOrder: sdk.NFTOrderRequestV3 = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, buyToken: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, allOrNone: false, fillAmountBOrS: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 1000, }; const makerOrderEddsaSignature = sdk.get_EddsaSig_NFT_Order( makerOrder, eddsaKey.sk ); const takerOrder: sdk.NFTOrderRequestV3 = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo2.accountId, storageId: storageId2.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, buyToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, allOrNone: false, fillAmountBOrS: true, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 100, }; const takerOrderEddsaSignature = sdk.get_EddsaSig_NFT_Order( takerOrder, eddsaKey2.sk ); mockData = { takerOrder, takerOrderEddsaSignature, makerOrder, makerOrderEddsaSignature, makerFeeBips: 1000, maxFeeBips: 100, }; ``` ================================================ FILE: docs/js_sdk/NFTAction/validateNFTOrder.md ================================================ # Validate NFT Order Definition: Loopring L2 support a method help for Validate NFT one side Order, validate NFT Order is not required for Loopring, but when make NFT Trade, it should pass this validation ## SellNFTByERC20 ```ts // Step 1. getAccount const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); // let hash: any = new BN(nftId,'hex') // hash = toHex(hash);//new BigInteger(sha256(nftId.toString()).toString(), 16) // Step 5. submitNFTValidateOrder const response = await LoopringAPI.userAPI.submitNFTValidateOrder({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, buyToken: { tokenId: 1, amount: "10000000000000", }, allOrNone: false, fillAmountBOrS: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 1000, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log("sellNFT NFTOrderRequestV3:", response); ``` *** ## BuyNFTByERC20 ```ts // Step 1. getAccount const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 5. submitNFTValidateOrder const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); const response = await LoopringAPI.userAPI.submitNFTValidateOrder({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: 1, amount: "10000000000000", }, buyToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, fillAmountBOrS: true, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 100, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ================================================ FILE: docs/js_sdk/README.md ================================================ # Loopring SDK Loopring SDK Initialize and Mock Data structure *** ## SDK Initialize ```ts import * as sdk from "../index"; export class LoopringAPIClass { public static userAPI: UserAPI; public static exchangeAPI: ExchangeAPI; public static ammpoolAPI: AmmpoolAPI; public static walletAPI: WalletAPI; public static wsAPI: WsAPI; public static nftAPI: NFTAPI; public static delegate: DelegateAPI; public static globalAPI: GlobalAPI; public static contractAPI: typeof ContractAPI; public static __chainId__: sdk.ChainId; public static InitApi = (chainId: sdk.ChainId) => { LoopringAPI.userAPI = new UserAPI({ chainId }); LoopringAPI.exchangeAPI = new ExchangeAPI({ chainId }); LoopringAPI.globalAPI = new GlobalAPI({ chainId }); LoopringAPI.ammpoolAPI = new AmmpoolAPI({ chainId }); LoopringAPI.walletAPI = new WalletAPI({ chainId }); LoopringAPI.wsAPI = new WsAPI({ chainId }); LoopringAPI.nftAPI = new NFTAPI({ chainId }); LoopringAPI.delegate = new DelegateAPI({ chainId }); LoopringAPI.__chainId__ = chainId; LoopringAPI.contractAPI = ContractAPI; }; } /* env: * test: sdk.ChainId.GOERLI * eth: sdk.ChainId.MAINNET */ LoopringAPIClass.InitApi({sdk.ChainId.MAINNET}); ``` *** ## Connect Wallet Suggestion web3 provider ```shell # Using npm npm i @loopring-web/web3-provider --save # Using yarn yarn add @loopring-web/web3-provider ``` - [Demo Vue](https://codesandbox.io/s/vue-8nco78) - [Demo React](https://codesandbox.io/s/react-4v50ft) [Mock Provider](#mock-provider) *** ## Loopring ERC20 Data Structure ```ts const {tokensMap, idIndex, addressIndex} = LoopringAPI.exchangeAPI.getTokens(); ``` [Mock ERC20](#mock-erc20-token-map) *** ## Token Decimal ```ts const uiValue = 100; const tradeValue = sdk.toBig(uiValue).times("1e" + TOKEN_INFO.tokenMap.LRC.decimals); ``` *** ## Test Mock Data ##### Mock Account ```ts export const LOOPRING_EXPORTED_ACCOUNT = { address: "0x727e0fa09389156fc803eaf9c7017338efd76e7f", privateKey: "491aecdb1d5f6400a6b62fd12a41a86715bbab675c37a4060ba115fecf94083c", accountId: 12454, address2: "0xb6d8c39D5528357dBCe6BEd82aC71c74e9D19079", privateKey2: "e020ed769032ba95d9a5207687a663d6198fe2f5cedf28a250f7cbd8c81a5263", accountId2: 10488, addressCF: "0x23dE4Da688c94a66E8bbE9BCc95CB03b4e209C15", accountIdCF: 11632, addressContractWallet: "0xD4BD7c71B6d4A09217ccc713f740d6ed8f4EA0cd", depositAddress: "0xb684B265f650a77afd27Ce0D95252a7329B5bD72", exchangeAddress: "0x2e76EBd1c7c0C8e7c2B875b6d505a260C525d25e", whitelistedAddress: "0x35405E1349658BcA12810d0f879Bf6c5d89B512C", whitelistedEddkey: "0x27a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33", // const eddkeyWhitelisted = // "0x27a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33"; // apiKey: "2PYgTOZwXHkPXtJMlOMG06ZX1QKJInpoky6iYIbtMgmkbfdL4PvxyEOj0LPOfgYX", chainId: 5, nftTokenAddress: "0x8394cB7e768070217592572582228f62CdDE4FCE", nftTokenId: 32768, nftId: "0xa0ce8990402955e559799af24ea765b14ffecc32dfa1cce2dadaf20016b074e6", nftData: "0x1a2001aac7a1fd00cef07889cdb67b1355f86e5bc9df71cfa44fa1c7b49f598f", testNotOx: "727e0fa09389156fc803eaf9c7017338efd76e7f", tradeLRCValue: 1000000000000000000, tradeETHValue: 0.0001, //same as UI gasPrice: 20, // for test gasLimit: 200000, // for test validUntil: Math.round(Date.now() / 1000) + 30 * 86400, }; export const CUSTOMER_KEY_SEED = "XXXXXX" + " with key nonce: " + "${nonce}"; ``` ##### Mock provider ```ts const provider = new PrivateKeyProvider( LOOPRING_EXPORTED_ACCOUNT.privateKey, "https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3" ); const provider2 = new PrivateKeyProvider( LOOPRING_EXPORTED_ACCOUNT.privateKey2, "https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3" ); export const web3 = new Web3(provider); export const web3_2 = new Web3(provider2); ``` ##### Mock ERC20 Token Map ```ts export let TOKEN_INFO = { addressIndex: { "0x0000000000000000000000000000000000000000": "ETH", "0xfc28028d9b1f6966fe74710653232972f50673be": "LRC", "0xd4e71c4bb48850f5971ce40aa428b09f242d3e8a": "USDT", "0xfeb069407df0e1e4b365c10992f1bc16c078e34b": "LP-LRC-ETH", "0x049a02fa9bc6bd54a2937e67d174cc69a9194f8e": "LP-ETH-USDT", "0xcd2c81b322a5b530b5fa3432e57da6803b0317f7": "DAI", "0x47525e6a5def04c9a56706e93f54cc70c2e8f165": "USDC", "0xf37cf4ced77b985708d591acc6bfd08586ab3409": "LP-USDC-ETH", }, 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, }, }, }; ``` ##### Mock AMM MAP ```ts export let AMM_MAP = { "AMM-LRC-ETH": { name: "LRCETH-Pool", market: "AMM-LRC-ETH", address: "0xfEB069407df0e1e4B365C10992F1bc16c078E34b", version: "1.0.0", tokens: { pooled: [1, 0], lp: 4 }, feeBips: 20, precisions: { price: 6, amount: 5 }, createdAt: "1617967800000", status: 31, }, "AMM-ETH-USDT": { name: "AMM-ETH-USDT", market: "AMM-ETH-USDT", address: "0x049a02FA9bc6bd54a2937E67D174cc69a9194f8e", version: "1.0.0", tokens: { pooled: [0, 2], lp: 7 }, feeBips: 20, precisions: { price: 3, amount: 3 }, createdAt: "1617972300000", status: 31, }, "AMM-USDC-ETH": { name: "AMM-USDC-ETH", market: "AMM-USDC-ETH", address: "0xf37cf4CEd77b985708D591AcC6BfD08586Ab3409", version: "1.0.0", tokens: { pooled: [8, 0], lp: 9, }, feeBips: 20, precisions: { price: 3, amount: 4, }, createdAt: "1636974420000", status: 0, }, }; ``` ##### Mock EIP712 Typed Data ```ts export const testTypedData: EIP712TypedData = { types: { EIP712Domain: [ { name: "name", type: "string" }, { name: "version", type: "string" }, { name: "chainId", type: "uint256" }, { name: "verifyingContract", type: "address" }, ], TestTypedData: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "tokenID", type: "uint16" }, ], }, primaryType: "TestTypedData", domain: { name: "Loopring Protocol", version: "3.6.0", chainId: sdk.ChainId.GOERLI, verifyingContract: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, }, message: { from: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address2, tokenID: TOKEN_INFO.tokenMap.LRC.tokenId, }, }; ``` ##### Mock generate eddsaKey ```ts export async function signatureKeyPairMock( accInfo: sdk.AccountInfo, _web3: Web3 = web3 ) { const eddsaKey = await sdk.generateKeyPair({ web3: _web3, address: accInfo.owner, keySeed: accInfo.keySeed ?? sdk.GlobalAPI.KEY_MESSAGE.replace( "${exchangeAddress}", LOOPRING_EXPORTED_ACCOUNT.exchangeAddress ).replace("${nonce}", (accInfo.nonce - 1).toString()), walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); return eddsaKey; } ``` ================================================ FILE: docs/js_sdk/account/activeAccount.md ================================================ # Active Account Definition: After user Deposit or (Third-Part Transfer), how to active Loopring L2 account. *** ## Step 1. get account info ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); ``` *** ## Step 2. use keySeed or CUSTOMER_KEY_SEED generateKeyPair ```ts const keySeed = sdk.BaseAPI.KEY_MESSAGE.replace( "${exchangeAddress}", LOOPRING_EXPORTED_ACCOUNT.exchangeAddress ).replace("${nonce}", accInfo.nonce.toString()); const eddsaKey = await sdk.generateKeyPair({ web3, address: accInfo.owner, keySeed, walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); console.log("eddsakey:", eddsaKey.sk); ``` Or ```ts // CUSTOMER_KEY_SEED = "XXXXXX" + " with key nonce: " + "${nonce}"; const keySeed = CUSTOMER_KEY_SEED.replace( "${nonce}", accInfo.nonce.toString() const eddsaKey = await sdk.generateKeyPair({ web3, address: accInfo.owner, keySeed, walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); console.log("eddsakey:", eddsaKey.sk); ``` *** ## Step 3. get fee ```ts const fee = await LoopringAPI.globalAPI.getActiveFeeInfo({ accountId: accInfo.accountId, }); console.log("fee:", fee); ``` *** ## Step 4. updateAccount (active or rest) ```ts const result = await LoopringAPI.userAPI.updateAccount({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, owner: accInfo.owner, accountId: accInfo.accountId, publicKey: {x: eddsaKey.formatedPx, y: eddsaKey.formatedPy}, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, keySeed, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, nonce: accInfo.nonce as number, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, isHWAddr: false, }); const {accInfo: updateAccountInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log( "updateAccount Result: ", result, "updateAccountInfo:", updateAccountInfo ); ``` ================================================ FILE: docs/js_sdk/account/fee.md ================================================ # Fee Loopring have 2 get Fee api: - getOffchainFeeAmt: - `ORDER`, - `FFCHAIN_WITHDRAWAL`, - `UPDATE_ACCOUNT`, - `TRANSFER`, - `FAST_OFFCHAIN_WITHDRAWAL`, - `OPEN_ACCOUNT`, - `AMM_EXIT`, - `DEPOSIT`, - `AMM_JOIN`, - getNFTOffchainFeeAmt: - `NFT_MINT`, - `NFT_WITHDRAWAL`, - `NFT_TRANSFER`, - `NFT_DEPLOY`, *** ## Fee: updateAccount ```ts // Step 1. get account info console.log(LoopringAPI.exchangeAPI.getAccount); const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.UPDATE_ACCOUNT, }, apiKey ); console.log("updateAccount:", response); ``` *** ## Fee: transfer ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.TRANSFER, }, apiKey ); console.log("transfer:", response); ``` *** ## Fee: withdraw ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, }, apiKey ); console.log("withdraw:", response); ``` *** ## Fee: fastWithdraw ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, }, apiKey ); console.log("fastWithdraw:", response); ``` *** ## Fee: order ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.ORDER, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, }, apiKey ); console.log("order:", response); ``` *** ## Fee: amm_exit ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.AMM_EXIT, }, apiKey ); console.log("amm_exit:", response); ``` *** ## Fee: amm_join ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.AMM_JOIN, }, apiKey ); console.log("amm_join:", response); ``` *** ## Fee: NFT Transfer ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, }, apiKey ); console.log("NFTTransfer:", response); ``` *** ## Fee: NFT Withdrawal ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, }, apiKey ); console.log("NFTWithdrawal:", response); ``` ## Fee: NFT Mint ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress }, apiKey ); console.log("NFTWithdrawal:", response); ``` ## Fee: NFT Deploy ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount( {owner: LOOPRING_EXPORTED_ACCOUNT.address,}); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_DEPLOY, }, apiKey ); console.log("NFTWithdrawal:", response); ``` *** ## getActiveFeeInfo without apikey & accountId ```ts const response = await LoopringAPI.globalAPI.getActiveFeeInfo({}); ``` *** ## getActiveFeeInfo without apikey with accountId ```ts const response = await LoopringAPI.globalAPI.getActiveFeeInfo({accountId: LOOPRING_EXPORTED_ACCOUNT.accountId}); ``` ================================================ FILE: docs/js_sdk/account/historyRecord.md ================================================ # User Actions History For check account Actions, more detail such as filters please read SDK interface or API . *** ## getUserTrades ```ts async () => { const result = await LoopringAPI.userAPI.getUserTrades( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, offset: 0, limit: 20, fillTypes: sdk.TradesFillTypes.dex, }, apiKey ); console.log("getUserTrades:", result); ``` *** ## getUserTxs ```ts async () => { const result = await LoopringAPI.userAPI.getUserTxs( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, types: [ sdk.UserTxTypes.DEPOSIT, sdk.UserTxTypes.TRANSFER, sdk.UserTxTypes.ONCHAIN_WITHDRAWAL, ], }, apiKey ); console.log("getUserTxs:", result); ``` *** ## getUserNFTTransactionHistory ```ts async () => { const result = await LoopringAPI.userAPI.getUserNFTTransactionHistory( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, types: [ sdk.UserNFTTxTypes.DEPOSIT, sdk.UserNFTTxTypes.TRANSFER, sdk.UserNFTTxTypes.WITHDRAW, sdk.UserNFTTxTypes.MINT, ], }, apiKey ); console.log("getUserNFTTransactionHistory:", result); ``` *** ## getOrders ```ts async () => { const result = await LoopringAPI.userAPI.getOrders( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, orderTypes: sdk.OrderType.LimitOrder, }, apiKey ); console.log("getOrders:", result); ``` ================================================ FILE: docs/js_sdk/account/signature.md ================================================ # Signature Loopring SDK support EOA (EOA hardware wallet) & Loopring Smart wallet Signature - For Browser extension, Dapp, Hardware wallet we only support for EOA - For Loopring Smart wallet (App), Provider gateway only can be walletConnect ## Follow is the provider gateway we inject & test: - MetaMask (Ledger, Trezor) - WalletConnect (Authereum, Loopring Smart wallet ) - Coinbase - [Coming soon...](https://desk.zoho.com/portal/loopring/en/newticket) ## For signature: For eth_sign signing types (eth_sign, personal_sign, v1, v3, v4) ### EOA: - For Browser extension ([More information: signing-data](https://docs.metamask.io/guide/signing-data.html#a-brief-history)) + common EOA we use the `v4` signature and `web3.eth.personal.ecRecover` validate signature + when `v4` signature is failed for any step, we will try `personal_sign` and `web3.eth.personal.ecRecover` validate signature - For Dapp + when loopring Dex is inside Dapp Webview & connect by `window.ethereum`, we remove the `web3.eth.personal.ecRecover` validate ### Loopring Smart wallet: - For Smart wallet we send `eth_signTypedData` by walletConnect & validate ABI.Contracts.ContractWallet.encodeInputs `isValidSignature(bytes32,bytes)` ### Loopring Counterfactual wallet: - signature is same as Smart wallet - But ecRecover is by walletOwner, `const {walletOwner} = await LoopringAPI.exchangeAPI.getCounterFactualInfo({ accountId: LOOPRING_EXPORTED_ACCOUNT.accountIdCF, });` > ❗ when add `SigSuffix` `02|03` ( follow EIP712 + `02`, personal_sign + `03`) >- for `v4` ecdsaSignature the result signature should + `SigSuffix.Suffix02`; >- for `personal_sign` ecdsaSignature the result signature should + `SigSuffix.Suffix03`; *** ## generateKeyPair ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const result = await signatureKeyPairMock(accInfo); console.log(result.sk); ``` *** ## getEcDSASig: eth_signTypedData_v4 ```ts // test case is not allow brock by Mock provider const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.HasDataStruct, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, "", sdk.ConnectorNames.Unknown ); console.log("getEcDSASig:eth_signTypedData_v4", result, "ecdsaSig+sdk.SigSuffix.Suffix02", result.ecdsaSig + sdk.SigSuffix.Suffix02 ); ``` *** ## getEcDSASig: personalSign(WithoutDataStruct--Hardware wallet) ```ts const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.WithoutDataStruct, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, "", sdk.ConnectorNames.Unknown ); console.log( "getEcDSASig:WithoutDataStruct(personalSign)", result, "ecdsaSig+sdk.SigSuffix.Suffix03", result.ecdsaSig + sdk.SigSuffix.Suffix03 ); ``` *** ## getEcDSASig: personalSign(Contract) ```ts // test case is not allow brock by Mock provider const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.Contract, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, "", sdk.ConnectorNames.Unknown ); console.log( "getEcDSASig:personalSign(Contract)", result ); ``` ## Validate signature [github: src/api/base_api.ts#personalSign](https://github.com/Loopring/loopring_sdk/blob/2c79c1837114f4f383e2d292de3da4b2dac02252/src/api/base_api.ts#L549) ``` export async function personalSign( web3: any, account: string | undefined, pwd: string, msg: string, walletType: ConnectorNames, chainId: ChainId, accountId?: number, counterFactualInfo?: CounterFactualInfo, isMobile?: boolean ) { if (!account) { return { error: "personalSign got no account" }; } return new Promise((resolve) => { try { web3.eth.personal.sign( msg, account, pwd, async function (err: any, result: any) { if (!err) { // Valid:1. counter Factual signature Valid if (counterFactualInfo && accountId) { myLog("fcWalletValid counterFactualInfo accountId:"); const fcValid = await fcWalletValid( web3, account, msg, result, accountId, chainId, counterFactualInfo ); if (fcValid.result) { resolve({ sig: result, counterFactualInfo: fcValid.counterFactualInfo, }); return; } } // Valid: 2. webview directory signature Valid if ( (window?.ethereum?.isImToken || window?.ethereum?.isMetaMask) && isMobile && // Mobile directory connect will sign ConnectorNames as MetaMask only walletType === ConnectorNames.MetaMask ) { const address: string[] = await window.ethereum?.request({ method: "eth_requestAccounts", }); if ( address?.find( (item) => item.toLowerCase() === account.toLowerCase() ) ) { return resolve({ sig: result }); } } // Valid: 3. EOA signature Valid by ecRecover const valid: any = await ecRecover(web3, account, msg, result); if (valid.result) { return resolve({ sig: result }); } // Valid: 4. contractWallet signature Valid `isValidSignature(bytes32,bytes)` const walletValid2: any = await contractWalletValidate32( web3, account, msg, result ); if (walletValid2.result) { return resolve({ sig: result }); } // Valid: 5. counter Factual signature Valid when no counterFactualInfo if (accountId) { const fcValid = await fcWalletValid( web3, account, msg, result, accountId, chainId ); if (fcValid.result) { return resolve({ sig: result, counterFactualInfo: fcValid.counterFactualInfo, }); } } // Valid: 6. myKeyValid Valid again const myKeyValid: any = await mykeyWalletValid( web3, account, msg, result ); if (myKeyValid.result) { return resolve({ sig: result }); } // Valid: Error cannot pass personalSign Valid // eslint-disable-next-line no-console console.log( "web3.eth.personal.sign Valid, valid 5 ways, all failed!" ); return resolve({ error: "web3.eth.personal.sign Valid, valid 5 ways, all failed!", }); } else { return resolve({ error: "personalSign err before Validate:" + err, }); } } ); } catch (reason) { resolve({ error: reason }); } }); } ``` ================================================ FILE: docs/js_sdk/account/wallet_api.md ================================================ # Whitelisted User Part ##### 1) getUserAssets ```javascript // step 1. get account info const request: GetUserAssetsRequest = { wallet: "0xeF041462825bFdF79b2f1f02A70b2753cB5b1516", offset: 10, limit: 10, }; const response = await api.getUserAssets(request); ``` ##### 2) getTokenPrices ```javascript // step 1. get account info const request: GetTokenPricesRequest = { token: "0xdac17f958d2ee523a2206206994597c13d831ec7", }; const response = await api.getTokenPrices(request); ``` ##### 3) getLatestTokenPrices ```javascript // step 1. get account info const response = await api.getLatestTokenPrices(); ``` ================================================ FILE: docs/js_sdk/account/whitelisted_user_api.md ================================================ # Whitelisted User Part ## submitInternalTransfer ```javascript // step 1. get account info let addressWhitlisted = "0x35405E1349658BcA12810d0f879Bf6c5d89B512C"; let eddkeyWhitelisted = "0x27a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33"; const { accInfo } = await exchange.getAccount({ owner: addressWhitlisted }); console.log("accInfo:", accInfo); const { exchangeInfo } = await exchange.getExchangeInfo(); // step 2 get apikey const request: GetUserApiKeyRequest = { accountId: accInfo.accountId, }; const { apiKey } = await userApi.getUserApiKey(request, eddkeyWhitelisted); console.log("apiKey:", apiKey); // step 3 get storageId const request2: GetNextStorageIdRequest = { accountId: accInfo.accountId, sellTokenId: 1, }; const storageId = await userApi.getNextStorageId(request2, apiKey); // step 4 transfer const request3: OriginTransferRequestV3 = { exchange: exchangeInfo.exchangeAddress, payerAddr: addressWhitlisted, payerId: accInfo.accountId, payeeAddr: "0xb6AdaC3e924B4985Ad74646FEa3610f14cDFB79c", payeeId: 0, storageId: storageId.offchainId, token: { tokenId: 1, volume: "100000000000000000000", }, maxFee: { tokenId: 1, volume: "9400000000000000000", }, validUntil: VALID_UNTIL, }; console.log("request3:", request3); const response = await whitelistedUserApi.submitInternalTransfer( request3, eddkeyWhitelisted, apiKey ); console.log(response); ``` ## submitOffchainWithdraw is unavailable. TODO: submitOffchainWithdraw example ================================================ FILE: docs/js_sdk/deposit/depositERC20.md ================================================ # Deposit ERC20 Definition: Move user L1 ERC20 assets to Loopring L2 > **All Deposit Method, User should have enough `ETH` pay for the Ethereum Gas (Loopring have no charge, no fee for Deposit).** Provider will give the Gas Price & Limit, sdk also have a method get gasPrice: `const gasPrice = (await LoopringAPI.exchangeAPI.getGasPrice() ).gasPrice;` # ETH *** ## Step 1. getNonce ```ts const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: ${TOKEN_INFO.tokenMap.ETH.symbol}-${LOOPRING_EXPORTED_ACCOUNT.tradeETHValue}, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); ``` *** ## Step 2. deposit ```ts const response = await sdk.deposit( web3, LOOPRING_EXPORTED_ACCOUNT.address, LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, TOKEN_INFO.tokenMap.ETH, LOOPRING_EXPORTED_ACCOUNT.tradeETHValue, 0, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); console.log(`nonce: ${nonce} deposit_ETH: `, response); ``` # ERC20 *** ## Step 1. getAllowances ```ts const {tokenAllowances} = await LoopringAPI.exchangeAPI.getAllowances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, token: [TOKEN_INFO.tokenMap.LRC.address], }); if ( tokenAllowances.has(TOKEN_INFO.tokenMap.LRC.address) && Number(tokenAllowances.get(TOKEN_INFO.tokenMap.LRC.address)) < LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue ) { const nonce = await web3.eth.getTransactionCount( LOOPRING_EXPORTED_ACCOUNT.address ); await sdk.approveMax( web3, LOOPRING_EXPORTED_ACCOUNT.address, TOKEN_INFO.tokenMap.LRC.address, // LRC address {tokenIdMap} = getTokens(); tokenIdMap['LRC'] LOOPRING_EXPORTED_ACCOUNT.depositAddress, //{exchangeInfo} = getExchangeInfo() exchangeInfo.depositAddress LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); } ``` *** ## Step 2. getNonce ```ts const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: ${TOKEN_INFO.tokenMap.LRC.symbol}-${LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue}, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); ``` *** ## Step 3. deposit ```ts const response = await sdk.deposit( web3, LOOPRING_EXPORTED_ACCOUNT.address, LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, TOKEN_INFO.tokenMap.LRC, sdk .toBig(LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue) .div("1e" + TOKEN_INFO.tokenMap.LRC.decimals) .toNumber(), 0, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); console.log(`nonce: ${nonce} deposit_LRC: `, response); ``` ================================================ FILE: docs/js_sdk/deposit/depositNFT.md ================================================ # Deposit NFT Definition: Move user L1 NFT assets to Loopring L2 > **All Deposit Method, User should have enough `ETH` pay for the Ethereum Gas (Loopring have no charge, no fee for Deposit).** *** ## Step 1. getNFTBalance & getEthBalances ```ts const {ethBalance} = await LoopringAPI.exchangeAPI.getEthBalances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const nftBalance = await LoopringAPI.nftAPI.getNFTBalance({ web3, account: LOOPRING_EXPORTED_ACCOUNT.address, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); ``` *** ## Step 2. isApprovedForAll ```ts const isApprovedForAll = await LoopringAPI.nftAPI.isApprovedForAll({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, exchangeAddress: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, nftType: sdk.NFTType.ERC1155, // todo: sdk.NFTType.ERC721 tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, }); console.log(`check is approveNFT`, isApprovedForAll); ``` *** ## Step 3. approveNFT All ```ts if (!isApprovedForAll) { const nonce = await sdk.getNonce( web3, LOOPRING_EXPORTED_ACCOUNT.address ); const approveNFT = await LoopringAPI.nftAPI.approveNFT({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, depositAddress: LOOPRING_EXPORTED_ACCOUNT.depositAddress, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftType: sdk.NFTType.ERC1155, // todo: sdk.NFTType.ERC721 gasPrice: LOOPRING_EXPORTED_ACCOUNT.gasPrice, gasLimit: LOOPRING_EXPORTED_ACCOUNT.gasLimit, chainId: sdk.ChainId.GOERLI, nonce, sendByMetaMask: true, }); console.log(`nonce: ${nonce} approveNFT: ${approveNFT?.result}`); } ``` *** ## Step 3. nonce ```ts const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: NFT, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); ``` *** ## Step 4. depositNFT ```ts const response = await LoopringAPI.nftAPI.depositNFT({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, exchangeAddress: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, nftType: sdk.NFTType.ERC1155, // todo: sdk.NFTType.ERC721 tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, amount: 2, // todo:when sdk.NFTType.ERC721 amount: 1, gasPrice: LOOPRING_EXPORTED_ACCOUNT.gasPrice, gasLimit: LOOPRING_EXPORTED_ACCOUNT.gasLimit + 100000, chainId: sdk.ChainId.GOERLI, nonce, sendByMetaMask: true, }); console.log(`nonce: ${nonce} deposit NFT ERC1155: `, response); ``` ================================================ FILE: docs/js_sdk/erc20Trade/orderERC20.md ================================================ # Order ERC20 *** > ### mini-order To support small quantity trading, we introduce an additional concept "tradeCost", which is the minimum gas fee when a trade transaction is uplink to Ethereum. Let's take LRC-ETH trading as an example. Below are the steps - **1) Query api/v3/exchange/tokens to get the dust value of orderAmounts for both LRC and ETH.** The dust value is the minimum value to pass Relayer check. Any amount less than "dust" can't be traded. In this case, we will get both minTOkenLRC and minTokenETH after getting dust value. If user wants to convert LRC to ETH, the set LRC amount can't be less than minTokenLRC and the converted ETH amount can't be less than minTokenETH. **2) Query api/v3/user/orderUserRateAmount to get the tradeCost value.** The parameters to call this interface are "accountId" and "market=LRC-ETH". In this example, we will get two tradeCost values for LRC and ETH as tradeCostLRC and tradeCostETH. **3) Set maxAllowBips = 50% as the maxFeeBips can't exceed 50%** **4) Set slippage as the slippage value user configured in UI (for example 0.1%)** **5)Caculate minCostLRC and minCostETH as below** minCostLRC = max(minTokenLRC, tradeCostLRC/maxAllowBips) minCostETH = max(minTokenETH, tradeCostETH/maxAllowBips) **6) Caculate the cost by considering slippage** minCostLRCSlip = minCostLRC/(1-slippage) minCostETHSlip = minCostETH/(1-slippage) **7) Cacluate the minimum quantity user has to set** tradeCostSellLRC = max(tradeCostSellLRC, minTokenLRC) * 1.1 tradeCostSellETH = max(tradeCostSellETH, minTokenETH) * 1.1 Here we add additonally 10% tolerance. **8) Caculate the previous minimum token amount per calling api/v3/user/orderUserRateAmount (existing logic)** This is the threshold to distinguish small quantity trading and normal trading We will get two values (configSellLRC and configSellETH) which are used for previous trading quantity limit (Per USD 100) caculation **9)Caculate the new maxFeeBips and start trading** Let's take LRC->ETH as the example User inputs the amount of LRC to convert, amount = sellLRC if sellLRC >= configSellLRC then // Normal trading case, stay with previous logic maxFeeBips=63 (the default value for maxFeeBips) Trade else if sellLRC < tradeCostSellLRC then // Really too small to support Prompt user the amount is too small to support Exit else // This is what we call as small quantity costRate = Ceil(tradeCostETH/minbuyETH) maxFeeBips = max(costRate, takerRate) Trade End If > ### price impact update > 1. sellTokenMinAmount = baseOrderInfo.minAmount from LoopringAPI.userAPI.getMinimumTokenAmt({accountId,marke}, apiKey); > 2. {output} from sdk.getOutputAmount(input: sellTokenMinAmount, isAtoB: isAtoB,…}).output > 3. PriceBase = output / sellTokenMinAmount > 4. tradePrice = calcTradeParams.minReceive / userInputSell > 5. priceImpact = 1 - tradePrice/PriceBase - 0.005 > 6. If priceImpact < 0 priceImpact = 0 > Else priceImpact ## calculateSwap function ```ts const calculateSwap = ( sellSymbol = "LRC", buySymbol = "ETH", isInputSellToBuy: boolean, inputValue: number, // user Input value no decimal, _slippage = 0.1, // MOCK value amountMap: { [key: string]: any } = userAmount, market: string = deepMock.symbol, // close = ticker.tickers[7], depth: any = deepMock, ammPoolSnapshot: sdk.AmmPoolSnapshot = ammPoolSnapshotMock, tokenMap: sdk.LoopringMap = TokenMapMockSwap, ammMap: { [key: string]: any } = AMM_MAP ) => { let calcFor100USDAmount, calcForMinCost, calcForPriceImpact; if (depth && market && tokenMap) { const sellToken = tokenMap[sellSymbol]; const buyToken = tokenMap[buySymbol]; const isInputSellOutputBuy = isInputSellToBuy; let input: any = inputValue; console.log( "sellToken: Symbol ", sellSymbol, "buyToken: Symbol", buySymbol, "is Input Sell Output Buy:", isInputSellOutputBuy, "input value", input ); input = input === undefined || isNaN(Number(input)) ? 0 : Number(input); let slippage = sdk.toBig(_slippage).times(100).toString(); let totalFee = undefined; let feeTakerRate = undefined; let feeBips = undefined; let takerRate = undefined; let buyMinAmtInfo = undefined; let sellMinAmtInfo = undefined; let tradeCost = undefined; let basePrice = undefined; let maxFeeBips = MAPFEEBIPS; let minAmt = undefined; if (amountMap && amountMap[market] && ammMap) { console.log(`amountMap[${market}]:`, amountMap[market]); const ammMarket = `AMM-${market}`; const amountMarket = amountMap[market]; // userAmount from LRC-ETH(Market) buyMinAmtInfo = amountMarket[buySymbol]; sellMinAmtInfo = amountMarket[sellSymbol]; console.log( `buyMinAmtInfo: ${market}, ${buySymbol}`, buyMinAmtInfo, `sellMinAmtInfo: ${market}, ${sellSymbol}`, sellMinAmtInfo ); feeBips = ammMap[ammMarket] ? ammMap[ammMarket].feeBips : 1; feeTakerRate = amountMarket[buySymbol] && amountMarket[buySymbol].userOrderInfo.takerRate; tradeCost = amountMarket[buySymbol].tradeCost; /** @description for charge fee calc, calcFor100USDAmount * Loopring market consider buyToken value small then max(buyMinAmtInfo.userOrderInfo.minAmount,buyToken.orderAmounts.dust) is a small order, * the fee will take the Max(tradeCost,userTakeRate) * use the buyMinAmount Input calc the selltoken value, * please read Line:321 * **/ const minAmountInput = BigNumber.max( buyMinAmtInfo.userOrderInfo.minAmount, buyToken.orderAmounts.dust ) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div("1e" + buyToken.decimals) .toString(); calcFor100USDAmount = sdk.getOutputAmount({ input: minAmountInput, sell: sellSymbol, buy: buySymbol, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: slippage, }); console.log( "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: sellSymbol, buy: buySymbol, isAtoB: true, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: "10", }); basePrice = sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput); console.log( "calcForPriceImpact input: ", sellMinAmtInput, ", output: ", sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput).toNumber(), ", calcForPriceImpact:", calcForPriceImpact?.amountBOutSlip?.minReceivedVal, ", calcForPriceImpact basePrice: ", basePrice.toNumber() ); /**** calc for mini Cost ****/ //minCostBuyToken = max(dustBuyToken, tradeCostETH/maxAllowBips) const dustToken = buyToken; let minCostBuyTokenInput = BigNumber.max( sdk.toBig(tradeCost).times(2), //maxAllowBips = 50% tradeCostETH/50% dustToken.orderAmounts.dust ); const tradeCostInput = sdk .toBig(minCostBuyTokenInput) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div("1e" + dustToken.decimals) .toString(); console.log( `tradeCost: ${tradeCost}*2:`, sdk.toBig(tradeCost).times(2).toString(), "buyToken.orderAmounts.dust", buyToken.orderAmounts.dust, "minCostBuyToken:", minCostBuyTokenInput.toString(), `calcForMinCostInput, with slippage:${slippage}`, sdk .toBig(minCostBuyTokenInput ?? 0) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .toString(), "calcForMinCost, Input", tradeCostInput ); calcForMinCost = sdk.getOutputAmount({ input: tradeCostInput, sell: sellSymbol, buy: buySymbol, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: slippage, }); //add additionally 10% tolerance for minimum quantity user has to set on sell Token /** * @output: minAmt for UI * this value mini-order Sell token amount (show on the UI for available order check) * setSellMinAmt(minAmt.toString()); */ minAmt = BigNumber.max( sellToken.orderAmounts.dust, calcForMinCost?.amountS ?? 0 ).times(1.1); console.log( "UI show mini-order Sell token amount:", minAmt.toString(), sdk .toBig(minAmt) .div("1e" + sellToken.decimals) .toString() ); console.log( `calcFor100USDAmount.amountS`, sdk .toBig(calcFor100USDAmount?.amountS ?? 0) .div("1e" + sellToken.decimals) .toString(), "calcForMinCost.amountS", sdk .toBig(calcForMinCost?.amountS ?? 0) .div("1e" + sellToken.decimals) .toString() ); } const calcTradeParams = sdk.getOutputAmount({ input: input.toString(), sell: sellSymbol, buy: buySymbol, isAtoB: isInputSellOutputBuy, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", // for new calc miniReceive will minus fee, so takeRate can fix as 0 slipBips: slippage, }); const minSymbol = buySymbol; const tradePrice = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(isInputSellOutputBuy ? 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"); } console.log( "calcTradeParams input:", input.toString(), ", calcTradeParams Price: ", sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(input.toString()) .toNumber(), `isAtoB mean isInputSellOutputBuy:${isInputSellOutputBuy}, ${ isInputSellOutputBuy ? 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 ); if ( tradeCost && calcTradeParams && calcTradeParams.amountBOutSlip?.minReceived && feeTakerRate ) { let value = sdk .toBig(calcTradeParams.amountBOutSlip?.minReceived) .times(feeTakerRate) .div(10000); console.log( "input Accounts", calcTradeParams?.amountS, "100 U Amount Sell:", calcFor100USDAmount?.amountS ); let validAmt = !!( calcTradeParams?.amountS && calcFor100USDAmount?.amountS && sdk.toBig(calcTradeParams?.amountS).gte(calcFor100USDAmount.amountS) ); let totalFeeRaw; console.log( `${minSymbol} tradeCost:`, tradeCost, "useTakeRate Fee:", value.toString(), "calcFor100USDAmount?.amountS:", calcFor100USDAmount?.amountS, `is setup minTrade amount, ${calcFor100USDAmount?.amountS}:`, validAmt ); if (!validAmt) { if (sdk.toBig(tradeCost).gte(value)) { totalFeeRaw = sdk.toBig(tradeCost); } else { totalFeeRaw = value; } console.log( "maxFeeBips update for tradeCost before value:", maxFeeBips, "totalFeeRaw", totalFeeRaw.toString() ); maxFeeBips = Math.ceil( totalFeeRaw .times(10000) .div(calcTradeParams.amountBOutSlip?.minReceived) .toNumber() ); console.log("maxFeeBips update for tradeCost after value:", maxFeeBips); } else { totalFeeRaw = sdk.toBig(value); } /** * totalFee */ totalFee = totalFeeRaw .div("1e" + tokenMap[minSymbol].decimals) .toString(); /** @output: UI * getValuePrecisionThousand( * totalFeeRaw.div("1e" + tokenMap[minSymbol].decimals).toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ tradeCost = sdk .toBig(tradeCost) // @ts-ignore .div("1e" + tokenMap[minSymbol].decimals) .toString(); /** @output: UI code with precision * getValuePrecisionThousand( * sdk * .toBig(tradeCost) * .div("1e" + tokenMap[minSymbol].decimals) * .toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ console.log("totalFee view value:", totalFee + " " + minSymbol); console.log("tradeCost view value:", tradeCost + " " + minSymbol); } const minimumReceived = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .minus(totalFee ?? 0) .toString(); console.log("minimumReceived:", minimumReceived); /** @output: UI code with precision * getValuePrecisionThousand( * toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) * .minus(totalFee) * .toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ let priceImpactView: any = calcTradeParams?.priceImpact ? parseFloat(calcTradeParams?.priceImpact) * 100 : undefined; console.log("priceImpact view:", priceImpactView + "%"); // @output: UI code with color alert // const priceImpactObj = getPriceImpactInfo(calcTradeParams); // const _tradeCalcData: Partial> = { // priceImpact: priceImpactObj.value.toString(), // priceImpactColor: priceImpactObj.priceImpactColor, // minimumReceived: !minimumReceived?.toString().startsWith("-") // ? minimumReceived // : undefined, // fee: totalFee, // feeTakerRate, // tradeCost, // }; console.log( `isInputSellOutputBuy:${isInputSellOutputBuy}`, `output ${isInputSellOutputBuy ? "Buy" : "Sell"}`, calcTradeParams?.output ); return { market, feeBips, takerRate, sellMinAmtInfo: sellMinAmtInfo as any, buyMinAmtInfo: buyMinAmtInfo as any, totalFee, maxFeeBips, feeTakerRate, tradeCost, minimumReceived, calcTradeParams, minAmt, }; } }; ``` ### Step 1. get apikey & eddsaKey ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const eddsaKey = await signatureKeyPairMock(accInfo); apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; ``` ### Step Step 2 : storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, sellTokenId: TOKEN_INFO.tokenMap[sell].tokenId, }, apiKey ); ``` ### Step 3. get user AmountMap, which decided user minimum order ```ts const amountMap = { [AMM_MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: AMM_MAP[AMM_MARKET].market, }, apiKey ) ).amountMap, [MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: MARKET, }, apiKey ) ).amountMap, }; ``` ### Step 4. depth, ammPoolSnapshot ,tickMap ```ts const [{depth}, {ammPoolSnapshot}] = await Promise.all([ LoopringAPI.exchangeAPI.getMixDepth({ market: AMM_MAP["AMM-LRC-ETH"].market, }), LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: AMM_MAP["AMM-LRC-ETH"].address, }), ]); ``` ### Step 5. check MinAmt ```ts let buyMinAmtInfo = (amountMap[AMM_MARKET] ?? amountMap[MARKET])[buy]; let takerRate = buyMinAmtInfo ? buyMinAmtInfo.userOrderInfo.takerRate : 0; const minAmountInput = buyMinAmtInfo.userOrderInfo.minAmount; ``` ### Step 6. calcTradeParams ```ts const calcTradeParams = sdk.getOutputAmount({ input: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), sell, buy, isAtoB, marketArr: ["LRC-ETH", "ETH-USDT", "DAI-USDT", "USDC-ETH"], tokenMap: TOKEN_INFO.tokenMap, marketMap: TOKEN_INFO.marketMap, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: AMM_MAP["AMM-LRC-ETH"].feeBips.toString(), takerRate: takerRate ? takerRate.toString() : "0", slipBips: slippage, }); console.log( "Buy", ",LRC:", LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), ",minAmountInput LRC:", minAmountInput, ",ETH:", calcTradeParams?.amountBOutSlip?.minReceivedVal ); const response: { hash: string } | any = await LoopringAPI.userAPI.submitOrder( { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, storageId: storageId.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap[sell].tokenId, volume: calcTradeParams?.amountS as string, }, buyToken: { tokenId: TOKEN_INFO.tokenMap[buy].tokenId, volume: calcTradeParams?.amountBOutSlip.minReceived as string, }, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 63, fillAmountBOrS: false, // amm only false tradeChannel: calcTradeParams?.exceedDepth ? sdk.TradeChannel.BLANK : sdk.TradeChannel.MIXED, orderType: calcTradeParams?.exceedDepth ? sdk.OrderType.ClassAmm : sdk.OrderType.TakerOnly, eddsaSignature: "", }, eddsaKey.sk, apiKey ); console.log("submitOrder", response); ``` *** ## ETH-LRC : for Quote to Base ### MOCK Data > user should had apikey, please check get apikey ```ts const buy = "ETH", sell = "LRC", MARKET = "LRC-ETH", AMM_MARKET = "AMM-LRC-ETH", slippage = "50"; const isAtoB = false; ``` ### Step 1. get apikey & eddsaKey ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const eddsaKey = await signatureKeyPairMock(accInfo); apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; ``` ### Step 2. storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, sellTokenId: TOKEN_INFO.tokenMap[sell].tokenId, }, apiKey ); ``` ### Step 3. get user AmountMap, which decided user minimum order ```ts const amountMap = { [AMM_MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: AMM_MAP[AMM_MARKET].market, }, apiKey ) ).amountMap, [MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: MARKET, }, apiKey ) ).amountMap, }; ``` ### Step 4. depth, ammPoolSnapshot ,tickMap ```ts const [{depth}, {ammPoolSnapshot}] = await Promise.all([ LoopringAPI.exchangeAPI.getMixDepth({ market: AMM_MAP["AMM-LRC-ETH"].market, }), LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: AMM_MAP["AMM-LRC-ETH"].address, }), ]); ``` ### Step 5. check MinAmt see log and calc mini receive and ouput value & maxfeeBips & priceImpact ```ts const { calcTradeParams, maxFeeBips, minimumReceived } = calculateSwap( sell, buy, isAtoB, 10, // user Input value no decimal 10 lrc, 0.1, //TODO MOCK value amountMap, "LRC-ETH", // close = ticker.tickers[7], depth, ammPoolSnapshot, TOKEN_INFO.tokenMap, AMM_MAP ); console.log( "Buy", ",ETH:", LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), ",minAmountInput ETH:", minAmountInput, ",LRC:", calcTradeParams?.amountBOutSlip?.minReceivedVal ); const response: { hash: string } | any = await LoopringAPI.userAPI.submitOrder( { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, storageId: storageId.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap[sell].tokenId, volume: calcTradeParams?.amountS as string, }, buyToken: { tokenId: TOKEN_INFO.tokenMap[buy].tokenId, volume: calcTradeParams?.amountBOutSlip.minReceived as string, }, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 63, fillAmountBOrS: false, // amm only false tradeChannel: calcTradeParams?.exceedDepth ? sdk.TradeChannel.BLANK : sdk.TradeChannel.MIXED, orderType: calcTradeParams?.exceedDepth ? sdk.OrderType.ClassAmm : sdk.OrderType.TakerOnly, eddsaSignature: "", }, eddsaKey.sk, apiKey ); console.log("submitOrder", response); ``` ### MockSwapData ```ts //Default config value from getTokens & getMixMarkets import * as sdk from "../index"; export const marketArray = ["LRC-ETH"]; export const marketMap = { "LRC-ETH": { baseTokenId: 1, enabled: true, market: "LRC-ETH", orderbookAggLevels: 5, precisionForPrice: 6, quoteTokenId: 0, status: 3, isSwapEnabled: true, createdAt: 1617967800000, }, }; //v3/mix/depth?level=0&limit=50&market=LRC-ETH export const deepMock = { symbol: "LRC-ETH", version: 23249677, timestamp: 1655719492365, mid_price: 0.00033248, bids: [ { price: 0.00030689, amt: "12041160324514792497908", vol: "3695372332571085210", amtTotal: "618450503644320209925641", volTotal: "198539605794234049017", }, { price: 0.00030752, amt: "12016302126785109160251", vol: "3695372332571085210", amtTotal: "606409343319805417427733", volTotal: "194844233461662963807", }, { price: 0.00030816, amt: "11991520826895479525387", vol: "3695372332571085210", amtTotal: "594393041193020308267482", volTotal: "191148861129091878597", }, { price: 0.00030881, amt: "12329062048917727073625", vol: "3807353312345966580", amtTotal: "582401520366124828742095", volTotal: "187453488796520793387", }, { price: 0.00030945, amt: "11941442525358097768419", vol: "3695372332571085210", amtTotal: "570072458317207101668470", volTotal: "183646135484174826807", }, { price: 0.0003102, amt: "3223726000000000000000", vol: "999999805200000000", amtTotal: "558131015791849003900051", volTotal: "179950763151603741597", }, { price: 0.0003104, amt: "11904963012947201084062", vol: "3695372332571085210", amtTotal: "554907289791849003900051", volTotal: "178950763346403741597", }, { price: 0.00031072, amt: "11892800074855146120151", vol: "3695372332571085210", amtTotal: "543002326778901802815989", volTotal: "175255391013832656387", }, { price: 0.00031137, amt: "12227667622887455762012", vol: "3807353312345966580", amtTotal: "531109526704046656695838", volTotal: "171560018681261571177", }, { price: 0.00031202, amt: "11843337524732768607817", vol: "3695372332571085210", amtTotal: "518881859081159200933826", volTotal: "167752665368915604597", }, { price: 0.00031266, amt: "11819088718260537718160", vol: "3695372332571085210", amtTotal: "507038521556426432326009", volTotal: "164057293036344519387", }, { price: 0.0003133, amt: "11794914308461194855590", vol: "3695372332571085210", amtTotal: "495219432838165894607849", volTotal: "160361920703773434177", }, { price: 0.00031395, amt: "12127129883064173669497", vol: "3807353312345966580", amtTotal: "483424518529704699752259", volTotal: "156666548371202348967", }, { price: 0.0003146, amt: "11746060536480763829870", vol: "3695372332571085210", amtTotal: "471297388646640526082762", volTotal: "152859195058856382387", }, { price: 0.00031524, amt: "11722109721002274877424", vol: "3695372332571085210", amtTotal: "459551328110159762252892", volTotal: "149163822726285297177", }, { price: 0.0003159, amt: "12052351993023897680738", vol: "3807353312345966580", amtTotal: "447829218389157487375468", volTotal: "145468450393714211967", }, { price: 0.00031655, amt: "11673707113082743010268", vol: "3695372332571085210", amtTotal: "435776866396133589694730", volTotal: "141661097081368245387", }, { price: 0.00031699, amt: "615000000000000000000", vol: "194954999999999949", amtTotal: "424103159283050846684462", volTotal: "137965724748797160177", }, { price: 0.00031719, amt: "11649977142974837964110", vol: "3695372332571085210", amtTotal: "423488159283050846684462", volTotal: "137770769748797160228", }, { price: 0.00031784, amt: "11626319455782556517212", vol: "3695372332571085210", amtTotal: "411838182140076008720352", volTotal: "134075397416226075018", }, { price: 0.0003185, amt: "11953964321007990024755", vol: "3807353312345966580", amtTotal: "400211862684293452203140", volTotal: "130380025083654989808", }, { price: 0.00031934, amt: "11571837200722111826863", vol: "3695372332571085210", amtTotal: "388257898363285462178385", volTotal: "126572671771309023228", }, { price: 0.00031979, amt: "11555429099390519988949", vol: "3695372332571085210", amtTotal: "376686061162563350351522", volTotal: "122877299438737938018", }, { price: 0.00032, amt: "300000000000000000000", vol: "96000000000000000", amtTotal: "365130632063172830362573", volTotal: "119181927106166852808", }, { price: 0.00032044, amt: "11532058329906875386268", vol: "3695372332571085210", amtTotal: "364830632063172830362573", volTotal: "119085927106166852808", }, { price: 0.0003211, amt: "11857145662584872212009", vol: "3807353312345966580", amtTotal: "353298573733265954976305", volTotal: "115390554773595767598", }, { price: 0.00032176, amt: "11484826169666631095087", vol: "3695372332571085210", amtTotal: "341441428070681082764296", volTotal: "111583201461249801018", }, { price: 0.00032241, amt: "11461669156035188891431", vol: "3695372332571085210", amtTotal: "329956601901014451669209", volTotal: "107887829128678715808", }, { price: 0.00032294, amt: "22266286128259997368320", vol: "7190674442260284416", amtTotal: "318494932744979262777778", volTotal: "104192456796107630598", }, { price: 0.00032307, amt: "11784846142950476791130", vol: "3807353312345966580", amtTotal: "296228646616719265409458", volTotal: "97001782353847346182", }, { price: 0.00032333, amt: "31042151210389998665728", vol: "10037169172367501312", amtTotal: "284443800473768788618328", volTotal: "93194429041501379602", }, { price: 0.00032354, amt: "19082720347089998446592", vol: "6174214168300968960", amtTotal: "253401649263378789952600", volTotal: "83157259869133878290", }, { price: 0.00032373, amt: "11414868337793698506576", vol: "3695372332571085210", amtTotal: "234318928916288791506008", volTotal: "76983045700832909330", }, { price: 0.00032438, amt: "11391922481177051106050", vol: "3695372332571085210", amtTotal: "222904060578495092999432", volTotal: "73287673368261824120", }, { price: 0.00032503, amt: "11369045742792164433715", vol: "3695372332571085210", amtTotal: "211512138097318041893382", volTotal: "69592301035690738910", }, { price: 0.00032512, amt: "7101984539779999465472", vol: "2308997213573273600", amtTotal: "200143092354525877459667", volTotal: "65896928703119653700", }, { price: 0.0003257, amt: "11689707913498419991686", vol: "3807353312345966580", amtTotal: "193041107814745877994195", volTotal: "63587931489546380100", }, { price: 0.00032636, amt: "11322810509578528739940", vol: "3695372332571085210", amtTotal: "181351399901247458002509", volTotal: "59780578177200413520", }, { price: 0.00032702, amt: "11300141532643741902081", vol: "3695372332571085210", amtTotal: "170028589391668929262569", volTotal: "56085205844629328310", }, { price: 0.00032709, amt: "1511898000000000000000", vol: "494541835799999898", amtTotal: "158728447859025187360488", volTotal: "52389833512058243100", }, { price: 0.00032767, amt: "11277540564700637960582", vol: "3695372332571085210", amtTotal: "157216549859025187360488", volTotal: "51895291676258243202", }, { price: 0.00032838, amt: "11594014901713773553177", vol: "3807353312345966580", amtTotal: "145939009294324549399906", volTotal: "48199919343687157992", }, { price: 0.00032899, amt: "11232203228917728004878", vol: "3695372332571085210", amtTotal: "134344994392610775846729", volTotal: "44392566031341191412", }, { price: 0.00032959, amt: "31100749000000000753664", vol: "10250806870400000000", amtTotal: "123112791163693047841851", volTotal: "40697193698770106202", }, { price: 0.00032965, amt: "11209805337410429375102", vol: "3695372332571085210", amtTotal: "92012042163693047088187", volTotal: "30446386828370106202", }, { price: 0.00033031, amt: "11187474373893810850495", vol: "3695372332571085210", amtTotal: "80802236826282617713085", volTotal: "26751014495799020992", }, { price: 0.00033098, amt: "11503202913004006553102", vol: "3807353312345966580", amtTotal: "69614762452388806862590", volTotal: "23055642163227935782", }, { price: 0.00033099, amt: "26515451000000002129920", vol: "8776614281000000512", amtTotal: "58111559539384800309488", volTotal: "19248288850881969202", }, { price: 0.0003313, amt: "20499506300000000802816", vol: "6791486437190000640", amtTotal: "31596108539384798179568", volTotal: "10471674569881968690", }, { price: 0.00033165, amt: "11096602239384797376752", vol: "3680188132691968050", amtTotal: "11096602239384797376752", volTotal: "3680188132691968050", }, ], bids_prices: [ 0.00030689, 0.00030752, 0.00030816, 0.00030881, 0.00030945, 0.0003102, 0.0003104, 0.00031072, 0.00031137, 0.00031202, 0.00031266, 0.0003133, 0.00031395, 0.0003146, 0.00031524, 0.0003159, 0.00031655, 0.00031699, 0.00031719, 0.00031784, 0.0003185, 0.00031934, 0.00031979, 0.00032, 0.00032044, 0.0003211, 0.00032176, 0.00032241, 0.00032294, 0.00032307, 0.00032333, 0.00032354, 0.00032373, 0.00032438, 0.00032503, 0.00032512, 0.0003257, 0.00032636, 0.00032702, 0.00032709, 0.00032767, 0.00032838, 0.00032899, 0.00032959, 0.00032965, 0.00033031, 0.00033098, 0.00033099, 0.0003313, 0.00033165, ], bids_amtTotals: [ "618450503644320209925641", "606409343319805417427733", "594393041193020308267482", "582401520366124828742095", "570072458317207101668470", "558131015791849003900051", "554907289791849003900051", "543002326778901802815989", "531109526704046656695838", "518881859081159200933826", "507038521556426432326009", "495219432838165894607849", "483424518529704699752259", "471297388646640526082762", "459551328110159762252892", "447829218389157487375468", "435776866396133589694730", "424103159283050846684462", "423488159283050846684462", "411838182140076008720352", "400211862684293452203140", "388257898363285462178385", "376686061162563350351522", "365130632063172830362573", "364830632063172830362573", "353298573733265954976305", "341441428070681082764296", "329956601901014451669209", "318494932744979262777778", "296228646616719265409458", "284443800473768788618328", "253401649263378789952600", "234318928916288791506008", "222904060578495092999432", "211512138097318041893382", "200143092354525877459667", "193041107814745877994195", "181351399901247458002509", "170028589391668929262569", "158728447859025187360488", "157216549859025187360488", "145939009294324549399906", "134344994392610775846729", "123112791163693047841851", "92012042163693047088187", "80802236826282617713085", "69614762452388806862590", "58111559539384800309488", "31596108539384798179568", "11096602239384797376752", ], bids_volTotals: [ "198539605794234049017", "194844233461662963807", "191148861129091878597", "187453488796520793387", "183646135484174826807", "179950763151603741597", "178950763346403741597", "175255391013832656387", "171560018681261571177", "167752665368915604597", "164057293036344519387", "160361920703773434177", "156666548371202348967", "152859195058856382387", "149163822726285297177", "145468450393714211967", "141661097081368245387", "137965724748797160177", "137770769748797160228", "134075397416226075018", "130380025083654989808", "126572671771309023228", "122877299438737938018", "119181927106166852808", "119085927106166852808", "115390554773595767598", "111583201461249801018", "107887829128678715808", "104192456796107630598", "97001782353847346182", "93194429041501379602", "83157259869133878290", "76983045700832909330", "73287673368261824120", "69592301035690738910", "65896928703119653700", "63587931489546380100", "59780578177200413520", "56085205844629328310", "52389833512058243100", "51895291676258243202", "48199919343687157992", "44392566031341191412", "40697193698770106202", "30446386828370106202", "26751014495799020992", "23055642163227935782", "19248288850881969202", "10471674569881968690", "3680188132691968050", ], bids_amtTotal: "618450503644320209925641", bids_volTotal: "198539605794234049017", asks: [ { price: 0.00033331, amt: "26466599999999999737856", vol: "8821317780000000000", amtTotal: "26466599999999999737856", volTotal: "8821317780000000000", }, { price: 0.0003336, amt: "33880800000000001048576", vol: "11302634880000000000", amtTotal: "60347400000000000786432", volTotal: "20123952660000000000", }, { price: 0.00033371, amt: "11142477800817399987988", vol: "3718356266910774549", amtTotal: "71489877800817400774420", volTotal: "23842308926910774549", }, { price: 0.00033439, amt: "11410124103475892197402", vol: "3815340399218399705", amtTotal: "82900001904293292971822", volTotal: "27657649326129174254", }, { price: 0.00033501, amt: "39334199999999992922112", vol: "13176957000000000000", amtTotal: "122234201904293285893934", volTotal: "40834606326129174254", }, { price: 0.00033506, amt: "11052295534472154230337", vol: "3703108939445215718", amtTotal: "133286497438765440124271", volTotal: "44537715265574389972", }, { price: 0.00033572, amt: "11030456322913363709718", vol: "3703093651997124565", amtTotal: "144316953761678803833989", volTotal: "48240808917571514537", }, { price: 0.00033638, amt: "11008681778518069039476", vol: "3703078409816047858", amtTotal: "155325635540196872873465", volTotal: "51943887327387562395", }, { price: 0.000337, amt: "7188173683760000139264", vol: "2422342649690282496", amtTotal: "162513809223956873012729", volTotal: "54366229977077844891", }, { price: 0.00033706, amt: "11319571950108460741855", vol: "3815277012711042502", amtTotal: "173833381174065333754584", volTotal: "58181506989788887393", }, { price: 0.00033773, amt: "10964670732513311387215", vol: "3703047602083844528", amtTotal: "184798051906578645141799", volTotal: "61884554591872731921", }, { price: 0.00033848, amt: "10943090596879450870975", vol: "3703921209677097807", amtTotal: "195741142503458096012774", volTotal: "65588475801549829728", }, { price: 0.00033893, amt: "31086310012520000126976", vol: "10535772189443278848", amtTotal: "226827452515978096139750", volTotal: "76124247990993108576", }, { price: 0.00033907, amt: "10921574108084085072499", vol: "3703126650187824910", amtTotal: "237749026624062181212249", volTotal: "79827374641180933486", }, { price: 0.00033934, amt: "22061773569720001232896", vol: "7486221625413088256", amtTotal: "259810800193782182445145", volTotal: "87313596266594021742", }, { price: 0.00033975, amt: "11230093484570461106334", vol: "3815326678720011608", amtTotal: "271040893678352643551479", volTotal: "91128922945314033350", }, { price: 0.00034042, amt: "10878083875111520592396", vol: "3703095772122414389", amtTotal: "281918977553464164143875", volTotal: "94832018717436447739", }, { price: 0.00034061, amt: "19029641944770000453632", vol: "6481686342808109056", amtTotal: "300948619498234164597507", volTotal: "101313705060244556795", }, { price: 0.00034109, amt: "10856758733704286197912", vol: "3703080631272015254", amtTotal: "311805378231938450795419", volTotal: "105016785691516572049", }, { price: 0.00034176, amt: "10835496238854895014260", vol: "3703065534900672185", amtTotal: "322640874470793345809679", volTotal: "108719851226417244234", }, { price: 0.00034244, amt: "11141671799245742102774", vol: "3815263899323431057", amtTotal: "333782546270039087912453", volTotal: "112535115125740675291", }, { price: 0.00034312, amt: "10792518633700140402041", vol: "3703035020801012310", amtTotal: "344575064903739228314494", volTotal: "116238150146541687601", }, { price: 0.00034379, amt: "10771444484888773263977", vol: "3703020058155356239", amtTotal: "355346509388628001578471", volTotal: "119941170204697043840", }, { price: 0.00034447, amt: "11075875299655656611658", vol: "3815217183808722096", amtTotal: "366422384688283658190129", volTotal: "123756387388505765936", }, { price: 0.00034515, amt: "10728847020295584791583", vol: "3702989813955495075", amtTotal: "377151231708579242981712", volTotal: "127459377202461261011", }, { price: 0.00034582, amt: "10707958999548106661900", vol: "3702974983460764366", amtTotal: "387859190708127349643612", volTotal: "131162352185922025377", }, { price: 0.00034649, amt: "10687131919892014508549", vol: "3702960196234208540", amtTotal: "398546322628019364152161", volTotal: "134865312382156233917", }, { price: 0.00034718, amt: "10989265205593964126102", vol: "3815155690641938295", amtTotal: "409535587833613328278263", volTotal: "138680468072798172212", }, { price: 0.00034809, amt: "10645033127428079147754", vol: "3705341637309061860", amtTotal: "420180620961041407426017", volTotal: "142385809710107234072", }, { price: 0.00034854, amt: "10624389276271547157517", vol: "3703021892850000724", amtTotal: "430805010237312954583534", volTotal: "146088831602957234796", }, { price: 0.00034922, amt: "10603805418694082174472", vol: "3703007072472544949", amtTotal: "441408815656007036758006", volTotal: "149791838675429779745", }, { price: 0.00034991, amt: "10903667052066234563130", vol: "3815203952623454269", amtTotal: "452312482708073271321136", volTotal: "153607042628053234014", }, { price: 0.00035059, amt: "10562197544947631702552", vol: "3702977114803447505", amtTotal: "462874680253020903023688", volTotal: "157310019742856681519", }, { price: 0.00035127, amt: "10541794072549149477757", vol: "3702962424303320597", amtTotal: "473416474325570052501445", volTotal: "161012982167160002116", }, { price: 0.00035196, amt: "10839964505283434387937", vol: "3815158086789770653", amtTotal: "484256438830853486889382", volTotal: "164828140253949772769", }, { price: 0.00035265, amt: "10500550295026604907238", vol: "3702932728783504366", amtTotal: "494756989125880091796620", volTotal: "168531072982733277135", }, { price: 0.00035333, amt: "10480325106225117837726", vol: "3702918166647567295", amtTotal: "505237314232105209634346", volTotal: "172233991149380844430", }, { price: 0.00035401, amt: "10460158295034970667917", vol: "3702903646543510388", amtTotal: "515697472527140180302263", volTotal: "175936894795924354818", }, { price: 0.0003547, amt: "10756101480844670483856", vol: "3815097705412174743", amtTotal: "526453574007984850786119", volTotal: "179751992501336529561", }, { price: 0.000355, amt: "2879934000000000000000", vol: "1022376570000000000", amtTotal: "529333508007984850786119", volTotal: "180774369071336529561", }, { price: 0.00035539, amt: "10419392212908119384882", vol: "3702874294964379056", amtTotal: "539752900220892970171001", volTotal: "184477243366300908617", }, { price: 0.00035607, amt: "10399400937404414745812", vol: "3702859901246016389", amtTotal: "550152301158297384916813", volTotal: "188180103267546925006", }, { price: 0.00035675, amt: "10379467141462596339294", vol: "3702845548912938279", amtTotal: "560531768299759981256107", volTotal: "191882948816459863285", }, { price: 0.00035781, amt: "10673207910450037674513", vol: "3818930797471485007", amtTotal: "571204976210210018930620", volTotal: "195701879613931348292", }, { price: 0.00035815, amt: "10339171406073422424770", vol: "3702919927697518809", amtTotal: "581544147616283441355390", volTotal: "199404799541628867101", }, { price: 0.00035883, amt: "10319410450646547471681", vol: "3702905502200057190", amtTotal: "591863558066929988827071", volTotal: "203107705043828924291", }, { price: 0.00035887, amt: "1253970999999999795200", vol: "449999996860000051", amtTotal: "593117529066929988622271", volTotal: "203557705040688924342", }, { price: 0.00035952, amt: "10299706093934767574874", vol: "3702891118019657590", amtTotal: "603417235160864756197145", volTotal: "207260596158708581932", }, { price: 0.00036, amt: "491031000000000000000", vol: "176771040000000000", amtTotal: "603908266160864756197145", volTotal: "207437367198708581932", }, { price: 0.00036022, amt: "10591268908994289896668", vol: "3815084938649532411", amtTotal: "614499535069859046093813", volTotal: "211252452137358114343", }, ], asks_prices: [ 0.00033331, 0.0003336, 0.00033371, 0.00033439, 0.00033501, 0.00033506, 0.00033572, 0.00033638, 0.000337, 0.00033706, 0.00033773, 0.00033848, 0.00033893, 0.00033907, 0.00033934, 0.00033975, 0.00034042, 0.00034061, 0.00034109, 0.00034176, 0.00034244, 0.00034312, 0.00034379, 0.00034447, 0.00034515, 0.00034582, 0.00034649, 0.00034718, 0.00034809, 0.00034854, 0.00034922, 0.00034991, 0.00035059, 0.00035127, 0.00035196, 0.00035265, 0.00035333, 0.00035401, 0.0003547, 0.000355, 0.00035539, 0.00035607, 0.00035675, 0.00035781, 0.00035815, 0.00035883, 0.00035887, 0.00035952, 0.00036, 0.00036022, ], asks_amtTotals: [ "26466599999999999737856", "60347400000000000786432", "71489877800817400774420", "82900001904293292971822", "122234201904293285893934", "133286497438765440124271", "144316953761678803833989", "155325635540196872873465", "162513809223956873012729", "173833381174065333754584", "184798051906578645141799", "195741142503458096012774", "226827452515978096139750", "237749026624062181212249", "259810800193782182445145", "271040893678352643551479", "281918977553464164143875", "300948619498234164597507", "311805378231938450795419", "322640874470793345809679", "333782546270039087912453", "344575064903739228314494", "355346509388628001578471", "366422384688283658190129", "377151231708579242981712", "387859190708127349643612", "398546322628019364152161", "409535587833613328278263", "420180620961041407426017", "430805010237312954583534", "441408815656007036758006", "452312482708073271321136", "462874680253020903023688", "473416474325570052501445", "484256438830853486889382", "494756989125880091796620", "505237314232105209634346", "515697472527140180302263", "526453574007984850786119", "529333508007984850786119", "539752900220892970171001", "550152301158297384916813", "560531768299759981256107", "571204976210210018930620", "581544147616283441355390", "591863558066929988827071", "593117529066929988622271", "603417235160864756197145", "603908266160864756197145", "614499535069859046093813", ], asks_volTotals: [ "8821317780000000000", "20123952660000000000", "23842308926910774549", "27657649326129174254", "40834606326129174254", "44537715265574389972", "48240808917571514537", "51943887327387562395", "54366229977077844891", "58181506989788887393", "61884554591872731921", "65588475801549829728", "76124247990993108576", "79827374641180933486", "87313596266594021742", "91128922945314033350", "94832018717436447739", "101313705060244556795", "105016785691516572049", "108719851226417244234", "112535115125740675291", "116238150146541687601", "119941170204697043840", "123756387388505765936", "127459377202461261011", "131162352185922025377", "134865312382156233917", "138680468072798172212", "142385809710107234072", "146088831602957234796", "149791838675429779745", "153607042628053234014", "157310019742856681519", "161012982167160002116", "164828140253949772769", "168531072982733277135", "172233991149380844430", "175936894795924354818", "179751992501336529561", "180774369071336529561", "184477243366300908617", "188180103267546925006", "191882948816459863285", "195701879613931348292", "199404799541628867101", "203107705043828924291", "203557705040688924342", "207260596158708581932", "207437367198708581932", "211252452137358114343", ], asks_amtTotal: "614499535069859046093813", asks_volTotal: "211252452137358114343", }; //v3/amm/balance?poolAddress=0xfEB069407df0e1e4B365C10992F1bc16c078E34b export const ammPoolSnapshot: sdk.AmmPoolSnapshot = { poolName: "AMM-LRC-ETH", poolAddress: "0x18920d6e6fb7ebe057a4dd9260d6d95845c95036", pooled: [ { tokenId: 1, volume: "11198097977488137000000000", }, { tokenId: 0, volume: "3725368050950874300000", }, ], lp: { tokenId: 83, volume: "34138981282200", }, risky: false, }; //v3/amm/balance?poolAddress=0xfEB069407df0e1e4B365C10992F1bc16c078E34b export const ammPool = { poolName: "AMM-LRC-ETH", poolAddress: "0x18920d6e6fb7ebe057a4dd9260d6d95845c95036", pooled: [ { tokenId: 1, volume: "11215027899488137000000000" }, { tokenId: 0, volume: "3720052711450874300000" }, ], lp: { tokenId: 83, volume: "34141365482200" }, risky: false, }; //v3/mix/ticker?market=LRC-ETH export const ticker = { tickers: [ [ "COMBINE-LRC-ETH", "1655625077879", "2429371927000000000000000", "814655525450000000000", "0.00035052", "0.00035222", "0.00032201", "0.00033367", "772", "", "", "", "", ], ], }; //v3/user/orderUserRateAmount?accountId=10427&market=LRC-ETH export const userAmount = { "LRC-ETH": { LRC: { tokenSymbol: "LRC", baseOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, tradeCost: "231046501539337141", }, ETH: { tokenSymbol: "ETH", baseOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, tradeCost: "75150737796750", }, }, "AMM-LRC-ETH": { LRC: { tokenSymbol: "LRC", baseOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, tradeCost: "231046501539337141", }, ETH: { tokenSymbol: "ETH", baseOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, tradeCost: "75150737796750", }, }, }; export const TokenMapMockSwap = { ETH: { type: "ETH", tokenId: 0, symbol: "ETH", name: "Ethereum", address: "0x0000000000000000000000000000000000000000", decimals: 18, precision: 7, precisionForOrder: 3, orderAmounts: { minimum: "1700000000000000", maximum: "1000000000000000000000", dust: "200000000000000", }, luckyTokenAmounts: { minimum: "50000000000000", maximum: "1000000000000000000000", dust: "50000000000000", }, fastWithdrawLimit: "100000000000000000000", gasAmounts: { distribution: "85000", deposit: "110000", }, enabled: true, isLpToken: false, tradePairs: ["LRC"], }, LRC: { type: "ERC20", tokenId: 1, symbol: "LRC", name: "Loopring", address: "0xbbbbca6a901c926f240b89eacb641d8aec7aeafd", 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: "150000", }, enabled: true, isLpToken: false, tradePairs: ["ETH"], }, }; export const MAPFEEBIPS = 63; ``` ================================================ FILE: docs/js_sdk/exchange/ammpool_api.md ================================================ # AmmPool API ## getAmmPoolConf ```typescript const { ammpools, pairs } = await api.getAmmPoolConf(); ``` ## getAmmPoolUserRewards ```typescript const response: any = await api.getAmmPoolUserRewards({ owner: acc.accountId.toString(), }); ``` ## getAmmPoolActivityRules ```typescript const response: any = await api.getAmmPoolActivityRules(); ``` ## getAmmPoolStats ```typescript const response: any = await api.getAmmPoolStats(); ``` ## getAmmPoolSnapshot ```typescript const request: GetAmmPoolSnapshotRequest = { poolAddress, }; const response = await api.getAmmPoolSnapshot(request, acc.apiKey); ``` ## getAmmPoolBalances ```typescript const response = await api.getAmmPoolBalances(); ``` ## getAmmPoolTrades ```typescript const request: GetAmmPoolTradesRequest = { ammPoolAddress: poolAddress, }; const response = await api.getAmmPoolTrades(request); ``` ## getUserAmmPoolTxs ```typescript const request: GetUserAmmPoolTxsRequest = { accountId: acc.accountId, }; const response = await api.getUserAmmPoolTxs(request, acc.apiKey); ``` ## joinAmmPool ```typescript const request2: JoinAmmPoolRequest = { owner: acc.address, poolAddress, joinTokens: { pooled: [ { tokenId: "1", volume: "1000000000000000000000" }, { tokenId: "0", volume: "1000000000000000000" }, ], minimumLp: { tokenId: "4", volume: "100000" }, }, storageIds: [storageId_1.offchainId, storageId.offchainId], fee: "1000000000000000000", }; const patch: AmmPoolRequestPatch = { chainId: ChainId.GORLI, ammName: "LRCETH-Pool", poolAddress, eddsaKey: acc.eddsaKey, }; const response = await api.joinAmmPool(request2, patch, acc.apiKey); ``` ## exitAmmPool ```typescript const request2: ExitAmmPoolRequest = { owner: acc.address, poolAddress, exitTokens: { unPooled: [ { tokenId: "1", volume: "1000000000000000000000" }, { tokenId: "0", volume: "1000000000000000000" }, ], burned: { tokenId: "4", volume: "100000" }, }, storageId: storageId_1.offchainId, maxFee: "1000000000000000000", }; const patch: AmmPoolRequestPatch = { chainId: ChainId.GORLI, ammName: "LRCETH-Pool", poolAddress, eddsaKey: acc.eddsaKey, }; const response = await api.exitAmmPool(request2, patch, acc.apiKey); ``` ================================================ FILE: docs/js_sdk/exchange/exchange.md ================================================ # Loopring Exchange Definition: Loopring Dex Main API for get Exchange Information, L2 Block, ERC20 Token Information, AMM Information, Market Config and so on static and dynamic information *** ## getExchangeInfo ```ts const response = await LoopringAPI.exchangeAPI.getExchangeInfo(); console.log(response); ``` *** ## getTokens ```ts const {tokensMap, coinMap, totalCoinMap, idIndex, addressIndex} = await LoopringAPI.exchangeAPI.getTokens(); console.log( "tokenMap:", tokensMap, coinMap, totalCoinMap, idIndex, addressIndex ); ``` *** ## getMixMarkets ```ts const {markets, pairs, tokenArr, tokenArrStr, marketArr, marketArrStr} = await LoopringAPI.exchangeAPI.getMixMarkets(); console.log("markets:", markets); console.log("pairs:", pairs); console.log("tokenArr:", tokenArr); console.log("tokenArrStr:", tokenArrStr); console.log("marketArr", marketArr); console.log("marketArrStr", marketArrStr); ``` *** ## getAmmPoolConf ```ts const response = await LoopringAPI.ammpoolAPI.getAmmPoolConf(); console.log(response.ammpools); console.log(response.pairs); ``` *** ## getAvailableBroker ```ts const result = await LoopringAPI.exchangeAPI.getAvailableBroker(); console.log(result); ``` *** ## getTokenPrices ```ts const response = await LoopringAPI.walletAPI.getTokenPrices({ token: TOKEN_INFO.tokenMap.LRC.address, }); console.log(response); ``` *** ## getLatestTokenPrices ```ts const response = await LoopringAPI.walletAPI.getLatestTokenPrices(); console.log(response); ``` *** ## getLatestTokenPrices_cny ```ts const response = await LoopringAPI.walletAPI.getLatestTokenPrices({ currency: sdk.Currency.cny, }); console.log(response); ``` *** ## getWithdrawalAgents ```ts const response = await LoopringAPI.exchangeAPI.getWithdrawalAgents({ tokenId: 1, amount: "10000000000", }); console.log(response); ``` *** ## getCandlestick ```ts const response = await LoopringAPI.exchangeAPI.getCandlestick({ market: "LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); ``` *** ## getAccountServices ```ts const response = await LoopringAPI.exchangeAPI.getAccountServices({}); console.log(response); ``` *** ## getExchangeFeeInfo ```ts const response = await LoopringAPI.exchangeAPI.getExchangeFeeInfo(); console.log(response); console.log( response.raw_data[sdk.VipCatergory.ORDERBOOK_TRADING_FEES_STABLECOIN] ); ``` *** ## getProtocolPortrait ```ts const response = await LoopringAPI.exchangeAPI.getProtocolPortrait(); console.log(response); ``` *** ## getRecommendedMarkets ```ts const response = await LoopringAPI.exchangeAPI.getRecommendedMarkets(); console.log(response); ``` *** ## getGasPrice ```ts const response = await LoopringAPI.exchangeAPI.getGasPrice(); console.log(response); ``` *** ## getGasPriceRange ```ts const response = await LoopringAPI.exchangeAPI.getGasPriceRange(); console.log(response); ``` *** ## getMarketTrades ```ts const response = await LoopringAPI.exchangeAPI.getMarketTrades({ market: "ETH-USDT", }); console.log(response.raw_data.trades); ``` *** ## getRelayerCurrentTime ```ts const response = await LoopringAPI.exchangeAPI.getRelayerCurrentTime(); console.log(response); ``` *** ## getFiatPriceUSD ```ts const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "USD", }); console.log(response); ``` *** ## getFiatPriceCNY ```ts const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "CNY", }); console.log(response); ``` *** ## getMarkets ```ts const response = await LoopringAPI.exchangeAPI.getMarkets(); console.log(response); console.log(response.pairs.LRC.tokenList); console.log( "hasMarket LRC-ETH:", sdk.hasMarket(response.marketArr, "LRC-ETH") ); console.log( "market 1:", sdk.getExistedMarket(response.marketArr, "LRC", "ETH") ); console.log( "market 2:", sdk.getExistedMarket(response.marketArr, "ETH", "LRC") ); ``` *** ## getDepth ```ts const response = await LoopringAPI.exchangeAPI.getDepth({ market: "LRC-ETH", }); console.log(response); ``` *** ## getTicker ```ts const response = await LoopringAPI.exchangeAPI.getTicker({ market: "LRC-ETH", }); console.log(response); ``` *** ## getAllTickers ```ts const response = await LoopringAPI.exchangeAPI.getAllTickers(); console.log(response); ``` *** ## getMixDepth ```ts const response = await LoopringAPI.exchangeAPI.getMixDepth({ market: "LRC-ETH", }); console.log(response); console.log(response.depth.bids); ``` *** ## getMixTicker ```ts const response = await LoopringAPI.exchangeAPI.getMixTicker({ market: ["LRC-ETH", "ETH-USDC", "DAI-USDT"].join(","), }); console.log(response.tickMap["DAI-USDT"]); ``` *** ## getAllMixTickers ```ts const response: any = await LoopringAPI.exchangeAPI.getAllMixTickers(); console.log(response?.tickMap); ``` *** ## getMixCandlestickAMM ```ts const response = await LoopringAPI.exchangeAPI.getMixCandlestick({ market: "AMM-LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); ``` }); ================================================ FILE: docs/js_sdk/exchange/webSocket.md ================================================ #WebSocket Command Definition: Loopring L2 websocket ### Loopring L2 websocket topic type: WsTopicType - `account` - `orderbook` - `mixorder` - `mixtrade` - `ticker` - `candlestick` - `ammpool` ## getWsKey (required by account related socket) ```ts const response = await LoopringAPI.wsAPI.getWsKey(); console.log(response); ``` ## getOrderBookArg ```ts const arg1 = sdk.getMixOrderArg({ market: "LRC-ETH", level: 50 }); console.log(arg1); const arg2 = sdk.getOrderBookArg({ market: "LRC-ETH", level: 50, count: 40, snapshot: false, }); console.log(arg2); ``` ================================================ FILE: docs/js_sdk/transfer/transferERC20.md ================================================ # Transfer ERC20 Definition: Send ERC20 tokens to other account on Loopring L2, > trade value should with decimals `sdk.toBig(value).times("1e" + TOKEN_INFO.tokenMap.LRC.decimals)` *** ## Step 1. get account Info const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); const LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo; ```ts const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apikey ```ts const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const { userBalances } = await LoopringAPI.userAPI.getUserBalances( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, tokens: "" }, apiKey ); ``` *** ## Step 4. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey ); console.log("storageId:", storageId); ``` *** ## Step 5. get fee ```ts const fee = await LoopringAPI.userAPI.getOffchainFeeAmt({ accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.TRANSFER, }, apiKey); console.log("fee:", fee); ``` *** ## Step 6. transfer ```ts const transferResult = await LoopringAPI.userAPI.submitInternalTransfer({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: accInfo.owner, payerId: accInfo.accountId, payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, payeeId: LOOPRING_EXPORTED_ACCOUNT.accountId2, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Trezor, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log("transferResult:", transferResult); ``` ================================================ FILE: docs/js_sdk/transfer/transferNFT.md ================================================ # Transfer NFT Definition: Send NFT to other account on Loopring L2 *** ## Step 1. get account Info ```ts const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); const LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo; const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apiKey ```ts const { apiKey } = await LoopringAPI.userAPI.getUserApiKey({ accountId: accInfo.accountId}, eddsaKey.sk ); console.log("apiKey:", apiKey); const { userNFTBalances } = await LoopringAPI.userAPI.getUserNFTBalances( { accountId: accInfo.accountId, limit: 20 }, apiKey ); ``` *** ##Step 4. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId({ accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey); ``` *** ## Step 5. get fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt({ accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, amount: "0", }, apiKey); console.log("fee:", fee); ``` *** ## Step 6. Transfer NFT ```ts const transferResult = await LoopringAPI.userAPI.submitNFTInTransfer({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, fromAccountId: LOOPRING_EXPORTED_ACCOUNT.accountId, fromAddress: LOOPRING_EXPORTED_ACCOUNT.address, toAccountId: 0, // toAccountId is not required, input 0 as default toAddress: LOOPRING_EXPORTED_ACCOUNT.address2, token: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: fee.fees["LRC"].fee ?? "9400000000000000000", }, storageId: storageId.offchainId, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey, }); console.log("transfer Result:", transferResult); ``` ================================================ FILE: docs/js_sdk/withdraw/withdrawERC20.md ================================================ # Withdraw ERC20 Definition: Loopring L2 withdraw ERC20 to Ethereum L1, > trade value should with decimals `sdk.toBig(value).times("1e" + TOKEN_INFO.tokenMap.LRC.decimals)` *** ## Step 1. getAccount ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` ##Step 2. eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey({ accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId({ accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey ); console.log("storageId:", storageId); ``` *** ## Step 5. fee ```ts const fee = await LoopringAPI.userAPI.getOffchainFeeAmt({ accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, tokenSymbol: TOKEN_INFO.tokenMap["LRC"].symbol, }, apiKey ); console.log("fee:", fee); ``` *** ## Step 6. withdraw ```ts const response = await LoopringAPI.userAPI.submitOffchainWithdraw({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, counterFactualInfo: undefined, fastWithdrawalMode: false, hashApproved: "", maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, minGas: 0, owner: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address, storageId: 0, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, validUntil: 0, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.MetaMask, eddsaKey: eddsaKey.sk, apiKey, }); console.log("response:", response); ``` ================================================ FILE: docs/js_sdk/withdraw/withdrawNFT.md ================================================ # Withdraw NFT Definition: Loopring L2 withdraw NFT to Ethereum L1 *** ## Step 1. getAccount ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); //Step 5. getUserNFTBalances const {userNFTBalances} = await LoopringAPI.userAPI.getUserNFTBalances( {accountId: LOOPRING_EXPORTED_ACCOUNT.accountId}, apiKey ); const tokenInfo = userNFTBalances.find( (item) => item.tokenAddress?.toLowerCase() === LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress.toLowerCase() && item.nftId && web3.utils.hexToNumberString(item.nftId) === LOOPRING_EXPORTED_ACCOUNT.nftTokenId.toString() ); ``` *** ## Step 5. fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, deployInWithdraw: tokenInfo?.deploymentStatus === DEPLOYMENT_STATUS.NOT_DEPLOYED, // when token is not deploy the fee is diff }, apiKey ); console.log("fee:", fee); ``` *** ## Step 6. withdraw ```ts const response = await LoopringAPI.userAPI.submitNFTWithdraw({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, counterFactualInfo: undefined, hashApproved: "", maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: fee.fees["LRC"].fee ?? "9400000000000000000", }, minGas: 0, owner: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address, storageId: 0, token: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, validUntil: 0, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.MetaMask, eddsaKey: eddsaKey.sk, apiKey, }); console.log("response:", response); ``` ================================================ FILE: jest.config.cjs ================================================ module.exports = { preset: 'ts-jest', verbose: true, transform: { '^.+\\.(js|jsx|ts|tsx)$': './node_modules/babel-jest', }, "moduleNameMapper": { "axios": "axios/dist/node/axios.cjs" }, "transformIgnorePatterns": ["node_modules\/(?!axios)"] } ================================================ FILE: package.json ================================================ { "name": "@loopring-web/loopring-sdk", "version": "3.9.23", "author": "Loopring Dev Team", "description": "Loopring SDK", "license": "SEE LICENSE IN LICENSE", "main": "dist/index.cjs", "module": "dist/index.esm.js", "types": "dist/index.d.ts", "source": "src/index.ts", "exports": { "require": "./dist/index.cjs", "import": "./dist/index.esm.js" }, "files": [ "dist", "!tests", "!__snapshots__", "!*.test.js", "!*.test.js.map", "!*.test.ts", "!*.test.d.ts" ], "type": "module", "prettier": { "semi": false, "tabWidth": 2, "printWidth": 100, "singleQuote": true, "trailingComma": "all", "jsxSingleQuote": true, "bracketSpacing": true }, "dependencies": { "@ethereumjs/common": "^2.4.0", "@ethereumjs/tx": "^3.3.0", "@types/jsbn": "^1.2.30", "axios": "^1.4.0", "bignumber.js": "9.1.1", "blake-hash": "^2.0.0", "blake2b": "^2.1.3", "bn.js": "^5.2.1", "buffer": "^6.0.3", "core-js": "3", "crypto-js": "4.1.1", "eth-sig-util": "2.3.0", "ethereumjs-abi": "0.6.8", "ethereumjs-util": "5.2.0", "ethers": "^5.7.2", "js-sha3": "^0.8.0", "js-sha512": "^0.8.0", "jsbn": "^1.1.0", "multiformats": "^12.0.1", "web-encoding": "^1.1.5", "web3": "1.10.0" }, "build": { "files": [ "src/**/*", "node_modules/**/*" ], "publish": { "provider": "custom", "repo": "https://github.com/Loopring/loopring_sdk", "owner": "Loopring Dev Team" } }, "scripts": { "build": "rimraf dist && NODE_ENV=production cross-env BABEL_ENV=prod rollup -c --bundleConfigAsCjs", "start": "NODE_ENV=development rollup -c --bundleConfigAsCjs -w", "test": "tsdx test", "test_calc": "tsdx test", "lint": "eslint . --fix --quiet --ext .ts", "prepublishDev": "NODE_ENV=dev build build", "prepublishOnly": "yarn build", "doc": "typedoc --out docs/detail src\\/", "proxy": "export http_proxy=http://127.0.0.1:1087;export https_proxy=http://127.0.0.1:1087;", "build-book": "npx honkit build", "cp-file-doc": "cp README.md ./docs/README.md; cp Changelog.md ./docs/Changelog.md; ", "build-b": "npm run doc; npm run build-book", "serve-book": "npx honkit serve", "deploy-book": "gh-pages -d _book" }, "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" ] }, "lint-staged": { "**/*": "prettier --write --ignore-unknown" }, "devDependencies": { "@babel/plugin-transform-optional-chaining": "^7.23.4", "@babel/plugin-transform-typescript": "^7.14.6", "@babel/preset-env": "^7.14.5", "@babel/preset-typescript": "^7.14.5", "@microsoft/tsdoc": "^0.13.2", "@rollup/plugin-babel": "^6.0.3", "@rollup/plugin-commonjs": "^25.0.2", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.1.0", "@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.0.0", "@types/bignumber.js": "^5.0.0", "@types/blake2b": "^2.1.0", "@types/classnames": "^2.2.11", "@types/collections": "^5.1.2", "@types/crypto-js": "^4.0.1", "@types/eth-sig-util": "^2.1.0", "@types/ethereumjs-abi": "^0.6.3", "@types/ethereumjs-tx": "^2.0.0", "@types/ethereumjs-util": "5.2.0", "@types/jest": "^26.0.23", "@types/lodash": "^4.14.168", "@types/mocha": "^8.2.2", "@types/ms": "^0.7.31", "@types/node": "^12.0.0", "@types/node-fetch": "^3.0.3", "@types/request": "^2.48.6", "@types/request-promise": "^4.1.48", "@types/truffle-privatekey-provider": "^1.1.0", "@typescript-eslint/eslint-plugin": "^4.6.0", "@typescript-eslint/parser": "^4.6.0", "async": "^3.2.0", "babel-jest": "^26.6.3", "babel-plugin-import": "^1.13.3", "babel-plugin-transform-class-properties": "^6.24.1", "babel-preset-env": "^1.7.0", "chai": "^4.3.4", "cross-env": "^7.0.3", "gh-pages": "^3.2.3", "honkit": "^3.6.20", "jest": "^26.6.3", "mocha": "^8.3.2", "prettier": "2.8.8", "request-promise": "^4.2.6", "rimraf": "^5.0.1", "rollup": "^3.25.3", "rollup-plugin-typescript2": "^0.36.0", "truffle-privatekey-provider": "1.5.0", "ts-node": "^10.0.0", "tslib": "^2.3.0", "typedoc": "^0.22.9", "typedoc-plugin-markdown": "^3.11.7", "typescript": "^5.1.3" } } ================================================ FILE: rollup.config.mjs ================================================ import commonjs from '@rollup/plugin-commonjs'; import babel from '@rollup/plugin-babel' import terser from '@rollup/plugin-terser' import json from '@rollup/plugin-json'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import typescript from 'rollup-plugin-typescript2'; import { builtinModules } from 'module'; import pkg from './package.json' assert { type: "json" }; // console.log('pkg',pkg) module.exports = { input: pkg.source, output: [ { file:pkg.main , format: 'cjs', sourcemap: true, plugins: [terser()], }, { file: pkg.module , format: 'esm', sourcemap: true, plugins: [terser()], } ], external: [ ...builtinModules, ...(pkg.dependencies ? Object.keys(pkg.dependencies) : []), ...(pkg.devDependencies ? Object.keys(pkg.devDependencies) : []), ...(pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : []) ], watch: { include: 'src/**', }, plugins: [ json(), typescript({ abortOnError: process.env.NODE_ENV === 'production', tsconfig:'./tsconfig.json', tsconfigDefaults: { exclude: [ // all TS test files, regardless whether co-located or in test/ etc '**/*.spec.ts', '**/*.test.ts', '**/*.spec.ts', '**/*.test.ts', // TS defaults below 'node_modules', 'bower_components', 'jspm_packages', ], compilerOptions: { sourceMap: true, declaration: true, jsx: 'react', }, }, tsconfigOverride: { compilerOptions: Object.assign({ // TS -> esnext, then leave the rest to babel-preset-env target: 'esnext' }, { declaration: true, declarationMap: true }), }, } ), commonjs() , nodeResolve({ exportConditions: ['import', 'default', 'require'], mainFields: ['module', 'main', 'browser'], modulesOnly: true, preferBuiltins: false, }), babel({ babelHelpers: 'bundled', include: ['src/**/*.ts'], exclude: './node_modules/**', }), ] }; ================================================ FILE: src/api/ammpool_api.ts ================================================ /* eslint-disable camelcase */ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' import { LOOPRING_URLs } from '../defs' import * as sign_tools from './sign/sign_tools' import { makeAmmPool } from '../utils' export class AmmpoolAPI extends BaseAPI { /* * Returns the fee rate of users placing orders in specific markets */ public async getAmmPoolConf(): Promise<{ raw_data: R ammpools: loopring_defs.LoopringMap pairs: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: LOOPRING_URLs.GET_AMM_POOLS_CONF, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const { ammpools, pairs } = makeAmmPool(raw_data) // if (raw_data.code) { // return { // ...raw_data, // }; // } return { ammpools, pairs, raw_data, } } /* */ public async getAmmPoolUserRewards(request: loopring_defs.GetAmmUserRewardsRequest): Promise<{ raw_data: R ammUserRewardMap: loopring_defs.AmmUserRewardMap }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_AMMPOOL_REWARDS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const ammUserRewardMap: loopring_defs.AmmUserRewardMap = {} if (raw_data?.current) { raw_data?.current.forEach((item: loopring_defs.AmmUserReward) => { ammUserRewardMap[item.market] = { current: item, lastDay: undefined, } }) } if (raw_data?.lastDay) { raw_data?.lastDay.forEach((item: loopring_defs.AmmUserReward) => { ammUserRewardMap[item.market] = { ...ammUserRewardMap[item.market], lastDay: item, } }) } return { ammUserRewardMap, raw_data, } } /* */ public async getAmmPoolGameRank(request: loopring_defs.GetAmmPoolGameRankRequest): Promise<{ raw_data: R totalRewards: loopring_defs.TokenVolumeV3[] userRankList: loopring_defs.GameRankInfo[] }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_AMMPOOL_GAME_RANK, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const totalRewards: loopring_defs.TokenVolumeV3[] = raw_data?.totalRewards ? raw_data.totalRewards : [] const userRankList: loopring_defs.GameRankInfo[] = raw_data?.userRankList ? raw_data.userRankList : [] return { totalRewards, userRankList, raw_data, } } /* */ public async getAmmPoolGameUserRank( request: loopring_defs.GetAmmPoolGameUserRankRequest, apiKey: string, ): Promise<{ raw_data: R userRank: loopring_defs.GameRankInfo }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, apiKey, url: LOOPRING_URLs.GET_AMMPOOL_GAME_USER_RANK, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } const userRank: loopring_defs.GameRankInfo = raw_data.data return { userRank, raw_data: raw_data.data, } } private getOrderList(lst: loopring_defs.AmmPoolActivityRule[], order: loopring_defs.SortOrder) { return lst.sort( (a: loopring_defs.AmmPoolActivityRule, b: loopring_defs.AmmPoolActivityRule) => { if (order === loopring_defs.SortOrder.ASC) { return a.rangeFrom < b.rangeFrom ? 1 : 0 } return a.rangeFrom > b.rangeFrom ? 1 : 0 }, ) } /* */ public async getAmmPoolActivityRules(): Promise<{ raw_data: R activityInProgressRules: loopring_defs.LoopringMap activityDateMap: loopring_defs.LoopringMap<{ AMM_MINING?: loopring_defs.LoopringMap ORDERBOOK_MINING?: loopring_defs.LoopringMap SWAP_VOLUME_RANKING?: loopring_defs.LoopringMap }> groupByRuleType: loopring_defs.LoopringMap groupByActivityStatus: loopring_defs.LoopringMap groupByRuleTypeAndStatus: loopring_defs.LoopringMap< loopring_defs.LoopringMap > }> { const reqParams: loopring_defs.ReqParams = { url: LOOPRING_URLs.GET_AMM_ACTIVITY_RULES, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } let activityInProgressRules: loopring_defs.LoopringMap = {} const activityDateMap: loopring_defs.LoopringMap<{ AMM_MINING?: loopring_defs.LoopringMap ORDERBOOK_MINING?: loopring_defs.LoopringMap SWAP_VOLUME_RANKING?: loopring_defs.LoopringMap }> = {} //{AMM_MINING:{},ORDERBOOK_MINING:{},SWAP_VOLUME_RANKING:{}} const groupByRuleType: loopring_defs.LoopringMap = {} let groupByRuleTypeAndStatus: loopring_defs.LoopringMap< loopring_defs.LoopringMap > = {} const groupByActivityStatus: loopring_defs.LoopringMap = {} const currentTs = new Date().getTime() if (raw_data instanceof Array) { raw_data.forEach((item: loopring_defs.AmmPoolActivityRule) => { const status = currentTs < item.rangeFrom ? loopring_defs.AmmPoolActivityStatus.NotStarted : currentTs >= item.rangeFrom && currentTs <= item.rangeTo ? loopring_defs.AmmPoolActivityStatus.InProgress : loopring_defs.AmmPoolActivityStatus.EndOfGame item.status = status if (status === loopring_defs.AmmPoolActivityStatus.InProgress) { const ruleType = activityInProgressRules[item.market] ? [...activityInProgressRules[item.market].ruleType, item.ruleType] : [item.ruleType] activityInProgressRules = { ...activityInProgressRules, [item.market]: { ...item, ruleType }, } } groupByRuleType[item.ruleType] = [ ...(groupByRuleType[item.ruleType] ? groupByRuleType[item.ruleType] : []), item, ] groupByActivityStatus[status] = [ ...(groupByActivityStatus[status] ? groupByActivityStatus[status] : []), item, ] activityDateMap[item.rangeFrom] = { ...(activityDateMap[item.rangeFrom] ? activityDateMap[item.rangeFrom] : {}), [item.ruleType]: { ...(activityDateMap[item.rangeFrom] ? activityDateMap[item.rangeFrom][item.ruleType] ? activityDateMap[item.rangeFrom][item.ruleType] : {} : {}), [item.market]: item, }, } groupByRuleTypeAndStatus = { ...groupByRuleTypeAndStatus, [item.ruleType]: { ...(groupByRuleTypeAndStatus[item.ruleType] ? groupByRuleTypeAndStatus[item.ruleType] : {}), [status]: [ ...(groupByRuleTypeAndStatus[item.ruleType] ? groupByRuleTypeAndStatus[item.ruleType][status] ? groupByRuleTypeAndStatus[item.ruleType][status] : [] : []), item, ], }, } }) } return { activityInProgressRules, activityDateMap, groupByRuleType, groupByActivityStatus, groupByRuleTypeAndStatus, raw_data, } } /* */ public async getAmmAssetHistory(request: loopring_defs.GetAmmAssetRequest): Promise<{ raw_data: R poolAddress: string market: string dataSeries: any }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_AMM_ASSET_HISTORY, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const poolAddress = raw_data.poolAddress const market = raw_data.market const dataSeries = raw_data.data return { poolAddress, market, dataSeries, raw_data, } } /* */ public async getAmmPoolStats(): Promise<{ raw_data: R ammPoolStats: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: LOOPRING_URLs.GET_AMM_POOL_STATS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } const ammPoolStats: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item: loopring_defs.AmmPoolStat) => { ammPoolStats[item.market] = item }) } return { ammPoolStats, raw_data, } } /* */ public async getAmmPoolSnapshot(request: loopring_defs.GetAmmPoolSnapshotRequest): Promise<{ raw_data: R ammPoolSnapshot: loopring_defs.AmmPoolSnapshot }> { const reqParams: loopring_defs.ReqParams = { url: LOOPRING_URLs.GET_AMM_POOLS_SNAPSHOT, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const ammPoolSnapshot: loopring_defs.AmmPoolSnapshot = raw_data return { ammPoolSnapshot, raw_data, } } /* */ public async getAmmPoolBalances(): Promise<{ raw_data: R ammpoolsbalances: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: LOOPRING_URLs.GET_AMM_POOLS_BALANCES, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const ammpoolsbalances: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item: any) => { const tempPooled: any = {} if (item?.pooled instanceof Array) { item.pooled.forEach((item2: any) => { tempPooled[item2.tokenId] = item2 }) } item.pooledMap = tempPooled let poolName = item.poolName if (poolName.indexOf('LRCETH') >= 0) { poolName = 'AMM-LRC-ETH' } ammpoolsbalances[poolName] = item }) } return { ammpoolsbalances, raw_data, } } /* */ public async getLiquidityMining( request: loopring_defs.GetLiquidityMiningRequest, apiKey: string, ): Promise<{ raw_data: R rewards: loopring_defs.RewardItem[] }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, apiKey, url: LOOPRING_URLs.GET_LIQUIDITY_MINING, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { rewards: raw_data?.data ? (raw_data.data as loopring_defs.RewardItem[]) : [], raw_data, } } /* */ public async getLiquidityMiningUserHistory( request: loopring_defs.GetLiquidityMiningUserHistoryRequest, ): Promise<{ raw_data: R userMiningInfos: loopring_defs.UserMiningInfo[] }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_LIQUIDITY_MINING_USER_HISTORY, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { userMiningInfos: raw_data.data as loopring_defs.UserMiningInfo[], raw_data, } } /* */ public async getUserAmmPoolTxs( request: loopring_defs.GetUserAmmPoolTxsRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userAmmPoolTxs: loopring_defs.UserAmmPoolTx[] }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, apiKey, url: LOOPRING_URLs.GET_USER_AMM_POOL_TXS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data.totalNum, userAmmPoolTxs: raw_data.transactions as loopring_defs.UserAmmPoolTx[], raw_data, } } /* */ public async getAmmPoolTxs(request: loopring_defs.GetAmmPoolTxsRequest): Promise<{ raw_data: R totalNum: number transactions: loopring_defs.AmmPoolTx[] }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_AMM_POOL_TXS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } let transactions = undefined if (raw_data?.transactions) { transactions = raw_data?.transactions } return { totalNum: raw_data.totalNum, transactions: transactions as loopring_defs.AmmPoolTx[], raw_data, } } /* */ public async getAmmPoolTrades(request: loopring_defs.GetAmmPoolTradesRequest): Promise<{ raw_data: R totalNum: number ammPoolTrades: loopring_defs.AmmPoolTrade[] }> { const reqParams: loopring_defs.ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_AMM_POOL_TRADE_TXS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data.totalNum, ammPoolTrades: raw_data.transactions as loopring_defs.AmmPoolTrade[], raw_data, } } /* */ public async joinAmmPool( request: loopring_defs.JoinAmmPoolRequest, patch: loopring_defs.AmmPoolRequestPatch, apiKey: string, ): Promise<{ raw_data: R joinAmmPoolResult: loopring_defs.JoinAmmPoolResult }> { if (!request?.validUntil) request.validUntil = Date.now() const reqParams: loopring_defs.ReqParams = { bodyParams: request, apiKey, url: LOOPRING_URLs.POST_JOIN_AMM_POOL, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const { eddsaSig } = sign_tools.get_EddsaSig_JoinAmmPool(request, patch) request.eddsaSignature = eddsaSig const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { joinAmmPoolResult: raw_data as loopring_defs.JoinAmmPoolResult, raw_data, } } /* */ public async exitAmmPool( request: loopring_defs.ExitAmmPoolRequest, patch: loopring_defs.AmmPoolRequestPatch, apiKey: string, ): Promise<{ raw_data: R exitAmmPoolResult: loopring_defs.ExitAmmPoolResult }> { if (!request?.validUntil) request.validUntil = Date.now() const reqParams: loopring_defs.ReqParams = { bodyParams: request, apiKey, url: LOOPRING_URLs.POST_EXIT_AMM_POOL, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const { eddsaSig } = sign_tools.get_EddsaSig_ExitAmmPool(request, patch) request.eddsaSignature = eddsaSig const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { exitAmmPoolResult: raw_data as loopring_defs.ExitAmmPoolResult, raw_data, } } } ================================================ FILE: src/api/base_api.ts ================================================ import * as loopring_defs from '../defs' import { Request } from './request' import { addHexPrefix, toBuffer, toHex } from '../utils' import { myLog } from '../utils/log_tools' import { contracts as abi } from './ethereum/contracts' import { AxiosResponse } from 'axios' import * as ethUtil from 'ethereumjs-util' import { isContract } from './contract_api' import { getWindowSafely } from 'utils/window_utils' export const KEY_MESSAGE = 'Sign this message to access Loopring Exchange: ' + '${exchangeAddress}' + ' with key nonce: ' + '${nonce}' export class BaseAPI { static KEY_MESSAGE: string = KEY_MESSAGE protected baseUrl = '' protected chainId: loopring_defs.ChainId = loopring_defs.ChainId.MAINNET public genErr(err: Error | (AxiosResponse & Error)): loopring_defs.RESULT_INFO { if (err.hasOwnProperty('request')) { // const axiosError = errorInfo as AxiosResponse; return { // @ts-ignore; message: loopring_defs.ConnectorError.HTTP_ERROR, ...err, msg: loopring_defs.ConnectorError.HTTP_ERROR, code: loopring_defs.LoopringErrorCode.HTTP_ERROR, } as loopring_defs.RESULT_INFO err?.message } else if (!err || !err?.message) { return { message: 'unKnown', code: loopring_defs.LoopringErrorCode.SKD_UNKNOW, } } else { const key = Reflect.ownKeys(loopring_defs.ConnectorError).find( (key) => err?.message.search( loopring_defs.ConnectorError[key as keyof typeof loopring_defs.ConnectorError], ) !== -1, ) if (key) { return { ...err, message: key as keyof typeof loopring_defs.ConnectorError, code: loopring_defs.LoopringErrorCode[key as keyof typeof loopring_defs.ConnectorError], } as loopring_defs.RESULT_INFO } return { ...(err instanceof Error ? Reflect.ownKeys(err).reduce((prev, item) => { // @ts-ignore return { ...prev, [item]: err[item.toString()] } }, {}) : err), code: loopring_defs.LoopringErrorCode.SKD_UNKNOW, } } } protected returnTxHash( raw_data: T, ): (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO { if (raw_data?.resultInfo) { return { ...raw_data.resultInfo, message: raw_data.resultInfo?.msg ? raw_data.resultInfo?.msg : raw_data?.resultInfo.message, } } return { ...raw_data, raw_data, } } private timeout: number private baseUrlMap: { [key: number]: string } | undefined public constructor( param: InitParam, timeout: number = 6000, baseUrlMap = { [loopring_defs.ChainId.MAINNET]: 'https://api3.loopring.io', [loopring_defs.ChainId.GOERLI]: 'https://uat2.loopring.io', }, ) { if (param.baseUrl) { this.baseUrl = param.baseUrl } else if (param.chainId !== undefined) { this.setChainId(param.chainId) } else { this.setChainId(loopring_defs.ChainId.GOERLI) } this.baseUrlMap = baseUrlMap this.timeout = timeout } public async getAvailableBroker( request: loopring_defs.GetAvailableBrokerRequest, ): Promise<{ broker: string }> { const reqParams: loopring_defs.ReqParams = { sigFlag: loopring_defs.SIG_FLAG.NO_SIG, queryParams: request, url: loopring_defs.LOOPRING_URLs.GET_AVAILABLE_BROKER, method: loopring_defs.ReqMethod.GET, } const result = (await this.makeReq().request(reqParams)).data return result } public async getCounterFactualInfo( request: loopring_defs.GetCounterFactualInfoRequest, ): Promise<{ raw_data: T counterFactualInfo: loopring_defs.CounterFactualInfo | undefined error?: loopring_defs.RESULT_INFO }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.COUNTER_FACTUAL_INFO, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data let counterFactualInfo: loopring_defs.CounterFactualInfo | undefined let error: loopring_defs.RESULT_INFO | undefined = undefined if (raw_data && raw_data?.resultInfo) { error = raw_data?.resultInfo } else { counterFactualInfo = { ...raw_data, } as loopring_defs.CounterFactualInfo } return { counterFactualInfo, error, raw_data, } } public setChainId(chainId: loopring_defs.ChainId) { this.baseUrl = this.baseUrlMap && this.baseUrlMap[0] ? getBaseUrlByChainId(chainId, this.baseUrlMap as any) : getBaseUrlByChainId(chainId) this.chainId = chainId } public setBaseUrl(baseUrl: string) { this.baseUrl = baseUrl } protected makeReq(): Request { return new Request(this.baseUrl, this.timeout) } } export function ecRecover( account: string, msg: string, sig: any, // time = 3000 ) { try { // let timer; const hash = ethUtil.hashPersonalMessage(toBuffer(msg)) const signature = ethUtil.fromRpcSig(sig) const result = ethUtil.ecrecover(hash, signature.v, signature.r, signature.s) const result2 = ethUtil.pubToAddress(result) const recAddress = toHex(result2) myLog('ecRecover recAddress', result, result2, recAddress) return { result: recAddress.toLowerCase() === account.toLowerCase(), } } catch (error) { return { error } } } export async function contractWalletValidate32(web3: any, account: string, msg: string, sig: any) { return new Promise((resolve) => { const hash = ethUtil.hashPersonalMessage(toBuffer(msg)) const data = abi.Contracts.ContractWallet.encodeInputs('isValidSignature(bytes32,bytes)', { _data: hash, _signature: toBuffer(sig), }) web3.eth.call( { to: account, // contract addr data: data, }, function (err: any, result: any) { if (!err) { const valid = abi.Contracts.ContractWallet.decodeOutputs( 'isValidSignature(bytes32,bytes)', result, ) resolve({ result: toHex(toBuffer(valid[0])) === data.slice(0, 10), }) } else resolve({ error: err }) }, ) }) } export async function mykeyWalletValid(web3: any, account: string, msg: string, sig: any) { const myKeyContract = '0xADc92d1fD878580579716d944eF3460E241604b7' return new Promise((resolve) => { web3.eth.call( { to: myKeyContract, data: abi.Contracts.ContractWallet.encodeInputs('getKeyData', { _account: account, _index: 3, }), }, function (err: any, res: any) { if (!err) { const signature = ethUtil.fromRpcSig(sig) const hash = ethUtil.hashPersonalMessage(ethUtil.keccak256(toBuffer(msg))) const address = addHexPrefix( abi.Contracts.ContractWallet.decodeOutputs('getKeyData', res)[0], ) const recAddress = toHex( ethUtil.pubToAddress(ethUtil.ecrecover(hash, signature.v, signature.r, signature.s)), ) resolve({ result: recAddress.toLowerCase() === address.toLowerCase(), }) } else { resolve({ error: err }) } }, ) }) } export async function ecRecover2(account: string, message: string, signature: any) { const messageBuffer = Buffer.from(message, 'utf8') signature = signature.split('x')[1] const parts = [ Buffer.from(`\x19Ethereum Signed Message:\n${messageBuffer.length}`, 'utf8'), messageBuffer, ] const totalHash = ethUtil.keccak(Buffer.concat(parts)) const r = Buffer.from(signature.substring(0, 64), 'hex') const s = Buffer.from(signature.substring(64, 128), 'hex') const old_v = Number(addHexPrefix(signature.substring(128, 130))) let v = old_v if (v <= 1) v += 27 const pub = ethUtil.ecrecover(totalHash, v, r, s) const recoveredAddress = '0x' + ethUtil.pubToAddress(pub).toString('hex') // if (account.toLowerCase() !== recoveredAddress.toLowerCase()) { // myLog('v:', v, 'old_v:', old_v, ' recoveredAddress:', recoveredAddress) // } return new Promise((resolve) => resolve({ result: account.toLowerCase() === recoveredAddress.toLowerCase(), }), ) } const getBaseUrlByChainId = ( id: loopring_defs.ChainId, baseUrlMap = { [loopring_defs.ChainId.MAINNET]: 'https://api3.loopring.io', [loopring_defs.ChainId.GOERLI]: 'https://uat2.loopring.io', }, ) => { let baseUrl = '' switch (id) { case loopring_defs.ChainId.MAINNET: baseUrl = baseUrlMap[loopring_defs.ChainId.MAINNET] break default: baseUrl = baseUrlMap[loopring_defs.ChainId.GOERLI] break } return baseUrl } /** * @default chainId 1 * @default keySeed `Sign this message to access Loopring exchange: ${exchangeAddress} with key nonce: ${nonce}` */ export interface InitParam { chainId?: loopring_defs.ChainId baseUrl?: string } export function formatSig(rpcSig: string) { const sig = ethUtil.fromRpcSig(rpcSig) return ethUtil.toRpcSig(sig.v, sig.r, sig.s) } export function recoverSignType(web3: any, account: string, msg: string, sig: string) { const ethRecover: any = ecRecover(account, msg, sig) if (ethRecover.result) { return '03' } else { return '' } } export async function personalSign( web3: any, account: string | undefined, pwd: string, msg: string, walletType: loopring_defs.ConnectorNames, chainId: loopring_defs.ChainId, accountId?: number, counterFactualInfo?: loopring_defs.CounterFactualInfo, isMobile?: boolean, ) { if (!account) { return { error: 'personalSign got no account' } } return new Promise((resolve) => { try { web3.eth.personal.sign(msg, account, pwd, async function (err: any, result: any) { if (!err) { // LOG: for signature myLog('ecRecover before', 'msg', msg, 'result', result, counterFactualInfo) // Valid:1. counter Factual signature Valid if (counterFactualInfo && accountId) { myLog('fcWalletValid counterFactualInfo accountId:') const fcValid = await fcWalletValid( web3, account, msg, result, accountId, chainId, counterFactualInfo, ) if (fcValid.result) { resolve({ sig: result, counterFactualInfo: fcValid.counterFactualInfo, }) return } } // Valid: 2. webview directory signature Valid myLog('ecRecover before', result) const valid: any = ecRecover(account, msg, result) myLog('ecRecover after', valid.result) if (valid.result) { return resolve({ sig: result }) } else { myLog('ecRecover error', valid) } // Valid: 3. contractWallet no recover // signature Valid `isValidSignature(bytes32,bytes)` // LOG: for signature myLog('Valid: 3. contractWallet before') const isContractCheck = await isContract(web3, account) if (isContractCheck) { // LOG: for signature myLog('Valid: 5 failed isContract. no ecrecover') return resolve({ sig: result }) } // Valid: 5. contractWallet signature Valid `isValidSignature(bytes32,bytes)` // const walletValid2: any = await contractWalletValidate32( // web3, // account, // msg, // result // ); // if (walletValid2.result) { // return resolve({ sig: result }); // } // Valid: 6. counter Factual signature Valid when no counterFactualInfo if (accountId) { const fcValid = await fcWalletValid(web3, account, msg, result, accountId, chainId) if (fcValid.result) { return resolve({ sig: result, counterFactualInfo: fcValid.counterFactualInfo, }) } } // Valid: 7. myKeyValid Valid again const myKeyValid: any = await mykeyWalletValid(web3, account, msg, result) if (myKeyValid.result) { return resolve({ sig: result }) } // Valid: Error cannot pass personalSign Valid // eslint-disable-next-line no-console console.log('web3.eth.personal.sign Valid, valid 5 ways, all failed!') return resolve({ error: 'web3.eth.personal.sign Valid, valid 5 ways, all failed!', }) } else { return resolve({ error: 'personalSign err before Validate:' + err, }) } }) } catch (err) { // LOG: for signature myLog('personalSign callback err', (err as unknown as any)?.message) resolve({ error: err as any }) } }) } export async function fcWalletValid( web3: any, account: string, msg: string, result: any, accountId: number, chainId: loopring_defs.ChainId, counterFactualInfo?: loopring_defs.CounterFactualInfo, ): Promise<{ counterFactualInfo?: loopring_defs.CounterFactualInfo error?: any result?: boolean }> { const api = new BaseAPI({ chainId }) if (counterFactualInfo === undefined || !counterFactualInfo.walletOwner) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore counterFactualInfo = (await api.getCounterFactualInfo({ accountId })).counterFactualInfo } if (counterFactualInfo && counterFactualInfo.walletOwner) { let _result: string if (result.startsWith('0x')) { _result = result.slice(0, 132) } else { _result = result } const valid: any = ecRecover(counterFactualInfo.walletOwner, msg, _result) if (valid.result) { myLog('fcWalletValid e:', result, counterFactualInfo) return { result, counterFactualInfo } } else { return { error: 'valid walletOwner failed' } } } else { return { error: 'valid walletOwner failed' } } } ================================================ FILE: src/api/config/abis/contractWallet.ts ================================================ export const contractWallet = [ { inputs: [ { internalType: 'bytes', name: '_data', type: 'bytes', }, { internalType: 'bytes', name: '_signature', type: 'bytes', }, ], name: 'isValidSignature', outputs: [ { internalType: 'bytes4', name: 'magicValue', type: 'bytes4', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'bytes', name: '_data', type: 'bytes32', }, { internalType: 'bytes', name: '_signature', type: 'bytes', }, ], name: 'isValidSignature', outputs: [ { internalType: 'bytes4', name: 'magicValue', type: 'bytes4', }, ], stateMutability: 'view', type: 'function', }, { constant: true, inputs: [ { name: '_account', type: 'address', }, { name: '_index', type: 'uint256', }, ], name: 'getKeyData', outputs: [ { name: '', type: 'address', }, ], payable: false, stateMutability: 'view', type: 'function', }, ] ================================================ FILE: src/api/config/abis/erc1155.ts ================================================ export const erc1155 = [ { inputs: [ { internalType: 'string', name: 'uri_', type: 'string', }, ], stateMutability: 'nonpayable', type: 'constructor', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'account', type: 'address', }, { indexed: true, internalType: 'address', name: 'operator', type: 'address', }, { indexed: false, internalType: 'bool', name: 'approved', type: 'bool', }, ], name: 'ApprovalForAll', type: 'event', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'operator', type: 'address', }, { indexed: true, internalType: 'address', name: 'from', type: 'address', }, { indexed: true, internalType: 'address', name: 'to', type: 'address', }, { indexed: false, internalType: 'uint256[]', name: 'ids', type: 'uint256[]', }, { indexed: false, internalType: 'uint256[]', name: 'values', type: 'uint256[]', }, ], name: 'TransferBatch', type: 'event', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'operator', type: 'address', }, { indexed: true, internalType: 'address', name: 'from', type: 'address', }, { indexed: true, internalType: 'address', name: 'to', type: 'address', }, { indexed: false, internalType: 'uint256', name: 'id', type: 'uint256', }, { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256', }, ], name: 'TransferSingle', type: 'event', }, { anonymous: false, inputs: [ { indexed: false, internalType: 'string', name: 'value', type: 'string', }, { indexed: true, internalType: 'uint256', name: 'id', type: 'uint256', }, ], name: 'URI', type: 'event', }, { inputs: [ { internalType: 'bytes4', name: 'interfaceId', type: 'bytes4', }, ], name: 'supportsInterface', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'uri', outputs: [ { internalType: 'string', name: '', type: 'string', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address', name: 'account', type: 'address', }, { internalType: 'uint256', name: 'id', type: 'uint256', }, ], name: 'balanceOf', outputs: [ { internalType: 'uint256', name: '', type: 'uint256', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address[]', name: 'accounts', type: 'address[]', }, { internalType: 'uint256[]', name: 'ids', type: 'uint256[]', }, ], name: 'balanceOfBatch', outputs: [ { internalType: 'uint256[]', name: '', type: 'uint256[]', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address', name: 'operator', type: 'address', }, { internalType: 'bool', name: 'approved', type: 'bool', }, ], name: 'setApprovalForAll', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'account', type: 'address', }, { internalType: 'address', name: 'operator', type: 'address', }, ], name: 'isApprovedForAll', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address', name: 'from', type: 'address', }, { internalType: 'address', name: 'to', type: 'address', }, { internalType: 'uint256', name: 'id', type: 'uint256', }, { internalType: 'uint256', name: 'amount', type: 'uint256', }, { internalType: 'bytes', name: 'data', type: 'bytes', }, ], name: 'safeTransferFrom', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'from', type: 'address', }, { internalType: 'address', name: 'to', type: 'address', }, { internalType: 'uint256[]', name: 'ids', type: 'uint256[]', }, { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]', }, { internalType: 'bytes', name: 'data', type: 'bytes', }, ], name: 'safeBatchTransferFrom', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] ================================================ FILE: src/api/config/abis/erc20.ts ================================================ export const erc20 = [ { 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: 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: src/api/config/abis/erc721.ts ================================================ export const erc721 = [ { inputs: [ { internalType: 'string', name: 'name_', type: 'string', }, { internalType: 'string', name: 'symbol_', type: 'string', }, ], stateMutability: 'nonpayable', type: 'constructor', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'owner', type: 'address', }, { indexed: true, internalType: 'address', name: 'approved', type: 'address', }, { indexed: true, internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'Approval', type: 'event', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'owner', type: 'address', }, { indexed: true, internalType: 'address', name: 'operator', type: 'address', }, { indexed: false, internalType: 'bool', name: 'approved', type: 'bool', }, ], name: 'ApprovalForAll', type: 'event', }, { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'from', type: 'address', }, { indexed: true, internalType: 'address', name: 'to', type: 'address', }, { indexed: true, internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'Transfer', type: 'event', }, { inputs: [ { internalType: 'address', name: 'to', type: 'address', }, { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'approve', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'owner', type: 'address', }, ], name: 'balanceOf', outputs: [ { internalType: 'uint256', name: '', type: 'uint256', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'getApproved', outputs: [ { internalType: 'address', name: '', type: 'address', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address', name: 'owner', type: 'address', }, { internalType: 'address', name: 'operator', type: 'address', }, ], name: 'isApprovedForAll', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, { inputs: [], name: 'name', outputs: [ { internalType: 'string', name: '', type: 'string', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'ownerOf', outputs: [ { internalType: 'address', name: '', type: 'address', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address', name: 'from', type: 'address', }, { internalType: 'address', name: 'to', type: 'address', }, { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'safeTransferFrom', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'from', type: 'address', }, { internalType: 'address', name: 'to', type: 'address', }, { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, { internalType: 'bytes', name: '_data', type: 'bytes', }, ], name: 'safeTransferFrom', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'operator', type: 'address', }, { internalType: 'bool', name: 'approved', type: 'bool', }, ], name: 'setApprovalForAll', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'bytes4', name: 'interfaceId', type: 'bytes4', }, ], name: 'supportsInterface', outputs: [ { internalType: 'bool', name: '', type: 'bool', }, ], stateMutability: 'view', type: 'function', }, { inputs: [], name: 'symbol', outputs: [ { internalType: 'string', name: '', type: 'string', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'tokenURI', outputs: [ { internalType: 'string', name: '', type: 'string', }, ], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address', name: 'from', type: 'address', }, { internalType: 'address', name: 'to', type: 'address', }, { internalType: 'uint256', name: 'tokenId', type: 'uint256', }, ], name: 'transferFrom', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] ================================================ FILE: src/api/config/abis/exchange_3_6.ts ================================================ export const exchange = [ { inputs: [ { internalType: "address", name: "owner", type: "address", }, { internalType: "address", name: "tokenAddress", type: "address", }, { internalType: "uint32", name: "accountID", type: "uint32", }, ], name: "forceWithdraw", outputs: [], stateMutability: "payable", type: "function", }, { 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: "bytes", name: "extraData", type: "bytes", }, ], name: "deposit", outputs: [], stateMutability: "payable", type: "function", }, { inputs: [ { internalType: "address", name: "from", type: "address", }, { internalType: "address", name: "to", type: "address", }, { internalType: "enum ExchangeData.NftType", name: "nftType", type: "uint8", }, { internalType: "address", name: "tokenAddress", type: "address", }, { internalType: "uint256", name: "nftId", type: "uint256", }, { internalType: "uint96", name: "amount", type: "uint96", }, { internalType: "bytes", name: "extraData", type: "bytes", }, ], name: "depositNFT", outputs: [], stateMutability: "nonpayable", type: "function", }, { inputs: [ { internalType: "address", name: "owner", type: "address", }, { internalType: "address", name: "token", type: "address", }, ], name: "withdrawFromDepositRequest", outputs: [], stateMutability: "nonpayable", type: "function", }, { inputs: [ { internalType: "address[]", name: "owners", type: "address[]", }, { internalType: "address[]", name: "tokens", type: "address[]", }, ], name: "withdrawFromApprovedWithdrawals", outputs: [], stateMutability: "nonpayable", type: "function", }, { inputs: [ { internalType: "address", name: "owner", type: "address", }, { internalType: "address", name: "token", type: "address", }, ], name: "getAmountWithdrawable", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [ { internalType: "address", name: "owner", type: "address", }, { internalType: "bytes32", name: "txHash", type: "bytes32", }, ], name: "approveTransaction", outputs: [], stateMutability: "nonpayable", type: "function", }, ]; ================================================ FILE: src/api/config/abis/hebao.ts ================================================ export const hebao = [ { inputs: [ { internalType: 'address', name: 'wallet', type: 'address', }, ], name: 'lock', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'wallet', type: 'address', }, { internalType: 'address', name: 'guardian', type: 'address', }, ], name: 'unlock', outputs: [], stateMutability: 'nonpayable', type: 'function', }, ] ================================================ FILE: src/api/config/abis/index.ts ================================================ export * as contractWalletAbi from './contractWallet' export * as erc20Abi from './erc20' export * as erc721Abi from './erc721' export * as erc1155Abi from './erc1155' export * as exchange36Abi from './exchange_3_6' export * as hebao from './hebao' export * as smartWallet from './smartWallet' ================================================ FILE: src/api/config/abis/smartWallet.ts ================================================ export default [ { inputs: [ { internalType: 'contract PriceOracle', name: '_priceOracle', type: 'address' }, { internalType: 'address', name: '_blankOwner', type: 'address' }, ], stateMutability: 'nonpayable', type: 'constructor', }, { inputs: [], name: 'DOMAIN_SEPARATOR', outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], stateMutability: 'view', type: 'function', }, { inputs: [{ internalType: 'address', name: 'guardian', type: 'address' }], name: 'addGuardian', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [{ internalType: 'address', name: 'addr', type: 'address' }], name: 'addToWhitelist', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'token', type: 'address' }, { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'uint256', name: 'amount', type: 'uint256' }, { internalType: 'uint256', name: 'value', type: 'uint256' }, { internalType: 'bytes', name: 'data', type: 'bytes' }, { internalType: 'bool', name: 'forceUseQuota', type: 'bool' }, ], name: 'approveThenCallContract', outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'token', type: 'address' }, { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'uint256', name: 'amount', type: 'uint256' }, { internalType: 'bool', name: 'forceUseQuota', type: 'bool' }, ], name: 'approveToken', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'address[]', name: 'signers', type: 'address[]' }, { internalType: 'bytes[]', name: 'signatures', type: 'bytes[]' }, { internalType: 'uint256', name: 'validUntil', type: 'uint256' }, { internalType: 'address', name: 'wallet', type: 'address' }, ], internalType: 'struct Approval', name: 'approval', type: 'tuple', }, { internalType: 'address', name: 'addr', type: 'address' }, ], name: 'addToWhitelistWA', outputs: [{ internalType: 'bytes32', name: 'approvedHash', type: 'bytes32' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'address[]', name: 'signers', type: 'address[]' }, { internalType: 'bytes[]', name: 'signatures', type: 'bytes[]' }, { internalType: 'uint256', name: 'validUntil', type: 'uint256' }, { internalType: 'address', name: 'wallet', type: 'address' }, ], internalType: 'struct Approval', name: 'approval', type: 'tuple', }, { internalType: 'address', name: 'token', type: 'address' }, { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'uint256', name: 'amount', type: 'uint256' }, { internalType: 'uint256', name: 'value', type: 'uint256' }, { internalType: 'bytes', name: 'data', type: 'bytes' }, ], name: 'approveThenCallContractWA', outputs: [ { internalType: 'bytes32', name: 'approvedHash', type: 'bytes32' }, { internalType: 'bytes', name: 'returnData', type: 'bytes' }, ], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address[]', name: 'to', type: 'address[]' }, { internalType: 'bytes[]', name: 'data', type: 'bytes[]' }, ], name: 'batchCall', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [], name: 'blankOwner', outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'uint256', name: 'value', type: 'uint256' }, { internalType: 'bytes', name: 'data', type: 'bytes' }, { internalType: 'bool', name: 'forceUseQuota', type: 'bool' }, ], name: 'callContract', outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'address[]', name: 'signers', type: 'address[]' }, { internalType: 'bytes[]', name: 'signatures', type: 'bytes[]' }, { internalType: 'uint256', name: 'validUntil', type: 'uint256' }, { internalType: 'address', name: 'wallet', type: 'address' }, ], internalType: 'struct Approval', name: 'approval', type: 'tuple', }, { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'uint256', name: 'value', type: 'uint256' }, { internalType: 'bytes', name: 'data', type: 'bytes' }, ], name: 'callContractWA', outputs: [ { internalType: 'bytes32', name: 'approvedHash', type: 'bytes32' }, { internalType: 'bytes', name: 'returnData', type: 'bytes' }, ], stateMutability: 'nonpayable', type: 'function', }, { inputs: [{ internalType: 'uint256', name: 'newQuota', type: 'uint256' }], name: 'changeDailyQuota', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'address[]', name: 'signers', type: 'address[]' }, { internalType: 'bytes[]', name: 'signatures', type: 'bytes[]' }, { internalType: 'uint256', name: 'validUntil', type: 'uint256' }, { internalType: 'address', name: 'wallet', type: 'address' }, ], internalType: 'struct Approval', name: 'approval', type: 'tuple', }, { internalType: 'uint256', name: 'newQuota', type: 'uint256' }, ], name: 'changeDailyQuotaWA', outputs: [{ internalType: 'bytes32', name: 'approvedHash', type: 'bytes32' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'address[]', name: 'signers', type: 'address[]' }, { internalType: 'bytes[]', name: 'signatures', type: 'bytes[]' }, { internalType: 'uint256', name: 'validUntil', type: 'uint256' }, { internalType: 'address', name: 'wallet', type: 'address' }, ], internalType: 'struct Approval', name: 'approval', type: 'tuple', }, { internalType: 'address', name: 'newMasterCopy', type: 'address' }, ], name: 'changeMasterCopy', outputs: [{ internalType: 'bytes32', name: 'approvedHash', type: 'bytes32' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'uint256', name: 'nonce', type: 'uint256' }, { internalType: 'address', name: 'gasToken', type: 'address' }, { internalType: 'uint256', name: 'gasPrice', type: 'uint256' }, { internalType: 'uint256', name: 'gasLimit', type: 'uint256' }, { internalType: 'uint256', name: 'gasOverhead', type: 'uint256' }, { internalType: 'address', name: 'feeRecipient', type: 'address' }, { internalType: 'bool', name: 'requiresSuccess', type: 'bool' }, { internalType: 'bytes', name: 'data', type: 'bytes' }, { internalType: 'bytes', name: 'signature', type: 'bytes' }, ], name: 'executeMetaTx', outputs: [{ internalType: 'bool', name: '', type: 'bool' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [], name: 'getCreationTimestamp', outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }], stateMutability: 'view', type: 'function', }, { inputs: [{ internalType: 'bool', name: 'includePendingAddition', type: 'bool' }], name: 'getGuardians', outputs: [ { components: [ { internalType: 'address', name: 'addr', type: 'address' }, { internalType: 'uint8', name: 'status', type: 'uint8' }, { internalType: 'uint64', name: 'timestamp', type: 'uint64' }, ], internalType: 'struct Guardian[]', name: '', type: 'tuple[]', }, ], stateMutability: 'view', type: 'function', }, { inputs: [], name: 'getMasterCopy', outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', type: 'function', }, { inputs: [], name: 'getOwner', outputs: [{ internalType: 'address', name: '', type: 'address' }], stateMutability: 'view', type: 'function', }, { inputs: [{ internalType: 'address', name: 'addr', type: 'address' }], name: 'getWhitelistEffectiveTime', outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], stateMutability: 'view', type: 'function', }, { inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }], name: 'inherit', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'owner', type: 'address' }, { internalType: 'address[]', name: 'guardians', type: 'address[]' }, { internalType: 'uint256', name: 'quota', type: 'uint256' }, { internalType: 'address', name: 'inheritor', type: 'address' }, { internalType: 'address', name: 'feeRecipient', type: 'address' }, { internalType: 'address', name: 'feeToken', type: 'address' }, { internalType: 'uint256', name: 'feeAmount', type: 'uint256' }, ], name: 'initialize', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'addr', type: 'address' }, { internalType: 'bool', name: 'includePendingAddition', type: 'bool' }, ], name: 'isGuardian', outputs: [{ internalType: 'bool', name: '', type: 'bool' }], stateMutability: 'view', type: 'function', }, { inputs: [ { internalType: 'bytes32', name: 'signHash', type: 'bytes32' }, { internalType: 'bytes', name: 'signature', type: 'bytes' }, ], name: 'isValidSignature', outputs: [{ internalType: 'bytes4', name: 'magicValue', type: 'bytes4' }], stateMutability: 'view', type: 'function', }, { inputs: [{ internalType: 'address', name: 'addr', type: 'address' }], name: 'isWhitelisted', outputs: [{ internalType: 'bool', name: '', type: 'bool' }], stateMutability: 'view', type: 'function', }, { inputs: [], name: 'lock', outputs: [], stateMutability: 'nonpayable', type: 'function' }, { inputs: [], name: 'priceOracle', outputs: [{ internalType: 'contract PriceOracle', name: '', type: 'address' }], stateMutability: 'view', type: 'function', }, { inputs: [ { components: [ { internalType: 'address[]', name: 'signers', type: 'address[]' }, { internalType: 'bytes[]', name: 'signatures', type: 'bytes[]' }, { internalType: 'uint256', name: 'validUntil', type: 'uint256' }, { internalType: 'address', name: 'wallet', type: 'address' }, ], internalType: 'struct Approval', name: 'approval', type: 'tuple', }, { internalType: 'address', name: 'newOwner', type: 'address' }, { internalType: 'address[]', name: 'newGuardians', type: 'address[]' }, ], name: 'recover', outputs: [{ internalType: 'bytes32', name: 'approvedHash', type: 'bytes32' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [{ internalType: 'address', name: 'addr', type: 'address' }], name: 'removeFromWhitelist', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [{ internalType: 'address', name: 'guardian', type: 'address' }], name: 'removeGuardian', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [{ internalType: 'address[]', name: 'newGuardians', type: 'address[]' }], name: 'resetGuardians', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { components: [ { internalType: 'address[]', name: 'signers', type: 'address[]' }, { internalType: 'bytes[]', name: 'signatures', type: 'bytes[]' }, { internalType: 'uint256', name: 'validUntil', type: 'uint256' }, { internalType: 'address', name: 'wallet', type: 'address' }, ], internalType: 'struct Approval', name: 'approval', type: 'tuple', }, { internalType: 'address[]', name: 'newGuardians', type: 'address[]' }, ], name: 'resetGuardiansWA', outputs: [{ internalType: 'bytes32', name: 'approvedHash', type: 'bytes32' }], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'inheritor', type: 'address' }, { internalType: 'uint32', name: 'waitingPeriod', type: 'uint32' }, ], name: 'setInheritor', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [{ internalType: 'address', name: '_owner', type: 'address' }], name: 'transferOwnership', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'token', type: 'address' }, { internalType: 'address', name: 'to', type: 'address' }, { internalType: 'uint256', name: 'amount', type: 'uint256' }, { internalType: 'bytes', name: 'logdata', type: 'bytes' }, { internalType: 'bool', name: 'forceUseQuota', type: 'bool' }, ], name: 'transferToken', outputs: [], stateMutability: 'nonpayable', type: 'function', }, { inputs: [], name: 'wallet', outputs: [ { internalType: 'address', name: 'owner', type: 'address' }, { internalType: 'uint64', name: 'creationTimestamp', type: 'uint64' }, { internalType: 'uint256', name: 'nonce', type: 'uint256' }, { internalType: 'bool', name: 'locked', type: 'bool' }, { internalType: 'address', name: 'inheritor', type: 'address' }, { internalType: 'uint32', name: 'inheritWaitingPeriod', type: 'uint32' }, { internalType: 'uint64', name: 'lastActive', type: 'uint64' }, { components: [ { internalType: 'uint128', name: 'currentQuota', type: 'uint128' }, { internalType: 'uint128', name: 'pendingQuota', type: 'uint128' }, { internalType: 'uint128', name: 'spentAmount', type: 'uint128' }, { internalType: 'uint64', name: 'spentTimestamp', type: 'uint64' }, { internalType: 'uint64', name: 'pendingUntil', type: 'uint64' }, ], internalType: 'struct Quota', name: 'quota', type: 'tuple', }, ], stateMutability: 'view', type: 'function', }, { stateMutability: 'payable', type: 'receive' }, ] // recovery // transfer // add_guardian // remove_guardian // unlock_wallet // upgrade_contract // deposit_wallet ================================================ FILE: src/api/config/guardianTypeData.ts ================================================ import { ChainId, ConnectorNames, HEBAO_META_TYPE, SigSuffix } from '../../defs' import { myLog } from '../../utils/log_tools' import { getEcDSASig, GetEcDSASigType } from '../sign/sign_tools' import { personalSign } from '../base_api' import { utils } from 'ethers' const EIP712Domain = [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ] const domain = (chainId: ChainId, guardiaContractAddress: any, name: string, version: string) => { return { name, version: version, chainId: chainId, verifyingContract: guardiaContractAddress, } } function getApproveRecoverTypedData({ chainId, guardiaContractAddress, wallet, validUntil, message, walletVersion, }: { chainId: ChainId guardiaContractAddress: any wallet: any validUntil: any // newOwner: any, newGuardians?: Buffer | any message?: { [key: string]: any } walletVersion: 2 | 1 }) { const typedData = { types: { EIP712Domain, recover: [ { name: 'wallet', type: 'address' }, { name: 'validUntil', type: 'uint256' }, { name: 'newOwner', type: 'address' }, ...(walletVersion == 1 ? [] : [{ name: 'newGuardians', type: 'address[]' }]), ], }, domain: domain( chainId, guardiaContractAddress, walletVersion == 1 ? 'GuardianModule' : 'LoopringWallet', walletVersion == 1 ? '1.2.0' : '2.0.0', ), primaryType: 'recover', message: { wallet: wallet, validUntil: validUntil, ...message, // newOwner: newOwner, }, } const hash = utils._TypedDataEncoder.hash(typedData.domain, {recover: typedData.types.recover}, typedData.message) console.log('EIP712 hash', hash) return { typedData, hash } } function getApproveTransferTypedData({ chainId, guardiaContractAddress, wallet, validUntil, newGuardians, message, walletVersion, }: { chainId: ChainId guardiaContractAddress: any wallet: any validUntil: any newGuardians?: Buffer | any message?: { [key: string]: any } walletVersion: 2 | 1 }) { const typedData = { types: { EIP712Domain, transferToken: [ { name: 'wallet', type: 'address' }, { name: 'validUntil', type: 'uint256' }, { name: 'token', type: 'address' }, { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, { name: 'logdata', type: 'bytes' }, ], }, domain: domain( chainId, guardiaContractAddress, walletVersion == 1 ? 'TransferModule' : 'LoopringWallet', walletVersion == 1 ? '1.2.0' : '2.0.0', ), primaryType: 'transferToken', message: { wallet: wallet, validUntil: validUntil, ...message, }, } const hash= utils._TypedDataEncoder.hash(typedData.domain, {transferToken: typedData.types.transferToken}, typedData.message) console.log('EIP712 hash', hash) return { typedData, hash } } // function getAddGuardianTypedData({ // chainId, // guardiaContractAddress, // wallet, // validUntil, // message, // }: { // chainId: ChainId // guardiaContractAddress: any // wallet: any // validUntil: any // message?: { [key: string]: any } // }) { // const typedData = { // types: { // EIP712Domain, // addGuardian: [ // { name: 'wallet', type: 'address' }, // { name: 'validUntil', type: 'uint256' }, // { name: 'guardian', type: 'address' }, // ], // }, // domain: domain(chainId, guardiaContractAddress), // primaryType: 'addGuardian', // message: { // wallet: wallet, // validUntil: validUntil, // ...message, // }, // } // return typedData // } function getRemoveGuardianTypedData({ chainId, guardiaContractAddress, wallet, validUntil, message, walletVersion, }: { chainId: ChainId guardiaContractAddress: any wallet: any validUntil: any message?: { [key: string]: any } walletVersion: 2 | 1 }) { const typedData = { types: { EIP712Domain, removeGuardian: [ { name: 'wallet', type: 'address' }, { name: 'validUntil', type: 'uint256' }, { name: 'guardian', type: 'address' }, ], }, domain: domain( chainId, guardiaContractAddress, walletVersion == 1 ? 'GuardianModule' : 'LoopringWallet', walletVersion == 1 ? '1.2.0' : '2.0.0', ), primaryType: 'removeGuardian', message: { wallet: wallet, validUntil: validUntil, guardian: message!['guardian'], }, } const hash= utils._TypedDataEncoder.hash(typedData.domain, {removeGuardian: typedData.types.removeGuardian}, typedData.message) console.log('EIP712 hash', hash) return { typedData, hash } } function getUnlockWalletTypedData({ chainId, guardiaContractAddress, wallet, validUntil, walletVersion, }: { chainId: ChainId guardiaContractAddress: any wallet: any validUntil: any walletVersion: 2 | 1 // message?: { [key: string]: any } }) { const typedData = { types: { EIP712Domain, unlock: [ { name: 'wallet', type: 'address' }, { name: 'validUntil', type: 'uint256' }, ], }, // EIP712.hash( // EIP712.Domain("LoopringWallet", "2.0.0", address(this)) // ) domain: domain( chainId, guardiaContractAddress, walletVersion == 1 ? 'GuardianModule' : 'LoopringWallet', walletVersion == 1 ? '1.2.0' : '2.0.0', ), primaryType: 'unlock', message: { wallet: wallet, validUntil: validUntil, }, } const hash= utils._TypedDataEncoder.hash(typedData.domain, {unlock: typedData.types.unlock}, typedData.message) console.log('EIP712 hash', hash) return { typedData, hash } } function getApproveChangeMasterCopy({ chainId, guardiaContractAddress, wallet, validUntil, newGuardians, message, walletVersion, }: { chainId: ChainId guardiaContractAddress: any wallet: any validUntil: any newGuardians?: Buffer | any message?: { [key: string]: any } walletVersion: 2 | 1 }) { const typedData = { types: { EIP712Domain, changeMasterCopy: [ { name: 'wallet', type: 'address' }, { name: 'validUntil', type: 'uint256' }, { name: 'masterCopy', type: 'address' }, ], }, domain: domain( chainId, guardiaContractAddress, walletVersion == 1 ? 'GuardianModule' : 'LoopringWallet', walletVersion == 1 ? '1.2.0' : '2.0.0', ), primaryType: 'changeMasterCopy', message: { wallet: wallet, validUntil: validUntil, ...message, }, } const hash= utils._TypedDataEncoder.hash(typedData.domain, {changeMasterCopy: typedData.types.changeMasterCopy}, typedData.message) console.log('EIP712 hash', hash) return { typedData, hash } } function getDepositWalletTypedData({ chainId, guardiaContractAddress, wallet, validUntil, newGuardians, message, walletVersion, }: { chainId: ChainId guardiaContractAddress: any wallet: any validUntil: any newGuardians?: Buffer | any message?: { [key: string]: any } walletVersion: 2 | 1 }) { const typedData = { types: { EIP712Domain, callContract: [ { name: 'wallet', type: 'address' }, { name: 'validUntil', type: 'uint256' }, { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'data', type: 'bytes' }, ], }, domain: domain( chainId, guardiaContractAddress, walletVersion == 1 ? 'TransferModule' : 'LoopringWallet', walletVersion == 1 ? '1.2.0' : '2.0.0', ), primaryType: 'callContract', message: { wallet: wallet, validUntil: validUntil, ...message, }, } const hash= utils._TypedDataEncoder.hash(typedData.domain, {callContract: typedData.types.callContract}, typedData.message) console.log('EIP712 hash', hash) return { typedData, hash } } function getApproveTokenCopy({ chainId, guardiaContractAddress, wallet, validUntil, message, walletVersion, }: { chainId: ChainId guardiaContractAddress: any wallet: any validUntil: any newGuardians?: Buffer | any message?: { [key: string]: any } walletVersion: 2 | 1 }) { const typedData = { types: { EIP712Domain, approveToken: [ { name: 'wallet', type: 'address' }, { name: 'validUntil', type: 'uint256' }, { name: 'token', type: 'address' }, { name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }, ], }, domain: domain( chainId, guardiaContractAddress, walletVersion == 1 ? 'TransferModule' : 'LoopringWallet', walletVersion == 1 ? '1.2.0' : '2.0.0', ), primaryType: 'approveToken', message: { wallet: wallet, validUntil: validUntil, ...message, }, } const hash= utils._TypedDataEncoder.hash(typedData.domain, {approveToken: typedData.types.approveToken}, typedData.message) console.log('EIP712 hash', hash) return { typedData, hash } } export async function signHebaoApproveWrap( props: { web3: any chainId: ChainId owner: string isHWAddr: boolean type: HEBAO_META_TYPE } & { newGuardians?: any masterCopy?: string wallet: string validUntil: number forwarderModuleAddress?: string messageData?: { [key: string]: any } guardian: any walletVersion: 1 | 2 }, ) { try { const { chainId, web3, type, owner, isHWAddr, wallet, validUntil, forwarderModuleAddress, masterCopy, messageData, guardian, walletVersion, } = props as any let messageHash let data: {typedData: any, hash: string} | undefined myLog('backend hash', guardian?.messageHash) switch (type) { case HEBAO_META_TYPE.recovery: let newOwner = messageData?.newOwner if (!newOwner) { throw 'no newOwner' } data = getApproveRecoverTypedData({ chainId, guardiaContractAddress: forwarderModuleAddress ? forwarderModuleAddress : masterCopy, wallet, // guardian.signedRequest.wallet, validUntil, // guardian.signedRequest.validUntil, message: { newOwner, ...(walletVersion == 1 ? {} : { newGuardians: messageData?.newGuardians, // : ethers.utils.solidityKeccak256( // Array(messageData?.newGuardians?.length).fill('address'), // messageData?.newGuardians, // ), // ethUtil.keccak256( // messageData?.newGuardians // .map((address: string) => address.toLowerCase().replace('0x', '')) // .join(''), // ), // ethers.utils.solidityKeccak256( // Array(messageData?.newGuardians?.length).fill('address'), // messageData?.newGuardians, // ), }), }, walletVersion, }) break case HEBAO_META_TYPE.remove_guardian: data = getRemoveGuardianTypedData({ chainId, guardiaContractAddress: forwarderModuleAddress ? forwarderModuleAddress : masterCopy, wallet, // guardian.signedRequest.wallet, validUntil, // guardian.signedRequest.validUntil, message: { guardian: messageData?.guardianAddress, }, walletVersion, }) break case HEBAO_META_TYPE.unlock_wallet: data = getUnlockWalletTypedData({ chainId, guardiaContractAddress: forwarderModuleAddress ? forwarderModuleAddress : masterCopy, wallet, // guardian.signedRequest.wallet, validUntil, // guardian.signedRequest.validUntil, // message: { // guardian: messageData.guardian, // }, walletVersion, }) break case HEBAO_META_TYPE.transfer: data = getApproveTransferTypedData({ chainId, guardiaContractAddress: forwarderModuleAddress ? forwarderModuleAddress : masterCopy, wallet, // guardian.signedRequest.wallet, validUntil, // guardian.signedRequest.validUntil, message: { token: messageData.token, to: messageData.to, amount: messageData.amount, logdata: messageData.logdata, }, walletVersion, }) break case HEBAO_META_TYPE.deposit_wallet: data = getDepositWalletTypedData({ chainId, guardiaContractAddress: forwarderModuleAddress ? forwarderModuleAddress : masterCopy, wallet, // guardian.signedRequest.wallet, validUntil, // guardian.signedRequest.validUntil, message: { to: messageData.to, value: messageData.value, data: messageData.data, }, walletVersion, }) break case HEBAO_META_TYPE.approve_token: data = getApproveTokenCopy({ chainId, guardiaContractAddress: forwarderModuleAddress ? forwarderModuleAddress : masterCopy, wallet, // guardian.signedRequest.wallet, validUntil, // guardian.signedRequest.validUntil, message: { token: messageData.token, to: messageData.to, amount: messageData.amount, // logdata: messageData.logdata, // masterCopy: messageData.newMasterCopy, }, walletVersion, }) break case HEBAO_META_TYPE.upgrade_contract: data = getApproveChangeMasterCopy({ chainId, guardiaContractAddress: forwarderModuleAddress ? forwarderModuleAddress : masterCopy, wallet, // guardian.signedRequest.wallet, validUntil, // guardian.signedRequest.validUntil, message: { masterCopy: messageData.newMasterCopy, }, walletVersion, }) break default: messageHash = guardian?.messageHash } if (data && data?.typedData) { const result = await getEcDSASig( web3, data.typedData, owner, isHWAddr ? GetEcDSASigType.WithoutDataStruct : GetEcDSASigType.HasDataStruct, chainId, undefined, '', ConnectorNames.Unknown, // counterFactualInfo ) // ecdsaSignature return { signature: result.ecdsaSig += SigSuffix.Suffix02, hash: data.hash } } else { // const messageHash = const signature: any = await personalSign( web3, owner, '', messageHash, ConnectorNames.Unknown, chainId, ) if (signature?.sig) { return { signature: signature.sig += SigSuffix.Suffix03, hash: data?.hash } } else { throw 'empty' } } // } } catch (error) { // console.log('EcDSASig error try sign WithoutDataStruct') throw error } } ================================================ FILE: src/api/config/index.ts ================================================ /* tslint:disable */ // @ts-nocheck import { toBig, toFixed } from '../../utils/formatter' import BigNumber from 'bignumber.js' function getTokenBySymbol(symbol, tokens) { if (typeof symbol === 'undefined') { return {} } return tokens.find((token) => token.symbol.toLowerCase() === symbol.toLowerCase()) || {} } function fromWEI(symbol, valueInWEI, tokens, { precision, ceil }: any = {}) { try { const token = getTokenBySymbol(symbol, tokens) const precisionToFixed = precision ? precision : token.precision const value = toBig(valueInWEI).div('1e' + token.decimals) return toFixed(value, precisionToFixed, ceil) } catch (err) { return undefined } } function toWEI(symbol, value, tokens, rm = BigNumber.ROUND_FLOOR) { const token = getTokenBySymbol(symbol, tokens) if (typeof token === 'undefined') { return 0 } return toBig(value) .times('1e' + token.decimals) .toFixed(0, rm) } export { fromWEI, toWEI } export * from './guardianTypeData' ================================================ FILE: src/api/contacts_api.ts ================================================ /* eslint-disable camelcase */ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' export class ContactAPI extends BaseAPI { public async getContacts( request: loopring_defs.GetContactsRequest, apiKey: string, // url: string = loopring_defs.LOOPRING_URLs.GET_CONTACTS ) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_CONTACTS, queryParams: request, //request method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, apiKey, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } as { raw_data: R } & R } public async createContact(request: loopring_defs.CreateContactRequest, apiKey: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.CREATE_CONTACT, bodyParams: request, //request method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, apiKey, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } public async updateContact(request: loopring_defs.UpdateContactRequest, apiKey: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.UPDATE_CONTACT, bodyParams: request, //request method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, apiKey, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } public async deleteContact( request: loopring_defs.DeleteContactRequest, apiKey: string, // url: string = loopring_defs.LOOPRING_URLs.GET_CONTACTS ) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.DELETE_CONTACT, bodyParams: request, //request method: loopring_defs.ReqMethod.DELETE, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, apiKey, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } } // ContactAPI.get ================================================ FILE: src/api/contract_api.ts ================================================ import Web3 from 'web3' import { Transaction } from '@ethereumjs/tx' import * as loopring_defs from '../defs' import * as fm from '../utils/formatter' import { addHexPrefix, toHex, toNumber } from '../utils/formatter' import { Contracts } from './ethereum/contracts' export enum ERC20Method { Approve = 'approve', Deposit = 'deposit', ForceWithdraw = 'forceWithdraw', } export const ApproveVal = { Zero: '0x0', Max: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', } function checkWeb3(web3: any) { if (!web3) throw new Error('got undefined web3') } /** * @description sign hash * @param web3 * @param account * @param hash * @returns {Promise.<*>} */ export async function sign(web3: any, account: string, pwd: string, hash: string) { checkWeb3(web3) return new Promise((resolve) => { web3.eth.sign(hash, account, pwd, function (err: any, result: any) { if (!err) { const r = result.slice(0, 66) const s = addHexPrefix(result.slice(66, 130)) let v = toNumber(addHexPrefix(result.slice(130, 132))) if (v === 0 || v === 1) v = v + 27 // 修复ledger的签名 resolve({ result: { r, s, v } }) } else { const errorMsg = err.message.substring(0, err.message.indexOf(' at ')) resolve({ error: { message: errorMsg } }) } }) }) } /** * @description Signs ethereum tx * @param web3 * @param account * @param rawTx * @returns {Promise.<*>} */ export async function signEthereumTx( web3: any, account: string, rawTx: any, chainId: loopring_defs.ChainId, ): Promise<{ result: string; rawTx: object } | { error: any }> { const ethTx = Transaction.fromSerializedTx(rawTx) const hash = toHex(ethTx.hash()) try { const response: any = await sign(web3, account, '', hash) if (!response.error) { const signature = response['result'] signature.v += chainId * 2 + 8 const jsonTx = Object.assign(ethTx.toJSON(), signature) jsonTx.from = rawTx.from return { result: fm.toHex(JSON.stringify(jsonTx)), rawTx: jsonTx } } else { return { error: response.error } // throw new Error(response["error"]["message"]); } } catch (err) { return { error: err } } } export async function getNonce(web3: Web3, addr: string) { if (web3) return await web3.eth.getTransactionCount(addr) return -1 } export async function sendRawTx( web3: any, from: string, to: string, value: any, data: any, chainId: loopring_defs.ChainId, nonce: string | number | undefined | null, gasPrice: any, gasLimit: string | number | undefined, sendByMetaMask = true, ) { checkWeb3(web3) gasPrice = fm.fromGWEI(gasPrice).toNumber() const rawTx = { from, to, value, data, chainId, nonce, gasPrice, gasLimit, } if (sendByMetaMask) { return await sendTransaction(web3, rawTx) } const res: any = await signEthereumTx(web3, from, rawTx, chainId) if (res?.rawTx) { return await sendTransaction(web3, res.rawTx) } else { } return res } function _genContractData(Contract: any, method: string, data: any) { return Contract.encodeInputs(method, data) } function genERC20Data(method: string, data: any) { return _genContractData(Contracts.ERC20Token, method, data) } export function genExchangeData(method: string, data: any) { return _genContractData(Contracts.ExchangeContract, method, data) } export async function approve( web3: Web3, from: string, to: string, depositAddress: string, _value: string, chainId: loopring_defs.ChainId, nonce: string | number, gasPrice: string | number, gasLimit: string | number, sendByMetaMask: boolean, ) { const data = genERC20Data(ERC20Method.Approve, { _spender: depositAddress, _value, }) return await sendRawTx( web3, from, to, '0', data, chainId, nonce, gasPrice, gasLimit, sendByMetaMask, ) } // 3.6 /** * Approve Zero * @param tokenAddress: approve token symbol to zero * @param nonce: Ethereum nonce of this address * @param gasPrice: gas price in gwei * @param sendByMetaMask */ export async function approveZero( web3: any, owner: string, tokenAddress: string, depositAddress: string, gasPrice: number, gasLimit: number, chainId: loopring_defs.ChainId = loopring_defs.ChainId.GOERLI, nonce: number, sendByMetaMask = false, ) { return await approve( web3, owner, tokenAddress, depositAddress, ApproveVal.Zero, chainId, nonce, gasPrice, gasLimit, sendByMetaMask, ) } // 3.6 /** * Approve Max * @param tokenAddress: approve token symbol to max * @param nonce: Ethereum nonce of this address * @param gasPrice: gas price in gwei * @param sendByMetaMask */ export async function approveMax( web3: any, owner: string, tokenAddress: string, depositAddress: string, gasPrice: string, gasLimit: string, chainId: loopring_defs.ChainId = loopring_defs.ChainId.GOERLI, nonce: string, sendByMetaMask = false, ) { return await approve( web3, owner, tokenAddress, depositAddress, ApproveVal.Max, chainId, nonce, gasPrice, gasLimit, sendByMetaMask, ) } // 3.6 /** * deposit */ export async function deposit( web3: any, from: string, exchangeAddress: string, token: loopring_defs.TokenInfo, value: number, fee: number, gasPrice: string, gasLimit: string, chainId: loopring_defs.ChainId = loopring_defs.ChainId.GOERLI, nonce: string, sendByMetaMask = true, to?: string, ) { let valueC = fm.toBig(value).times('1e' + token.decimals) const amount = fm.toHex(valueC) const data = genExchangeData(ERC20Method.Deposit, { tokenAddress: token.address, amount, from, to: to ? to : from, extraData: '', }) if (token.type === 'ETH') { valueC = valueC.plus(fee) } else { valueC = fm.toBig(fee) } return await sendRawTx( web3, from, exchangeAddress, valueC.toFixed(), data, chainId, nonce, gasPrice, gasLimit, sendByMetaMask, ) } /** * forceWithdrawal */ export async function forceWithdrawal( web3: any, from: string, accountID: number, exchangeAddress: string, token: loopring_defs.TokenInfo, fee: number, gasPrice: number, gasLimit: number, chainId: loopring_defs.ChainId = loopring_defs.ChainId.GOERLI, nonce: number, sendByMetaMask = false, ) { const valueC = fm.toBig(fee) const data = genExchangeData(ERC20Method.ForceWithdraw, { owner: from, tokenAddress: token.address, accountID, }) return await sendRawTx( web3, from, exchangeAddress, valueC.toFixed(), data, chainId, nonce, gasPrice, gasLimit, sendByMetaMask, ) } /** * @description Sends ethereum tx through MetaMask * @param web3 * @param tx * @returns {*} */ export async function sendTransaction(web3: any, tx: any) { delete tx.gasPrice // delete tx.gas; const response: any = await new Promise((resolve) => { web3.eth.sendTransaction(tx, function (err: any, transactionHash: string) { if (!err) { resolve({ result: transactionHash }) } else { resolve({ error: { message: err.message } }) } }) }) if (response['result']) { return response } else { throw new Error(response['error']['message']) } } export async function isContract(web3: any, address: string) { const code = await web3.eth.getCode(address) return code && code.length > 2 } ================================================ FILE: src/api/defi_api.ts ================================================ /* eslint-disable camelcase */ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' import { makeInvestMarkets, makeMarkets, sortObjDictionary } from '../utils' import * as sign_tools from './sign/sign_tools' import { AxiosResponse } from 'axios' import { getMidPrice } from './exchange_api' import { getEdDSASigWithPoseidon } from './sign/sign_tools' import * as string_decoder from 'string_decoder' export class DefiAPI extends BaseAPI { /* * Returns the fee rate of users placing orders in specific markets */ public async getDefiToken(): Promise<{ raw_data: R tokensMap: loopring_defs.LoopringMap idIndex: loopring_defs.LoopringMap addressIndex: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DEFI_TOKENS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const tokensMap: loopring_defs.LoopringMap = {} const addressIndex: loopring_defs.LoopringMap = {} const idIndex: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item: loopring_defs.TokenInfo) => { if (item.symbol.startsWith('LP-')) { item.isLpToken = true } else { item.isLpToken = false } tokensMap[item.symbol] = item const coinInfo = { icon: loopring_defs.SoursURL + `ethereum/assets/${item.address}/logo.png`, name: item.symbol, simpleName: item.symbol, description: item.type, company: '', } // totalCoinMap[item.symbol] = coinInfo; addressIndex[item.address.toLowerCase()] = item.symbol idIndex[item.tokenId] = item.symbol }) } return { tokensMap, idIndex, addressIndex, raw_data, } } public async getDefiMarkets( request: loopring_defs.GetDefiMarketRequest, url: string = loopring_defs.LOOPRING_URLs.GET_DEFI_MARKETS, ): Promise<{ markets: loopring_defs.LoopringMap pairs: loopring_defs.LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url, queryParams: {}, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const types = request?.defiType?.toString()?.split(',') return { ...makeInvestMarkets(raw_data, types), raw_data, } } public async orderDefi( request: loopring_defs.DefiOrderRequest, privateKey: string, apiKey: string, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const dataToSig = [ request.exchange, request.storageId, request.accountId, request.sellToken.tokenId, request.buyToken.tokenId, request.sellToken.volume, request.buyToken.volume, request.validUntil, request.maxFeeBips, request.fillAmountBOrS ? 1 : 0, 0, ] const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_DEFI_ORDER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG_POSEIDON, sigObj: { dataToSig, sigPatch: loopring_defs.SigPatchField.EddsaSignature, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } public async getDefiReward( request: loopring_defs.GetUserDefiRewardRequest, apiKey: string, ): Promise< | { raw_data: R totalNum: number totalRewards: string lastDayRewards: string rewards: [] } | loopring_defs.RESULT_INFO > { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DEFI_REWARDS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, raw_data, } } public async getDefiTransaction( request: loopring_defs.GetUserDefiTxRequest, apiKey: string, ): Promise< | { raw_data: R totalNum: number userDefiTxs: loopring_defs.UserDefiTxsHistory[] } | loopring_defs.RESULT_INFO > { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DEFI_TRANSACTIONS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userDefiTxs: raw_data.transactions as loopring_defs.UserDefiTxsHistory[], raw_data, } } public async getDualInfos(request: loopring_defs.GetDualInfosRequest): Promise< | loopring_defs.RESULT_INFO | { totalNum: number dualInfo: { infos: loopring_defs.DualProductAndPrice[] index: loopring_defs.DualIndex balance: loopring_defs.DualBalance[] rules: loopring_defs.DualRulesCoinsInfo[] } raw_data: R } > { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DUAL_INFOS, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, dualInfo: { infos: raw_data.infos as loopring_defs.DualProductAndPrice[], index: raw_data.index as loopring_defs.DualIndex, balance: raw_data.balance as loopring_defs.DualBalance[], rules: raw_data.rules as loopring_defs.DualRulesCoinsInfo[], }, raw_data, } } public async getDualBalance(request = undefined) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DUAL_BALANCE, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, dualBalanceMap: [...raw_data].reduce((prev, item) => { return { ...prev, [item.coin]: item } }, {} as loopring_defs.LoopringMap), } } public async getDualPrices(request: loopring_defs.GetDualPricesRequest) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DUAL_PRICES, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, totalNum: raw_data?.totalNum, infos: raw_data.infos as loopring_defs.DualPrice[], } } public async getDualIndex(request: { baseSymbol: string; quoteSymbol: string }) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DUAL_INDEX, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, dualPrice: raw_data as loopring_defs.DualPrice[], } } public async getDualTransactions(request: loopring_defs.GetUserDualTxRequest, apiKey: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DUAL_TRANSACTIONS, queryParams: { ...request, ...(request.retryStatuses ? { retryStatuses: request.retryStatuses.join(',') } : {}), }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, indexes: raw_data?.indexes, userDualTxs: raw_data.transactions as loopring_defs.UserDualTxsHistory[], raw_data, } return } public async orderDual( request: loopring_defs.DualOrderRequest, privateKey: string, apiKey: string, ) { const dataToSig = [ request.exchange, request.storageId, request.accountId, request.sellToken.tokenId, request.buyToken.tokenId, request.sellToken.volume, request.buyToken.volume, request.validUntil, request.maxFeeBips, request.fillAmountBOrS ? 1 : 0, 0, ] const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_DUAL_ORDER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG_POSEIDON, sigObj: { dataToSig, sigPatch: loopring_defs.SigPatchField.EddsaSignature, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } public async editDual( request: loopring_defs.DualEditRequest, privateKey: string, apiKey: string, ) { const { newOrder } = request let bodyParams = { ...request } if (newOrder) { const dataToSig = [ newOrder.exchange, newOrder.storageId, newOrder.accountId, newOrder.sellToken.tokenId, newOrder.buyToken.tokenId, newOrder.sellToken.volume, newOrder.buyToken.volume, newOrder.validUntil, newOrder.maxFeeBips, newOrder.fillAmountBOrS ? 1 : 0, 0, ] const eddsaSignature = getEdDSASigWithPoseidon(dataToSig, privateKey).result bodyParams = { ...bodyParams, newOrder: { ...newOrder, eddsaSignature } } } const _dataToSig: Map = sortObjDictionary(bodyParams) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_DUAL_EDIT, bodyParams, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig: _dataToSig, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } public async getDualUserLocked( { lockTag = [loopring_defs.DUAL_TYPE.DUAL_BASE, loopring_defs.DUAL_TYPE.DUAL_CURRENCY], ...request }: loopring_defs.DualUserLockedRequest, apiKey: string, ) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DUAL_USER_LOCKED, queryParams: { ...request, lockTag: lockTag.join(',') }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { lockRecord: raw_data.lockRecord, raw_data, } return } public async sendStakeClaim( req: loopring_defs.OriginStakeClaimRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ) { const { request, web3, chainId, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const { transfer } = request const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined transfer.payeeId = 0 transfer.memo = `STAKE-CLAIM->${request.accountId}` try { ecdsaSignature = await sign_tools.transferWrap({ transfer: transfer as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } if (counterFactualInfo) { transfer.counterFactualInfo = counterFactualInfo } transfer.eddsaSignature = sign_tools.get_EddsaSig_Transfer( transfer as loopring_defs.OriginTransferRequestV3, eddsaKey, ).result transfer.ecdsaSignature = ecdsaSignature const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_STAKE_CLAIM, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } catch (error) { throw error as AxiosResponse } // return this.returnTxHash(raw_data); // const raw_data = (await this.makeReq().request(reqParams)).data; } public async sendStakeRedeem( request: { accountId: number hash: string token: loopring_defs.TokenVolumeV3 }, privateKey: string, apiKey: string, ) { const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_STAKE_REDEEM, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async sendStake( request: { accountId: number token: loopring_defs.TokenVolumeV3 timestamp: number }, privateKey: string, apiKey: string, ) { const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_STAKE, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async getStakeProducts(): Promise<{ products: loopring_defs.STACKING_PRODUCT[] raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_STAKE_PRODUCTS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { products: raw_data, raw_data } } public async getStakeSummary( request: { accountId: number tokenId: number start?: number end?: number limit?: number offset?: number hashes?: string statuses?: string }, apiKey: string, ): Promise< | { raw_data: R totalNum: number totalStaked: string totalStakedRewards: string totalLastDayPendingRewards: string totalClaimableRewards: string list: loopring_defs.StakeInfoOrigin[] } | loopring_defs.RESULT_INFO > { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_STAKE_SUMMARY, queryParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, list: raw_data.staking, raw_data } } public async getStakeTransactions( request: { accountId: number tokenId: number start?: number end?: number limit?: number offset?: number hashes?: string types?: string }, apiKey: string, ): Promise<{ list: loopring_defs.STACKING_TRANSACTIONS[] totalNum: number raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_STAKE_TRANSACTIONS, queryParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { list: raw_data.transactions, totalNum: raw_data.totalNum, raw_data, } } public async getBtradeMarkets(): Promise<{ markets: loopring_defs.LoopringMap pairs: loopring_defs.LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string raw_data: R }> { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_BTRATE_MARKETS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } let result: any = {} const pairs: loopring_defs.LoopringMap = {} // const isMix = url === loopring_defs.LOOPRING_URLs.GET_MIX_MARKETS; if (raw_data instanceof Array) { const reformat = raw_data.reduce((prev, ele: loopring_defs.BTRADE_MARKET) => { if (/-/gi.test(ele.market)) { return [ ...prev, { ...ele, btradeMarket: ele.market, market: ele.market.replace(loopring_defs.BTRADENAME, ''), // enabled: true, } as loopring_defs.BTRADE_MARKET, ] } else { return prev } }, [] as loopring_defs.BTRADE_MARKET[]) result = makeMarkets({ markets: reformat }) } return { markets: result.markets, pairs: result.pairs, tokenArr: result.tokenArr, tokenArrStr: result.tokenArrStr, marketArr: result.marketArr, marketArrStr: result.marketArrStr, raw_data, } } public async getBtradeDepth({ request, tokenMap, }: { request: { market: string level: number limit?: number } tokenMap?: any }): Promise<{ depth: loopring_defs.DepthData raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_BTRATE_DEPTH, queryParams: { ...request }, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const symbol = raw_data.market const timestamp = raw_data.timestamp const { asks, bids, mid_price } = getMidPrice({ _asks: raw_data.asks, _bids: raw_data.bids, }) const depth: loopring_defs.DepthData = { symbol, // @ts-ignore market: raw_data.market, version: raw_data.version, timestamp, mid_price, bids: bids.ab_arr, bids_prices: bids.ab_prices, bids_amtTotals: bids.ab_amtTotals, bids_volTotals: bids.ab_volTotals, bids_amtTotal: bids.amtTotal.toString(), bids_volTotal: bids.volTotal.toString(), asks: asks.ab_arr, asks_prices: asks.ab_prices, asks_amtTotals: asks.ab_amtTotals, asks_volTotals: asks.ab_volTotals, asks_amtTotal: asks.amtTotal.toString(), asks_volTotal: asks.volTotal.toString(), } return { depth, raw_data: raw_data as unknown as R, } } public async getBtradeOrders({ request, apiKey, }: { request: loopring_defs.GetOrdersRequest apiKey: string }) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_BTRATE_ORDERS, queryParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { // baseSettled quoteSettled list: raw_data.transactions, totalNum: raw_data.totalNum, raw_data, } } public async sendBtradeOrder({ request, privateKey, apiKey, }: { request: loopring_defs.OriginBTRADEV3OrderRequest privateKey: string apiKey: string }) { // const dataToSig: Map = sortObjDictionary(request); const dataToSig = [ request.exchange, request.storageId, request.accountId, request.sellToken.tokenId, request.buyToken.tokenId, request.sellToken.volume, request.buyToken.volume, request.validUntil, request.maxFeeBips, request.fillAmountBOrS ? 1 : 0, 0, ] const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_BTRATE_ORDER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG_POSEIDON, sigObj: { dataToSig, sigPatch: loopring_defs.SigPatchField.EddsaSignature, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async getDefiApys< R = { product: string defiType: string apys: { apy: string createdAt: string }[] }, >({ request, }: { request: { start?: number end?: number defiType: 'LIDO' | 'ROCKETPOOL' | 'L2STAKING' | 'CIAN' | string product: string } }): Promise<{ raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DEFI_APYS, queryParams: { ...request }, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, raw_data, } } public async getDefiDepositList( { types, markets, number = 10, ...request }: { accountId: number number: number // markets: Array | string types: Array<'LIDO' | 'ROCKETPOOL' | 'L2STAKING' | 'CIAN' | string> | string product: string }, apiKey: string, ): Promise<{ raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DEFI_STAKE_TRANSACTIONS, apiKey, queryParams: { ...request, number, markets: typeof markets === 'string' ? markets : markets?.join(','), types: typeof types === 'string' ? types : types?.join(','), }, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, raw_data, } } public async getTaikoFarmingPositionInfo({ accountId, }: { accountId: number }): Promise<{ data: { tokenId: number symbol: string address: string decimals: number status: number apr: string precision: number stakedTotal: string claimedTotal: string holdClaimedTotal: string claimableTotal: string totalPoints: string minAmount: string maxAmount: string }[] account: { status: number // 0 => init, 1 => opened, 2 => minting, 3 => redeeming } }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TAIKO_FARMING_POSITION_INFO, queryParams: { accountId, }, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data.resultInfo.code !== 0) { throw raw_data.resultInfo } else { return raw_data } } public async getTaikoFarmingTransactions( queryParams: { accountId: number tokenId: number start?: number end?: number limit?: number offset?: number hashes?: string types?: string }, apiKey: string, ): Promise<{ totalNum: number transactions: { accountId: number tokenId: number amount: string productId: string hash: string stakingType: 'subscribe' | 'unsubscribe' createdAt: number updatedAt: number }[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TAIKO_FARMING_TRANSACTIONS, apiKey, queryParams: queryParams, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data return raw_data } public async getTaikoFarmingUserSummary(queryParams: { accountId: number tokenId: number start?: number end?: number limit?: number offset?: number hashes?: string statuses?: string }): Promise<{ totalNum: number totalStaked: string totalStakedRewards: string totalLastDayPendingRewards: string totalClaimableRewards: string staking: { 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 reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TAIKO_FARMING_USER_SUMMARY, queryParams: queryParams, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data return raw_data } public async getTaikoFarmingAvailableNft( queryParams: { accountId: number }, apiKey: string, ): Promise { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TAIKO_FARMING_AVAILABLE_NFT, apiKey, queryParams: queryParams, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data return raw_data } public async getTaikoFarmingTransactionByHash( queryParams: { accountId: number hash: string }, apiKey: string, ) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TAIKO_FARMING_TRANSACTION_BY_HASH, apiKey, queryParams: queryParams, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data return raw_data as { operation: { accountId: number tokenId: number amount: string productId: string hash: string stakingType: string status: number createdAt: number updatedAt: number } } } public async getTaikoFarmingDepositDurationList(queryParams: { accountId: number tokenId: number start?: number end?: number limit?: number offset?: number hashes?: string statuses?: string }) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TAIKO_FARMING_DEPOSIT_DURATION_LIST, queryParams: queryParams, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data return raw_data as { totalNum: number data: { accountId: number tokenId: number stakeAt: number txHash: string eventIndex: number lockDuration: number hash: string status: string createdAt: number updatedAt: number }[] } } public async getTaikoFarmingGetRedeem( queryParams: { accountId: number tokenId: number }, apiKey: string, ) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TAIKO_FARMING_GET_REDEEM, queryParams: queryParams, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, apiKey, } const raw_data = (await this.makeReq().request(reqParams)).data return raw_data as { redeemAmount: string profit: string profitOfU: string resultInfo?: loopring_defs.RESULT_INFO } } public async submitTaikoFarmingClaim({ request, eddsaKey, apiKey, }: loopring_defs.TaikoFarmingSubmitOrderNFTRequestV3WithPatch) { const takerOrderEddsaSignature = sign_tools.get_EddsaSig_NFT_Order(request, eddsaKey).result const _request = { ...request, eddsaSignature: takerOrderEddsaSignature, } const dataToSig: Map = sortObjDictionary(_request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_TAIKO_FARMING_SUMBIT_CLAIM, bodyParams: _request, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, apiKey, } try { const raw_data = (await this.makeReq().request(reqParams)).data return raw_data as { hash: string status: string isIdempotent: boolean accountId: number tokens: number[] storageId: number } } catch (error) { throw error as AxiosResponse } } } ================================================ FILE: src/api/delegate_api.ts ================================================ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' export class DelegateAPI extends BaseAPI { public async getCode(address: string): Promise { const reqParams: loopring_defs.ReqParams = { sigFlag: loopring_defs.SIG_FLAG.NO_SIG, url: loopring_defs.LOOPRING_URLs.GET_DELEGATE_GET_CODE, method: loopring_defs.ReqMethod.POST, bodyParams: { address }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return raw_data } public async getIPFS(path: string): Promise { const reqParams: loopring_defs.ReqParams = { sigFlag: loopring_defs.SIG_FLAG.NO_SIG, url: loopring_defs.LOOPRING_URLs.GET_DELEGATE_GET_IPFS, method: loopring_defs.ReqMethod.GET, queryParams: { path }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return raw_data } public getCollectionDomain() { return this.chainId === loopring_defs.ChainId.GOERLI ? 'https://uatnftinfos.loopring.io' : 'https://nftinfos.loopring.io' } } ================================================ FILE: src/api/ethereum/contracts/AbiFunction.ts ================================================ /* tslint:disable */ // @ts-nocheck import { addHexPrefix, clearHexPrefix, toBuffer, toHex } from '../../../utils/formatter' import { methodID, rawDecode, rawEncode } from 'ethereumjs-abi' import BN from 'bn.js' export class AbiFunction { name inputTypes inputs outputTypes outputs constant methodAbiHash constructor({ inputs, name, outputs, constant }: any) { this.name = name this.inputTypes = inputs.map(({ type }) => type) this.inputs = inputs this.outputTypes = outputs.map(({ type }) => type) this.outputs = outputs this.constant = constant this.methodAbiHash = toHex(methodID(name, this.inputTypes)) } /** * @description Returns encoded methodId and inputs * @param inputs Object, examples {owner:'0x000...} * @returns {string} */ encodeInputs(inputs) { const abiInputs = this.parseInputs(inputs) return this.methodAbiHash + clearHexPrefix(toHex(rawEncode(this.inputTypes, abiInputs))) } /** * @description decode ethereum jsonrpc response result * @param outputs * @returns {*} */ decodeOutputs(outputs) { return this.parseOutputs(rawDecode(this.outputTypes, toBuffer(outputs))) } /** * @description decode encoded inputs * @param encoded * @returns {*} */ decodeEncodedInputs(encoded) { return this.parseOutputs(rawDecode(this.inputTypes, toBuffer(addHexPrefix(encoded)))) } parseInputs(inputs = {}) { return this.inputs.map(({ name, type }) => { if (inputs[name] === undefined) { throw new Error(`Parameter ${name} of type ${type} is required!`) } return inputs[name] }) } parseOutputs(outputs) { return outputs.map((output) => { if (output instanceof BN) { return toHex(output) } return output }) } } ================================================ FILE: src/api/ethereum/contracts/Contract.ts ================================================ /* tslint:disable */ // @ts-nocheck import { methodID } from 'ethereumjs-abi' import { toHex } from '../../../utils/formatter' import { AbiFunction } from './AbiFunction' export class Contract { abiFunctions constructor(abi) { const funAbi = abi.filter(({ type }) => type === 'function') this.abiFunctions = funAbi.reduce((acc, item) => { const inputTypes = item.inputs.map(({ type }) => type) const key = `${item.name}(${inputTypes.toString()})` const methodHash = methodID(item.name, inputTypes) return { ...acc, [item.name]: new AbiFunction(item), [key]: new AbiFunction(item), //@ts-ignore [methodHash]: new AbiFunction(item), } }, {}) } /** * @description Encodes inputs data according to ethereum abi * @param method string can be full method or just method name, examples: 'balanceOf' or balanceOf(address) * @param inputs array * @returns {*|string} */ encodeInputs(method, inputs) { const abiFunction = this.abiFunctions[method] if (abiFunction) { return abiFunction.encodeInputs(inputs) } else { throw new Error(`No ${method} method according to abi `) } } /** * @description Decodes outputs * @param method string can be full method or just method name, examples: 'balanceOf' or balanceOf(address) * @param outputs string * @returns {*} */ decodeOutputs(method, outputs) { const abiFunction = this.abiFunctions[method] if (abiFunction) { return abiFunction.decodeOutputs(outputs) } else { throw new Error(`No ${method} method according to abi `) } } /** * @description Decode encoded method and inputs * @param encode string | Buffer * @returns {*} */ decodeEncodeInputs(encode) { encode = toHex(encode) const methodId = encode.slice(0, 10) const abiFunction = this.abiFunctions[methodId] if (abiFunction) { return abiFunction.decodeEncodedInputs(encode.slice(10)) } else { throw new Error(`No corresponding method according to abi `) } } } ================================================ FILE: src/api/ethereum/contracts/Contracts.ts ================================================ /* eslint-disable */ import { contractWalletAbi, erc20Abi, erc721Abi, erc1155Abi, exchange36Abi, hebao, } from '../../config/abis' import { Contract } from './Contract' const ERC20Token = new Contract(erc20Abi.erc20) const ExchangeContract = new Contract(exchange36Abi.exchange) const ContractWallet = new Contract(contractWalletAbi.contractWallet) const ERC1155 = new Contract(erc1155Abi.erc1155) const ERC721 = new Contract(erc721Abi.erc721) const HeBao = new Contract(hebao.hebao) export { ERC20Token, ERC1155, ERC721, ExchangeContract, ContractWallet, erc721Abi, erc1155Abi, HeBao, exchange36Abi } ================================================ FILE: src/api/ethereum/contracts/index.ts ================================================ import { AbiFunction } from './AbiFunction' import { Contract } from './Contract' import * as Contracts from './Contracts' const contracts = { Contracts } export { AbiFunction, Contract, contracts, Contracts } ================================================ FILE: src/api/exchange_api.ts ================================================ /* eslint-disable camelcase */ import { BaseAPI } from './base_api' import { LOOPRING_URLs, SIG_FLAG, ReqMethod, Side, VipCatergory, TradingInterval, ReqParams, MarketTradeInfo, FiatPriceInfo, LoopringMap, GetAccountRequest, GetCandlestickRequest, GetDepthRequest, GetTickerRequest, GetMarketTradesRequest, GetFiatPriceRequest, GetTokenBalancesRequest, GetAllowancesRequest, MarketInfo, ExchangeInfo, TickerData, DepthData, Candlestick, TokenRelatedInfo, ABInfo, GetEthBalancesRequest, GetEthNonceRequest, GetWithdrawalAgentsRequest, GetAccountServicesRequest, VipFeeRateInfoMap, AccountInfo, TokenAddress, SEP, GetALLTokenBalancesRequest, TOKENMAPLIST, DatacenterTokenInfo, GetDatacenterTokenInfoRequest, GetDatacenterTokenQuoteTrend, GetDatacenterTokenQuoteTrendRequest, DatacenterTokenInfoSimple, GetDatacenterTokenOhlcvQuoteTrendRequest, GetCmcTokenRelationsRequest, getLatestTokenPricesRequest, GetUserTradeAmount, } from '../defs' import BigNumber from 'bignumber.js' import { getBaseQuote, makeMarket, makeMarkets } from '../utils' const checkAmt = (rawStr: string) => { if (rawStr.trim() === '') { return '0' } return rawStr } function getFeeMap(feeArr: any[], type = 0) { const feesMap: any = {} if (feeArr instanceof Array) { feeArr.forEach((item: any, index: number, array: any) => { let key = '' switch (type) { case 1: key = item.type break default: key = item.token } // feesMap[key] = new BigNumber(item.fee) feesMap[key] = item.fee }) } return feesMap } function genAB(data: any[], isReverse = false) { const ab_arr: ABInfo[] = [] let amtTotal: BigNumber = new BigNumber(0) let volTotal: BigNumber = new BigNumber(0) const ab_prices: number[] = [] const ab_amtTotals: string[] = [] const ab_volTotals: string[] = [] const best = 0 if (data instanceof Array) { data.forEach((item: any) => { const price = parseFloat(item[0]) const amt = new BigNumber(item[1]) // base amt const vol = new BigNumber(item[2]) // quote vol amtTotal = amtTotal.plus(amt) volTotal = volTotal.plus(vol) ab_arr.push({ price: price, amt: amt.toString(), vol: vol.toString(), amtTotal: amtTotal.toString(), volTotal: volTotal.toString(), }) ab_prices.push(price) ab_amtTotals.push(amtTotal.toString()) ab_volTotals.push(volTotal.toString()) }) } if (isReverse) { ab_arr.reverse() ab_prices.reverse() ab_amtTotals.reverse() ab_volTotals.reverse() } return { ab_arr, ab_prices, amtTotal, volTotal, ab_amtTotals, ab_volTotals, best, } } export function getMidPrice({ _asks, askReverse, _bids, bidReverse, }: { _asks: any askReverse?: boolean _bids: any bidReverse?: boolean }) { if (askReverse === undefined) { askReverse = false } if (bidReverse === undefined) { bidReverse = true } const bids = genAB(_bids, bidReverse) const asks = genAB(_asks, askReverse) const mid_price = (bids.ab_prices[bids.ab_prices.length - 1] + asks.ab_prices[0]) / 2 return { bids, asks, mid_price, } } export function getBtradeMidPrice({ _asks, _bids }: { _asks: any[]; _bids: any[] }) { const bids = genAB(_bids) const asks = genAB(_asks) const mid_price = (bids.ab_prices[bids.ab_prices.length - 1] + asks.ab_prices[0]) / 2 return { bids, asks, mid_price, } } export class ExchangeAPI extends BaseAPI { /* * Returns the relayer's current time in millisecond */ public async getRelayerCurrentTime(): Promise< { raw_data: R } & R > { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_RELAYER_CURRENT_TIME, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, raw_data, } } /* * Returns Protocol Portrait */ public async getProtocolPortrait(): Promise< { raw_data: R } & R > { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_PROTOCOL_PORTRAIT, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, raw_data, } } /* * Returns exchange fee info */ public async getExchangeFeeInfo(): Promise<{ raw_data: R orderbookTradingFeesStablecoin: VipFeeRateInfoMap orderbookTradingFees: VipFeeRateInfoMap ammTradingFees: VipFeeRateInfoMap otherFees: { [key: string]: string } }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_EXCHANGE_FEEINFO, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { orderbookTradingFeesStablecoin: raw_data[ VipCatergory.ORDERBOOK_TRADING_FEES_STABLECOIN ] as VipFeeRateInfoMap, orderbookTradingFees: raw_data[VipCatergory.ORDERBOOK_TRADING_FEES] as VipFeeRateInfoMap, ammTradingFees: raw_data[VipCatergory.AMM_TRADING_FEES] as VipFeeRateInfoMap, otherFees: raw_data[VipCatergory.OTHER_FEES] as { [key: string]: string }, raw_data, } } public async getWithdrawalAgents(request: GetWithdrawalAgentsRequest): Promise<{ raw_data: R supportTokenMap: { [key: string]: any } }> { const reqParams: ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_WITHDRAWAL_AGENTS, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const supportTokenMap: { [key: string]: any } = {} if (raw_data && raw_data.length > 0) { raw_data.forEach((item: any) => { if (item.symbol) { supportTokenMap[item.symbol] = item } }) } return { supportTokenMap, raw_data, } } public async getRecommendedMarkets(): Promise<{ raw_data: R recommended: string[] }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_RECOMENDED_MARKETS, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } let recommended = [] if (raw_data?.recommended) { if (typeof raw_data.recommended === 'string') { recommended = raw_data.recommended.split(',') } else { recommended = raw_data.recommended } } return { recommended, raw_data, } } /* * Returns the configurations of all supported markets (trading pairs) */ public async getMarkets(url: string = LOOPRING_URLs.GET_MARKETS): Promise<{ markets: LoopringMap pairs: LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string raw_data: R }> { const reqParams: ReqParams = { url, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const { markets, pairs, tokenArr, tokenArrStr, marketArr, marketArrStr } = makeMarkets( raw_data, url, ) return { markets, pairs, tokenArr, tokenArrStr, // tokenArrStr: tokenArr.join(SEP), marketArr, marketArrStr, // marketArrStr: marketArr.join(SEP), raw_data, } } /* * Returns the configurations of all supported markets (trading pairs) */ public async getMixMarkets(): Promise<{ markets: LoopringMap pairs: LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string raw_data: R }> { return await this.getMarkets(LOOPRING_URLs.GET_MIX_MARKETS) } /* * Returns the configurations of all supported tokens, including Ether. */ public async getTokens(): Promise< TOKENMAPLIST & { raw_data: R } > { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_TOKENS, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } // raw_data // const coinMap: LoopringMap<{ // icon?: string; // name: string; makeMarket // simpleName: string; // description?: string; // company: string; // }> = {}; // const totalCoinMap: LoopringMap<{ // icon?: string; // name: string; // simpleName: string; // description?: string; // company: string; // }> = {}; // const addressIndex: LoopringMap = {}; // const idIndex: LoopringMap = {}; // const tokensMap: LoopringMap = {}; // if (raw_data instanceof Array) { // raw_data.forEach((item: TokenInfo) => { // if (item.symbol.startsWith("LP-")) { // item.isLpToken = true; // } else { // item.isLpToken = false; // } // tokensMap[item.symbol] = item; // // const coinInfo = { // icon: SoursURL + `ethereum/assets/${item.address}/logo.png`, // name: item.symbol, // simpleName: item.symbol, // description: item.type, // company: "", // }; // if (!item.symbol.startsWith("LP-")) { // coinMap[item.symbol] = coinInfo; // } // totalCoinMap[item.symbol] = coinInfo; // addressIndex[item.address.toLowerCase()] = item.symbol; // idIndex[item.tokenId] = item.symbol; // }); // } // raw_data: R; return { ...makeMarket(raw_data), // tokensMap, // coinMap, // totalCoinMap, // idIndex, // addressIndex, raw_data, } } // private splitTokens(token: string, tokens: LoopringMap) { // let tokenArray: any = []; // const tokenAddrArr: string[] = []; // // if (tokens) { // if (token) { // tokenArray = token.split(SEP); // } // // if ( // tokenArray.length <= 0 || // (tokenArray.length === 1 && tokenArray[0] === "") // ) { // tokenArray = Reflect.ownKeys(tokens); // } // // tokenArray.forEach((item: string) => { // tokenAddrArr.push(tokens[item].address); // }); // // token = tokenAddrArr.join(SEP); // } // // return { // tokenArray, // token, // }; // } /* * Returns the balances of all supported tokens, including Ether. */ public async getEthBalances(request: GetEthBalancesRequest): Promise<{ raw_data: R ethBalance: string }> { const reqParams: ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_ETH_BALANCES, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const ethBalance = raw_data.amount return { ethBalance, raw_data, } } /* * Returns the balances of all supported tokens, including Ether. */ public async getTokenBalances( request: GetTokenBalancesRequest, ): Promise<{ tokenBalances: Map raw_data: R }> { const reqParams: ReqParams = { queryParams: { ...request, token: request.token.join(SEP) }, url: LOOPRING_URLs.GET_TOKEN_BALANCES, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const tokenBalances: Map = new Map() if (raw_data?.amount instanceof Array) { raw_data.amount.forEach((value: any, index: number) => { // tokenBalances[tokenArray[index]] = raw_data.amount[index]; tokenBalances.set(request.token[index] as unknown as T, value) }) } return { tokenBalances, raw_data, } } public async getAllTokenBalances( request: GetALLTokenBalancesRequest, ): Promise<{ tokenBalances: LoopringMap raw_data: R }> { const reqParams: ReqParams = { queryParams: { ...request }, url: LOOPRING_URLs.GET_AKK_TOKEN_BALANCES, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { tokenBalances: raw_data, raw_data, } } /* * Returns the allowances of all supported tokens */ public async getAllowances( request: GetAllowancesRequest, // tokens: any ): Promise<{ raw_data: R tokenAllowances: Map }> { const reqParams: ReqParams = { queryParams: { ...request, token: request.token.join(SEP), }, url: LOOPRING_URLs.GET_ALLOWANCES, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const tokenAllowances: Map = new Map() if (raw_data?.allowances instanceof Array) { raw_data.allowances.forEach((value: any, index: number) => { tokenAllowances.set(request.token[index] as unknown as T, value) }) } return { tokenAllowances, raw_data, } } /* * Return various configurations of Loopring.io */ public async getExchangeInfo(): Promise<{ exchangeInfo: ExchangeInfo raw_data: R }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_EXCHANGE_INFO, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const exchangeInfo: ExchangeInfo = { ammExitFees: getFeeMap(raw_data.ammExitFees), chainId: raw_data.chainId, depositAddress: raw_data.depositAddress, exchangeAddress: raw_data.exchangeAddress, fastWithdrawalFees: getFeeMap(raw_data.fastWithdrawalFees), onchainFees: getFeeMap(raw_data.onchainFees, 1), openAccountFees: getFeeMap(raw_data.openAccountFees), transferFees: getFeeMap(raw_data.transferFees), updateFees: getFeeMap(raw_data.updateFees), withdrawalFees: getFeeMap(raw_data.withdrawalFees), } return { exchangeInfo, raw_data, } } public async getMixDepth(request: GetDepthRequest) { return await this.getDepth(request, LOOPRING_URLs.GET_MIX_DEPTH) } /* * Returns the order book of a given trading pair. */ public async getDepth( request: GetDepthRequest, url: string = LOOPRING_URLs.GET_DEPTH, ): Promise<{ depth: DepthData raw_data: R }> { if (request?.level === undefined) { request.level = 0 } if (request?.limit === undefined) { request.limit = 50 } const reqParams: ReqParams = { queryParams: request, url, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const timestamp = raw_data['timestamp'] const { asks, bids, mid_price } = getMidPrice({ _asks: raw_data['asks'], _bids: raw_data['bids'], }) const depth: DepthData = { symbol: raw_data.market, version: parseInt(raw_data.version), timestamp, mid_price, bids: bids.ab_arr, bids_prices: bids.ab_prices, bids_amtTotals: bids.ab_amtTotals, bids_volTotals: bids.ab_volTotals, bids_amtTotal: bids.amtTotal.toString(), bids_volTotal: bids.volTotal.toString(), asks: asks.ab_arr, asks_prices: asks.ab_prices, asks_amtTotals: asks.ab_amtTotals, asks_volTotals: asks.ab_volTotals, asks_amtTotal: asks.amtTotal.toString(), asks_volTotal: asks.volTotal.toString(), } return { depth, raw_data, } } public async getMixTicker(request: GetTickerRequest): Promise<{ tickMap: LoopringMap tickList: TickerData[] raw_data: R }> { return await this.getTicker(request, LOOPRING_URLs.GET_MIX_TICKER) } /* * Gets a markets ticker. * Generally speaking, a ticker in Loopring consists in data from the market taken last 24Hours. */ public async getTicker( request: GetTickerRequest, url: string = LOOPRING_URLs.GET_TICKER, ): Promise<{ tickMap: LoopringMap tickList: TickerData[] raw_data: R }> { const reqParams: ReqParams = { url, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const tickers = raw_data.tickers const tickMap: LoopringMap = {} const tickList: TickerData[] = [] if (tickers && tickers.length) { tickers.forEach((item: any, ind: number, arr: any) => { const open = parseFloat(item[4]) const close = parseFloat(item[7]) const symbol = item[0].replace('COMBINE-', '') const { base, quote } = getBaseQuote(symbol) let change = 0 if (!isNaN(open) && !isNaN(close)) { change = (close - open) / open } const timestamp = parseInt(item[1]) const tick: TickerData = { symbol, base, quote, timestamp, base_token_volume: item[2], quote_token_volume: item[3], open, high: parseFloat(item[5]), low: parseFloat(item[6]), close, count: parseInt(item[8]), bid: parseFloat(item[9]), ask: parseFloat(item[10]), base_fee_amt: checkAmt(item[11]), quote_fee_amt: checkAmt(item[12]), change, } tickMap[symbol] = tick tickList.push(tick) }) } return { tickMap, tickList, raw_data, } } public async getAllMixTickers(markets: string | undefined = undefined) { let request: GetTickerRequest if (!markets) { const result = await this.getMixMarkets() if (result.marketArrStr) { request = { market: result.marketArrStr, } } else { return result } } else { request = { market: markets, } } return await this.getMixTicker(request) } public async getAllTickers(markets: string | undefined = undefined) { let request: GetTickerRequest if (!markets) { const result = await this.getMarkets() if (result.marketArrStr) { request = { market: result.marketArrStr, } } else { return result } } else { request = { market: markets, } } return await this.getTicker(request) } public async getMixCandlestick(request: GetCandlestickRequest) { return await this.getCandlestick(request, LOOPRING_URLs.GET_MIX_CANDLESTICK) } /* * Gets candlesticks. */ public async getCandlestick( request: GetCandlestickRequest, url: string = LOOPRING_URLs.GET_CANDLESTICK, ): Promise<{ candlesticks: Candlestick[] raw_data: R }> { const reqParams: ReqParams = { url, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } let tsStep = 60000 switch (request.interval) { case TradingInterval.min1: break case TradingInterval.min5: tsStep = 300000 break case TradingInterval.min15: tsStep = 900000 break case TradingInterval.min30: tsStep = 1800000 break case TradingInterval.hr1: tsStep = 3600000 break case TradingInterval.hr2: tsStep = 7200000 break case TradingInterval.hr4: tsStep = 14400000 break case TradingInterval.hr12: tsStep = 43200000 break case TradingInterval.d1: tsStep = 86400000 break case TradingInterval.w1: tsStep = 604800000 break default: break } let candlesticks: Candlestick[] = [] if (raw_data?.candlesticks instanceof Array) { const rawCandlesticks = raw_data.candlesticks.reverse() let lastCandlestick: Candlestick | undefined = undefined let lastTs = -1 rawCandlesticks.forEach((item: any) => { const curTs = parseInt(item[0]) if (lastCandlestick === undefined) { lastTs = curTs const candlestick: Candlestick = { timestamp: curTs, txs: parseInt(item[1]), open: parseFloat(item[2]), close: parseFloat(item[3]), high: parseFloat(item[4]), low: parseFloat(item[5]), baseVol: item[6], quoteVol: item[7], } lastCandlestick = candlestick candlesticks.push(candlestick) } else { const counter = (curTs - lastTs) / tsStep // myLog('counter:', curTs, lastTs, counter) for (let i = 1; i <= counter; i++) { let candlestick: Candlestick if (i === counter) { candlestick = { timestamp: lastTs + i * tsStep, txs: parseInt(item[1]), open: parseFloat(item[2]), close: parseFloat(item[3]), high: parseFloat(item[4]), low: parseFloat(item[5]), baseVol: item[6], quoteVol: item[7], } lastTs = curTs lastCandlestick = candlestick } else { candlestick = { timestamp: lastTs + i * tsStep, txs: 0, open: lastCandlestick.close, close: lastCandlestick.close, high: lastCandlestick.close, low: lastCandlestick.close, baseVol: '0', quoteVol: '0', } } candlesticks.push(candlestick) } } }) } candlesticks = candlesticks.reverse() return { candlesticks, raw_data, } } /* * Fetches, for all the tokens supported by Loopring, their fiat price. */ public async getFiatPrice(request: GetFiatPriceRequest) { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_FIAT_PRICE, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const fiatPrices: LoopringMap = {} if (raw_data?.prices instanceof Array) { raw_data.prices.forEach((item: FiatPriceInfo) => { fiatPrices[item.symbol] = item }) } return { fiatPrices, raw_data, } } /* * Fetches, for all the tokens supported by Loopring, their fiat price. */ public async disableWithdrawTokenList(): Promise<{ raw_data: R disableWithdrawTokenList: any[] }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_IGNORE_WITHDRAW, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const disableWithdrawTokenList = [...raw_data] return { disableWithdrawTokenList, raw_data, } } /* * Query trades with specified market */ public async getMarketTrades(request: GetMarketTradesRequest): Promise<{ totalNum: number marketTrades: MarketTradeInfo[] raw_data: R }> { if (request.limit === undefined) { request.limit = 20 } const reqParams: ReqParams = { url: LOOPRING_URLs.GET_TRADES, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const marketTrades: MarketTradeInfo[] = [] if (raw_data?.trades instanceof Array) { raw_data.trades.forEach((item: any) => { marketTrades.push({ tradeTime: parseInt(item[0]), tradeId: item[1], side: item[2] as Side, volume: item[3], price: item[4], market: item[5], fee: item[6], type: item[13], }) }) } return { totalNum: raw_data.totalNum, marketTrades, raw_data, } } /* * Returns data associated with the user's exchange account. */ public async getAccount(request: GetAccountRequest): Promise<{ accInfo: AccountInfo raw_data: R }> { const reqParams: ReqParams = { url: LOOPRING_URLs.ACCOUNT_ACTION, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const accInfo: AccountInfo = raw_data as AccountInfo return { accInfo, raw_data, } } /* */ public async getEthNonce( request: GetEthNonceRequest, ): Promise<{ nonce: number; raw_data: R }> { const reqParams: ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_ETH_NONCE, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { nonce: raw_data?.nonce, raw_data, } } /* */ public async getGasPrice(): Promise<{ gasPrice: number raw_data: R }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_GAS_PRICE, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const gasPrice = raw_data?.price return { gasPrice, raw_data, } } /* */ public async getGasPriceRange(): Promise<{ gasPriceRanges: any raw_data: R }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_GAS_PRICE_RANGE, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const gasPriceRanges = raw_data?.ranges return { gasPriceRanges, raw_data, } } public async getAccountServices(request: GetAccountServicesRequest): Promise<{ register: any order: any joinAmm: any dAppTrade: any legal: any raw_data: R }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_ACCOUNT_SERVICES, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, register: raw_data?.register, order: raw_data?.order, joinAmm: raw_data?.joinAmm, dAppTrade: raw_data?.dAppTrade, legal: raw_data?.dAppTrade, raw_data, } } public async getTokenInfo( request: { token: string; currency: 'USD' }, url: string = LOOPRING_URLs.GET_QUOTE_TOKEN_INFO, ) { const reqParams: ReqParams = { url, queryParams: { ...request }, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, list: raw_data.data, } } public async getSupportTokens( request: GetDatacenterTokenInfoRequest, url: string = LOOPRING_URLs.GET_SUPPORT_TOKENS, ): Promise<{ raw_data: R; list: R }> { const reqParams: ReqParams = { url, queryParams: { ...request, cmcTokenIds: typeof request.cmcTokenIds === 'string' ? request.cmcTokenIds : request.cmcTokenIds?.join(','), }, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, list: raw_data.data, } } public async getQuoteTokenInfo( request: GetDatacenterTokenQuoteTrendRequest, url: string = LOOPRING_URLs.GET_QUOTE_TREND, ): Promise<{ list: R; raw_data: R }> { const reqParams: ReqParams = { url, queryParams: { ...request }, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, list: raw_data?.data, } } public async getQuoteTokenOhlcv( request: GetDatacenterTokenQuoteTrendRequest, url: string = LOOPRING_URLs.GET_QUOTE_TOKEN_OHLCV_TREND, ): Promise<{ list: R; raw_data: R }> { const reqParams: ReqParams = { url, queryParams: { ...request, }, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, list: raw_data?.data, } } public async getCmcTokenRelations< R = { cmcTokenId: number tokenAddress: string symbol: string }[], >( request: GetCmcTokenRelationsRequest, url: string = LOOPRING_URLs.GET_QUOTE_TOKEN_GETCMCTOKENRELATIONS, ): Promise<{ list: R; raw_data: R }> { const reqParams: ReqParams = { url, queryParams: { ...request, cmcTokenIds: typeof request.cmcTokenIds === 'string' ? request.cmcTokenIds : request.cmcTokenIds?.join(','), tokenAddresses: typeof request.tokenAddresses === 'string' ? request.tokenAddresses : request.tokenAddresses?.join(',') ?? '', }, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, list: raw_data?.data, } } public async getLatestTokenPrices(request?: getLatestTokenPricesRequest) { const reqParams: ReqParams = { queryParams: request, url: LOOPRING_URLs.GET_LATEST_TOKEN_PRICES, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const tokenPrices: LoopringMap = {} if (raw_data?.data instanceof Array) { raw_data.data.forEach((item: any) => { tokenPrices[item.token.toLowerCase()] = parseFloat(item.price) }) } return { tokenPrices, raw_data, } } /* * Get user trade amount */ public async getUserTradeAmount(request: GetUserTradeAmount) { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_USER_TRADE_AMOUNT, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, } } } ================================================ FILE: src/api/global_api.ts ================================================ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' import { sortObjDictionary } from '../utils' const GLOBAL_KEY = { GOERLI: { key: '685xvATlBCsvzyiTxaS02vu0b1xN0DAFpNpslKUNCuSxDhx8gyyz8VmvUqqe5HSQ', id: 10013, }, MAIN: { key: 're356TcrQ6KhlpkvWxP4UN0C4EqxQVV7ZjvLjunwTjaQPZ20ue2ZgClFeT7okpDQ', id: 22638, }, } export class GlobalAPI extends BaseAPI { public async getActiveFeeInfo(request: { accountId?: number }) { const _request: loopring_defs.GetOffchainFeeAmtRequest = { accountId: request.accountId ? request.accountId : this.chainId === loopring_defs.ChainId.MAINNET ? GLOBAL_KEY.MAIN.id : GLOBAL_KEY.GOERLI.id, requestType: loopring_defs.OffchainFeeReqType.UPDATE_ACCOUNT, } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_OFFCHAIN_FEE_AMT, queryParams: _request, apiKey: this.chainId === loopring_defs.ChainId.MAINNET ? GLOBAL_KEY.MAIN.key : GLOBAL_KEY.GOERLI.key, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const gasPrice = parseInt(raw_data.gasPrice) const fees: loopring_defs.LoopringMap = {} if (raw_data?.fees instanceof Array) { raw_data.fees.forEach((item: loopring_defs.OffchainFeeInfo) => { fees[item.token] = item }) } return { fees, gasPrice, raw_data, } } public async getUserBalanceForFee(request: { accountId: number; tokens: string }) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_EXCHANGE_BALANCES, queryParams: request, apiKey: this.chainId === loopring_defs.ChainId.MAINNET ? GLOBAL_KEY.MAIN.key : GLOBAL_KEY.GOERLI.key, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const userBalances: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item: loopring_defs.UserBalanceInfo) => { userBalances[item.tokenId] = item }) } return { userBalances, raw_data, } } public async getAmmPoolGameUserRank( request: loopring_defs.GetAmmPoolGameUserRankRequest, ): Promise<{ raw_data: R userRank: loopring_defs.GameRankInfo }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_AMMPOOL_GAME_USER_RANK, queryParams: request, apiKey: this.chainId === loopring_defs.ChainId.MAINNET ? GLOBAL_KEY.MAIN.key : GLOBAL_KEY.GOERLI.key, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } const userRank: loopring_defs.GameRankInfo = raw_data.data return { userRank, raw_data: raw_data.data, } } public async getBanxaAPI( { method, query, payload, url, accountId, }: { method: loopring_defs.ReqMethod query: string payload: string url: string accountId: number }, eddsaKey: string, apiKey: string, ): Promise<{ result: R raw_data: R }> { const queryParams = { accountId, url, method: method.toString(), query: query, payload: payload ? payload : '', } const dataToSig = sortObjDictionary({ ...queryParams, url: encodeURIComponent(queryParams.url), query: encodeURIComponent(queryParams.query), payload: encodeURIComponent(queryParams.payload), }) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_BANXA_API_KEY, method: loopring_defs.ReqMethod.GET, queryParams, apiKey, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { PrivateKey: eddsaKey, dataToSig: dataToSig, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { result: raw_data.result, raw_data: raw_data, } } // public async getBanxaAPIRequest({}) {} // http://dev.loopring.io?method=GET&query=/api/coins&payload } ================================================ FILE: src/api/index.ts ================================================ export * from './ws_api' export * from './exchange_api' export * from './ammpool_api' export * from './user_api' export * from './wallet_api' export * from './whitelisted_user_api' export * from './contract_api' export * from './sign/sign_tools' export * from './nft_api' export * from './global_api' export * from './delegate_api' export * from './base_api' export * from './defi_api' export * from './luckToken_api' export * from './contacts_api' export * from './vault_api' export * from './ethereum/contracts' export { signHebaoApproveWrap } from './config' export { RabbitWithdrawAPI } from './rabbitWithdraw_api' ================================================ FILE: src/api/luckToken_api.ts ================================================ /* eslint-disable camelcase */ import { BaseAPI } from './base_api' import { LOOPRING_URLs, NFTTokenInfo, ReqMethod, ReqParams, RESULT_INFO, SIG_FLAG, UserNFTBalanceInfo, } from '../defs' import * as loopring_defs from '../defs' import { sortObjDictionary } from '../utils' import * as sign_tools from './sign/sign_tools' import { AxiosResponse } from 'axios' export class LuckTokenAPI extends BaseAPI { public async getLuckTokenAgents(): Promise<{ raw_data: R luckTokenAgents: { [key: string]: loopring_defs.LuckyTokenInfo } }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_AGENTS, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const luckTokenAgents = raw_data.reduce( ( prev: { [key: string]: loopring_defs.LuckyTokenInfo }, item: { owner: string; infos: any[] }, ) => { prev[item.owner] = { signer: item.infos[0], signerUrl: item.infos[1], logoUrl: item.infos[2], memo: item.infos[3], } return prev }, {} as { [key: string]: loopring_defs.LuckyTokenInfo }, ) return { raw_data, luckTokenAgents, } } public async getLuckTokenAuthorizedSigners(): Promise<{ raw_data: R luckTokenAgents: { [key: string]: loopring_defs.LuckyTokenInfo } }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_AUTHORIZEDSIGNERS, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const luckTokenAgents = raw_data.reduce( ( prev: { [key: string]: loopring_defs.LuckyTokenInfo }, item: { owner: string; infos: any[] }, ) => { prev[item.owner] = { signer: item.infos[0], signerUrl: item.infos[1], logoUrl: item.infos[2], memo: item.infos[3], } return prev }, {} as { [key: string]: loopring_defs.LuckyTokenInfo }, ) return { raw_data, luckTokenAgents, } } public async getLuckTokenClaimHistory( request: { fromId: number; limit?: number; isNft?: boolean }, apiKey: string, ): Promise<{ totalNum: number list: loopring_defs.LuckTokenHistory[] raw_data: R }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_CLAIMHISTORY, queryParams: request, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const totalNum: number = raw_data.totalNum const list: loopring_defs.LuckTokenHistory[] = raw_data.list return { totalNum, list, raw_data, } } public async getLuckTokenLuckyTokens( request: { senderId: number hash: string partitions: string modes: string scopes: string statuses: string startTime: number endTime: number fromId: number limit?: number official: boolean isNft?: boolean }, apiKey: string, ): Promise<{ raw_data: R totalNum: number list: loopring_defs.LuckyTokenItemForReceive[] }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_LUCKYTOKENS, queryParams: request, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const totalNum: number = raw_data.totalNum const list: loopring_defs.LuckyTokenItemForReceive[] = raw_data.list return { totalNum, list, raw_data, } } public async getLuckTokenDetail( request: { limit?: number hash: string fromId?: number accountId?: number, serialNo?: number }, apiKey: string, ): Promise<{ raw_data: R detail: loopring_defs.LuckTokenClaimDetail }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_LUCKYTOKENDETAIL, queryParams: request, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, detail: raw_data } } public async getBlindBoxDetail( request: { limit?: number hash: string fromId?: number showHelper: boolean accountId?: number, serialNo?: number }, apiKey: string, ): Promise<{ raw_data: R // detail: loopring_defs.LuckTokenClaimDetail; }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_BLINDBOXDETAIL, queryParams: request, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data } } public async getLuckTokenWithdrawals( request: { statuses: loopring_defs.LuckyTokenWithdrawStatus[] tokenId?: number startTime?: number endTime?: number fromId?: number offset?: number limit?: number isNft?: boolean }, apiKey: string, ): Promise<{ raw_data: R totalNum: number luckTokenWithdraw: loopring_defs.LuckTokenWithdraw[] }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_WITHDRAWALS, queryParams: { ...request, statuses: request.statuses.join(',') }, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, totalNum: raw_data?.totalNum, luckTokenWithdraw: raw_data.list, } } public async getLuckTokenBalances( request: { accountId: number tokens?: number[] isNft?: boolean offset?: number limit?: number }, apiKey: string, ): Promise<{ raw_data: R totalNum: number tokenBalance: Array< loopring_defs.UserBalanceInfo & { isNft?: boolean nftTokenInfo?: loopring_defs.NFTTokenInfo } > }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_BALANCES, queryParams: { ...request, // statuses: request.tokens?.join(",") }, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, totalNum: raw_data?.length, tokenBalance: raw_data, } } public async getLuckTokenClaimedLuckyTokens( request: { fromId: number limit?: number hashes?: string[] isNft?: boolean }, apiKey: string, ): Promise<{ raw_data: R totalNum: number claimedHistory: Array }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_CLAIMEDLUCKYTOKENS, queryParams: { ...request, hashes: request?.hashes?.join(',') }, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, totalNum: raw_data?.totalNum, claimedHistory: raw_data.list, } } public async getLuckTokenSummary(apiKey: string): Promise<{ raw_data: R tokenSummaryList: { tokenId: number amount: string isNft?: Boolean nftTokenInfo?: NFTTokenInfo & Partial }[] totalNum: number }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_SUMMARY, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, totalNum: raw_data.count, tokenSummaryList: raw_data.tokenSummaryList, } } public async getLuckTokenNFTBalances( request: { accountId: number }, apiKey: string, ): Promise<{ raw_data: R amount: number }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_NFTBALANCES, queryParams: request, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, amount: raw_data.amount as number, // totalNum: raw_data.count, // tokenSummaryList: raw_data.tokenSummaryList, } } public async sendLuckTokenClaimLuckyToken({ request, apiKey, eddsaKey, }: { request: { hash: string claimer: string referrer: string, serialNo?: number } eddsaKey: string apiKey: string }): Promise<{ raw_data: R }> { const dataToSig: Map = sortObjDictionary(request) const reqParams: ReqParams = { url: LOOPRING_URLs.POST_LUCK_TOKEN_CLAIMLUCKYTOKEN, bodyParams: request, method: ReqMethod.POST, sigFlag: SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, apiKey, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async sendLuckTokenClaimBlindBox({ request, apiKey, eddsaKey, }: { request: { hash: string claimer: string referrer: string serialNo?: number } eddsaKey: string apiKey: string }): Promise<{ raw_data: R }> { const dataToSig: Map = sortObjDictionary(request) const reqParams: ReqParams = { url: LOOPRING_URLs.POST_LUCK_TOKEN_CLAIMBLINDBOX, bodyParams: request, method: ReqMethod.POST, sigFlag: SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, apiKey, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async sendLuckTokenWithdraws( req: loopring_defs.OriginLuckTokenWithdrawsRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise<(Omit & { raw_data: Omit }) | RESULT_INFO> { let { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined let { transfer } = request transfer.payeeId = 0 transfer.memo = `LuckTokenWithdrawalBy${request.claimer}` try { ecdsaSignature = await sign_tools.transferWrap({ transfer: transfer as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } if (counterFactualInfo) { transfer.counterFactualInfo = counterFactualInfo } let { maxFee, token, ..._transfer } = transfer // @ts-ignore _transfer = { ..._transfer, maxFeeAmount: maxFee?.volume ? maxFee?.volume : '0', feeToken: maxFee?.tokenId ? maxFee?.tokenId : 0, amount: token.volume, token: token.tokenId, ecdsaAuth: ecdsaSignature, eddsaSig: sign_tools.get_EddsaSig_Transfer( transfer as loopring_defs.OriginTransferRequestV3, eddsaKey, ).result, } as any request = { ...request, transfer: JSON.stringify(_transfer) as any, } const reqParams: ReqParams = { url: LOOPRING_URLs.POST_LUCK_TOKEN_WITHDRAWALS, apiKey, method: ReqMethod.POST, bodyParams: { ...request }, sigFlag: SIG_FLAG.NO_SIG, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } public async sendLuckTokenSend< R = { hash: string status: string isIdempotent: boolean accountId: number tokenId: number storageId: number }, >( req: loopring_defs.OriginLuckTokenSendRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise<(Omit & { raw_data: Omit }) | RESULT_INFO> { let { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined const { luckyToken: { maxFeeAmount, token, amount, feeToken, ...rest }, // @ts-ignore nftData, } = request try { let transfer: any, eddsaSig if (nftData) { transfer = { ...rest, fromAccountId: rest.payerId, fromAddress: rest.payerAddr, toAccountId: 0, toAddress: rest.payeeAddr, maxFee: { tokenId: feeToken, amount: maxFeeAmount, }, payeeId: 0, memo: `LuckTokenSendBy${accountId}`, token: { nftData, tokenId: token, amount: amount, }, } as loopring_defs.OriginNFTTransferRequestV3 try { ecdsaSignature = await sign_tools.transferNFTWrap({ transfer, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } eddsaSig = sign_tools.get_EddsaSig_NFT_Transfer(transfer, eddsaKey).result } else { transfer = { ...rest, maxFee: { tokenId: feeToken, volume: maxFeeAmount, }, payeeId: 0, memo: `LuckTokenSendBy${accountId}`, token: { tokenId: token, volume: amount, }, } as loopring_defs.OriginTransferRequestV3 try { ecdsaSignature = await sign_tools.transferWrap({ transfer: transfer as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } if (counterFactualInfo) { transfer.counterFactualInfo = counterFactualInfo } transfer.eddsaSignature = sign_tools.get_EddsaSig_Transfer( transfer as loopring_defs.OriginTransferRequestV3, eddsaKey, ).result eddsaSig = sign_tools.get_EddsaSig_Transfer(transfer, eddsaKey).result } request = { ...request, nftData, luckyToken: { ...request.luckyToken, payeeId: 0, memo: `LuckTokenSendBy${accountId}`, eddsaSig, counterFactualInfo, }, } const reqParams: loopring_defs.ReqParams = { url: LOOPRING_URLs.POST_LUCK_TOKEN_SENDLUCKYTOKEN, bodyParams: { ...request }, apiKey, method: ReqMethod.POST, sigFlag: SIG_FLAG.NO_SIG, ecdsaSignature, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } catch (error) { throw error } } public async getLuckTokenClaimedBlindBox( request: { fromId: number limit?: number isNft?: boolean offset?: number statuses?: number[] }, apiKey: string, ): Promise<{ raw_data: R totalNum: number list: Array }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_CLAIMEDBLINDBOX, queryParams: { ...request, statuses: request?.statuses?.join(',') }, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, totalNum: raw_data?.totalNum, list: raw_data.list, } } public async getLuckTokenUnclaimNFTBlindboxCnt( request: { accountId: number }, apiKey: string, ): Promise<{ raw_data: R count: number }> { const reqParams: ReqParams = { url: LOOPRING_URLs.POST_LUCK_TOKEN_UNCLAIMNFTANDBLINDCNT, queryParams: request, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, count: raw_data?.count, } } public async getLuckTokenUserLuckyTokenTargets( request: { statuses?: number[] fromId?: number limit?: number offset?: number isAll?: number // 0-all, 1-unexpired }, apiKey: string, ): Promise<{ raw_data: R totalNum: number list: loopring_defs.LuckyTokenItemForReceive[] }> { const reqParams: ReqParams = { url: LOOPRING_URLs.GET_LUCK_TOKEN_LUCKYTOKENTARGETS, queryParams: { ...request, statuses: request.statuses ? request.statuses.join(',') : undefined, }, apiKey, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, totalNum: raw_data?.totalNum as number, list: raw_data?.list as loopring_defs.LuckyTokenItemForReceive[], } } public async sendLuckTokenSubmitAddTarget( request: { claimer: string[] hash: string notifyType: number // 0-red dot, 1-pop }, eddsaSignKey: string, apiKey: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = sortObjDictionary(request) const reqParams: ReqParams = { url: LOOPRING_URLs.POST_LUCK_TOKEN_SUBMITADDTARGET, bodyParams: request, apiKey, method: ReqMethod.POST, sigFlag: SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaSignKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, } } } ================================================ FILE: src/api/nft_api.ts ================================================ /* eslint-disable @typescript-eslint/no-var-requires */ import { BaseAPI } from './base_api' import CID from 'cids' import * as loopring_defs from '../defs' import { ChainId, ConnectorError, LoopringErrorCode, NftData, NFTFactory, NFTTokenInfo, ReqMethod, ReqParams, SIG_FLAG, } from '../defs' import { myLog } from '../utils/log_tools' import * as ethUtil from 'ethereumjs-util' import { genExchangeData, sendRawTx } from './contract_api' import { contracts } from './ethereum/contracts' import BN from 'bn.js' const CREATION_CODE = { [ChainId.GOERLI]: '3d602d80600a3d3981f3363d3d373d3d3d363d735854e62554ce1c146a375c370bc0d323368b372d5af43d82803e903d91602b57fd5bf3', [ChainId.MAINNET]: '3d602d80600a3d3981f3363d3d373d3d3d363d73b25f6d711aebf954fb0265a3b29f7b9beba7e55d5af43d82803e903d91602b57fd5bf3', [ChainId.SEPOLIA]: '3d602d80600a3d3981f3363d3d373d3d3d363d73f5c315fe77e81f49e3390b167b78a72bfffcf01c5af43d82803e903d91602b57fd5bf3', [ChainId.TAIKOHEKLA]: '', [ChainId.TAIKO]: '', [ChainId.BASE]: '', [ChainId.BASESEPOLIA]: '', } const { Contracts: { erc721Abi: { erc721 }, erc1155Abi: { erc1155 }, }, } = contracts export enum NFTType { ERC1155 = 0, ERC721, } export enum NFT_TYPE_STRING { ERC1155 = 'ERC1155', ERC721 = 'ERC721', } export enum NFTMethod { setApprovalForAll = 'setApprovalForAll', isApprovedForAll = 'isApprovedForAll', uri = 'uri', tokenURI = 'tokenURI', depositNFT = 'depositNFT', balanceOf = 'balanceOf', ownerOf = 'ownerOf', // Deposit = 'deposit', // ForceWithdraw = 'forceWithdraw' } export class NFTAPI extends BaseAPI { private async callContractMethod( web3: any, method: string, data: any[], contractAddress: string, type: NFTType = NFTType.ERC1155, ) { // return _genContractData(Contracts.ERC20Token, method, data) const contract = this._genContract(web3, contractAddress, type) return contract.methods[method](...data).call() } private _genContractData(Contract: any, method: string, data: any) { return Contract.encodeInputs(method, data) } private _genERC1155Data(method: string, data: any) { return this._genContractData(contracts.Contracts.ERC1155, method, data) } private _genERC721Data(method: string, data: any) { return this._genContractData(contracts.Contracts.ERC721, method, data) } private _genContract(web3: any, contractAddress: string, type: NFTType = NFTType.ERC1155) { return new web3.eth.Contract(type === NFTType.ERC1155 ? erc1155 : erc721, contractAddress) } /** * getNFTBalance * @param web3 * @param tokenAddress * @param account * @param nftId * @param nftType */ public async getNFTBalance({ web3, tokenAddress, account, nftId, nftType = NFTType.ERC1155, }: loopring_defs.UserNFTBalanceParam): Promise<{ count?: string }> { try { if (nftType === NFTType.ERC721) { const result: string = await this.callContractMethod( web3, NFTMethod.ownerOf, [nftId], tokenAddress, nftType, ) if (result.toLowerCase() === account.toLowerCase()) { return { count: '1', } } else { return { count: '0', } } } else { const result: string = await this.callContractMethod( web3, NFTMethod.balanceOf, [account, web3.utils.hexToNumberString(nftId)], tokenAddress, nftType, ) return { count: result.toString(), } } } catch (err) { return { ...(err as any), code: LoopringErrorCode.CONTRACTNFT_BALANCE, message: ConnectorError.CONTRACTNFT_BALANCE, } } } /** * getInfoForNFTTokens * @param nftDatas NftData[] */ public async getInfoForNFTTokens({ nftDatas, }: { nftDatas: NftData[] }): Promise<{ [key: string]: NFTTokenInfo } | undefined> { try { const reqParams: ReqParams = { sigFlag: SIG_FLAG.NO_SIG, url: loopring_defs.LOOPRING_URLs.GET_NFTs_INFO, method: ReqMethod.GET, queryParams: { nftDatas: nftDatas.join(',') }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const result = raw_data.reduce( (prev: { [key: string]: NFTTokenInfo }, item: NFTTokenInfo) => { if (item.nftId && item.nftId.startsWith('0x')) { const hashBN = new BN(item.nftId.replace('0x', ''), 16) item.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') } prev[item.nftData] = item return prev }, {}, ) return { ...result, raw_data, } } catch (err) { return undefined } } public async callRefreshNFT( request: loopring_defs.CallRefreshNFT, ): Promise<{ status: string; createdAt: number; updatedAt: number } | undefined> { try { const reqParams: ReqParams = { sigFlag: SIG_FLAG.NO_SIG, bodyParams: request, url: loopring_defs.LOOPRING_URLs.POST_NFT_VALIDATE_REFRESH_NFT, method: ReqMethod.POST, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const result = raw_data.reduce( (prev: { [key: string]: NFTTokenInfo }, item: NFTTokenInfo) => { if (item.nftId && item.nftId.startsWith('0x')) { const hashBN = new BN(item.nftId.replace('0x', ''), 16) item.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') } prev[item.nftData] = item return prev }, {}, ) return { ...result, raw_data, } } catch (err) { return undefined } } /** * getContractNFTMeta * @param web3 * @param tokenAddress * @param _id * @param nftType */ public async getContractNFTMeta( { web3, tokenAddress, nftId, nftType = NFTType.ERC1155 }: loopring_defs.ContractNFTMetaParam, _IPFS_META_URL: string = loopring_defs.LOOPRING_URLs.IPFS_META_URL, ) { try { myLog(tokenAddress, 'nftid', nftId, web3.utils.hexToNumberString(nftId)) let result: string result = await this.callContractMethod( web3, nftType === NFTType.ERC1155 ? NFTMethod.uri : NFTMethod.tokenURI, [web3.utils.hexToNumberString(nftId)], tokenAddress, nftType, ) result = result.replace(/^ipfs:\/\/(ipfs\/)?/, loopring_defs.LOOPRING_URLs.IPFS_META_URL) result = result.replace('{id}', web3.utils.hexToNumberString(nftId)) return await fetch(result).then((response) => response.json()) } catch (err) { return { code: LoopringErrorCode.CONTRACTNFT_URI, message: ConnectorError.CONTRACTNFT_URI, ...(err as any), } } } /** * approveNFT * @param web3 * @param from The address that deposits the funds to the exchange * @param to The address deposits to * @param nftId the nftId * @param chainId * @param nftType The type of NFTAction contract address (ERC721/ERC1155/...) * @param nonce * @param gasPrice * @param gasLimit * @param sendByMetaMask */ public async approveNFT({ web3, from, depositAddress, tokenAddress, nftId, nftType = NFTType.ERC1155, gasPrice, gasLimit, chainId, nonce, approved = true, sendByMetaMask = true, }: loopring_defs.ApproveParam) { let data: any if (nftType === NFTType.ERC1155) { data = this._genERC1155Data(NFTMethod.setApprovalForAll, { operator: depositAddress, approved, }) } else if (nftType === NFTType.ERC721) { data = this._genERC721Data(NFTMethod.setApprovalForAll, { operator: depositAddress, approved, }) } try { return await sendRawTx( web3, from, tokenAddress, '0', data, chainId, nonce, gasPrice, gasLimit, sendByMetaMask, ) } catch (err) { return { ...(err as any), code: LoopringErrorCode.CONTRACTNFT_SET_APPROVE, message: ConnectorError.CONTRACTNFT_SET_APPROVE, } } } public ipfsCid0ToNftID(cidV0Str: string): string { // eslint-disable-next-line @typescript-eslint/no-var-requires const cid = new CID(cidV0Str) const hashHex = Buffer.from(cid.multihash.slice(2)).toString('hex') const hashBN = new BN(hashHex, 16) return '0x' + hashBN.toString('hex').padStart(64, '0') } /** * * @param nftId 16 */ public ipfsNftIDToCid(nftId: string) { const hashBN = new BN(nftId.replace('0x', ''), 16) const hex = hashBN.toString(16, 64) const buf = Buffer.from('1220' + hex, 'hex') const cid = new CID(buf) return cid.toString() } /** * isApprovedForAll * @param web3 * @param from The address that deposits the funds to the exchange * @param exchangeAddress loopring exchange address * @param nftType NFTType * @param tokenAddress The address of NFT token */ public async isApprovedForAll({ web3, from, exchangeAddress, nftType = NFTType.ERC1155, tokenAddress, }: loopring_defs.IsApproveParam) { try { const result = await this.callContractMethod( web3, NFTMethod.isApprovedForAll, [from, exchangeAddress], tokenAddress, nftType, ) return result } catch (err) { return { ...(err as any), code: LoopringErrorCode.CONTRACTNFT_IS_APPROVE, message: ConnectorError.CONTRACTNFT_IS_APPROVE, } } } /** * @DepositParam an NFTAction to the specified account. * @param web3 * @param from The address that deposits the funds to the exchange * @param to The account owner's address receiving the funds * @param nftType The type of NFTAction contract address (ERC721/ERC1155/...) * @param tokenAddress The address of NFTAction token * @param nftId The token type 'id`. * @param amount The amount of tokens to deposit. * @param nonce: number, * @param gasPrice: number, * @param gasLimit: number, * @param extraData Optional extra data used by the deposit contract. * @param chainId 0|5 * @param sendByMetaMask boolean */ public async depositNFT({ web3, from, exchangeAddress, nftType = NFTType.ERC1155, tokenAddress, nftId, amount, gasPrice, gasLimit, chainId = ChainId.MAINNET, nonce, extraData, sendByMetaMask = true, }: loopring_defs.DepositNFTParam) { const data = genExchangeData(NFTMethod.depositNFT, { from, to: from, nftType, tokenAddress, nftId, amount, extraData: extraData ? extraData : '', }) // myLog('depositNFT data',data) return await sendRawTx( web3, from, exchangeAddress, '0', data, chainId as ChainId, nonce, gasPrice, gasLimit, sendByMetaMask, ) } /** * * @function computeNFTAddress * @param owner {string} nftOwner address * @param nftFactory {string} Hash address * @return tokenAddress * @throws Error */ public computeNFTAddress({ nftOwner, nftFactory = '0xDB42E6F6cB2A2eFcF4c638cb7A61AdE5beD82609', nftBaseUri = '', }: { nftOwner: string nftFactory?: string nftBaseUri?: string }): { tokenAddress: string } { try { if (!nftFactory) { nftFactory = NFTFactory[this.chainId] } if (nftOwner.startsWith('0x')) { nftOwner = nftOwner.slice(2) } const saltBuf = Buffer.concat([ Buffer.from('NFT_CONTRACT_CREATION', 'utf8'), Buffer.from(nftOwner, 'hex'), Buffer.from(nftBaseUri, 'utf8'), ]) const codeHash = ethUtil.keccak(Buffer.from(CREATION_CODE[this.chainId], 'hex')) const saltHash = ethUtil.keccak(saltBuf) const rawBuf = Buffer.concat([ Buffer.from('ff', 'hex'), Buffer.from(nftFactory.slice(2), 'hex'), saltHash, codeHash, ]) const addr = ethUtil.keccak(rawBuf).slice(12).toString('hex') return { tokenAddress: ethUtil.toChecksumAddress('0x' + addr), } } catch (err) { return err as any } } public async getPublicCollectionById(request: { id: string }): Promise<({ raw_data: R } & loopring_defs.CollectionMeta) | loopring_defs.RESULT_INFO> { try { const reqParams: ReqParams = { sigFlag: SIG_FLAG.NO_SIG, queryParams: request, url: loopring_defs.LOOPRING_URLs.GET_NFT_COLLECTION_PUBLISH, method: ReqMethod.GET, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const result = raw_data as loopring_defs.CollectionMeta return { ...result, raw_data, } } catch (err) { return { ...(err as any), code: exports.LoopringErrorCode.SKD_UNKNOW, } } } async getCollectionWholeNFTs(request: loopring_defs.GetCollectionWholeNFTsRequest) { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_COLLECTION_WHOLE_NFTS, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } if (raw_data.nftTokenInfos.length) { raw_data.nftTokenInfos = raw_data.nftTokenInfos.reduce( (prev: loopring_defs.UserNFTBalanceInfo[], item: loopring_defs.UserNFTBalanceInfo) => { if (item.nftId && item.nftId.startsWith('0x')) { const hashBN = new BN(item.nftId.replace('0x', ''), 16) item.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') if ( request.metadata === true && item.metadata && item.metadata.nftId && item.metadata.nftId.startsWith('0x') ) { // const hashBN = new BN(item.metadata.nftId.replace("0x", ""), 16); item.metadata.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') } } return [...prev, item] }, [], ) // const hashBN = new BN(raw_data.transactions.metadata.nftId.replace("0x", ""), 16); // raw_data.transactions.metadata.nftId= "0x" + hashBN.toString("hex").padStart(64, "0"); } return { totalNum: raw_data?.totalNum, userNFTBalances: raw_data.nftTokenInfos as loopring_defs.UserNFTBalanceInfo[], raw_data, } } async getHadUnknownCollection(request: { accountId: number }): Promise { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_HAD_UNKNOWN_COLLECTION, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return raw_data } async getUserNFTBurnAddress(request: { accountId: number tokenId: number }): Promise { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_BURN_ADDRESS, queryParams: request, method: ReqMethod.GET, sigFlag: SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return raw_data } } ================================================ FILE: src/api/rabbitWithdraw_api.ts ================================================ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' import {RabbitWithdrawRequest} from '../defs' import { ethers, utils } from 'ethers'; import { get_EddsaSig_Transfer } from './sign/sign_tools'; import { AxiosResponse } from 'axios'; const {_TypedDataEncoder} = utils interface CounterFactualInfo { walletFactory: string; walletOwner: string; walletSalt: string; } export class RabbitWithdrawAPI extends BaseAPI { public async getConfig(): Promise<{ config: string }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_RABBIT_WITHDRAW_CONFIG, // You'll need to add this URL in url_defs.ts method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const data = (await this.makeReq().request(reqParams)).data return data as { config: string } } public async getNetworkWithdrawalAgents(req: { tokenId: number amount: string network: string }): Promise< { address: string tokenId: number symbol: string totalAmount: string freezeAmount: string timestamp: number }[] > { const reqParams: loopring_defs.ReqParams = { queryParams: req, url: loopring_defs.LOOPRING_URLs.GET_NETWORK_WITHDRAWAL_AGENTS, // You'll need to add this URL in url_defs.ts method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const data = (await this.makeReq().request(reqParams)).data return data } public async submitRabitWithdraw( req: RabbitWithdrawRequest, extra: { signer: ethers.providers.JsonRpcSigner chainId: number exchangeAddr: string eddsaSignKey: string }, ): Promise<{ hash: string status: string isIdempotent: boolean accountId: number tokenId: number storageId: number }> { const { signer, chainId, exchangeAddr } = extra const mapRequestToTypedData = (request: Record) => { return { fromNetwork: request['fromNetwork'], toNetwork: request['toNetwork'], withdrawToAddr: request['toAddress'], payerId: request['transfer']['payerId'], payerAddr: request['transfer']['payerAddr'], payeeId: request['transfer']['payeeId'], payeeAddr: request['transfer']['payeeAddr'], tokenID: request['transfer']['token']['tokenId'], amount: request['transfer']['token']['volume'], feeTokenID: request['transfer']['maxFee']['tokenId'], maxFee: request['transfer']['maxFee']['volume'], validUntil: request['transfer']['validUntil'], storageID: request['transfer']['storageId'], } as Record } const signFastWithdrawTypedData = (args: { signer: ethers.providers.JsonRpcSigner chainId: number exchangeAddress: string data: Record }) => { const { signer, chainId, exchangeAddress } = args const domain = { name: 'Loopring Protocol', version: '3.6.0', chainId: chainId, verifyingContract: exchangeAddress, } const types = { RabbitWithdraw: [ { type: 'uint32', name: 'payerId' }, { type: 'address', name: 'payerAddr' }, { type: 'uint32', name: 'payeeId' }, { type: 'address', name: 'payeeAddr' }, { type: 'uint16', name: 'tokenID' }, { type: 'uint96', name: 'amount' }, { type: 'uint16', name: 'feeTokenID' }, { type: 'uint96', name: 'maxFee' }, { type: 'uint32', name: 'validUntil' }, { type: 'uint32', name: 'storageID' }, { type: 'string', name: 'fromNetwork' }, { type: 'string', name: 'toNetwork' }, { type: 'address', name: 'withdrawToAddr' }, ], } console.log('EIP712 hash', _TypedDataEncoder.hash(domain, types, args.data)) return signer._signTypedData(domain, types, args.data) } const ecdsaSignature = await signFastWithdrawTypedData({ signer: signer, chainId: chainId, exchangeAddress: exchangeAddr, data: mapRequestToTypedData(req), }) const eddsaSignature = get_EddsaSig_Transfer(req.transfer, extra.eddsaSignKey).result const reqParams: loopring_defs.ReqParams = { bodyParams: { ...req, transfer: { ...req.transfer, eddsaSignature }, }, url: loopring_defs.LOOPRING_URLs.POST_RABBIT_WITHDRAW, // You'll need to add this URL in url_defs.ts method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature: ecdsaSignature, } const data = (await this.makeReq().request(reqParams)).data return data } public async getUserCrossChainFee( request: loopring_defs.GetUserCrossChainFeeRequest, apiKey: string, ): Promise<{ gasPrice: string fees: { token: string tokenId: number fee: string discount: number }[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_CROSSCHAIN_OFFCHAIN_FEE_AMT, queryParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return raw_data } } ================================================ FILE: src/api/request.ts ================================================ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' import { SIG_FLAG, ReqParams, ReqOptions } from '../defs/loopring_enums' import { getEdDSASig, getEdDSASigWithPoseidon } from './sign/sign_tools' import { sortObject } from '../utils/obj_tools' /** * * @export */ export const setSearchParams = function (url: URL, ...objects: any[]) { const searchParams = new URLSearchParams(url.search) for (const object of objects) { if (object) { const objectTmp = sortObject(object) for (const key in objectTmp) { if (objectTmp[key] != undefined) searchParams.set(key, objectTmp[key]) } } } url.search = searchParams.toString() } /** * * @export */ export const serializeDataIfNeeded_For_SetReffer = function (value: any) { return JSON.stringify(value, Object.keys(value).sort()) } export const serializeDataIfNeeded = function (value: any) { const nonString = typeof value !== 'string' return nonString ? JSON.stringify(value !== undefined ? sortObject(value) : {}) : value || '' } /** * * @export */ export const toPathString = function (url: URL) { return url.pathname + url.search + url.hash } export class Request { private _axios: AxiosInstance private baseOptions: any = {} public getIns() { return this._axios } constructor(baseUrl: string, timeout: number) { this.baseOptions = { baseURL: baseUrl, timeout: timeout, headers: { // 'Accept': '*/*', // 'Accept-Encoding': 'gzip, deflate, br', feeVersion: 'v2', 'Content-Type': 'application/json', pf: 'web', // tag for recognizing source }, validateStatus: function (status: any) { if ((status >= 200 && status < 300) || status === 400) { return true } return false // return true // always true, handle exception in each bussiness logic }, insecure: true, } this._axios = axios.create(this.baseOptions) } public async request(params: ReqParams, options: any = {}) { const localUrl = new URL(params.url, this.baseOptions.baseURL) const localVarRequestOptions = { method: params.method, ...options } setSearchParams(localUrl, params?.queryParams) const urlPathStr = toPathString(localUrl) let headers: any = {} if (params?.apiKey) { headers['X-API-KEY'] = params?.apiKey } let sig: any = params.sigObj?.sig if (params.sigFlag !== SIG_FLAG.NO_SIG && !params?.sigObj?.dataToSig) { throw Error('no dataToSig field!') } switch (params.sigFlag) { case SIG_FLAG.NO_SIG: break case SIG_FLAG.EDDSA_SIG_POSEIDON: sig = getEdDSASigWithPoseidon(params.sigObj?.dataToSig, params.sigObj?.PrivateKey).result break case SIG_FLAG.EDDSA_SIG: sig = getEdDSASig( params.method, this.baseOptions.baseURL, params.url, params.sigObj?.dataToSig, params.sigObj?.PrivateKey, ) break default: break } if (sig) { headers['X-API-SIG'] = sig } else if (params?.ecdsaSignature) { headers['X-API-SIG'] = params?.ecdsaSignature } else if (params?.eddsaSignature) { headers['X-API-SIG'] = params?.eddsaSignature } if (params?.eddsaSignatureREFER && params.sigObj?.PrivateKey) { const sig = getEdDSASig( params.method, this.baseOptions.baseURL, params.url, params.sigObj?.dataToSig, params.sigObj?.PrivateKey, ) headers['X-API-SIG-REFER'] = sig } // myLog('headers["X-API-SIG"]', headers["X-API-SIG"]); if (params?.bodyParams) { const bodyParams = params?.bodyParams if (sig && params.sigObj?.sigPatch) { bodyParams[params.sigObj?.sigPatch] = sig } if (params?.ecdsaSignature) { bodyParams.ecdsaSignature = params?.ecdsaSignature } if (params?.eddsaSignature) { bodyParams.eddsaSignature = params?.eddsaSignature } localVarRequestOptions.data = serializeDataIfNeeded(bodyParams) } headers = { ...this.baseOptions.headers, ...headers } const optInOne = { ...this.baseOptions, headers: { ...headers, ...params.extraHeaders, }, ...localVarRequestOptions, url: this.baseOptions.baseURL + urlPathStr, } // myLog(optInOne); // myLog("headers config", optInOne); return await this._axios.request(optInOne) } public updateOpt(reqOpt: ReqOptions) { // @ts-ignore this._axios.interceptors.request.use((req: AxiosRequestConfig) => { if (reqOpt?.baseUrl) { req.baseURL = reqOpt?.baseUrl } if (reqOpt?.url) { req.url = reqOpt?.url } req.headers = req.headers ?? {} if (reqOpt?.apiKey) { req.headers['X-API-KEY'] = reqOpt?.apiKey } if (reqOpt?.signature) { req.headers['X-API-SIG'] = reqOpt?.signature } return req }) return this } public addApiKey(apiKey: string) { return this.updateOpt({ apiKey }) } public addSig(signature: string) { return this.updateOpt({ signature }) } public updateBaseUrl(baseUrl: string) { return this.updateOpt({ baseUrl }) } public handle400(callback: any = undefined) { this._axios.interceptors.response.use((res: AxiosResponse) => { if (res.status == 400) { // eslint-disable-next-line no-console console.log(res.request + ' got ' + res.statusText) } if (callback) { callback() } return res }) } } ================================================ FILE: src/api/sign/poseidon/EDDSAUtil.ts ================================================ import { BigNumber } from "ethers"; import { SignatureScheme } from "./eddsa"; import { FQ } from "./field"; import { jubjub } from "./jubjub"; import { babyJub } from "./babyJub"; export class EDDSAUtil { static sign(PrivateKey: string | undefined, hash: any) { const strKey = BigNumber.from(PrivateKey) const msg = BigNumber.from(hash) // console.log("strKey", strKey.toString()) // console.log("msg", msg.toString()) const copyKey = new FQ(strKey) const B = SignatureScheme.B() const signed = SignatureScheme.sign(msg, copyKey, B) // console.log("signed", signed.toStr()) const x = EDDSAUtil.formatted(signed.sig.R.x.n.toHexString().slice(2)) const y = EDDSAUtil.formatted(signed.sig.R.y.n.toHexString().slice(2)) const s = EDDSAUtil.formatted(signed.sig.s.n.toHexString().slice(2)) const result = `0x${x}${y}${s}` // console.log("result", result) return { "Rx": signed.sig.R.x.n.toString(), "Ry": signed.sig.R.y.n.toString(), "s": signed.sig.s.n.toString() } } static formatted(hexString: string) { const outputLength = 32 * 2 const more = outputLength - hexString.length if (more > 0) { for (let i = 0; i < more; i++) { hexString = "0" + (hexString) } } else { hexString = hexString.slice(0, outputLength) } return hexString } static generateKeyPair(seed: any) { let bigInt = BigNumber.from(0) for (let i = 0; i < seed.length; i++) { const item = seed[i] const itemBigInt = BigNumber.from(item) const tmp = BigNumber.from("256").pow(BigNumber.from(i)) bigInt = bigInt.add(itemBigInt.mul(tmp)) } // console.log("sum", bigInt.toString()) const secretKey = bigInt.mod(jubjub.JUBJUB_L) // console.log("secretKey", secretKey.toString()) const copySecretKey = BigNumber.from(secretKey.toString()) // console.log("copySecretKey", copySecretKey.toString()) const B = SignatureScheme.B() // console.log("B", B.toString()) const publicKey = B.mul(copySecretKey) // console.log("publicKey", publicKey.x.n.toString(), publicKey.y.n.toString()) const keyPair = { "publicKeyX": publicKey.x.n.toString(), "publicKeyY": publicKey.y.n.toString(), "secretKey": secretKey.toString() } return keyPair } static pack(publicKeyX: string, publicKeyY: string) { const P0 = BigNumber.from(publicKeyX) const P1 = BigNumber.from(publicKeyY) const newPack = babyJub.packPoint(P0, P1) return newPack } } ================================================ FILE: src/api/sign/poseidon/TestsEDDSAUtil_test.ts ================================================ import { BigNumber } from "ethers"; import { EDDSAUtil } from "./EDDSAUtil"; import { bnToBuf, bnToBufWithFixedLength, SignatureScheme } from "./eddsa"; import { assert } from "console"; function testEddsaPack_1() { console.log("testEddsaPack") // Input const publicKeyX = "0x10440b6de1fc92536e20501e7513b5ca78d4c8c876450d97cfc2a4e24a4c67c7" const publicKeyY = "0x10dfbb7cd80bde2eeedbf9e0fad12819ca20be36b96a7f28c37c6a7550ed366c" // Output const eddsaPackOutput = "10dfbb7cd80bde2eeedbf9e0fad12819ca20be36b96a7f28c37c6a7550ed366c" const packed = EDDSAUtil.pack(publicKeyX, publicKeyY) assert(packed === eddsaPackOutput) console.log("testEddsaPack passed") } function testEddsaPack_2() { console.log("testEddsaPack") // Input const publicKeyX = "0x191f8455196d7c9116065b69a745edbe3f5b6193125f48d818e16101b0ebd13f" const publicKeyY = "0x0729569e74800ba4344767d608ec54f12b14cc4404a8726b5f73ebf8b58f9a09" // Output const eddsaPackOutput = "8729569e74800ba4344767d608ec54f12b14cc4404a8726b5f73ebf8b58f9a09" const packed = EDDSAUtil.pack(publicKeyX, publicKeyY) assert(packed === eddsaPackOutput) console.log("testEddsaPack passed") } function testEddsaPack_3() { console.log("testEddsaPack") // Input const publicKeyX = "0x1ae187c43a6b534be1ac60b335d5cc8c12a9d479339e5ce4134315e20c2940c4" const publicKeyY = "0x16fb785193a8657feb41b7944afde61fe05ea519621fa0f19d872a8fbba83463" // Output const eddsaPackOutput = "96fb785193a8657feb41b7944afde61fe05ea519621fa0f19d872a8fbba83463" const packed = EDDSAUtil.pack(publicKeyX, publicKeyY) assert(packed === eddsaPackOutput) console.log("testEddsaPack passed") } function testEddsaSign() { console.log("testEddsaSign") const strKey = "1965533437444427599736796973543479035828634172708055838572430750620147597402" const msg = "20823375595941673465102915960468301465677704522962441935281926279865178787657" const result = EDDSAUtil.sign(strKey, msg) // assert(result == "0x04ad08469a4a8622a661a793615ebddfa6ea2b72c8f1b02e33288ea2bd71cede0ed7f67939097ab95d3c9d8647e198f4ef523bf6cfb8da0ead89a57e27eff0d12ea6ac32cc118b1bc0aeb8f1be37f317f399197f9e41c8a90ecfe9e296be42e5") console.log(result.Rx) console.log(result.Ry) console.log(result.s) assert(result.Rx === "2114973053955517366033592592501464590076342821657201629830614924692550700766") assert(result.Ry === "6713953096854639492359183468711112854151280690992619923536842965423886430417") assert(result.s === "21100876117443371431735908718802018647851328087147897184613053393129281831653") console.log("testEddsaSign passed") } function test_generateKeyPair() { console.log("generateKeyPair") const seedHex = "0x" + "7f16a4c491d3494c3bc8ef097e7c123a9da6fa8167631efd5f82b89e803b0682" const seed = BigNumber.from(seedHex) console.log(`seed ${seed.toString()}`) const bitIntDataItems = bnToBufWithFixedLength(seed.toString(), 32); console.log(`bigIntData ${bitIntDataItems}`) const keyPair = EDDSAUtil.generateKeyPair(bitIntDataItems) assert(keyPair.publicKeyX === "18584920749226215196041463810216086509508234553287520526360090588893385486657") assert(keyPair.publicKeyY === "4521439321743413283487130903749053907482128013050103604539374548984130428531") console.log("generateKeyPair passed") } function main() { console.log("\n\TestsEDDSAUtil_test\n") // testEddsaPack_1() // testEddsaPack_2() // testEddsaPack_3() test_generateKeyPair() // testEddsaSign() } main(); ================================================ FILE: src/api/sign/poseidon/babyJub.ts ================================================ import { BigNumber } from "ethers"; import { SignatureScheme, bytesToHexString } from "./eddsa"; import { field } from "./field"; export class babyJub { static packPoint(P0: BigNumber, P1: BigNumber) { const packed = SignatureScheme.to_bytes(P1).reverse() // console.log("packed", packed) if (babyJub.lt(P0, BigNumber.from("0"))) { // console.log("Update .... lt ") packed[0] = packed[0] | 0x80 } const hexStr = bytesToHexString(packed) // console.log("hexStr", hexStr) return hexStr } static lt(a: BigNumber, b: BigNumber) { const half = field.SNARK_SCALAR_FIELD.div(BigNumber.from("2")) const p = field.SNARK_SCALAR_FIELD let aa: BigNumber let bb: BigNumber if (a.gt(half)) { aa = a.sub(p) } else { aa = a } if (b.gt(half)) { bb = b.sub(p) } else { bb = b } // console.log("lt", a.toString(), b.toString(), aa.toString(), bb.toString()); return aa.lt(bb) } static gt(a: BigNumber, b: BigNumber) { const half = field.SNARK_SCALAR_FIELD.div(BigNumber.from("2")) const p = field.SNARK_SCALAR_FIELD let aa: BigNumber let bb: BigNumber if (a.gt(half)) { aa = a.sub(p) } else { aa = a } if (b.gt(half)) { bb = b.sub(p) } else { bb = b } // console.log("gt", a.toString(), b.toString(), aa.toString(), bb.toString()); return aa.gt(bb) } } ================================================ FILE: src/api/sign/poseidon/eddsa.ts ================================================ /* Implements Pure-EdDSA and Hash-EdDSA The signer has two secret values: * k = Secret key * r = Per-(message,key) nonce The signer provides a signature consiting of two values: * R = Point, image of `r*B` * s = Image of `r + (k*t)` The signer provides the verifier with their public key: * A = k*B Both the verifier and the signer calculate the common reference string: * t = H(R, A, M) The nonce `r` is secret, and protects the value `s` from revealing the signers secret key. For Hash-EdDSA, the message `M` is compressed before H(R,A,M) For further information see: https://ed2519.cr.yp.to/eddsa-20150704.pdf */ import { BigNumber } from "ethers"; import { field, FQ } from "./field"; import { jubjub, Point } from "./jubjub"; import { sha512 } from "js-sha512"; import { permunation, PoseidonParams } from "./permutation"; export class Signature { public R: Point; public s: FQ; constructor(R: Point, s: FQ) { this.R = R; this.s = s; } toStr() { return `${this.R.x.n} ${this.R.y.n} ${this.s.n}`; } } export class SignedMessage { public A: Point; public sig: Signature; public msg: BigNumber; constructor(A: Point, sig: Signature, msg: BigNumber) { this.A = A; this.sig = sig; this.msg = msg; } toStr() { return `${this.A.x.n} ${ this.A.y.n } ${this.sig.toStr()} ${this.msg.toString()}`; } } export class SignatureScheme { static to_bytes(arg: BigNumber) { const outputLength = 32; // console.log(`input ${arg.toString()}`) let bitIntDataItems = bnToBuf(arg.toString()); // console.log(`bigIntData ${bitIntDataItems}`) const more = outputLength - bitIntDataItems.length; // console.log('more', more) if (more > 0) { for (let i = 0; i < more; i++) { bitIntDataItems = [0].concat(bitIntDataItems); } } else { bitIntDataItems = bitIntDataItems.slice(0, outputLength); } bitIntDataItems = bitIntDataItems.reverse(); // console.log(`bigIntData return ${bitIntDataItems}`) return bitIntDataItems; } /* Identity function for message Can be used to truncate the message before hashing it as part of the public parameters. */ static prehash_message(M: BigNumber) { return M; } /* Hash the key and message to create `r`, the blinding factor for this signature. If the same `r` value is used more than once, the key for the signature is revealed. From: https://eprint.iacr.org/2015/677.pdf (EdDSA for more curves) Page 3: (Implementation detail: To save time in the computation of `rB`, the signer can replace `r` with `r mod L` before computing `rB`.) */ static hash_secret_python(k: FQ, arg: BigNumber) { const byteArray0 = this.to_bytes(k.n); const byteArray1 = this.to_bytes(arg); const sum = byteArray0.concat(byteArray1); // console.log("sum", sum) // let byteArrayHexStr = bytesToHexString(sum) // console.log("byteArrayHexStr", byteArrayHexStr) const digest1 = sha512.array(new Uint8Array(sum).buffer); // let digest1 = createHash('sha512').update .digest("SHA-512", new Uint8Array(sum).buffer) let sha512StrItems: any; // console.log('digest1', digest1); for (let i = 0; i < digest1.length; i++) { const itemInt = digest1[i]; let st = itemInt.toString(16); if (st.length == 1) { st = "0" + st; } sha512StrItems = [st].concat(sha512StrItems); } const sha512MessageHexStr = sha512StrItems.join(""); // console.log(`sha512MessageHexStr ${sha512MessageHexStr}`) const sha512MessageHexStrBigInt = BigNumber.from( "0x" + sha512MessageHexStr ); // console.log(`sha512MessageHexStrBigInt ${sha512MessageHexStrBigInt}`) const hashed = sha512MessageHexStrBigInt.mod(jubjub.JUBJUB_L); // console.log(`hashed ${hashed.toString()}`) return hashed; } static B() { return Point.generate(); } static sign(msg: BigNumber, key: FQ, B: Point) { // console.log("B ", B.x.n.toString(), B.y.n.toString()) const copyKey = new FQ(key.n, key.m); const A = B.mul(copyKey.n); // console.log("A.x ", A.x.n.toString(), A.x.m.toString()) // console.log("A.y ", B.y.n.toString(), A.y.m.toString()) const M = this.prehash_message(msg); // console.log("M ", M.toString()) const r = this.hash_secret_python(key, M); // console.log("r ", r.toString()) const copy_r = BigNumber.from(r.toString()); const R = B.mul(copy_r); // console.log("R.x ", R.x.n.toString(), R.x.m.toString()) // console.log("R.y ", R.y.n.toString(), R.y.m.toString()) const t = this.hash_public(R, A, M); // console.log("hello world") // console.log("t ", t.toString()) const t_c = t; const key_n_t = key.n.mul(t_c); const left = r.add(key_n_t); const S = left.mod(jubjub.JUBJUB_E); // console.log("S ", S.toString()) const signatureResult = new Signature(R, new FQ(S)); // console.log("signatureResult", signatureResult.toStr()) const signedMessage = new SignedMessage(A, signatureResult, msg); // console.log("signedMessage", signedMessage.toStr()) return signedMessage; } static as_scalar(point: Point) { // console.log(`as_scalar ${point.x.n.toString()}`) return [point.x.n, point.y.n]; } static hash_public(R: Point, A: Point, M: BigNumber) { let inputMsg: any; inputMsg = this.as_scalar(R).concat(this.as_scalar(A)).concat([M]); // console.log(`inputMsg ${inputMsg}`) const params = new PoseidonParams( field.SNARK_SCALAR_FIELD, 6, 6, 52, "poseidon", BigNumber.from(5), null, null, 128 ); const result = permunation.poseidon(inputMsg, params); return result; } } export function bnToBuf(bn: string) { let hex = BigInt(bn).toString(16); if (hex.length % 2) { hex = "0" + hex; } const len = hex.length / 2; // console.log("length", len); const u8 = new Uint8Array(len); let i = 0; let j = 0; while (i < len) { u8[i] = parseInt(hex.slice(j, j + 2), 16); i += 1; j += 2; } return Array.from(u8); } export function bnToBufWithFixedLength(bn: string, outputLength: number) { let hex = BigInt(bn).toString(16); if (hex.length % 2) { hex = "0" + hex; } const len = hex.length / 2; // console.log("len", len); const u8 = new Uint8Array(len); let i = 0; let j = 0; while (i < len) { u8[i] = parseInt(hex.slice(j, j + 2), 16); i += 1; j += 2; } let bitIntDataItems = Array.from(u8); const more = outputLength - bitIntDataItems.length; // console.log('diff len', more) if (more > 0) { for (let i = 0; i < more; i++) { bitIntDataItems = [0].concat(bitIntDataItems); } } else { bitIntDataItems = bitIntDataItems.slice(0, outputLength); } return bitIntDataItems; } export function bufToBn(buf: any) { let hex: any; hex = []; const u8 = Uint8Array.from(buf); u8.forEach(function (i) { let h = i.toString(16); if (h.length % 2) { h = "0" + h; } hex.push(h); }); return BigInt("0x" + hex.join("")); } export function bytesToHexString(bytes: any) { let strItems: any; strItems = []; for (let i = 0; i < bytes.length; i++) { const item = bytes[i]; let st = item.toString(16); if (st.length == 1) { st = "0" + st; } // st = st.toUpperCase() strItems.push(st); } const strItemsJoined = strItems.join(""); return strItemsJoined; } ================================================ FILE: src/api/sign/poseidon/eddsa_test.ts ================================================ import { assert } from "console" import { BigNumber } from "ethers" import { SignatureScheme } from "./eddsa" import { FQ } from "./field" import { Point } from "./jubjub" function test_sign_1() { // console.log("test_sign_1") const startTime: any = new Date(); const msgHash = BigNumber.from("20693456676802104653139582814194312788878632719314804297029697306071204881418") const private_key = new FQ(BigNumber.from("1")) const signed = SignatureScheme.sign(msgHash, private_key, SignatureScheme.B()) assert(signed.toStr() === "16540640123574156134436876038791482806971768689494387082833631921987005038935 20819045374670962167435360035096875258406992893633759881276124905556507972311 4991609103248925747358645194965349262579784734809679007552644294476920671344 423391641476660815714427268720766993055332927752794962916609674122318189741 4678160339597842896640121413028167917237396460457527040724180632868306529961 20693456676802104653139582814194312788878632719314804297029697306071204881418") const endTime: any = new Date(); let timeDiff = endTime - startTime; //in ms timeDiff /= 1000; // console.log(timeDiff + " seconds"); // console.log("test_sign_1 passed") } function test_prehash_message_1() { // console.log("test_prehash_message_1") const msg = BigNumber.from("20693456676802104653139582814194312788878632719314804297029697306071204881418") const result = SignatureScheme.prehash_message(msg) assert(result.eq(BigNumber.from("20693456676802104653139582814194312788878632719314804297029697306071204881418"))) // console.log("test_prehash_message_1 passed") } function test_to_bytes_1() { // console.log("test_to_bytes_1") const arg = BigNumber.from("20693456676802104653139582814194312788878632719314804297029697306071204881418") const resutls = SignatureScheme.to_bytes(arg) assert(JSON.stringify(resutls) === JSON.stringify([10, 228, 215, 147, 146, 102, 9, 42, 66, 160, 26, 94, 171, 73, 235, 194, 245, 106, 249, 114, 50, 52, 155, 182, 188, 18, 133, 216, 215, 20, 192, 45])) // console.log("test_to_bytes_1 passed") } function test_hash_secret_1() { // console.log("test_hash_secret_1") const startTime: any = new Date(); const arg = BigNumber.from("20693456676802104653139582814194312788878632719314804297029697306071204881418") const result = SignatureScheme.hash_secret_python(new FQ(BigNumber.from("1")), arg) assert(result.eq(BigNumber.from("456425617452149303537516185998917840598824274191970480768523181450944242406"))) const endTime: any = new Date(); let timeDiff = endTime - startTime; //in ms timeDiff /= 1000; // console.log(timeDiff + " seconds"); // strip the ms // console.log("test_hash_secret_1 passed") } function test_hash_public_1() { // console.log("test_hash_public_1") const R = new Point(new FQ(BigNumber.from("4991609103248925747358645194965349262579784734809679007552644294476920671344")), new FQ(BigNumber.from("423391641476660815714427268720766993055332927752794962916609674122318189741"))) const A = new Point(new FQ(BigNumber.from("16540640123574156134436876038791482806971768689494387082833631921987005038935")), new FQ(BigNumber.from("20819045374670962167435360035096875258406992893633759881276124905556507972311"))) const M = BigNumber.from("20693456676802104653139582814194312788878632719314804297029697306071204881418") const result = SignatureScheme.hash_public(R, A, M) assert(result.eq(BigNumber.from("4221734722145693593102605227029250076638572186265556559955657451417362287555"))) // console.log("test_hash_public_1 passed") } function main() { // console.log("\n\neddsa_test\n") test_sign_1() test_prehash_message_1() test_to_bytes_1() test_hash_secret_1() test_hash_public_1() } main(); ================================================ FILE: src/api/sign/poseidon/field.ts ================================================ import { BigNumber } from "ethers"; import { BigInteger } from "jsbn"; export class field { // Fq is the base field of Jubjub static SNARK_SCALAR_FIELD = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617") // Fr is the scalar field of Jubjub static FR_ORDER = BigNumber.from("21888242871839275222246405745257275088614511777268538073601725287587578984328") } // A class for field elements in FQ. Wrap a number in this class, // and it becomes a field element. export class FQ { public m: BigNumber; public n: BigNumber; constructor(n: BigNumber, field_modulus = field.SNARK_SCALAR_FIELD) { this.m = field_modulus; this.n = n.mod(this.m); } // // Use this.n as other // add(other: BigNumber) { const on = other const n = (this.n.add(on)).mod(this.m) return new FQ(n, this.m) } mul(other: BigNumber) { const on = other const n = this.n.mul(on).mod(this.m) return new FQ(n, this.m) } sub(other: BigNumber) { const on = other let new_n: BigNumber; if (this.n.gte(on)) { new_n = (this.n.sub(on)).mod(this.m) } else { new_n = (this.n.sub(on).add(this.m)).mod(this.m) } return new FQ(new_n, this.m) } div(other: BigNumber) { const on_c = other const m_c = this.m const two_c = BigNumber.from("2") const on_power_c = modulo(on_c, m_c.sub(two_c), m_c) const n_on_power_remainder = this.n.mul(on_power_c).mod(this.m) return new FQ(n_on_power_remainder, this.m) } static one(modulus: BigNumber = field.SNARK_SCALAR_FIELD) { return new FQ(BigNumber.from("1"), modulus) } static zero(modulus: BigNumber = field.SNARK_SCALAR_FIELD) { return new FQ(BigNumber.from("0"), modulus) } } export function modulo(n: BigNumber, p: BigNumber, m: BigNumber) { const n_ = new BigInteger(n.toString()) const p_ = new BigInteger(p.toString()) const m_ = new BigInteger(m.toString()) // console.log("modulo", n_.toString(), p_.toString(), m_.toString()); const result = n_.modPow(p_, m_) // console.log(n_.toString(), p_.toString(), m_.toString(), result.toString()) return BigNumber.from(result.toString()) } ================================================ FILE: src/api/sign/poseidon/field_test.ts ================================================ import { assert } from "console"; import { BigNumber } from "ethers"; import { field, FQ, modulo } from "./field"; function test_constants() { // console.log("test_constants") assert(field.SNARK_SCALAR_FIELD.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) assert(field.FR_ORDER.eq(BigNumber.from("21888242871839275222246405745257275088614511777268538073601725287587578984328"))) // console.log("test_constants passed") } function test_half() { // console.log("test_half") const half = BigNumber.from("10944121435919637611123202872628637544274182200208017171849102093287904247808") assert(field.SNARK_SCALAR_FIELD.div(2).eq(half)) // console.log("test_half passed") } function test_p() { // console.log("test_p") const p = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617") assert(field.SNARK_SCALAR_FIELD.eq(p)) // console.log("test_p passed") } function test_FQ_1() { // console.log("test_FQ_1") const n = BigNumber.from("1") const privateKey = new FQ(n) assert(privateKey.n.eq(BigNumber.from("1"))) assert(privateKey.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) // console.log("test_FQ_1 passed") } function test_FQ_add_1() { // console.log("test_FQ_add_1") const field1 = new FQ(BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const field2 = new FQ(BigNumber.from("64019726205844806607227168444173457603185468776494125031546307012808629654"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const result1 = field1.add(field2.n) const result2 = field2.add(field1.n) assert(result1.n.eq(result2.n)) assert(result1.m.eq(result2.m)) // console.log("test_FQ_add_1 passed") } function test_FQ_mul_1() { // console.log("test_FQ_mul_1") const other = BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822") const field = new FQ(BigNumber.from("8023312754331632317345164874475855606161388395970421403351236980717209379200"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const result = field.mul(other) assert(result.n.eq("7078307911818432186422689430568175567157289995259698798344014234848622444761")) assert(result.m.eq("21888242871839275222246405745257275088548364400416034343698204186575808495617")) // console.log("test_FQ_mul_1 passed") } function test_FQ_mul_2() { // console.log("test_FQ_mul_2") const other = BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822") const field = new FQ(BigNumber.from("64019726205844806607227168444173457603185468776494125031546307012808629654"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const result = field.mul(other) assert(result.n.eq("8023312754331632317345164874475855606161388395970421403351236980717209379200")) assert(result.m.eq("21888242871839275222246405745257275088548364400416034343698204186575808495617")) // console.log("test_FQ_mul_2 passed") } function test_FQ_mul_3() { // console.log("test_FQ_mul_3") const field1 = new FQ(BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const field2 = new FQ(BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const result1 = field1.add(field2.n) const result2 = field2.add(field1.n) assert(result1.n.eq(result2.n)) assert(result1.m.eq(result2.m)) // console.log("test_FQ_mul_3 passed") } function test_FQ_mul_4() { // console.log("test_FQ_mul_4") const field1 = new FQ(BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const field2 = new FQ(BigNumber.from("64019726205844806607227168444173457603185468776494125031546307012808629654"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const field3 = new FQ(BigNumber.from("8023312754331632317345164874475855606161388395970421403351236980717209379200"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const result1 = field1.mul(field2.n).mul(field3.n) const result2 = field2.mul(field1.n).mul(field3.n) const result3 = field3.mul(field1.n).mul(field2.n) const result4 = field3.mul(field2.n).mul(field1.n) assert(result1.n.eq(result2.n)) assert(result1.m.eq(result2.m)) assert(result1.n.eq(result3.n)) assert(result1.m.eq(result3.m)) assert(result1.n.eq(result4.n)) assert(result1.m.eq(result4.m)) // console.log("test_FQ_mul_4 passed") } function test_FQ_module() { // console.log("test_FQ_module") const n = BigNumber.from("-14716512740585223458018047979811180521493135094147670344522878941283115968141") const m = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617") const n_m = n.add(m).mod(m) assert(n_m.eq(BigNumber.from("7171730131254051764228357765446094567055229306268363999175325245292692527476"))) // console.log("test_FQ_module passed") } function test_FQ_sub() { // console.log("test_FQ_sub") const field = new FQ(BigNumber.from("397593468802287547516812218524989251246873285051017590397180750166966730405"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const other = BigNumber.from("15114106209387511005534860198336169772740008379198687934920059691450082698546") // console.log("test_FQ_sub passed") } function test_modulo() { console.log("test_modulo") const n = BigNumber.from("15511699678189798553051672416861159023986881664249705525317240441617049428951") const p = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495615") const m = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617") const on_power_c = modulo(n, p, m) console.log("on_power_c", on_power_c.toString()) assert(on_power_c.toString() === "5307867014497036273760343161114758635681413365004069877684017182635568142756") } function main() { // console.log("\n\nfield_test\n") // test_constants() // test_half() // test_p() // test_FQ_1() // test_FQ_add_1() // test_FQ_mul_1() // test_FQ_mul_2() // test_FQ_mul_3() // test_FQ_mul_4() // test_FQ_module() // test_FQ_sub() test_modulo() } main(); ================================================ FILE: src/api/sign/poseidon/jubjub.ts ================================================ /* This module implements the extended twisted edwards and extended affine coordinates described in the paper "Twisted Edwards Curves Revisited": - https://iacr.org/archive/asiacrypt2008/53500329/53500329.pdf Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson Information Security Institute, Queensland University of Technology, QLD, 4000, Australia {h.hisil, kk.wong, g.carter, e.dawson}@qut.edu.au By using the extended coordinate system we can avoid expensive modular exponentiation calls, for example - a scalar multiplication call (or multiple...) may perform only one 3d->2d projection at the point where affine coordinates are necessary, and every intermediate uses a much faster form. # XXX: none of these functions are constant time, they should not be used interactively! */ import { BigNumber } from "ethers"; import { field, FQ } from "./field"; export class jubjub { static JUBJUB_Q = field.SNARK_SCALAR_FIELD static JUBJUB_E = BigNumber.from("21888242871839275222246405745257275088614511777268538073601725287587578984328") static JUBJUB_C = BigNumber.from("8") // Cofactor static JUBJUB_L = jubjub.JUBJUB_E.div(jubjub.JUBJUB_C) // L*B = 0, and (2^C)*L == #E static JUBJUB_A = BigNumber.from("168700") // Coefficient A static JUBJUB_D = BigNumber.from("168696") // Coefficient D } export class Point { public x: FQ public y: FQ constructor(x: FQ, y: FQ) { this.x = x this.y = y } static generate() { const xBigInt = BigNumber.from("16540640123574156134436876038791482806971768689494387082833631921987005038935") const yBigInt = BigNumber.from("20819045374670962167435360035096875258406992893633759881276124905556507972311") const point = new Point(new FQ(xBigInt), new FQ(yBigInt)) return point } mul(scaler: BigNumber) { let p = new Point(this.x, this.y) let a = Point.infinity() let i = 0 while (!scaler.eq(BigNumber.from("0"))) { const bitwiseAnd = scaler.and(BigNumber.from("1")) if (!bitwiseAnd.eq(BigNumber.from("0"))) { a = a.add(p) } let copyP1 = new Point(p.x, p.y) let copyP2 = new Point(p.x, p.y) p = copyP1.add(copyP2) scaler = scaler.div(BigNumber.from("2")) // console.log(i + " scaler", scaler.toString()) i = i + 1 } return a } add(other: Point) { if (this.x.n.eq(BigNumber.from("0")) && this.y.n.eq(BigNumber.from("0"))) { return other } const u1 = this.x const v1 = this.y const u2 = other.x const v2 = other.y const u3_tmp0 = (u1.mul(v2.n)).add(v1.mul(u2.n).n) const u3_tmp1 = u1.mul(u2.n).mul(v1.n).mul(v2.n).mul(jubjub.JUBJUB_D) const u3_tmp2 = FQ.one().add(u3_tmp1.n) const u3 = u3_tmp0.div(u3_tmp2.n) const v3_tmp0 = v1.mul(v2.n) const v3_tmp1 = u1.mul(u2.n).mul(jubjub.JUBJUB_A) const v3_tmp3 = v3_tmp0.sub(v3_tmp1.n) const v3_tmp5 = FQ.one().sub(u3_tmp1.n) const v3 = v3_tmp3.div(v3_tmp5.n) return new Point(u3, v3) } static infinity() { return new Point(new FQ(BigNumber.from("0")), new FQ(BigNumber.from("1"))) } } ================================================ FILE: src/api/sign/poseidon/jubjub_test.ts ================================================ import { assert } from "console"; import { BigNumber } from "ethers"; import { FQ } from "./field"; import { jubjub, Point } from "./jubjub"; function test_constants() { // console.log("test_constants") assert(jubjub.JUBJUB_Q.eq("21888242871839275222246405745257275088548364400416034343698204186575808495617")) assert(jubjub.JUBJUB_E.eq("21888242871839275222246405745257275088614511777268538073601725287587578984328")) assert(jubjub.JUBJUB_C.eq("8")) assert(jubjub.JUBJUB_L.eq("2736030358979909402780800718157159386076813972158567259200215660948447373041")) assert(jubjub.JUBJUB_A.eq("168700")) assert(jubjub.JUBJUB_D.eq("168696")) // console.log("test_constants passed") } function test_add_1() { // console.log("test_add_1") const point = new Point(new FQ(BigNumber.from("5925710879559963920674585068280151559572021649049974518737186312396312983287")), new FQ(BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822"))) const other = new Point(new FQ(BigNumber.from("5925710879559963920674585068280151559572021649049974518737186312396312983287")), new FQ(BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822"))) const sum = point.add(other) assert(sum.x.n.eq(BigNumber.from("3921821752680400551661691533275335336907961697969280331905459386565873550491"))) assert(sum.x.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) assert(sum.y.n.eq(BigNumber.from("8522068897570808837785568881356377871354274006792075192589502922612862896342"))) assert(sum.y.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) // console.log("test_add_1 passed") } function test_add_2() { // console.log("test_add_2") const point = new Point(new FQ(BigNumber.from("10975113445185536695224737904225227344281568447400334915125839333792816477396")), new FQ(BigNumber.from("18445435810976842694581549336952093637971779711294581156054437925992025486446"))) const other = new Point(new FQ(BigNumber.from("5925710879559963920674585068280151559572021649049974518737186312396312983287")), new FQ(BigNumber.from("16975020951829843291561856284829257584634286376639034318405002894754175986822"))) const sum = point.add(other) assert(sum.x.n.eq(BigNumber.from("4991609103248925747358645194965349262579784734809679007552644294476920671344"))) assert(sum.x.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) assert(sum.y.n.eq(BigNumber.from("423391641476660815714427268720766993055332927752794962916609674122318189741"))) assert(sum.y.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) // console.log("test_add_2 passed") } function test_divide_1() { // console.log("test_divide_1") const scaler = BigNumber.from("1") const result = scaler.div(2) assert(result.eq(BigNumber.from("0"))) // console.log("test_divide_1 passed") } function test_divide_2() { // console.log("test_divide_2") const scaler = BigNumber.from("2") const result = scaler.div(2) assert(result.eq(BigNumber.from("1"))) // console.log("test_divide_2 passed") } function test_bitwiseand_1() { // console.log("test_bitwiseand_1") const scaler_c = BigNumber.from("1") const result_c = scaler_c.and(BigNumber.from(1)) assert(result_c.eq(BigNumber.from("1"))) // console.log("test_bitwiseand_1 passed") } function test_bitwiseand_2() { // console.log("test_bitwiseand_2") const scaler_c = BigNumber.from("2") const result_c = scaler_c.and(BigNumber.from(1)) assert(result_c.eq(BigNumber.from("0"))) // console.log("test_bitwiseand_2 passed") } function test_mul_1() { // console.log("test_mul_1") const B = new Point(new FQ(BigNumber.from("16540640123574156134436876038791482806971768689494387082833631921987005038935")), new FQ(BigNumber.from("20819045374670962167435360035096875258406992893633759881276124905556507972311"))) const k = BigNumber.from("1") const A = B.mul(k) assert(A.x.n.eq(BigNumber.from("16540640123574156134436876038791482806971768689494387082833631921987005038935"))) assert(A.x.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) assert(A.y.n.eq(BigNumber.from("20819045374670962167435360035096875258406992893633759881276124905556507972311"))) assert(A.y.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) // console.log("test_mul_1 passed") } function test_mul_2() { // console.log("test_mul_2") const B = new Point(new FQ(BigNumber.from("16540640123574156134436876038791482806971768689494387082833631921987005038935")), new FQ(BigNumber.from("20819045374670962167435360035096875258406992893633759881276124905556507972311"))) const k = BigNumber.from("2") const A = B.mul(k) assert(A.x.n.eq(BigNumber.from("17324563846726889236817837922625232543153115346355010501047597319863650987830"))) assert(A.x.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) assert(A.y.n.eq(BigNumber.from("20022170825455209233733649024450576091402881793145646502279487074566492066831"))) assert(A.y.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617"))) // console.log("test_mul_2 passed") } function test_mul_3() { // console.log("test_mul_3") const x = new FQ(BigNumber.from("16540640123574156134436876038791482806971768689494387082833631921987005038935"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const y = new FQ(BigNumber.from("20819045374670962167435360035096875258406992893633759881276124905556507972311"), BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")) const B = new Point(x, y) const k = BigNumber.from("456425617452149303537516185998917840598824274191970480768523181450944242406") const A = B.mul(k) assert(A.x.n.eq(BigNumber.from("4991609103248925747358645194965349262579784734809679007552644294476920671344")), "A.x.n" + A.x.n.toString()) assert(A.x.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")), "A.x.m" + A.x.m.toString()) assert(A.y.n.eq(BigNumber.from("423391641476660815714427268720766993055332927752794962916609674122318189741")), "A.y.n" + A.y.n.toString()) assert(A.y.m.eq(BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617")), "A.y.m" + A.y.m.toString()) // console.log("test_mul_3 passed") } function main() { // console.log("hello") test_constants() test_add_1() test_add_2() test_divide_1() test_divide_2() test_bitwiseand_1() test_bitwiseand_2() test_mul_1() test_mul_2() test_mul_3() } main(); ================================================ FILE: src/api/sign/poseidon/permutation.ts ================================================ /* Implements the Poseidon permutation: Starkad and Poseidon: New Hash Functions for Zero Knowledge Proof Systems - Lorenzo Grassi, Daniel Kales, Dmitry Khovratovich, Arnab Roy, Christian Rechberger, and Markus Schofnegger - https://eprint.iacr.org/2019/458.pdf Other implementations: - https://github.com/shamatar/PoseidonTree/ - https://github.com/iden3/circomlib/blob/master/src/poseidon.js - https://github.com/dusk-network/poseidon252 */ import { BigNumber } from 'ethers' import { SignatureScheme } from './eddsa' import { modulo } from './field' import { TextEncoder } from 'web-encoding' import blake2b from 'blake2b' export class PoseidonParams { public p: BigNumber public t: number public nRoundsF: number public nRoundsP: number public seed: string public e: BigNumber public constants_C: [BigNumber] public constants_M: [[BigNumber]] public security_target: number constructor( p: BigNumber, t: number, nRoundsF: number, nRoundsP: number, seed: string, e: BigNumber, constants_C: [BigNumber] | null, constants_M: [[BigNumber]] | null, security_target: number, ) { this.p = p this.t = t this.nRoundsF = nRoundsF this.nRoundsP = nRoundsP this.seed = seed this.e = e if (constants_C == null) { this.constants_C = permunation.poseidon_constants(p, `${seed}_constants`, nRoundsF + nRoundsP) } else { this.constants_C = constants_C } if (constants_M == null) { this.constants_M = permunation.poseidon_matrix(p, `${seed}_matrix_0000`, t) } else { this.constants_M = constants_M } this.security_target = security_target } } export class permunation { static H(arg: string) { const outputLength = 32 const enc = new TextEncoder() const message = enc.encode(arg) // console.log(`message ${message}`) const buf = Buffer.alloc(outputLength) // console.log(`hashOfSize32Bytes ${buf.toString()}`) // console.log(`message ${message}`) // @ts-ignore blake2b(buf.length, undefined).update(message).final(buf) const items = buf.toJSON().data // console.log(`H items ${items}`) let sum = BigNumber.from('0') var i = 0 for (var i = 0; i < items.length; i++) { const itemBigInt = BigNumber.from(items[i]) const tmp = itemBigInt.mul(BigNumber.from('256').pow(BigNumber.from(i))) sum = sum.add(tmp) } // console.log(`sum ${sum}`) return sum } static H_Bigint(arg: BigNumber) { const outputLength = 32 const message = new Uint8Array(SignatureScheme.to_bytes(arg)) // console.log(`message ${message}`) const buf = Buffer.alloc(outputLength) // console.log(`hashOfSize32Bytes ${buf.toString()}`) // @ts-ignore blake2b(buf.length, undefined).update(message).final(buf) const items = buf.toJSON().data // console.log(`H_Bigint items ${items}`) let sum = BigNumber.from('0') var i = 0 for (var i = 0; i < items.length; i++) { const itemBigInt = BigNumber.from(items[i]) const tmp = itemBigInt.mul(BigNumber.from('256').pow(BigNumber.from(i))) sum = sum.add(tmp) } // console.log(`sum ${sum}`) return sum } static poseidon_constants(p: BigNumber, seed: string, n: number) { let c: any c = [] let seedBigInt = this.H(seed) const result = seedBigInt.mod(p) c.push(result) for (let i = 0; i < n - 1; i++) { seedBigInt = this.H_Bigint(seedBigInt) const result = seedBigInt.mod(p) c.push(result) } return c } static poseidon_matrix(p: BigNumber, seed: string, t: number) { const c = this.poseidon_constants(p, seed, t * 2) let matrix: any matrix = [] for (let i = 0; i < t; i++) { let row: any row = [] for (let j = 0; j < t; j++) { const c_i = c[i] const c_t_j = c[t + j] const p_c = p const c_t_j_p = c_t_j.mod(p_c) const left = c_i.sub(c_t_j_p) const p_2 = p_c.sub(2) const item_c = modulo(left, p_2, p_c) row.push(item_c) } matrix.push(row) } return matrix } static poseidon_sbox(state: [BigNumber], i: number, params: PoseidonParams) { /* iacr.org/2019/458 § 2.2 The Hades Strategy (pg 6) In more details, assume R_F = 2 · R_f is an even number. Then - the first R_f rounds have a full S-Box layer, - the middle R_P rounds have a partial S-Box layer (i.e., 1 S-Box layer), - the last R_f rounds have a full S-Box layer */ const half_F = params.nRoundsF / 2 if (i < half_F || i >= half_F + params.nRoundsP) { for (let j = 0; j < state.length; j++) { const element_c = state[j] const e_c = params.e const p_c = params.p const item = modulo(element_c, e_c, p_c) state[j] = item } } else { const element_c = state[0] const e_c = params.e const p_c = params.p const item = modulo(element_c, e_c, p_c) state[0] = item } return state } static poseidon_mix(state: [BigNumber], M: [[BigNumber]], p: BigNumber) { /* The mixing layer is a matrix vector product of the state with the mixing matrix - https://mathinsight.org/matrix_vector_multiplication */ let newState: any newState = [] for (let i = 0; i < M.length; i++) { let sum = BigNumber.from(0) for (let j = 0; j < state.length; j++) { const element = state[j] sum = sum.add(M[i][j].mul(element)) } newState.push(sum.mod(p)) } return newState } // poseidon /* Main instansiation of the Poseidon permutation The state is `t` elements wide, there are `F` full-rounds followed by `P` partial rounds, then `F` full rounds again. [ ARK ] --, | | | | | | | [ SBOX ] - Full Round | | | | | | | [ MIX ] --` [ ARK ] --, | | | | | | | [ SBOX ] - Partial Round | | Only 1 element is substituted in partial round [ MIX ] --` There are F+P rounds for the full permutation. You can provide `r = N - 2s` bits of input per round, where `s` is the desired security level, in most cases this means you can provide `t-1` inputs with appropriately chosen parameters. The permutation can be 'chained' together to form a sponge construct. */ static poseidon(inputs: [BigNumber], params: PoseidonParams) { let state: any state = [] state = state.concat(inputs) // console.log(`state ${state}`) for (var i = 0; i < params.t - inputs.length; i++) { state.push(BigNumber.from(0)) } // console.log(`state ${state}`) // console.log(`params.constants_C.length ${params.constants_C.length}`) for (var i = 0; i < params.constants_C.length; i++) { const C_i = params.constants_C[i] for (let index = 0; index < state.length; index++) { const element = state[index] state[index] = element.add(C_i) } state = this.poseidon_sbox(state, i, params) // console.log(`after poseidon_sbox ${state}`) state = this.poseidon_mix(state, params.constants_M, params.p) // console.log(`after poseidon_mix ${state}`) } // console.log(`hash is ${state[0]}`) return state[0] } } ================================================ FILE: src/api/sign/poseidon/permutation_test.ts ================================================ import { assert } from "console"; import { BigNumber } from "ethers"; import { field } from "./field"; import { permunation, PoseidonParams } from "./permutation"; function test_PoseidonParams() { // console.log("test_PoseidonParams") const p = field.SNARK_SCALAR_FIELD const t = 6 const nRoundsF = 8 const nRoundsP = 57 const seed = "poseidon" const e = BigNumber.from(5) const security_target = 126 const poseidonParams = new PoseidonParams(p, t, nRoundsF, nRoundsP, seed, e, null, null, security_target) // console.log(`poseidonParams ${poseidonParams.constants_C}`) // @ts-ignore assert(poseidonParams.constants_C[0].eq(BigNumber.from("14397397413755236225575615486459253198602422701513067526754101844196324375522")), "poseidonParams.constants_C[0]") // @ts-ignore assert(poseidonParams.constants_C[1].eq(BigNumber.from("10405129301473404666785234951972711717481302463898292859783056520670200613128")), "poseidonParams.constants_C[2]") // @ts-ignore assert(poseidonParams.constants_C[63].eq(BigNumber.from("14423660424692802524250720264041003098290275890428483723270346403986712981505")), "poseidonParams.constants_C[3]") // @ts-ignore assert(poseidonParams.constants_C[64].eq(BigNumber.from("10635360132728137321700090133109897687122647659471659996419791842933639708516")), "poseidonParams.constants_C[64]") // @ts-ignore assert(poseidonParams.constants_M[0][0].eq(BigNumber.from("19167410339349846567561662441069598364702008768579734801591448511131028229281")), "poseidonParams.constants_M[0][0]") // @ts-ignore assert(poseidonParams.constants_M[0][1].eq(BigNumber.from("14183033936038168803360723133013092560869148726790180682363054735190196956789")), "poseidonParams.constants_M[0][1]") // @ts-ignore assert(poseidonParams.constants_M[0][2].eq(BigNumber.from("9067734253445064890734144122526450279189023719890032859456830213166173619761")), "poseidonParams.constants_M[0][2]") // @ts-ignore assert(poseidonParams.constants_M[5][3].eq(BigNumber.from("8035240799672199706102747147502951589635001418759394863664434079699838251138")), "poseidonParams.constants_M[0][0]") // @ts-ignore assert(poseidonParams.constants_M[5][4].eq(BigNumber.from("21642389080762222565487157652540372010968704000567605990102641816691459811717")), "poseidonParams.constants_M[0][1]") // @ts-ignore assert(poseidonParams.constants_M[5][5].eq(BigNumber.from("20261355950827657195644012399234591122288573679402601053407151083849785332516")), "poseidonParams.constants_M[0][2]") // console.log("test_PoseidonParams passed") } function test_H_1() { console.log("test_H_1") const arg = "poseidon_constants" const hash = permunation.H(arg) assert(hash.eq(BigNumber.from("80062126029273061892314832722231078464247515902761170557848714403923749862373"))) console.log("test_H_1 passed") } function test_H_2() { console.log("test_H_2") const arg = "poseidon_matrix_0000" const hash = permunation.H(arg) assert(hash.eq(BigNumber.from("14132513739920849383792069751007754351800355055139761101807090020635929082500"))) console.log("test_H_2 passed") } function test_H_3() { console.log("test_H_3") const arg = BigNumber.from("14132513739920849383792069751007754351800355055139761101807090020635929082500") const hash = permunation.H_Bigint(arg) assert(hash.eq(BigNumber.from("2944673226682481007627110343206629017840128596422012786319796010373889882365"))) console.log("test_H_3 passed") } function test_poseidon_constants() { // console.log("test_poseidon_constants") const p = field.SNARK_SCALAR_FIELD const seed = "poseidon_constants" const n = 65 const constants_C = permunation.poseidon_constants(p, seed, n) assert(constants_C[0].eq(BigNumber.from("14397397413755236225575615486459253198602422701513067526754101844196324375522"))) assert(constants_C[1].eq(BigNumber.from("10405129301473404666785234951972711717481302463898292859783056520670200613128"))) assert(constants_C[2].eq(BigNumber.from("5179144822360023508491245509308555580251733042407187134628755730783052214509"))) assert(constants_C[63].eq(BigNumber.from("14423660424692802524250720264041003098290275890428483723270346403986712981505"))) assert(constants_C[64].eq(BigNumber.from("10635360132728137321700090133109897687122647659471659996419791842933639708516"))) // console.log("test_poseidon_constants passed") } function test_poseidon_matrix() { console.log("test_poseidon_matrix") const p = field.SNARK_SCALAR_FIELD const seed = "poseidon_matrix_0000" const t = 9 const constants_M = permunation.poseidon_matrix(p, seed, t) assert(constants_M[0][0].eq(BigNumber.from("16378664841697311562845443097199265623838619398287411428110917414833007677155"))) assert(constants_M[0][1].eq(BigNumber.from("12968540216479938138647596899147650021419273189336843725176422194136033835172"))) assert(constants_M[0][2].eq(BigNumber.from("3636162562566338420490575570584278737093584021456168183289112789616069756675"))) assert(constants_M[1][3].eq(BigNumber.from("8642889650254799419576843603477253661899356105675006557919250564400804756641"))) assert(constants_M[8][8].eq(BigNumber.from("11398590172899810645820530606484864595574598270604175688862890426075002823331"))) console.log("test_poseidon_matrix passed") } function test_poseidon_1() { console.log("test_poseidon_1") const p = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617") const MAX_INPUT = 8 const poseidonParams = new PoseidonParams(p, MAX_INPUT+1, 6, 53, "poseidon", BigNumber.from(5), null, null, 128) let intputs: any intputs = [] intputs.push(BigNumber.from(1)) const state0 = permunation.poseidon(intputs, poseidonParams) assert(state0.eq(BigNumber.from("20640057815290657586474256351705900781103272844170318852926520138679251832017"))) console.log("test_poseidon_1 passed") } function test_poseidon_2() { console.log("test_poseidon_2") const p = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617") const MAX_INPUT = 8 const poseidonParams = new PoseidonParams(p, MAX_INPUT+1, 6, 53, "poseidon", BigNumber.from(5), null, null, 128) let intputs: any intputs = [] intputs.push(BigNumber.from(1)) intputs.push(BigNumber.from(2)) const state0 = permunation.poseidon(intputs, poseidonParams) assert(state0.eq(BigNumber.from("9251914430137119038619835991672259215400712688203929648293348181354900386736"))) console.log("test_poseidon_2 passed") } function test_poseidon_3() { console.log("test_poseidon_3") const p = BigNumber.from("21888242871839275222246405745257275088548364400416034343698204186575808495617") const MAX_INPUT = 8 const poseidonParams = new PoseidonParams(p, MAX_INPUT+1, 6, 53, "poseidon", BigNumber.from(5), null, null, 128) let intputs: any intputs = [] intputs.push(BigNumber.from(1)) intputs.push(BigNumber.from(2)) intputs.push(BigNumber.from(3)) intputs.push(BigNumber.from(4)) intputs.push(BigNumber.from(5)) intputs.push(BigNumber.from(6)) intputs.push(BigNumber.from(7)) intputs.push(BigNumber.from(8)) const state0 = permunation.poseidon(intputs, poseidonParams) assert(state0.eq(BigNumber.from("1792233229836714442925799757877868602259716425270865187624398529027734741166"))) console.log("test_poseidon_3 passed") } function main() { // console.log("\n\npermutation_test\n") // test_PoseidonParams() test_H_1() test_H_2() test_H_3() // test_poseidon_constants() test_poseidon_matrix() test_poseidon_1() test_poseidon_2() test_poseidon_3() } main(); ================================================ FILE: src/api/sign/sign_tools.ts ================================================ // import sha256 from 'crypto-js/sha256' import * as crypto from 'crypto-js' import * as abi from 'ethereumjs-abi' import * as sigUtil from 'eth-sig-util' import * as ethUtil from 'ethereumjs-util' import BN from 'bn.js' import BigInteger from 'bignumber.js' import { bnToBufWithFixedLength } from './poseidon/eddsa' import { EDDSAUtil } from './poseidon/EDDSAUtil' import { field } from './poseidon/field' import { permunation, PoseidonParams } from './poseidon/permutation' import * as fm from '../../utils/formatter' import { toBig, toHex } from '../../utils' import { AmmPoolRequestPatch, ChainId, ConnectorNames, CounterFactualInfo, DualOrderRequest, ExitAmmPoolRequest, IsMobile, JoinAmmPoolRequest, NFTMintRequestV3, NFTOrderRequestV3, NFTTokenAmountInfo, NFTTradeRequestV3, NFTWithdrawRequestV3, OffChainWithdrawalRequestV3, OriginNFTTransferRequestV3, OriginTransferRequestV3, PublicKey, SubmitOrderRequestV3, UpdateAccountRequestV3, } from '../../defs' import { Buffer } from 'buffer' /** * BigNumber -> BigInt * BigNumber.from() -> BigInt() or \dn * div -> / * sub -> - * gt -> > * lt -> < * gte -> >= * eq -> == * and -> &&= */ import * as loopring_defs from '../../defs/loopring_defs' import Web3 from 'web3' import { myLog } from '../../utils/log_tools' import { personalSign } from '../base_api' import { BigNumber } from '@ethersproject/bignumber' import { getWindowSafely } from 'utils/window_utils' import { ethers } from 'ethers' // import { hashMessage } from 'ethers/lib/utils' export enum GetEcDSASigType { HasDataStruct, WithoutDataStruct, Contract, } const MIN_NFT_TOKENID = 32768 const SNARK_SCALAR_FIELD = new BigInteger( '21888242871839275222246405745257275088548364400416034343698204186575808495617', 10, ) export interface KeyPairParams { web3: any address: string walletType: ConnectorNames keySeed: string chainId: ChainId accountId?: number counterFactualInfo?: CounterFactualInfo isMobile?: boolean } export function generatePrivateKey(result: { sig: string; counterFactualInfo: any; error: any }) { if (!result.error && result.sig) { // myLog("sig:", result.sig); const seedBuff = ethUtil.sha256(fm.toBuffer(result.sig)) // myLog(`seedBuff.toString('hex') ${seedBuff.toString('hex')}`) const seed = BigNumber.from('0x' + seedBuff.toString('hex')) // myLog(`seed ${seed.toString()}`) const bitIntDataItems = bnToBufWithFixedLength(seed.toString(), 32) // myLog(`bigIntData ${bitIntDataItems}`) const keyPair = EDDSAUtil.generateKeyPair(bitIntDataItems) // myLog("keyPair", keyPair) const formatedPx = fm.formatEddsaKey(fm.toHex(fm.toBig(keyPair.publicKeyX))) const formatedPy = fm.formatEddsaKey(fm.toHex(fm.toBig(keyPair.publicKeyY))) const sk = toHex(toBig(keyPair.secretKey)) return { keyPair, formatedPx, formatedPy, sk, counterFactualInfo: result.counterFactualInfo, } } else { console.log('generateKeyPair personalSign error', result.error) throw Error(result.error) } } export async function generateKeyPair( { web3, address, walletType, keySeed, chainId, accountId, counterFactualInfo, isMobile, }: KeyPairParams, publicKey: { x: string; y: string } | undefined = undefined, ) { // LOG: for signature myLog( 'personalSign ->', 'counterFactualInfo', counterFactualInfo, 'keySeed', keySeed, 'walletType', walletType, 'publicKey from sever side ', publicKey, ) const result: any = await personalSign( web3, address, '', keySeed, walletType, chainId, accountId, counterFactualInfo, isMobile === undefined ? IsMobile.any() : isMobile, ) if (process.env.REACT_APP_LOG_SIGNATURE === 'true') { try { const hash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(result.sig)); console.log('UnlockAccountEcdsaSigHashOutput', 'message hash', keySeed, ethers.utils.hashMessage(keySeed)) console.log('UnlockAccountEcdsaSigHashOutput', 'signature hash', hash) console.log('UnlockAccountEcdsaSigHashOutput', 'isSigEmpty', result.sig === '') } catch {} } if (!result.error && !result.sig) { throw Error('sig is empty') } try { let { keyPair, formatedPx, formatedPy, sk, counterFactualInfo } = generatePrivateKey(result) if ( publicKey && result.sig.length > 3 && publicKey.x && publicKey.y && (!fm.toBig(formatedPx).eq(fm.toBig(publicKey.x)) || !fm.toBig(formatedPy).eq(fm.toBig(publicKey.y))) ) { let value = result.sig.split('') let end = value.splice(result.sig.length - 2, 2).join('') end = end == '1c' ? '01' : '1c' result.sig = value.concat(end.split('')).join('') let newValue = generatePrivateKey(result) // LOG: for signature myLog( 'personalSign ->', 'publicKey calc by sign', 'x', formatedPx, 'y', formatedPy, 'publicKey from server', publicKey, 'personalSign again->', 'publicKey calc by sign', 'x', end, newValue.formatedPx, 'y', newValue.formatedPy, ) return { keyPair: newValue.keyPair, formatedPx: newValue.formatedPx, formatedPy: newValue.formatedPy, sk: newValue.sk, counterFactualInfo, } } else { return { keyPair, formatedPx, formatedPy, sk, counterFactualInfo } } } catch (error) { throw Error(error as any) } } const makeRequestParamStr = (request: Map) => { const arrObj = Array.from(request) arrObj.sort(function (a, b) { return a[0].localeCompare(b[0]) }) const orderedMap = new Map(arrObj.map((i) => [i[0], i[1]])) const paramlist: Array = [] const keys = Object.keys(Object.fromEntries(orderedMap)) if (keys) { keys.forEach((key: string) => { const value = request.get(key) if (value !== undefined && value !== '') paramlist.push(`${key}=${value}`) }) } // force to change encode ',' due to different encode rules between server and client return encodeURIComponent(paramlist.join('&')).replace(/%2C/g, '%252C') } //submitOrderV3 const genSigWithPadding = (PrivateKey: string | undefined, hash: any) => { const signature = EDDSAUtil.sign(PrivateKey, hash) let signatureRx_Hex = fm.clearHexPrefix(fm.toHex(fm.toBN(signature.Rx))) if (signatureRx_Hex.length < 64) { const padding = new Array(64 - signatureRx_Hex.length).fill(0) signatureRx_Hex = padding.join('').toString() + signatureRx_Hex } let signatureRy_Hex = fm.clearHexPrefix(fm.toHex(fm.toBN(signature.Ry))) if (signatureRy_Hex.length < 64) { const padding = new Array(64 - signatureRy_Hex.length).fill(0) signatureRy_Hex = padding.join('').toString() + signatureRy_Hex } let signatureS_Hex = fm.clearHexPrefix(fm.toHex(fm.toBN(signature.s))) if (signatureS_Hex.length < 64) { const padding = new Array(64 - signatureS_Hex.length).fill(0) signatureS_Hex = padding.join('').toString() + signatureS_Hex } const result = '0x' + signatureRx_Hex + signatureRy_Hex + signatureS_Hex return result } const makeObjectStr = (request: Map) => { const jsonTxt = JSON.stringify(Object.fromEntries(request)) return encodeURIComponent(jsonTxt).replace(/[!'()]/g, escape) //replace(/'/ig, "%27") } export function getEdDSASig( method: string, basePath: string, api_url: string, requestInfo: any, PrivateKey: string | undefined, ) { let params = undefined method = method.toUpperCase().trim() if (method === 'GET' || method === 'DELETE') { params = makeRequestParamStr(requestInfo) } else if (method === 'POST' || method === 'PUT') { params = makeObjectStr(requestInfo) } else { throw new Error(`${method} is not supported yet!`) } const uri = encodeURIComponent(`${basePath}${api_url}`) const message = `${method}&${uri}&${params}` // LOG: for signature myLog('getEdDSASig', message) let _hash: any = new BigInteger(crypto.SHA256(message).toString(), 16) let hash = _hash.mod(SNARK_SCALAR_FIELD).toFormat(0, 0, {}) // LOG: for signature myLog('getEdDSASig hash', message, '_hash', _hash.toString(), 'hash', hash) const sig = genSigWithPadding(PrivateKey, hash) return sig } export function creatEdDSASigHasH({ method, basePath, api_url, requestInfo, }: { method: string basePath: string api_url: string requestInfo: any }) { let params = undefined method = method.toUpperCase().trim() if (method === 'GET' || method === 'DELETE') { params = makeRequestParamStr(requestInfo) } else if (method === 'POST' || method === 'PUT') { params = makeObjectStr(requestInfo) } else { throw new Error(`${method} is not supported yet!`) } const uri = encodeURIComponent(`${basePath}${api_url}`) const message = `${method}&${uri}&${params}` // LOG: for signature myLog('getEdDSASig', message) let _hash: any = new BigInteger(crypto.SHA256(message).toString(), 16) let hash = _hash.mod(SNARK_SCALAR_FIELD).toFormat(0, 0, {}) // LOG: for signature myLog('getEdDSASig hash', message, '_hash', _hash.toString(), 'hash', hash) return { hash, hashRaw: toHex(_hash) } } export function verifyEdDSASig( hash: string, input: { Rx: string; Ry: string; s: string }, ): Boolean { return true } export const getEdDSASigWithPoseidon = (inputs: any, PrivateKey: string | undefined) => { const p = field.SNARK_SCALAR_FIELD const poseidonParams = new PoseidonParams( p, inputs.length + 1, 6, 53, 'poseidon', BigNumber.from(5), null, null, 128, ) let bigIntInputs: any bigIntInputs = [] for (let i = 0; i < inputs.length; i++) { const input = inputs[i] bigIntInputs.push(BigNumber.from(input)) } const hash = permunation.poseidon(bigIntInputs, poseidonParams) return { hash, result: genSigWithPadding(PrivateKey, hash), } } /** * @description sign EIP712 * @param web3 * @param account * @param method * @param params * @returns {Promise.<*>} */ export async function signEip712(web3: any, account: string, method: string, params: any) { const provider = new ethers.providers.Web3Provider(web3.currentProvider) const response: any = await provider.send( method, params ).catch(err => { return { error: { message: err.message } } }) if (response?.result) { return response } else { throw new Error(response['error']['message']) } } export async function signEip712WalletConnect(web3: any, account: string, typedData: any) { try { let response: any if (getWindowSafely()?.ethereum?.isLoopring || !web3.currentProvider?.signer?.session) { const result: any = await new Promise((resolve) => { web3.currentProvider?.sendAsync( { method: 'eth_signTypedData', params: [account, typedData], account, }, (err: any, result: any) => { if (err) { resolve({ error: { message: err.message } }) return } if (result.error) { resolve({ error: { message: result.error.message } }) return } resolve({ result: result.result }) }, ) }) // LOG: for signature myLog('eth_signTypedData', result) response = result?.result } else { const provider = new ethers.providers.Web3Provider(web3.currentProvider) response = await provider.send('eth_signTypedData', [account, typedData]) } // LOG: for signature myLog('eth_signTypedData success', response) return response } catch (err) { // LOG: for signature myLog('eth_signTypedData error', err) return { error: err as any } } } export async function getEcDSASig( web3: any, typedData: any, address: string | undefined, type: GetEcDSASigType, chainId: ChainId, accountId?: number, pwd = '', walletType?: ConnectorNames, counterFactualInfo?: CounterFactualInfo, ) { const msgParams = JSON.stringify(typedData) const params = [address, msgParams] let response: any, hash: Buffer | string, signEip712Result: any, signature: any switch (type) { case GetEcDSASigType.HasDataStruct: try { const provider = new ethers.providers.Web3Provider(web3.currentProvider) response = await provider.send( 'eth_signTypedData_v4', params, ) } catch (error) { console.log('eth_signTypedData_v4 error', error) throw error } return { ecdsaSig: response, } case GetEcDSASigType.WithoutDataStruct: hash = sigUtil.TypedDataUtils.sign(typedData) hash = fm.toHex(hash) if (!walletType) { throw Error('no walletType set!') } signature = await personalSign( web3, address, pwd, hash, walletType, chainId, counterFactualInfo ? counterFactualInfo.accountId : undefined, counterFactualInfo, IsMobile.any(), ) if (signature?.sig) { return { ecdsaSig: signature.sig, counterFactualInfo: signature.counterFactualInfo, } } console.log('WithoutDataStruct error', signature?.error) if (typeof signature?.error == 'string') { throw new Error(signature.error) } else { throw signature?.error } // case GetEcDSASigType.Contract: // signEip712Result = await signEip712WalletConnect( // web3, // address as string, // msgParams // ); // // if (signEip712Result.error) { // throw Error("Contract sig error"); // } // // return { // ecdsaSig: signEip712Result, // }; default: break } throw Error('getEcDSASig unsupported switch case:' + type) } export function convertPublicKey2(pk: PublicKey) { // return new BN(EdDSA.pack(pk.x, pk.y), 16); return new BN(EDDSAUtil.pack(pk.x, pk.y), 16) } export function convertPublicKey(pk: PublicKey) { const publicKeyX = fm.formatEddsaKey(fm.toHex(fm.toBig(pk.x))) const publicKeyY = fm.formatEddsaKey(fm.toHex(fm.toBig(pk.y))) // return new BN(EdDSA.pack(publicKeyX, publicKeyY), 16); return new BN(EDDSAUtil.pack(publicKeyX, publicKeyY), 16) } export function getUpdateAccountEcdsaTypedData(data: UpdateAccountRequestV3, chainId: ChainId) { const message: any = { owner: data.owner, accountID: data.accountId, feeTokenID: data.maxFee.tokenId, maxFee: data.maxFee.volume, publicKey: fm.addHexPrefix(convertPublicKey2(data.publicKey).toString(16)), validUntil: data.validUntil, nonce: data.nonce, } const typedData: sigUtil.EIP712TypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], AccountUpdate: [ { name: 'owner', type: 'address' }, { name: 'accountID', type: 'uint32' }, { name: 'feeTokenID', type: 'uint16' }, { name: 'maxFee', type: 'uint96' }, { name: 'publicKey', type: 'uint256' }, { name: 'validUntil', type: 'uint32' }, { name: 'nonce', type: 'uint32' }, ], }, primaryType: 'AccountUpdate', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId, verifyingContract: data.exchange, }, message: message, } return typedData } // withdraw export function get_EddsaSig_OffChainWithdraw( request: OffChainWithdrawalRequestV3, eddsaKey: string, ) { const onchainDataHash = abi .soliditySHA3( ['uint256', 'address', 'bytes'], [ request.minGas, new BN(fm.clearHexPrefix(request.to), 16), new Buffer(request.extraData ?? ''), ], ) .slice(0, 20) const orderHashStr = fm.addHexPrefix(onchainDataHash.toString('hex')) const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.accountId, request.token.tokenId, request.token.volume, request.maxFee.tokenId, request.maxFee.volume, orderHashStr, request.validUntil, request.storageId, ] return getEdDSASigWithPoseidon(inputs, eddsaKey) } export function getOrderHash(request: SubmitOrderRequestV3) { const p = field.SNARK_SCALAR_FIELD const poseidonParams = new PoseidonParams( p, 12, 6, 53, 'poseidon', BigNumber.from(5), null, null, 128, ) const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.storageId, request.accountId, request.sellToken.tokenId, request.buyToken.tokenId, request.sellToken.volume, request.buyToken.volume, request.validUntil, request.maxFeeBips, request.fillAmountBOrS ? 1 : 0, new BN(ethUtil.toBuffer(request.taker)).toString(), ] let bigIntInputs: any bigIntInputs = [] for (let i = 0; i < inputs.length; i++) { const input = inputs[i] bigIntInputs.push(BigNumber.from(input)) } const hash = permunation.poseidon(bigIntInputs, poseidonParams) let hashInHex = hash.toHexString() return hashInHex } export function getWithdrawTypedData( data: OffChainWithdrawalRequestV3, chainId: ChainId, ): sigUtil.EIP712TypedData { const message = { owner: data.owner, accountID: data.accountId, tokenID: data.token.tokenId, amount: data.token.volume, feeTokenID: data.maxFee.tokenId, maxFee: data.maxFee.volume, to: data.to, extraData: data.extraData ? data.extraData : '0x', minGas: data.minGas, validUntil: data.validUntil, storageID: data.storageId, } const typedData: sigUtil.EIP712TypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], Withdrawal: [ { name: 'owner', type: 'address' }, { name: 'accountID', type: 'uint32' }, { name: 'tokenID', type: 'uint16' }, { name: 'amount', type: 'uint96' }, { name: 'feeTokenID', type: 'uint16' }, { name: 'maxFee', type: 'uint96' }, { name: 'to', type: 'address' }, { name: 'extraData', type: 'bytes' }, { name: 'minGas', type: 'uint256' }, { name: 'validUntil', type: 'uint32' }, { name: 'storageID', type: 'uint32' }, ], }, primaryType: 'Withdrawal', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId: chainId, verifyingContract: data.exchange, }, message: message, } return typedData } export async function offchainWithdrawWrap({ withdraw, chainId, web3, accountId, isHWAddr, counterFactualInfo, }: { withdraw: loopring_defs.OffChainWithdrawalRequestV3 web3: any chainId: ChainId accountId: number isHWAddr: boolean counterFactualInfo?: CounterFactualInfo }) { const typedData = getWithdrawTypedData(withdraw, chainId) try { const result = await getEcDSASig( web3, typedData, withdraw.owner, isHWAddr ? GetEcDSASigType.WithoutDataStruct : GetEcDSASigType.HasDataStruct, chainId, accountId, '', ConnectorNames.Unknown, counterFactualInfo, ) return result.ecdsaSig } catch (error) { // console.log('EcDSASig error try sign WithoutDataStruct') throw error } } //NFT Withdraw export function get_EddsaSig_NFT_Withdraw(request: NFTWithdrawRequestV3, eddsaKey: string) { const onchainDataHash = abi .soliditySHA3( ['uint256', 'address', 'bytes'], [ request.minGas, new BN(fm.clearHexPrefix(request.to), 16), new Buffer(request.extraData ?? ''), ], ) .slice(0, 20) const orderHashStr = fm.addHexPrefix(onchainDataHash.toString('hex')) const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.accountId, request.token.tokenId, request.token.amount, request.maxFee.tokenId, request.maxFee.amount, orderHashStr, request.validUntil, request.storageId, ] return getEdDSASigWithPoseidon(inputs, eddsaKey) } export function getNftData(request: NFTMintRequestV3) { const p = field.SNARK_SCALAR_FIELD const poseidonParams = new PoseidonParams( p, 7, 6, 52, 'poseidon', BigNumber.from(5), null, null, 128, ) const idNo0x = fm.clearHexPrefix(request.nftId) let nftIdLo, nftIdHi if (idNo0x.length > 32) { nftIdLo = new BN(idNo0x.substr(idNo0x.length - 32, 32), 16).toString(10) nftIdHi = new BN(idNo0x.substr(0, idNo0x.length - 32), 16).toString(10) } else { nftIdLo = new BN(idNo0x.substr(0, idNo0x.length), 16).toString(10) nftIdHi = 0 } myLog('nftIdLo', nftIdLo, 'nftIdHi', nftIdHi) const inputs = [ request.minterAddress, request.nftType, request.tokenAddress, nftIdLo, nftIdHi, request.royaltyPercentage, ] let bigIntInputs: any bigIntInputs = [] for (let i = 0; i < inputs.length; i++) { const input = inputs[i] bigIntInputs.push(BigNumber.from(input)) } const hash = permunation.poseidon(bigIntInputs, poseidonParams) return hash } export function getNFTMintTypedData( data: NFTMintRequestV3, chainId: ChainId, web3: Web3, ): sigUtil.EIP712TypedData { let nftId = data.nftId if (data.nftId.startsWith('0x')) { nftId = web3.utils.hexToNumberString(data.nftId) } const message = { minterAddress: data.minterAddress, toAccountId: data.toAccountId, nftType: data.nftType.toString(), amount: data.amount, nftId: nftId, nftAddress: data.tokenAddress, feeTokenID: data.maxFee.tokenId, maxFee: data.maxFee.amount, validUntil: data.validUntil, storageID: data.storageId, } const typedData: sigUtil.EIP712TypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], Mint: [ { name: 'minterAddress', type: 'address' }, { name: 'toAccountId', type: 'uint32' }, { name: 'nftType', type: 'string' }, { name: 'amount', type: 'uint96' }, { name: 'nftId', type: 'uint256' }, { name: 'nftAddress', type: 'address' }, { name: 'feeTokenID', type: 'uint16' }, { name: 'maxFee', type: 'uint96' }, { name: 'validUntil', type: 'uint32' }, { name: 'storageID', type: 'uint32' }, ], }, primaryType: 'Mint', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId: chainId, verifyingContract: data.exchange, }, message: message, } return typedData } export function getNFTWithdrawTypedData( data: NFTWithdrawRequestV3, chainId: ChainId, ): sigUtil.EIP712TypedData { const message = { owner: data.owner, accountID: data.accountId, tokenID: data.token.tokenId, amount: data.token.amount, feeTokenID: data.maxFee.tokenId, maxFee: data.maxFee.amount, to: data.to, extraData: data.extraData ? data.extraData : '', minGas: data.minGas, validUntil: data.validUntil, storageID: data.storageId, } const typedData: sigUtil.EIP712TypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], Withdrawal: [ { name: 'owner', type: 'address' }, { name: 'accountID', type: 'uint32' }, { name: 'tokenID', type: 'uint16' }, { name: 'amount', type: 'uint96' }, { name: 'feeTokenID', type: 'uint16' }, { name: 'maxFee', type: 'uint96' }, { name: 'to', type: 'address' }, { name: 'extraData', type: 'bytes' }, { name: 'minGas', type: 'uint256' }, { name: 'validUntil', type: 'uint32' }, { name: 'storageID', type: 'uint32' }, ], }, primaryType: 'Withdrawal', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId: chainId, verifyingContract: data.exchange, }, message: message, } return typedData } export async function withdrawNFTWrap({ withdraw, chainId, web3, accountId, isHWAddr, counterFactualInfo, }: { withdraw: loopring_defs.NFTWithdrawRequestV3 web3: any chainId: ChainId accountId: number isHWAddr: boolean counterFactualInfo?: CounterFactualInfo }) { const typedData = getNFTWithdrawTypedData(withdraw, chainId) try { const result = await getEcDSASig( web3, typedData, withdraw.owner, isHWAddr ? GetEcDSASigType.WithoutDataStruct : GetEcDSASigType.HasDataStruct, chainId, accountId, '', ConnectorNames.Unknown, counterFactualInfo, ) return result.ecdsaSig } catch (error) { // console.log('EcDSASig error try sign WithoutDataStruct') throw error } } //NFT Mint export function get_EddsaSig_NFT_Mint(request: NFTMintRequestV3, eddsaKey: string) { const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.minterId, request.toAccountId, getNftData(request), request.amount, request.maxFee.tokenId, request.maxFee.amount, request.validUntil, request.storageId, ] return getEdDSASigWithPoseidon(inputs, eddsaKey) } export function get_Is_Nft_Token(tokenId: number) { return tokenId >= MIN_NFT_TOKENID } // NFT Order export function get_EddsaSig_NFT_Order(request: NFTOrderRequestV3, eddsaKey: string) { let fillAmountBOrS = 0 if (request.fillAmountBOrS) { fillAmountBOrS = 1 } const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.storageId, request.accountId, request.sellToken?.tokenId !== undefined ? request.sellToken.tokenId : '', (request.buyToken as any)?.nftData ? (request.buyToken as NFTTokenAmountInfo).nftData : request.buyToken.tokenId, request.sellToken?.amount ? request.sellToken.amount : 0, request.buyToken?.amount ? request.buyToken.amount : 0, request.validUntil, request.maxFeeBips, fillAmountBOrS, new BN(ethUtil.toBuffer(request.taker)).toString(), ] return getEdDSASigWithPoseidon(inputs, eddsaKey) } export function get_EddsaSig_Dual_Order(request: DualOrderRequest, eddsaKey: string) { const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.storageId, request.accountId, request.sellToken.tokenId, request.buyToken.tokenId, request.sellToken.volume, request.buyToken.volume, request.validUntil, request.maxFeeBips, request.fillAmountBOrS ? 1 : 0, 0, ] return getEdDSASigWithPoseidon(inputs, eddsaKey) } export async function mintNFTWrap({ mint, chainId, web3, accountId, isHWAddr, counterFactualInfo, }: { mint: loopring_defs.NFTMintRequestV3 chainId: ChainId web3: any accountId: number isHWAddr: boolean counterFactualInfo?: CounterFactualInfo }) { const typedData = getNFTMintTypedData(mint, chainId, web3) try { const result = await getEcDSASig( web3, typedData, mint.minterAddress, isHWAddr ? GetEcDSASigType.WithoutDataStruct : GetEcDSASigType.HasDataStruct, chainId, accountId, '', ConnectorNames.Unknown, counterFactualInfo, ) return result.ecdsaSig } catch (error) { // console.log('EcDSASig error try sign WithoutDataStruct') throw error } } // transfer export function get_EddsaSig_Transfer(request: OriginTransferRequestV3, eddsaKey: string) { const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.payerId, request.payeeId, request.token.tokenId, request.token.volume, request.maxFee.tokenId, request.maxFee.volume, new BN(ethUtil.toBuffer(request.payeeAddr)).toString(), 0, 0, request.validUntil, request.storageId, ] return getEdDSASigWithPoseidon(inputs, eddsaKey) } export function getTransferOldTypedData( data: OriginTransferRequestV3, chainId: ChainId, ): sigUtil.EIP712TypedData { const message = { from: data.payerAddr, to: data.payeeAddr, tokenID: data.token.tokenId, amount: data.token.volume, feeTokenID: data.maxFee.tokenId, maxFee: data.maxFee.volume, validUntil: data.validUntil, storageID: data.storageId, } const typedData: sigUtil.EIP712TypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], Transfer: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'tokenID', type: 'uint16' }, { name: 'amount', type: 'uint96' }, { name: 'feeTokenID', type: 'uint16' }, { name: 'maxFee', type: 'uint96' }, { name: 'validUntil', type: 'uint32' }, { name: 'storageID', type: 'uint32' }, ], }, primaryType: 'Transfer', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId: chainId, verifyingContract: data.exchange, }, message: message, } return typedData } export function getTransferTypedData( data: OriginTransferRequestV3, chainId: ChainId, ): sigUtil.EIP712TypedData { const message = { from: data.payerAddr, to: data.payeeAddr, tokenID: data.token.tokenId, amount: data.token.volume, feeTokenID: data.maxFee.tokenId, maxFee: data.maxFee.volume, validUntil: data.validUntil, storageID: data.storageId, } const typedData: sigUtil.EIP712TypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], Transfer: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'tokenID', type: 'uint16' }, { name: 'amount', type: 'uint96' }, { name: 'feeTokenID', type: 'uint16' }, { name: 'maxFee', type: 'uint96' }, { name: 'validUntil', type: 'uint32' }, { name: 'storageID', type: 'uint32' }, ], }, primaryType: 'Transfer', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId: chainId, verifyingContract: data.exchange, }, message: message, } return typedData } export async function transferWrap({ transfer, chainId, web3, isHWAddr, accountId, counterFactualInfo, }: { transfer: loopring_defs.OriginTransferRequestV3 web3: any chainId: ChainId accountId: number isHWAddr: boolean counterFactualInfo?: CounterFactualInfo }): Promise { const typedData = getTransferTypedData(transfer, chainId) try { const result = await getEcDSASig( web3, typedData, transfer.payerAddr, isHWAddr ? GetEcDSASigType.WithoutDataStruct : GetEcDSASigType.HasDataStruct, chainId, accountId, '', ConnectorNames.Unknown, counterFactualInfo, ) return result.ecdsaSig } catch (error) { // console.log('EcDSASig error try sign WithoutDataStruct') throw error } } export async function transferNFTWrap({ transfer, chainId, web3, isHWAddr, accountId, counterFactualInfo, }: { transfer: loopring_defs.OriginNFTTransferRequestV3 chainId: ChainId web3: any accountId: number isHWAddr: boolean counterFactualInfo?: CounterFactualInfo }): Promise { const typedData = getNFTTransferTypedData(transfer, chainId) try { const result = await getEcDSASig( web3, typedData, transfer.fromAddress, isHWAddr ? GetEcDSASigType.WithoutDataStruct : GetEcDSASigType.HasDataStruct, chainId, accountId, '', ConnectorNames.Unknown, counterFactualInfo, ) return result.ecdsaSig } catch (error) { // console.log('EcDSASig error try sign WithoutDataStruct') throw error } } export function get_EddsaSig_NFT_Transfer(request: OriginNFTTransferRequestV3, eddsaKey: string) { const inputs = [ new BN(ethUtil.toBuffer(request.exchange)).toString(), request.fromAccountId, request.toAccountId, request.token.tokenId, request.token.amount, request.maxFee.tokenId, request.maxFee.amount, new BN(ethUtil.toBuffer(request.toAddress)).toString(), 0, 0, request.validUntil, request.storageId, ] return getEdDSASigWithPoseidon(inputs, eddsaKey) } export function getNftTradeHash(request: NFTTradeRequestV3) { const p = field.SNARK_SCALAR_FIELD const poseidonParams = new PoseidonParams( p, 7, 6, 52, 'poseidon', BigNumber.from(5), null, null, 128, ) const inputs = [ request.taker.accountId, request.taker.sellToken.tokenId, request.taker.storageId, request.maker.accountId, request.maker.sellToken.tokenId, request.maker.storageId, ] let bigIntInputs: any bigIntInputs = [] for (let i = 0; i < inputs.length; i++) { const input = inputs[i] bigIntInputs.push(BigNumber.from(input)) } const hash = permunation.poseidon(bigIntInputs, poseidonParams) let hashInHex = hash.toHexString() return hashInHex } export function getNFTTransferTypedData( data: OriginNFTTransferRequestV3, chainId: ChainId, ): sigUtil.EIP712TypedData { const message = { from: data.fromAddress, to: data.toAddress, tokenID: data.token.tokenId, amount: data.token.amount, feeTokenID: data.maxFee.tokenId, maxFee: data.maxFee.amount, validUntil: data.validUntil, storageID: data.storageId, } const typedData: sigUtil.EIP712TypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], Transfer: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'tokenID', type: 'uint16' }, { name: 'amount', type: 'uint96' }, { name: 'feeTokenID', type: 'uint16' }, { name: 'maxFee', type: 'uint96' }, { name: 'validUntil', type: 'uint32' }, { name: 'storageID', type: 'uint32' }, ], }, primaryType: 'Transfer', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId: chainId, verifyingContract: data.exchange, }, message: message, } return typedData } export function eddsaSign(typedData: any, eddsaKey: string) { const hash = fm.toHex(sigUtil.TypedDataUtils.sign(typedData)) // LOG: for signature // myLog('eddsaSign', hash) const sigHash = fm.toHex(new BigInteger(hash, 16).idiv(8)) const signature = EDDSAUtil.sign(eddsaKey, sigHash) return { eddsaSig: fm.formatEddsaKey(fm.toHex(fm.toBig(signature.Rx))) + fm.clearHexPrefix(fm.formatEddsaKey(fm.toHex(fm.toBig(signature.Ry)))) + fm.clearHexPrefix(fm.formatEddsaKey(fm.toHex(fm.toBig(signature.s)))), } } export function eddsaSignWithDomain( domainHax: string, primaryType: string, message: sigUtil.EIP712Message, types: sigUtil.EIP712Types, eddsaKey: string, ) { const parts = [Buffer.from('1901', 'hex')] parts.push(Buffer.from(domainHax.slice(2), 'hex')) //https://github.com/MetaMask/eth-sig-util/blob/main/CHANGELOG.md parts.push(sigUtil.TypedDataUtils.hashStruct(primaryType, message, types)) const hash = fm.toHex(ethUtil.keccak(Buffer.concat(parts))) //5.2.0 - 2018-04-27 keccak sha3() -> keccak() const sigHash = fm.toHex(new BigInteger(hash, 16).idiv(8)) const signature = EDDSAUtil.sign(eddsaKey, sigHash) return { eddsaSig: fm.formatEddsaKey(fm.toHex(fm.toBig(signature.Rx))) + fm.clearHexPrefix(fm.formatEddsaKey(fm.toHex(fm.toBig(signature.Ry)))) + fm.clearHexPrefix(fm.formatEddsaKey(fm.toHex(fm.toBig(signature.s)))), } } export function getAmmJoinEcdsaTypedData(data: JoinAmmPoolRequest, patch: AmmPoolRequestPatch) { const message = { owner: data.owner, joinAmounts: [data.joinTokens.pooled[0].volume, data.joinTokens.pooled[1].volume], joinStorageIDs: data.storageIds, mintMinAmount: data.joinTokens.minimumLp.volume, fee: data.fee, validUntil: data.validUntil, } const typedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], PoolJoin: [ { name: 'owner', type: 'address' }, { name: 'joinAmounts', type: 'uint96[]' }, { name: 'joinStorageIDs', type: 'uint32[]' }, { name: 'mintMinAmount', type: 'uint96' }, { name: 'fee', type: 'uint96' }, { name: 'validUntil', type: 'uint32' }, ], }, primaryType: 'PoolJoin', domain: { name: patch.ammName, version: '1.0.0', chainId: patch.chainId, verifyingContract: patch.poolAddress, }, message: message, } return typedData } // ammpool join export function get_EddsaSig_JoinAmmPool(data: JoinAmmPoolRequest, patch: AmmPoolRequestPatch) { if (data.domainSeparator) { const typedData = getAmmJoinEcdsaTypedData(data, patch) return eddsaSignWithDomain( data.domainSeparator, typedData.primaryType, typedData.message, typedData.types, patch.eddsaKey, ) } else { const typedData = getAmmJoinEcdsaTypedData(data, patch) return eddsaSign(typedData, patch.eddsaKey) } } export function getAmmExitEcdsaTypedData(data: ExitAmmPoolRequest, patch: AmmPoolRequestPatch) { const message: any = { owner: data.owner, burnAmount: data.exitTokens.burned.volume, burnStorageID: data.storageId, exitMinAmounts: [data.exitTokens.unPooled[0].volume, data.exitTokens.unPooled[1].volume], fee: data.maxFee, validUntil: data.validUntil, } const typedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], PoolExit: [ { name: 'owner', type: 'address' }, { name: 'burnAmount', type: 'uint96' }, { name: 'burnStorageID', type: 'uint32' }, { name: 'exitMinAmounts', type: 'uint96[]' }, { name: 'fee', type: 'uint96' }, { name: 'validUntil', type: 'uint32' }, ], }, primaryType: 'PoolExit', domain: { name: patch.ammName, version: '1.0.0', chainId: patch.chainId, verifyingContract: patch.poolAddress, }, message: message, } return typedData } // ammpool exit export function get_EddsaSig_ExitAmmPool(data: ExitAmmPoolRequest, patch: AmmPoolRequestPatch) { if (data.domainSeparator) { const typedData = getAmmExitEcdsaTypedData(data, patch) return eddsaSignWithDomain( data.domainSeparator, typedData.primaryType, typedData.message, typedData.types, patch.eddsaKey, ) } else { const typedData = getAmmExitEcdsaTypedData(data, patch) return eddsaSign(typedData, patch.eddsaKey) } } ================================================ FILE: src/api/user_api.ts ================================================ /* eslint-disable camelcase */ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' import * as sign_tools from './sign/sign_tools' import { generateKeyPair, getEcDSASig, GetEcDSASigType, getEdDSASigWithPoseidon, getUpdateAccountEcdsaTypedData, KeyPairParams, } from './sign/sign_tools' import BN from 'bn.js' import { RequiredPart, sortObjDictionary } from '../utils' import { AxiosResponse } from 'axios' export class UserAPI extends BaseAPI { /* * Change the ApiKey associated with the user's account. * The current ApiKey must be provided as the value of the X-API-KEY HTTP header. */ public async updateUserApiKey( request: loopring_defs.UpdateUserApiKeyRequest, apiKey: string, eddsaKey: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = new Map() dataToSig.set('accountId', request.accountId) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.API_KEY_ACTION, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, } } /* * Fetches the next order id for a given sold token. * If the need arises to repeatedly place orders in a short span of time, * the order id can be initially fetched through the API and then managed locally. * Each new order id can be derived from adding 2 to the last one */ public async getNextStorageId( request: loopring_defs.GetNextStorageIdRequest, apiKey: string, ): Promise<{ raw_data: R; orderId: number; offchainId: number }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_NEXT_STORAGE_ID, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const { orderId, offchainId } = raw_data return { orderId, offchainId, raw_data, } } /* * Get the details of an order based on order hash. */ public async getOrderDetails( request: loopring_defs.GetOrderDetailsRequest, apiKey: string, ): Promise<{ raw_data: R; orderDetail: loopring_defs.OrderDetail }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ORDER_ACTION, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { orderDetail: raw_data, raw_data, } } public async getOrders( request: loopring_defs.GetOrdersRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number orders: loopring_defs.OrderDetail[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_MULTI_ORDERS, queryParams: { ...request, status: request.status ? request.status.join(',') : '', }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const totalNum: number = raw_data.totalNum const orders: loopring_defs.OrderDetail[] = raw_data.orders return { totalNum, orders, raw_data, } } /* * Submit an order */ public async submitOrder( { extraOrderType = loopring_defs.EXTRAORDER_TYPE.TRADITIONAL_ORDER, ...orderRequest }: loopring_defs.SubmitOrderRequestV3, privateKey: string, apiKey: string, ) { if (!orderRequest.tradeChannel) { orderRequest.tradeChannel = loopring_defs.TradeChannel.MIXED } const dataToSig = [ orderRequest.exchange, orderRequest.storageId, orderRequest.accountId, orderRequest.sellToken.tokenId, orderRequest.buyToken.tokenId, orderRequest.sellToken.volume, orderRequest.buyToken.volume, orderRequest.validUntil, orderRequest.maxFeeBips, orderRequest.fillAmountBOrS ? 1 : 0, 0, ] const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ORDER_ACTION, bodyParams: orderRequest, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG_POSEIDON, sigObj: { dataToSig, sigPatch: loopring_defs.SigPatchField.EddsaSignature, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } public async submitStopOrder( { extraOrderType = loopring_defs.EXTRAORDER_TYPE.STOP_LIMIT, stopSide, // = STOP_SIDE.NO_CONDITION, ...orderRequest }: RequiredPart< loopring_defs.SubmitOrderRequestV3, 'extraOrderType' | 'stopSide' | 'stopPrice' >, privateKey: string, apiKey: string, ) { if (!orderRequest.tradeChannel) { orderRequest.tradeChannel = loopring_defs.TradeChannel.MIXED } const dataToSig = [ orderRequest.exchange, orderRequest.storageId, orderRequest.accountId, orderRequest.sellToken.tokenId, orderRequest.buyToken.tokenId, orderRequest.sellToken.volume, orderRequest.buyToken.volume, orderRequest.validUntil, orderRequest.maxFeeBips, orderRequest.fillAmountBOrS ? 1 : 0, 0, ] const eddsaSignature = getEdDSASigWithPoseidon(dataToSig, privateKey).result const bodyParams = { ...orderRequest, extraOrderType, stopSide, eddsaSignature, } const _dataToSig: Map = sortObjDictionary(bodyParams) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ORDER_ACTION, bodyParams: bodyParams, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig: _dataToSig, PrivateKey: privateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } /* * Cancel order using order hash or client-side ID. */ public async cancelOrder( request: loopring_defs.CancelOrderRequest, PrivateKey: string, apiKey: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = new Map() dataToSig.set('accountId', request.accountId) if (request.orderHash) dataToSig.set('orderHash', request.orderHash) if (request.clientOrderId) dataToSig.set('clientOrderId', request.clientOrderId) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ORDER_ACTION, queryParams: request, apiKey, method: loopring_defs.ReqMethod.DELETE, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, } } /* * Cancel multiple orders using order hashes */ public async cancelMultiOrdersByHash( request: loopring_defs.CancelMultiOrdersByHashRequest, PrivateKey: string, apiKey: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = new Map() dataToSig.set('accountId', request.accountId) dataToSig.set('orderHash', request.orderHash) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ORDER_CANCEL_HASH_LIST, queryParams: request, apiKey, method: loopring_defs.ReqMethod.DELETE, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, } } /* * Cancel multiple orders using clientOrderIds */ public async cancelMultiOrdersByCreditOrderId( request: loopring_defs.CancelMultiOrdersByClientOrderIdRequest, PrivateKey: string, apiKey: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = new Map() dataToSig.set('accountId', request.accountId) dataToSig.set('clientOrderId', request.clientOrderId) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ORDER_CANCEL_CLIENT_ORDER_ID_LIST, queryParams: request, apiKey, method: loopring_defs.ReqMethod.DELETE, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, } } /* * Returns a list Ethereum transactions from users for exchange account registration. */ public async getUserRegTxs( request: loopring_defs.GetUserRegTxsRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userRegTxs: loopring_defs.UserRegTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_REG_TXS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const userRegTxs: loopring_defs.UserRegTx[] = raw_data.transactions return { totalNum: raw_data.totalNum, userRegTxs, raw_data, } } /* * Returns a list Ethereum transactions from users for resetting exchange passwords. */ public async getUserPwdResetTxs( request: loopring_defs.GetUserPwdResetTxsRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userPwdResetTxs: loopring_defs.UserPwdResetTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_PWD_RESET_TXS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const userPwdResetTxs: loopring_defs.UserPwdResetTx[] = raw_data.transactions return { totalNum: raw_data.totalNum, userPwdResetTxs, raw_data, } } /* * Returns user's Ether and token balances on exchange. */ public async getUserBalances( request: loopring_defs.GetUserBalancesRequest, apiKey: string, ): Promise<{ raw_data: R userBalances: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_EXCHANGE_BALANCES, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const userBalances: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item: loopring_defs.UserBalanceInfo) => { userBalances[item.tokenId] = item }) } return { userBalances, raw_data, } } public async getAssetLookRecords( request: loopring_defs.GetUserBalancesRequest, apiKey: string, ): Promise<{ raw_data: R userBalances: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_ASSET_LOCK_RECORDS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const userBalances: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item: loopring_defs.UserBalanceInfo) => { userBalances[item.tokenId] = item }) } return { userBalances, raw_data, } } /* * Returns user's deposit records. */ public async getUserDepositHistory( request: loopring_defs.GetUserDepositHistoryRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userDepositHistory: loopring_defs.UserDepositHistoryTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_DEPOSITS_HISTORY, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userDepositHistory: raw_data.transactions as loopring_defs.UserDepositHistoryTx[], raw_data, } } /* * Get user onchain withdrawal history. */ public async getUserOnchainWithdrawalHistory( request: loopring_defs.GetUserOnchainWithdrawalHistoryRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userOnchainWithdrawalHistory: loopring_defs.UserOnchainWithdrawalHistoryTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.WITHDRAWALS_ACTION, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userOnchainWithdrawalHistory: raw_data.transactions as loopring_defs.UserOnchainWithdrawalHistoryTx[], raw_data, } } /* * Get user transfer list. */ public async getUserTransferList( request: loopring_defs.GetUserTransferListRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userTransfers: loopring_defs.UserTransferRecord[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_TRANSFERS_LIST, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userTransfers: raw_data.transactions as loopring_defs.UserTransferRecord[], raw_data, } } /* * Get user txs */ public async getUserTxs( request: loopring_defs.GetUserTxsRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userTxs: loopring_defs.UserTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_TXS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const userTxs: loopring_defs.UserTx[] = [] if (raw_data?.transactions instanceof Array) { raw_data.transactions.forEach((item: loopring_defs.UserTx) => { userTxs.push(item) }) } return { totalNum: raw_data?.totalNum, userTxs, raw_data, } } /* * Get user trade history */ public async getUserTrades( request: loopring_defs.GetUserTradesRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userTrades: loopring_defs.UserTrade[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_TRADE_HISTORY, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const userTrades: loopring_defs.UserTrade[] = [] if (raw_data?.trades instanceof Array) { raw_data.trades.forEach((item: any[]) => { userTrades.push({ tradeTime: item[0], tradeId: item[1], side: item[2], volume: item[3], price: item[4], market: item[5], fee: item[6], type: item[13], }) }) } return { totalNum: raw_data.totalNum, userTrades, raw_data, } } /* * deprecated * Returns the fee rate of users placing orders in specific markets */ public async getUserFeeRate( request: loopring_defs.GetUserFeeRateRequest, apiKey: string, ): Promise<{ raw_data: R userFreeRateMap: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_FEE_RATE, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const userFreeRateMap: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item: loopring_defs.UserFeeRateInfo) => { userFreeRateMap[item.symbol] = item }) } return { userFreeRateMap, raw_data, } } /* * Returns the user order fee rate of users placing orders in specific markets */ public async getUserOrderFeeRate( request: loopring_defs.GetUserOrderFeeRateRequest, apiKey: string, ): Promise<{ raw_data: R feeRate: loopring_defs.FeeRateInfo gasPrice: number }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_ORDER_FEE_RATE, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const gasPrice = parseInt(raw_data.gasPrice) return { feeRate: raw_data.feeRate as loopring_defs.FeeRateInfo, gasPrice, raw_data, } } /* * Query current token minimum amount to place order based on users VIP level and max fee bips */ public async getMinimumTokenAmt( request: loopring_defs.GetMinimumTokenAmtRequest, apiKey: string, ): Promise<{ raw_data: R amounts: [loopring_defs.TokenAmount, loopring_defs.TokenAmount] amountMap: loopring_defs.LoopringMap gasPrice: number cacheOverdueAt: any }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_MINIMUM_TOKEN_AMT, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const gasPrice = parseInt(raw_data.gasPrice) const amounts: [loopring_defs.TokenAmount, loopring_defs.TokenAmount] = raw_data?.amounts const amountMap: loopring_defs.LoopringMap = {} if (amounts instanceof Array) { amounts.forEach((item: loopring_defs.TokenAmount) => { amountMap[item.tokenSymbol] = item }) } return { amounts, amountMap, gasPrice, cacheOverdueAt: raw_data.cacheOverdueAt, raw_data, } } /* * Query current fee amount */ public async getOffchainFeeAmt( request: loopring_defs.GetOffchainFeeAmtRequest, apiKey: string, ): Promise<{ raw_data: R fees: loopring_defs.LoopringMap gasPrice: number }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_OFFCHAIN_FEE_AMT, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const gasPrice = parseInt(raw_data.gasPrice) const fees: loopring_defs.LoopringMap = {} if (raw_data?.fees instanceof Array) { raw_data.fees.forEach((item: loopring_defs.OffchainFeeInfo) => { fees[item.token] = item }) } return { fees, gasPrice, raw_data, } } /* * Query current NFTAction fee amount */ public async getNFTOffchainFeeAmt( request: loopring_defs.GetNFTOffchainFeeAmtRequest, apiKey: string, ): Promise<{ raw_data: R fees: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_NFT_OFFCHAIN_FEE_AMT, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const fees: loopring_defs.LoopringMap = {} if (raw_data?.fees instanceof Array) { raw_data.fees.forEach((item: loopring_defs.OffchainFeeInfo) => { fees[item.token] = item }) } return { fees, raw_data, } } /* * Submit NFTAction Validate Order request */ public async submitNFTValidateOrder( req: loopring_defs.OriginNFTValidateOrderRequestV3WithPatch, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, eddsaKey, apiKey } = req request.eddsaSignature = sign_tools.get_EddsaSig_NFT_Order(request, eddsaKey).result const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_VALIDATE_ORDER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } // myLog("NFTAction Validate Order request", request); const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } /* * Submit NFTAction Trade request */ public async submitNFTTrade( req: loopring_defs.OriginNFTTradeRequestV3WithPatch, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, apiKey, eddsaKey } = req const dataToSig: Map = new Map() dataToSig.set('maker', request.maker) dataToSig.set('makerFeeBips', request.makerFeeBips) dataToSig.set('taker', request.taker) dataToSig.set('takerFeeBips', request.takerFeeBips) // request.eddsaSignature = sign_tools.get_EddsaSig_Transfer( // request, // eddsaKey // ); const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_TRADE, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, // sigPatch: loopring_defs.SigPatchField.EddsaSignature, PrivateKey: eddsaKey, }, } // myLog("NFTAction Trade request", request); const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } async getUserOwenCollection( request: loopring_defs.GetUserOwnerCollectionRequest, apiKey: string, ) { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_NFT_COLLECTION, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { totalNum: raw_data == null ? void 0 : raw_data.totalNum, collections: raw_data.collections.map(({ collection, ...rest }: any) => { return { ...collection, extends: { ...rest, }, } }) as loopring_defs.CollectionMeta & { extends: { [a: string]: any } }[], raw_data, } } async getUserLegacyCollection( request: loopring_defs.GetUserLegacyCollectionRequest, apiKey: string, ) { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_NFT_LEGACY_COLLECTION, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { totalNum: raw_data == null ? void 0 : raw_data.totalNum, collections: raw_data.collections.map(({ collection, ...rest }: any) => { return { ...collection, extends: { ...rest, }, } }) as loopring_defs.CollectionMeta & { extends: { [a: string]: any } }[], raw_data, } } async getUserNFTCollection(request: loopring_defs.GetUserNFTCollectionRequest, apiKey: string) { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_NFT_COLLECTION_HASNFT, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { totalNum: raw_data == null ? void 0 : raw_data.totalNum, collections: raw_data.collections.map(({ collection, ...rest }: any) => { return { ...collection, extends: { ...rest, }, } }) as loopring_defs.CollectionMeta & { extends: { [a: string]: any } }[], raw_data, } } async getUserNFTLegacyTokenAddress(request: { accountId: number }, apiKey: string) { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_NFT_LEGACY_TOKENADDRESS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { result: raw_data.addresses, raw_data, } } /* * Returns User NFTAction deposit records. */ public async getUserNFTDepositHistory( request: loopring_defs.GetUserNFTDepositHistoryRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTDepositHistory: loopring_defs.UserNFTDepositHistoryTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_DEPOSIT_HISTORY, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userNFTDepositHistory: raw_data.deposits as loopring_defs.UserNFTDepositHistoryTx[], raw_data, } } /* * Get User NFTAction Withdrawal History. */ public async getUserNFTWithdrawalHistory( request: loopring_defs.GetUserNFTWithdrawalHistoryRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTWithdrawalHistory: loopring_defs.UserNFTWithdrawalHistoryTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_WITHDRAW_HISTORY, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userNFTWithdrawalHistory: raw_data.withdrawals as loopring_defs.UserNFTWithdrawalHistoryTx[], raw_data, } } /* * Get user NFTAction transfer list. */ public async getUserNFTTransferHistory( request: loopring_defs.GetUserNFTTransferHistoryRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTTransfers: loopring_defs.UserNFTTransferHistoryTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_TRANSFER_HISTORY, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userNFTTransfers: raw_data.transfers as loopring_defs.UserNFTTransferHistoryTx[], raw_data, } } /** * Get user NFTAction Mint list. * @param request * @param apiKey */ public async getUserNFTMintHistory( request: loopring_defs.GetUserNFTMintHistoryRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTMints: loopring_defs.UserNFTMintHistoryTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_MINT_HISTORY, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, userNFTMints: raw_data.transfers as loopring_defs.UserNFTMintHistoryTx[], raw_data, } } /* * Get user All NFTAction Transaction list. * */ public async getUserNFTTransactionHistory( request: loopring_defs.GetUserNFTTxsRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTTxs: loopring_defs.UserNFTTxsHistory[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_TRANSACTION_HISTORY, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } if ( request.metadata === true && raw_data.transactions.length // raw_data.transactions.metadata && // raw_data.transactions.metadata.nftId && // raw_data.transactions.metadata.nftId.startsWith("0x") ) { raw_data.transactions = raw_data.transactions.reduce( (prev: loopring_defs.UserNFTTxsHistory[], item: loopring_defs.UserNFTTxsHistory) => { if (item.metadata && item.metadata.nftId && item.metadata.nftId.startsWith('0x')) { const hashBN = new BN(item.metadata.nftId.replace('0x', ''), 16) item.metadata.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') } return [...prev, item] }, [], ) // const hashBN = new BN(raw_data.transactions.metadata.nftId.replace("0x", ""), 16); // raw_data.transactions.metadata.nftId= "0x" + hashBN.toString("hex").padStart(64, "0"); } return { totalNum: raw_data?.totalNum, userNFTTxs: raw_data.transactions as loopring_defs.UserNFTTxsHistory[], raw_data, } } public async getUserNFTTradeHistory( request: loopring_defs.GetUserNFTTradeRequest, apiKey: string, ): Promise< | { raw_data: R totalNum: number trades: loopring_defs.UserNFTTradeHistory[] } | loopring_defs.RESULT_INFO > { const reqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_TRADE_HISTORY, queryParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo) { return { ...raw_data.resultInfo, } } let trades = raw_data.trades return { totalNum: raw_data?.totalNum, trades, raw_data, } } public async SetReferrer( request: loopring_defs.SetReferrerRequest, eddsaKey: string, ): Promise<{ raw_data: R; result: any }> { const dataToSig: Map = new Map() dataToSig.set('address', request.address) dataToSig.set('promotionCode', request.promotionCode) dataToSig.set('publicKeyX', request.publicKeyX) dataToSig.set('publicKeyY', request.publicKeyY) dataToSig.set('referrer', request.referrer) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.SET_REFERRER, bodyParams: request, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { result: raw_data?.result, raw_data, } } // Get users NFTAction balance, besides amount, it also includes tokenId and nftData public async getUserNFTBalances( request: loopring_defs.GetUserNFTBalancesRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTBalances: loopring_defs.UserNFTBalanceInfo[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_BALANCES, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } if (raw_data.data.length) { raw_data.data = raw_data.data.reduce( (prev: loopring_defs.UserNFTBalanceInfo[], item: loopring_defs.UserNFTBalanceInfo) => { if (item.nftId && item.nftId.startsWith('0x')) { const hashBN = new BN(item.nftId.replace('0x', ''), 16) item.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') if ( request.metadata === true && item.metadata && item.metadata.nftId && item.metadata.nftId.startsWith('0x') ) { // const hashBN = new BN(item.metadata.nftId.replace("0x", ""), 16); item.metadata.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') } } return [...prev, item] }, [], ) // const hashBN = new BN(raw_data.transactions.metadata.nftId.replace("0x", ""), 16); // raw_data.transactions.metadata.nftId= "0x" + hashBN.toString("hex").padStart(64, "0"); } // if (raw_data.data.nftId && raw_data.data.nftId.startsWith("0x")) { // const hashBN = new BN(raw_data.data.nftId.replace("0x", ""), 16); // raw_data.data.nftId = "0x" + hashBN.toString("hex").padStart(64, "0"); // } return { totalNum: raw_data?.totalNum, userNFTBalances: raw_data.data as loopring_defs.UserNFTBalanceInfo[], raw_data, } } public async getUserNFTBalancesByCollection( request: loopring_defs.GetUserNFTBalancesByCollectionRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTBalances: loopring_defs.UserNFTBalanceInfo[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_NFT_BALANCES_BY_COLLECTION, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } if (raw_data.data.length) { raw_data.data = raw_data.data.reduce( (prev: loopring_defs.UserNFTBalanceInfo[], item: loopring_defs.UserNFTBalanceInfo) => { if (item.nftId && item.nftId.startsWith('0x')) { const hashBN = new BN(item.nftId.replace('0x', ''), 16) item.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') if ( request.metadata === true && item.metadata && item.metadata.nftId && item.metadata.nftId.startsWith('0x') ) { // const hashBN = new BN(item.metadata.nftId.replace("0x", ""), 16); item.metadata.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') } } return [...prev, item] }, [], ) // const hashBN = new BN(raw_data.transactions.metadata.nftId.replace("0x", ""), 16); // raw_data.transactions.metadata.nftId= "0x" + hashBN.toString("hex").padStart(64, "0"); } // if (raw_data.data.nftId && raw_data.data.nftId.startsWith("0x")) { // const hashBN = new BN(raw_data.data.nftId.replace("0x", ""), 16); // raw_data.data.nftId = "0x" + hashBN.toString("hex").padStart(64, "0"); // } return { totalNum: raw_data?.totalNum, userNFTBalances: raw_data.data as loopring_defs.UserNFTBalanceInfo[], raw_data, } } public async getUserNFTLegacyBalance( request: loopring_defs.GetUserNFTLegacyBalanceRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userNFTBalances: loopring_defs.UserNFTBalanceInfo[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_NFT_LEGACY_BALANCE, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } if (raw_data.data.length) { raw_data.data = raw_data.data.reduce( (prev: loopring_defs.UserNFTBalanceInfo[], item: loopring_defs.UserNFTBalanceInfo) => { if (item.nftId && item.nftId.startsWith('0x')) { const hashBN = new BN(item.nftId.replace('0x', ''), 16) item.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') if ( request.metadata === true && item.metadata && item.metadata.nftId && item.metadata.nftId.startsWith('0x') ) { // const hashBN = new BN(item.metadata.nftId.replace("0x", ""), 16); item.metadata.nftId = '0x' + hashBN.toString('hex').padStart(64, '0') } } return [...prev, item] }, [], ) // const hashBN = new BN(raw_data.transactions.metadata.nftId.replace("0x", ""), 16); // raw_data.transactions.metadata.nftId= "0x" + hashBN.toString("hex").padStart(64, "0"); } // if (raw_data.data.nftId && raw_data.data.nftId.startsWith("0x")) { // const hashBN = new BN(raw_data.data.nftId.replace("0x", ""), 16); // raw_data.data.nftId = "0x" + hashBN.toString("hex").padStart(64, "0"); // } return { totalNum: raw_data?.totalNum, userNFTBalances: raw_data.data as loopring_defs.UserNFTBalanceInfo[], raw_data, } } public async getUserVIPAssets( request: loopring_defs.getUserVIPAssetsRequest, ): Promise<{ raw_data: { data: R }; vipAsset: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_VIP_ASSETS, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { vipAsset: raw_data.data ? raw_data.data : raw_data, raw_data, } } public async getUserVIPInfo( request: loopring_defs.GetUserVIPInfoRequest, apiKey: string, ): Promise<{ raw_data: R vipInfo: { createdAt: number validTo: string org: any vipTag: any } }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_VIP_INFO, queryParams: request, method: loopring_defs.ReqMethod.GET, apiKey: apiKey, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const vipInfo = { createdAt: raw_data.created_at, validTo: raw_data.valid_to, org: raw_data.org, vipTag: raw_data.vip_tag, } return { vipInfo, raw_data, } } public async unLockAccount( { keyPair, request, }: { keyPair: KeyPairParams request: loopring_defs.GetUserApiKeyRequest }, publicKey: { x: string; y: string } | undefined = undefined, ): Promise< | AxiosResponse | loopring_defs.RESULT_INFO | { raw_data: R eddsaKey: { keyPair: object formatedPx: string formatedPy: string sk: string counterFactualInfo: loopring_defs.CounterFactualInfo } apiKey: string } > { let eddsaKey try { eddsaKey = await generateKeyPair(keyPair, publicKey) } catch (error) { throw error } if (eddsaKey) { const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.API_KEY_ACTION, queryParams: request, bodyParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey.sk, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo) { throw { ...raw_data?.resultInfo, } } else { return { apiKey: raw_data.apiKey, raw_data, eddsaKey, } } } else { throw { code: loopring_defs.LoopringErrorCode.NO_EDDSA_KEY, message: loopring_defs.ConnectorError.NO_EDDSA_KEY, } } } /* * Submit offchain withdraw request */ public async submitOffchainWithdraw( req: loopring_defs.OffChainWithdrawalRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined try { ecdsaSignature = await sign_tools.offchainWithdrawWrap({ withdraw: request, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } request.eddsaSignature = sign_tools.get_EddsaSig_OffChainWithdraw(request, eddsaKey).result if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.WITHDRAWALS_ACTION, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Submit Internal Transfer request */ public async submitInternalTransfer( req: loopring_defs.OriginTransferRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined try { ecdsaSignature = await sign_tools.transferWrap({ transfer: request as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } request.eddsaSignature = sign_tools.get_EddsaSig_Transfer(request, eddsaKey).result if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_INTERNAL_TRANSFER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Submit Force Withdrawals request */ public async submitForceWithdrawals( req: loopring_defs.OriginForcesWithdrawalsRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const { transfer } = request const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined transfer.payeeId = 0 transfer.memo = `ForceWithdrawalBy${request.requesterAddress}` transfer.maxFee = { volume: '0', tokenId: transfer.token.tokenId, } try { ecdsaSignature = await sign_tools.transferWrap({ transfer: transfer as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } if (counterFactualInfo) { transfer.counterFactualInfo = counterFactualInfo } transfer.eddsaSignature = sign_tools.get_EddsaSig_Transfer( transfer as loopring_defs.OriginTransferRequestV3, eddsaKey, ).result transfer.ecdsaSignature = ecdsaSignature const dataToSig: Map = new Map() dataToSig.set('requesterAddress', request.requesterAddress) dataToSig.set('tokenId', request.tokenId) dataToSig.set('transfer', request.transfer) dataToSig.set('withdrawAddress', request.withdrawAddress) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_FORCE_WITHDRAWALS, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Submit NFTAction Deploy request */ public async submitDeployNFT( req: loopring_defs.OriginDeployNFTRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const { transfer } = request const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined transfer.payeeId = 0 transfer.memo = `NFT-DEPLOY-CONTRACT->${request.tokenAddress}` transfer.maxFee = { volume: '0', tokenId: transfer.token.tokenId, } try { ecdsaSignature = await sign_tools.transferWrap({ transfer: transfer as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } if (counterFactualInfo) { transfer.counterFactualInfo = counterFactualInfo } transfer.eddsaSignature = sign_tools.get_EddsaSig_Transfer( transfer as loopring_defs.OriginTransferRequestV3, eddsaKey, ).result transfer.ecdsaSignature = ecdsaSignature const dataToSig: Map = new Map() dataToSig.set('nftData', request.nftData) dataToSig.set('tokenAddress', request.tokenAddress) dataToSig.set('transfer', request.transfer) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_DEPLOY_TOKEN_ADDRESS, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Submit NFTAction Transfer request */ public async submitNFTInTransfer( req: loopring_defs.OriginNFTTransferRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined try { ecdsaSignature = await sign_tools.transferNFTWrap({ transfer: request, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } request.eddsaSignature = sign_tools.get_EddsaSig_NFT_Transfer(request, eddsaKey).result if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_INTERNAL_TRANSFER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Submit NFTAction Withdraw request */ public async submitNFTWithdraw( req: loopring_defs.OriginNFTWithdrawRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined try { ecdsaSignature = await sign_tools.withdrawNFTWrap({ withdraw: request, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } request.eddsaSignature = sign_tools.get_EddsaSig_NFT_Withdraw(request, eddsaKey).result if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_WITHDRAWALS, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Submit NFTAction */ public async submitNFTMint( req: loopring_defs.OriginNFTMINTRequestV3WithPatch, options?: { accountId?: number counterFactualInfo?: any _noEcdsa?: boolean }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } if (request.counterFactualNftInfo === undefined) { request.counterFactualNftInfo = { nftFactory: loopring_defs.NFTFactory[chainId], nftOwner: request.minterAddress, nftBaseUri: '', } } request.royaltyPercentage = request.royaltyPercentage ? request.royaltyPercentage : 0 const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined // try { // ecdsaSignature = await sign_tools.mintNFTWrap({ // mint: request, // chainId, // web3, // isHWAddr, // accountId, // counterFactualInfo, // }) // // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 // } catch (error) { // throw error // } request.eddsaSignature = sign_tools.get_EddsaSig_NFT_Mint(request, eddsaKey).result if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_MINT, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, // ecdsaSignature, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } async submitNFTCollection( req: loopring_defs.CollectionBasicMeta, chainId: loopring_defs.ChainId, apiKey: string, eddsaKey: string, ): Promise { const _req = req.nftFactory ? req : //@ts-ignore { ...req, nftFactory: loopring_defs.NFTFactory_Collection[chainId] } const dataToSig: Map = sortObjDictionary(_req) const reqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_CREATE_COLLECTION, bodyParams: Object.fromEntries(dataToSig), apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { raw_data, contractAddress: raw_data == null ? void 0 : raw_data.contractAddress, } } async deleteNFTCollection( req: loopring_defs.CollectionDelete, chainId: loopring_defs.ChainId, apiKey: string, eddsaKey: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = sortObjDictionary(req) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.DELETE_NFT_CREATE_COLLECTION, queryParams: req, apiKey, method: loopring_defs.ReqMethod.DELETE, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, } } async submitNFTLegacyCollection( req: loopring_defs.CollectionLegacyMeta, chainId: loopring_defs.ChainId, apiKey: string, eddsaKey: string, ): Promise { // const _req = req.nftFactory // ? req // : { ...req, nftFactory: loopring_defs.NFTFactory_Collection[chainId] }; const dataToSig: Map = sortObjDictionary(req) const reqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_CREATE_LEGACY_COLLECTION, bodyParams: Object.fromEntries(dataToSig), apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { raw_data, result: raw_data.result, } } async submitEditNFTCollection( req: Omit & { collectionId: string accountId: number }, chainId: loopring_defs.ChainId, apiKey: string, eddsaKey: string, ): Promise { // const _req = req.nftFactory ? req : {...req, nftFactory: loopring_defs.NFTFactory_Collection[ chainId ]} const dataToSig: Map = sortObjDictionary(req) const reqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_EDIT_COLLECTION, bodyParams: Object.fromEntries(dataToSig), apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { raw_data, contractAddress: raw_data == null ? void 0 : raw_data.contractAddress, } } async submitUpdateNFTLegacyCollection( req: loopring_defs.UpdateNFTLegacyCollectionRequest, chainId: loopring_defs.ChainId, apiKey: string, eddsaKey: string, ): Promise { const _req = { ...req, nftHashes: req.nftHashes.join(',') } const dataToSig: Map = sortObjDictionary(_req) const reqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_LEGACY_UPDATE_COLLECTION, bodyParams: Object.fromEntries(dataToSig), apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { raw_data, result: raw_data.result, } } async submitUpdateNFTGroup( req: loopring_defs.UpdateNFTGroupRequest, chainId: loopring_defs.ChainId, apiKey: string, eddsaKey: string, ): Promise { const _req = { ...req, nftHashes: req.nftHashes.join(',') } const dataToSig: Map = sortObjDictionary(_req) const reqParams = { url: loopring_defs.LOOPRING_URLs.POST_NFT_UPDATE_NFT_GROUP, bodyParams: Object.fromEntries(dataToSig), apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data != null && raw_data.resultInfo && raw_data != null && raw_data.resultInfo.code) { return { ...raw_data.resultInfo, } } return { raw_data, result: raw_data.result, } } /* * Submit Deploy Collection request */ public async submitDeployCollection( req: loopring_defs.OriginDeployCollectionRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const { transfer } = request const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined transfer.payeeId = 0 transfer.memo = `NFT-DEPLOY-CONTRACT->${request.tokenAddress}` transfer.maxFee = { volume: '0', tokenId: transfer.token.tokenId, } try { ecdsaSignature = await sign_tools.transferWrap({ transfer: transfer as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } if (counterFactualInfo) { transfer.counterFactualInfo = counterFactualInfo } transfer.eddsaSignature = sign_tools.get_EddsaSig_Transfer( transfer as loopring_defs.OriginTransferRequestV3, eddsaKey, ).result transfer.ecdsaSignature = ecdsaSignature const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_DEPLOY_COLLECTION, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Updates the EDDSA key associated with the specified account, making the previous one invalid in the process. */ public async updateAccount( req: loopring_defs.UpdateAccountRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request, web3, chainId, walletType, isHWAddr: isHWAddrOld, privateKey } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined const typedData = getUpdateAccountEcdsaTypedData(request, chainId) try { ecdsaSignature = ( await getEcDSASig( web3, typedData, request.owner, isHWAddr ? GetEcDSASigType.WithoutDataStruct : GetEcDSASigType.HasDataStruct, chainId, accountId, '', loopring_defs.ConnectorNames.Unknown, counterFactualInfo, ) )?.ecdsaSig // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { console.log('EcDSASig error try sign WithoutDataStruct', error) throw error } if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const dataToSig = sortObjDictionary({ ...request, ecdsaSignature: ecdsaSignature, }) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ACCOUNT_ACTION, bodyParams: request, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature, ...(privateKey && request.recommenderAccountId ? { eddsaSignatureREFER: true, sigObj: { PrivateKey: privateKey, dataToSig: dataToSig, }, } : {}), } as unknown as loopring_defs.ReqParams let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } /* * Get the ApiKey associated with the user's account. */ public async getUserApiKey( request: loopring_defs.GetUserApiKeyRequest, eddsaKey: string, ): Promise<{ raw_data: R; apiKey: string }> { const dataToSig: Map = new Map() dataToSig.set('accountId', request.accountId) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.API_KEY_ACTION, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo) { throw { ...raw_data?.resultInfo, } } else { return { apiKey: raw_data.apiKey, raw_data, } } } /* * Get user txs */ public async getUserBills( request: loopring_defs.GetUserBillsRequest, apiKey: string, ): Promise<{ raw_data: R totalNum: number userTxs: loopring_defs.UserTx[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_BILLS, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const userTxs: loopring_defs.UserTx[] = [] if (raw_data?.transactions instanceof Array) { raw_data.transactions.forEach((item: loopring_defs.UserTx) => { userTxs.push(item) }) } return { totalNum: raw_data?.totalNum, userTxs, raw_data, } } public async getReferDownsides( request: loopring_defs.GetReferDownsides, apiKey: string, ): Promise<{ raw_data: R totalNum: number records: loopring_defs.ReferDownsides[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_REFER_DOWNSIDES, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, records: raw_data.records, raw_data, } } public async getReferSelf( request: loopring_defs.GetReferSelf, apiKey: string, ): Promise<{ raw_data: R totalNum: number records: loopring_defs.ReferSelf[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_REFER_SELF, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { totalNum: raw_data?.totalNum, records: raw_data.records, raw_data, } } public async geReferStatistic( request: loopring_defs.GetReferStatistic, apiKey: string, ): Promise<{ raw_data: R } & R> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_REFER_STATISTIC, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { ...raw_data, raw_data: raw_data, } } /* * Get user txs */ public async getUserRewards( request: loopring_defs.GetUserRewardRequest, apiKey: string, ): Promise<{ raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_PROTOCOL_REWARDS, queryParams: { ...request, size: request?.size ?? 200 }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } /* * Get user GET_USER_LOCKSUMMAR */ public async getUserLockSummary( request: loopring_defs.getUserLockSummaryRequest, apiKey: string, ): Promise< R & { raw_data: R } > { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_LOCKSUMMAR, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } public async sendTotalClaim( req: loopring_defs.OriginClaimRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, ) { const { request, web3, chainId, eddsaKey, apiKey, isHWAddr: isHWAddrOld } = req const { accountId, counterFactualInfo }: any = options ? options : { accountId: 0 } const { transfer } = request const isHWAddr = !!isHWAddrOld let ecdsaSignature = undefined transfer.payeeId = 0 transfer.memo = `CLAIM—ALL->${request.accountId}` try { ecdsaSignature = await sign_tools.transferWrap({ transfer: transfer as loopring_defs.OriginTransferRequestV3, chainId, web3, isHWAddr, accountId, counterFactualInfo, }) // ecdsaSignature += isHWAddr ? SigSuffix.Suffix03 : SigSuffix.Suffix02 } catch (error) { throw error } if (counterFactualInfo) { transfer.counterFactualInfo = counterFactualInfo } transfer.eddsaSignature = sign_tools.get_EddsaSig_Transfer( transfer as loopring_defs.OriginTransferRequestV3, eddsaKey, ).result transfer.ecdsaSignature = ecdsaSignature const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_TOTAL_CLAIM, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } catch (error) { throw error as AxiosResponse } } public async getUserTotalClaim( request: loopring_defs.GetTotalClaimRequest, apiKey?: string, ): Promise<{ raw_data: R accountId: number items: loopring_defs.ClaimItem[] }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TOTAL_CLAIM_INFO, queryParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } public async getNotificationAll( request: { accountId?: number offset?: number limit?: number network?: loopring_defs.NetworkWallet notRead: boolean | undefined }, apiKey?: string, ): Promise<{ raw_data: any totalNum: number notifications: R[] notRead: number }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_NOTIFICATION_ALL, queryParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, totalNum: raw_data.totalNum, notRead: raw_data.notRead, notifications: raw_data.notifications, raw_data, } } public async submitNotificationClear( request: { accountId: number network?: loopring_defs.NetworkWallet }, privateKey: string, apiKey?: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = sortObjDictionary({ ...request }) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NOTIFICATION_CLEAR, bodyParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: privateKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } public async submitNotificationReadAll( request: { accountId: number network?: loopring_defs.NetworkWallet }, privateKey: string, apiKey?: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = sortObjDictionary({ ...request }) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NOTIFICATION_READ_ALL, bodyParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: privateKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } public async submitNotificationReadOne( request: { accountId: number id: number }, privateKey: string, apiKey?: string, ): Promise<{ raw_data: R }> { const dataToSig: Map = sortObjDictionary({ ...request }) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_NOTIFICATION_READ_ONE, bodyParams: { ...request }, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: privateKey, }, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data.resultInfo, } } return { ...raw_data, raw_data, } } public async checkUpdateAccount( req: loopring_defs.UpdateAccountRequestV3WithPatch, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const { request } = req const {ecdsaSignature, ..._request} = request const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.ACCOUNT_ACTION, bodyParams: _request, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, sigObj: { sig: ecdsaSignature, }, } as unknown as loopring_defs.ReqParams let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } public async submitEncryptedEcdsaKey( req: { accountId: number eddsaEncryptedPrivateKey: string nonce: number }, eddsaSignKey: string, apiKey: string, ): Promise< (Omit & { raw_data: Omit }) | loopring_defs.RESULT_INFO > { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_ENCRYPTED_ECDSA_KEY, bodyParams: req, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, apiKey, sigObj: { PrivateKey: eddsaSignKey, dataToSig: sortObjDictionary(req), }, } as loopring_defs.ReqParams let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) } public async getEncryptedEcdsaKey( req: { owner: string ecdsaSig: string validUntilInMs: number } ): Promise< {data: {nonce: number, encryptedEddsaPrivateKey: string}} > { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_ENCRYPTED_ECDSA_KEY, queryParams: req, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } as loopring_defs.ReqParams let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return raw_data } } ================================================ FILE: src/api/vault_api.ts ================================================ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' import { get_EddsaSig_NFT_Order, getMidPrice, sortObjDictionary } from '../index' import * as sign_tools from './sign/sign_tools' import { AxiosResponse } from 'axios' export class VaultAPI extends BaseAPI { public async getVaultTokens(apiVersion?: string): Promise<{ raw_data: R tokens: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_TOKENS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, tokens: raw_data } } public async getVaultBalance( request: loopring_defs.GetUserBalancesRequest, apiKey: string, apiVersion?: string ): Promise<{ raw_data: R[] userBalances: loopring_defs.LoopringMap }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_BALANCE, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const userBalances: loopring_defs.LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item) => { userBalances[item.tokenId] = item }) } return { userBalances, raw_data, } } public async getVaultMarkets(apiVersion?: string): Promise<{ raw_data: R markets: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_MARKETS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, markets: raw_data } } public async getVaultGetAvailableNFT( request: { accountId: number }, apiKey: string, apiVersion?: string ): Promise<{ raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_GETAVAILABLENFT, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async getVaultInfoAndBalance( request: { accountId: number }, apiKey: string, apiVersion?: string ): Promise<{ raw_data: R } & R> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_ACCOUNT, queryParams: request, apiKey, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async getVaultGetOperationHistory< R = Array<{ operation: loopring_defs.VaultOperation order: loopring_defs.VaultOrder }>, >( request: { accountId: number operateTypes: loopring_defs.VaultOperationEnum[] | string // VaultOperationType seperate by ',', offset: number start?: number end?: number limit: number }, apiKey: string, apiVersion?: string ): Promise<{ raw_data: { data: R[]; total: number } } & { list: R[]; totalNum: number }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_GETOPERATIONS, queryParams: { ...request, operateTypes: typeof request.operateTypes === 'string' ? request.operateTypes : request.operateTypes.join(','), }, method: loopring_defs.ReqMethod.GET, apiKey, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, list: raw_data.data as R[], totalNum: raw_data.total } } public async getVaultGetOperationByHash< R = { operation: loopring_defs.VaultOperation order: loopring_defs.VaultOrder }, >( request: { accountId: string hash: string // OperationHash }, apiKey: string, apiVersion?: string ): Promise { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_GETOPERATIONBY_HASH, queryParams: request, method: loopring_defs.ReqMethod.GET, apiKey, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } public async getVaultDepth({ request, }: // tokenMap, { request: { market: string level: number limit?: number } tokenMap?: any }, apiVersion?: string): Promise<{ depth: loopring_defs.DepthData raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_DEPTH, queryParams: { ...request }, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const symbol = raw_data.market const timestamp = raw_data.timestamp const { asks, bids, mid_price } = getMidPrice({ _asks: raw_data.asks, _bids: raw_data.bids, }) const depth: loopring_defs.DepthData = { symbol, // @ts-ignore market: raw_data.market, version: raw_data.version, timestamp, mid_price, bids: bids.ab_arr, bids_prices: bids.ab_prices, bids_amtTotals: bids.ab_amtTotals, bids_volTotals: bids.ab_volTotals, bids_amtTotal: bids.amtTotal.toString(), bids_volTotal: bids.volTotal.toString(), asks: asks.ab_arr, asks_prices: asks.ab_prices, asks_amtTotals: asks.ab_amtTotals, asks_volTotals: asks.ab_volTotals, asks_amtTotal: asks.amtTotal.toString(), asks_volTotal: asks.volTotal.toString(), } return { depth, raw_data: raw_data as unknown as R, } } public async submitVaultJoin({ request, eddsaKey, apiKey, }: loopring_defs.VaultOrderNFTRequestV3WithPatch, apiVersion?: string) { const takerOrderEddsaSignature = get_EddsaSig_NFT_Order(request, eddsaKey).result const _request = { ...request, eddsaSignature: takerOrderEddsaSignature, } const dataToSig: Map = sortObjDictionary(_request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_VAULT_JOIN, bodyParams: _request, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, apiKey, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } try { const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } catch (error) { throw error as AxiosResponse } } public async submitVaultOrder({ request, privateKey, apiKey, }: { request: loopring_defs.VaultOrderRequest privateKey: string apiKey: string }, apiVersion?: string) { const dataToSig = [ request.exchange, request.storageId, request.accountId, request.sellToken.tokenId, request.buyToken.tokenId, request.sellToken.volume, request.buyToken.volume, request.validUntil, request.maxFeeBips, request.fillAmountBOrS ? 1 : 0, 0, ] const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_VAULT_ORDER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG_POSEIDON, sigObj: { dataToSig, sigPatch: loopring_defs.SigPatchField.EddsaSignature, PrivateKey: privateKey, }, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } try { const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } catch (error) { throw error as AxiosResponse } } public async submitVaultExit({ request, privateKey, apiKey, }: { request: loopring_defs.VaultExitRequest privateKey: string apiKey: string }, apiVersion?: string) { const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_VAULT_EXIT, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: privateKey, }, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } try { const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } catch (error) { throw error as AxiosResponse } } public async submitVaultTransfer(apiVersion?: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_VAULT_TRANSFER, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data } } public async submitVaultBorrow({ request, privateKey, apiKey, }: { request: loopring_defs.VaultBorrowRequest privateKey: string apiKey: string }, apiVersion?: string) { const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_VAULT_LOAN, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: privateKey, }, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } try { const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } catch (error) { throw error as AxiosResponse } } public async submitVaultRepay( req: loopring_defs.VaultRepayRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, apiVersion?: string ) { let { request, eddsaKey, apiKey } = req const { counterFactualInfo }: any = options ? options : { accountId: 0 } let eddsaSignature = sign_tools.get_EddsaSig_Transfer(request, eddsaKey)?.result request = { ...request, eddsaSignature, } if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_VAULT_REPAY, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature: undefined, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } try { const raw_data = (await this.makeReq().request(reqParams)).data return this.returnTxHash(raw_data) } catch (error) { throw error as AxiosResponse } } public async getVaultConfig(apiKey: string, apiVersion?: string): Promise<{ data: {penaltyFeeBips: number} }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_CONFIG, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, apiKey, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { data: raw_data } } public async getVaultInfos(): Promise<{ raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_INFOS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data } } public async sendVaultResetToken( req: loopring_defs.OriginTransferRequestV3WithPatch, options?: { accountId?: number; counterFactualInfo?: any }, apiVersion?: string ) { let { request, eddsaKey, apiKey } = req const { counterFactualInfo }: any = options ? options : { accountId: 0 } let eddsaSignature = sign_tools.get_EddsaSig_Transfer(request, eddsaKey)?.result request = { ...request, eddsaSignature, } if (counterFactualInfo) { request.counterFactualInfo = counterFactualInfo } const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_INTERNAL_TRANSFER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, ecdsaSignature: undefined, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { raw_data, ...raw_data } } catch (error) { throw error as AxiosResponse } } public async getVaultPrice(request: { tokenIds: string | number[] }, apiVersion?: string) { // let { request, eddsaKey, apiKey } = req const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_PRICE, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, queryParams: { ...request, tokenIds: typeof request.tokenIds === 'string' ? request?.tokenIds : request.tokenIds?.join(','), }, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { return { list: [...raw_data], raw_data, } } } public async getCredit(request: { accountId: number }, apiKey: string, apiVersion?: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_CREDIT, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, queryParams: { ...request }, apiKey, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { throw { ...raw_data?.resultInfo, } } else { return { tokenFactors: raw_data.tokenFactors as { symbol: string factor: string }[], maxLeverage: raw_data.maxLeverage as string } } } public async getCollaterals(request: { accountId: number }, apiKey: string, apiVersion?: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_COLLATERALS, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, queryParams: { ...request }, apiKey, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { throw { ...raw_data?.resultInfo, } } else { return { collateralTokens: raw_data.data as { orderHash: string collateralTokenId: number collateralTokenAmount: string nftTokenId: number nftData: string }[] } } } public async submitLeverage({ request, // privateKey, apiKey, }: { request: { accountId: string leverage: string } apiKey: string }, apiVersion?: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_SUBMIT_LEVERAGE, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } try { const raw_data = (await this.makeReq().request(reqParams)).data return raw_data } catch (error) { throw error as AxiosResponse } } public async submitDustCollector( req: loopring_defs.VaultDustCollectorRequest, apiVersion?: string ) { let { dustTransfers, eddsaKey, apiKey, accountId } = req const signedDustTransfers = dustTransfers.map(dustTransfer => { const eddsaSignature= sign_tools.get_EddsaSig_Transfer(dustTransfer, eddsaKey)?.result return { ...dustTransfer, eddsaSignature } }) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_SUBMIT_DUST_COLLECTOR, bodyParams: { dustTransfers: signedDustTransfers, accountId }, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } try { const raw_data = (await this.makeReq().request(reqParams)).data return { hash: raw_data.hash as string } } catch (error) { throw error as AxiosResponse } } public async getMaxBorrowable(request: { accountId: number, symbol: string }, apiKey: string, apiVersion?: string) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_GEMAX_BORROWABLE, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, queryParams: { ...request }, apiKey, extraHeaders: apiVersion ? { 'X-API-VERSION': apiVersion, } : undefined, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { throw { ...raw_data?.resultInfo, } } else { return raw_data as { accountId: number maxBorrowableOfUsdt: string } } } public async closeShort( { request, }: { request: { accountId: number tokenId: number timestamp: number } }, apiKey: string, eddsaKey: string, ) { const dataToSig: Map = sortObjDictionary(request) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_VAULT_CLOSE_SHORT, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.EDDSA_SIG, sigObj: { dataToSig, PrivateKey: eddsaKey, }, } try { const raw_data = (await this.makeReq().request(reqParams)).data return raw_data } catch (error) { throw error as AxiosResponse } } } ================================================ FILE: src/api/wallet_api.ts ================================================ import { BaseAPI, personalSign } from './base_api' import { sendRawTx } from './contract_api' import * as loopring_defs from '../defs' import { contracts as abi } from './ethereum/contracts' import * as sign_tools from './sign/sign_tools' import { sortObjDictionary, toHex } from '../utils' import { myLog } from '../utils/log_tools' import { AxiosResponse } from 'axios' import { signHebaoApproveWrap } from './config' export class WalletAPI extends BaseAPI { /* * Get user assets */ public async getUserAssets(request: loopring_defs.GetUserAssetsRequest) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_ASSETS, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const assetSeries: string[] = [] const timestampSeries: number[] = [] const dateSeries: string[] = [] if (raw_data?.data instanceof Array) { raw_data.data.forEach((item: loopring_defs.UserAssetInfo) => { assetSeries.push(item.amount) timestampSeries.push(item.createdAt) dateSeries.push(item.createdAtStr) }) } return { assetSeries, timestampSeries, dateSeries, userAssets: raw_data.data as loopring_defs.UserAssetInfo[], raw_data, } } /** * * @param approveRecordId request.id */ public async rejectHebao(req: loopring_defs.RejectHebaoRequestV3WithPatch) { const { web3, address, request, chainId } = req const dataToSig = sortObjDictionary(request) const { hashRaw } = sign_tools.creatEdDSASigHasH({ method: loopring_defs.ReqMethod.POST, basePath: this.baseUrl, api_url: loopring_defs.LOOPRING_URLs.REJECT_APPROVE_SIGNATURE, requestInfo: dataToSig, }) myLog('signHash', hashRaw) const result: any = await personalSign( web3, address, '', toHex(hashRaw), loopring_defs.ConnectorNames.Unknown, chainId, ) const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.REJECT_APPROVE_SIGNATURE, queryParams: {}, method: loopring_defs.ReqMethod.POST, bodyParams: request, apiKey: '', sigFlag: loopring_defs.SIG_FLAG.NO_SIG, sigObj: { sig: result?.sig.slice(0, 132), }, } let hash: string | undefined = undefined const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { hash = raw_data.data } return { hash, raw_data, } } public async submitApproveSignature( req: loopring_defs.SubmitApproveSignatureRequestWithPatch, guardians: string[] = [], isContract1XAddress?: boolean, masterCopy?: string, forwarderModuleAddress: string = '', ): Promise | loopring_defs.RESULT_INFO> { const { request, web3, chainId, // walletType, guardian, apiKey, isHWAddr: isHWAddrOld, } = req const isHWAddr = !!isHWAddrOld const { signature: ecdsaSignature } = await signHebaoApproveWrap({ chainId, web3, owner: request.signer, isHWAddr, wallet: guardian.signedRequest.wallet, validUntil: guardian.signedRequest.validUntil, messageData: guardian?.businessDataJson?.value?.value ?? {}, masterCopy: isContract1XAddress ? undefined : masterCopy, forwarderModuleAddress, type: guardian.type, guardian, walletVersion: isContract1XAddress ? 1 : 2, }) request.signature = ecdsaSignature?.toString() const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.SUBMIT_APPROVE_SIGNATURE, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let raw_data try { raw_data = (await this.makeReq().request(reqParams)).data } catch (error) { throw error as AxiosResponse } return this.returnTxHash(raw_data) as loopring_defs.TX_HASH_RESULT } public async getAddressByENS( request: loopring_defs.GetEnsAddressRequest, ): Promise<{ address: string | undefined raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.RESOLVE_ENS, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let address: T | undefined const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { address = raw_data.data as T } return { address, raw_data, } } public async getWalletType( request: loopring_defs.GET_WALLET_TYPE, ): Promise<{ walletType: loopring_defs.WalletType | undefined raw_data: T }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_WALLET_TYPE, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let walletType: loopring_defs.WalletType | undefined const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { walletType = raw_data.data } return { walletType, raw_data, } } public async getContractType( request: loopring_defs.GET_WALLET_TYPE, ): Promise<{ contractType: T | undefined raw_data: T }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_WALLET_CONTRACTVERSION, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data let contractType: T | undefined if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { contractType = raw_data.data[0] } return { contractType, raw_data, } } public async getWalletModules( request: loopring_defs.GET_WALLET_TYPE, ): Promise<{ walletModule: T | undefined raw_data: T }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_WALLET_MODULES, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data let walletModule: T | undefined if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { walletModule = raw_data.data[0] } return { walletModule, raw_data, } } public async getEnsByAddress( request: loopring_defs.GetEnsNameRequest, ): Promise<{ ensName: string | undefined raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.RESOLVE_NAME, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let ensName: T | undefined const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { ensName = raw_data.data as T } return { ensName, raw_data, } } public async lockHebaoWallet({ web3, from, contractAddress, gasPrice, gasLimit = '0x' + Number(150000).toString(16), chainId = 1, wallet, nonce, isVersion1, }: // sendByMetaMask = true, loopring_defs.LockHebaoHebaoParam) { if (isVersion1) { const data = abi.Contracts.HeBao.encodeInputs('lock', { wallet, }) return await sendRawTx( web3, from, contractAddress, 0, data, chainId as loopring_defs.ChainId, nonce, gasPrice, gasLimit, true, ) } else { return await sendRawTx( web3, from, contractAddress, 0, '0xf83d08ba', chainId as loopring_defs.ChainId, nonce, gasPrice, gasLimit, true, ) } } public async getHebaoConfig(request: { network?: loopring_defs.NetworkWallet }) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_HEBAO_CONFIG, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data: raw_data.data } } public async sendMetaTx( request: loopring_defs.SendMetaTxRequest, apiKey: string, ): Promise<{ raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.SEND_META_TX, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, bodyParams: request, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data } } public async getGuardianApproveList( request: loopring_defs.GetGuardianApproveListRequest, ): Promise<{ guardiansArray: Array raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_GUARDIAN_APPROVE_LIST, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let guardiansArray: Array = [] const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { guardiansArray = raw_data?.data?.guardians?.map((r: any) => ({ ens: r.ens ? r.ens : '', address: r.wallet, type: loopring_defs.HEBAO_META_TYPE[r.metaTxType], id: r.approveId, messageHash: r.txAwareHash, businessDataJson: r.businessDataJson, signedRequest: r.signedRequest, ...r, })) ?? [] } return { guardiansArray, raw_data, } } // /api/appWallet/v3/operationLogs?from=0x189a3c44a39c5ab22712543c0f62a9833bbe8df9&fromTime=0&to=&offset=0&network=ETHEREUM&statues=&hebaoTxType=&limit=20 /** * getProtectors * @param {GetUserTradesRequest} request * @param apiKey */ public async getProtectors( request: loopring_defs.GetProtectorRequest, apiKey: string, ): Promise<{ protectorArray: Array raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_PROTECTORS, apiKey: apiKey, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } let protectorArray: Array = [] const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } else { protectorArray = raw_data?.data?.map((p: any) => ({ ens: p.protectEns, address: p.protectAddress, lockStatus: p.walletStatus?.toUpperCase(), })) ?? [] } return { protectorArray, raw_data, } } /* * Get user trade amount */ public async getHebaoOperationLogs( request: loopring_defs.HebaoOperationLogs, ): Promise<{ operationArray: Array raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_OPERATION_LOGS, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { operationArray: raw_data?.data ?? ([] as T[]), raw_data, } } /* * Get user trade amount */ public async getUserTradeAmount(request: loopring_defs.GetUserTradeAmount) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_USER_TRADE_AMOUNT, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, } } /* * Get token prices * e.g. http://api3.loopring.io/api/wallet/v3/tokenPrices?token=0xdac17f958d2ee523a2206206994597c13d831ec7&intervalType=1&limit=30¤cy=CNY */ public async getTokenPrices(request: loopring_defs.GetTokenPricesRequest) { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_TOKEN_PRICES, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const priceSeries: string[] = [] const timestampSeries: number[] = [] if (raw_data?.data instanceof Array) { raw_data.data.forEach((item: loopring_defs.TokenPriceInfo) => { priceSeries.push(item.price) timestampSeries.push(item.createdAt) }) } return { tokenPrices: raw_data.data as loopring_defs.TokenPriceInfo[], priceSeries, timestampSeries, raw_data, } } /* * Fetches, for all the tokens supported by Loopring, their fiat price. * response: { [key: string]: } key is token address */ public async getLatestTokenPrices(request?: loopring_defs.getLatestTokenPricesRequest) { const reqParams: loopring_defs.ReqParams = { queryParams: request, url: loopring_defs.LOOPRING_URLs.GET_LATEST_TOKEN_PRICES, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } const tokenPrices: loopring_defs.LoopringMap = {} if (raw_data?.data instanceof Array) { raw_data.data.forEach((item: any) => { tokenPrices[item.token.toLowerCase()] = parseFloat(item.price) }) } return { tokenPrices, raw_data, } } public async getAccountServices(request: loopring_defs.GetAccountServicesRequest): Promise<{ register: any order: any joinAmm: any dAppTrade: any legal: any raw_data: R }> { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_ACCOUNT_SERVICES, queryParams: request, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } return { ...raw_data, register: raw_data?.register, order: raw_data?.order, joinAmm: raw_data?.joinAmm, dAppTrade: raw_data?.dAppTrade, legal: raw_data?.dAppTrade, raw_data, } } } ================================================ FILE: src/api/whitelisted_user_api.ts ================================================ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' import * as sign_tools from './sign/sign_tools' export class WhitelistedUserAPI extends BaseAPI { /* * Submit offchain withdraw request * not supported for now. */ private async submitOffchainWithdraw( request: loopring_defs.OffChainWithdrawalRequestV3, eddsaKey: string, apiKey: string, ) { request.eddsaSignature = sign_tools.get_EddsaSig_OffChainWithdraw(request, eddsaKey).result const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.WITHDRAWALS_ACTION, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, } } /* * Submit offchain withdraw request */ public async submitInternalTransfer( request: loopring_defs.OriginTransferRequestV3, eddsaKey: string, apiKey: string, ) { request.eddsaSignature = sign_tools.get_EddsaSig_Transfer(request, eddsaKey).result const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.POST_INTERNAL_TRANSFER, bodyParams: request, apiKey, method: loopring_defs.ReqMethod.POST, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo && raw_data?.resultInfo.code) { return { ...raw_data?.resultInfo, } } return { raw_data, } } } ================================================ FILE: src/api/ws_api.ts ================================================ import { BaseAPI } from './base_api' import * as loopring_defs from '../defs' export class WsAPI extends BaseAPI { /* * Get wsApiKey by access REST path "/v3/ws/key" */ public async getWsKey() { const reqParams: loopring_defs.ReqParams = { url: loopring_defs.LOOPRING_URLs.GET_WS_KEY, method: loopring_defs.ReqMethod.GET, sigFlag: loopring_defs.SIG_FLAG.NO_SIG, } const raw_data = (await this.makeReq().request(reqParams)).data if (raw_data?.resultInfo) { return { ...raw_data?.resultInfo, } } const wsKey = raw_data.key return { wsKey, raw_data, } } } ================================================ FILE: src/defs/account_defs.ts ================================================ import { PublicKey } from './loopring_enums' /** * AccountInfo * @property accountId number Account ID * @property owner string Ethereum address * @property frozen boolean The frozen state of the account, true stands for frozen, if the account is frozen, the user cant submit order. * @property publicKey PublicKey The user's public key * @property tags? string Comma separated list of tags such as VIP levels, etc * @property nonce number field.DexAccountV3.nonce * @property keyNonce number Nonce of users key change request, for backward compatible * @property keySeed string KeySeed of users L2 eddsaKey, the L2 key should be generated from this seed, i.e., L2_EDDSA_KEY=eth.sign(keySeed). Otherwise, user may meet error in login loopring DEX */ export interface AccountInfo { accountId: number owner: string frozen: boolean publicKey: PublicKey tags?: string nonce: number keyNonce: number keySeed: string } /** * */ export interface CounterFactualInfo { accountId: number owner: string walletFactory: string walletSalt: string walletOwner: string } export interface NFTCounterFactualInfo { nftFactory: string nftOwner: string nftBaseUri: string } ================================================ FILE: src/defs/error_codes.ts ================================================ import { AxiosResponse } from "axios"; export enum LoopringErrorCode { Unknown_Error = 100000, Invalid_Args = 100001, Address_Not_Found = 101001, User_Not_Found = 101002, ExchangeId_Incorrect = 102001, Unsupported_TokenId = 102002, Invalid_AccountId = 102003, //dup Invalid_OrderId = 102004, Market_Not_Supported = 102005, //dup Illegal_Rate = 102006, Order_Already_Existed = 102007, Order_Already_Expired = 102008, Order_Missing_Sig = 102010, Invalid_User_Balance = 102011, Order_Amount_Too_Small = 102012, Failed_To_Freeze_Amt = 102014, Exceed_Max_Order_Amt = 102020, Invalid_Nonce = 102021, Invalid_Transfer_Sender = 102022, Invalid_Transfer_Receiver = 102023, Unsuported_Fee_Token = 102024, Transfer_Token_Is_Not_Consistent_With_Fee_Token = 102025, Sumbit_Order_Failed = 102027, No_Order_To_Cancel = 102117, Fail_To_Cancel_Order = 102118, Order_Is_Not_Valid = 102120, Empty_Apikey = 104001, Invalid_Apikey = 104002, Invalid_AccountID = 104003, //dup No_Sig_Provided = 104004, Wrong_Sig = 104005, User_Cannot_Be_Empty = 107001, Orderhash_Cannot_Be_Empty = 107002, Order_Not_Exist = 107003, Unsupported_Market = 108000, //dup Unsupported_Depth_Level = 108001, SKD_UNKNOW = 500000, CONTRACTNFT_URI = 500001, CONTRACTNFT_BALANCE = 500002, CONTRACTNFT_IS_APPROVE = 500003, CONTRACTNFT_SET_APPROVE = 500004, NOT_SUPPORT_ERROR = 500005, USER_DENIED = 500006, USER_DENIED_2 = 500007, NO_EDDSA_KEY = 500008, HTTP_ERROR = 500009, BTRADE_NO_DEPTH_ERROR = 500010, BTRADE_NO_PRODUCT = 500011, } export enum ConnectorError { NOT_SUPPORT_ERROR = "Not supported on this device", USER_DENIED = "User denied message signature", USER_DENIED_2 = "personalSign err before Validate", CONTRACTNFT_URI = "contract nft uri Error", CONTRACTNFT_BALANCE = "contract nft balance error", CONTRACTNFT_IS_APPROVE = "ContractNFT is Approve error", CONTRACTNFT_SET_APPROVE = "ContractNFT set Approve error", NO_EDDSA_KEY = "No EDDSA KEY", HTTP_ERROR = "HTTP Request Failed!", BTRADE_NO_DEPTH_ERROR = "NO Depth for BTRADE", BTRADE_NO_PRODUCT = "NO Market for BTRADE", } export interface RESULT_INFO { code?: number; msg?: string; message?: string; } export type ERROR_INFO = { resultInfo: RESULT_INFO; }; export const checkErrorInfo = ( errorInfo: RESULT_INFO, isFirstTime?: boolean ) => { const message = errorInfo.message; if (isFirstTime && message === "NOT_SUPPORT_ERROR") { return ConnectorError.NOT_SUPPORT_ERROR; } else if (message === "USER_DENIED" || message === "USER_DENIED_2") { return ConnectorError.USER_DENIED; } else if ( message && (message.startsWith("personalSign last") || message.indexOf("User denied transaction") > 0) ) { return ConnectorError.USER_DENIED; } return message; }; ================================================ FILE: src/defs/index.ts ================================================ import { getNavigatorSafely, getWindowSafely } from 'utils/window_utils' export * from './loopring_enums' export * from './url_defs' export * from './account_defs' export * from './web3_defs' export * from './ws_defs' export * from './loopring_constants' export * from './loopring_defs' export * from './error_codes' export * from './nft_defs' export const IsMobile = { Android: function () { return getNavigatorSafely()?.userAgent.match(/Android/i) }, BlackBerry: function () { return getNavigatorSafely()?.userAgent.match(/BlackBerry/i) }, iOS: function () { return getNavigatorSafely()?.userAgent.match(/iPhone|iPad|iPod/i) }, Opera: function () { return getNavigatorSafely()?.userAgent.match(/Opera Mini/i) }, Windows: function () { return getNavigatorSafely()?.userAgent.match(/IEMobile/i) || getNavigatorSafely()?.userAgent.match(/WPDesktop/i) }, Ethereum: function () { return getWindowSafely()?.ethereum && getWindowSafely()?.ethereum.isImToken }, any: function () { if (typeof global.navigator === 'undefined' || typeof navigator === 'undefined') { console.log('IsMobile any navigator is undefined') return false } return ( IsMobile.Android() || IsMobile.BlackBerry() || IsMobile.iOS() || IsMobile.Opera() || IsMobile.Windows() || IsMobile.Ethereum() ) }, } type Ethereum = any declare global { interface Window { ethereum?: Ethereum & { [key: string]: boolean; isLoopring: boolean } // socketEventMap: {[key:string]:any // imageConfig:{[key:string]:any}|undefined } interface Global { ethereum?: Ethereum & { [key: string]: boolean; isLoopring: boolean } } } ================================================ FILE: src/defs/loopring_constants.ts ================================================ export enum HEBAO_LOCK_STATUS { LOCK_FAILED = 'LOCK_FAILED', CREATED = 'CREATED', LOCK_WAITING = 'LOCK_WAITING', UNLOCK_FAILED = 'UNLOCK_FAILED', LOCKED = 'LOCKED', UNLOCK_WAITING = 'UNLOCK_WAITING', } export enum HEBAO_META_TYPE { recovery = 16, transfer = 18, // add_guardian = 34, approve_token = 23, remove_guardian = 35, unlock_wallet = 37, upgrade_contract = 201, deposit_wallet = 202, } export const SoursURL = 'https://static.loopring.io/assets/' ================================================ FILE: src/defs/loopring_defs.ts ================================================ import { ChainId, ConnectorNames } from './web3_defs' import Web3 from 'web3' import { AmmPoolActivityStatus, AmmTxType, AssetType, BillType, Currency, IntervalType, MarketStatus, NetworkWallet, OffchainFeeReqType, OffchainNFTFeeReqType, OrderStatus, OrderType, OrderTypeResp, PublicKey, ReqMethod, RuleType, Side, SIG_FLAG, TradeChannel, TradingInterval, TransferType, TxStatus, TxType, UserBillTypes, UserNFTTxTypes, UserTxTypes, WithdrawalTypes, } from './loopring_enums' import { RESULT_INFO } from './error_codes' import { HEBAO_LOCK_STATUS, HEBAO_META_TYPE } from './loopring_constants' import { CounterFactualInfo, NFTCounterFactualInfo } from './account_defs' import { NFTType } from '../api' export type Without = { [P in Exclude]?: never } export type XOR = T | U extends { [key: string]: any } ? (Without & U) | (Without & T) : T | U export interface VipFeeRateInfo { symbol: string makerRate: number takerRate: number } export type VipFeeRateInfoMap = { [key: string]: VipFeeRateInfo } export type TX_HASH_RESULT = T & { raw_data: T } export interface LoopringMap { [key: string]: T } export interface ExchangeInfo { ammExitFees: Array[] chainId: number depositAddress: string exchangeAddress: string fastWithdrawalFees: Array[] onchainFees: Array[] openAccountFees: Array[] transferFees: Array[] updateFees: Array[] withdrawalFees: Array[] } export interface TickerData { symbol: string base: string | undefined quote: string | undefined timestamp: number base_token_volume: string quote_token_volume: string base_fee_amt: string quote_fee_amt: string open: number high: number low: number close: number count: number bid: number ask: number change?: number } export interface ABInfo { price: number amt: string vol: string amtTotal: string volTotal: string } export interface DepthData { symbol: string version: number timestamp: number mid_price: number bids: ABInfo[] bids_prices: number[] bids_amtTotals: string[] bids_volTotals: string[] bids_amtTotal: string bids_volTotal: string asks: ABInfo[] asks_prices: number[] asks_amtTotals: string[] asks_volTotals: string[] asks_amtTotal: string asks_volTotal: string } export interface Candlestick { timestamp: number txs: number open: number close: number high: number low: number baseVol: string quoteVol: string } export interface TradesData { totalNum: number trades: any[][] } export interface OrdersData { totalNum: number trades: any[][] } export interface QuotesData { quotes: any[][] } export interface TokenInfo { type: string tokenId: number symbol: string name: string address: string decimals: number precision: number precisionForOrder: number orderAmounts: { minimum: string maximum: string dust: string } luckyTokenAmounts: { minimum: string maximum: string dust: string } fastWithdrawLimit: string gasAmounts: { distribution: string deposit: string } enabled: boolean isLpToken: boolean } export interface AmmPoolStat { market: string liquidity: string[] lpLiquidity: string liquidityUSD: string ohlc: string[] volume: string[] fees: string[] apyBips: string isRecommended: boolean rewards: TokenVolumeV3[] } export interface AmmPoolActivityRule { market: string ruleType: RuleType rangeFrom: number rangeTo: number awardRules: TokenVolumeV3[] maxSpread: number topK: number status: AmmPoolActivityStatus } export interface AmmPoolInProgressActivityRule { market: string ruleType: RuleType[] rangeFrom: number rangeTo: number awardRules: TokenVolumeV3[] maxSpread: number topK: number status: AmmPoolActivityStatus } export interface AmmTrade { accountId: number orderHash: string market: string side: string size: string price: number feeAmount: string createdAt: number } export interface AmmPoolInfoV3 { name: string market: string address: string version: string tokens: { pooled: string[] lp: number } feeBips: number precisions: { price: number amount: number } createdAt: string status: number domainSeparator: string } export interface TokenRelatedInfo { tokenId: string tokenList: string[] } export interface AmmPoolConfResponse { ammpools: LoopringMap pairs: LoopringMap raw_data: any } export interface PooledMap { [key: number]: TokenVolumeV3 } export interface AmmPoolBalance { poolName: string poolAddress: string pooled: [TokenVolumeV3, TokenVolumeV3] lp: any risky: boolean pooledMap: PooledMap } export interface AmmPoolBalancesResponse { ammpoolsbalances: LoopringMap raw_data: any } export interface TokensResponse { tokenSymbolMap: LoopringMap tokenIdMap: LoopringMap tokenAddressMap: LoopringMap getTokenInfoBySymbol: any getTokenInfoById: any tokenSymbolArr: string[] tokenSymbolArrStr: string tokenIdArr: string[] tokenIdArrStr: string tokenAddressArr: string[] tokenAddressArrStr: string raw_data: any } export interface MarketInfo { baseTokenId: number enabled: boolean market: string orderbookAggLevels: number precisionForPrice: number quoteTokenId: number status?: MarketStatus isSwapEnabled?: boolean createdAt?: number } export enum DefiMarketStatus { hide = 0, show = 1, depositOnly = 3, depositAll = 7, withdrawOnly = 9, depositAllAndWithdraw = 15, withdrawAll = 25, WithdrawAllAndDeposit = 27, depositAndWithdraw = 11, all = 31, } export interface DefiMarketInfo { type: string market: string apy: string baseTokenId: number quoteTokenId: number precisionForPrice: number orderbookAggLevels: number enabled: boolean currency: string status: DefiMarketStatus accountId: number address: string depositFeeBips: number withdrawFeeBips: number depositPrice: string withdrawPrice: string baseVolume: string quoteVolume: string quoteLimitAmount: string baseLimitAmount: string quoteAlias: string stepLength: string extra: { depositFeeBips: number withdrawFeeBips: number orderbookAggLevels: number isLeverage: number baseAssetSymbol: string quoteAssetSymbol: string } } export interface MarketsResponse { hasMarket: any getExistedMarket: any markets: LoopringMap pairs: LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string raw_data: any } export interface TokenVolumeV3 { /** * The Loopring\'s token identifier. * @type {string} * @memberof TokenVolumeV3 */ tokenId: string | number /** * The volume of the token * @type {string} * @memberof TokenVolumeV3 */ volume: string } export interface TokenVolumeV5 { /** * The Loopring\'s token identifier. * @type {string} * @memberof TokenVolumeV3 */ tokenId: string | number /** * The volume of the token * @type {string} * @memberof TokenVolumeV3 */ amount: string } export interface TokenVolumeNFT { /** * The Loopring\'s token identifier. * @type {string} * @memberof TokenVolumeV3 */ tokenId: string | number /** * The amount of the token * @type {string} * @memberof TokenVolumeV3 */ amount: string /** * The Loopring's NFTAction token data identifier which is a hash string of NFTAction token address and NFT_ID * @type {string} * @memberof The Loopring's NFTAction token data identifier which is a hash string of NFTAction token address and NFT_ID */ nftData: NftData } export interface AmmPoolJoinTokens { pooled: TokenVolumeV3[] minimumLp: TokenVolumeV3 } export interface AmmPoolExitTokens { unPooled: TokenVolumeV3[] burned: TokenVolumeV3 } export interface GameRankInfo { address: string volume: string rank: number rewards: TokenVolumeV3[] } export interface SetReferrerRequest { address: string referrer?: number promotionCode?: string publicKeyX: string publicKeyY: string } export interface GetAmmUserRewardsRequest { owner: number // accountId ammPoolMarkets?: string // pool-name list } export interface AmmUserReward { market: string feeRewards: string[] extraRewards: TokenVolumeV3[] currentRewards: TokenVolumeV3[] } export interface AmmUserRewardMap { [key: string]: { current?: AmmUserReward lastDay?: AmmUserReward } } export interface GetAmmPoolGameRankRequest { ammPoolMarket: string // symbol AMM-LRC-ETH } export interface GetAmmAssetRequest { poolAddress: string limit?: number } export interface GetAmmPoolGameUserRankRequest { owner: string // address ammPoolMarket: string // symbol AMM-LRC-ETH } export interface GetAmmPoolSnapshotRequest { poolAddress: string } export interface AmmPoolSnapshot { poolName: string poolAddress: string pooled: [TokenVolumeV3, TokenVolumeV3] lp: TokenVolumeV3 risky: boolean } export interface AmmPoolRequestPatch { chainId: ChainId ammName: string poolAddress: string eddsaKey: string } export interface JoinAmmPoolRequest { owner: string poolAddress: string joinTokens: AmmPoolJoinTokens storageIds: number[] fee: string validUntil?: number eddsaSignature?: string ecdsaSignature?: string domainSeparator?: string } export interface JoinAmmPoolResult { hash: string status: TxStatus isIdempotent: boolean } export interface ExitAmmPoolRequest { owner: string poolAddress: string exitTokens: AmmPoolExitTokens storageId: number maxFee: string validUntil?: number eddsaSignature?: string ecdsaSignature?: string domainSeparator?: string } export interface ExitAmmPoolResult { hash: string status: TxStatus isIdempotent: boolean } export interface GetAmmPoolTradesRequest { ammPoolAddress: string limit?: number offset?: number } export interface AmmPoolTrade { accountId: number orderHash: string market: string side: Side size: string price: number feeAmount: string createdAt: number } export interface GetAmmPoolTxsRequest { poolAddress: string billType?: BillType start?: number end?: number limit?: number offset?: number tokenId?: number income?: boolean transferAddress?: string fromAddress?: string } export interface AmmPoolTxOld { id: number from: string to: string token: string amount: string tokenF: string amountF: string status: TxStatus txHash: string billType: BillType income: boolean timestamp: number memo: string price: string transferType: TransferType label: string } export interface TokenVolumeV4 { tokenId: number amount: string actualAmount: string feeAmount: string } export interface AmmPoolTx { hash: string txType: AmmTxType txStatus: TxStatus ammPoolAddress: string ammLayerType: string poolTokens: [TokenVolumeV4, TokenVolumeV4] lpToken: TokenVolumeV4 createdAt: number updatedAt: number } export enum AMMtxTypes { JOIN = 0, EXIT = 1, } export interface GetUserAmmPoolTxsRequest { accountId: number start?: number end?: number limit?: number offset?: number txTypes?: AMMtxTypes // combine of AmmTxType txStatus?: TxStatus ammPoolAddress?: string } export interface PooledToken { tokenId: number amount: string actualAmount: string feeAmount: string } export interface UserAmmPoolTx { hash: string txType: AmmTxType txStatus: TxStatus ammPoolAddress: string ammLayerType: string poolTokens: [PooledToken, PooledToken] lpToken: PooledToken createdAt: number updatedAt: number } export interface GetLiquidityMiningRequest { accountId: number market: string size: number } export interface RewardItem { startAt: number timeInterval: string accountId: number tokenId: number market: string score: number amount: string } export interface GetLiquidityMiningUserHistoryRequest { accountId: number start?: number end?: number } export interface UserMiningInfo { account_id: number market: string start: number end: number awards: TokenVolumeV3[] } export interface GetFiatPriceRequest { legal: string } export interface FiatPriceInfo { symbol: string price: number updatedAt: number } export interface GetMarketTradesRequest { market: string limit?: number } export enum OrderMakerType { taker = 'taker', maker = 'maker', } export interface MarketTradeInfo { tradeTime: number tradeId: string side: Side volume: string price: string market: string fee: string type: OrderMakerType } export interface GetWithdrawalAgentsRequest { tokenId: number amount: string } export interface GetEthBalancesRequest { owner: string } export type TokenAddress = string export interface GetTokenBalancesRequest { owner: string token: TokenAddress[] } export interface GetALLTokenBalancesRequest { owner: string } export interface GetAllowancesRequest { owner: string token: TokenAddress[] // tokenAddress } export interface GetDepthRequest { market: string level?: number limit?: number } export interface GetTickerRequest { market: string } export interface GetCandlestickRequest { market: string interval: TradingInterval start?: number end?: number limit?: number } export type GetAccountRequest = | { owner: string } | { accountId: number } export interface GetCounterFactualInfoRequest { accountId: number } export interface GetAvailableBrokerRequest { type: number } export interface GetEthNonceRequest { owner: string } export interface GetUserApiKeyRequest { accountId: number } export interface UpdateUserApiKeyRequest { accountId: number } export type GetOffchainFeeAmtRequest = | ({ accountId: number amount?: string } & { requestType: Omit< OffchainFeeReqType, | OffchainFeeReqType.OFFCHAIN_WITHDRAWAL | OffchainFeeReqType.AMM_JOIN | OffchainFeeReqType.AMM_EXIT | OffchainFeeReqType.ORDER | OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL > }) | { requestType: | OffchainFeeReqType.OFFCHAIN_WITHDRAWAL | OffchainFeeReqType.AMM_JOIN | OffchainFeeReqType.AMM_EXIT | OffchainFeeReqType.ORDER tokenSymbol: string } | { requestType: OffchainFeeReqType.DEFI_EXIT | OffchainFeeReqType.DEFI_JOIN market: string } | { requestType: OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL tokenSymbol: string amount: string } | { requestType: OffchainFeeReqType.RABBIT_OFFCHAIN_WITHDRAWAL tokenSymbol: string amount: string } | { requestType: OffchainFeeReqType.EXTRA_TYPES; extraType: any } /** * @methodOf OffchainNFTFeeReqType.NFT_MINT * @requires tokenAddress * * @methodOf {} OffchainNFTFeeReqType.NFT_WITHDRAWAL * @param deployInWithdraw */ export type GetNFTOffchainFeeAmtRequest = { accountId: number amount?: string } & XOR< { requestType: Omit< OffchainNFTFeeReqType, | OffchainNFTFeeReqType.NFT_MINT | OffchainNFTFeeReqType.NFT_WITHDRAWAL | OffchainNFTFeeReqType.EXTRA_TYPES > }, | { requestType: OffchainNFTFeeReqType.NFT_MINT tokenAddress: string } | { requestType: OffchainNFTFeeReqType.NFT_WITHDRAWAL tokenAddress: string deployInWithdraw?: boolean } | { requestType: OffchainNFTFeeReqType.EXTRA_TYPES; extraType: any } > export interface OrderInfo { minAmount: string makerRate: number takerRate: number } export interface TokenAmount { tokenSymbol: string discount: number baseOrderInfo: OrderInfo userOrderInfo: OrderInfo tradeCost: string } export interface GetMinimumTokenAmtRequest { accountId: number market: string } export interface OffchainFeeInfo { token: string fee: string discount: number } export interface GetUserBalancesRequest { accountId: number tokens: string } // export interface GetUserBalancesRequest { // accountId: number; // tokens: string; // *accountId 10106 // *tokenId 1 // *status 10106 // *lockTag DUAL_CURRENCY,DUAL_BASE // offset 3 // limit // hash // delegatorAccountId // start // } export interface UserBalanceInfo { tokenId: number total: string locked: string pending: { withdraw: string deposit: string } } export interface GetOrderDetailsRequest { accountId: number orderHash: string } export interface OrderDetail { hash: string clientOrderId: string side: Side market: string price: string volumes: { baseAmount: string quoteAmount: string baseFilled: string quoteFilled: string fee: string } validity: { start: number; end: number } orderType: OrderTypeResp tradeChannel: TradeChannel status: OrderStatus extraOrderType?: boolean stopPrice?: string stopSide: STOP_SIDE } export interface GetUserOrderFeeRateRequest { accountId: number market: string tokenB: number amountB: string } export interface FeeRateInfo { symbol: string makerRate: number takerRate: number } export interface GetUserFeeRateRequest { accountId: number markets: string } export interface UserFeeRateInfo { symbol: string makerRate: number takerRate: number } export interface GetNextStorageIdRequest { accountId: number sellTokenId: number } /** * * @export * @interface OffChainWithdrawalRequestV3 */ export interface OffChainWithdrawalRequestV3 { /** * exchange address * @type {string} * @memberof OffChainWithdrawalRequestV3 */ exchange: string /** * account ID * @type {number} * @memberof OffChainWithdrawalRequestV3 */ accountId: number /** * account owner address * @type {string} * @memberof OffChainWithdrawalRequestV3 */ owner: string /** * * @type {TokenVolumeV3} * @memberof OffChainWithdrawalRequestV3 */ token: TokenVolumeV3 /** * * @type {TokenVolumeV3} * @memberof OffChainWithdrawalRequestV3 */ maxFee: TokenVolumeV3 /** * offchain ID * @type {number} * @memberof OffChainWithdrawalRequestV3 */ storageId: number /** * Timestamp for order to become invalid * @type {number} * @memberof OffChainWithdrawalRequestV3 */ validUntil: number /** * min gas for on-chain withdraw, Loopring exchange allocates gas for each distribution, * but people can also assign this min gas, * so Loopring has to allocate higher gas value for this specific distribution. * Normally no need to take care of this value, * 0 means let loopring choose the reasonable gas * @type {number} * @memberof OffChainWithdrawalRequestV3 */ minGas: number /** * withdraw to address * @type {string} * @memberof OffChainWithdrawalRequestV3 */ to: string /** * extra data for complex withdraw mode, normally none * @type {string} * @memberof OffChainWithdrawalRequestV3 */ extraData?: string /** * is fast withdraw mode * @type {boolean} * @memberof OffChainWithdrawalRequestV3 */ fastWithdrawalMode?: boolean /** * eddsa signature * @type {string} * @memberof OffChainWithdrawalRequestV3 */ eddsaSignature?: string /** * ecdsa signature * @type {string} * @memberof OffChainWithdrawalRequestV3 */ ecdsaSignature?: string /** * An approved hash string which was already submitted on eth mainnet * @type {string} * @memberof OffChainWithdrawalRequestV3 */ hashApproved?: string counterFactualInfo?: CounterFactualInfo } export enum EXTRA_ORDER_TYPES { TRADITIONAL_ORDER = 'TRADITIONAL_ORDER', STOP_LIMIT = 'STOP_LIMIT', TRAILING_STOP = 'TRAILING_STOP', OCO = 'OCO', } export interface GetOrdersRequest { accountId: number market?: string start?: number end?: number side?: Side[] status?: string[] tradeChannels?: string[] limit?: number offset?: number orderTypes?: OrderType extraOrderTypes?: string } export interface GetUserRegTxsRequest { accountId: number start?: number end?: number status?: string limit?: number offset?: number } export interface UserRegTx { id: number hash: string owner: string txHash: string feeTokenSymbol: string feeAmount: number status: TxStatus progress: string timestamp: number blockNum: number updatedAt: number } export interface GetUserPwdResetTxsRequest { accountId: number start?: number end?: number status?: string limit?: number offset?: number } export type UserPwdResetTx = UserRegTx export interface GetUserDepositHistoryRequest { accountId?: number hashes?: string start?: number end?: number status?: string limit?: number tokenSymbol?: string offset?: number } export interface UserDepositHistoryTx { id: number hash: string symbol: string amount: string txHash: string status: TxStatus progress: string timestamp: number blockNum: number updatedAt: number } export interface UserOnchainWithdrawalHistoryTx { id: number txType: WithdrawalTypes hash: string symbol: string amount: string txHash: string feeTokenSymbol: string feeAmount: string status: TxStatus progress: string timestamp: number blockNum: number updatedAt: number distributeHash: string requestId: number fastStatus: string } export interface GetUserOnchainWithdrawalHistoryRequest { accountId?: number hashes?: string start?: number end?: number status?: string limit?: number tokenSymbol?: string offset?: number withdrawalTypes?: string } export interface GetUserTransferListRequest { accountId?: number hashes?: string start?: number end?: number status?: string limit?: number tokenSymbol?: string offset?: number transferTypes?: string // transfer, transfer_red } export interface UserTransferRecord { id: number hash: string txType: TxType symbol: string amount: string senderAddress: string receiver: number receiverAddress: string feeTokenSymbol: string feeAmount: string status: TxStatus progress: string timestamp: number updatedAt: number memo: string } export interface UserAssetInfo { amount: string createdAt: number createdAtStr: string } export interface GetUserAssetsRequest { wallet: string assetType?: AssetType currency?: Currency limit?: number offset?: number network?: NetworkWallet } export interface GetUserTradeAmount { accountId: number markets?: string limit?: number } export interface TokenPriceInfo { price: string createdAt: number } export interface GetTokenPricesRequest { token: string intervalType?: IntervalType currency?: Currency limit?: number network?: NetworkWallet } export interface getLatestTokenPricesRequest { tokens?: string currency?: string } export interface GetUserTxsRequest { accountId: number tokenSymbol?: string start?: number end?: number offset?: number limit?: number types?: UserTxTypes[] | string } export interface GetUserBillsRequest { accountId: number fromAddress?: string transferAddress?: string start?: number end?: number offset?: number limit?: number billType?: UserBillTypes[] } export interface GetReferSelf { accountId: string offset?: number limit?: number start?: number end?: number } export interface ReferSelf { accountId: string lrcAmount: string startAt: number endAt: number } export enum GetReferStatisticReason { Recommender = 9, Invited = 1, } export interface GetReferStatistic { accountId: string reason: GetReferStatisticReason } export interface ReferStatistic { accountId: number totalProfit: string claimableProfit: string tradeNum: number reason: string createdAt: number updatedAt: number downsidesNum: number } export interface GetReferDownsides { accountId: string offset?: number limit?: number start?: number end?: number } export interface ReferDownsides { address: string lrcAmount: string startAt: number endAt: number } export enum REWARD_TYPE { MakerRewards = 1, ReferralRewards = 2, RefereeRewards = 3, } export interface GetUserRewardRequest { rewardType: REWARD_TYPE accountId: number size?: number } export interface GetUserNFTTxsRequest { accountId: number // tokenSymbol?: string; metadata?: boolean start?: number end?: number offset?: number limit?: number types?: UserNFTTxTypes[] | string } export declare enum NFT_TRADE { SELL = 'SELL', BUY = 'BUY', } export interface GetUserNFTTradeRequest { accountId: number nftData?: boolean orderHash?: string tradeHash?: string start?: number end?: number side: NFT_TRADE offset?: number limit?: number metadata?: boolean } export interface UserTx { id: number txType: string hash: string symbol: string amount: string receiver: number txHash: string feeTokenSymbol: string feeAmount: string status: TxStatus progress: string timestamp: number blockNum: number updatedAt: number distributeHash: string receiverAddress: string senderAddress: string memo: string requestId: number fastStatus: string recipient: string } /** * @param {string} guardian address */ export interface SendMetaTxRequest { wallet: string module: string value: string data: string nonce: string validUntil: string gasToken: string gasPrice: string gasLimit: string gasOverhead: string feeRecipient: string signatures: string signers: string metaTxType: 0 requestId: string securityId: string guardianType: string network?: NetworkWallet } /** * @param {string} guardian address */ export interface GetGuardianApproveListRequest { guardian: string network?: NetworkWallet } /** * @param {string} owner address */ export interface GetEnsNameRequest { owner: string network?: NetworkWallet } export interface GET_WALLET_TYPE { wallet: string network?: NetworkWallet } /** * @param {string} fullName ENSName */ export interface GetEnsAddressRequest { fullName: string network?: NetworkWallet } export interface SubmitApproveSignatureRequestWithPatch { request: ApproveSignatureRequest guardian: Guardian web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } /** * @param {string} approveRecordId * @param {string} securityNumber * @param {string} txAwareHash * @param {string} signer * @param {string} wallet * @param {string} signature */ export interface ApproveSignatureRequest { approveRecordId: string txAwareHash?: string //currentRequest.messageHash, securityNumber: string signer: string //address, signature: string network?: NetworkWallet } /** * @param {string} guardian address * @param {string} protectAddress? address */ export interface GetProtectorRequest { guardian: string protectAddress?: string network?: NetworkWallet } export enum TradesFillTypes { dex = 'dex', amm = 'amm', } export interface GetUserTradesRequest { accountId: number market?: string orderHash?: string offset?: number limit?: number fromId?: number fillTypes?: TradesFillTypes } export type UserTrade = MarketTradeInfo export interface UserTrades { totalNum: number trades: UserTrade[] } export interface CancelOrderRequest { accountId: number clientOrderId?: string orderHash?: string } export interface CancelMultiOrdersByHashRequest { accountId: number orderHash: string // comma seprated string } export interface CancelMultiOrdersByClientOrderIdRequest { accountId: number clientOrderId: string // comma seprated string } export enum EXTRAORDER_TYPE { TRADITIONAL_ORDER = 'TRADITIONAL_ORDER', STOP_LIMIT = 'STOP_LIMIT', } export enum STOP_SIDE { NO_CONDITION = 'NO_CONDITION', GREAT_THAN_AND_EQUAL = 'GREAT_THAN_AND_EQUAL', LESS_THAN_AND_EQUAL = 'LESS_THAN_AND_EQUAL', } export interface SubmitOrderRequestV3 { /** * The adderss of the exchange which has to process this order * @type {string} * @memberof SubmitOrderRequestV3 */ exchange: string /** * Loopring\'s account ID * @type {number} * @memberof SubmitOrderRequestV3 */ accountId: number /** * The unique identifier of the L2 Merkle tree storage slot where the burn made in order to exit the pool will or has been stored. * @type {number} * @memberof SubmitOrderRequestV3 */ storageId: number /** * * @type {TokenVolumeV3} * @memberof SubmitOrderRequestV3 */ sellToken: TokenVolumeV3 /** * * @type {TokenVolumeV3} * @memberof SubmitOrderRequestV3 */ buyToken: TokenVolumeV3 /** * Whether the order supports partial fills or not.Currently only supports false as a valid value * @type {string} * @memberof SubmitOrderRequestV3 */ allOrNone: boolean /** * Fill size by buy token or by sell token * @type {string} * @memberof SubmitOrderRequestV3 */ fillAmountBOrS: boolean /** * Order expiration time, accuracy is in seconds * @type {number} * @memberof SubmitOrderRequestV3 */ validUntil: number /** * Maximum order fee that the user can accept, value range (in ten thousandths) 1 ~ 63 * @type {number} * @memberof SubmitOrderRequestV3 */ maxFeeBips: number /** * The orders EdDSA signature. The signature is a hexadecimal string obtained by signing the order itself and concatenating the resulting signature parts (Rx, Ry, and S). Used to authenticate and authorize the operation. * @type {string} * @memberof SubmitOrderRequestV3 */ eddsaSignature: string /** * An arbitrary, client-set unique order identifier, max length is 120 bytes * @type {string} * @memberof SubmitOrderRequestV3 */ clientOrderId?: string /** * Order types, can be AMM, LIMIT_ORDER, MAKER_ONLY, TAKER_ONLY * @type {string} * @memberof SubmitOrderRequestV3 */ orderType?: OrderType /** * Used by the P2P order which user specify the taker, so far its 0x0000000000000000000000000000000000000000 * @type {string} * @memberof SubmitOrderRequestV3 */ tradeChannel?: TradeChannel taker?: string /** * The AMM pool address if order type is AMM * @type {string} * @memberof SubmitOrderRequestV3 */ poolAddress?: string /** * Aux data to mark the order source * @type {string} * @memberof SubmitOrderRequestV3 */ channelId?: string extraOrderType?: EXTRAORDER_TYPE stopPrice?: string stopSide?: STOP_SIDE } /** * Submit internal transfer params * @export * @interface OriginTransferRequestV3 */ export interface OriginTransferRequestV3 { /** * exchange address * @type {string} * @memberof OriginTransferRequestV3 */ exchange: string /** * payer account ID * @type {number} * @memberof OriginTransferRequestV3 */ payerId: number /** * payer account address * @type {string} * @memberof OriginTransferRequestV3 */ payerAddr: string /** * payee account ID * @type {number} * @memberof OriginTransferRequestV3 */ payeeId: number /** * payee account address * @type {string} * @memberof OriginTransferRequestV3 */ payeeAddr: string /** * * @type {TokenVolumeV3} * @memberof OriginTransferRequestV3 */ token: TokenVolumeV3 /** * * @type {TokenVolumeV3} * @memberof OriginTransferRequestV3 */ maxFee: TokenVolumeV3 /** * offchain Id * @type {number} * @memberof OriginTransferRequestV3 */ storageId: number /** * Timestamp for order to become invalid * @type {number} * @memberof OriginTransferRequestV3 */ validUntil: number /** * eddsa signature * @type {string} * @memberof OriginTransferRequestV3 */ eddsaSignature?: string /** * ecdsa signature * @type {string} * @memberof OriginTransferRequestV3 */ ecdsaSignature?: string /** * An approved hash string which was already submitted on eth mainnet * @type {string} * @memberof OriginTransferRequestV3 */ hashApproved?: string /** * transfer memo * @type {string} * @memberof OriginTransferRequestV3 */ memo?: string /** * A user-defined id * @type {string} * @memberof OriginTransferRequestV3 */ clientId?: string /** * CounterFactualInfo * @type {CounterFactualInfo} * @memberof OriginTransferRequestV3 */ counterFactualInfo?: CounterFactualInfo /** * If true, let the sender transferring to the receiver pay the receiver's account activation fee * @type {boolean} * @memberof OriginTransferRequestV3 */ payPayeeUpdateAccount?: boolean } /** * Submit Forces Withdrawals params * @export * @interface OriginForcesWithdrawalsV3 */ export interface OriginForcesWithdrawalsV3 { /** * requesterAddress account address * @type {string} * @memberof OriginForcesWithdrawalsV3 */ requesterAddress: string /** * requester withdrawls tokenId * @type {number} * @memberof OriginForcesWithdrawalsV3 */ tokenId: number /** * withdrawAddress account address * @type {string} * @memberof OriginForcesWithdrawalsV3 */ withdrawAddress: string /** * Transfer Request * @type {OriginTransferRequestV3} * @memberof OriginForcesWithdrawalsV3 */ transfer: Omit & { payeeId?: 0 memo?: string maxFee?: { volume: '0' tokenId: number | string } } } /** * Submit Deploy NFTAction params * @export * @interface OriginDeployNFTRequestV3 */ export interface OriginDeployNFTRequestV3 { /** * Transfer * @type {OriginTransferRequestV3} * @memberof OriginDeployNFTRequestV3 */ transfer: Omit & { payeeId?: 0 memo?: string maxFee?: { volume: '0' tokenId: number | string } } /** * nftData * @type {string} * @memberof OriginDeployNFTRequestV3 */ nftData: string /** * NFTAction address * @type {string} * @memberof OriginDeployNFTRequestV3 */ tokenAddress: string counterFactualInfo?: CounterFactualInfo } export interface OriginDeployCollectionRequestV3 { /** * Transfer * @type {OriginTransferRequestV3} * @memberof OriginDeployNFTRequestV3 */ transfer: Omit & { payeeId?: 0 memo?: string maxFee?: { volume: '0' tokenId: number | string } } /** * nftOwner * @type {string} * @memberof OriginDeployCollectionRequestV3 */ nftOwner: string /** * nftBaseUri * @type {string} * @memberof OriginDeployCollectionRequestV3 */ nftBaseUri: string /** * nftFactory * @type {string} * @memberof OriginDeployCollectionRequestV3 */ nftFactory: string /** * tokenAddress * @type {string} * @memberof OriginDeployCollectionRequestV3 */ tokenAddress: string counterFactualInfo?: CounterFactualInfo } /** * Submit internal transfer params * @export * @interface OriginNFTTransferRequestV3 */ export interface OriginNFTTransferRequestV3 { /** * exchange address * @type {string} * @memberof OriginNFTTransferRequestV3 */ exchange: string /** * fromAccountId * @type {number} * @memberof OriginNFTTransferRequestV3 */ fromAccountId: number /** * payer account address * @type {string} * @memberof OriginNFTTransferRequestV3 */ fromAddress: string /** * to account ID * @type {number} * @memberof OriginNFTTransferRequestV3 */ toAccountId: number /** * toAddress address * @type {string} * @memberof OriginNFTTransferRequestV3 */ toAddress: string /** * * @type {TokenVolumeNFT} * @memberof OriginNFTTransferRequestV3 */ token: TokenVolumeNFT /** * * @type { Pick & {amount:string}} * @memberof OriginNFTTransferRequestV3 */ maxFee: Pick & { amount: string } /** * offchain Id * @type {number} * @memberof OriginNFTTransferRequestV3 */ storageId: number /** * Timestamp for order to become invalid * @type {number} * @memberof OriginNFTTransferRequestV3 */ validUntil: number /** * eddsa signature * @type {string} * @memberof OriginNFTTransferRequestV3 */ eddsaSignature?: string /** * ecdsa signature * @type {string} * @memberof OriginNFTTransferRequestV3 */ ecdsaSignature?: string /** * An approved hash string which was already submitted on eth mainnet * @type {string} * @memberof OriginNFTTransferRequestV3 */ hashApproved?: string /** * transfer memo * @type {string} * @memberof OriginNFTTransferRequestV3 */ memo?: string /** * A user-defined id * @type {string} * @memberof OriginNFTTransferRequestV3 */ clientId?: string /** * CounterFactualInfo * @type {CounterFactualInfo} * @memberof OriginNFTTransferRequestV3 */ counterFactualInfo?: CounterFactualInfo /** * If true, let the sender transferring to the receiver pay the receiver's account activation fee * @type {boolean} * @memberof OriginNFTTransferRequestV3 */ payPayeeUpdateAccount?: boolean } /** * * @export * @interface NFTWithdrawRequestV3 */ export interface NFTWithdrawRequestV3 { /** * exchange address * @type {string} * @memberof OriginNFTWithdrawRequestV3 */ exchange: string /** * account ID * @type {number} * @memberof OriginNFTWithdrawRequestV3 */ accountId: number /** * account owner address * @type {string} * @memberof OriginNFTWithdrawRequestV3 */ owner: string /** * * @type {TokenVolumeNFT} * @memberof OriginNFTTransferRequestV3 */ token: TokenVolumeNFT /** * * @type {Pick & {amount:string}}; * @memberof OriginNFTTransferRequestV3 */ maxFee: Pick & { amount: string } /** * offchain ID * @type {number} * @memberof OriginNFTWithdrawRequestV3 */ storageId: number /** * Timestamp for order to become invalid * @type {number} * @memberof OriginNFTWithdrawRequestV3 */ validUntil: number /** * min gas for on-chain withdraw, Loopring exchange allocates gas for each distribution, but people can also assign this min gas, so Loopring have to allocate higher gas value for this specific distribution. Normally no need to take care of this value, 0 means let loopring choose the reasonable gas * @type {number} * @memberof OriginNFTWithdrawRequestV3 */ minGas: number /** * withdraw to address * @type {string} * @memberof OriginNFTWithdrawRequestV3 */ to: string /** * extra data for complex withdraw mode, normally none * @type {string} * @memberof OriginNFTWithdrawRequestV3 */ extraData?: string // /** // * is fast withdraw mode // * @type {boolean} // * @memberof OriginNFTWithdrawRequestV3 // */ // fastWithdrawalMode?: boolean; /** * eddsa signature * @type {string} * @memberof OriginNFTWithdrawRequestV3 */ eddsaSignature?: string /** * ecdsa signature * @type {string} * @memberof OriginNFTWithdrawRequestV3 */ ecdsaSignature?: string /** * An approved hash string which was already submitted on eth mainnet * @type {string} * @memberof OriginNFTWithdrawRequestV3 */ hashApproved?: string counterFactualInfo?: CounterFactualInfo } /** * * @export * @interface NFTMintRequestV3 */ export interface NFTMintRequestV3 { /** * exchange address * @type {string} * @memberof OriginNFTMintRequestV3 */ exchange: string /** * account ID * @type {number} * @memberof OriginNFTMintRequestV3 */ minterId: number /** * account owner address * @type {string} * @memberof OriginNFTMintRequestV3 */ minterAddress: string /** * The account receive the minted NFTAction token, now should be minter himself. * @type {number} * @memberof OriginNFTMintRequestV3 */ toAccountId: number /** * The account receive the minted NFTAction token, now should be minter himself. * @type {string} * @memberof OriginNFTMintRequestV3 */ toAddress?: string /** * nftType: 0 for EIP1155, 1 for EIP712. EIP1155 by default. * @type {number} * @memberof OriginNFTMintRequestV3 */ nftType: 0 | 1 /** * Contract address * @type{string} * @memberof OriginNFTTransferRequestV3 */ tokenAddress: string /** * NFT_ID url_id * @type {string} toString(16) * @memberof OriginNFTTransferRequestV3 */ nftId: string /** * The amount of the token * @type {string} * @memberof TokenVolumeV3 */ amount: string /** * Timestamp for order to become invalid * @type {number} * @memberof OriginNFTMintRequestV3 */ validUntil: number /** * offchain ID * @type {number} * @memberof OriginNFTMintRequestV3 */ storageId: number /** * * @type { Pick & {amount:string}} * @memberof OriginNFTTransferRequestV3 */ maxFee: Pick & { amount: string } /** * 0-50 * @type number * @memberof OriginNFTMintRequestV3 */ royaltyPercentage: number /** * force to mint, regardless the previous mint record * @type {boolean} * @memberof OriginNFTMintRequestV3 */ forceToMint?: boolean /** * eddsa signature * @type {string} * @memberof OriginNFTMintRequestV3 */ eddsaSignature?: string /** * ecdsa signature * @type {string} * @memberof OriginNFTMintRequestV3 */ ecdsaSignature?: string /** * An approved hash string which was already submitted on eth mainnet * @type {string} * @memberof OriginNFTMintRequestV3 */ hashApproved?: string counterFactualNftInfo?: NFTCounterFactualInfo | null counterFactualInfo?: CounterFactualInfo } // /** // * // * @export // * @interface NFTCollectionCreateRequestV3 // */ // export interface NFTollectionCreateRequestV3 { // /** // * exchange address // * @type {string} // * @memberof OriginNFTMintRequestV3 // */ // exchange: string; // /** // * account ID // * @type {number} // * @memberof OriginNFTMintRequestV3 // */ // minterId: number; // /** // * account owner address // * @type {string} // * @memberof OriginNFTMintRequestV3 // */ // minterAddress: string; // /** // * The account receive the minted NFTAction token, now should be minter himself. // * @type {number} // * @memberof OriginNFTMintRequestV3 // */ // toAccountId: number; // /** // * The account receive the minted NFTAction token, now should be minter himself. // * @type {string} // * @memberof OriginNFTMintRequestV3 // */ // toAddress?: string; // /** // * nftType: 0 for EIP1155, 1 for EIP712. EIP1155 by default. // * @type {number} // * @memberof OriginNFTMintRequestV3 // */ // nftType: 0 | 1; // /** // * Contract address // * @type{string} // * @memberof OriginNFTTransferRequestV3 // */ // tokenAddress: string; // /** // * NFT_ID url_id // * @type {string} toString(16) // * @memberof OriginNFTTransferRequestV3 // */ // nftId: string; // /** // * The amount of the token // * @type {string} // * @memberof TokenVolumeV3 // */ // amount: string; // // /** // * Timestamp for order to become invalid // * @type {number} // * @memberof OriginNFTMintRequestV3 // */ // validUntil: number; // /** // * offchain ID // * @type {number} // * @memberof OriginNFTMintRequestV3 // */ // storageId: number; // /** // * // * @type { Pick & {amount:string}} // * @memberof OriginNFTTransferRequestV3 // */ // maxFee: Pick & { amount: string }; // /** // * 0-50 // * @type number // * @memberof OriginNFTMintRequestV3 // */ // royaltyPercentage: number; // /** // * force to mint, regardless the previous mint record // * @type {boolean} // * @memberof OriginNFTMintRequestV3 // */ // forceToMint?: boolean; // /** // * eddsa signature // * @type {string} // * @memberof OriginNFTMintRequestV3 // */ // eddsaSignature?: string; // /** // * ecdsa signature // * @type {string} // * @memberof OriginNFTMintRequestV3 // */ // ecdsaSignature?: string; // /** // * An approved hash string which was already submitted on eth mainnet // * @type {string} // * @memberof OriginNFTMintRequestV3 // */ // hashApproved?: string; // counterFactualNftInfo?: NFTCounterFactualInfo | null; // counterFactualInfo?: CounterFactualInfo; // } /** * * @export * @interface NFTOrderRequestV3 */ export type NFTOrderRequestV3 = { /** * exchange address * @type {string} * @memberof NFTOrderRequestV3 */ exchange: string /** * account ID * @type {number} * @memberof NFTOrderRequestV3 */ accountId: number /** * storage ID * @type {number} * @memberof NFTOrderRequestV3 */ storageId: number /** * * @type {boolean} * @memberof NFTOrderRequestV3 */ allOrNone: boolean /** * * @type {boolean} * @memberof NFTOrderRequestV3 */ fillAmountBOrS: boolean /** * Timestamp for order to become invalid * @type {number} * @memberof NFTOrderRequestV3 */ validUntil: number /** * max fee bips. * @type {number | 0} * @memberof NFTOrderRequestV3 */ maxFeeBips?: number | 0 /** * eddsa signature. * @type {string} * @memberof NFTOrderRequestV3 */ eddsaSignature?: string /** * client order id. * @type {string} * @memberof NFTOrderRequestV3 */ clientOrderId?: string /** * order type * @type{string} * @memberof NFTOrderRequestV3 */ orderType?: string /** * trade channel * @type {string} * @memberof NFTOrderRequestV3 */ tradeChannel?: string /** * taker address * @type {string} * @memberof NFTOrderRequestV3 */ taker?: string /** * affiliate account id * @type {string} * @memberof NFTOrderRequestV3 */ affiliate?: string } & XOR< { /** * maker order * sell token info * @type {NFTTokenAmountInfo} * @memberof NFTOrderRequestV3 */ sellToken: NFTTokenAmountInfo /** * buy token info * @type {TokenVolumeV5} * @memberof NFTOrderRequestV3 */ buyToken: TokenVolumeV5 }, { /** * taker order * sell token info * @type {TokenVolumeV5} * @memberof NFTOrderRequestV3 */ sellToken: TokenVolumeV5 /** * maker Order * buy token info * @type {NFTTokenAmountInfo} * @memberof NFTOrderRequestV3 */ buyToken: NFTTokenAmountInfo } > /** * * @export * @interface NFTTradeRequestV3 */ export interface NFTTradeRequestV3 { /** * maker order * @type {NFTOrderRequestV3} * @memberof NFTTradeRequestV3 */ maker: NFTOrderRequestV3 /** * maker fee bips * @type {number} * @memberof NFTOrderRequestV3 */ makerFeeBips: number /** * taker order * @type {NFTOrderRequestV3} * @memberof NFTTradeRequestV3 */ taker: NFTOrderRequestV3 /** * taker fee bips * @type {number} * @memberof NFTTradeRequestV3 */ takerFeeBips: number } /** * * @export * @interface OffChainWithdrawalRequestV3 */ export interface OffChainWithdrawalRequestV3 { /** * exchange address * @type {string} * @memberof OffChainWithdrawalRequestV3 */ exchange: string /** * account ID * @type {number} * @memberof OffChainWithdrawalRequestV3 */ accountId: number /** * account owner address * @type {string} * @memberof OffChainWithdrawalRequestV3 */ owner: string /** * * @type {TokenVolumeV3} * @memberof OffChainWithdrawalRequestV3 */ token: TokenVolumeV3 /** * * @type {TokenVolumeV3} * @memberof OffChainWithdrawalRequestV3 */ maxFee: TokenVolumeV3 /** * offchain ID * @type {number} * @memberof OffChainWithdrawalRequestV3 */ storageId: number /** * Timestamp for order to become invalid * @type {number} * @memberof OffChainWithdrawalRequestV3 */ validUntil: number /** * min gas for on-chain withdraw, Loopring exchange allocates gas for each distribution, but people can also assign this min gas, so Loopring have to allocate higher gas value for this specific distribution. Normally no need to take care of this value, 0 means let loopring choose the reasonable gas * @type {number} * @memberof OffChainWithdrawalRequestV3 */ minGas: number /** * withdraw to address * @type {string} * @memberof OffChainWithdrawalRequestV3 */ to: string /** * extra data for complex withdraw mode, normally none * @type {string} * @memberof OffChainWithdrawalRequestV3 */ extraData?: string /** * is fast withdraw mode * @type {boolean} * @memberof OffChainWithdrawalRequestV3 */ fastWithdrawalMode?: boolean /** * eddsa signature * @type {string} * @memberof OffChainWithdrawalRequestV3 */ eddsaSignature?: string /** * ecdsa signature * @type {string} * @memberof OffChainWithdrawalRequestV3 */ ecdsaSignature?: string /** * An approved hash string which was already submitted on eth mainnet * @type {string} * @memberof OffChainWithdrawalRequestV3 */ hashApproved?: string } /** * Describes the users public key which is a point of the selected eclipse curve. * @export * @interface PublicKey */ export interface UpdateAccountRequestV3 { /** * exchange address * @type {string} * @memberof UpdateAccountRequestV3 */ exchange: string /** * owner address * @type {string} * @memberof UpdateAccountRequestV3 */ owner: string /** * user account ID * @type {number} * @memberof UpdateAccountRequestV3 */ accountId: number /** * * @type {PublicKey} * @memberof UpdateAccountRequestV3 */ publicKey: PublicKey /** * * @type {TokenVolumeV3} * @memberof UpdateAccountRequestV3 */ maxFee: TokenVolumeV3 /** * Timestamp for order to become invalid * @type {number} * @memberof UpdateAccountRequestV3 */ validUntil: number /** * Nonce of users exchange account that used in off-chain requests. * @type {number} * @memberof UpdateAccountRequestV3 */ nonce: number /** * eddsa signature of this request * @type {string} * @memberof UpdateAccountRequestV3 */ eddsaSignature?: string /** * ecdsa signature of this request * @type {string} * @memberof UpdateAccountRequestV3 */ ecdsaSignature?: string /** * An approved hash string which was submitted on eth mainnet * @type {string} * @memberof UpdateAccountRequestV3 */ hashApproved?: string keySeed?: string recommenderAccountId?: number counterFactualInfo?: CounterFactualInfo } export interface OffChainWithdrawalRequestV3WithPatch { request: OffChainWithdrawalRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginTransferRequestV3WithPatch { request: OriginTransferRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginForcesWithdrawalsRequestV3WithPatch { request: OriginForcesWithdrawalsV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginDeployNFTRequestV3WithPatch { request: OriginDeployNFTRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginDeployCollectionRequestV3WithPatch { request: OriginDeployCollectionRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginNFTTransferRequestV3WithPatch { request: OriginNFTTransferRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginNFTWithdrawRequestV3WithPatch { request: NFTWithdrawRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginNFTMINTRequestV3WithPatch { request: NFTMintRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginNFTCreateCollectionRequestV3WithPatch { // request: NFTMintRequestV3; // web3: Web3; // chainId: ChainId; // walletType: ConnectorNames; // eddsaKey: string; // apiKey: string; // isHWAddr?: boolean; } export interface OriginNFTValidateOrderRequestV3WithPatch { request: NFTOrderRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginNFTTradeRequestV3WithPatch { request: NFTTradeRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface UpdateAccountRequestV3WithPatch { request: UpdateAccountRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames isHWAddr?: boolean privateKey?: string } export interface GetAccountServicesRequest { phone?: string email?: string wallet?: string } // NFTAction export interface GetUserNFTBalancesRequest { accountId: number nftDatas?: string tokenAddrs?: string tokenIds?: string offset?: number limit?: number nonZero?: boolean metadata?: boolean } export interface GetUserNFTBalancesByCollectionRequest { accountId: number tokenAddress: string collectionId: number offset?: number limit?: number nonZero?: boolean metadata?: boolean favourite?: boolean hidden?: boolean } export enum LegacyNFT { inside = 'inside', outside = 'outside', undecided = 'undecided', } export interface GetUserNFTLegacyBalanceRequest { accountId: number tokenAddress: string collectionId: number filter?: LegacyNFT offset?: number limit?: number metadata?: boolean } export enum DEPLOYMENT_STATUS { NOT_DEPLOYED = 'NOT_DEPLOYED', DEPLOY_FAILED = 'DEPLOY_FAILED', DEPLOYING = 'DEPLOYING', DEPLOYED = 'DEPLOYED', } export enum NFT_IMAGE_SIZES { small = '240-240', large = '332-332', original = 'original', } export type IPFS_METADATA = { uri: string base: { name: string decimals: number description: string image: string properties: string localization: string } imageSize: { [P in NFT_IMAGE_SIZES]?: string } extra: { imageData: string externalUrl: string attributes: string backgroundColor: string animationUrl: string youtubeUrl: string minter: string } nftId?: string nftType: NFTType network: 0 tokenAddress: string tokenId: string } export interface UserNFTBalanceInfo extends NFTTokenInfo { accountId: number tokenId: number total?: string locked?: string pending: { withdraw: string deposit: string } preference: { favourite: boolean hide: boolean } collectionInfo: CollectionMeta metadata?: IPFS_METADATA deploymentStatus: DEPLOYMENT_STATUS isCounterFactualNFT: boolean } export interface GetUserVIPInfoRequest { userAddress: string } export interface getUserVIPAssetsRequest { address: string currency?: string assetTypes?: string token?: string limit?: number } export type NftData = string export interface NFTTokenInfo { nftData: string minter: string nftType: string tokenAddress: string nftId: string status: boolean } export interface NFTTokenAmountInfo { tokenId: number nftData?: string amount: string } export type GetUserNFTTransferHistoryRequest = { accountId: number nftData?: string start?: number end?: number hashes?: string txStatus?: string limit?: number // offset?: number // transferTypes?: string // transfer, transfer_red } export type GetUserNFTMintHistoryRequest = { accountId: number nftData?: string start?: number startId?: number end?: number hashes?: string txStatus?: string limit?: number // offset?: number // transferTypes?: string // transfer, transfer_red } export type GetUserNFTDepositHistoryRequest = { accountId: number nftData?: string start?: number end?: number startId?: number hashes?: string txStatus?: string limit?: number } export type GetUserNFTWithdrawalHistoryRequest = { accountId: number nftData?: string start?: number end?: number startId?: number hashes?: string txStatus?: string limit?: number } export interface UserNFTDepositHistoryTx { id: number requestId: number nftData: string amount: string hash: string txHash: string accountId: number owner: string status: TxStatus progress: string timestamp: number blockId: number indexInBlock: number createdAt: number updatedAt: number feeTokenSymbol: string feeAmount: string memo?: string depositFrom: string depositFromAccountId: string } export interface UserNFTWithdrawalHistoryTx { id: number requestId: number hash: string txHash: string accountId: number owner: string status: string nftData?: string amount?: string feeTokenSymbol?: string feeAmount: string createdAt: number updatedAt: number memo?: string recipient: string distributeHash: string fastWithdrawStatus: string isFast: false blockIdInfo: { blockId: number indexInBlock: number } storageInfo: { accountId: number tokenId: number storageId: number } } export interface UserNFTTransferHistoryTx { id: string requestId: number hash: string txHash: string accountId: number owner: string status: string nftData: string amount: string feeTokenSymbol: string feeAmount: string createdAt: number updatedAt: number memo: string payeeId: number payeeAddress: string blockIdInfo: { blockId: number indexInBlock: number } storageInfo: { accountId: number tokenId: number storageId: number } } export type UserNFTMintHistoryTx = { id: string requestId: number hash: string txHash: string accountId: number owner: string status: string nftData: string amount: string feeTokenSymbol: string feeAmount: string createdAt: number updatedAt: number memo: string minterId: number minterAddress: string blockIdInfo: { blockId: number indexInBlock: number } storageInfo: { accountId: number tokenId: number storageId: number } } export interface UserNFTTxsHistory { id: string requestId: number hash: string txHash: string accountId: number owner: string nftData: string amount: string feeTokenSymbol: string feeAmount: string createdAt: number updatedAt: number memo: string payeeId: number payeeAddress: string nftTxType: string symbol: string receiver: number status: TxStatus progress: string timestamp: number blockNum: number distributeHash: string receiverAddress: string senderAddress: string fastStatus: string recipient: string minterInfo: { accountId: number; minter: string; originalMinter: string } nftStatusInfo: {} withdrawalInfo: { distributeHash: string fastStatus: boolean recipient: string } metadata?: IPFS_METADATA storageInfo: { accountId: number tokenId: number storageId: number } } export declare type NFTOrderInfo = { orderHash: string accountId: number feeAmount: string storageId: number address: string } export interface UserNFTTradeHistory { fillId: number nftHash: string feeTokenId: number price: string nftAmount: string feeAmount: string feeTokenSymbol: string createdAt: number hash: string blockId: number indexInBlock: number tokenId: number counter: number tokenAddress: number sInfo: NFTOrderInfo bInfo: NFTOrderInfo metadata: IPFS_METADATA } export type Protector = { ens: string address: string lockStatus: HEBAO_LOCK_STATUS } export type HebaoOperationLog = { createdAt: number ens: string from: string hebaoTxType: HEBAO_META_TYPE id: number status: 0 | 1 to: string } export type Guardian = { ens: string address: string type: HEBAO_META_TYPE id: string messageHash: string businessDataJson: any signedRequest: any createAt: number } /** * * @export * @interface ApproveHebaoRequest */ export type GuardiaContractAddress = string export interface ApproveHebaoRequestV3WithPatch { request: Guardian & { code: string } web3: Web3 address: string chainId: ChainId guardiaContractAddress: GuardiaContractAddress walletType?: ConnectorNames } export interface RejectHebaoRequestV3WithPatch { request: { approveRecordId: string; signer: string; network?: NetworkWallet } web3: Web3 address: string chainId: ChainId guardiaContractAddress: GuardiaContractAddress walletType?: ConnectorNames } export interface LockHebaoHebaoParam { web3: Web3 from: string wallet: string value?: string | number contractAddress: string gasPrice: string gasLimit: string chainId?: ChainId nonce: number isVersion1: boolean sendByMetaMask?: boolean } export interface HebaoOperationLogs { from: string fromTime: number to?: string offset?: number statues?: string hebaoTxType?: string limit?: number network?: NetworkWallet } export interface WalletType { isInCounterFactualStatus: boolean isContract: boolean loopringWalletContractVersion: string } export interface ContractType { network: string contractVersion: string //V1_x_x"|V2_x_x masterCopy?: string // V2 only walletFactory?: string // V2 only ens?: string walletStatus: number queueStatus: number walletType: number // HEBAO = 0; EOA = 1; isCounterFactual: boolean //isCounterFactual } export interface ModuleType { moduleName: string // FORWARDER_MODULE moduleAddress: string } /** * DefiOrderRequest */ export interface DefiOrderRequest { /** * exchange address * @type {string} * @memberof DefiOrderRequest */ exchange: string /** * storageId * @type {number} * @memberof DefiOrderRequest */ storageId: number /** * accountId * @type {number} * @memberof DefiOrderRequest */ accountId: number /** * sellToken * @type TokenVolumeV3 * @memberof DefiOrderRequest */ sellToken: TokenVolumeV3 /** * buyToken * @type TokenVolumeV3 * @memberof DefiOrderRequest */ buyToken: TokenVolumeV3 /** * Timestamp for order become invalid * @type {number} * @memberof DefiOrderRequest */ validUntil: number /** * fee * @type {string} * @memberof DefiOrderRequest */ fee: string /** * Maximum order fee that the user can accept, value range (in ten thousandths) 1 ~ 63 * @type {number} * @memberof DefiOrderRequest */ maxFeeBips: number /** * fillAmountBOrS * @type boolean * @memberof DefiOrderRequest */ fillAmountBOrS: boolean /** * taker address * @type {string} * @memberof DefiOrderRequest */ taker?: string /** * The orders EdDSA signature. The signature is a hexadecimal string obtained by signing the order itself and concatenating the resulting signature parts (Rx, Ry, and S). Used to authenticate and authorize the operation. * @type {string} * @memberof DefiOrderRequest */ eddsaSignature?: string /** * type * @type {string} * @memberof DefiOrderRequest */ type: string /** * action * @type {string} * @memberof DefiOrderRequest */ action: string } export interface DefiResult { hash: string clientOrderId: string status: TxStatus isIdempotent: boolean } export const SEP = ',' export enum DefiAction { Deposit = 'deposit', Withdraw = 'withdraw', } export interface UserDefiTxsHistory { id: string txType: string action: DefiAction hash: string market: string sellToken: TokenVolumeV3 buyToken: TokenVolumeV3 fee: TokenVolumeV3 status: TxStatus updatedAt: number storageInfo: { accountId: number tokenId: number storageId: number } } export interface GetDefiMarketRequest { defiType?: string } export interface GetUserDefiRewardRequest { accountId: number } export interface GetUserDefiTxRequest { accountId: number offset: number start?: number end?: number limit: number } export type CollectionExtendsKey = { thumbnail?: string cid?: string id?: string contractAddress: string collectionAddress: string deployStatus: DEPLOYMENT_STATUS updatedAt: number createdAt: number nftType: string baseUri: string collectionTitle?: string extra: { mintChannel: string properties: { isCounterFactualNFT: boolean isDeletable: boolean isEditable: boolean isLegacy: boolean isMintable: boolean isPublic: boolean } } cached: { avatar: string banner: string thumbnail: string tileUri: string } isCounterFactualNFT?: boolean isDeletable?: boolean isEditable?: boolean isLegacy?: boolean isMintable?: boolean isPublic?: boolean } export type CollectionBasicMeta = { name: string tileUri: string nftFactory?: string description?: string avatar?: string banner?: string owner: string } export type CollectionDelete = { accountId: number collectionId: number } export type CollectionLegacyMeta = Omit & { accountId: number tokenAddress: string } export enum NFT_PREFERENCE_TYPE { fav = 'fav', hide = 'hide', } export type UpdateNFTLegacyCollectionRequest = { accountId: number nftHashes: string[] collectionId?: number } export type UpdateNFTGroupRequest = { accountId: number nftHashes: string[] collectionId?: number preferenceType: NFT_PREFERENCE_TYPE statusToUpdate: boolean } /** * 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 = CollectionExtendsKey & CollectionBasicMeta export interface GetUserOwnerCollectionRequest { owner: string offset?: number limit?: number tokenAddress?: string isMintable?: boolean } export interface GetUserLegacyCollectionRequest { accountId: string tokenAddress: string offset?: number limit?: number } export interface GetCollectionWholeNFTsRequest { id: number metadata?: boolean offset?: number limit?: number } export interface GetUserNFTCollectionRequest { accountId: string offset?: number limit?: number collectionId?: number tokenAddress?: string } export enum DUAL_TYPE { DUAL_BASE = 'DUAL_BASE', DUAL_CURRENCY = 'DUAL_CURRENCY', } export type GetDualInfosRequest = { baseSymbol: string quoteSymbol: string currency: string dualType: DUAL_TYPE minStrike?: string maxStrike?: string startTime?: number timeSpan?: number limit: number } export type GetDualPricesRequest = { baseSymbol: string productIds: string } export type GetDualRuleRequest = { baseSymbol: string; currency?: string } export type DualBid = { baseProfit: string baseQty: string } export type DualPrice = { productId: string cacheQty: string priceTime: number dualBid: DualBid[] } export type DualIndex = { index: string base: string quote: string indexTime: number } export type DualProductAndPrice = { productId: string base: string quote: string currency: string createTime: number expireTime: number strike: string expired: boolean dualType: DUAL_TYPE ratio: number profit: string baseSize: string } export type DualRulesCoinsInfo = { base: string quote: string currency: string basePrecision: number currencyPrecision: number baseMin: string currencyMin: string baseMax: string currencyMax: string granulation: number baseProfitStep: number } export type DualBalance = { coin: string free: string frozen: string } export enum LABEL_INVESTMENT_STATUS { // INVESTMENT_SUCCEEDED = "INVESTMENT_SUCCEEDED", // INVESTMENT_FAILED = "INVESTMENT_FAILED", // INVESTMENT_RECEIVED = "INVESTMENT_RECEIVED", PAID = 'paid', UNSETTLED = 'unsettled', SETTLED = 'settled', REJECTED = 'rejected', SUCCESS = 'success', RECEIVED = 'received', PROCESSED = 'processed', FAILED = 'failed', PROCESSING = 'processing', CANCELLED = 'cancelled', } export enum SETTLEMENT_STATUS { UNSETTLED = 'UNSETTLED', SETTLED = 'SETTLED', PAID = 'PAID', } export enum DUAL_RETRY_STATUS { NO_RETRY = 'NO_RETRY', RETRYING = 'RETRYING', RETRY_SUCCESS = 'RETRY_SUCCESS', RETRY_FAILED = 'RETRY_FAILED', } export interface GetUserDualTxRequest { accountId: number dualTypes: DUAL_TYPE hashes: string investmentStatuses?: LABEL_INVESTMENT_STATUS settlementStatuses?: SETTLEMENT_STATUS offset?: number start?: number end?: number limit: number retryStatuses?: DUAL_RETRY_STATUS[] } export type DUAL_REINVEST_INFO = { isRecursive: boolean maxDuration: string newStrike: number retryStatus: DUAL_RETRY_STATUS } export interface UserDualTxsHistory { id: string hash: string productId: string dualType: DUAL_TYPE settleRatio: number filled: string dualFilled: string deliveryPrice: number strike: number market: string tokenInfoOrigin: { base: string quote: string currency: string amountIn: string amountOut: string market: string tokenIn: number tokenOut: number } timeOrigin: { expireTime: number createTime: number updateTime: number settlementTime: number } investmentStatus: LABEL_INVESTMENT_STATUS settlementStatus: SETTLEMENT_STATUS dualReinvestInfo?: DUAL_REINVEST_INFO createdAt: number updatedAt: number } /** * DualOrderRequest */ export interface DualOrderRequest { /** * exchange address * @type {string} * @memberof DefiOrderRequest */ exchange: string /** * storageId * @type {number} * @memberof DefiOrderRequest */ storageId: number /** * accountId * @type {number} * @memberof DefiOrderRequest */ accountId: number /** * sellToken * @type TokenVolumeV3 * @memberof DefiOrderRequest */ sellToken: TokenVolumeV3 /** * buyToken * @type TokenVolumeV3 * @memberof DefiOrderRequest */ buyToken: TokenVolumeV3 /** * Timestamp for order become invalid * @type {number} * @memberof DefiOrderRequest */ validUntil: number /** * fee * @type {string} * @memberof DefiOrderRequest */ fee: string /** * Maximum order fee that the user can accept, value range (in ten thousandths) 1 ~ 63 * @type {number} * @memberof DefiOrderRequest */ maxFeeBips: number /** * fillAmountBOrS * @type boolean * @memberof DefiOrderRequest */ fillAmountBOrS: boolean /** * The orders EdDSA signature. The signature is a hexadecimal string obtained by signing the order itself and concatenating the resulting signature parts (Rx, Ry, and S). Used to authenticate and authorize the operation. * @type {string} * @memberof DefiOrderRequest */ eddsaSignature?: string baseProfit: string clientOrderId: string productId: string settleRatio: string expireTime: number /** * If this dual order is recursive * @memberof DefiOrderRequest * @type {boolean} */ isRecursive?: boolean /** * If this dual order is recursive, max expire time of next dual product (1-10days in millieseconds) * @memberof DefiOrderRequest * @type {numbwe} */ maxRecurseProductDuration?: number } export type DualEditRequest = { newOrder?: Omit currentDualHash: string isRecursive: boolean maxDuration: number newStrike: string accountId: null } export type CalDualResult = { sellVol: string quota: string lessEarnVol: string lessEarnTokenSymbol: string greaterEarnVol: string greaterEarnTokenSymbol: string maxSellAmount: string miniSellVol: string feeVol: string | undefined feeTokenSymbol?: string maxFeeBips: number sellToken: TokenInfo } export interface DualUserLockedRequest { accountId: number lockTag: DUAL_TYPE[] status: string } export enum LuckyTokenItemStatusIndex { SUBMITTING = 0, NOT_EFFECTIVE = 1, PENDING = 2, COMPLETED = 3, OVER_DUE = 4, FAILED = 5, } export enum LuckyTokenWithdrawStatus { RECEIVED = 0, PROCESSING = 1, PROCESSED = 2, WITHDRAW_FAILED = 3, PREPARE_FAILED = 4, } export enum LuckyTokenItemStatus { SUBMITTING = 'SUBMITTING', NOT_EFFECTIVE = 'NOT_EFFECTIVE', PENDING = 'PENDING', COMPLETED = 'COMPLETED', OVER_DUE = 'OVER_DUE', FAILED = 'FAILED', } export enum LuckyTokenAmountType { RANDOM = 0, AVERAGE = 1, } export enum LuckyTokenViewType { PUBLIC = 0, PRIVATE = 1, TARGET = 2, } export enum LuckyTokenClaimType { RELAY = 0, COMMON = 1, BLIND_BOX = 2, } export enum ClaimRecordStatus { WAITING_CLAIM = 'WAITING_CLAIM', CLAIMED = 'CLAIMED', EXPIRED = 'EXPIRED', CLAIMING = 'CLAIMING', } export enum BlindBoxStatus { NOT_OPENED = 'NOT_OPENED', OPENED = 'OPENED', EXPIRED = 'EXPIRED', } export type LuckyTokenChampion = { accountId: number address: string ens: string amount: number } export type LuckyTokenAmount = { totalCount: number remainCount: number totalAmount: string remainAmount: string claimedBoxCount: number giftCount: number } export type LuckyTokenType = { partition: LuckyTokenAmountType scope: LuckyTokenViewType mode: LuckyTokenClaimType } export type LuckyTokenInfo = { memo: string signer: string signerUrl: string logoUrl: string } export type LuckyTokenSender = { accountId: number address: string ens: string } export type LuckyTokenItemForReceive = { hash: string sender: LuckyTokenSender champion: LuckyTokenChampion tokenId: number tokenAmount: LuckyTokenAmount type: LuckyTokenType status: LuckyTokenItemStatus validSince: number validUntil: number info: LuckyTokenInfo templateNo: number createdAt: number nftTokenInfo?: UserNFTBalanceInfo isNft?: boolean isOfficial?: boolean nftExpireTime: number notifyType?: string serialNo?: number } export type BlindBoxClaimInfo = { // 盲盒信息 id: number hash: string claimer: { accountId: number address: string ens: string } tokenId: number nftHash: string amount: string status: string openTime: string expireTime: string createdAt: number serialNo?: number } export type LuckyTokenBlindBoxItemReceive = { luckyToken: LuckyTokenItemForReceive claim: BlindBoxClaimInfo } export type LuckTokenClaim = { hash: string claimer: { accountId: number address: string ens: string } referrer: { accountId: number address: number ens: string } helper: { accountId: number address: number ens: number } amount: number createdAt: number claimId: number serialNo?: number } export type LuckyTokenSignerFlag = 0 | 1 export type LuckTokenHistory = { champion: LuckyTokenChampion claimAmount: number claim: { id: number } & LuckTokenClaim tokenId: number hash: string helpers: { accountId: number address: number ens: number }[] luckyToken: { id: number } & LuckyTokenItemForReceive } export type LuckTokenClaimDetail = { champion: LuckyTokenChampion claimAmount: number claims: Array< { id: number } & LuckTokenClaim > tokenId: number hash: string helpers: { accountId: number address: number ens: number amount: number }[] luckyToken: { id: number } & LuckyTokenItemForReceive claimStatus: ClaimRecordStatus } export type LuckTokenWithdraw = { id: number hash: string claimer: { accountId: number address: number ens: string } tokenId: number amount: string feeTokenId: number feeAmount: number status: 0 | 1 | 2 // PENDING:0 SUCCESS:1 FAIL:2 createdAt: number updatedAt: number isNft: boolean nftTokenInfo?: UserNFTBalanceInfo } export type TOKENMAPLIST = { tokensMap: LoopringMap coinMap: LoopringMap<{ icon?: string name: string simpleName: string description?: string company: string }> totalCoinMap: LoopringMap<{ icon?: string name: string simpleName: string description?: string company: string }> idIndex: LoopringMap addressIndex: LoopringMap } export interface OriginLuckTokenWithdrawsRequestV3 { tokenId: number feeTokenId: number amount: string claimer: string transfer: Omit & { payeeId?: 0 memo?: string maxFee?: { volume: '0' tokenId: number | string } } nftData?: string luckyTokenHash?: string serialNo?: number } export interface OriginLuckTokenWithdrawsRequestV3WithPatch { request: OriginLuckTokenWithdrawsRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } /** * LuckyTokenItemForSend * */ export type LuckyTokenItemForSendV3 = { type: LuckyTokenType /** * numbers * @type {number} ERC20 [1,10000], NFT [1,20000] * @memberof LuckyTokenItemForSend */ numbers: number // <10000 giftNumbers: number // <10000 memo: string signerFlag: boolean templateId: number validSince: number validUntil: number } & ( | { luckyToken: OriginTransfer3RequestV3 /** * nftData * @type {string} NFT required * @memberof LuckyTokenItemForSend */ nftData: string } | { luckyToken: OriginTransfer3RequestV3 } ) export interface OriginLuckTokenSendRequestV3WithPatch { request: LuckyTokenItemForSendV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface OriginTransfer3RequestV3 { /** * exchange address * @type {string} * @memberof OriginNFTTransferRequestV3 */ exchange: string /** * payer account ID * @type {number} * @memberof OriginTransferRequestV3 */ payerId: number /** * payer account address * @type {string} * @memberof OriginTransferRequestV3 */ payerAddr: string /** * payee account ID * @type {number} * @memberof OriginTransferRequestV3 */ payeeId: number /** * payee account address * @type {string} * @memberof OriginTransferRequestV3 */ payeeAddr: string /** * * @type {TokenVolumeV3} * @memberof OriginTransferRequestV3 */ token: string amount: string /** * * @type { Pick & {amount:string}} * @memberof OriginNFTTransferRequestV3 */ feeToken: string maxFeeAmount: string //Pick & { amount: string }; /** * offchain Id * @type {number} * @memberof OriginNFTTransferRequestV3 */ storageId: number /** * Timestamp for order to become invalid * @type {number} * @memberof OriginNFTTransferRequestV3 */ validUntil: number /** * transfer memo * @type {string} * @memberof OriginNFTTransferRequestV3 */ memo?: string /** * eddsa signature * @type {string} * @memberof OriginNFTTransferRequestV3 */ eddsaSig?: string /** * ecdsa signature * @type {string} * @memberof OriginNFTTransferRequestV3 */ counterFactualInfo?: CounterFactualInfo } export type STACKING_PRODUCT = { tokenId: number symbol: string address: string decimals: number status: number apr: string precision: number staked: string rewardPeriod: string minAmount: string maxAmount: string } export enum StakeStatus { received = 'received', locked = 'locked', partial_unlocked = 'partial_unlocked', completely_unlocked = 'completely_unlocked', failed = 'failed', } export type StakeInfoOrigin = { accountId: number tokenId: number stakeAt: number createdAt: number updatedAt: number claimableTime: number apr: string lastDayPendingRewards: string initialAmount: string remainAmount: string totalRewards: string productId: string hash: string status: StakeStatus } export type STACKING_SUMMARY = { totalStaked: string totalLastDayPendingRewards: string totalStakedRewards: string totalClaimableRewards: string staking: StakeInfoOrigin[] } export enum StakeTransactionType { subscribe = 'subscribe', redeem = 'redeem', claim = 'claim', } export type STACKING_TRANSACTIONS = { accountId: number tokenId: number amount: string productId: string hash: string type: StakeTransactionType createdAt: number updatedAt: number } export interface OriginClaimRequestV3 { accountId: number token: TokenVolumeV3 transfer: Omit & { payeeId?: 0 memo?: string } } export type OriginStakeClaimRequestV3 = OriginClaimRequestV3 export interface OriginClaimRequestV3WithPatch { request: OriginClaimRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export type OriginStakeClaimRequestV3WithPatch = Omit & { request: OriginStakeClaimRequestV3 } export interface GetContactsRequest { isHebao: boolean accountId: number limit?: number offset?: number } export const AddressType = { UNKNOWN_ADDRESS: 0, LOOPRING_HEBAO_CF: 100, // hebao LOOPRING_HEBAO_CONTRACT_1_1_6: 2000, LOOPRING_HEBAO_CONTRACT_1_2_0: 2001, LOOPRING_HEBAO_CONTRACT_2_0_0: 2002, LOOPRING_HEBAO_CONTRACT_2_1_0: 2003, LOOPRING_HEBAO_CONTRACT_2_2_0: 2004, LOOPRING_HEBAO_CONTRACT_3_0_0: 2005, LOOPRING_DEX_EOA: 300, //exchange EXCHANGE_OTHER: 4000, EXCHANGE_BINANCE: 4001, EXCHANGE_OKX: 4002, EXCHANGE_HUOBI: 4003, EXCHANGE_COINBASE: 4004, EOA: 5000, EOA_METAMASK: 5001, EOA_COINBASE: 5002, EOA_LEDGER: 5003, CONTRACT: 600, OFFICIAL: 200, } export type AddressTypeKeys = keyof typeof AddressType export interface GetContactsResponse { contacts: { addressType: (typeof AddressType)[AddressTypeKeys] contactAddress: string contactMemo: string contactName: string isFavourite: boolean network: string ownerAccountId: number }[] total: number } export interface CreateContactRequest { accountId: number isHebao: boolean contactAddress: string contactName: string contactMemo?: string network?: string addressType?: (typeof AddressType)[AddressTypeKeys] } export interface UpdateContactRequest { contactAddress: string isHebao: boolean accountId: number contactName?: string contactMemo?: string addressType?: (typeof AddressType)[AddressTypeKeys] } export interface DeleteContactRequest { contactAddress: string isHebao: boolean accountId: number contactName?: string contactMemo?: string } export type BTRADE_MARKET = { market: string //`LRC-USDT`; baseTokenId: number quoteTokenId: number precisionForPrice: number orderbookAggLevels: number precisionForAmount: number precisionForTotal: number enabled: boolean feeBips: number minAmount: { base: string | '' quote: string | '' } btradeAmount: { base: string | '' quote: string | '' } l2Amount: { base: string | '' quote: string | '' } btradeMarket: string //`${BTRADENAME}LRC-USDT`; } export interface OriginBTRADEV3OrderRequest { /** * exchange address * @type {string} * @memberof OriginBTRADEV3OrderRequest */ exchange: string /** * storageId * @type {number} * @memberof OriginBTRADEV3OrderRequest */ storageId: number /** * accountId * @type {number} * @memberof OriginBTRADEV3OrderRequest */ accountId: number /** * sellToken * @type TokenVolumeV3 * @memberof OriginBTRADEV3OrderRequest */ sellToken: TokenVolumeV3 /** * buyToken * @type TokenVolumeV3 * @memberof OriginBTRADEV3OrderRequest */ buyToken: TokenVolumeV3 /** * allOrNone * @description Whether the order supports partial fills or not.Currently only supports false as a valid value * @type boolean * @memberof OriginBTRADEV3OrderRequest */ allOrNone: Boolean /** * fillAmountBOrS * @type boolean * @memberof OriginBTRADEV3OrderRequest */ fillAmountBOrS: boolean /** * Timestamp for order become invalid * @type {number} * @memberof OriginBTRADEV3OrderRequest */ validUntil: number /** * fee * @type {string} * @memberof OriginBTRADEV3OrderRequest */ maxFeeBips: number /** * The orders EdDSA signature. The signature is a hexadecimal string obtained by signing the order itself and concatenating the resulting signature parts (Rx, Ry, and S). Used to authenticate and authorize the operation. * @type {string} * @memberof OriginBTRADEV3OrderRequest */ eddsaSignature?: string clientOrderId: string orderType: OrderTypeResp fastMode: boolean } export type BtradeResult = { exceedDepth: boolean isAtoB: boolean isReverse: boolean feeBips: string /** * with decimals */ amountS: string | undefined amountBSlipped: | { minReceived: string minReceivedVal: string minimumDecimal: number } | undefined // amountBMiniReceiveCutFee: string | undefined; /** * with decimals */ amountB: string | undefined info: R // view sellVol: string | undefined // view buyVol: string | undefined } export const BTRADENAME = 'BTRADE-' export enum LOCK_TYPE { DUAL_CURRENCY = 'DUAL_CURRENCY', DUAL_BASE = 'DUAL_BASE', BTRADE = 'BTRADE', L2STAKING = 'L2STAKING', STOP_LIMIT = 'STOP_LIMIT', VAULT_COLLATERAL = 'VAULT_COLLATERAL', TAIKO_FARMING = 'TAIKO_FARMING', } export type getUserLockSummaryRequest = { accountId: number tokenId: number lockTags: LOCK_TYPE[] } export type UserLockSummary = { lockRecord: { amount: string lockTag: string tokenId: number }[] } export interface GetTotalClaimRequest { accountId: number } export interface GetUserCrossChainFeeRequest { receiveFeeNetwork: string, calFeeNetwork: string, requestType: number, tokenSymbol?: string, amount?: string, market?: string } export enum CLAIM_TYPE { PROTOCOL_FEE = 'PROTOCOL_FEE', RECOMMENDER_FEE = 'RECOMMENDER_FEE', REFERER_FEE = 'REFERER_FEE', REBATE_FEE = 'REBATE_FEE', LRC_STAKING = 'LRC_STAKING', } export type ClaimItem = { tokenId: number claimableInfo: Array<{ amount: string claimType: CLAIM_TYPE }> } export const VTokenPrefix = 'VAULT' export const VMarketPrefix = 'VAULT-' export type VaultToken = Omit & { type: 'Vault' interestRate: string /** VaultToken Token ID * @type number * @memberof VaultToken */ vaultTokenId: number /** * ERC20 Token ID * @type number * @memberof VaultToken */ gasAmounts: any vaultTokenAmounts: { maxAmount: string minAmount: string qtyStepScale: number minLoanAmount: string // bit1:show // bit2:join // bit3:exit // bit4:loan // bit5:repay status: number } } export type VaultMarket = { market: string wsMarket: string baseTokenId: number quoteTokenId: number precisionForPrice: number orderbookAggLevels: number precisionForAmount: number precisionForTotal: number enabled: true accountId: number address: string feeBips: number minAmount: { base: string quote: string } maxAmount: { base: string quote: string } l2Amount: { base: string quote: string } minTradeAmount: { base: string quote: string } minTradePromptAmount: { base: string quote: string } upSlippageFeeBips: { base: string quote: string } // cefiMarket: LRCUSDT, riskThreshold: number interestThreshold: number priceTolerance: number qtyStepScale: number } export type VaultAvaiableNFT = { nftTokenInfo: NFTTokenInfo accountId: null tokenId: null nftData: string broker: string brokerId: null } export type VaultBalance = { accountId: null tokenId: null symbol: string total: string borrowed: string netAsset: string interest: string creditLimit: string } export type CollateralInfo = { nftHash: string orderHash: string collateralTokenId: number collateralTokenAmount: string openDate: number nftTokenId: number nftData: string } export enum VaultAccountStatus { FREE = 'FREE', IN_STAKING = 'IN_STAKING', IN_REDEEM = 'IN_REDEEM', UNDEFINED = 'UNDEFINED', } export type VaultAccountInfo = { accountStatus: VaultAccountStatus marginLevel: string totalBalanceOfUsdt: string totalDebtOfUsdt: string totalEquityOfUsdt: string totalCollateralOfUsdt: string collateralInfo: CollateralInfo maxBorrowableOfUsdt: string userAssets: VaultBalance[] openDate: number leverage: string totalInterestOfUsdt: string totalBorrowedOfUsdt: string } export enum VaultOperationType { VAULT_OPEN_POSITION = 'VAULT_OPEN_POSITION', VAULT_MARGIN_CALL = 'VAULT_MARGIN_CALL', VAULT_BORROW = 'VAULT_BORROW', VAULT_REPAY = 'VAULT_REPAY', VAULT_TRADE = 'VAULT_TRADE', VAULT_CLOSE_OUT = 'VAULT_CLOSE_OUT', VAULT_CONVERT = 'VAULT_CONVERT', VAULT_JOIN_REDEEM = 'VAULT_JOIN_REDEEM', VAULT_CLOSE_SHORT = 'VAULT_CLOSE_SHORT', } export enum VaultOperationEnum { VAULT_OPEN_POSITION, VAULT_MARGIN_CALL, VAULT_BORROW, VAULT_REPAY, VAULT_TRADE, VAULT_CLOSE_OUT, } export enum VaultOperationStatus { VAULT_STATUS_EARNING = 'VAULT_STATUS_EARNING', VAULT_STATUS_RECEIVED = 'VAULT_STATUS_RECEIVED', VAULT_STATUS_PROCESSING = 'VAULT_STATUS_PROCESSING', VAULT_STATUS_SUCCEED = 'VAULT_STATUS_SUCCEED', VAULT_STATUS_FAILED = 'VAULT_STATUS_FAILED', VAULT_STATUS_PENDING = 'VAULT_STATUS_PENDING', } export type VaultOperation = { hash: string operateType: string operateSubType: VaultOperationType status: VaultOperationStatus tokenIn: number amountIn: string tokenOut: number amountOut: string totalBalance: string Collateral: string totalDebt: string totalEquity: string createdAt: number updatedAt: number } export type VaultOrder = { hash: string clientOrderId: string status: string tokenS: number amountS: string fillAmountS: string tokenB: number amountB: string fillAmountB: string price: string fee: string createdAt: number updatedAt: number } //Taker order export type VaultJoinRequest = NFTOrderRequestV3 & { joinHash: string } //Maker order export type VaultExitRequest = { accountId: number joinHash: string timestamp: number } export type VaultOrderRequest = OriginBTRADEV3OrderRequest export interface VaultOrderResult { hash: string clientOrderId: string status: TxStatus isIdempotent: boolean accountId: number storageId: number tokens: any[] } export interface VaultOrderNFTRequestV3WithPatch { request: VaultJoinRequest web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface VaultBorrowRequest { accountId: number token: { tokenId: number volume: string } timestamp: number } export interface VaultRepayRequestV3WithPatch { request: OriginTransferRequestV3 web3: Web3 chainId: ChainId walletType: ConnectorNames eddsaKey: string apiKey: string isHWAddr?: boolean } export interface VaultDustCollectorRequest { dustTransfers: { exchange: string payerId: number payerAddr: string payeeId: number payeeAddr: string token: { tokenId: number volume: string } maxFee: { tokenId: number volume: string } storageId: number validUntil: number memo: string }[] eddsaKey: string apiKey: string accountId: number } export interface DatacenterTokenQuote { price: string volume24H: string volumeChange24H: string percentChange1H: string percentChange24H: string percentChange7D: string percentChange30D: string marketCap: string fullyDilutedMarketCap: string } export interface GetDatacenterTokenInfoRequest { cmcTokenIds?: number[] | string currency: 'USD' } export interface DatacenterTokenInfoSimple { tokenAddress: string symbol: string price: string marketCap: string volume24H: string volumeChange24H: string percentChange24H: string percentChange7D: string tokenId: number cmcRank: number timestamp: number } export interface DatacenterTokenInfo { tokenId: number tokenAddress: string name: string symbol: string description: string slug: string logo: string cmcRank: number website: string explorer: string platform: string dateAdded: string dateLaunched: string selfReportedCirculatingSupply: string selfReportedMarketCap: string infiniteSupply: boolean circulatingSupply: string totalSupply: string maxSupply: string quote: DatacenterTokenQuote timestamp: number } export enum DatacenterRange { RANGE_ONE_HOUR = 'RANGE_ONE_HOUR', RANGE_ONE_DAY = 'RANGE_ONE_DAY', RANGE_ONE_WEEK = 'RANGE_ONE_WEEK', RANGE_ONE_MONTH = 'RANGE_ONE_MONTH', } export enum OHLCVDatacenterRange { OHLCV_RANGE_ONE_DAY = 'OHLCV_RANGE_ONE_DAY', OHLCV_RANGE_ONE_WEEK = 'OHLCV_RANGE_ONE_WEEK', OHLCV_RANGE_ONE_MONTH = 'OHLCV_RANGE_ONE_MONTH', OHLCV_RANGE_ONE_YEAR = 'OHLCV_RANGE_ONE_YEAR', OHLCV_RANGE_ALL = 'OHLCV_RANGE_ALL', } export interface GetDatacenterTokenQuoteTrendRequest { token?: string cmcTokenId?: number range?: DatacenterRange currency: 'USD' } export interface GetCmcTokenRelationsRequest { tokenAddresses?: string[] | string cmcTokenIds?: string[] | string } export interface GetDatacenterTokenOhlcvQuoteTrendRequest { token?: string cmcTokenId?: number range?: OHLCVDatacenterRange currency: 'USD' } export enum GetDatacenterTokenQuoteTrend { timestamp, price, volume24H, volumeChange24H, percentChange1H, percentChange24H, percentChange7D, percentChange30D, marketCap, } export enum NotificationMessageType { L1_CREATED = 0, //noweb L2_CREATED = 1, L1_RECEIVE = 2, L1_SEND = 3, L2_RECEIVE = 4, L2_SEND = 5, DEPOSIT = 6, WITHDRAW = 7, GUARDIAN_RECEIVE_PENDING_REQUEST = 8, REQUEST_UPDATED = 9, GUARDIAN_ADDITION = 10, EVENT = 11, L1_CREATING = 12, GUARDIAN_COMMON = 13, PROTECTED_ADD_GUARDIAN_SUCCESS = 14, PROTECTED_ADD_GUARDIAN_REJECTED = 15, PROTECTED_ADD_GUARDIAN_NONCE_FAILED = 16, PROTECTED_REQUEST_APPROVED = 17, PROTECTED_REQUEST_REJECTED = 18, QUOTA_CHANGED_ON_CHAIN_SUCCESS = 19, PROTECTED_REMOVE_GUARDIAN_SUCCESS = 20, ADD_TRUST_ON_CHAIN_SUCCESS = 30, REMOVE_TRUST_ON_CHAIN_SUCCESS = 31, WALLET_LOCKED = 32, WALLET_UNLOCKED = 33, DEAD_LOCK_GUARDIAN = 34, GUARDIAN_SAFETY_CHECK_NOTIFICATION = 35, TAIKO_A3_CREATE = 36, FACING_FORCED_SETTLEMENT = 40, DUAL_SETTLED = 50, DUAL_RECURES_ORDER_SWAP = 51, DUAL_RECURES_RETRY_FAILED = 52, DUAL_RECURES_RETRY_SUCCESS = 53, DUAL_PRICE_ALERT = 54, } export type UserNotification = { id: number walletAddress: string network: NetworkWallet messageType: NotificationMessageType | number message: string read: boolean createAt: number redirectionContext: string } export type TaikoFarmingAvaiableNFT = { nftTokenInfo: NFTTokenInfo accountId: number tokenId: number nftData: string broker: string brokerId: number } export interface RabbitWithdrawRequest { fromNetwork: string; toNetwork: string; toAddress: string; transfer: OriginTransferRequestV3 } // export type TaikoFarmingSubmitRequest = NFTOrderRequestV3 & { preOrderHash?: string } export interface TaikoFarmingSubmitOrderNFTRequestV3WithPatch { request: TaikoFarmingSubmitRequest eddsaKey: string apiKey: string } ================================================ FILE: src/defs/loopring_enums.ts ================================================ import { RESULT_INFO } from './error_codes' export enum ReqMethod { GET = 'GET', POST = 'POST', DELETE = 'DELETE', } export enum SigPatchField { EddsaSignature = 'eddsaSignature', } export enum MarketStatus { AMM = 1, ORDER_BOOK = 2, ALL = 3, } export enum VipCatergory { ORDERBOOK_TRADING_FEES_STABLECOIN = 'ORDERBOOK_TRADING_FEES_STABLECOIN', ORDERBOOK_TRADING_FEES = 'ORDERBOOK_TRADING_FEES', AMM_TRADING_FEES = 'AMM_TRADING_FEES', OTHER_FEES = 'OTHER_FEES', } export enum TradeChannel { BLANK = '', ORDER_BOOK = 'ORDER_BOOK', // 0 AMM_POOL = 'AMM_POOL', // 1 MIXED = 'MIXED', // 2 } export enum OrderType { LimitOrder = 'LIMIT_ORDER', TakerOnly = 'TAKER_ONLY', MakerOnly = 'MAKER_ONLY', ClassAmm = 'AMM', } export enum OrderTypeResp { LimitOrder = 'LIMIT_ORDER', TakerOnly = 'TAKER_ONLY', MakerOnly = 'MAKER_ONLY', ClassAmm = 'CLASS_AMM', } export enum Currency { usd = 'usd', cny = 'cny', } export enum OffchainFeeReqType { ORDER = 0, OFFCHAIN_WITHDRAWAL = 1, UPDATE_ACCOUNT = 2, TRANSFER = 3, FAST_OFFCHAIN_WITHDRAWAL = 4, OPEN_ACCOUNT = 5, AMM_EXIT = 6, DEPOSIT = 7, AMM_JOIN = 8, TRANSFER_AND_UPDATE_ACCOUNT = 15, DEFI_JOIN = 21, DEFI_EXIT = 22, FORCE_WITHDRAWAL = 23, EXTRA_TYPES = 24, RABBIT_OFFCHAIN_WITHDRAWAL = 25, } export enum OffchainNFTFeeReqType { NFT_MINT = 9, NFT_WITHDRAWAL = 10, NFT_TRANSFER = 11, NFT_DEPLOY = 13, NFT_TRANSFER_AND_UPDATE_ACCOUNT = 19, EXTRA_TYPES = 24, } export enum TradingInterval { min1 = '1min', min5 = '5min', min15 = '15min', min30 = '30min', hr1 = '1hr', hr2 = '2hr', hr4 = '4hr', hr12 = '12hr', d1 = '1d', w1 = '1w', } export enum TxStatus { processing = 'processing', processed = 'processed', received = 'received', failed = 'failed', } export enum OrderStatus { processing = 'processing', processed = 'processed', failed = 'failed', cancelled = 'cancelled', cancelling = 'cancelling', expired = 'expired', } export enum Side { Buy = 'BUY', Sell = 'SELL', } export enum WithdrawalTypes { OFFCHAIN_WITHDRAWAL = 'OFFCHAIN_WITHDRAWAL', ONCHAIN_WITHDRAWAL = 'ONCHAIN_WITHDRAWAL', FORCE_WITHDRAWAL = 'FORCE_WITHDRAWAL', } export enum UserTxTypes { DEPOSIT = 'deposit', TRANSFER = 'transfer', OFFCHAIN_WITHDRAWAL = 'offchain_withdrawal', FORCE_WITHDRAWAL = 'force_withdrawal', DELEGATED_FORCE_WITHDRAW = 'delegated_force_withdraw', SEND_LUCKY_TOKEN = 'send_lucky_token', WITHDRAW_LUCKY_TOKEN = 'withdraw_lucky_token', WITHDRAW = 'onchain_withdrawal', SEND_BACK_LUCKY_TOKEN = 'send_back_lucky_token', DUAL_INVESTMENT = 'dual_investment', L2_STAKING = 'l2_staking', UNIFIED_CLAIM = 'unified_claim', CHANGE_PWD = 'change_pwd', } export enum UserBillTypes { DEPOSIT = 0, ONCHAIN_WITHDRAWAL = 1, TRANSFER = 2, } export enum UserNFTTxTypes { DEPOSIT = 'deposit', TRANSFER = 'transfer', WITHDRAW = 'onchain_withdrawal', MINT = 'mint', } export enum TransferType { transfer = 'transfer', transfer_red = 'transfer_red', } export enum BillType { ORDER = 'order', DEPOSIT = 'deposit', ONCHAIN_WITHDRAWAL = 'onchain_withdrawal', OFFCHAIN_WITHDRAWAL = 'offchain_withdrawal', TRANSFER = 'transfer', TRANSFER_RED = 'transfer_red', } export enum FilledType { dex = 'dex', amm = 'amm', } export enum TxType { TRANSFER = 'TRANSFER', DEPOSIT = 'DEPOSIT', OFFCHAIN_WITHDRAWAL = 'OFFCHAIN_WITHDRAWAL', } export enum TxNFTType { TRANSFER = 'TRANSFER', DEPOSIT = 'DEPOSIT', WITHDRAW = 'WITHDRAW', ALL = 'ALL', MINT = 'MINT', SEND_LUCKY_TOKEN = 'SEND_LUCKY_TOKEN', WITHDRAW_LUCKY_TOKEN = 'WITHDRAW_LUCKY_TOKEN', SEND_BACK_LUCKY_TOKEN = 'SEND_BACK_LUCKY_TOKEN', } export enum AmmTxType { JOIN = 'AMM_JOIN', EXIT = 'AMM_EXIT', } export enum SortOrder { ASC = 0, DESC = 1, } export enum RuleType { AMM_MINING = 'AMM_MINING', SWAP_VOLUME_RANKING = 'SWAP_VOLUME_RANKING', ORDERBOOK_MINING = 'ORDERBOOK_MINING', } export enum AmmPoolActivityStatus { NotStarted = 'NotStarted', InProgress = 'InProgress', EndOfGame = 'EndOfGame', } export enum SIG_FLAG { NO_SIG, EDDSA_SIG, EDDSA_SIG_POSEIDON, } export enum AssetType { LEVEL_ONE = 0, DEX = 1, } export enum IntervalType { HOUR = 0, DAY = 1, } export interface ReqOptions { baseUrl?: string apiKey?: string signature?: string url?: string } export type TX_HASH_API = { hash?: string; resultInfo?: RESULT_INFO } export interface ReqParams { url: string method: ReqMethod sigFlag: SIG_FLAG queryParams?: any bodyParams?: any apiKey?: string sigObj?: { dataToSig?: any sig?: string sigPatch?: string PrivateKey?: string owner?: string pwd?: string web3?: any hasDataStruct?: boolean } eddsaSignature?: string ecdsaSignature?: string eddsaSignatureREFER?: boolean extraHeaders?: Record } export interface PublicKey { /** * The public keys x part. * @type {string} * @memberof PublicKey */ x: string /** * The public keys y part. * @type {string} * @memberof PublicKey */ y: string } export enum NetworkWallet { ETHEREUM = 'ETHEREUM', ARBITRUM = 'ARBITRUM', GOERLI = 'GOERLI', TAIKO = 'TAIKO', SEPOLIA = 'SEPOLIA', TAIKOHEKLA = 'TAIKOHEKLA', BASE = 'BASE', BASESEPOLIA = 'BASESEPOLIA', } ================================================ FILE: src/defs/nft_defs.ts ================================================ import Web3 from "web3"; import { ChainId } from "./web3_defs"; import { NFT_TYPE_STRING, NFTType } from "../api"; /** * @interface DepositNFTParam * @description an NFTAction to the specified account. * @property web3 * @property DepositParam * @property from The address that deposits the funds to the exchange * @property to The account owner's address receiving the funds * @property nftType The type of NFTAction contract address (ERC721/ERC1155/...) * @property tokenAddress The address of the token * @property nftId The token type 'id`. * @property amount The amount of tokens to deposit. * @property nonce: number, * @property gasPrice: number, * @property gasLimit: number, * @property extraData Optional extra data used by the deposit contract. * @property chainId 0|5 * @property sendByMetaMask boolean */ export interface DepositNFTParam { web3: Web3; from: string; exchangeAddress: string; nftType?: NFTType; tokenAddress: string; nftId: string; amount: number; gasPrice: number; gasLimit: number | undefined; chainId?: ChainId; nonce: number; extraData?: any; sendByMetaMask?: boolean; } /** * isApprovedForAll * @property web3 * @property from The address that deposits the funds to the exchange * @property exchangeAddress loopring exchange address * @property nftType NFTType * @property tokenAddress The address of NFTAction token */ export interface IsApproveParam { web3: Web3; from: string; exchangeAddress: string; nftType: NFTType; tokenAddress: string; } /** * approveNFT * @property web3 * @property from string address that deposits the funds to the exchange * @property to string address deposits to * @property loopringAddress string loopring exchange Address * @property nftId ntId * @property chainId number * @property nftType number The type of NFTAction contract address (ERC721/ERC1155) * @property nonce number * @property gasPrice * @property gasLimit * @property sendByMetaMask */ export interface ApproveParam { web3: Web3; from: string; depositAddress: string; tokenAddress: string; nftId?: string; nftType: NFTType; gasPrice: number; gasLimit: number | undefined; chainId: ChainId; nonce: number; approved?: boolean; sendByMetaMask?: boolean; } export type ContractNFTParam = { web3: any; tokenAddress: string; nftId: string; nftType?: NFTType; }; export type ContractNFTMetaParam = ContractNFTParam & { _id?: string }; export type UserNFTBalanceParam = ContractNFTParam & { account: string }; export type CallRefreshNFT = { network: "ETHEREUM", tokenAddress: string, nftId: string, nftType: NFT_TYPE_STRING }; ================================================ FILE: src/defs/url_defs.ts ================================================ export enum LOOPRING_URLs { GET_AVAILABLE_BROKER = '/api/v3/getAvailableBroker', GET_RELAYER_CURRENT_TIME = '/api/v3/timestamp', API_KEY_ACTION = '/api/v3/apiKey', // get update GET_NEXT_STORAGE_ID = '/api/v3/storageId', ORDER_ACTION = '/api/v3/order', // get submit cancel ORDER_CANCEL_HASH_LIST = '/api/v3/orders/byHash', // cancel multiple orders by hashs ORDER_CANCEL_CLIENT_ORDER_ID_LIST = '/api/v3/orders/byClientOrderId', // cancel multiple orders by clientOrderids GET_MULTI_ORDERS = '/api/v3/orders', GET_MARKETS = '/api/v3/exchange/markets', GET_TOKENS = '/api/v3/exchange/tokens', GET_EXCHANGE_INFO = '/api/v3/exchange/info', GET_WITHDRAWAL_AGENTS = '/api/v3/exchange/withdrawalAgents', GET_EXCHANGE_FEEINFO = '/api/v3/exchange/feeInfo', GET_IGNORE_WITHDRAW = '/api/v3/exchange/notWithdrawContractTokens', GET_MIX_MARKETS = '/api/v3/mix/markets', GET_DEPTH = '/api/v3/depth', GET_MIX_DEPTH = '/api/v3/mix/depth', GET_TICKER = '/api/v3/ticker', GET_MIX_TICKER = '/api/v3/mix/ticker', GET_CANDLESTICK = '/api/v3/candlestick', GET_MIX_CANDLESTICK = '/api/v3/mix/candlestick', GET_FIAT_PRICE = '/api/v3/price', GET_TRADES = '/api/v3/trade', POST_INTERNAL_TRANSFER = '/api/v3/transfer', ACCOUNT_ACTION = '/api/v3/account', // get or update COUNTER_FACTUAL_INFO = '/api/v3/counterFactualInfo', GET_USER_REG_TXS = '/api/v3/user/createInfo', GET_PWD_RESET_TXS = '/api/v3/user/updateInfo', GET_USER_EXCHANGE_BALANCES = '/api/v3/user/balances', GET_USER_DEPOSITS_HISTORY = '/api/v3/user/deposits', WITHDRAWALS_ACTION = '/api/v3/user/withdrawals', // post get POST_FORCE_WITHDRAWALS = '/api/v3/user/forceWithdrawals', GET_USER_TRANSFERS_LIST = '/api/v3/user/transfers', GET_USER_TRADE_HISTORY = '/api/v3/user/trades', GET_USER_TXS = '/api/v3/user/transactions', GET_USER_FEE_RATE = '/api/v3/user/feeRates', // deprecated GET_USER_ORDER_FEE_RATE = '/api/v3/user/orderFee', GET_MINIMAL_ORDER_AMT = '/api/v3/user/orderAmount', // IGNORE for now. GET_MINIMUM_TOKEN_AMT = '/api/v3/user/orderUserRateAmount', GET_OFFCHAIN_FEE_AMT = '/api/v3/user/offchainFee', GET_USER_BILLS = '/api/v3/user/bills', GET_CROSSCHAIN_OFFCHAIN_FEE_AMT = '/api/v3/user/crosschain/offchainFee', GET_ENCRYPTED_ECDSA_KEY = '/api/v3/account/ecdsa/enctypted', POST_ENCRYPTED_ECDSA_KEY = '/api/v3/account/ecdsa/enctypted', // Contacts GET_CONTACTS = '/api/v3/user/contact', CREATE_CONTACT = '/api/v3/user/contact/add', UPDATE_CONTACT = '/api/v3/user/contact/update', DELETE_CONTACT = '/api/v3/user/contact', // Refer GET_REFER_DOWNSIDES = '/api/v3/user/refer/profit/downsides', GET_REFER_SELF = '/api/v3/user/refer/profit/self', GET_REFER_STATISTIC = '/api/v3/user/refer/statistic', // Notification GET_NOTIFICATION_ALL = '/api/v3/user/notification', POST_NOTIFICATION_CLEAR = '/api/v3/user/notification/clearAll', POST_NOTIFICATION_READ_ALL = '/api/v3/user/notification/readAll', POST_NOTIFICATION_READ_ONE = '/api/v3/user/notification/read', GET_ALLOWANCES = '/api/v3/eth/allowances', GET_ETH_NONCE = '/api/v3/eth/nonce', GET_ETH_BALANCES = '/api/v3/eth/balances', GET_TOKEN_BALANCES = '/api/v3/eth/tokenBalances', GET_AKK_TOKEN_BALANCES = '/api/v3/eth/tokenBalances/all', GET_GAS_PRICE = '/api/v3/eth/recommendedGasPrice', GET_GAS_PRICE_RANGE = '/api/v3/eth/recommendedGasPriceRange', GET_RECOMENDED_MARKETS = '/api/v3/exchange/recommended', GET_AMM_POOLS_CONF = '/api/v3/amm/pools', GET_AMM_POOLS_SNAPSHOT = '/api/v3/amm/balance', GET_AMM_POOLS_BALANCES = '/api/v3/amm/balances', GET_AMM_POOL_STATS = '/api/v3/amm/poolsStats', POST_JOIN_AMM_POOL = '/api/v3/amm/join', POST_EXIT_AMM_POOL = '/api/v3/amm/exit', GET_AMM_POOL_TXS = '/api/v3/amm/transactions', GET_USER_AMM_POOL_TXS = '/api/v3/amm/user/transactions', GET_AMM_POOL_TRADE_TXS = '/api/v3/amm/trades', GET_AMM_ACTIVITY_RULES = '/api/v3/sidecar/activityRules', GET_AMMPOOL_USER_REWARDS = '/api/v3/amm/user/rewards', GET_AMMPOOL_REWARDS = '/api/v3/amm/rewards', GET_AMMPOOL_GAME_RANK = '/api/v3/game/rank', GET_AMMPOOL_GAME_USER_RANK = '/api/v3/game/user/rank', GET_LIQUIDITY_MINING = '/api/v3/sidecar/liquidityMining', GET_DELEGATE_GET_CODE = '/api/v3/delegator/getCode', GET_DELEGATE_GET_IPFS = '/api/v3/delegator/ipfs', GET_LIQUIDITY_MINING_USER_HISTORY = '/api/v3/sidecar/liquidityMiningUserHistory', GET_PROTOCOL_PORTRAIT = '/api/v3/sidecar/ProtocolPortrait', GET_PROTOCOL_REWARDS = '/api/v3/sidecar/commissionReward', GET_AMM_ASSET_HISTORY = '/api/v3/amm/assets', GET_ASSET_LOCK_RECORDS = '/api/v3/user/lockRecords', GET_DEFI_TOKENS = '/api/v3/defi/tokens', GET_DEFI_MARKETS = '/api/v3/defi/markets', POST_DEFI_ORDER = '/api/v3/defi/order', GET_DEFI_REWARDS = '/api/v3/defi/rewards', GET_DEFI_TRANSACTIONS = '/api/v3/defi/transactions', SET_REFERRER = '/api/v3/refer', GET_WS_KEY = '/v3/ws/key', GET_LATEST_TOKEN_PRICES = '/api/v3/datacenter/getLatestTokenPrices', GET_USER_TRADE_AMOUNT = '/api/v3/datacenter/getUserTradeAmount', GET_SUPPORT_TOKENS = '/api/v3/datacenter/getSupportTokens', GET_QUOTE_TREND = '/api/v3/datacenter/getTokenQuoteTrend', GET_QUOTE_TOKEN_INFO = '/api/v3/datacenter/getTokenInfo', GET_QUOTE_TOKEN_OHLCV_TREND = '/api/v3/datacenter/getTokenOHLCVTrend', GET_QUOTE_TOKEN_GETCMCTOKENRELATIONS = '/api/v3/datacenter/getCmcTokenRelations', GET_USER_ASSETS = '/api/wallet/v3/userAssets', GET_TOKEN_PRICES = '/api/wallet/v3/tokenPrices', GET_GUARDIAN_APPROVE_LIST = '/api/wallet/v3/getGuardianApproveList', GET_PROTECTORS = '/api/wallet/v3/getProtects', GET_OPERATION_LOGS = '/api/wallet/v3/operationLogs', GET_HEBAO_CONFIG = '/api/wallet/v3/getAppConfigs', GET_WALLET_TYPE = '/api/wallet/v3/wallet/type', GET_WALLET_MODULES = '/api/wallet/v3/walletModules', GET_WALLET_CONTRACTVERSION = '/api/wallet/v3/contractVersion', RESOLVE_ENS = '/api/wallet/v3/resolveEns', RESOLVE_NAME = '/api/wallet/v3/resolveName', // SUBMIT_APPROVE_SIGNATURE = '/api/wallet/v3/submitApproveSignature', REJECT_APPROVE_SIGNATURE = '/api/wallet/v3/rejectApproveSignature', // OFFICIAL_LOCK_OR_UNLOCK = "/api/wallet/v3/officialLockOrUnlock", SEND_META_TX = '/api/wallet/v3/sendMetaTx', GET_ACCOUNT_SERVICES = '/api/v3/spi/getAccountServices', // VIP GET_USER_VIP_INFO = '/api/v3/user/vipInfo', GET_USER_VIP_ASSETS = '/api/v3/datacenter/getUserAssets', GET_USER_NFT_BALANCES = '/api/v3/user/nft/balances', GET_USER_NFT_BALANCES_BY_COLLECTION = '/api/v3/user/nft/collection/balances', GET_NFT_OFFCHAIN_FEE_AMT = '/api/v3/user/nft/offchainFee', POST_NFT_INTERNAL_TRANSFER = '/api/v3/nft/transfer', POST_NFT_WITHDRAWALS = '/api/v3/nft/withdrawal', POST_NFT_MINT = '/api/v3/nft/mint', POST_NFT_TRADE = '/api/v3/nft/trade', POST_NFT_VALIDATE_ORDER = '/api/v3/nft/validateOrder', // post get POST_NFT_EDIT_COLLECTION = '/api/v3/nft/collection/edit', POST_NFT_CREATE_LEGACY_COLLECTION = '/api/v3/nft/collection/legacy/tokenAddress', POST_NFT_VALIDATE_REFRESH_NFT = '/api/v3/nft/image/refresh', POST_DEPLOY_COLLECTION = '/api/v3/collection/deployTokenAddress', POST_NFT_LEGACY_UPDATE_COLLECTION = '/api/v3/nft/collection/legacy/updateNftCollection', POST_NFT_UPDATE_NFT_GROUP = '/api/v3/user/nft/updateNftPreference', GET_NFT_COLLECTION = '/api/v3/nft/collection', POST_NFT_CREATE_COLLECTION = '/api/v3/nft/collection', DELETE_NFT_CREATE_COLLECTION = '/api/v3/nft/collection', GET_COLLECTION_WHOLE_NFTS = '/api/v3/nft/public/collection/items', GET_NFT_COLLECTION_PUBLISH = '/api/v3/nft/public/collection', GET_NFT_COLLECTION_HASNFT = '/api/v3/user/collection/details', GET_NFT_LEGACY_COLLECTION = '/api/v3/nft/collection/legacy', GET_NFT_LEGACY_TOKENADDRESS = '/api/v3/nft/collection/legacy/tokenAddress', GET_NFT_LEGACY_BALANCE = '/api/v3/nft/collection/legacy/balance', GET_USER_LOCKSUMMAR = '/api/v3/user/lockSummary', GET_USER_HAD_UNKNOWN_COLLECTION = '/api/v3/nft/collection/unknown', GET_NFTs_INFO = '/api/v3/nft/info/nfts', GET_USER_NFT_TRANSFER_HISTORY = '/api/v3/user/nft/transfers', GET_USER_NFT_DEPOSIT_HISTORY = '/api/v3/user/nft/deposits', GET_USER_NFT_WITHDRAW_HISTORY = '/api/v3/user/nft/withdrawals', GET_USER_NFT_TRANSACTION_HISTORY = '/api/v3/user/nft/transactions', GET_USER_NFT_TRADE_HISTORY_OLD = '/api/v3/user/nft/trades', GET_USER_NFT_TRADE_HISTORY = '/api/v3/new/user/nft/trades', GET_USER_NFT_MINT_HISTORY = '/api/v3/user/nft/mints', GET_USER_NFT_BURN_ADDRESS = '/api/v3/datacenter/getNftBurnAddress', GET_DEPLOY_TOKEN_ADDRESS = '/api/v3/nft/deployTokenAddress', IPFS_META_URL = 'https://ipfs.loopring.io/ipfs/', GET_DUAL_INDEX = '/api/v3/dual/index', GET_DUAL_PRICES = '/api/v3/dual/prices', GET_DUAL_INFOS = '/api/v3/dual/infos', GET_DUAL_TRANSACTIONS = '/api/v3/dual/transactions', GET_DUAL_BALANCE = '/api/v3/dual/balance', GET_DUAL_RULE = '/api/v3/dual/rules', POST_DUAL_ORDER = '/api/v3/dual/order', GET_DUAL_USER_LOCKED = '/api/v3/dual/lockRecordAmount', POST_DUAL_EDIT = '/api/v3/dual/order/reinvest', GET_LUCK_TOKEN_AGENTS = '/api/v3/luckyToken/agents', GET_LUCK_TOKEN_AUTHORIZEDSIGNERS = '/api/v3/luckyToken/authorizedSigners', GET_LUCK_TOKEN_CLAIMHISTORY = '/api/v3/luckyToken/user/claimHistory', GET_LUCK_TOKEN_LUCKYTOKENS = '/api/v3/luckyToken/user/luckyTokens', GET_LUCK_TOKEN_LUCKYTOKENDETAIL = '/api/v3/luckyToken/user/luckyTokenDetail', GET_LUCK_TOKEN_BLINDBOXDETAIL = '/api/v3/luckyToken/user/blindBoxDetail', GET_LUCK_TOKEN_WITHDRAWALS = '/api/v3/luckyToken/user/withdraws ', GET_LUCK_TOKEN_BALANCES = '/api/v3/luckyToken/user/balances', GET_LUCK_TOKEN_CLAIMEDLUCKYTOKENS = '/api/v3/luckyToken/user/claimedLuckyTokens', GET_LUCK_TOKEN_CLAIMEDBLINDBOX = '/api/v3/luckyToken/user/claimBlindBoxHistory', GET_LUCK_TOKEN_SUMMARY = '/api/v3/luckyToken/user/summary', GET_LUCK_TOKEN_NFTBALANCES = '/api/v3/luckyToken/user/nftBalances', POST_LUCK_TOKEN_SENDLUCKYTOKEN = '/api/v3/luckyToken/sendLuckyToken', POST_LUCK_TOKEN_CLAIMLUCKYTOKEN = '/api/v3/luckyToken/claimLuckyToken', POST_LUCK_TOKEN_CLAIMBLINDBOX = '/api/v3/luckyToken/claimBlindBox', POST_LUCK_TOKEN_WITHDRAWALS = '/api/v3/luckyToken/user/withdrawals', POST_LUCK_TOKEN_UNCLAIMNFTANDBLINDCNT = '/api/v3/luckyToken/user/unclaimNftAndBlindCnt', GET_LUCK_TOKEN_LUCKYTOKENTARGETS = '/api/v3/luckyToken/user/luckyTokenTargets', POST_LUCK_TOKEN_SUBMITADDTARGET = '/api/v3/luckyToken/submitAddTarget', GET_BANXA_API_KEY = '/api/v3/hmacAuthentication', GET_STAKE_PRODUCTS = '/api/v3/stake/products', POST_STAKE_CLAIM = '/api/v3/stake/claim', POST_STAKE = '/api/v3/stake/stake', POST_STAKE_REDEEM = '/api/v3/stake/redeem', GET_STAKE_SUMMARY = '/api/v3/stake/user/summary', GET_STAKE_TRANSACTIONS = '/api/v3/stake/user/transactions', // CEFI_MARKETS GET_BTRATE_MARKETS = '/api/v3/btrade/markets', GET_BTRATE_DEPTH = '/api/v3/btrade/depth', GET_BTRATE_ORDERS = '/api/v3/btrade/orders', POST_BTRATE_ORDER = '/api/v3/btrade/order', GET_TOTAL_CLAIM_INFO = '/api/v3/claim/totalClaimInfo', POST_TOTAL_CLAIM = '/api/v3/claim/claim', GET_VAULT_TOKENS = '/api/v3/vault/tokens', GET_VAULT_MARKETS = '/api/v3/vault/markets', GET_VAULT_GETAVAILABLENFT = '/api/v3/vault/getAvailableNft', GET_VAULT_ACCOUNT = '/api/v3/vault/account', GET_VAULT_GETOPERATIONS = '/api/v3/vault/getOperations', GET_VAULT_GETOPERATIONBY_HASH = '/api/v3/vault/getOperationByHash', GET_VAULT_INFOS = '/api/v3/vault/infos', GET_VAULT_DEPTH = '/api/v3/vault/depth', GET_VAULT_BALANCE = '/api/v3/vault/balances', POST_VAULT_JOIN = '/api/v3/vault/join', POST_VAULT_ORDER = '/api/v3/vault/order', POST_VAULT_EXIT = '/api/v3/vault/exit', POST_VAULT_TRANSFER = '/api/v3/vault/transfer', POST_VAULT_LOAN = '/api/v3/vault/loan', POST_VAULT_REPAY = '/api/v3/vault/repay', GET_VAULT_PRICE = '/api/v3/vault/tokenPrice', GET_VAULT_CREDIT = '/api/v3/vault/getCredit', GET_VAULT_COLLATERALS = '/api/v3/vault/getCollaterals', GET_VAULT_SUBMIT_LEVERAGE = '/api/v3/vault/submitLeverage', GET_VAULT_SUBMIT_DUST_COLLECTOR = '/api/v3/vault/submitDustCollector', GET_VAULT_GEMAX_BORROWABLE = '/api/v3/vault/getMaxBorrowable', GET_VAULT_CLOSE_SHORT = '/api/v3/vault/closeShort', GET_VAULT_CONFIG = '/api/v3/vault/getConfig', GET_DEFI_APYS = '/api/v3/datacenter/getDefiApys', GET_DEFI_STAKE_TRANSACTIONS = '/api/v3/defi/stake/transactions', GET_TAIKO_FARMING_POSITION_INFO = '/api/v3/taiko/farming/getPositionInfo', GET_TAIKO_FARMING_TRANSACTIONS = '/api/v3/taiko/farming/transactions', GET_TAIKO_FARMING_USER_SUMMARY = '/api/v3/taiko/farming/user/summary', GET_TAIKO_FARMING_AVAILABLE_NFT = '/api/v3/taiko/farming/getAvailableNft', GET_TAIKO_FARMING_TRANSACTION_BY_HASH = '/api/v3/taiko/farming/getTaikoFarmingTransactionByHash', GET_TAIKO_FARMING_DEPOSIT_DURATION_LIST = '/api/v3/taiko/farming/getDepositDurationList', GET_TAIKO_FARMING_GET_REDEEM ='api/v3/taiko/farming/getRedeem', POST_TAIKO_FARMING_SUMBIT_CLAIM = '/api/v3/taiko/farming/sumbitClaim', GET_RABBIT_WITHDRAW_CONFIG = '/api/v3/rabbitWithdraw/config', GET_NETWORK_WITHDRAWAL_AGENTS = '/api/v3/exchange/networkWithdrawalAgents', POST_RABBIT_WITHDRAW = '/api/v3/rabbitWithdraw', } ================================================ FILE: src/defs/web3_defs.ts ================================================ export enum ChainId { MAINNET = 1, GOERLI = 5, SEPOLIA = 11155111, TAIKO = 167000, TAIKOHEKLA = 167009, BASE = 8453, BASESEPOLIA = 84532, } export const NetworkContextName = 'NETWORK' export enum ConnectorNames { Unknown = 'Unknown', MetaMask = 'MetaMask', Network = 'Network', WalletConnect = 'WalletConnect', Gamestop = 'Gamestop', OtherExtension = 'OtherExtension', Coinbase = 'Coinbase', Ledger = 'Ledger', Trezor = 'Trezor', Authereum = 'Authereum', } export enum SigSuffix { Suffix02 = '02', Suffix03 = '03', } export const NFTFactory = { [ChainId.MAINNET]: '0xc852aC7aAe4b0f0a0Deb9e8A391ebA2047d80026', [ChainId.GOERLI]: '0x355E9941C5e301033ecfD37184E78443c5241035', [ChainId.SEPOLIA]: '0x8cC68c28c7E3d8Eeb1D74434164a1e91aCdA088D', [ChainId.TAIKOHEKLA]: '', [ChainId.TAIKO]: '', [ChainId.BASE]: '', [ChainId.BASESEPOLIA]: '', } export const NFTFactory_Collection = { [ChainId.MAINNET]: '0x97BE94250AEF1Df307749aFAeD27f9bc8aB911db', [ChainId.GOERLI]: '0x355E9941C5e301033ecfD37184E78443c5241035', [ChainId.SEPOLIA]: '0x8cC68c28c7E3d8Eeb1D74434164a1e91aCdA088D', [ChainId.BASE]: '', [ChainId.BASESEPOLIA]: '', } ================================================ FILE: src/defs/ws_defs.ts ================================================ import { OrderStatus, Side, NetworkWallet } from './' export interface WsProps { topics: any[] needApiKey: boolean apikey?: string } export enum WsOps { Sub = 'sub', Unsub = 'unSub', } export enum WsTopicType { account = 'account', order = 'order', trade = 'trade', mixtrade = 'mixtrade', ticker = 'ticker', candlestick = 'candlestick', ammpool = 'ammpool', orderbook = 'orderbook', mixorder = 'mixorder', btradedepth = 'btradedepth', crawlTokenPrices = 'crawltokenprices', notification = 'notification', vaultAccount = 'vaultAccount', l2Common = 'l2Common', } export const getCrawlTokenPrices = ({ topic = WsTopicType.crawlTokenPrices, currency = 'USD', }: { topic?: WsTopicType.crawlTokenPrices } & Omit<{ currency?: 'USD' }, 'topic'>) => { return { topic, currency } } export const getAccountArg = () => { return { topic: WsTopicType.account, } } export interface WsAccount { accountId: number totalAmount: string tokenId: number amountLocked: string } export const getOrderArg = (market: string) => { return { topic: WsTopicType.order, market, } } export interface WsOrder { hash: string clientOrderId: string size: string volume: string price: string filledSize: string filledVolume: string filledFee: string status: OrderStatus createdAt: string validSince: string validUntil: string side: Side market: string } export type OrderWsRequest = { topic?: WsTopicType.orderbook | WsTopicType.mixorder | WsTopicType.btradedepth market: string level: number count?: number snapshot?: boolean showOverlap?: boolean } export const getOrderBookArg = ({ topic = WsTopicType.orderbook, market, level, count, snapshot, showOverlap, }: OrderWsRequest) => { const obj: any = { topic, market, level, count, snapshot, showOverlap, } Object.keys(obj).forEach((key) => (obj[key] === undefined ? delete obj[key] : {})) return obj } export const getMixOrderArg = ({ topic = WsTopicType.mixorder, ...orderWsRequest }: { topic?: WsTopicType.mixorder } & Omit) => { return getOrderBookArg({ topic, ...orderWsRequest, }) } export const getBtradeOrderBook = ({ topic = WsTopicType.btradedepth, ...orderWsRequest }: { topic?: WsTopicType.btradedepth } & Omit) => { return getOrderBookArg({ topic, ...orderWsRequest, }) } export const getTradeArg = (market: string) => { return { topic: WsTopicType.trade, market, } } export const getMixTradeArg = (market: string) => { return { topic: WsTopicType.mixtrade, market, } } export const getTickerArg = (market: string) => { return { topic: WsTopicType.ticker, market, } } export const getCandlestickArg = (market: string) => { return { topic: WsTopicType.candlestick, market, } } export const getAmmpoolArg = (poolAddress: string) => { return { topic: WsTopicType.ammpool, snapshot: true, poolAddress, } } export const getNotificationArg = ({ address, network, }: { address: string network: NetworkWallet }) => { return { topic: WsTopicType.notification, address, network, } } export const getL2Common = ({ address, network }: { address: string; network: NetworkWallet }) => { return { topic: WsTopicType.l2Common, address, network, } } export enum WS_ACTIONT_YPE { VAULT_ACCOUNT_UPDATE = 'VAULT_ACCOUNT_UPDATE', } export interface WsL2Common { accountId: number address: string isUpdated: true } ================================================ FILE: src/index.ts ================================================ export * from './utils' export * from './defs' export * from './api' ================================================ FILE: src/tests/.eslintrc ================================================ { "root": true, "parser": "@typescript-eslint/parser", "plugins": [ "@typescript-eslint" ], "extends": [ "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], "rules": { "no-console": 1 , // Remember, this means error! "camelcase": ["warn"], "@typescript-eslint/no-var-requires": "off", "no-case-declarations": "warn" } } ================================================ FILE: src/tests/.gitignore ================================================ output ================================================ FILE: src/tests/MockData.ts ================================================ import { AmmpoolAPI, ExchangeAPI, UserAPI, WalletAPI, WsAPI, NFTAPI, DelegateAPI, GlobalAPI, WhitelistedUserAPI, DefiAPI, } from '../api' import Web3 from 'web3' import * as sdk from '../index' import { providers } from 'ethers' // import * as PrivateKeyProvider from 'truffle-privatekey-provider' const PrivateKeyProvider = require('truffle-privatekey-provider') /*** * LoopringAPIClass */ // export class LoopringAPIClass { // public static userAPI: UserAPI; // public static exchangeAPI: ExchangeAPI; // public static ammpoolAPI: AmmpoolAPI; // public static walletAPI: WalletAPI; // public static wsAPI: WsAPI; // public static nftAPI: NFTAPI; // public static delegate: DelegateAPI; // public static globalAPI: GlobalAPI; // public static WhitelistedUserAPI: WhitelistedUserAPI; // public static contractAPI: typeof ContractAPI; // public static __chainId__: sdk.ChainId; // public static InitApi = (chainId: sdk.ChainId) => { // LoopringAPI.userAPI = new UserAPI({ chainId }); // LoopringAPI.exchangeAPI = new ExchangeAPI({ chainId }); // LoopringAPI.globalAPI = new GlobalAPI({ chainId }); // LoopringAPI.ammpoolAPI = new AmmpoolAPI({ chainId }); // LoopringAPI.walletAPI = new WalletAPI({ chainId }); // LoopringAPI.wsAPI = new WsAPI({ chainId }); // LoopringAPI.WhitelistedUserAPI = new WhitelistedUserAPI({ chainId }); // LoopringAPI.nftAPI = new NFTAPI({ chainId }); // LoopringAPI.delegate = new DelegateAPI({ chainId }); // LoopringAPI.__chainId__ = chainId; // LoopringAPI.contractAPI = ContractAPI; // }; // } // LoopringAPIClass.InitApi({sdk.ChainId.GOERLI}) export const DEFAULT_TIMEOUT = 30000 // @ts-ignore global.ethereum = {} const chainId = sdk.ChainId.GOERLI const baseUrl = 'https://dev.loopring.io' // 'uat2.loopring.io' export const LoopringAPI = { // second params is http request timeout default is 6000 userAPI: new UserAPI({ chainId, baseUrl }, 6000), exchangeAPI: new ExchangeAPI({ chainId, baseUrl }), globalAPI: new GlobalAPI({ chainId, baseUrl }), ammpoolAPI: new AmmpoolAPI({ chainId, baseUrl }), walletAPI: new WalletAPI({ chainId, baseUrl }), wsAPI: new WsAPI({ chainId, baseUrl }), whitelistedUserAPI: new WhitelistedUserAPI({ chainId, baseUrl }), nftAPI: new NFTAPI({ chainId, baseUrl }), defiAPI: new DefiAPI({ chainId, baseUrl }), delegate: new DelegateAPI({ chainId, baseUrl }), __chainId__: chainId, } export const LOOPRING_EXPORTED_ACCOUNT = { uatNFTFactory: '0x355E9941C5e301033ecfD37184E78443c5241035', devNFTFactory: '0x85829dD619f11129e8ea08764F0E1A021aD579B9', address: '0x727e0fa09389156fc803eaf9c7017338efd76e7f', privateKey: '491aecdb1d5f6400a6b62fd12a41a86715bbab675c37a4060ba115fecf94083c', accountId: 10149, //UAT 10037, address2: '0xb6d8c39D5528357dBCe6BEd82aC71c74e9D19079', privateKey2: 'e020ed769032ba95d9a5207687a663d6198fe2f5cedf28a250f7cbd8c81a5263', accountId2: 10395, addressCF: '0x23dE4Da688c94a66E8bbE9BCc95CB03b4e209C15', accountIdCF: 11632, addressContractWallet: '0xD4BD7c71B6d4A09217ccc713f740d6ed8f4EA0cd', depositAddress: '0x4E8e6a86762546cCfCBaa8A259c7d442383c5127', exchangeAddress: '0xC7CcB943782aBC702C014EfA9F99cF84FaDE6Cb9', //UAT '0x12b7cccF30ba360e5041C6Ce239C9a188B709b2B', whitelistedAddress: '0x35405E1349658BcA12810d0f879Bf6c5d89B512C', whitelistedEddkey: '0x27a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33', // const eddkeyWhitelisted = // "0x27a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33"; // apiKey: "2PYgTOZwXHkPXtJMlOMG06ZX1QKJInpoky6iYIbtMgmkbfdL4PvxyEOj0LPOfgYX", chainId: 5, nftTokenAddress: '0x202a3057310f33d9ae4d2b0c650e93d2b2172d6b', nftTokenId: 32768, nftId: '0x8590625e520c6da9a84b261332ce4c9ccc46955f3eb0f5e5fd5a5973efa4c7d1', nftData: '0x249df421b4b2f3ec2efb3f16614c04695217509ef442e54c87b7dd065b7f1955', testNotOx: '60412713752920209836220633720062444896781011774580807407425243696918755067857n', // nftTokenAddress: '0x1e29fd62ee556ee6bb9494faab32b6f269ebb4bf', // nftTokenId: 32768, // nftId: '0xfed3c4c4e2e1471c3f457a7ee9780ae3c77ee7e05ac77bf573ccd5145db7c674', //UAT // nftData: '0x0e5542cc682fae472f0f9909573244551e56ad25da62a82a1e18f28d0f392147', // testNotOx: '727e0fa09389156fc803eaf9c7017338efd76e7f', tradeLRCValue: 80000000000000, tradeETHValue: 0.0001, //same as UI gasPrice: 20, // for test gasLimit: 200000, // for test validUntil: Math.round(Date.now() / 1000) + 30 * 86400, } const provider = new PrivateKeyProvider( LOOPRING_EXPORTED_ACCOUNT.privateKey, 'https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3', ) const provider2 = new PrivateKeyProvider( LOOPRING_EXPORTED_ACCOUNT.privateKey2, 'https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3', ) // const providerWhiteList = new PrivateKeyProvider( // LOOPRING_EXPORTED_ACCOUNT.whitelistedEddkey, // "https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3" // ); export const web3 = new Web3(provider) export const web3_2 = new Web3(provider2) export const CUSTOMER_KEY_SEED = 'XXXXXX' + ' with key nonce: ' + '${nonce}' // LoopringAPI.exchangeAPI.getTokens() // const {markets:marketMap} LoopringAPI.exchangeAPI?.getMixMarkets() export let TOKEN_INFO = { addressIndex: { '0x0000000000000000000000000000000000000000': 'ETH', '0xfc28028d9b1f6966fe74710653232972f50673be': 'LRC', '0xd4e71c4bb48850f5971ce40aa428b09f242d3e8a': 'USDT', '0xfeb069407df0e1e4b365c10992f1bc16c078e34b': 'LP-LRC-ETH', '0x049a02fa9bc6bd54a2937e67d174cc69a9194f8e': 'LP-ETH-USDT', '0xcd2c81b322a5b530b5fa3432e57da6803b0317f7': 'DAI', '0x47525e6a5def04c9a56706e93f54cc70c2e8f165': 'USDC', '0xf37cf4ced77b985708d591acc6bfd08586ab3409': 'LP-USDC-ETH', }, 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, }, }, } // const {} = LoopringAPI.ammpoolAPI?.getAmmPoolConf()) export let AMM_MAP = { 'AMM-LRC-ETH': { name: 'LRCETH-Pool', market: 'AMM-LRC-ETH', address: '0xfEB069407df0e1e4B365C10992F1bc16c078E34b', version: '1.0.0', tokens: { pooled: [1, 0], lp: 4 }, feeBips: 20, precisions: { price: 6, amount: 5 }, createdAt: '1617967800000', status: 31, }, 'AMM-ETH-USDT': { name: 'AMM-ETH-USDT', market: 'AMM-ETH-USDT', address: '0x049a02FA9bc6bd54a2937E67D174cc69a9194f8e', version: '1.0.0', tokens: { pooled: [0, 2], lp: 7 }, feeBips: 20, precisions: { price: 3, amount: 3 }, createdAt: '1617972300000', status: 31, }, 'AMM-USDC-ETH': { name: 'AMM-USDC-ETH', market: 'AMM-USDC-ETH', address: '0xf37cf4CEd77b985708D591AcC6BfD08586Ab3409', version: '1.0.0', tokens: { pooled: [8, 0], lp: 9, }, feeBips: 20, precisions: { price: 3, amount: 4, }, createdAt: '1636974420000', status: 0, }, } export const testTypedData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], TestTypedData: [ { name: 'from', type: 'address' }, { name: 'to', type: 'address' }, { name: 'tokenID', type: 'uint16' }, ], }, primaryType: 'TestTypedData', domain: { name: 'Loopring Protocol', version: '3.6.0', chainId: sdk.ChainId.GOERLI, verifyingContract: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, }, message: { from: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address2, tokenID: TOKEN_INFO.tokenMap.LRC.tokenId, }, } export async function signatureKeyPairMock(accInfo: sdk.AccountInfo, _web3: Web3 = web3) { //@ts-ignore global.ethereum = providers?.EtherscanProvider // console.log('accInfo', accInfo) //@ts-ignore const eddsaKey = await sdk.generateKeyPair({ web3: _web3, address: accInfo.owner, keySeed: !accInfo.keySeed && accInfo.keySeed == '' ? sdk.GlobalAPI.KEY_MESSAGE.replace( '${exchangeAddress}', LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, ).replace('${nonce}', (accInfo.nonce - 1).toString()) : accInfo.keySeed, walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }) return eddsaKey } ================================================ FILE: src/tests/MockSwapData.ts ================================================ //Default config value from getTokens & getMixMarkets import * as sdk from "../index"; export const marketArray = ["LRC-ETH"]; export const marketMap = { "LRC-ETH": { baseTokenId: 1, enabled: true, market: "LRC-ETH", orderbookAggLevels: 5, precisionForPrice: 6, quoteTokenId: 0, status: 3, isSwapEnabled: true, createdAt: 1617967800000, }, }; //v3/mix/depth?level=0&limit=50&market=LRC-ETH export const deepMock = { symbol: "LRC-ETH", version: 23249677, timestamp: 1655719492365, mid_price: 0.00033248, bids: [ { price: 0.00030689, amt: "12041160324514792497908", vol: "3695372332571085210", amtTotal: "618450503644320209925641", volTotal: "198539605794234049017", }, { price: 0.00030752, amt: "12016302126785109160251", vol: "3695372332571085210", amtTotal: "606409343319805417427733", volTotal: "194844233461662963807", }, { price: 0.00030816, amt: "11991520826895479525387", vol: "3695372332571085210", amtTotal: "594393041193020308267482", volTotal: "191148861129091878597", }, { price: 0.00030881, amt: "12329062048917727073625", vol: "3807353312345966580", amtTotal: "582401520366124828742095", volTotal: "187453488796520793387", }, { price: 0.00030945, amt: "11941442525358097768419", vol: "3695372332571085210", amtTotal: "570072458317207101668470", volTotal: "183646135484174826807", }, { price: 0.0003102, amt: "3223726000000000000000", vol: "999999805200000000", amtTotal: "558131015791849003900051", volTotal: "179950763151603741597", }, { price: 0.0003104, amt: "11904963012947201084062", vol: "3695372332571085210", amtTotal: "554907289791849003900051", volTotal: "178950763346403741597", }, { price: 0.00031072, amt: "11892800074855146120151", vol: "3695372332571085210", amtTotal: "543002326778901802815989", volTotal: "175255391013832656387", }, { price: 0.00031137, amt: "12227667622887455762012", vol: "3807353312345966580", amtTotal: "531109526704046656695838", volTotal: "171560018681261571177", }, { price: 0.00031202, amt: "11843337524732768607817", vol: "3695372332571085210", amtTotal: "518881859081159200933826", volTotal: "167752665368915604597", }, { price: 0.00031266, amt: "11819088718260537718160", vol: "3695372332571085210", amtTotal: "507038521556426432326009", volTotal: "164057293036344519387", }, { price: 0.0003133, amt: "11794914308461194855590", vol: "3695372332571085210", amtTotal: "495219432838165894607849", volTotal: "160361920703773434177", }, { price: 0.00031395, amt: "12127129883064173669497", vol: "3807353312345966580", amtTotal: "483424518529704699752259", volTotal: "156666548371202348967", }, { price: 0.0003146, amt: "11746060536480763829870", vol: "3695372332571085210", amtTotal: "471297388646640526082762", volTotal: "152859195058856382387", }, { price: 0.00031524, amt: "11722109721002274877424", vol: "3695372332571085210", amtTotal: "459551328110159762252892", volTotal: "149163822726285297177", }, { price: 0.0003159, amt: "12052351993023897680738", vol: "3807353312345966580", amtTotal: "447829218389157487375468", volTotal: "145468450393714211967", }, { price: 0.00031655, amt: "11673707113082743010268", vol: "3695372332571085210", amtTotal: "435776866396133589694730", volTotal: "141661097081368245387", }, { price: 0.00031699, amt: "615000000000000000000", vol: "194954999999999949", amtTotal: "424103159283050846684462", volTotal: "137965724748797160177", }, { price: 0.00031719, amt: "11649977142974837964110", vol: "3695372332571085210", amtTotal: "423488159283050846684462", volTotal: "137770769748797160228", }, { price: 0.00031784, amt: "11626319455782556517212", vol: "3695372332571085210", amtTotal: "411838182140076008720352", volTotal: "134075397416226075018", }, { price: 0.0003185, amt: "11953964321007990024755", vol: "3807353312345966580", amtTotal: "400211862684293452203140", volTotal: "130380025083654989808", }, { price: 0.00031934, amt: "11571837200722111826863", vol: "3695372332571085210", amtTotal: "388257898363285462178385", volTotal: "126572671771309023228", }, { price: 0.00031979, amt: "11555429099390519988949", vol: "3695372332571085210", amtTotal: "376686061162563350351522", volTotal: "122877299438737938018", }, { price: 0.00032, amt: "300000000000000000000", vol: "96000000000000000", amtTotal: "365130632063172830362573", volTotal: "119181927106166852808", }, { price: 0.00032044, amt: "11532058329906875386268", vol: "3695372332571085210", amtTotal: "364830632063172830362573", volTotal: "119085927106166852808", }, { price: 0.0003211, amt: "11857145662584872212009", vol: "3807353312345966580", amtTotal: "353298573733265954976305", volTotal: "115390554773595767598", }, { price: 0.00032176, amt: "11484826169666631095087", vol: "3695372332571085210", amtTotal: "341441428070681082764296", volTotal: "111583201461249801018", }, { price: 0.00032241, amt: "11461669156035188891431", vol: "3695372332571085210", amtTotal: "329956601901014451669209", volTotal: "107887829128678715808", }, { price: 0.00032294, amt: "22266286128259997368320", vol: "7190674442260284416", amtTotal: "318494932744979262777778", volTotal: "104192456796107630598", }, { price: 0.00032307, amt: "11784846142950476791130", vol: "3807353312345966580", amtTotal: "296228646616719265409458", volTotal: "97001782353847346182", }, { price: 0.00032333, amt: "31042151210389998665728", vol: "10037169172367501312", amtTotal: "284443800473768788618328", volTotal: "93194429041501379602", }, { price: 0.00032354, amt: "19082720347089998446592", vol: "6174214168300968960", amtTotal: "253401649263378789952600", volTotal: "83157259869133878290", }, { price: 0.00032373, amt: "11414868337793698506576", vol: "3695372332571085210", amtTotal: "234318928916288791506008", volTotal: "76983045700832909330", }, { price: 0.00032438, amt: "11391922481177051106050", vol: "3695372332571085210", amtTotal: "222904060578495092999432", volTotal: "73287673368261824120", }, { price: 0.00032503, amt: "11369045742792164433715", vol: "3695372332571085210", amtTotal: "211512138097318041893382", volTotal: "69592301035690738910", }, { price: 0.00032512, amt: "7101984539779999465472", vol: "2308997213573273600", amtTotal: "200143092354525877459667", volTotal: "65896928703119653700", }, { price: 0.0003257, amt: "11689707913498419991686", vol: "3807353312345966580", amtTotal: "193041107814745877994195", volTotal: "63587931489546380100", }, { price: 0.00032636, amt: "11322810509578528739940", vol: "3695372332571085210", amtTotal: "181351399901247458002509", volTotal: "59780578177200413520", }, { price: 0.00032702, amt: "11300141532643741902081", vol: "3695372332571085210", amtTotal: "170028589391668929262569", volTotal: "56085205844629328310", }, { price: 0.00032709, amt: "1511898000000000000000", vol: "494541835799999898", amtTotal: "158728447859025187360488", volTotal: "52389833512058243100", }, { price: 0.00032767, amt: "11277540564700637960582", vol: "3695372332571085210", amtTotal: "157216549859025187360488", volTotal: "51895291676258243202", }, { price: 0.00032838, amt: "11594014901713773553177", vol: "3807353312345966580", amtTotal: "145939009294324549399906", volTotal: "48199919343687157992", }, { price: 0.00032899, amt: "11232203228917728004878", vol: "3695372332571085210", amtTotal: "134344994392610775846729", volTotal: "44392566031341191412", }, { price: 0.00032959, amt: "31100749000000000753664", vol: "10250806870400000000", amtTotal: "123112791163693047841851", volTotal: "40697193698770106202", }, { price: 0.00032965, amt: "11209805337410429375102", vol: "3695372332571085210", amtTotal: "92012042163693047088187", volTotal: "30446386828370106202", }, { price: 0.00033031, amt: "11187474373893810850495", vol: "3695372332571085210", amtTotal: "80802236826282617713085", volTotal: "26751014495799020992", }, { price: 0.00033098, amt: "11503202913004006553102", vol: "3807353312345966580", amtTotal: "69614762452388806862590", volTotal: "23055642163227935782", }, { price: 0.00033099, amt: "26515451000000002129920", vol: "8776614281000000512", amtTotal: "58111559539384800309488", volTotal: "19248288850881969202", }, { price: 0.0003313, amt: "20499506300000000802816", vol: "6791486437190000640", amtTotal: "31596108539384798179568", volTotal: "10471674569881968690", }, { price: 0.00033165, amt: "11096602239384797376752", vol: "3680188132691968050", amtTotal: "11096602239384797376752", volTotal: "3680188132691968050", }, ], bids_prices: [ 0.00030689, 0.00030752, 0.00030816, 0.00030881, 0.00030945, 0.0003102, 0.0003104, 0.00031072, 0.00031137, 0.00031202, 0.00031266, 0.0003133, 0.00031395, 0.0003146, 0.00031524, 0.0003159, 0.00031655, 0.00031699, 0.00031719, 0.00031784, 0.0003185, 0.00031934, 0.00031979, 0.00032, 0.00032044, 0.0003211, 0.00032176, 0.00032241, 0.00032294, 0.00032307, 0.00032333, 0.00032354, 0.00032373, 0.00032438, 0.00032503, 0.00032512, 0.0003257, 0.00032636, 0.00032702, 0.00032709, 0.00032767, 0.00032838, 0.00032899, 0.00032959, 0.00032965, 0.00033031, 0.00033098, 0.00033099, 0.0003313, 0.00033165, ], bids_amtTotals: [ "618450503644320209925641", "606409343319805417427733", "594393041193020308267482", "582401520366124828742095", "570072458317207101668470", "558131015791849003900051", "554907289791849003900051", "543002326778901802815989", "531109526704046656695838", "518881859081159200933826", "507038521556426432326009", "495219432838165894607849", "483424518529704699752259", "471297388646640526082762", "459551328110159762252892", "447829218389157487375468", "435776866396133589694730", "424103159283050846684462", "423488159283050846684462", "411838182140076008720352", "400211862684293452203140", "388257898363285462178385", "376686061162563350351522", "365130632063172830362573", "364830632063172830362573", "353298573733265954976305", "341441428070681082764296", "329956601901014451669209", "318494932744979262777778", "296228646616719265409458", "284443800473768788618328", "253401649263378789952600", "234318928916288791506008", "222904060578495092999432", "211512138097318041893382", "200143092354525877459667", "193041107814745877994195", "181351399901247458002509", "170028589391668929262569", "158728447859025187360488", "157216549859025187360488", "145939009294324549399906", "134344994392610775846729", "123112791163693047841851", "92012042163693047088187", "80802236826282617713085", "69614762452388806862590", "58111559539384800309488", "31596108539384798179568", "11096602239384797376752", ], bids_volTotals: [ "198539605794234049017", "194844233461662963807", "191148861129091878597", "187453488796520793387", "183646135484174826807", "179950763151603741597", "178950763346403741597", "175255391013832656387", "171560018681261571177", "167752665368915604597", "164057293036344519387", "160361920703773434177", "156666548371202348967", "152859195058856382387", "149163822726285297177", "145468450393714211967", "141661097081368245387", "137965724748797160177", "137770769748797160228", "134075397416226075018", "130380025083654989808", "126572671771309023228", "122877299438737938018", "119181927106166852808", "119085927106166852808", "115390554773595767598", "111583201461249801018", "107887829128678715808", "104192456796107630598", "97001782353847346182", "93194429041501379602", "83157259869133878290", "76983045700832909330", "73287673368261824120", "69592301035690738910", "65896928703119653700", "63587931489546380100", "59780578177200413520", "56085205844629328310", "52389833512058243100", "51895291676258243202", "48199919343687157992", "44392566031341191412", "40697193698770106202", "30446386828370106202", "26751014495799020992", "23055642163227935782", "19248288850881969202", "10471674569881968690", "3680188132691968050", ], bids_amtTotal: "618450503644320209925641", bids_volTotal: "198539605794234049017", asks: [ { price: 0.00033331, amt: "26466599999999999737856", vol: "8821317780000000000", amtTotal: "26466599999999999737856", volTotal: "8821317780000000000", }, { price: 0.0003336, amt: "33880800000000001048576", vol: "11302634880000000000", amtTotal: "60347400000000000786432", volTotal: "20123952660000000000", }, { price: 0.00033371, amt: "11142477800817399987988", vol: "3718356266910774549", amtTotal: "71489877800817400774420", volTotal: "23842308926910774549", }, { price: 0.00033439, amt: "11410124103475892197402", vol: "3815340399218399705", amtTotal: "82900001904293292971822", volTotal: "27657649326129174254", }, { price: 0.00033501, amt: "39334199999999992922112", vol: "13176957000000000000", amtTotal: "122234201904293285893934", volTotal: "40834606326129174254", }, { price: 0.00033506, amt: "11052295534472154230337", vol: "3703108939445215718", amtTotal: "133286497438765440124271", volTotal: "44537715265574389972", }, { price: 0.00033572, amt: "11030456322913363709718", vol: "3703093651997124565", amtTotal: "144316953761678803833989", volTotal: "48240808917571514537", }, { price: 0.00033638, amt: "11008681778518069039476", vol: "3703078409816047858", amtTotal: "155325635540196872873465", volTotal: "51943887327387562395", }, { price: 0.000337, amt: "7188173683760000139264", vol: "2422342649690282496", amtTotal: "162513809223956873012729", volTotal: "54366229977077844891", }, { price: 0.00033706, amt: "11319571950108460741855", vol: "3815277012711042502", amtTotal: "173833381174065333754584", volTotal: "58181506989788887393", }, { price: 0.00033773, amt: "10964670732513311387215", vol: "3703047602083844528", amtTotal: "184798051906578645141799", volTotal: "61884554591872731921", }, { price: 0.00033848, amt: "10943090596879450870975", vol: "3703921209677097807", amtTotal: "195741142503458096012774", volTotal: "65588475801549829728", }, { price: 0.00033893, amt: "31086310012520000126976", vol: "10535772189443278848", amtTotal: "226827452515978096139750", volTotal: "76124247990993108576", }, { price: 0.00033907, amt: "10921574108084085072499", vol: "3703126650187824910", amtTotal: "237749026624062181212249", volTotal: "79827374641180933486", }, { price: 0.00033934, amt: "22061773569720001232896", vol: "7486221625413088256", amtTotal: "259810800193782182445145", volTotal: "87313596266594021742", }, { price: 0.00033975, amt: "11230093484570461106334", vol: "3815326678720011608", amtTotal: "271040893678352643551479", volTotal: "91128922945314033350", }, { price: 0.00034042, amt: "10878083875111520592396", vol: "3703095772122414389", amtTotal: "281918977553464164143875", volTotal: "94832018717436447739", }, { price: 0.00034061, amt: "19029641944770000453632", vol: "6481686342808109056", amtTotal: "300948619498234164597507", volTotal: "101313705060244556795", }, { price: 0.00034109, amt: "10856758733704286197912", vol: "3703080631272015254", amtTotal: "311805378231938450795419", volTotal: "105016785691516572049", }, { price: 0.00034176, amt: "10835496238854895014260", vol: "3703065534900672185", amtTotal: "322640874470793345809679", volTotal: "108719851226417244234", }, { price: 0.00034244, amt: "11141671799245742102774", vol: "3815263899323431057", amtTotal: "333782546270039087912453", volTotal: "112535115125740675291", }, { price: 0.00034312, amt: "10792518633700140402041", vol: "3703035020801012310", amtTotal: "344575064903739228314494", volTotal: "116238150146541687601", }, { price: 0.00034379, amt: "10771444484888773263977", vol: "3703020058155356239", amtTotal: "355346509388628001578471", volTotal: "119941170204697043840", }, { price: 0.00034447, amt: "11075875299655656611658", vol: "3815217183808722096", amtTotal: "366422384688283658190129", volTotal: "123756387388505765936", }, { price: 0.00034515, amt: "10728847020295584791583", vol: "3702989813955495075", amtTotal: "377151231708579242981712", volTotal: "127459377202461261011", }, { price: 0.00034582, amt: "10707958999548106661900", vol: "3702974983460764366", amtTotal: "387859190708127349643612", volTotal: "131162352185922025377", }, { price: 0.00034649, amt: "10687131919892014508549", vol: "3702960196234208540", amtTotal: "398546322628019364152161", volTotal: "134865312382156233917", }, { price: 0.00034718, amt: "10989265205593964126102", vol: "3815155690641938295", amtTotal: "409535587833613328278263", volTotal: "138680468072798172212", }, { price: 0.00034809, amt: "10645033127428079147754", vol: "3705341637309061860", amtTotal: "420180620961041407426017", volTotal: "142385809710107234072", }, { price: 0.00034854, amt: "10624389276271547157517", vol: "3703021892850000724", amtTotal: "430805010237312954583534", volTotal: "146088831602957234796", }, { price: 0.00034922, amt: "10603805418694082174472", vol: "3703007072472544949", amtTotal: "441408815656007036758006", volTotal: "149791838675429779745", }, { price: 0.00034991, amt: "10903667052066234563130", vol: "3815203952623454269", amtTotal: "452312482708073271321136", volTotal: "153607042628053234014", }, { price: 0.00035059, amt: "10562197544947631702552", vol: "3702977114803447505", amtTotal: "462874680253020903023688", volTotal: "157310019742856681519", }, { price: 0.00035127, amt: "10541794072549149477757", vol: "3702962424303320597", amtTotal: "473416474325570052501445", volTotal: "161012982167160002116", }, { price: 0.00035196, amt: "10839964505283434387937", vol: "3815158086789770653", amtTotal: "484256438830853486889382", volTotal: "164828140253949772769", }, { price: 0.00035265, amt: "10500550295026604907238", vol: "3702932728783504366", amtTotal: "494756989125880091796620", volTotal: "168531072982733277135", }, { price: 0.00035333, amt: "10480325106225117837726", vol: "3702918166647567295", amtTotal: "505237314232105209634346", volTotal: "172233991149380844430", }, { price: 0.00035401, amt: "10460158295034970667917", vol: "3702903646543510388", amtTotal: "515697472527140180302263", volTotal: "175936894795924354818", }, { price: 0.0003547, amt: "10756101480844670483856", vol: "3815097705412174743", amtTotal: "526453574007984850786119", volTotal: "179751992501336529561", }, { price: 0.000355, amt: "2879934000000000000000", vol: "1022376570000000000", amtTotal: "529333508007984850786119", volTotal: "180774369071336529561", }, { price: 0.00035539, amt: "10419392212908119384882", vol: "3702874294964379056", amtTotal: "539752900220892970171001", volTotal: "184477243366300908617", }, { price: 0.00035607, amt: "10399400937404414745812", vol: "3702859901246016389", amtTotal: "550152301158297384916813", volTotal: "188180103267546925006", }, { price: 0.00035675, amt: "10379467141462596339294", vol: "3702845548912938279", amtTotal: "560531768299759981256107", volTotal: "191882948816459863285", }, { price: 0.00035781, amt: "10673207910450037674513", vol: "3818930797471485007", amtTotal: "571204976210210018930620", volTotal: "195701879613931348292", }, { price: 0.00035815, amt: "10339171406073422424770", vol: "3702919927697518809", amtTotal: "581544147616283441355390", volTotal: "199404799541628867101", }, { price: 0.00035883, amt: "10319410450646547471681", vol: "3702905502200057190", amtTotal: "591863558066929988827071", volTotal: "203107705043828924291", }, { price: 0.00035887, amt: "1253970999999999795200", vol: "449999996860000051", amtTotal: "593117529066929988622271", volTotal: "203557705040688924342", }, { price: 0.00035952, amt: "10299706093934767574874", vol: "3702891118019657590", amtTotal: "603417235160864756197145", volTotal: "207260596158708581932", }, { price: 0.00036, amt: "491031000000000000000", vol: "176771040000000000", amtTotal: "603908266160864756197145", volTotal: "207437367198708581932", }, { price: 0.00036022, amt: "10591268908994289896668", vol: "3815084938649532411", amtTotal: "614499535069859046093813", volTotal: "211252452137358114343", }, ], asks_prices: [ 0.00033331, 0.0003336, 0.00033371, 0.00033439, 0.00033501, 0.00033506, 0.00033572, 0.00033638, 0.000337, 0.00033706, 0.00033773, 0.00033848, 0.00033893, 0.00033907, 0.00033934, 0.00033975, 0.00034042, 0.00034061, 0.00034109, 0.00034176, 0.00034244, 0.00034312, 0.00034379, 0.00034447, 0.00034515, 0.00034582, 0.00034649, 0.00034718, 0.00034809, 0.00034854, 0.00034922, 0.00034991, 0.00035059, 0.00035127, 0.00035196, 0.00035265, 0.00035333, 0.00035401, 0.0003547, 0.000355, 0.00035539, 0.00035607, 0.00035675, 0.00035781, 0.00035815, 0.00035883, 0.00035887, 0.00035952, 0.00036, 0.00036022, ], asks_amtTotals: [ "26466599999999999737856", "60347400000000000786432", "71489877800817400774420", "82900001904293292971822", "122234201904293285893934", "133286497438765440124271", "144316953761678803833989", "155325635540196872873465", "162513809223956873012729", "173833381174065333754584", "184798051906578645141799", "195741142503458096012774", "226827452515978096139750", "237749026624062181212249", "259810800193782182445145", "271040893678352643551479", "281918977553464164143875", "300948619498234164597507", "311805378231938450795419", "322640874470793345809679", "333782546270039087912453", "344575064903739228314494", "355346509388628001578471", "366422384688283658190129", "377151231708579242981712", "387859190708127349643612", "398546322628019364152161", "409535587833613328278263", "420180620961041407426017", "430805010237312954583534", "441408815656007036758006", "452312482708073271321136", "462874680253020903023688", "473416474325570052501445", "484256438830853486889382", "494756989125880091796620", "505237314232105209634346", "515697472527140180302263", "526453574007984850786119", "529333508007984850786119", "539752900220892970171001", "550152301158297384916813", "560531768299759981256107", "571204976210210018930620", "581544147616283441355390", "591863558066929988827071", "593117529066929988622271", "603417235160864756197145", "603908266160864756197145", "614499535069859046093813", ], asks_volTotals: [ "8821317780000000000", "20123952660000000000", "23842308926910774549", "27657649326129174254", "40834606326129174254", "44537715265574389972", "48240808917571514537", "51943887327387562395", "54366229977077844891", "58181506989788887393", "61884554591872731921", "65588475801549829728", "76124247990993108576", "79827374641180933486", "87313596266594021742", "91128922945314033350", "94832018717436447739", "101313705060244556795", "105016785691516572049", "108719851226417244234", "112535115125740675291", "116238150146541687601", "119941170204697043840", "123756387388505765936", "127459377202461261011", "131162352185922025377", "134865312382156233917", "138680468072798172212", "142385809710107234072", "146088831602957234796", "149791838675429779745", "153607042628053234014", "157310019742856681519", "161012982167160002116", "164828140253949772769", "168531072982733277135", "172233991149380844430", "175936894795924354818", "179751992501336529561", "180774369071336529561", "184477243366300908617", "188180103267546925006", "191882948816459863285", "195701879613931348292", "199404799541628867101", "203107705043828924291", "203557705040688924342", "207260596158708581932", "207437367198708581932", "211252452137358114343", ], asks_amtTotal: "614499535069859046093813", asks_volTotal: "211252452137358114343", }; export const dexDethRawMock = { version: 4855849, timestamp: 1680161323317, market: "LRC-USDT", bids: [ ["327.41", "18.54000", "18540000000000000000"], ["327.00", "1.84000", "1840000000000000000"], ["326.90", "0.21000", "210000000000000000"], ["326.80", "8.70000", "8700000000000000000"], ["326.70", "5.09000", "5090000000000000000"], ["326.60", "6.47000", "6470000000000000000"], ["326.50", "7.23000", "7230000000000000000"], ["326.40", "6.13000", "6130000000000000000"], ["326.30", "7.45000", "7450000000000000000"], ["326.21", "56.05000", "56050000000000000000"], ["326.20", "5.98000", "5980000000000000000"], ], asks: [ ["310.64", "1.35000", "1350000000000000000"], ["313.30", "9.36000", "9360000000000000000"], ["313.40", "6.55000", "6550000000000000000"], ["313.50", "5.65000", "5650000000000000000"], ["313.60", "7.56000", "7560000000000000000"], ["313.70", "8.68000", "8680000000000000000"], ["313.80", "5.29000", "5290000000000000000"], ["313.90", "6.25000", "6250000000000000000"], ["314.00", "9.05000", "9050000000000000000"], ["314.10", "7.90000", "7900000000000000000"], ["314.20", "5.38000", "5380000000000000000"], ], }; //v3/amm/balance?poolAddress=0xfEB069407df0e1e4B365C10992F1bc16c078E34b export const ammPoolSnapshot: sdk.AmmPoolSnapshot = { poolName: "AMM-LRC-ETH", poolAddress: "0x18920d6e6fb7ebe057a4dd9260d6d95845c95036", pooled: [ { tokenId: 1, volume: "11198097977488137000000000", }, { tokenId: 0, volume: "3725368050950874300000", }, ], lp: { tokenId: 83, volume: "34138981282200", }, risky: false, }; //v3/amm/balance?poolAddress=0xfEB069407df0e1e4B365C10992F1bc16c078E34b export const ammPool = { poolName: "AMM-LRC-ETH", poolAddress: "0x18920d6e6fb7ebe057a4dd9260d6d95845c95036", pooled: [ { tokenId: 1, volume: "11215027899488137000000000" }, { tokenId: 0, volume: "3720052711450874300000" }, ], lp: { tokenId: 83, volume: "34141365482200" }, risky: false, }; //v3/mix/ticker?market=LRC-ETH export const ticker = { tickers: [ [ "COMBINE-LRC-ETH", "1655625077879", "2429371927000000000000000", "814655525450000000000", "0.00035052", "0.00035222", "0.00032201", "0.00033367", "772", "", "", "", "", ], ], }; //v3/user/orderUserRateAmount?accountId=10427&market=LRC-ETH export const userAmount = { "LRC-ETH": { LRC: { tokenSymbol: "LRC", baseOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, tradeCost: "231046501539337141", }, ETH: { tokenSymbol: "ETH", baseOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, tradeCost: "75150737796750", }, }, "AMM-LRC-ETH": { LRC: { tokenSymbol: "LRC", baseOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, tradeCost: "231046501539337141", }, ETH: { tokenSymbol: "ETH", baseOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, tradeCost: "75150737796750", }, }, }; export const TokenMapMockSwap = { ETH: { type: "ETH", tokenId: 0, symbol: "ETH", name: "Ethereum", address: "0x0000000000000000000000000000000000000000", decimals: 18, precision: 7, precisionForOrder: 3, orderAmounts: { minimum: "1700000000000000", maximum: "1000000000000000000000", dust: "200000000000000", }, luckyTokenAmounts: { minimum: "50000000000000", maximum: "1000000000000000000000", dust: "50000000000000", }, fastWithdrawLimit: "100000000000000000000", gasAmounts: { distribution: "85000", deposit: "110000", }, enabled: true, isLpToken: false, tradePairs: ["LRC"], }, LRC: { type: "ERC20", tokenId: 1, symbol: "LRC", name: "Loopring", address: "0xbbbbca6a901c926f240b89eacb641d8aec7aeafd", 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: "150000", }, enabled: true, isLpToken: false, tradePairs: ["ETH"], }, }; export const MAPFEEBIPS = 63; ================================================ FILE: src/tests/README.md ================================================ # Loopring SDK Loopring SDK Initialize and Mock Data structure *** ## SDK Initialize ```ts import * as sdk from "../index"; export class LoopringAPIClass { public static userAPI: UserAPI; public static exchangeAPI: ExchangeAPI; public static ammpoolAPI: AmmpoolAPI; public static walletAPI: WalletAPI; public static wsAPI: WsAPI; public static nftAPI: NFTAPI; public static delegate: DelegateAPI; public static globalAPI: GlobalAPI; public static contractAPI: typeof ContractAPI; public static __chainId__: sdk.ChainId; public static InitApi = (chainId: sdk.ChainId) => { LoopringAPI.userAPI = new UserAPI({ chainId }); LoopringAPI.exchangeAPI = new ExchangeAPI({ chainId }); LoopringAPI.globalAPI = new GlobalAPI({ chainId }); LoopringAPI.ammpoolAPI = new AmmpoolAPI({ chainId }); LoopringAPI.walletAPI = new WalletAPI({ chainId }); LoopringAPI.wsAPI = new WsAPI({ chainId }); LoopringAPI.nftAPI = new NFTAPI({ chainId }); LoopringAPI.delegate = new DelegateAPI({ chainId }); LoopringAPI.__chainId__ = chainId; LoopringAPI.contractAPI = ContractAPI; }; } /* env: * test: sdk.ChainId.GOERLI * eth: sdk.ChainId.MAINNET */ LoopringAPIClass.InitApi({sdk.ChainId.MAINNET}); ``` *** ## Connect Wallet Suggestion web3 provider ```shell # Using npm npm i @loopring-web/web3-provider --save # Using yarn yarn add @loopring-web/web3-provider ``` - [Demo Vue](https://codesandbox.io/s/vue-8nco78) - [Demo React](https://codesandbox.io/s/react-4v50ft) [Mock Provider](#mock-provider) *** ## Loopring ERC20 Data Structure ```ts const {tokensMap, idIndex, addressIndex} = LoopringAPI.exchangeAPI.getTokens(); ``` [Mock ERC20](#mock-erc20-token-map) *** ## Token Decimal ```ts const uiValue = 100; const tradeValue = sdk.toBig(uiValue).times("1e" + TOKEN_INFO.tokenMap.LRC.decimals); ``` *** ## Test Mock Data ##### Mock Account ```ts export const LOOPRING_EXPORTED_ACCOUNT = { address: "0x727e0fa09389156fc803eaf9c7017338efd76e7f", privateKey: "491aecdb1d5f6400a6b62fd12a41a86715bbab675c37a4060ba115fecf94083c", accountId: 12454, address2: "0xb6d8c39D5528357dBCe6BEd82aC71c74e9D19079", privateKey2: "e020ed769032ba95d9a5207687a663d6198fe2f5cedf28a250f7cbd8c81a5263", accountId2: 10488, addressCF: "0x23dE4Da688c94a66E8bbE9BCc95CB03b4e209C15", accountIdCF: 11632, addressContractWallet: "0xD4BD7c71B6d4A09217ccc713f740d6ed8f4EA0cd", depositAddress: "0xb684B265f650a77afd27Ce0D95252a7329B5bD72", exchangeAddress: "0x2e76EBd1c7c0C8e7c2B875b6d505a260C525d25e", whitelistedAddress: "0x35405E1349658BcA12810d0f879Bf6c5d89B512C", whitelistedEddkey: "0x27a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33", // const eddkeyWhitelisted = // "0x27a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33"; // apiKey: "2PYgTOZwXHkPXtJMlOMG06ZX1QKJInpoky6iYIbtMgmkbfdL4PvxyEOj0LPOfgYX", chainId: 5, nftTokenAddress: "0x8394cB7e768070217592572582228f62CdDE4FCE", nftTokenId: 32768, nftId: "0xa0ce8990402955e559799af24ea765b14ffecc32dfa1cce2dadaf20016b074e6", nftData: "0x1a2001aac7a1fd00cef07889cdb67b1355f86e5bc9df71cfa44fa1c7b49f598f", testNotOx: "727e0fa09389156fc803eaf9c7017338efd76e7f", tradeLRCValue: 1000000000000000000, tradeETHValue: 0.0001, //same as UI gasPrice: 20, // for test gasLimit: 200000, // for test validUntil: Math.round(Date.now() / 1000) + 30 * 86400, }; export const CUSTOMER_KEY_SEED = "XXXXXX" + " with key nonce: " + "${nonce}"; ``` ##### Mock provider ```ts const provider = new PrivateKeyProvider( LOOPRING_EXPORTED_ACCOUNT.privateKey, "https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3" ); const provider2 = new PrivateKeyProvider( LOOPRING_EXPORTED_ACCOUNT.privateKey2, "https://goerli.infura.io/v3/a06ed9c6b5424b61beafff27ecc3abf3" ); export const web3 = new Web3(provider); export const web3_2 = new Web3(provider2); ``` ##### Mock ERC20 Token Map ```ts export let TOKEN_INFO = { addressIndex: { "0x0000000000000000000000000000000000000000": "ETH", "0xfc28028d9b1f6966fe74710653232972f50673be": "LRC", "0xd4e71c4bb48850f5971ce40aa428b09f242d3e8a": "USDT", "0xfeb069407df0e1e4b365c10992f1bc16c078e34b": "LP-LRC-ETH", "0x049a02fa9bc6bd54a2937e67d174cc69a9194f8e": "LP-ETH-USDT", "0xcd2c81b322a5b530b5fa3432e57da6803b0317f7": "DAI", "0x47525e6a5def04c9a56706e93f54cc70c2e8f165": "USDC", "0xf37cf4ced77b985708d591acc6bfd08586ab3409": "LP-USDC-ETH", }, 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, }, }, }; ``` ##### Mock AMM MAP ```ts export let AMM_MAP = { "AMM-LRC-ETH": { name: "LRCETH-Pool", market: "AMM-LRC-ETH", address: "0xfEB069407df0e1e4B365C10992F1bc16c078E34b", version: "1.0.0", tokens: { pooled: [1, 0], lp: 4 }, feeBips: 20, precisions: { price: 6, amount: 5 }, createdAt: "1617967800000", status: 31, }, "AMM-ETH-USDT": { name: "AMM-ETH-USDT", market: "AMM-ETH-USDT", address: "0x049a02FA9bc6bd54a2937E67D174cc69a9194f8e", version: "1.0.0", tokens: { pooled: [0, 2], lp: 7 }, feeBips: 20, precisions: { price: 3, amount: 3 }, createdAt: "1617972300000", status: 31, }, "AMM-USDC-ETH": { name: "AMM-USDC-ETH", market: "AMM-USDC-ETH", address: "0xf37cf4CEd77b985708D591AcC6BfD08586Ab3409", version: "1.0.0", tokens: { pooled: [8, 0], lp: 9, }, feeBips: 20, precisions: { price: 3, amount: 4, }, createdAt: "1636974420000", status: 0, }, }; ``` ##### Mock EIP712 Typed Data ```ts export const testTypedData: EIP712TypedData = { types: { EIP712Domain: [ { name: "name", type: "string" }, { name: "version", type: "string" }, { name: "chainId", type: "uint256" }, { name: "verifyingContract", type: "address" }, ], TestTypedData: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "tokenID", type: "uint16" }, ], }, primaryType: "TestTypedData", domain: { name: "Loopring Protocol", version: "3.6.0", chainId: sdk.ChainId.GOERLI, verifyingContract: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, }, message: { from: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address2, tokenID: TOKEN_INFO.tokenMap.LRC.tokenId, }, }; ``` ##### Mock generate eddsaKey ```ts export async function signatureKeyPairMock( accInfo: sdk.AccountInfo, _web3: Web3 = web3 ) { const eddsaKey = await sdk.generateKeyPair({ web3: _web3, address: accInfo.owner, keySeed: accInfo.keySeed ?? sdk.GlobalAPI.KEY_MESSAGE.replace( "${exchangeAddress}", LOOPRING_EXPORTED_ACCOUNT.exchangeAddress ).replace("${nonce}", (accInfo.nonce - 1).toString()), walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); return eddsaKey; } ``` ================================================ FILE: src/tests/UTC--2021-02-04T02-55-36.490219109Z--ef439044717c3af35f4f46e52aa99280217a7114 ================================================ {'address':'ef439044717c3af35f4f46e52aa99280217a7114','crypto':{'cipher':'aes-128-ctr','ciphertext':'143482fb2ed65e3abc9cc13fcaeb6ba74c4d756c8214df71ac976aec8252ed09','cipherparams':{'iv':'5a968f30a2345ab47f17b27b903dc211'},'kdf':'scrypt','kdfparams':{'dklen':32,'n':262144,'p':1,'r':8,'salt':'da706a1d49a37f901393ea20604587f70a04bbfecec861f8d5188dc5477410c8'},'mac':'983f9b60b7bfd030f14d1445001f2375c654a8a1a116ac6321e0e58d9a1f63f8'},'id':'13090125-668c-4935-9bcd-77512c377afd','version':3} ================================================ FILE: src/tests/demo/NFTAction/collectionNFT.md ================================================ # Collection NFT Definition: allow user create a NFT Collection with different Contract(token) address. - allow user mint the NFTs with collection - when collection deployed, NFTs will have different Contract(token) address in layer1 - Loopring own collection on L2, allow user to view/edit their Collection information. - will soon enable the "Import Collection" to manage legacy NFTs *** ##Create Collection ```ts const eddsaKey = await signatureKeyPairMock(accInfo); const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId,}, eddsaKey.sk ); const response = await LoopringAPI.userAPI.submitNFTCollection({ name: 'XXX' + Date.now(), //required, one account is not able to multiple tileUri: 'ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM', //required description: 'test', owner: mockData.accInfo.owner, avatar: 'ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM', banner: 'ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM', nftFactory: NFTFactory_Collection[LOOPRING_EXPORTED_ACCOUNT.chainId as ChainId ]}, LOOPRING_EXPORTED_ACCOUNT.chainId as ChainId, mockData.apiKey, mockData.eddsaKey.sk) console.log('createCollection', response) ``` ## getUserNFFByCollection ```ts const response = await LoopringAPI.userAPI .getUserNFTCollection( { accountId: mockData.accInfo.accountId.toString(), limit: 24, offset: 0, }, mockData.apiKey ) .catch((_error) => { throw _error; }); console.log("getUserNFFByCollection", response); ``` ## getUserOwnCollection (User own create Collection) ```ts const response = await LoopringAPI.userAPI .getUserOwenCollection( { owner: mockData.accInfo.owner, limit: 24, offset: 0, tokenAddress: undefined, isMintable: false, //false }, mockData.apiKey ) .catch((_error) => { throw _error; }); console.log("getUserNFFByCollection", response); ``` ## getUserNFTCollection (User asset NFT's Collections) ```ts const response = await LoopringAPI.userAPI .getUserNFTCollection({ tokenAddress: contract, // option collectionId: id, // option accountId: mockData.accInfo.accountId, limit: 20, offset:10, }, mockData.apiKey) ``` ## getCollectionWholeNFTs ```ts const response = await LoopringAPI.nftAPI.getCollectionWholeNFTs({ id: 279, offset: 0, limit: 24, metadata: true, }); console.log("getCollectionWholeNFTs", response); ``` ================================================ FILE: src/tests/demo/NFTAction/collectionNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, signatureKeyPairMock, } from "../../MockData"; import { AccountInfo, ChainId, NFTFactory_Collection } from "../../../index"; let mockData: | { accInfo: AccountInfo; apiKey: string; eddsaKey: any; } | undefined = undefined; describe("metaNFT", function () { beforeEach(async () => { // Step 1. getAccount const accInfo = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) ).accInfo; const eddsaKey = await signatureKeyPairMock(accInfo); // Step 3. apiKey const apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; mockData = { apiKey, accInfo, eddsaKey, }; }, DEFAULT_TIMEOUT * 3); it( "getUserOwnCollection", async () => { if (mockData) { const response = await LoopringAPI.userAPI .getUserOwenCollection( { owner: mockData.accInfo.owner, limit: 24, offset: 0, tokenAddress: undefined, isMintable: false, //false }, mockData.apiKey ) .catch((_error) => { throw _error; }); console.log("getUserNFFByCollection", response); } }, DEFAULT_TIMEOUT ); it( "getUserNFFByCollection", async () => { if (mockData) { const response = await LoopringAPI.userAPI .getUserNFTCollection( { accountId: mockData.accInfo.accountId.toString(), limit: 24, offset: 0, }, mockData.apiKey ) .catch((_error) => { throw _error; }); console.log("getUserNFFByCollection", response); } }, DEFAULT_TIMEOUT ); it( "getCollectionWholeNFTs", async () => { const response = await LoopringAPI.nftAPI.getCollectionWholeNFTs({ id: 279, offset: 0, limit: 24, metadata: true, }); console.log("getCollectionWholeNFTs", response); }, DEFAULT_TIMEOUT ); it( "createCollection", async () => { if (mockData) { const response = await LoopringAPI.userAPI.submitNFTCollection( { name: "XXX" + Date.now(), //required, one account is not able to multiple tileUri: "ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM", //required description: "test", owner: mockData.accInfo.owner, avatar: "ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM", banner: "ipfs://QmaNZ2FCgvBPqnxtkbToVVbK2Nes6xk5K4Ns6BsmkPucAM", nftFactory: NFTFactory_Collection[ LOOPRING_EXPORTED_ACCOUNT.chainId as ChainId ], }, LOOPRING_EXPORTED_ACCOUNT.chainId as ChainId, mockData.apiKey, mockData.eddsaKey.sk ); console.log("createCollection", response); } }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/NFTAction/deployNFT.md ================================================ # Deploy NFT Definition: Only nft minter can deploy NFT *** ## Step 1. get Account ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. get fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_DEPLOY, amount: "0", }, apiKey ); console.log(fee); ``` *** ## Step 5. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, // same as Step 7. transfer->token->tokenId }, apiKey ); ``` *** ## Step 6. broker ```ts const {broker} = await LoopringAPI.exchangeAPI.getAvailableBroker(); ``` *** ## Step 7. Build transfer & Deploy ```ts const transfer = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: LOOPRING_EXPORTED_ACCOUNT.address, payerId: LOOPRING_EXPORTED_ACCOUNT.accountId, payeeAddr: broker, // payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }; const response = await LoopringAPI.userAPI.submitDeployNFT({ request: { transfer, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log(response); ``` ================================================ FILE: src/tests/demo/NFTAction/deployNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; describe("deployNFT", function () { it( "submitDeployNFT", async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. fee const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_DEPLOY, amount: "0", }, apiKey ); console.log(fee); // Step 5. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, // same as Step 7. transfer->token->tokenId }, apiKey ); // Step 6. broker const { broker } = await LoopringAPI.exchangeAPI.getAvailableBroker({ type: 0, }); // Step 7. Build transfer & Deploy const transfer = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: LOOPRING_EXPORTED_ACCOUNT.address, payerId: LOOPRING_EXPORTED_ACCOUNT.accountId, payeeAddr: broker, // payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }; const response = await LoopringAPI.userAPI.submitDeployNFT({ request: { transfer, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log(response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/NFTAction/metaNFT.md ================================================ # NFT META METHODS *** ## getContractNFTMeta ```ts const result = await LoopringAPI.nftAPI.getContractNFTMeta({ web3, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); console.log(result); ``` *** ## getInfoForNFTTokens ```ts const response = await LoopringAPI.nftAPI.getInfoForNFTTokens({ nftDatas: [LOOPRING_EXPORTED_ACCOUNT.nftData], }); console.log(`getInfoForNFTTokens: response: `, JSON.stringify(response)); ``` *** ## computeNFTAddress ```ts const response = LoopringAPI.nftAPI.computeNFTAddress({ nftOwner: "0xE20cF871f1646d8651ee9dC95AAB1d93160b3467", nftFactory: "0x40F2C1770E11c5bbA3A26aEeF89616D209705C5D", }); console.log( `computeNFTAddress:`, response, "0xee354d81778a4c5a08fd9dbeb5cfd01a840a746d" ); ``` *** ## ipfsCid0ToNftID ```ts const ipfs = "QmNuqdeWUJ9iEiw5qZfJ2pJ9onqAS45ZffvV8JQSUzp7DQ"; const nftID = "0x0880847b7587968f32ba6c741f9d797d9dc64971979922a80c4e590453b8dc2f"; console.log( `ipfsCid0ToNftID: ipfs: `, ipfs, LoopringAPI.nftAPI.ipfsCid0ToNftID(ipfs) ); ``` *** ## ipfsNftIDToCid ```ts const ipfs = "QmNuqdeWUJ9iEiw5qZfJ2pJ9onqAS45ZffvV8JQSUzp7DQ"; const nftID = "0x0880847b7587968f32ba6c741f9d797d9dc64971979922a80c4e590453b8dc2f"; console.log( `ipfsCid0ToNftID: nftID: `, nftID, LoopringAPI.nftAPI.ipfsNftIDToCid(nftID) ); ``` ================================================ FILE: src/tests/demo/NFTAction/metaNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, } from "../../MockData"; import * as sdk from "../../../index"; describe("metaNFT", function () { it( "getContractNFTMeta", async () => { const result = await LoopringAPI.nftAPI.getContractNFTMeta({ web3, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); console.log(result); }, DEFAULT_TIMEOUT ); it( "getInfoForNFTTokens", async () => { const response = await LoopringAPI.nftAPI.getInfoForNFTTokens({ nftDatas: [LOOPRING_EXPORTED_ACCOUNT.nftData], }); console.log(`getInfoForNFTTokens: response: `, JSON.stringify(response)); }, DEFAULT_TIMEOUT ); it("computeNFTAddress", async () => { const response = LoopringAPI.nftAPI.computeNFTAddress({ nftOwner: "0xE20cF871f1646d8651ee9dC95AAB1d93160b3467", nftFactory: "0x40F2C1770E11c5bbA3A26aEeF89616D209705C5D", }); console.log( `computeNFTAddress:`, response, "0xee354d81778a4c5a08fd9dbeb5cfd01a840a746d" ); }); it("ipfsCid0ToNftID", () => { const ipfs = "QmNuqdeWUJ9iEiw5qZfJ2pJ9onqAS45ZffvV8JQSUzp7DQ"; const nftID = "0x0880847b7587968f32ba6c741f9d797d9dc64971979922a80c4e590453b8dc2f"; console.log( `ipfsCid0ToNftID: ipfs: `, ipfs, LoopringAPI.nftAPI.ipfsCid0ToNftID(ipfs) ); expect(LoopringAPI.nftAPI.ipfsCid0ToNftID(ipfs)).toBe(nftID); }); it("ipfsNftIDToCid", () => { const ipfs = "QmNuqdeWUJ9iEiw5qZfJ2pJ9onqAS45ZffvV8JQSUzp7DQ"; const nftID = "0x0880847b7587968f32ba6c741f9d797d9dc64971979922a80c4e590453b8dc2f"; console.log( `ipfsCid0ToNftID: nftID: `, nftID, LoopringAPI.nftAPI.ipfsNftIDToCid(nftID) ); expect(LoopringAPI.nftAPI.ipfsNftIDToCid(nftID)).toBe(ipfs); }); }); ================================================ FILE: src/tests/demo/NFTAction/mintNFT.md ================================================ # Mint NFT Definition: Mint Layer2 NFT, Loopring follow the ipfs NFT format, IPFS CID will convert to nftId, please view MetaNFT.md >

!!! important describe

>

Follow method is the simple way for mint NTF, but this kind of NFT will using the same contact & with no Contract metadata forever on L1

>

New Version of NFT will has it isolate Contract/collection with metadata information

>

From Step 3. nftTokenAddress please follow create `collectionNFT` step create collection(contract), the api will return follow info for mint NFT

> ```ts tokenAddress: collectionMeta.contractAddress counterFactualNftInfo: { nftOwner: ccInfo.owner, nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[chainId], nftBaseUri: collectionMeta?.baseUri ?? "", }, ``` ## Step 1. get Account ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey({ accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, // same as maxFee tokenId }, apiKey ); ``` *** ## Step 5. get collection Information(tokenAddress) ```ts const collectionRes = await LoopringAPI.userAPI .getUserOwenCollection({ owner: accInfo.owner, tokenAddress: mockData.nftTokenAddress, isMintable: true }, apiKey ) if ((collectionRes && ((collectionRes as sdk.RESULT_INFO).code || (collectionRes as sdk.RESULT_INFO).message)) || !collectionRes.collections.length ) { console.log("Collection is disable to mint "); throw "Collection is disable to mint "; } const collectionMeta = (collectionRes as any).collections[ 0 ] as CollectionMeta; const counterFactualNftInfo: NFTCounterFactualInfo = { nftOwner: accInfo.owner, nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[ sdk.ChainId.GOERLI ], nftBaseUri: collectionMeta.baseUri, }; ``` *** ## Step 6. fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: collectionMeta.contractAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey ); ``` *** ## Step7. Mint ```ts const response = await LoopringAPI.userAPI.submitNFTMint({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: nftTokenAddress, // please read the description -> tokenAddress: collectionMeta.contractAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: "1", validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, amount: fee.fees[ "LRC" ].fee ?? "9400000000000000000", }, counterFactualNftInfo, royaltyPercentage: 5, forceToMint: true, // suggest use as false, for here is just for run test // please read the description // counterFactualNftInfo: { // nftOwner: ccInfo.owner, // nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[chainId], // nftBaseUri: collectionMeta?.baseUri ?? "", // }, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ____ ### ps: Mint with legacy nftFactory ! Mint NFT from this way has no collection information at deploy contract(tokenAdress is unique) *** #### Step 1,2,3,4 is same logic #### Step 5. get tokenAddress ```ts const counterFactualNftInfo = { nftOwner: accInfo.owner, nftFactory: sdk.NFTFactory[ sdk.ChainId.GOERLI ], nftBaseUri: "", }; const nftTokenAddress = LoopringAPI.nftAPI.computeNFTAddress(counterFactualNftInfo) .tokenAddress || ""; console.log("nftTokenAddress", nftTokenAddress); ``` *** #### Step 6. get fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: nftTokenAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey ); ``` #### Step 7. Mint ```ts const response = await LoopringAPI.userAPI.submitNFTMint({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: "1", validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap[ "LRC" ].tokenId, amount: fee.fees[ "LRC" ].fee ?? "9400000000000000000", }, royaltyPercentage: 5, counterFactualNftInfo, forceToMint: true, // suggest use as false, for here is just for run test }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ================================================ FILE: src/tests/demo/NFTAction/mintNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from '../../MockData' import * as sdk from '../../../index' import { CollectionMeta, NFTCounterFactualInfo } from '../../../defs' const mockData = { nftTokenAddress: '0xfc26d5b9277f4375ba8ff7d4708f4dbf78954124', } /** * !!!important describe * Follow mehod is the simple way for mint NTF, but this kind of NFT will using the same contact & with no Contract metadata forever on L1, * New Version of NFT will has it isolate Contract/colletion with metadata inforamtion * From Step 3. nftTokenAddress please follow create `collectionNFT` step create collection(contract), the api will return follow info for mint NFT * tokenAddress: collectionMeta.contractAddress, * counterFactualNftInfo: { * nftOwner: ccInfo.owner, * nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[chainId], * nftBaseUri: collectionMeta?.baseUri ?? "", * }, **/ describe('mintNFT', function () { it( 'submitNFTMint', async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) console.log('accInfo:', accInfo) // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo) console.log('eddsaKey:', eddsaKey.sk) // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk, ) console.log('apiKey:', apiKey) // Step 3. nftTokenAddress const counterFactualNftInfo = { nftOwner: accInfo.owner, nftFactory: LOOPRING_EXPORTED_ACCOUNT.uatNFTFactory, nftBaseUri: '', } const nftTokenAddress = LoopringAPI.nftAPI.computeNFTAddress(counterFactualNftInfo).tokenAddress || '' console.log('nftTokenAddress', nftTokenAddress) // Step 4. fee const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: nftTokenAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey, ) // Step 5. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap['LRC'].tokenId, }, apiKey, ) // Step 7. Mint const response = await LoopringAPI.userAPI.submitNFTMint( { request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: nftTokenAddress, // please read the description -> tokenAddress: collectionMeta.contractAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: '1', validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap['LRC'].tokenId, amount: fee.fees['LRC'].fee ?? '9400000000000000000', }, royaltyPercentage: 5, forceToMint: true, // suggest use as false, for here is just for run test // please read the description // counterFactualNftInfo: { // nftOwner: ccInfo.owner, // nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[chainId], // nftBaseUri: collectionMeta?.baseUri ?? "", // }, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }, { _noEcdsa: true }, ) console.log(response) }, DEFAULT_TIMEOUT * 3, ) //Suggest use this function it( 'submitNFTMintWithCollection', async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) console.log('accInfo:', accInfo) // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo) console.log('eddsaKey:', eddsaKey.sk) // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk, ) console.log('apiKey:', apiKey) // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap['LRC'].tokenId, // same as maxFee tokenId }, apiKey, ) // Step 5. get collection Information(tokenAddress) const collectionRes = await LoopringAPI.userAPI.getUserOwenCollection( { owner: accInfo.owner, tokenAddress: mockData.nftTokenAddress, isMintable: true, }, apiKey, ) if ( (collectionRes && ((collectionRes as sdk.RESULT_INFO).code || (collectionRes as sdk.RESULT_INFO).message)) || !collectionRes.collections.length ) { console.log('Collection is disable to mint ') throw 'Collection is disable to mint ' } const collectionMeta = (collectionRes as any).collections[0] as CollectionMeta const counterFactualNftInfo: NFTCounterFactualInfo = { nftOwner: accInfo.owner, nftFactory: collectionMeta.nftFactory ?? sdk.NFTFactory_Collection[sdk.ChainId.GOERLI], nftBaseUri: collectionMeta.baseUri, } // Step 6. fee const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: collectionMeta.contractAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, }, apiKey, ) // Step 7. Mint const response = await LoopringAPI.userAPI.submitNFTMint({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: accInfo.accountId, minterAddress: accInfo.owner, toAccountId: accInfo.accountId, toAddress: accInfo.owner, nftType: 0, tokenAddress: collectionMeta.contractAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, //nftId.toString(16), amount: '1', validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: storageId.offchainId ?? 9, maxFee: { tokenId: TOKEN_INFO.tokenMap['LRC'].tokenId, amount: fee.fees['LRC'].fee ?? '9400000000000000000', }, counterFactualNftInfo, royaltyPercentage: 5, forceToMint: true, // suggest use as false, for here is just for run test }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }) console.log(response) }, DEFAULT_TIMEOUT * 3, ) }) ================================================ FILE: src/tests/demo/NFTAction/tradeNFT.md ================================================ # Trade NFT Definition: This method is help for understand how to match a maker with a taker order *** ## tradeNFT > Private or third account can signature and approve this order [mock order](#MockOrder) ```ts // Step 1. getAccount const accInfoC = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) ).accInfo; // Step 1. eddsaKeyC const eddsaKeyC = await signatureKeyPairMock(accInfoC); // Step 3. apiKey const apiKeyC = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfoC.accountId, }, eddsaKeyC.sk ) ).apiKey; // NFT Trade const response = await LoopringAPI.userAPI.submitNFTTrade({ request: { maker: { ...mockData.makerOrder, eddsaSignature: mockData.makerOrderEddsaSignature, }, makerFeeBips: 1000, taker: { ...mockData.takerOrder, eddsaSignature: mockData.takerOrderEddsaSignature, }, takerFeeBips: 100, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, apiKey: apiKeyC, eddsaKey: eddsaKeyC.sk, }); console.log(response); ``` *** ## MockOrder > Validate NFT Order please reader @Validate NFT Order ```ts // Step 1. getAccount const accInfo = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) ).accInfo; const accInfo2 = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address2, }) ).accInfo; // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); const eddsaKey2 = await signatureKeyPairMock(accInfo2, web3_2); // Step 3. apiKey const apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; const apiKey2 = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo2.accountId, }, eddsaKey2.sk ) ).apiKey; // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); const storageId2 = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo2.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey2 ); // Step 5. generate Order, please read validateNFTOrder const makerOrder: sdk.NFTOrderRequestV3 = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, buyToken: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, allOrNone: false, fillAmountBOrS: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 1000, }; const makerOrderEddsaSignature = sdk.get_EddsaSig_NFT_Order( makerOrder, eddsaKey.sk ); const takerOrder: sdk.NFTOrderRequestV3 = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo2.accountId, storageId: storageId2.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, buyToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, allOrNone: false, fillAmountBOrS: true, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 100, }; const takerOrderEddsaSignature = sdk.get_EddsaSig_NFT_Order( takerOrder, eddsaKey2.sk ); mockData = { takerOrder, takerOrderEddsaSignature, makerOrder, makerOrderEddsaSignature, makerFeeBips: 1000, maxFeeBips: 100, }; ``` ================================================ FILE: src/tests/demo/NFTAction/tradeNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, signatureKeyPairMock, web3_2, TOKEN_INFO, } from '../../MockData' import * as sdk from '../../../index' import { myLog } from '../../../utils/log_tools' let mockData: any = { takerOrder: undefined, takerOrderEddsaSignature: undefined, makerOrder: undefined, makerOrderEddsaSignature: undefined, makerFeeBips: 1000, maxFeeBips: 100, } let accInfoC: any = undefined let eddsaKeyC: any = undefined describe('tradeNFT group', function () { beforeEach(async () => { // console.log('tradeNFT before all', LoopringAPI.exchangeAPI) // Step 1. getAccount const accInfo = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) ).accInfo const accInfo2 = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address2, }) ).accInfo // console.log(accInfo, accInfo2) // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo) const eddsaKey2 = await signatureKeyPairMock(accInfo2, web3_2) accInfoC = accInfo eddsaKeyC = eddsaKey // Step 3. apiKey const apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk, ) ).apiKey const apiKey2 = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo2.accountId, }, eddsaKey2.sk, ) ).apiKey // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey, ) const storageId2 = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo2.accountId, sellTokenId: TOKEN_INFO.tokenMap['LRC'].tokenId, }, apiKey2, ) // Step 5. generate Order, please read validateNFTOrder const makerOrder: sdk.NFTOrderRequestV3 = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: '1', }, buyToken: { tokenId: TOKEN_INFO.tokenMap['LRC'].tokenId, amount: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, allOrNone: false, fillAmountBOrS: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 1000, } const makerOrderEddsaSignature = sdk.get_EddsaSig_NFT_Order(makerOrder, eddsaKey.sk).result const takerOrder: sdk.NFTOrderRequestV3 = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo2.accountId, storageId: storageId2.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap['LRC'].tokenId, amount: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, buyToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: '1', }, allOrNone: false, fillAmountBOrS: true, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 100, } const takerOrderEddsaSignature = sdk.get_EddsaSig_NFT_Order(takerOrder, eddsaKey2.sk).result mockData = { takerOrder, takerOrderEddsaSignature, makerOrder, makerOrderEddsaSignature, makerFeeBips: 1000, maxFeeBips: 100, } }, DEFAULT_TIMEOUT * 3) it( 'tradeNFT', async () => { jest.useFakeTimers('legacy') // private or third account can signature and approve this order try { myLog( mockData, 'mockData.makerOrder', mockData.makerOrder, 'mockData.takerOrder', mockData.takerOrder, ) // Step 1. getAccount // const accInfoC = ( // await LoopringAPI.exchangeAPI.getAccount({ // owner: LOOPRING_EXPORTED_ACCOUNT.address, // }) // ).accInfo; // Step 2. eddsaKeyC // const eddsaKeyC = await signatureKeyPairMock(accInfoC, web3); myLog('accInfoC.accountId', accInfoC.accountId, 'eddsaKeyC', eddsaKeyC) // Step 3. apiKey const apiKeyC = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfoC.accountId, }, eddsaKeyC.sk, ) ).apiKey myLog( 'mockData.makerOrder', mockData.makerOrder, 'mockData.takerOrder', mockData.takerOrder, ) // NFT Trade const response = await LoopringAPI.userAPI.submitNFTTrade({ request: { maker: { ...mockData.makerOrder, eddsaSignature: mockData.makerOrderEddsaSignature, }, makerFeeBips: 1000, taker: { ...mockData.takerOrder, eddsaSignature: mockData.takerOrderEddsaSignature, }, takerFeeBips: 100, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, apiKey: apiKeyC, eddsaKey: eddsaKeyC.sk, }) console.log(response) } catch (e) { console.log(e) } }, DEFAULT_TIMEOUT * 4, ) }) ================================================ FILE: src/tests/demo/NFTAction/validateNFTOrder.md ================================================ # Validate NFT Order Definition: Loopring L2 support a method help for Validate NFT one side Order, validate NFT Order is not required for Loopring, but when make NFT Trade, it should pass this validation ## SellNFTByERC20 ```ts // Step 1. getAccount const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); // let hash: any = new BN(nftId,'hex') // hash = toHex(hash);//new BigInteger(sha256(nftId.toString()).toString(), 16) // Step 5. submitNFTValidateOrder const response = await LoopringAPI.userAPI.submitNFTValidateOrder({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, buyToken: { tokenId: 1, amount: "10000000000000", }, allOrNone: false, fillAmountBOrS: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 1000, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log("sellNFT NFTOrderRequestV3:", response); ``` *** ## BuyNFTByERC20 ```ts // Step 1. getAccount const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 5. submitNFTValidateOrder const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); const response = await LoopringAPI.userAPI.submitNFTValidateOrder({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: 1, amount: "10000000000000", }, buyToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, fillAmountBOrS: true, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 100, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); ``` ================================================ FILE: src/tests/demo/NFTAction/validateNFTOrder.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; describe("validateNFTOrder", function () { it( "sellNFTByERC20", async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); // let hash: any = new BN(nftId,'hex') // hash = toHex(hash);//new BigInteger(sha256(nftId.toString()).toString(), 16) // Step 5. submitNFTValidateOrder const response = await LoopringAPI.userAPI.submitNFTValidateOrder({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, buyToken: { tokenId: 1, amount: "10000000000000", }, allOrNone: false, fillAmountBOrS: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 1000, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log("sellNFT NFTOrderRequestV3:", response); }, DEFAULT_TIMEOUT ); it( "buyNFTByERC20", async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 5. submitNFTValidateOrder const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); const response = await LoopringAPI.userAPI.submitNFTValidateOrder({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: accInfo.accountId, storageId: storageId.orderId, sellToken: { tokenId: 1, amount: "10000000000000", }, buyToken: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, fillAmountBOrS: true, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 100, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log("buyNFT NFTOrderRequestV3:", response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/account/account.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, signatureKeyPairMock, TOKEN_INFO, } from "../../MockData"; import * as sdk from "../../../index"; /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ describe("Account", function () { it( "getAccount", async () => { const response = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log(response); }, DEFAULT_TIMEOUT ); it("getWalletType", async () => { const [ { walletType: CFWalletType }, { walletType: EOAWalletType }, { walletType: ContractWalletType }, ] = await Promise.all([ LoopringAPI.walletAPI.getWalletType({ wallet: LOOPRING_EXPORTED_ACCOUNT.addressCF, }), LoopringAPI.walletAPI.getWalletType({ wallet: LOOPRING_EXPORTED_ACCOUNT.address, }), LoopringAPI.walletAPI.getWalletType({ wallet: LOOPRING_EXPORTED_ACCOUNT.addressContractWallet, }), ]); console.log( "CFWalletType, EOAWalletType, ContractWalletType", CFWalletType, EOAWalletType, ContractWalletType ); }); it( "getAllowances", async () => { const { tokenAllowances } = await LoopringAPI.exchangeAPI.getAllowances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, token: [ TOKEN_INFO.tokenMap.LRC.address, TOKEN_INFO.tokenMap.DAI.address, ], }); console.log( "getAllowances:", tokenAllowances.get(TOKEN_INFO.tokenMap.LRC.address), tokenAllowances.get(TOKEN_INFO.tokenMap.DAI.address) ); }, DEFAULT_TIMEOUT ); it( "getUserApiKey", async () => { const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const eddsaKey = await signatureKeyPairMock(accInfo); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); }, DEFAULT_TIMEOUT ); it( "getEthNonce", async () => { const response = await LoopringAPI.exchangeAPI.getEthNonce({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("getEthNonce:", response); }, DEFAULT_TIMEOUT ); it( "Layer1_ETH_Balance", async () => { const { ethBalance } = await LoopringAPI.exchangeAPI.getEthBalances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log(`Layer1 ethBalance: ${ethBalance}`); }, DEFAULT_TIMEOUT ); it( "Layer1_ERC20_Balance", async () => { const { tokenBalances } = await LoopringAPI.exchangeAPI.getTokenBalances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, token: [TOKEN_INFO.tokenMap.LRC.address], }); console.log( `Layer1 ERC20 Balance LRC: ${tokenBalances.get( TOKEN_INFO.tokenMap.LRC.address as unknown as sdk.TokenAddress )}` ); }, DEFAULT_TIMEOUT ); it("Layer1_getNFTBalance", async () => { const response = await LoopringAPI.nftAPI.getNFTBalance({ web3, account: LOOPRING_EXPORTED_ACCOUNT.address, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); console.log(response); }); it( "Layer2_getUserBalances", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. getUserBalances const { userBalances } = await LoopringAPI.userAPI.getUserBalances( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, tokens: "" }, apiKey ); console.log(`Layer2 ERC20 Balance: ${userBalances}`); }, DEFAULT_TIMEOUT ); it( "Layer2_getUserNFTBalances", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. getUserNFTBalances const { userNFTBalances } = await LoopringAPI.userAPI.getUserNFTBalances( { accountId: accInfo.accountId, limit: 20 }, apiKey ); console.log(`Layer2 NFT Balance: ${userNFTBalances}`); }, DEFAULT_TIMEOUT ); it( "getCounterFactualInfo", async () => { const response = await LoopringAPI.exchangeAPI.getCounterFactualInfo({ accountId: LOOPRING_EXPORTED_ACCOUNT.accountIdCF, }); console.log("getCounterFactualInfo", response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/account/activeAccount.md ================================================ # Active Account Definition: After user Deposit or (Third-Part Transfer), how to active Loopring L2 account. *** ## Step 1. get account info ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); ``` *** ## Step 2. use keySeed or CUSTOMER_KEY_SEED generateKeyPair ```ts const keySeed = sdk.BaseAPI.KEY_MESSAGE.replace( "${exchangeAddress}", LOOPRING_EXPORTED_ACCOUNT.exchangeAddress ).replace("${nonce}", accInfo.nonce.toString()); const eddsaKey = await sdk.generateKeyPair({ web3, address: accInfo.owner, keySeed, walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); console.log("eddsakey:", eddsaKey.sk); ``` Or ```ts // CUSTOMER_KEY_SEED = "XXXXXX" + " with key nonce: " + "${nonce}"; const keySeed = CUSTOMER_KEY_SEED.replace( "${nonce}", accInfo.nonce.toString() const eddsaKey = await sdk.generateKeyPair({ web3, address: accInfo.owner, keySeed, walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); console.log("eddsakey:", eddsaKey.sk); ``` *** ## Step 3. get fee ```ts const fee = await LoopringAPI.globalAPI.getActiveFeeInfo({ accountId: accInfo.accountId, }); console.log("fee:", fee); ``` *** ## Step 4. updateAccount (active or rest) ```ts const result = await LoopringAPI.userAPI.updateAccount({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, owner: accInfo.owner, accountId: accInfo.accountId, publicKey: {x: eddsaKey.formatedPx, y: eddsaKey.formatedPy}, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, keySeed, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, nonce: accInfo.nonce as number, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, isHWAddr: false, }); const {accInfo: updateAccountInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log( "updateAccount Result: ", result, "updateAccountInfo:", updateAccountInfo ); ``` ================================================ FILE: src/tests/demo/account/activeAccount.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, TOKEN_INFO, web3, CUSTOMER_KEY_SEED, } from "../../MockData"; import * as sdk from "../../../index"; /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddressess * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ describe("ActiveAccount", function () { it( "updateAccount", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); // Step 2. use keySeed generateKeyPair const keySeed = sdk.BaseAPI.KEY_MESSAGE.replace( "${exchangeAddress}", LOOPRING_EXPORTED_ACCOUNT.exchangeAddress ).replace("${nonce}", accInfo.nonce.toString()); const eddsaKey = await sdk.generateKeyPair({ web3, address: accInfo.owner, keySeed, walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); console.log("eddsakey:", eddsaKey.sk); // Step 3. fee const fee = await LoopringAPI.globalAPI.getActiveFeeInfo({ accountId: accInfo.accountId, }); console.log("fee:", fee); // Step 4. updateAccount (active or rest) const result = await LoopringAPI.userAPI.updateAccount({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, owner: accInfo.owner, accountId: accInfo.accountId, publicKey: { x: eddsaKey.formatedPx, y: eddsaKey.formatedPy }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, keySeed, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, nonce: accInfo.nonce as number, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, isHWAddr: false, }); const { accInfo: updateAccountInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log( "updateAccount Result: ", result, "updateAccountInfo:", updateAccountInfo ); }, DEFAULT_TIMEOUT * 2 ); it( "customer_keySeed", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); // Step 2. use keySeed generateKeyPair const keySeed = CUSTOMER_KEY_SEED.replace( "${nonce}", accInfo.nonce.toString() ); const eddsaKey = await sdk.generateKeyPair({ web3, address: accInfo.owner, keySeed, walletType: sdk.ConnectorNames.MetaMask, chainId: sdk.ChainId.GOERLI, }); console.log("eddsakey:", eddsaKey.sk); // Step 3. fee const fee = await LoopringAPI.globalAPI.getActiveFeeInfo({ accountId: accInfo.accountId, }); console.log("fee:", fee); // Step 4. updateAccount (active or rest) const result = await LoopringAPI.userAPI.updateAccount({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, owner: accInfo.owner, accountId: accInfo.accountId, publicKey: { x: eddsaKey.formatedPx, y: eddsaKey.formatedPy }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, keySeed, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, nonce: accInfo.nonce as number, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, isHWAddr: false, }); const { accInfo: updateAccountInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log( "updateAccount Result: ", result, "updateAccountInfo:", updateAccountInfo, "keySeed:", updateAccountInfo.keySeed ); }, DEFAULT_TIMEOUT * 2 ); }); ================================================ FILE: src/tests/demo/account/fee.md ================================================ # Fee Loopring have 2 get Fee api: - getOffchainFeeAmt: - `ORDER`, - `FFCHAIN_WITHDRAWAL`, - `UPDATE_ACCOUNT`, - `TRANSFER`, - `FAST_OFFCHAIN_WITHDRAWAL`, - `OPEN_ACCOUNT`, - `AMM_EXIT`, - `DEPOSIT`, - `AMM_JOIN`, - getNFTOffchainFeeAmt: - `NFT_MINT`, - `NFT_WITHDRAWAL`, - `NFT_TRANSFER`, - `NFT_DEPLOY`, *** ## Fee: updateAccount ```ts // Step 1. get account info console.log(LoopringAPI.exchangeAPI.getAccount); const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.UPDATE_ACCOUNT, }, apiKey ); console.log("updateAccount:", response); ``` *** ## Fee: transfer ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.TRANSFER, }, apiKey ); console.log("transfer:", response); ``` *** ## Fee: withdraw ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, }, apiKey ); console.log("withdraw:", response); ``` *** ## Fee: fastWithdraw ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, }, apiKey ); console.log("fastWithdraw:", response); ``` *** ## Fee: order ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.ORDER, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, }, apiKey ); console.log("order:", response); ``` *** ## Fee: amm_exit ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.AMM_EXIT, }, apiKey ); console.log("amm_exit:", response); ``` *** ## Fee: amm_join ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.AMM_JOIN, }, apiKey ); console.log("amm_join:", response); ``` *** ## Fee: NFT Transfer ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, }, apiKey ); console.log("NFTTransfer:", response); ``` *** ## Fee: NFT Withdrawal ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return {accInfo, eddsaKey, apiKey}; const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, requestType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, }, apiKey ); console.log("NFTWithdrawal:", response); ``` ## Fee: NFT Mint ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress }, apiKey ); console.log("NFTWithdrawal:", response); ``` ## Fee: NFT Deploy ```ts // Step 1. get account info const {accInfo} = await LoopringAPI.exchangeAPI.getAccount( {owner: LOOPRING_EXPORTED_ACCOUNT.address,}); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_DEPLOY, }, apiKey ); console.log("NFTWithdrawal:", response); ``` *** ## getActiveFeeInfo without apikey & accountId ```ts const response = await LoopringAPI.globalAPI.getActiveFeeInfo({}); ``` *** ## getActiveFeeInfo without apikey with accountId ```ts const response = await LoopringAPI.globalAPI.getActiveFeeInfo({accountId: LOOPRING_EXPORTED_ACCOUNT.accountId}); ``` ================================================ FILE: src/tests/demo/account/fee.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, signatureKeyPairMock, TOKEN_INFO, LoopringAPI, } from "../../MockData"; import * as sdk from "../../../index"; /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ describe("Fee", function () { it( "fee:updateAccount", async () => { // Step 1. get account info console.log(LoopringAPI.exchangeAPI.getAccount); const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.UPDATE_ACCOUNT, }, apiKey ); console.log("updateAccount:", response); }, DEFAULT_TIMEOUT ); it( "fee:transfer", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.TRANSFER, }, apiKey ); console.log("transfer:", response); }, DEFAULT_TIMEOUT ); it( "fee:withdraw", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, }, apiKey ); console.log("withdraw:", response); }, DEFAULT_TIMEOUT ); it( "fee:fastWithdraw", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, }, apiKey ); console.log("fastWithdraw:", response); }, DEFAULT_TIMEOUT ); it( "fee:order", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.ORDER, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, }, apiKey ); console.log("order:", response); }, DEFAULT_TIMEOUT ); it( "fee:amm_exit", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.AMM_EXIT, }, apiKey ); console.log("amm_exit:", response); }, DEFAULT_TIMEOUT ); it( "fee:amm_join", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.AMM_JOIN, }, apiKey ); console.log("amm_join:", response); }, DEFAULT_TIMEOUT ); it( "fee:NFTTransfer", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); return { accInfo, eddsaKey, apiKey }; const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, }, apiKey ); console.log("NFTTransfer:", response); }, DEFAULT_TIMEOUT ); it( "fee:NFTWithdrawal", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, }, apiKey ); console.log("NFTWithdrawal:", response); }, DEFAULT_TIMEOUT ); it( "fee:NFTMint", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_MINT, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, }, apiKey ); console.log("NFTWithdrawal:", response); }, DEFAULT_TIMEOUT ); it( "fee:NFTDeploy", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_DEPLOY, }, apiKey ); console.log("NFTWithdrawal:", response); }, DEFAULT_TIMEOUT ); it( "getActiveFeeInfo without apikey & accountId", async () => { const response = await LoopringAPI.globalAPI.getActiveFeeInfo({}); console.log("updateAccount without apikey & accountId:", response); }, DEFAULT_TIMEOUT ); it( "getActiveFeeInfo without apikey with accountId", async () => { const response = await LoopringAPI.globalAPI.getActiveFeeInfo({ accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, }); console.log("updateAccount without apikey with accountId:", response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/account/historyRecord.md ================================================ # User Actions History For check account Actions, more detail such as filters please read SDK interface or API . *** ## getUserTrades ```ts async () => { const result = await LoopringAPI.userAPI.getUserTrades( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, offset: 0, limit: 20, fillTypes: sdk.TradesFillTypes.dex, }, apiKey ); console.log("getUserTrades:", result); ``` *** ## getUserTxs ```ts async () => { const result = await LoopringAPI.userAPI.getUserTxs( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, types: [ sdk.UserTxTypes.DEPOSIT, sdk.UserTxTypes.TRANSFER, sdk.UserTxTypes.ONCHAIN_WITHDRAWAL, ], }, apiKey ); console.log("getUserTxs:", result); ``` *** ## getUserNFTTransactionHistory ```ts async () => { const result = await LoopringAPI.userAPI.getUserNFTTransactionHistory( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, types: [ sdk.UserNFTTxTypes.DEPOSIT, sdk.UserNFTTxTypes.TRANSFER, sdk.UserNFTTxTypes.WITHDRAW, sdk.UserNFTTxTypes.MINT, ], }, apiKey ); console.log("getUserNFTTransactionHistory:", result); ``` *** ## getOrders ```ts async () => { const result = await LoopringAPI.userAPI.getOrders( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, orderTypes: sdk.OrderType.LimitOrder, }, apiKey ); console.log("getOrders:", result); ``` ================================================ FILE: src/tests/demo/account/historyRecord.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; let apiKey = ""; describe("historyRecord", function () { beforeEach(async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; console.log("apiKey:", apiKey); }, DEFAULT_TIMEOUT); it( "getUserTrades", async () => { const result = await LoopringAPI.userAPI.getUserTrades( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, offset: 0, limit: 20, fillTypes: sdk.TradesFillTypes.dex, }, apiKey ); console.log("getUserTrades:", result); }, DEFAULT_TIMEOUT ); it( "getUserTxs", async () => { const result = await LoopringAPI.userAPI.getUserTxs( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, types: [ sdk.UserTxTypes.DEPOSIT, sdk.UserTxTypes.TRANSFER, sdk.UserTxTypes.OFFCHAIN_WITHDRAWAL, ], }, apiKey ); console.log("getUserTxs:", result); }, DEFAULT_TIMEOUT ); it( "getUserNFTTransactionHistory", async () => { const result = await LoopringAPI.userAPI.getUserNFTTransactionHistory( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, types: [ sdk.UserNFTTxTypes.DEPOSIT, sdk.UserNFTTxTypes.TRANSFER, sdk.UserNFTTxTypes.WITHDRAW, sdk.UserNFTTxTypes.MINT, ], }, apiKey ); console.log("getUserNFTTransactionHistory:", result); }, DEFAULT_TIMEOUT ); it( "getOrders", async () => { const result = await LoopringAPI.userAPI.getOrders( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, orderTypes: sdk.OrderType.LimitOrder, }, apiKey ); console.log("getOrders:", result); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/account/signature.md ================================================ # Signature Loopring SDK support EOA (EOA hardware wallet) & Loopring Smart wallet Signature - For Browser extension, Dapp, Hardware wallet we only support for EOA - For Loopring Smart wallet (App), Provider gateway only can be walletConnect ## Follow is the provider gateway we inject & test: - MetaMask (Ledger, Trezor) - WalletConnect (Authereum, Loopring Smart wallet ) - Coinbase - [Coming soon...](https://desk.zoho.com/portal/loopring/en/newticket) ## For signature: For eth_sign signing types (eth_sign, personal_sign, v1, v3, v4) ### EOA: - For Browser extension ([More information: signing-data](https://docs.metamask.io/guide/signing-data.html#a-brief-history)) + common EOA we use the `v4` signature and `web3.eth.personal.ecRecover` validate signature + when `v4` signature is failed for any step, we will try `personal_sign` and `web3.eth.personal.ecRecover` validate signature - For Dapp + when loopring Dex is inside Dapp Webview & connect by `window.ethereum`, we remove the `web3.eth.personal.ecRecover` validate ### Loopring Smart wallet: - For Smart wallet we send `eth_signTypedData` by walletConnect & validate ABI.Contracts.ContractWallet.encodeInputs `isValidSignature(bytes32,bytes)` ### Loopring Counterfactual wallet: - signature is same as Smart wallet - But ecRecover is by walletOwner, `const {walletOwner} = await LoopringAPI.exchangeAPI.getCounterFactualInfo({ accountId: LOOPRING_EXPORTED_ACCOUNT.accountIdCF, });` *** ## generateKeyPair ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const result = await signatureKeyPairMock(accInfo); console.log(result.sk); ``` *** ## getEcDSASig: eth_signTypedData_v4 ```ts // test case is not allow brock by Mock provider const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.HasDataStruct, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, "", sdk.ConnectorNames.Unknown ); console.log("getEcDSASig:eth_signTypedData_v4", result, "ecdsaSig+sdk.SigSuffix.Suffix02", result.ecdsaSig + sdk.SigSuffix.Suffix02 ); ``` *** ## getEcDSASig: personalSign(WithoutDataStruct--Hardware wallet) ```ts const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.WithoutDataStruct, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, "", sdk.ConnectorNames.Unknown ); console.log( "getEcDSASig:WithoutDataStruct(personalSign)", result, "ecdsaSig+sdk.SigSuffix.Suffix03", result.ecdsaSig + sdk.SigSuffix.Suffix03 ); ``` *** ## getEcDSASig: personalSign(Contract) ```ts // test case is not allow brock by Mock provider const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.Contract, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, "", sdk.ConnectorNames.Unknown ); console.log( "getEcDSASig:personalSign(Contract)", result ); ``` ## Validate signature [github: src/api/base_api.ts#personalSign](https://github.com/Loopring/loopring_sdk/blob/2c79c1837114f4f383e2d292de3da4b2dac02252/src/api/base_api.ts#L549) ``` export async function personalSign( web3: any, account: string | undefined, pwd: string, msg: string, walletType: ConnectorNames, chainId: ChainId, accountId?: number, counterFactualInfo?: CounterFactualInfo, isMobile?: boolean ) { if (!account) { return { error: "personalSign got no account" }; } return new Promise((resolve) => { try { web3.eth.personal.sign( msg, account, pwd, async function (err: any, result: any) { if (!err) { // Valid:1. counter Factual signature Valid if (counterFactualInfo && accountId) { myLog("fcWalletValid counterFactualInfo accountId:"); const fcValid = await fcWalletValid( web3, account, msg, result, accountId, chainId, counterFactualInfo ); if (fcValid.result) { resolve({ sig: result, counterFactualInfo: fcValid.counterFactualInfo, }); return; } } // Valid: 2. webview directory signature Valid if ( (window?.ethereum?.isImToken || window?.ethereum?.isMetaMask) && isMobile && // Mobile directory connect will sign ConnectorNames as MetaMask only walletType === ConnectorNames.MetaMask ) { const address: string[] = await window.ethereum?.request({ method: "eth_requestAccounts", }); if ( address?.find( (item) => item.toLowerCase() === account.toLowerCase() ) ) { return resolve({ sig: result }); } } // Valid: 3. EOA signature Valid by ecRecover const valid: any = await ecRecover(web3, account, msg, result); if (valid.result) { return resolve({ sig: result }); } // Valid: 4. contractWallet signature Valid `isValidSignature(bytes32,bytes)` const walletValid2: any = await contractWalletValidate32( web3, account, msg, result ); if (walletValid2.result) { return resolve({ sig: result }); } // Valid: 5. counter Factual signature Valid when no counterFactualInfo if (accountId) { const fcValid = await fcWalletValid( web3, account, msg, result, accountId, chainId ); if (fcValid.result) { return resolve({ sig: result, counterFactualInfo: fcValid.counterFactualInfo, }); } } // Valid: 6. myKeyValid Valid again const myKeyValid: any = await mykeyWalletValid( web3, account, msg, result ); if (myKeyValid.result) { return resolve({ sig: result }); } // Valid: Error cannot pass personalSign Valid // eslint-disable-next-line no-console console.log( "web3.eth.personal.sign Valid, valid 5 ways, all failed!" ); return resolve({ error: "web3.eth.personal.sign Valid, valid 5 ways, all failed!", }); } else { return resolve({ error: "personalSign err before Validate:" + err, }); } } ); } catch (reason) { resolve({ error: reason }); } }); } ``` ================================================ FILE: src/tests/demo/account/signature.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, signatureKeyPairMock, testTypedData, web3, } from '../../MockData' import * as sdk from '../../../index' describe('signature', function () { it( 'generateKeyPair', async () => { const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) const result = await signatureKeyPairMock(accInfo) console.log(result.sk) }, DEFAULT_TIMEOUT, ) /** * test case is not allow brock by Mock provider */ // it( // "getEcDSASig:eth_signTypedData_v4", // async () => { // // test case is not allow brock by Mock provider // const result = await sdk.getEcDSASig( // web3, // testTypedData, // LOOPRING_EXPORTED_ACCOUNT.address, // sdk.GetEcDSASigType.HasDataStruct, // sdk.ChainId.GOERLI, // LOOPRING_EXPORTED_ACCOUNT.accountId, // "", // sdk.ConnectorNames.Unknown // ); // console.log( // "getEcDSASig:eth_signTypedData_v4", // result, // "ecdsaSig+sdk.SigSuffix.Suffix02", // result.ecdsaSig + sdk.SigSuffix.Suffix02 // ); // }, // DEFAULT_TIMEOUT // ); it('getEcDSASig:WithoutDataStruct(personalSign)', async () => { const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.WithoutDataStruct, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, '', sdk.ConnectorNames.Unknown, ) console.log('getEcDSASig:WithoutDataStruct(personalSign)', result) }) /** * test case is not allow brock by Mock provider */ // it("personalSign Contract", async () => { // await sdk.getEcDSASig( // web3, // testTypedData, // LOOPRING_EXPORTED_ACCOUNT.address, // sdk.GetEcDSASigType.Contract, // sdk.ChainId.GOERLI, // LOOPRING_EXPORTED_ACCOUNT.accountId, // "", // sdk.ConnectorNames.Unknown // ); // }); }) export default {} ================================================ FILE: src/tests/demo/deposit/deposit.md ================================================ # Deposit ERC20 Definition: Move user L1 ERC20 assets to Loopring L2 > **All Deposit Method, User should have enough `ETH` pay for the Ethereum Gas (Loopring have no charge, no fee for Deposit).** Provider will give the Gas Price & Limit, sdk also have a method get gasPrice: `const gasPrice = (await LoopringAPI.exchangeAPI.getGasPrice() ).gasPrice;` # ETH *** ## Step 1. getNonce ```ts const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: ${TOKEN_INFO.tokenMap.ETH.symbol}-${LOOPRING_EXPORTED_ACCOUNT.tradeETHValue}, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); ``` *** ## Step 2. deposit ```ts const response = await sdk.deposit( web3, LOOPRING_EXPORTED_ACCOUNT.address, LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, TOKEN_INFO.tokenMap.ETH, LOOPRING_EXPORTED_ACCOUNT.tradeETHValue, 0, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); console.log(`nonce: ${nonce} deposit_ETH: `, response); ``` # ERC20 *** ## Step 1. getAllowances ```ts const {tokenAllowances} = await LoopringAPI.exchangeAPI.getAllowances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, token: [TOKEN_INFO.tokenMap.LRC.address], }); if ( tokenAllowances.has(TOKEN_INFO.tokenMap.LRC.address) && Number(tokenAllowances.get(TOKEN_INFO.tokenMap.LRC.address)) < LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue ) { const nonce = await web3.eth.getTransactionCount( LOOPRING_EXPORTED_ACCOUNT.address ); await sdk.approveMax( web3, LOOPRING_EXPORTED_ACCOUNT.address, TOKEN_INFO.tokenMap.LRC.address, // LRC address {tokenIdMap} = getTokens(); tokenIdMap['LRC'] LOOPRING_EXPORTED_ACCOUNT.depositAddress, //{exchangeInfo} = getExchangeInfo() exchangeInfo.depositAddress LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); } ``` *** ## Step 2. getNonce ```ts const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: ${TOKEN_INFO.tokenMap.LRC.symbol}-${LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue}, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); ``` *** ## Step 3. deposit ```ts const response = await sdk.deposit( web3, LOOPRING_EXPORTED_ACCOUNT.address, LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, TOKEN_INFO.tokenMap.LRC, sdk .toBig(LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue) .div("1e" + TOKEN_INFO.tokenMap.LRC.decimals) .toNumber(), 0, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); console.log(`nonce: ${nonce} deposit_LRC: `, response); ``` ================================================ FILE: src/tests/demo/deposit/deposit.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, } from "../../MockData"; import * as sdk from "../../../index"; describe("deposit", function () { beforeEach(async () => { jest.setTimeout(DEFAULT_TIMEOUT * 3); LOOPRING_EXPORTED_ACCOUNT.gasPrice = ( await LoopringAPI.exchangeAPI.getGasPrice() ).gasPrice; }, DEFAULT_TIMEOUT); it( "deposit_LRC", async () => { // step1: getAllowances const { tokenAllowances } = await LoopringAPI.exchangeAPI.getAllowances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, token: [TOKEN_INFO.tokenMap.LRC.address], }); if ( tokenAllowances.has(TOKEN_INFO.tokenMap.LRC.address) && Number(tokenAllowances.get(TOKEN_INFO.tokenMap.LRC.address)) < LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue ) { const nonce = await web3.eth.getTransactionCount( LOOPRING_EXPORTED_ACCOUNT.address ); await sdk.approveMax( web3, LOOPRING_EXPORTED_ACCOUNT.address, TOKEN_INFO.tokenMap.LRC.address, // LRC address {tokenIdMap} = getTokens(); tokenIdMap['LRC'] LOOPRING_EXPORTED_ACCOUNT.depositAddress, //{exchangeInfo} = getExchangeInfo() exchangeInfo.depositAddress LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); } const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: ${TOKEN_INFO.tokenMap.LRC.symbol}-${LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue}, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); const response = await sdk.deposit( web3, LOOPRING_EXPORTED_ACCOUNT.address, LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, TOKEN_INFO.tokenMap.LRC, sdk .toBig(LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue) .div("1e" + TOKEN_INFO.tokenMap.LRC.decimals) .toNumber(), 0, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); console.log(`nonce: ${nonce} deposit_LRC: `, response); }, DEFAULT_TIMEOUT * 3 ); it( "deposit_ETH", async () => { const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: ${TOKEN_INFO.tokenMap.ETH.symbol}-${LOOPRING_EXPORTED_ACCOUNT.tradeETHValue}, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); const response = await sdk.deposit( web3, LOOPRING_EXPORTED_ACCOUNT.address, LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, TOKEN_INFO.tokenMap.ETH, LOOPRING_EXPORTED_ACCOUNT.tradeETHValue, 0, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); console.log(`nonce: ${nonce} deposit_ETH: `, response); }, DEFAULT_TIMEOUT * 3 ); }); ================================================ FILE: src/tests/demo/deposit/depositNFT.md ================================================ # Deposit NFT Definition: Move user L1 NFT assets to Loopring L2 > **All Deposit Method, User should have enough `ETH` pay for the Ethereum Gas (Loopring have no charge, no fee for Deposit).** *** ## Step 1. getNFTBalance & getEthBalances ```ts const {ethBalance} = await LoopringAPI.exchangeAPI.getEthBalances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const nftBalance = await LoopringAPI.nftAPI.getNFTBalance({ web3, account: LOOPRING_EXPORTED_ACCOUNT.address, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); ``` *** ## Step 2. isApprovedForAll ```ts const isApprovedForAll = await LoopringAPI.nftAPI.isApprovedForAll({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, exchangeAddress: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, nftType: sdk.NFTType.ERC1155, // todo: sdk.NFTType.ERC721 tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, }); console.log(`check is approveNFT`, isApprovedForAll); ``` *** ## Step 3. approveNFT All ```ts if (!isApprovedForAll) { const nonce = await sdk.getNonce( web3, LOOPRING_EXPORTED_ACCOUNT.address ); const approveNFT = await LoopringAPI.nftAPI.approveNFT({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, depositAddress: LOOPRING_EXPORTED_ACCOUNT.depositAddress, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftType: sdk.NFTType.ERC1155, // todo: sdk.NFTType.ERC721 gasPrice: LOOPRING_EXPORTED_ACCOUNT.gasPrice, gasLimit: LOOPRING_EXPORTED_ACCOUNT.gasLimit, chainId: sdk.ChainId.GOERLI, nonce, sendByMetaMask: true, }); console.log(`nonce: ${nonce} approveNFT: ${approveNFT?.result}`); } ``` *** ## Step 3. nonce ```ts const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: NFT, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); ``` *** ## Step 4. depositNFT ```ts const response = await LoopringAPI.nftAPI.depositNFT({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, exchangeAddress: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, nftType: sdk.NFTType.ERC1155, // todo: sdk.NFTType.ERC721 tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, amount: 2, // todo:when sdk.NFTType.ERC721 amount: 1, gasPrice: LOOPRING_EXPORTED_ACCOUNT.gasPrice, gasLimit: LOOPRING_EXPORTED_ACCOUNT.gasLimit + 100000, chainId: sdk.ChainId.GOERLI, nonce, sendByMetaMask: true, }); console.log(`nonce: ${nonce} deposit NFT ERC1155: `, response); ``` ================================================ FILE: src/tests/demo/deposit/depositNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, TOKEN_INFO, web3, } from "../../MockData"; import * as sdk from "../../../index"; describe("depositNFT", function () { beforeEach(async () => { jest.setTimeout(DEFAULT_TIMEOUT * 3); LOOPRING_EXPORTED_ACCOUNT.gasPrice = ( await LoopringAPI.exchangeAPI.getGasPrice() ).gasPrice; }, DEFAULT_TIMEOUT); it( "deposit NFTAction ERC1155", async () => { // Step 1. getNFTBalance & getEthBalances const { ethBalance } = await LoopringAPI.exchangeAPI.getEthBalances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const nftBalance = await LoopringAPI.nftAPI.getNFTBalance({ web3, account: LOOPRING_EXPORTED_ACCOUNT.address, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); // Step 2. isApprovedForAll const isApprovedForAll = await LoopringAPI.nftAPI.isApprovedForAll({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, exchangeAddress: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, nftType: sdk.NFTType.ERC1155, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, }); console.log(`check is approveNFT`, isApprovedForAll); // Step 3. approveNFT All if (!isApprovedForAll) { const nonce = await sdk.getNonce( web3, LOOPRING_EXPORTED_ACCOUNT.address ); const approveNFT = await LoopringAPI.nftAPI.approveNFT({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, depositAddress: LOOPRING_EXPORTED_ACCOUNT.depositAddress, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftType: sdk.NFTType.ERC1155, gasPrice: LOOPRING_EXPORTED_ACCOUNT.gasPrice, gasLimit: LOOPRING_EXPORTED_ACCOUNT.gasLimit, chainId: sdk.ChainId.GOERLI, nonce, sendByMetaMask: true, }); console.log(`nonce: ${nonce} approveNFT: ${approveNFT?.result}`); } // Step 4. nonce const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); console.log( `deposit: NFT, gasPrice: ${LOOPRING_EXPORTED_ACCOUNT.gasPrice}, ` ); // Step 5. depositNFT const response = await LoopringAPI.nftAPI.depositNFT({ web3, from: LOOPRING_EXPORTED_ACCOUNT.address, exchangeAddress: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, nftType: sdk.NFTType.ERC1155, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, amount: 1, gasPrice: LOOPRING_EXPORTED_ACCOUNT.gasPrice, gasLimit: LOOPRING_EXPORTED_ACCOUNT.gasLimit + 100000, chainId: sdk.ChainId.GOERLI, nonce, sendByMetaMask: true, }); console.log(`nonce: ${nonce} deposit NFT ERC1155: `, response); }, DEFAULT_TIMEOUT ); // it( // "deposit NFTAction ERC721", // async () => { // const nonce = await sdk.getNonce( // web3, // LOOPRING_EXPORTED_ACCOUNT.address // ); // const response = await LoopringAPI.nftAPI.depositNFT({ // web3, // from: LOOPRING_EXPORTED_ACCOUNT.address, // exchangeAddress: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, // nftType: NFTType.ERC721, // tokenAddress: '// TODO:', // nftId: '// TODO:', // amount: 1, // gasPrice: LOOPRING_EXPORTED_ACCOUNT.gasPrice, // gasLimit: LOOPRING_EXPORTED_ACCOUNT.gasLimit, // chainId: ChainId.GOERLI, // nonce, // sendByMetaMask: true, // }); // // console.log(`nonce: ${nonce} deposit NFT ERC1155: `, response); // }, // DEFAULT_TIMEOUT // ); }); ================================================ FILE: src/tests/demo/erc20Trade/orderERC20.md ================================================ # Order ERC20 *** ## mini-order To support small quantity trading, we introduce an additional concept "tradeCost", which is the minimum gas fee when a trade transaction is uplink to Ethereum. Let's take LRC-ETH trading as an example. Below are the steps - **1) Query api/v3/exchange/tokens to get the dust value of orderAmounts for both LRC and ETH.** The dust value is the minimum value to pass Relayer check. Any amount less than "dust" can't be traded. In this case, we will get both minTOkenLRC and minTokenETH after getting dust value. If user wants to convert LRC to ETH, the set LRC amount can't be less than minTokenLRC and the converted ETH amount can't be less than minTokenETH. **2) Query api/v3/user/orderUserRateAmount to get the tradeCost value.** The parameters to call this interface are "accountId" and "market=LRC-ETH". In this example, we will get two tradeCost values for LRC and ETH as tradeCostLRC and tradeCostETH. **3) Set maxAllowBips = 50% as the maxFeeBips can't exceed 50%** **4) Set slippage as the slippage value user configured in UI (for example 0.1%)** **5)Caculate minCostLRC and minCostETH as below** minCostLRC = max(minTokenLRC, tradeCostLRC/maxAllowBips) minCostETH = max(minTokenETH, tradeCostETH/maxAllowBips) **6) Caculate the cost by considering slippage** minCostLRCSlip = minCostLRC/(1-slippage) minCostETHSlip = minCostETH/(1-slippage) **7) Cacluate the minimum quantity user has to set** tradeCostSellLRC = max(tradeCostSellLRC, minTokenLRC) * 1.1 tradeCostSellETH = max(tradeCostSellETH, minTokenETH) * 1.1 Here we add additonally 10% tolerance. **8) Caculate the previous minimum token amount per calling api/v3/user/orderUserRateAmount (existing logic)** This is the threshold to distinguish small quantity trading and normal trading We will get two values (configSellLRC and configSellETH) which are used for previous trading quantity limit (Per USD 100) caculation **9) Caculate the new maxFeeBips and start trading** Let's take LRC->ETH as the example User inputs the amount of LRC to convert, amount = sellLRC if sellLRC >= configSellLRC then // Normal trading case, stay with previous logic maxFeeBips=63 (the default value for maxFeeBips) Trade else if sellLRC < tradeCostSellLRC then // Really too small to support Prompt user the amount is too small to support Exit else // This is what we call as small quantity costRate = Ceil(tradeCostETH/minbuyETH) maxFeeBips = max(costRate, takerRate) Trade End If ### price impact update **1)sellTokenMinAmount = baseOrderInfo.minAmount from LoopringAPI.userAPI.getMinimumTokenAmt({accountId,marke}, apiKey);** **2){output} from sdk.getOutputAmount(input: sellTokenMinAmount, isAtoB: isAtoB,…}).output** **3)PriceBase = output / sellTokenMinAmount** **4)tradePrice = calcTradeParams.minReceive / userInputSell** **5)priceImpact = 1 - tradePrice/PriceBase - 0.005** **6)If priceImpact < 0 priceImpact = 0 Else priceImpact** ## ```ts const calculateSwap = ( sellSymbol = "LRC", buySymbol = "ETH", isInputSellToBuy: boolean, inputValue: number, // user Input value no decimal, _slippage = 0.1, // MOCK value amountMap: { [key: string]: any } = userAmount, market: string = deepMock.symbol, // close = ticker.tickers[7], depth: any = deepMock, ammPoolSnapshot: sdk.AmmPoolSnapshot = ammPoolSnapshotMock, tokenMap: sdk.LoopringMap = TokenMapMockSwap, ammMap: { [key: string]: any } = AMM_MAP ) => { let calcFor100USDAmount, calcForMinCost, calcForPriceImpact; if (depth && market && tokenMap) { const sellToken = tokenMap[sellSymbol]; const buyToken = tokenMap[buySymbol]; const isInputSellOutputBuy = isInputSellToBuy; let input: any = inputValue; console.log( "sellToken: Symbol ", sellSymbol, "buyToken: Symbol", buySymbol, "is Input Sell Output Buy:", isInputSellOutputBuy, "input value", input ); input = input === undefined || isNaN(Number(input)) ? 0 : Number(input); let slippage = sdk.toBig(_slippage).times(100).toString(); let totalFee = undefined; let feeTakerRate = undefined; let feeBips = undefined; let takerRate = undefined; let buyMinAmtInfo = undefined; let sellMinAmtInfo = undefined; let tradeCost = undefined; let basePrice = undefined; let maxFeeBips = MAPFEEBIPS; let minAmt = undefined; if (amountMap && amountMap[market] && ammMap) { console.log(`amountMap[${market}]:`, amountMap[market]); const ammMarket = `AMM-${market}`; const amountMarket = amountMap[market]; // userAmount from LRC-ETH(Market) buyMinAmtInfo = amountMarket[buySymbol]; sellMinAmtInfo = amountMarket[sellSymbol]; console.log( `buyMinAmtInfo: ${market}, ${buySymbol}`, buyMinAmtInfo, `sellMinAmtInfo: ${market}, ${sellSymbol}`, sellMinAmtInfo ); feeBips = ammMap[ammMarket] ? ammMap[ammMarket].feeBips : 1; feeTakerRate = amountMarket[buySymbol] && amountMarket[buySymbol].userOrderInfo.takerRate; tradeCost = amountMarket[buySymbol].tradeCost; /** @description for charge fee calc, calcFor100USDAmount * Loopring market consider buyToken value small then max(buyMinAmtInfo.userOrderInfo.minAmount,buyToken.orderAmounts.dust) is a small order, * the fee will take the Max(tradeCost,userTakeRate) * use the buyMinAmount Input calc the selltoken value, * please read Line:321 * **/ const minAmountInput = BigNumber.max( buyMinAmtInfo.userOrderInfo.minAmount, buyToken.orderAmounts.dust ) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div("1e" + buyToken.decimals) .toString(); calcFor100USDAmount = sdk.getOutputAmount({ input: minAmountInput, sell: sellSymbol, buy: buySymbol, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: slippage, }); console.log( "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: sellSymbol, buy: buySymbol, isAtoB: true, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: "10", }); basePrice = sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput); console.log( "calcForPriceImpact input: ", sellMinAmtInput, ", output: ", sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput).toNumber(), ", calcForPriceImpact:", calcForPriceImpact?.amountBOutSlip?.minReceivedVal, ", calcForPriceImpact basePrice: ", basePrice.toNumber() ); /**** calc for mini Cost ****/ //minCostBuyToken = max(dustBuyToken, tradeCostETH/maxAllowBips) const dustToken = buyToken; let minCostBuyTokenInput = BigNumber.max( sdk.toBig(tradeCost).times(2), //maxAllowBips = 50% tradeCostETH/50% dustToken.orderAmounts.dust ); const tradeCostInput = sdk .toBig(minCostBuyTokenInput) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div("1e" + dustToken.decimals) .toString(); console.log( `tradeCost: ${tradeCost}*2:`, sdk.toBig(tradeCost).times(2).toString(), "buyToken.orderAmounts.dust", buyToken.orderAmounts.dust, "minCostBuyToken:", minCostBuyTokenInput.toString(), `calcForMinCostInput, with slippage:${slippage}`, sdk .toBig(minCostBuyTokenInput ?? 0) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .toString(), "calcForMinCost, Input", tradeCostInput ); calcForMinCost = sdk.getOutputAmount({ input: tradeCostInput, sell: sellSymbol, buy: buySymbol, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: slippage, }); //add additionally 10% tolerance for minimum quantity user has to set on sell Token /** * @output: minAmt for UI * this value mini-order Sell token amount (show on the UI for available order check) * setSellMinAmt(minAmt.toString()); */ minAmt = BigNumber.max( sellToken.orderAmounts.dust, calcForMinCost?.amountS ?? 0 ).times(1.1); console.log( "UI show mini-order Sell token amount:", minAmt.toString(), sdk .toBig(minAmt) .div("1e" + sellToken.decimals) .toString() ); console.log( `calcFor100USDAmount.amountS`, sdk .toBig(calcFor100USDAmount?.amountS ?? 0) .div("1e" + sellToken.decimals) .toString(), "calcForMinCost.amountS", sdk .toBig(calcForMinCost?.amountS ?? 0) .div("1e" + sellToken.decimals) .toString() ); } const calcTradeParams = sdk.getOutputAmount({ input: input.toString(), sell: sellSymbol, buy: buySymbol, isAtoB: isInputSellOutputBuy, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", // for new calc miniReceive will minus fee, so takeRate can fix as 0 slipBips: slippage, }); const minSymbol = buySymbol; const tradePrice = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(isInputSellOutputBuy ? 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"); } console.log( "calcTradeParams input:", input.toString(), ", calcTradeParams Price: ", sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(input.toString()) .toNumber(), `isAtoB mean isInputSellOutputBuy:${isInputSellOutputBuy}, ${ isInputSellOutputBuy ? 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 ); if ( tradeCost && calcTradeParams && calcTradeParams.amountBOutSlip?.minReceived && feeTakerRate ) { let value = sdk .toBig(calcTradeParams.amountBOutSlip?.minReceived) .times(feeTakerRate) .div(10000); console.log( "input Accounts", calcTradeParams?.amountS, "100 U Amount Sell:", calcFor100USDAmount?.amountS ); let validAmt = !!( calcTradeParams?.amountS && calcFor100USDAmount?.amountS && sdk.toBig(calcTradeParams?.amountS).gte(calcFor100USDAmount.amountS) ); let totalFeeRaw; console.log( `${minSymbol} tradeCost:`, tradeCost, "useTakeRate Fee:", value.toString(), "calcFor100USDAmount?.amountS:", calcFor100USDAmount?.amountS, `is setup minTrade amount, ${calcFor100USDAmount?.amountS}:`, validAmt ); if (!validAmt) { if (sdk.toBig(tradeCost).gte(value)) { totalFeeRaw = sdk.toBig(tradeCost); } else { totalFeeRaw = value; } console.log( "maxFeeBips update for tradeCost before value:", maxFeeBips, "totalFeeRaw", totalFeeRaw.toString() ); maxFeeBips = Math.ceil( totalFeeRaw .times(10000) .div(calcTradeParams.amountBOutSlip?.minReceived) .toNumber() ); console.log("maxFeeBips update for tradeCost after value:", maxFeeBips); } else { totalFeeRaw = sdk.toBig(value); } /** * totalFee */ totalFee = totalFeeRaw .div("1e" + tokenMap[minSymbol].decimals) .toString(); /** @output: UI * getValuePrecisionThousand( * totalFeeRaw.div("1e" + tokenMap[minSymbol].decimals).toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ tradeCost = sdk .toBig(tradeCost) // @ts-ignore .div("1e" + tokenMap[minSymbol].decimals) .toString(); /** @output: UI code with precision * getValuePrecisionThousand( * sdk * .toBig(tradeCost) * .div("1e" + tokenMap[minSymbol].decimals) * .toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ console.log("totalFee view value:", totalFee + " " + minSymbol); console.log("tradeCost view value:", tradeCost + " " + minSymbol); } const minimumReceived = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .minus(totalFee ?? 0) .toString(); console.log("minimumReceived:", minimumReceived); /** @output: UI code with precision * getValuePrecisionThousand( * toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) * .minus(totalFee) * .toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ let priceImpactView: any = calcTradeParams?.priceImpact ? parseFloat(calcTradeParams?.priceImpact) * 100 : undefined; console.log("priceImpact view:", priceImpactView + "%"); // @output: UI code with color alert // const priceImpactObj = getPriceImpactInfo(calcTradeParams); // const _tradeCalcData: Partial> = { // priceImpact: priceImpactObj.value.toString(), // priceImpactColor: priceImpactObj.priceImpactColor, // minimumReceived: !minimumReceived?.toString().startsWith("-") // ? minimumReceived // : undefined, // fee: totalFee, // feeTakerRate, // tradeCost, // }; console.log( `isInputSellOutputBuy:${isInputSellOutputBuy}`, `output ${isInputSellOutputBuy ? "Buy" : "Sell"}`, calcTradeParams?.output ); return { market, feeBips, takerRate, sellMinAmtInfo: sellMinAmtInfo as any, buyMinAmtInfo: buyMinAmtInfo as any, totalFee, maxFeeBips, feeTakerRate, tradeCost, minimumReceived, calcTradeParams, minAmt, }; } }; ``` ### Step 1. get apikey & eddsaKey ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const eddsaKey = await signatureKeyPairMock(accInfo); apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; ``` ### Step 2. storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, sellTokenId: TOKEN_INFO.tokenMap[sell].tokenId, }, apiKey ); ``` ### Step 3. get user AmountMap, which decided user minimum order ```ts const amountMap = { [AMM_MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: AMM_MAP[AMM_MARKET].market, }, apiKey ) ).amountMap, [MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: MARKET, }, apiKey ) ).amountMap, }; ``` ### Step 4. depth, ammPoolSnapshot ,tickMap ```ts const [{depth}, {ammPoolSnapshot}] = await Promise.all([ LoopringAPI.exchangeAPI.getMixDepth({ market: AMM_MAP["AMM-LRC-ETH"].market, }), LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: AMM_MAP["AMM-LRC-ETH"].address, }), ]); ``` ### Step 5. Check MinAmt see log and calc mini receive and ouput value & maxfeeBips & priceImpact & swap output ```ts const { calcTradeParams, maxFeeBips, minimumReceived } = calculateSwap( sell, buy, isAtoB, 10, // user Input value no decimal 10 lrc, 0.1, //TODO MOCK value amountMap, "LRC-ETH", // close = ticker.tickers[7], depth, ammPoolSnapshot, TOKEN_INFO.tokenMap, AMM_MAP ); ``` ### Step 6. Submit ```ts const response: { hash: string } | any = await LoopringAPI.userAPI.submitOrder( { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, storageId: storageId.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap[sell].tokenId, volume: calcTradeParams?.amountS as string, }, buyToken: { tokenId: TOKEN_INFO.tokenMap[buy].tokenId, volume: calcTradeParams?.amountBOutSlip.minReceived as string, }, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 63, fillAmountBOrS: false, // amm only false tradeChannel: calcTradeParams?.exceedDepth ? sdk.TradeChannel.BLANK : sdk.TradeChannel.MIXED, orderType: calcTradeParams?.exceedDepth ? sdk.OrderType.ClassAmm : sdk.OrderType.TakerOnly, eddsaSignature: "", }, eddsaKey.sk, apiKey ); console.log("submitOrder", response); ``` *** ## ETH-LRC : for Quote to Base ### MOCK Data > user should had apikey, please check get apikey ```ts const buy = "ETH", sell = "LRC", MARKET = "LRC-ETH", AMM_MARKET = "AMM-LRC-ETH", slippage = "50"; const isAtoB = false; ``` ### Step 1. get apikey & eddsaKey ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const eddsaKey = await signatureKeyPairMock(accInfo); apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; ``` ### Step 2. storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, sellTokenId: TOKEN_INFO.tokenMap[sell].tokenId, }, apiKey ); ``` ### Step 3. get user AmountMap, which decided user minimum order ```ts const amountMap = { [AMM_MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: AMM_MAP[AMM_MARKET].market, }, apiKey ) ).amountMap, [MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: MARKET, }, apiKey ) ).amountMap, }; ``` ### Step 4. depth, ammPoolSnapshot ,tickMap ```ts const [{depth}, {ammPoolSnapshot}] = await Promise.all([ LoopringAPI.exchangeAPI.getMixDepth({ market: AMM_MAP["AMM-LRC-ETH"].market, }), LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: AMM_MAP["AMM-LRC-ETH"].address, }), ]); ``` ### Step 5. check MinAmt see log and calc mini receive and ouput value & maxfeeBips & priceImpact ```ts const { calcTradeParams, maxFeeBips, minimumReceived } = calculateSwap( sell, buy, isAtoB, 10, // user Input value no decimal 10 lrc, 0.1, //TODO MOCK value amountMap, "LRC-ETH", // close = ticker.tickers[7], depth, ammPoolSnapshot, TOKEN_INFO.tokenMap, AMM_MAP ); console.log( "Buy", ",ETH:", LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), ",minAmountInput ETH:", minAmountInput, ",LRC:", calcTradeParams?.amountBOutSlip?.minReceivedVal ); const response: { hash: string } | any = await LoopringAPI.userAPI.submitOrder( { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, storageId: storageId.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap[sell].tokenId, volume: calcTradeParams?.amountS as string, }, buyToken: { tokenId: TOKEN_INFO.tokenMap[buy].tokenId, volume: calcTradeParams?.amountBOutSlip.minReceived as string, }, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: 63, fillAmountBOrS: false, // amm only false tradeChannel: calcTradeParams?.exceedDepth ? sdk.TradeChannel.BLANK : sdk.TradeChannel.MIXED, orderType: calcTradeParams?.exceedDepth ? sdk.OrderType.ClassAmm : sdk.OrderType.TakerOnly, eddsaSignature: "", }, eddsaKey.sk, apiKey ); console.log("submitOrder", response); ``` ### MockSwapData ```ts //Default config value from getTokens & getMixMarkets import * as sdk from "../index"; export const marketArray = ["LRC-ETH"]; export const marketMap = { "LRC-ETH": { baseTokenId: 1, enabled: true, market: "LRC-ETH", orderbookAggLevels: 5, precisionForPrice: 6, quoteTokenId: 0, status: 3, isSwapEnabled: true, createdAt: 1617967800000, }, }; //v3/mix/depth?level=0&limit=50&market=LRC-ETH export const deepMock = { symbol: "LRC-ETH", version: 23249677, timestamp: 1655719492365, mid_price: 0.00033248, bids: [ { price: 0.00030689, amt: "12041160324514792497908", vol: "3695372332571085210", amtTotal: "618450503644320209925641", volTotal: "198539605794234049017", }, { price: 0.00030752, amt: "12016302126785109160251", vol: "3695372332571085210", amtTotal: "606409343319805417427733", volTotal: "194844233461662963807", }, { price: 0.00030816, amt: "11991520826895479525387", vol: "3695372332571085210", amtTotal: "594393041193020308267482", volTotal: "191148861129091878597", }, { price: 0.00030881, amt: "12329062048917727073625", vol: "3807353312345966580", amtTotal: "582401520366124828742095", volTotal: "187453488796520793387", }, { price: 0.00030945, amt: "11941442525358097768419", vol: "3695372332571085210", amtTotal: "570072458317207101668470", volTotal: "183646135484174826807", }, { price: 0.0003102, amt: "3223726000000000000000", vol: "999999805200000000", amtTotal: "558131015791849003900051", volTotal: "179950763151603741597", }, { price: 0.0003104, amt: "11904963012947201084062", vol: "3695372332571085210", amtTotal: "554907289791849003900051", volTotal: "178950763346403741597", }, { price: 0.00031072, amt: "11892800074855146120151", vol: "3695372332571085210", amtTotal: "543002326778901802815989", volTotal: "175255391013832656387", }, { price: 0.00031137, amt: "12227667622887455762012", vol: "3807353312345966580", amtTotal: "531109526704046656695838", volTotal: "171560018681261571177", }, { price: 0.00031202, amt: "11843337524732768607817", vol: "3695372332571085210", amtTotal: "518881859081159200933826", volTotal: "167752665368915604597", }, { price: 0.00031266, amt: "11819088718260537718160", vol: "3695372332571085210", amtTotal: "507038521556426432326009", volTotal: "164057293036344519387", }, { price: 0.0003133, amt: "11794914308461194855590", vol: "3695372332571085210", amtTotal: "495219432838165894607849", volTotal: "160361920703773434177", }, { price: 0.00031395, amt: "12127129883064173669497", vol: "3807353312345966580", amtTotal: "483424518529704699752259", volTotal: "156666548371202348967", }, { price: 0.0003146, amt: "11746060536480763829870", vol: "3695372332571085210", amtTotal: "471297388646640526082762", volTotal: "152859195058856382387", }, { price: 0.00031524, amt: "11722109721002274877424", vol: "3695372332571085210", amtTotal: "459551328110159762252892", volTotal: "149163822726285297177", }, { price: 0.0003159, amt: "12052351993023897680738", vol: "3807353312345966580", amtTotal: "447829218389157487375468", volTotal: "145468450393714211967", }, { price: 0.00031655, amt: "11673707113082743010268", vol: "3695372332571085210", amtTotal: "435776866396133589694730", volTotal: "141661097081368245387", }, { price: 0.00031699, amt: "615000000000000000000", vol: "194954999999999949", amtTotal: "424103159283050846684462", volTotal: "137965724748797160177", }, { price: 0.00031719, amt: "11649977142974837964110", vol: "3695372332571085210", amtTotal: "423488159283050846684462", volTotal: "137770769748797160228", }, { price: 0.00031784, amt: "11626319455782556517212", vol: "3695372332571085210", amtTotal: "411838182140076008720352", volTotal: "134075397416226075018", }, { price: 0.0003185, amt: "11953964321007990024755", vol: "3807353312345966580", amtTotal: "400211862684293452203140", volTotal: "130380025083654989808", }, { price: 0.00031934, amt: "11571837200722111826863", vol: "3695372332571085210", amtTotal: "388257898363285462178385", volTotal: "126572671771309023228", }, { price: 0.00031979, amt: "11555429099390519988949", vol: "3695372332571085210", amtTotal: "376686061162563350351522", volTotal: "122877299438737938018", }, { price: 0.00032, amt: "300000000000000000000", vol: "96000000000000000", amtTotal: "365130632063172830362573", volTotal: "119181927106166852808", }, { price: 0.00032044, amt: "11532058329906875386268", vol: "3695372332571085210", amtTotal: "364830632063172830362573", volTotal: "119085927106166852808", }, { price: 0.0003211, amt: "11857145662584872212009", vol: "3807353312345966580", amtTotal: "353298573733265954976305", volTotal: "115390554773595767598", }, { price: 0.00032176, amt: "11484826169666631095087", vol: "3695372332571085210", amtTotal: "341441428070681082764296", volTotal: "111583201461249801018", }, { price: 0.00032241, amt: "11461669156035188891431", vol: "3695372332571085210", amtTotal: "329956601901014451669209", volTotal: "107887829128678715808", }, { price: 0.00032294, amt: "22266286128259997368320", vol: "7190674442260284416", amtTotal: "318494932744979262777778", volTotal: "104192456796107630598", }, { price: 0.00032307, amt: "11784846142950476791130", vol: "3807353312345966580", amtTotal: "296228646616719265409458", volTotal: "97001782353847346182", }, { price: 0.00032333, amt: "31042151210389998665728", vol: "10037169172367501312", amtTotal: "284443800473768788618328", volTotal: "93194429041501379602", }, { price: 0.00032354, amt: "19082720347089998446592", vol: "6174214168300968960", amtTotal: "253401649263378789952600", volTotal: "83157259869133878290", }, { price: 0.00032373, amt: "11414868337793698506576", vol: "3695372332571085210", amtTotal: "234318928916288791506008", volTotal: "76983045700832909330", }, { price: 0.00032438, amt: "11391922481177051106050", vol: "3695372332571085210", amtTotal: "222904060578495092999432", volTotal: "73287673368261824120", }, { price: 0.00032503, amt: "11369045742792164433715", vol: "3695372332571085210", amtTotal: "211512138097318041893382", volTotal: "69592301035690738910", }, { price: 0.00032512, amt: "7101984539779999465472", vol: "2308997213573273600", amtTotal: "200143092354525877459667", volTotal: "65896928703119653700", }, { price: 0.0003257, amt: "11689707913498419991686", vol: "3807353312345966580", amtTotal: "193041107814745877994195", volTotal: "63587931489546380100", }, { price: 0.00032636, amt: "11322810509578528739940", vol: "3695372332571085210", amtTotal: "181351399901247458002509", volTotal: "59780578177200413520", }, { price: 0.00032702, amt: "11300141532643741902081", vol: "3695372332571085210", amtTotal: "170028589391668929262569", volTotal: "56085205844629328310", }, { price: 0.00032709, amt: "1511898000000000000000", vol: "494541835799999898", amtTotal: "158728447859025187360488", volTotal: "52389833512058243100", }, { price: 0.00032767, amt: "11277540564700637960582", vol: "3695372332571085210", amtTotal: "157216549859025187360488", volTotal: "51895291676258243202", }, { price: 0.00032838, amt: "11594014901713773553177", vol: "3807353312345966580", amtTotal: "145939009294324549399906", volTotal: "48199919343687157992", }, { price: 0.00032899, amt: "11232203228917728004878", vol: "3695372332571085210", amtTotal: "134344994392610775846729", volTotal: "44392566031341191412", }, { price: 0.00032959, amt: "31100749000000000753664", vol: "10250806870400000000", amtTotal: "123112791163693047841851", volTotal: "40697193698770106202", }, { price: 0.00032965, amt: "11209805337410429375102", vol: "3695372332571085210", amtTotal: "92012042163693047088187", volTotal: "30446386828370106202", }, { price: 0.00033031, amt: "11187474373893810850495", vol: "3695372332571085210", amtTotal: "80802236826282617713085", volTotal: "26751014495799020992", }, { price: 0.00033098, amt: "11503202913004006553102", vol: "3807353312345966580", amtTotal: "69614762452388806862590", volTotal: "23055642163227935782", }, { price: 0.00033099, amt: "26515451000000002129920", vol: "8776614281000000512", amtTotal: "58111559539384800309488", volTotal: "19248288850881969202", }, { price: 0.0003313, amt: "20499506300000000802816", vol: "6791486437190000640", amtTotal: "31596108539384798179568", volTotal: "10471674569881968690", }, { price: 0.00033165, amt: "11096602239384797376752", vol: "3680188132691968050", amtTotal: "11096602239384797376752", volTotal: "3680188132691968050", }, ], bids_prices: [ 0.00030689, 0.00030752, 0.00030816, 0.00030881, 0.00030945, 0.0003102, 0.0003104, 0.00031072, 0.00031137, 0.00031202, 0.00031266, 0.0003133, 0.00031395, 0.0003146, 0.00031524, 0.0003159, 0.00031655, 0.00031699, 0.00031719, 0.00031784, 0.0003185, 0.00031934, 0.00031979, 0.00032, 0.00032044, 0.0003211, 0.00032176, 0.00032241, 0.00032294, 0.00032307, 0.00032333, 0.00032354, 0.00032373, 0.00032438, 0.00032503, 0.00032512, 0.0003257, 0.00032636, 0.00032702, 0.00032709, 0.00032767, 0.00032838, 0.00032899, 0.00032959, 0.00032965, 0.00033031, 0.00033098, 0.00033099, 0.0003313, 0.00033165, ], bids_amtTotals: [ "618450503644320209925641", "606409343319805417427733", "594393041193020308267482", "582401520366124828742095", "570072458317207101668470", "558131015791849003900051", "554907289791849003900051", "543002326778901802815989", "531109526704046656695838", "518881859081159200933826", "507038521556426432326009", "495219432838165894607849", "483424518529704699752259", "471297388646640526082762", "459551328110159762252892", "447829218389157487375468", "435776866396133589694730", "424103159283050846684462", "423488159283050846684462", "411838182140076008720352", "400211862684293452203140", "388257898363285462178385", "376686061162563350351522", "365130632063172830362573", "364830632063172830362573", "353298573733265954976305", "341441428070681082764296", "329956601901014451669209", "318494932744979262777778", "296228646616719265409458", "284443800473768788618328", "253401649263378789952600", "234318928916288791506008", "222904060578495092999432", "211512138097318041893382", "200143092354525877459667", "193041107814745877994195", "181351399901247458002509", "170028589391668929262569", "158728447859025187360488", "157216549859025187360488", "145939009294324549399906", "134344994392610775846729", "123112791163693047841851", "92012042163693047088187", "80802236826282617713085", "69614762452388806862590", "58111559539384800309488", "31596108539384798179568", "11096602239384797376752", ], bids_volTotals: [ "198539605794234049017", "194844233461662963807", "191148861129091878597", "187453488796520793387", "183646135484174826807", "179950763151603741597", "178950763346403741597", "175255391013832656387", "171560018681261571177", "167752665368915604597", "164057293036344519387", "160361920703773434177", "156666548371202348967", "152859195058856382387", "149163822726285297177", "145468450393714211967", "141661097081368245387", "137965724748797160177", "137770769748797160228", "134075397416226075018", "130380025083654989808", "126572671771309023228", "122877299438737938018", "119181927106166852808", "119085927106166852808", "115390554773595767598", "111583201461249801018", "107887829128678715808", "104192456796107630598", "97001782353847346182", "93194429041501379602", "83157259869133878290", "76983045700832909330", "73287673368261824120", "69592301035690738910", "65896928703119653700", "63587931489546380100", "59780578177200413520", "56085205844629328310", "52389833512058243100", "51895291676258243202", "48199919343687157992", "44392566031341191412", "40697193698770106202", "30446386828370106202", "26751014495799020992", "23055642163227935782", "19248288850881969202", "10471674569881968690", "3680188132691968050", ], bids_amtTotal: "618450503644320209925641", bids_volTotal: "198539605794234049017", asks: [ { price: 0.00033331, amt: "26466599999999999737856", vol: "8821317780000000000", amtTotal: "26466599999999999737856", volTotal: "8821317780000000000", }, { price: 0.0003336, amt: "33880800000000001048576", vol: "11302634880000000000", amtTotal: "60347400000000000786432", volTotal: "20123952660000000000", }, { price: 0.00033371, amt: "11142477800817399987988", vol: "3718356266910774549", amtTotal: "71489877800817400774420", volTotal: "23842308926910774549", }, { price: 0.00033439, amt: "11410124103475892197402", vol: "3815340399218399705", amtTotal: "82900001904293292971822", volTotal: "27657649326129174254", }, { price: 0.00033501, amt: "39334199999999992922112", vol: "13176957000000000000", amtTotal: "122234201904293285893934", volTotal: "40834606326129174254", }, { price: 0.00033506, amt: "11052295534472154230337", vol: "3703108939445215718", amtTotal: "133286497438765440124271", volTotal: "44537715265574389972", }, { price: 0.00033572, amt: "11030456322913363709718", vol: "3703093651997124565", amtTotal: "144316953761678803833989", volTotal: "48240808917571514537", }, { price: 0.00033638, amt: "11008681778518069039476", vol: "3703078409816047858", amtTotal: "155325635540196872873465", volTotal: "51943887327387562395", }, { price: 0.000337, amt: "7188173683760000139264", vol: "2422342649690282496", amtTotal: "162513809223956873012729", volTotal: "54366229977077844891", }, { price: 0.00033706, amt: "11319571950108460741855", vol: "3815277012711042502", amtTotal: "173833381174065333754584", volTotal: "58181506989788887393", }, { price: 0.00033773, amt: "10964670732513311387215", vol: "3703047602083844528", amtTotal: "184798051906578645141799", volTotal: "61884554591872731921", }, { price: 0.00033848, amt: "10943090596879450870975", vol: "3703921209677097807", amtTotal: "195741142503458096012774", volTotal: "65588475801549829728", }, { price: 0.00033893, amt: "31086310012520000126976", vol: "10535772189443278848", amtTotal: "226827452515978096139750", volTotal: "76124247990993108576", }, { price: 0.00033907, amt: "10921574108084085072499", vol: "3703126650187824910", amtTotal: "237749026624062181212249", volTotal: "79827374641180933486", }, { price: 0.00033934, amt: "22061773569720001232896", vol: "7486221625413088256", amtTotal: "259810800193782182445145", volTotal: "87313596266594021742", }, { price: 0.00033975, amt: "11230093484570461106334", vol: "3815326678720011608", amtTotal: "271040893678352643551479", volTotal: "91128922945314033350", }, { price: 0.00034042, amt: "10878083875111520592396", vol: "3703095772122414389", amtTotal: "281918977553464164143875", volTotal: "94832018717436447739", }, { price: 0.00034061, amt: "19029641944770000453632", vol: "6481686342808109056", amtTotal: "300948619498234164597507", volTotal: "101313705060244556795", }, { price: 0.00034109, amt: "10856758733704286197912", vol: "3703080631272015254", amtTotal: "311805378231938450795419", volTotal: "105016785691516572049", }, { price: 0.00034176, amt: "10835496238854895014260", vol: "3703065534900672185", amtTotal: "322640874470793345809679", volTotal: "108719851226417244234", }, { price: 0.00034244, amt: "11141671799245742102774", vol: "3815263899323431057", amtTotal: "333782546270039087912453", volTotal: "112535115125740675291", }, { price: 0.00034312, amt: "10792518633700140402041", vol: "3703035020801012310", amtTotal: "344575064903739228314494", volTotal: "116238150146541687601", }, { price: 0.00034379, amt: "10771444484888773263977", vol: "3703020058155356239", amtTotal: "355346509388628001578471", volTotal: "119941170204697043840", }, { price: 0.00034447, amt: "11075875299655656611658", vol: "3815217183808722096", amtTotal: "366422384688283658190129", volTotal: "123756387388505765936", }, { price: 0.00034515, amt: "10728847020295584791583", vol: "3702989813955495075", amtTotal: "377151231708579242981712", volTotal: "127459377202461261011", }, { price: 0.00034582, amt: "10707958999548106661900", vol: "3702974983460764366", amtTotal: "387859190708127349643612", volTotal: "131162352185922025377", }, { price: 0.00034649, amt: "10687131919892014508549", vol: "3702960196234208540", amtTotal: "398546322628019364152161", volTotal: "134865312382156233917", }, { price: 0.00034718, amt: "10989265205593964126102", vol: "3815155690641938295", amtTotal: "409535587833613328278263", volTotal: "138680468072798172212", }, { price: 0.00034809, amt: "10645033127428079147754", vol: "3705341637309061860", amtTotal: "420180620961041407426017", volTotal: "142385809710107234072", }, { price: 0.00034854, amt: "10624389276271547157517", vol: "3703021892850000724", amtTotal: "430805010237312954583534", volTotal: "146088831602957234796", }, { price: 0.00034922, amt: "10603805418694082174472", vol: "3703007072472544949", amtTotal: "441408815656007036758006", volTotal: "149791838675429779745", }, { price: 0.00034991, amt: "10903667052066234563130", vol: "3815203952623454269", amtTotal: "452312482708073271321136", volTotal: "153607042628053234014", }, { price: 0.00035059, amt: "10562197544947631702552", vol: "3702977114803447505", amtTotal: "462874680253020903023688", volTotal: "157310019742856681519", }, { price: 0.00035127, amt: "10541794072549149477757", vol: "3702962424303320597", amtTotal: "473416474325570052501445", volTotal: "161012982167160002116", }, { price: 0.00035196, amt: "10839964505283434387937", vol: "3815158086789770653", amtTotal: "484256438830853486889382", volTotal: "164828140253949772769", }, { price: 0.00035265, amt: "10500550295026604907238", vol: "3702932728783504366", amtTotal: "494756989125880091796620", volTotal: "168531072982733277135", }, { price: 0.00035333, amt: "10480325106225117837726", vol: "3702918166647567295", amtTotal: "505237314232105209634346", volTotal: "172233991149380844430", }, { price: 0.00035401, amt: "10460158295034970667917", vol: "3702903646543510388", amtTotal: "515697472527140180302263", volTotal: "175936894795924354818", }, { price: 0.0003547, amt: "10756101480844670483856", vol: "3815097705412174743", amtTotal: "526453574007984850786119", volTotal: "179751992501336529561", }, { price: 0.000355, amt: "2879934000000000000000", vol: "1022376570000000000", amtTotal: "529333508007984850786119", volTotal: "180774369071336529561", }, { price: 0.00035539, amt: "10419392212908119384882", vol: "3702874294964379056", amtTotal: "539752900220892970171001", volTotal: "184477243366300908617", }, { price: 0.00035607, amt: "10399400937404414745812", vol: "3702859901246016389", amtTotal: "550152301158297384916813", volTotal: "188180103267546925006", }, { price: 0.00035675, amt: "10379467141462596339294", vol: "3702845548912938279", amtTotal: "560531768299759981256107", volTotal: "191882948816459863285", }, { price: 0.00035781, amt: "10673207910450037674513", vol: "3818930797471485007", amtTotal: "571204976210210018930620", volTotal: "195701879613931348292", }, { price: 0.00035815, amt: "10339171406073422424770", vol: "3702919927697518809", amtTotal: "581544147616283441355390", volTotal: "199404799541628867101", }, { price: 0.00035883, amt: "10319410450646547471681", vol: "3702905502200057190", amtTotal: "591863558066929988827071", volTotal: "203107705043828924291", }, { price: 0.00035887, amt: "1253970999999999795200", vol: "449999996860000051", amtTotal: "593117529066929988622271", volTotal: "203557705040688924342", }, { price: 0.00035952, amt: "10299706093934767574874", vol: "3702891118019657590", amtTotal: "603417235160864756197145", volTotal: "207260596158708581932", }, { price: 0.00036, amt: "491031000000000000000", vol: "176771040000000000", amtTotal: "603908266160864756197145", volTotal: "207437367198708581932", }, { price: 0.00036022, amt: "10591268908994289896668", vol: "3815084938649532411", amtTotal: "614499535069859046093813", volTotal: "211252452137358114343", }, ], asks_prices: [ 0.00033331, 0.0003336, 0.00033371, 0.00033439, 0.00033501, 0.00033506, 0.00033572, 0.00033638, 0.000337, 0.00033706, 0.00033773, 0.00033848, 0.00033893, 0.00033907, 0.00033934, 0.00033975, 0.00034042, 0.00034061, 0.00034109, 0.00034176, 0.00034244, 0.00034312, 0.00034379, 0.00034447, 0.00034515, 0.00034582, 0.00034649, 0.00034718, 0.00034809, 0.00034854, 0.00034922, 0.00034991, 0.00035059, 0.00035127, 0.00035196, 0.00035265, 0.00035333, 0.00035401, 0.0003547, 0.000355, 0.00035539, 0.00035607, 0.00035675, 0.00035781, 0.00035815, 0.00035883, 0.00035887, 0.00035952, 0.00036, 0.00036022, ], asks_amtTotals: [ "26466599999999999737856", "60347400000000000786432", "71489877800817400774420", "82900001904293292971822", "122234201904293285893934", "133286497438765440124271", "144316953761678803833989", "155325635540196872873465", "162513809223956873012729", "173833381174065333754584", "184798051906578645141799", "195741142503458096012774", "226827452515978096139750", "237749026624062181212249", "259810800193782182445145", "271040893678352643551479", "281918977553464164143875", "300948619498234164597507", "311805378231938450795419", "322640874470793345809679", "333782546270039087912453", "344575064903739228314494", "355346509388628001578471", "366422384688283658190129", "377151231708579242981712", "387859190708127349643612", "398546322628019364152161", "409535587833613328278263", "420180620961041407426017", "430805010237312954583534", "441408815656007036758006", "452312482708073271321136", "462874680253020903023688", "473416474325570052501445", "484256438830853486889382", "494756989125880091796620", "505237314232105209634346", "515697472527140180302263", "526453574007984850786119", "529333508007984850786119", "539752900220892970171001", "550152301158297384916813", "560531768299759981256107", "571204976210210018930620", "581544147616283441355390", "591863558066929988827071", "593117529066929988622271", "603417235160864756197145", "603908266160864756197145", "614499535069859046093813", ], asks_volTotals: [ "8821317780000000000", "20123952660000000000", "23842308926910774549", "27657649326129174254", "40834606326129174254", "44537715265574389972", "48240808917571514537", "51943887327387562395", "54366229977077844891", "58181506989788887393", "61884554591872731921", "65588475801549829728", "76124247990993108576", "79827374641180933486", "87313596266594021742", "91128922945314033350", "94832018717436447739", "101313705060244556795", "105016785691516572049", "108719851226417244234", "112535115125740675291", "116238150146541687601", "119941170204697043840", "123756387388505765936", "127459377202461261011", "131162352185922025377", "134865312382156233917", "138680468072798172212", "142385809710107234072", "146088831602957234796", "149791838675429779745", "153607042628053234014", "157310019742856681519", "161012982167160002116", "164828140253949772769", "168531072982733277135", "172233991149380844430", "175936894795924354818", "179751992501336529561", "180774369071336529561", "184477243366300908617", "188180103267546925006", "191882948816459863285", "195701879613931348292", "199404799541628867101", "203107705043828924291", "203557705040688924342", "207260596158708581932", "207437367198708581932", "211252452137358114343", ], asks_amtTotal: "614499535069859046093813", asks_volTotal: "211252452137358114343", }; //v3/amm/balance?poolAddress=0xfEB069407df0e1e4B365C10992F1bc16c078E34b export const ammPoolSnapshot: sdk.AmmPoolSnapshot = { poolName: "AMM-LRC-ETH", poolAddress: "0x18920d6e6fb7ebe057a4dd9260d6d95845c95036", pooled: [ { tokenId: 1, volume: "11198097977488137000000000", }, { tokenId: 0, volume: "3725368050950874300000", }, ], lp: { tokenId: 83, volume: "34138981282200", }, risky: false, }; //v3/amm/balance?poolAddress=0xfEB069407df0e1e4B365C10992F1bc16c078E34b export const ammPool = { poolName: "AMM-LRC-ETH", poolAddress: "0x18920d6e6fb7ebe057a4dd9260d6d95845c95036", pooled: [ { tokenId: 1, volume: "11215027899488137000000000" }, { tokenId: 0, volume: "3720052711450874300000" }, ], lp: { tokenId: 83, volume: "34141365482200" }, risky: false, }; //v3/mix/ticker?market=LRC-ETH export const ticker = { tickers: [ [ "COMBINE-LRC-ETH", "1655625077879", "2429371927000000000000000", "814655525450000000000", "0.00035052", "0.00035222", "0.00032201", "0.00033367", "772", "", "", "", "", ], ], }; //v3/user/orderUserRateAmount?accountId=10427&market=LRC-ETH export const userAmount = { "LRC-ETH": { LRC: { tokenSymbol: "LRC", baseOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, tradeCost: "231046501539337141", }, ETH: { tokenSymbol: "ETH", baseOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, tradeCost: "75150737796750", }, }, "AMM-LRC-ETH": { LRC: { tokenSymbol: "LRC", baseOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "266240681576144834931", makerRate: 0, takerRate: 10, }, tradeCost: "231046501539337141", }, ETH: { tokenSymbol: "ETH", baseOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, userOrderInfo: { minAmount: "86598080986525339", makerRate: 0, takerRate: 10, }, tradeCost: "75150737796750", }, }, }; export const TokenMapMockSwap = { ETH: { type: "ETH", tokenId: 0, symbol: "ETH", name: "Ethereum", address: "0x0000000000000000000000000000000000000000", decimals: 18, precision: 7, precisionForOrder: 3, orderAmounts: { minimum: "1700000000000000", maximum: "1000000000000000000000", dust: "200000000000000", }, luckyTokenAmounts: { minimum: "50000000000000", maximum: "1000000000000000000000", dust: "50000000000000", }, fastWithdrawLimit: "100000000000000000000", gasAmounts: { distribution: "85000", deposit: "110000", }, enabled: true, isLpToken: false, tradePairs: ["LRC"], }, LRC: { type: "ERC20", tokenId: 1, symbol: "LRC", name: "Loopring", address: "0xbbbbca6a901c926f240b89eacb641d8aec7aeafd", 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: "150000", }, enabled: true, isLpToken: false, tradePairs: ["ETH"], }, }; export const MAPFEEBIPS = 63; ``` ================================================ FILE: src/tests/demo/erc20Trade/orderERC20.test.ts ================================================ import { AMM_MAP, DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, signatureKeyPairMock, TOKEN_INFO, } from "../../MockData"; import { deepMock, ammPool, ticker, userAmount, MAPFEEBIPS, marketArray, marketMap, TokenMapMockSwap, ammPoolSnapshot as ammPoolSnapshotMock, } from "../../MockSwapData"; import * as sdk from "../../../index"; import BigNumber from "bignumber.js"; let apiKey = ""; // const { tokenMap } = TOKEN_INFO; // const ammMap = AMM_MAP; const calculateSwap = ( sellSymbol = "LRC", buySymbol = "ETH", isInputSellToBuy: boolean, inputValue: number, // user Input value no decimal, _slippage = 0.1, // MOCK value amountMap: { [key: string]: any } = userAmount, market: string = deepMock.symbol, // close = ticker.tickers[7], depth: any = deepMock, ammPoolSnapshot: sdk.AmmPoolSnapshot = ammPoolSnapshotMock, tokenMap: sdk.LoopringMap = TokenMapMockSwap, ammMap: { [key: string]: any } = AMM_MAP ) => { let calcFor100USDAmount, calcForMinCost, calcForPriceImpact; if (depth && market && tokenMap) { const sellToken = tokenMap[sellSymbol]; const buyToken = tokenMap[buySymbol]; const isInputSellOutputBuy = isInputSellToBuy; let input: any = inputValue; console.log( "sellToken: Symbol ", sellSymbol, "buyToken: Symbol", buySymbol, "is Input Sell Output Buy:", isInputSellOutputBuy, "input value", input ); input = input === undefined || isNaN(Number(input)) ? 0 : Number(input); let slippage = sdk.toBig(_slippage).times(100).toString(); let totalFee = undefined; let feeTakerRate = undefined; let feeBips = undefined; let takerRate = undefined; let buyMinAmtInfo = undefined; let sellMinAmtInfo = undefined; let tradeCost = undefined; let basePrice = undefined; let maxFeeBips = MAPFEEBIPS; let minAmt = undefined; if (amountMap && amountMap[market] && ammMap) { console.log(`amountMap[${market}]:`, amountMap[market]); const ammMarket = `AMM-${market}`; const amountMarket = amountMap[market]; // userAmount from LRC-ETH(Market) buyMinAmtInfo = amountMarket[buySymbol]; sellMinAmtInfo = amountMarket[sellSymbol]; console.log( `buyMinAmtInfo: ${market}, ${buySymbol}`, buyMinAmtInfo, `sellMinAmtInfo: ${market}, ${sellSymbol}`, sellMinAmtInfo ); feeBips = ammMap[ammMarket] ? ammMap[ammMarket].feeBips : 1; feeTakerRate = amountMarket[buySymbol] && amountMarket[buySymbol].userOrderInfo.takerRate; tradeCost = amountMarket[buySymbol].tradeCost; /** @description for charge fee calc, calcFor100USDAmount * Loopring market consider buyToken value small then max(buyMinAmtInfo.userOrderInfo.minAmount,buyToken.orderAmounts.dust) is a small order, * the fee will take the Max(tradeCost,userTakeRate) * use the buyMinAmount Input calc the selltoken value, * please read Line:321 * **/ const minAmountInput = BigNumber.max( buyMinAmtInfo.userOrderInfo.minAmount, buyToken.orderAmounts.dust ) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div("1e" + buyToken.decimals) .toString(); calcFor100USDAmount = sdk.getOutputAmount({ input: minAmountInput, sell: sellSymbol, buy: buySymbol, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: slippage, }); console.log( "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: sellSymbol, buy: buySymbol, isAtoB: true, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: "10", }); basePrice = sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput); console.log( "calcForPriceImpact input: ", sellMinAmtInput, ", output: ", sdk.toBig(calcForPriceImpact?.output).div(sellMinAmtInput).toNumber(), ", calcForPriceImpact:", calcForPriceImpact?.amountBOutSlip?.minReceivedVal, ", calcForPriceImpact basePrice: ", basePrice.toNumber() ); /**** calc for mini Cost ****/ //minCostBuyToken = max(dustBuyToken, tradeCostETH/maxAllowBips) const dustToken = buyToken; let minCostBuyTokenInput = BigNumber.max( sdk.toBig(tradeCost).times(2), //maxAllowBips = 50% tradeCostETH/50% dustToken.orderAmounts.dust ); const tradeCostInput = sdk .toBig(minCostBuyTokenInput) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .div("1e" + dustToken.decimals) .toString(); console.log( `tradeCost: ${tradeCost}*2:`, sdk.toBig(tradeCost).times(2).toString(), "buyToken.orderAmounts.dust", buyToken.orderAmounts.dust, "minCostBuyToken:", minCostBuyTokenInput.toString(), `calcForMinCostInput, with slippage:${slippage}`, sdk .toBig(minCostBuyTokenInput ?? 0) .div(sdk.toBig(1).minus(sdk.toBig(slippage).div(10000))) .toString(), "calcForMinCost, Input", tradeCostInput ); calcForMinCost = sdk.getOutputAmount({ input: tradeCostInput, sell: sellSymbol, buy: buySymbol, isAtoB: false, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", slipBips: slippage, }); //add additionally 10% tolerance for minimum quantity user has to set on sell Token /** * @output: minAmt for UI * this value mini-order Sell token amount (show on the UI for available order check) * setSellMinAmt(minAmt.toString()); */ minAmt = BigNumber.max( sellToken.orderAmounts.dust, calcForMinCost?.amountS ?? 0 ).times(1.1); console.log( "UI show mini-order Sell token amount:", minAmt.toString(), sdk .toBig(minAmt) .div("1e" + sellToken.decimals) .toString() ); console.log( `calcFor100USDAmount.amountS`, sdk .toBig(calcFor100USDAmount?.amountS ?? 0) .div("1e" + sellToken.decimals) .toString(), "calcForMinCost.amountS", sdk .toBig(calcForMinCost?.amountS ?? 0) .div("1e" + sellToken.decimals) .toString() ); } const calcTradeParams = sdk.getOutputAmount({ input: input.toString(), sell: sellSymbol, buy: buySymbol, isAtoB: isInputSellOutputBuy, marketArr: marketArray as string[], tokenMap: tokenMap as any, marketMap: marketMap as any, depth, ammPoolSnapshot: ammPoolSnapshot, feeBips: feeBips ? feeBips.toString() : 1, takerRate: "0", // for new calc miniReceive will minus fee, so takeRate can fix as 0 slipBips: slippage, }); const minSymbol = buySymbol; const tradePrice = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(isInputSellOutputBuy ? 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"); } console.log( "calcTradeParams input:", input.toString(), ", calcTradeParams Price: ", sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .div(input.toString()) .toNumber(), `isAtoB mean isInputSellOutputBuy:${isInputSellOutputBuy}, ${ isInputSellOutputBuy ? 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 ); if ( tradeCost && calcTradeParams && calcTradeParams.amountBOutSlip?.minReceived && feeTakerRate ) { let value = sdk .toBig(calcTradeParams.amountBOutSlip?.minReceived) .times(feeTakerRate) .div(10000); console.log( "input Accounts", calcTradeParams?.amountS, "100 U Amount Sell:", calcFor100USDAmount?.amountS ); let validAmt = !!( calcTradeParams?.amountS && calcFor100USDAmount?.amountS && sdk.toBig(calcTradeParams?.amountS).gte(calcFor100USDAmount.amountS) ); let totalFeeRaw; console.log( `${minSymbol} tradeCost:`, tradeCost, "useTakeRate Fee:", value.toString(), "calcFor100USDAmount?.amountS:", calcFor100USDAmount?.amountS, `is setup minTrade amount, ${calcFor100USDAmount?.amountS}:`, validAmt ); if (!validAmt) { if (sdk.toBig(tradeCost).gte(value)) { totalFeeRaw = sdk.toBig(tradeCost); } else { totalFeeRaw = value; } console.log( "maxFeeBips update for tradeCost before value:", maxFeeBips, "totalFeeRaw", totalFeeRaw.toString() ); maxFeeBips = Math.ceil( totalFeeRaw .times(10000) .div(calcTradeParams.amountBOutSlip?.minReceived) .toNumber() ); console.log("maxFeeBips update for tradeCost after value:", maxFeeBips); } else { totalFeeRaw = sdk.toBig(value); } /** * totalFee */ totalFee = totalFeeRaw .div("1e" + tokenMap[minSymbol].decimals) .toString(); /** @output: UI * getValuePrecisionThousand( * totalFeeRaw.div("1e" + tokenMap[minSymbol].decimals).toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ tradeCost = sdk .toBig(tradeCost) // @ts-ignore .div("1e" + tokenMap[minSymbol].decimals) .toString(); /** @output: UI code with precision * getValuePrecisionThousand( * sdk * .toBig(tradeCost) * .div("1e" + tokenMap[minSymbol].decimals) * .toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ console.log("totalFee view value:", totalFee + " " + minSymbol); console.log("tradeCost view value:", tradeCost + " " + minSymbol); } const minimumReceived = sdk .toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) .minus(totalFee ?? 0) .toString(); console.log("minimumReceived:", minimumReceived); /** @output: UI code with precision * getValuePrecisionThousand( * toBig(calcTradeParams?.amountBOutSlip?.minReceivedVal ?? 0) * .minus(totalFee) * .toString(), * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * tokenMap[minSymbol].precision, * false, * { floor: true } * ); */ let priceImpactView: any = calcTradeParams?.priceImpact ? parseFloat(calcTradeParams?.priceImpact) * 100 : undefined; console.log("priceImpact view:", priceImpactView + "%"); // @output: UI code with color alert // const priceImpactObj = getPriceImpactInfo(calcTradeParams); // const _tradeCalcData: Partial> = { // priceImpact: priceImpactObj.value.toString(), // priceImpactColor: priceImpactObj.priceImpactColor, // minimumReceived: !minimumReceived?.toString().startsWith("-") // ? minimumReceived // : undefined, // fee: totalFee, // feeTakerRate, // tradeCost, // }; console.log( `isInputSellOutputBuy:${isInputSellOutputBuy}`, `output ${isInputSellOutputBuy ? "Buy" : "Sell"}`, calcTradeParams?.output ); return { market, feeBips, takerRate, sellMinAmtInfo: sellMinAmtInfo as any, buyMinAmtInfo: buyMinAmtInfo as any, totalFee, maxFeeBips, feeTakerRate, tradeCost, minimumReceived, calcTradeParams, minAmt, }; } }; describe("orderERC20", function () { beforeEach(async () => { // deepMock, ammPool, ticker should update every 15's, useAmount should update every 15 minutes jest.setTimeout(DEFAULT_TIMEOUT * 3); LOOPRING_EXPORTED_ACCOUNT.gasPrice = ( await LoopringAPI.exchangeAPI.getGasPrice() ).gasPrice; }, DEFAULT_TIMEOUT); it("Swap_SellLRC_BuyETH_USER_INPUT_LRC_miniOrder", async () => { const inputValue = 5.6; const slippage = 0.1; console.log( `output Swap_SellLRC_BuyETH_USER_INPUT_LRC_miniOrder Input value: ${inputValue},slippage: ${slippage} `, calculateSwap( "LRC", //sellSymbol "ETH", //buySymbol true, //isInputSellToBuy inputValue, // user Input value no decimal, slippage //_slippage ) ); }); it("Swap_SellLRC_BuyETH_USER_INPUT_LRC_lessThenMiniOrder", async () => { const inputValue = 1; const slippage = 0.1; console.log( `output Swap_SellLRC_BuyETH_USER_INPUT_LRC_miniOrder Input value: ${inputValue},slippage: ${slippage} `, calculateSwap( "LRC", //sellSymbol "ETH", //buySymbol true, //isInputSellToBuy inputValue, // user Input value no decimal, slippage //_slippage ) ); }); it("Swap_SellLRC_BuyETH_USER_INPUT_LRC_LargeOrder", async () => { const inputValue = 100000; const slippage = 0.1; console.log( `output Swap_SellLRC_BuyETH_USER_INPUT_LRC_miniOrder Input value: ${inputValue},slippage: ${slippage} `, calculateSwap( "LRC", //sellSymbol "ETH", //buySymbol true, //isInputSellToBuy inputValue, // user Input value no decimal, slippage //_slippage ) ); }); it("Swap_SellLRC_BuyETH_USER_INPUT_LRC_Slippage50", async () => { const inputValue = 5.6; const slippage = 50; console.log( `output Swap_SellLRC_BuyETH_USER_INPUT_LRC_miniOrder Input value: ${inputValue},slippage: ${ slippage * 50 } `, calculateSwap( "LRC", //sellSymbol "ETH", //buySymbol true, //isInputSellToBuy inputValue, // user Input value no decimal, slippage //_slippage ) ); }); it("Swap_SellLRC_BuyETH_USER_INPUT_ETH_miniOrder", async () => { const inputValue = 0.0019; const slippage = 0.1; console.log( `output Swap_SellLRC_BuyETH_USER_INPUT_LRC_miniOrder Input value: ${inputValue},slippage: ${ slippage * 50 } `, calculateSwap( "LRC", //sellSymbol "ETH", //buySymbol false, //isInputSellToBuy inputValue, // user Input value no decimal, slippage //_slippage ) ); }); it("Swap_SellETH_BuyLRC_USER_INPUT_ETH", async () => { const inputValue = 0.0017999; const slippage = 0.1; console.log( `output Swap_SellLRC_BuyETH_USER_INPUT_LRC_miniOrder Input value: ${inputValue},slippage: ${ slippage * 50 } `, calculateSwap( "ETH", //sellSymbol "LRC", //buySymbol false, //isInputSellToBuy inputValue, // user Input value no decimal, slippage //_slippage ) ); }); it( "RealOrderLRC-ETH_AtoB", async () => { try { // MOCK Data const buy = "ETH", sell = "LRC", MARKET = "LRC-ETH", AMM_MARKET = "AMM-LRC-ETH", isAtoB = true, slippage = "50"; // Step 1. get apikey & eddsaKey const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const eddsaKey = await signatureKeyPairMock(accInfo); apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ) ).apiKey; // Step 2. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, sellTokenId: TOKEN_INFO.tokenMap[sell].tokenId, }, apiKey ); // Step 3. get user AmountMap, which decided user minimum order const amountMap = { [AMM_MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: AMM_MAP[AMM_MARKET].market, }, apiKey ) ).amountMap, [MARKET]: ( await LoopringAPI.userAPI.getMinimumTokenAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: MARKET, }, apiKey ) ).amountMap, }; // Step 4. depth, ammPoolSnapshot, tickMap const [{ depth }, { ammPoolSnapshot }] = await Promise.all([ LoopringAPI.exchangeAPI.getMixDepth({ market: AMM_MAP["AMM-LRC-ETH"].market, }), LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: AMM_MAP["AMM-LRC-ETH"].address, }), ]); // Step 5. check MinAmt see log and calc mini receive and ouput value & maxfeeBips & priceImpact // @ts-ignore const { calcTradeParams, maxFeeBips, minimumReceived } = calculateSwap( sell, buy, isAtoB, 10, // user Input value no decimal 10 lrc, 0.1, amountMap, "LRC-ETH", // close = ticker.tickers[7], depth, ammPoolSnapshot, TOKEN_INFO.tokenMap, AMM_MAP ); console.log( "calcTradeParams", calcTradeParams, "minimumReceived", minimumReceived, "maxFeeBips", maxFeeBips ); // Step 6. submit const response: { hash: string } | any = await LoopringAPI.userAPI.submitOrder( { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, storageId: storageId.orderId, sellToken: { tokenId: TOKEN_INFO.tokenMap[sell].tokenId, volume: calcTradeParams?.amountS as string, }, buyToken: { tokenId: TOKEN_INFO.tokenMap[buy].tokenId, volume: calcTradeParams?.amountBOutSlip.minReceived as string, }, allOrNone: false, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, maxFeeBips: maxFeeBips, fillAmountBOrS: false, // amm only false tradeChannel: calcTradeParams?.exceedDepth ? sdk.TradeChannel.BLANK : sdk.TradeChannel.MIXED, orderType: calcTradeParams?.exceedDepth ? sdk.OrderType.ClassAmm : sdk.OrderType.TakerOnly, eddsaSignature: "", }, eddsaKey.sk, apiKey ); console.log("submitOrder", response); } catch (err) { console.log(err); } }, DEFAULT_TIMEOUT ); // // it( // "RealOrderETH-LRC_BtoA", // async () => { // try { // // before user should have apikey, please check get apikey // const buy = "ETH", // sell = "LRC", // MARKET = "LRC-ETH", // AMM_MARKET = "AMM-LRC-ETH", // slippage = "50"; // const isAtoB = false; // // // Step 1. get apikey & eddsaKey // const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ // owner: LOOPRING_EXPORTED_ACCOUNT.address, // }); // const eddsaKey = await signatureKeyPairMock(accInfo); // apiKey = ( // await LoopringAPI.userAPI.getUserApiKey( // { // accountId: accInfo.accountId, // }, // eddsaKey.sk // ) // ).apiKey; // // // Step 2. storageId // const storageId = await LoopringAPI.userAPI.getNextStorageId( // { // accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, // sellTokenId: TOKEN_INFO.tokenMap[sell].tokenId, // }, // apiKey // ); // // // Step 3. get user AmountMap, which decided user minimum order // const amountMap = { // [AMM_MARKET]: ( // await LoopringAPI.userAPI.getMinimumTokenAmt( // { // accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, // market: AMM_MAP[AMM_MARKET].market, // }, // apiKey // ) // ).amountMap, // [MARKET]: ( // await LoopringAPI.userAPI.getMinimumTokenAmt( // { // accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, // market: MARKET, // }, // apiKey // ) // ).amountMap, // }; // // // Step 4. depth, ammPoolSnapshot ,tickMap // const [{ depth }, { ammPoolSnapshot }] = await Promise.all([ // LoopringAPI.exchangeAPI.getMixDepth({ // market: AMM_MAP["AMM-LRC-ETH"].market, // }), // LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ // poolAddress: AMM_MAP["AMM-LRC-ETH"].address, // }), // ]); // // // Step 5. check MinAmt // const amount: sdk.LoopringMap = // amountMap[AMM_MARKET] ?? amountMap[MARKET]; // // let buyMinAmtInfo = amount[buy]; // // let sellMinAmtInfo = amount[sell]; // // let takerRate = buyMinAmtInfo // ? buyMinAmtInfo.userOrderInfo.takerRate // : 0; // // const minAmountInput = buyMinAmtInfo.userOrderInfo.minAmount; // // // Step 6. calcTradeParams // const calcTradeParams = sdk.getOutputAmount({ // input: LOOPRING_EXPORTED_ACCOUNT.tradeETHValue.toString(), // sell, // buy, // isAtoB, // marketArr: ["LRC-ETH", "ETH-USDT", "DAI-USDT", "USDC-ETH"], // tokenMap: TOKEN_INFO.tokenMap, // marketMap: TOKEN_INFO.marketMap, // depth, // ammPoolSnapshot: ammPoolSnapshot, // feeBips: AMM_MAP["AMM-LRC-ETH"].feeBips.toString(), // takerRate: takerRate ? takerRate.toString() : "0", // slipBips: slippage, // }); // console.log( // "Buy", // ",ETH:", // LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), // ",minAmountInput ETH:", // minAmountInput, // ",LRC:", // calcTradeParams?.amountBOutSlip?.minReceivedVal // ); // // const response: { hash: string } | any = // await LoopringAPI.userAPI.submitOrder( // { // exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, // accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, // storageId: storageId.orderId, // sellToken: { // tokenId: TOKEN_INFO.tokenMap[sell].tokenId, // volume: calcTradeParams?.amountS as string, // }, // buyToken: { // tokenId: TOKEN_INFO.tokenMap[buy].tokenId, // volume: calcTradeParams?.amountBOutSlip.minReceived as string, // }, // allOrNone: false, // validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, // maxFeeBips: 63, // fillAmountBOrS: false, // amm only false // tradeChannel: calcTradeParams?.exceedDepth // ? sdk.TradeChannel.BLANK // : sdk.TradeChannel.MIXED, // orderType: calcTradeParams?.exceedDepth // ? sdk.OrderType.ClassAmm // : sdk.OrderType.TakerOnly, // eddsaSignature: "", // }, // eddsaKey.sk, // apiKey // ); // console.log("submitOrder", response); // } catch (err) { // console.log(err); // } // }, // DEFAULT_TIMEOUT // ); }); export default {}; ================================================ FILE: src/tests/demo/exchange/exchange.md ================================================ # Loopring Exchange Definition: Loopring Dex Main API for get Exchange Information, L2 Block, ERC20 Token Information, AMM Information, Market Config and so on static and dynamic information *** ## getExchangeInfo ```ts const response = await LoopringAPI.exchangeAPI.getExchangeInfo(); console.log(response); ``` *** ## getTokens ```ts const {tokensMap, coinMap, totalCoinMap, idIndex, addressIndex} = await LoopringAPI.exchangeAPI.getTokens(); console.log( "tokenMap:", tokensMap, coinMap, totalCoinMap, idIndex, addressIndex ); ``` *** ## getMixMarkets ```ts const {markets, pairs, tokenArr, tokenArrStr, marketArr, marketArrStr} = await LoopringAPI.exchangeAPI.getMixMarkets(); console.log("markets:", markets); console.log("pairs:", pairs); console.log("tokenArr:", tokenArr); console.log("tokenArrStr:", tokenArrStr); console.log("marketArr", marketArr); console.log("marketArrStr", marketArrStr); ``` *** ## getAmmPoolConf ```ts const response = await LoopringAPI.ammpoolAPI.getAmmPoolConf(); console.log(response.ammpools); console.log(response.pairs); ``` *** ## getAvailableBroker ```ts const result = await LoopringAPI.exchangeAPI.getAvailableBroker(); console.log(result); ``` *** ## getTokenPrices ```ts const response = await LoopringAPI.walletAPI.getTokenPrices({ token: TOKEN_INFO.tokenMap.LRC.address, }); console.log(response); ``` *** ## getLatestTokenPrices ```ts const response = await LoopringAPI.walletAPI.getLatestTokenPrices(); console.log(response); ``` *** ## getLatestTokenPrices_cny ```ts const response = await LoopringAPI.walletAPI.getLatestTokenPrices({ currency: sdk.Currency.cny, }); console.log(response); ``` *** ## getWithdrawalAgents ```ts const response = await LoopringAPI.exchangeAPI.getWithdrawalAgents({ tokenId: 1, amount: "10000000000", }); console.log(response); ``` *** ## getCandlestick ```ts const response = await LoopringAPI.exchangeAPI.getCandlestick({ market: "LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); ``` *** ## getAccountServices ```ts const response = await LoopringAPI.exchangeAPI.getAccountServices({}); console.log(response); ``` *** ## getExchangeFeeInfo ```ts const response = await LoopringAPI.exchangeAPI.getExchangeFeeInfo(); console.log(response); console.log( response.raw_data[sdk.VipCatergory.ORDERBOOK_TRADING_FEES_STABLECOIN] ); ``` *** ## getProtocolPortrait ```ts const response = await LoopringAPI.exchangeAPI.getProtocolPortrait(); console.log(response); ``` *** ## getRecommendedMarkets ```ts const response = await LoopringAPI.exchangeAPI.getRecommendedMarkets(); console.log(response); ``` *** ## getGasPrice ```ts const response = await LoopringAPI.exchangeAPI.getGasPrice(); console.log(response); ``` *** ## getGasPriceRange ```ts const response = await LoopringAPI.exchangeAPI.getGasPriceRange(); console.log(response); ``` *** ## getMarketTrades ```ts const response = await LoopringAPI.exchangeAPI.getMarketTrades({ market: "ETH-USDT", }); console.log(response.raw_data.trades); ``` *** ## getRelayerCurrentTime ```ts const response = await LoopringAPI.exchangeAPI.getRelayerCurrentTime(); console.log(response); ``` *** ## getFiatPriceUSD ```ts const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "USD", }); console.log(response); ``` *** ## getFiatPriceCNY ```ts const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "CNY", }); console.log(response); ``` *** ## getMarkets ```ts const response = await LoopringAPI.exchangeAPI.getMarkets(); console.log(response); console.log(response.pairs.LRC.tokenList); console.log( "hasMarket LRC-ETH:", sdk.hasMarket(response.marketArr, "LRC-ETH") ); console.log( "market 1:", sdk.getExistedMarket(response.marketArr, "LRC", "ETH") ); console.log( "market 2:", sdk.getExistedMarket(response.marketArr, "ETH", "LRC") ); ``` *** ## getDepth ```ts const response = await LoopringAPI.exchangeAPI.getDepth({ market: "LRC-ETH", }); console.log(response); ``` *** ## getTicker ```ts const response = await LoopringAPI.exchangeAPI.getTicker({ market: "LRC-ETH", }); console.log(response); ``` *** ## getAllTickers ```ts const response = await LoopringAPI.exchangeAPI.getAllTickers(); console.log(response); ``` *** ## getMixDepth ```ts const response = await LoopringAPI.exchangeAPI.getMixDepth({ market: "LRC-ETH", }); console.log(response); console.log(response.depth.bids); ``` *** ## getMixTicker ```ts const response = await LoopringAPI.exchangeAPI.getMixTicker({ market: ["LRC-ETH", "ETH-USDC", "DAI-USDT"].join(","), }); console.log(response.tickMap["DAI-USDT"]); ``` *** ## getAllMixTickers ```ts const response: any = await LoopringAPI.exchangeAPI.getAllMixTickers(); console.log(response?.tickMap); ``` *** ## getMixCandlestickAMM ```ts const response = await LoopringAPI.exchangeAPI.getMixCandlestick({ market: "AMM-LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); ``` }); ================================================ FILE: src/tests/demo/exchange/exchange.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; import { LoopringMap, MarketInfo, TokenAddress, TokenInfo, TokenRelatedInfo, } from "../../../index"; import { concatSig } from "eth-sig-util"; describe("exchangeMain", function () { it( "getExchangeInfo", async () => { const response = await LoopringAPI.exchangeAPI.getExchangeInfo(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getTokens", async () => { const { tokensMap, coinMap, totalCoinMap, idIndex, addressIndex } = await LoopringAPI.exchangeAPI.getTokens(); console.log( "tokenMap:", tokensMap, coinMap, totalCoinMap, idIndex, addressIndex ); }, DEFAULT_TIMEOUT ); it( "getMixMarkets", async () => { const { markets, pairs, tokenArr, tokenArrStr, marketArr, marketArrStr } = await LoopringAPI.exchangeAPI.getMixMarkets(); console.log("markets:", markets); console.log("pairs:", pairs); console.log("tokenArr:", tokenArr); console.log("tokenArrStr:", tokenArrStr); console.log("marketArr", marketArr); console.log("marketArrStr", marketArrStr); }, DEFAULT_TIMEOUT ); it( "getAmmPoolConf", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolConf(); console.log(response.ammpools); console.log(response.pairs); }, DEFAULT_TIMEOUT ); it( "getAvailableBroker", async () => { const result = await LoopringAPI.exchangeAPI.getAvailableBroker({ type: 0, }); console.log(result); }, DEFAULT_TIMEOUT ); it( "getTokenPrices", async () => { const response = await LoopringAPI.walletAPI.getTokenPrices({ token: TOKEN_INFO.tokenMap.LRC.address, }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getLatestTokenPrices", async () => { const response = await LoopringAPI.walletAPI.getLatestTokenPrices(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getLatestTokenPrices_cny", async () => { const response = await LoopringAPI.walletAPI.getLatestTokenPrices({ currency: sdk.Currency.cny, }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getWithdrawalAgents", async () => { const response = await LoopringAPI.exchangeAPI.getWithdrawalAgents({ tokenId: 1, amount: "10000000000", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getCandlestick", async () => { const response = await LoopringAPI.exchangeAPI.getCandlestick({ market: "LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getAccountServices", async () => { const response = await LoopringAPI.exchangeAPI.getAccountServices({}); console.log(response); }, DEFAULT_TIMEOUT ); it( "getExchangeFeeInfo", async () => { const response = await LoopringAPI.exchangeAPI.getExchangeFeeInfo(); console.log(response); console.log( response.raw_data[sdk.VipCatergory.ORDERBOOK_TRADING_FEES_STABLECOIN] ); }, DEFAULT_TIMEOUT ); it( "getProtocolPortrait", async () => { const response = await LoopringAPI.exchangeAPI.getProtocolPortrait(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getRecommendedMarkets", async () => { const response = await LoopringAPI.exchangeAPI.getRecommendedMarkets(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getGasPrice", async () => { const response = await LoopringAPI.exchangeAPI.getGasPrice(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getGasPriceRange", async () => { const response = await LoopringAPI.exchangeAPI.getGasPriceRange(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getMarketTrades", async () => { const response = await LoopringAPI.exchangeAPI.getMarketTrades({ market: "ETH-USDT", }); console.log(response.raw_data.trades); }, DEFAULT_TIMEOUT ); it( "getRelayerCurrentTime", async () => { const response = await LoopringAPI.exchangeAPI.getRelayerCurrentTime(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getFiatPriceUSD", async () => { const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "USD", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getFiatPriceCNY", async () => { const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "CNY", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getMarkets", async () => { const response = await LoopringAPI.exchangeAPI.getMarkets(); console.log(response); console.log(response.pairs.LRC.tokenList); console.log( "hasMarket LRC-ETH:", sdk.hasMarket(response.marketArr, "LRC-ETH") ); console.log( "market 1:", sdk.getExistedMarket(response.marketArr, "LRC", "ETH") ); console.log( "market 2:", sdk.getExistedMarket(response.marketArr, "ETH", "LRC") ); }, DEFAULT_TIMEOUT ); it( "getDepth", async () => { const response = await LoopringAPI.exchangeAPI.getDepth({ market: "LRC-ETH", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getTicker", async () => { const response = await LoopringAPI.exchangeAPI.getTicker({ market: "LRC-ETH", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getAllTickers", async () => { const response = await LoopringAPI.exchangeAPI.getAllTickers(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getMixDepth", async () => { const response = await LoopringAPI.exchangeAPI.getMixDepth({ market: "LRC-ETH", }); console.log(response); console.log(response.depth.bids); }, DEFAULT_TIMEOUT ); it( "getMixTicker", async () => { const response = await LoopringAPI.exchangeAPI.getMixTicker({ market: ["LRC-ETH", "ETH-USDC", "DAI-USDT"].join(","), }); console.log(response.tickMap["DAI-USDT"]); }, DEFAULT_TIMEOUT ); it( "getAllMixTickers", async () => { const response: any = await LoopringAPI.exchangeAPI.getAllMixTickers(); console.log(response?.tickMap); }, DEFAULT_TIMEOUT ); it( "getMixCandlestickAMM", async () => { const response = await LoopringAPI.exchangeAPI.getMixCandlestick({ market: "AMM-LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/exchange/webSocket.md ================================================ #WebSocket Command Definition: Loopring L2 websocket ### Loopring L2 websocket topic type: WsTopicType - `account` - `orderbook` - `mixorder` - `mixtrade` - `ticker` - `candlestick` - `ammpool` ## getWsKey (required by account related socket) ```ts const response = await LoopringAPI.wsAPI.getWsKey(); console.log(response); ``` ## getOrderBookArg ```ts const arg1 = sdk.getMixOrderArg({ market: "LRC-ETH", level: 50 }); console.log(arg1); const arg2 = sdk.getOrderBookArg({ market: "LRC-ETH", level: 50, count: 40, snapshot: false, }); console.log(arg2); ``` ================================================ FILE: src/tests/demo/exchange/webSocket.test.ts ================================================ import * as sdk from "../../../index"; import { DEFAULT_TIMEOUT, LoopringAPI } from "../../MockData"; /** * @define WsTopicType * `account`, * `order`, * `orderbook`, * `mixorder`, * `trade`, * `ticker`, * `candlestick`, * `ammpool`, * */ describe("websocket", function () { it( "getWsKey", async () => { const response = await LoopringAPI.wsAPI.getWsKey(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getOrderBookArg", async () => { const arg1 = sdk.getMixOrderArg({ market: "LRC-ETH", level: 50 }); console.log(arg1); const arg2 = sdk.getOrderBookArg({ market: "LRC-ETH", level: 50, count: 40, snapshot: false, }); console.log(arg2); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/transfer/transferERC20.md ================================================ # Transfer ERC20 Definition: Send ERC20 tokens to other account on Loopring L2, > trade value should with decimals `sdk.toBig(value).times("1e" + TOKEN_INFO.tokenMap.LRC.decimals)` *** ## Step 1. get account Info const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); const LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo; ```ts const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apikey ```ts const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const { userBalances } = await LoopringAPI.userAPI.getUserBalances( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, tokens: "" }, apiKey ); ``` *** ## Step 4. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey ); console.log("storageId:", storageId); ``` *** ## Step 5. get fee ```ts const fee = await LoopringAPI.userAPI.getOffchainFeeAmt({ accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.TRANSFER, }, apiKey); console.log("fee:", fee); ``` *** ## Step 6. transfer ```ts const transferResult = await LoopringAPI.userAPI.submitInternalTransfer({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: accInfo.owner, payerId: accInfo.accountId, payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, payeeId: LOOPRING_EXPORTED_ACCOUNT.accountId2, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, /* * when payPayeeUpdateAccount = ture, will help payee active account, * maxFee from should: * const fee = await LoopringAPI.userAPI.getOffchainFeeAmt( * { * accountId: accInfo.accountId, * requestType: sdk.OffchainFeeReqType.TRANSFER_AND_UPDATE_ACCOUNT, * }, * apiKey * ); */ payPayeeUpdateAccount: false, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Trezor, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log("transferResult:", transferResult); ``` ================================================ FILE: src/tests/demo/transfer/transferERC20.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, TOKEN_INFO, signatureKeyPairMock, web3, } from "../../MockData"; import * as sdk from "../../../index"; import * as sign_tools from "../../../api/sign/sign_tools"; import { myLog } from "../../../utils/log_tools"; describe("Transfer", function () { it( "submitInternalTransfer", async () => { /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. get storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey ); console.log("storageId:", storageId); // Step 5. get fee const fee = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.TRANSFER, }, apiKey ); console.log("fee:", fee); const request = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: accInfo.owner, payerId: accInfo.accountId, payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, payeeId: LOOPRING_EXPORTED_ACCOUNT.accountId2, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, /* * when payPayeeUpdateAccount = ture, will help payee active account, * maxFee from should: * const fee = await LoopringAPI.userAPI.getOffchainFeeAmt( * { * accountId: accInfo.accountId, * requestType: sdk.OffchainFeeReqType.TRANSFER_AND_UPDATE_ACCOUNT, * }, * apiKey * ); */ payPayeeUpdateAccount: false, }; const hash = sign_tools.get_EddsaSig_Transfer(request, "").hash; myLog("hash", hash); // Step 6. transfer const transferResult = await LoopringAPI.userAPI.submitInternalTransfer({ request, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log("transferResult:", transferResult); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/transfer/transferNFT.md ================================================ # Transfer NFT Definition: Send NFT to other account on Loopring L2 *** ## Step 1. get account Info ```ts const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); const LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo; const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); ``` *** ## Step 2. get eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. get apiKey ```ts const { apiKey } = await LoopringAPI.userAPI.getUserApiKey({ accountId: accInfo.accountId}, eddsaKey.sk ); console.log("apiKey:", apiKey); const { userNFTBalances } = await LoopringAPI.userAPI.getUserNFTBalances( { accountId: accInfo.accountId, limit: 20 }, apiKey ); ``` *** ##Step 4. get storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId({ accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey); ``` *** ## Step 5. get fee ```ts const transferResult = await LoopringAPI.userAPI.submitNFTInTransfer({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, fromAccountId: LOOPRING_EXPORTED_ACCOUNT.accountId, fromAddress: LOOPRING_EXPORTED_ACCOUNT.address, toAccountId: 0, // toAccountId is not required, input 0 as default toAddress: LOOPRING_EXPORTED_ACCOUNT.address2, token: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: fee.fees["LRC"].fee ?? "9400000000000000000", }, storageId: storageId.offchainId, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, /* * when payPayeeUpdateAccount = ture, will help payee active account, * maxFee from: * const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( * { * accountId: accInfo.accountId, * requestType: sdk.OffchainFeeReqType.NFT_TRANSFER_AND_UPDATE_ACCOUNT, * }, * apiKey * ); */ payPayeeUpdateAccount: false, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey, }); console.log("fee:", fee); ``` *** ## Step 6. Transfer NFT ```ts const transferResult = await LoopringAPI.userAPI.submitNFTInTransfer({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, fromAccountId: LOOPRING_EXPORTED_ACCOUNT.accountId, fromAddress: LOOPRING_EXPORTED_ACCOUNT.address, toAccountId: 0, // toAccountId is not required, input 0 as default toAddress: LOOPRING_EXPORTED_ACCOUNT.address2, token: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: fee.fees["LRC"].fee ?? "9400000000000000000", }, storageId: storageId.offchainId, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey, }); console.log("transfer Result:", transferResult); ``` ================================================ FILE: src/tests/demo/transfer/transferNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; describe("TransferNFT", function () { it( "submitNFTInTransfer", async () => { /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); // Step 5. fee const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_TRANSFER, amount: "0", }, apiKey ); console.log("fee:", fee); const transferResult = await LoopringAPI.userAPI.submitNFTInTransfer({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, fromAccountId: LOOPRING_EXPORTED_ACCOUNT.accountId, fromAddress: LOOPRING_EXPORTED_ACCOUNT.address, toAccountId: 0, // toAccountId is not required, input 0 as default toAddress: LOOPRING_EXPORTED_ACCOUNT.address2, token: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: fee.fees["LRC"].fee ?? "9400000000000000000", }, storageId: storageId.offchainId, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, /* * when payPayeeUpdateAccount = ture, will help payee active account, * maxFee from: * const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( * { * accountId: accInfo.accountId, * requestType: sdk.OffchainFeeReqType.NFT_TRANSFER_AND_UPDATE_ACCOUNT, * }, * apiKey * ); */ payPayeeUpdateAccount: false, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey, }); console.log("transfer Result:", transferResult); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/demo/withdraw/withdrawERC20.md ================================================ # Withdraw ERC20 Definition: Loopring L2 withdraw ERC20 to Ethereum L1, > trade value should with decimals `sdk.toBig(value).times("1e" + TOKEN_INFO.tokenMap.LRC.decimals)` *** ## Step 1. getAccount ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` ##Step 2. eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey({ accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId({ accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey ); console.log("storageId:", storageId); ``` *** ## Step 5. fee ```ts const fee = await LoopringAPI.userAPI.getOffchainFeeAmt({ accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, tokenSymbol: TOKEN_INFO.tokenMap["LRC"].symbol, }, apiKey ); console.log("fee:", fee); ``` *** ## Step 6. withdraw ```ts const response = await LoopringAPI.userAPI.submitOffchainWithdraw({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, counterFactualInfo: undefined, fastWithdrawalMode: false, hashApproved: "", maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, minGas: 0, owner: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address, storageId: 0, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, validUntil: 0, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.MetaMask, eddsaKey: eddsaKey.sk, apiKey, }); console.log("response:", response); ``` ================================================ FILE: src/tests/demo/withdraw/withdrawERC20.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; describe("Withdraw", function () { beforeEach(async () => { jest.setTimeout(DEFAULT_TIMEOUT * 3); }, DEFAULT_TIMEOUT); it( "submitWithdraw", async () => { /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey ); console.log("storageId:", storageId); // Step 5. fee const fee = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.OFFCHAIN_WITHDRAWAL, tokenSymbol: TOKEN_INFO.tokenMap["LRC"].symbol, }, apiKey ); console.log("fee:", fee); // Step 6. withdraw const response = await LoopringAPI.userAPI.submitOffchainWithdraw({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, counterFactualInfo: undefined, fastWithdrawalMode: false, hashApproved: "", maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, minGas: 0, owner: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, //sdk.ConnectorNames.MetaMask UT only support perosonal sign, no V4 eddsaKey: eddsaKey.sk, apiKey, }); console.log("response:", response); }, DEFAULT_TIMEOUT * 3 ); }); ================================================ FILE: src/tests/demo/withdraw/withdrawNFT.md ================================================ # Withdraw NFT Definition: Loopring L2 withdraw NFT to Ethereum L1 *** ## Step 1. getAccount ```ts const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); ``` *** ## Step 2. eddsaKey ```ts const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); ``` *** ## Step 3. apiKey ```ts const {apiKey} = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); ``` *** ## Step 4. storageId ```ts const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); //Step 5. getUserNFTBalances const {userNFTBalances} = await LoopringAPI.userAPI.getUserNFTBalances( {accountId: LOOPRING_EXPORTED_ACCOUNT.accountId}, apiKey ); const tokenInfo = userNFTBalances.find( (item) => item.tokenAddress?.toLowerCase() === LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress.toLowerCase() && item.nftId && web3.utils.hexToNumberString(item.nftId) === LOOPRING_EXPORTED_ACCOUNT.nftTokenId.toString() ); ``` *** ## Step 5. fee ```ts const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, deployInWithdraw: tokenInfo?.deploymentStatus === DEPLOYMENT_STATUS.NOT_DEPLOYED, // when token is not deploy the fee is diff }, apiKey ); console.log("fee:", fee); ``` *** ## Step 6. withdraw ```ts const response = await LoopringAPI.userAPI.submitNFTWithdraw({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, counterFactualInfo: undefined, hashApproved: "", maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: fee.fees["LRC"].fee ?? "9400000000000000000", }, minGas: 0, owner: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address, storageId: 0, token: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, validUntil: 0, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.MetaMask, eddsaKey: eddsaKey.sk, apiKey, }); console.log("response:", response); ``` ================================================ FILE: src/tests/demo/withdraw/withdrawNFT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; import { DEPLOYMENT_STATUS } from "../../../index"; describe("WithdrawNFT", function () { it( "submitNFTWithdraw", async () => { /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, }, apiKey ); console.log("storageId:", storageId); //Step 5. getUserNFTBalances const { userNFTBalances } = await LoopringAPI.userAPI.getUserNFTBalances( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId }, apiKey ); const tokenInfo = userNFTBalances.find( (item) => item.tokenAddress?.toLowerCase() === LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress.toLowerCase() && item.nftId && web3.utils.hexToNumberString(item.nftId) === LOOPRING_EXPORTED_ACCOUNT.nftTokenId.toString() ); // Step 6. fee const fee = await LoopringAPI.userAPI.getNFTOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainNFTFeeReqType.NFT_WITHDRAWAL, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, deployInWithdraw: tokenInfo?.deploymentStatus === DEPLOYMENT_STATUS.NOT_DEPLOYED, // when token is not deploy the fee is diff }, apiKey ); console.log("fee:", fee); // Step 6. withdraw const response = await LoopringAPI.userAPI.submitNFTWithdraw({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, counterFactualInfo: undefined, hashApproved: "", maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, amount: fee.fees["LRC"].fee ?? "9400000000000000000", }, minGas: 0, owner: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address, storageId: 0, token: { tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, amount: "1", }, validUntil: 0, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.MetaMask, eddsaKey: eddsaKey.sk, apiKey, }); console.log("response:", response); }, DEFAULT_TIMEOUT * 3 ); }); ================================================ FILE: src/tests/formatter.test.ts ================================================ import { IsMobile, LoopringErrorCode } from "../defs"; // import fetch from "node-fetch"; import { addHexPrefix, clearHexPrefix, convertPublicKey, convertPublicKey2, formatAddress, formatEddsaKey, formatKey, fromGWEI, getDisplaySymbol, numberWithCommas, padLeftEven, PublicKey, sleep, toBig, toBN, toBuffer, toFixed, toGWEI, toHex, toNumber, zeroPad, } from ".."; import * as fm from "../utils/formatter"; import { BigNumber } from "bignumber.js"; import { LOOPRING_EXPORTED_ACCOUNT, web3 } from "./MockData"; const NUMBER = 40244024; const BUFFER = Buffer.from("40244024", "utf8"); const BIG_NUMBER = new BigNumber(123.4567); const TIMEOUT = 30000; describe("formatter test", function () { beforeEach(async () => { return; }, TIMEOUT); it( "enum_test", async () => { console.log(LoopringErrorCode[100000]); console.log(LoopringErrorCode[200000]); }, TIMEOUT ); // it( // "META_JSON", // async () => { // const ipfs = "QmayP595tA9L7D9XYhY6FzkixXWDPFZ3Y8Q3c9TUq3hqns"; // const value = await fetch(`https://localhost:8080/ipfs/${ipfs}`).then( // (response) => response.json() // ); // console.log(`ipfsCid0ToNftID: meta: `, ipfs, value); // }, // DEFAULT_TIMEOUT + 20000 // ); it( "formatter1", async () => { const px = fm.formatEddsaKey( "4966334643155205117396087046889763297010019574817548934733242435404623801022" ); const py = fm.formatEddsaKey( "12336121800785994591019936445923899190012791232373302800087997800255990054103" ); const pk: PublicKey = { x: px, y: py, }; console.log(px); console.log("px.length:", px.length); console.log(py); console.log("py.length:", py.length); console.log(convertPublicKey(pk)); }, TIMEOUT ); it( "formatter2", async () => { const px = fm.formatEddsaKey( fm.toHex( fm.toBig( "4966334643155205117396087046889763297010019574817548934733242435404623801022" ) ) ); const py = fm.formatEddsaKey( fm.toHex( fm.toBig( "12336121800785994591019936445923899190012791232373302800087997800255990054103" ) ) ); const pk: PublicKey = { x: px, y: py, }; console.log(px); console.log("px.length:", px.length); console.log(py); console.log("py.length:", py.length); const bn = convertPublicKey(pk); console.log(bn); console.log(bn.toString(10)); console.log(fm.addHexPrefix(bn.toString(16))); }, TIMEOUT ); it( "formatter3", async () => { const px = "4966334643155205117396087046889763297010019574817548934733242435404623801022"; const py = "12336121800785994591019936445923899190012791232373302800087997800255990054103"; const pk: PublicKey = { x: px, y: py, }; console.log(px); console.log("px.length:", px.length); console.log(py); console.log("py.length:", py.length); const bn = convertPublicKey2(pk); console.log(bn); console.log(bn.toString(10)); console.log(fm.addHexPrefix(bn.toString(16))); }, TIMEOUT ); it( "formatter4", async () => { const publicKey = { x: "0x01f9390f9ea86a9b98b180647f28454e2466bf14b4e28533161429f50a8fdea8", y: "0x006b5e578ef145c7339f56a54443b31d77b33b469748d029bdbeabc4928df7", }; const publicKeyNo0 = { x: "0x1f9390f9ea86a9b98b180647f28454e2466bf14b4e28533161429f50a8fdea8", y: "0x06b5e578ef145c7339f56a54443b31d77b33b469748d029bdbeabc4928df7", }; expect(fm.toBig(publicKey.x).eq(fm.toBig(publicKeyNo0.x))).toBe(true); expect(fm.toBig(publicKey.y).eq(fm.toBig(publicKeyNo0.y))).toBe(true); }, TIMEOUT ); it("test toBuffer", async () => { console.log(toBuffer("420")); // todo add assertion console.log(toBuffer(BUFFER)); // todo add assertion }); it("test zeroPad", async () => { console.log(zeroPad("420", 0)); // todo add assertion }); it("test toHex", async () => { expect(toHex(LOOPRING_EXPORTED_ACCOUNT.nftId)).toBe( "0xa0ce8990402955e559799af24ea765b14ffecc32dfa1cce2dadaf20016b074e6" ); }); it("test toNumber", async () => { expect(toNumber("69")).toBe(69); expect(toNumber(420)).toBe(420); expect(toNumber("12345.6789")).toBe(12345.6789); expect(web3.utils.hexToNumberString(LOOPRING_EXPORTED_ACCOUNT.nftId)).toBe( "72734975696905790806441216757602251046556131108796431318619325611208980067558" ); expect(toNumber(BIG_NUMBER)).toBe(123.4567); }); it("test padLeftEven", async () => { expect(padLeftEven(LOOPRING_EXPORTED_ACCOUNT.address)).toBe( LOOPRING_EXPORTED_ACCOUNT.address ); expect(padLeftEven(LOOPRING_EXPORTED_ACCOUNT.testNotOx.slice(0, -1))).toBe( "0" + LOOPRING_EXPORTED_ACCOUNT.testNotOx.slice(0, -1) ); }); it("test getDisplaySymbol", async () => { expect(getDisplaySymbol("USD")).toBe("$"); expect(getDisplaySymbol("CNY")).toBe("¥"); expect(getDisplaySymbol("EUR")).toBe(""); }); it("test toBig", async () => { expect(toBig("0x" + BUFFER)).toEqual(new BigNumber("0x" + BUFFER)); }); it("test toBig ", async () => { console.log( toBig("426702000000000000000") .div("1e" + 18) .toString() ); }); it("test toBN", async () => { // expect(toBN("0x" + BIG_NUMBER)).toEqual(BIG_NUMBER); expect(toBN(2).toString()).toEqual("2"); }); it("test fromGWEI", async () => { expect(fromGWEI(420)).toEqual(new BigNumber(4.2e11)); expect(fromGWEI(420.0)).toEqual(new BigNumber(4.2e11)); expect(fromGWEI("420")).toEqual(new BigNumber(4.2e11)); }); it("test toGWEI", async () => { expect(toGWEI(420)).toEqual(new BigNumber(4.2e-7)); expect(toGWEI(420.0)).toEqual(new BigNumber(4.2e-7)); expect(toGWEI("420")).toEqual(new BigNumber(4.2e-7)); }); it("test formatKey", async () => { expect(formatKey("0x" + BUFFER)).toBe(NUMBER.toString()); expect(formatKey(LOOPRING_EXPORTED_ACCOUNT.address.toLowerCase())).toBe( LOOPRING_EXPORTED_ACCOUNT.testNotOx ); expect(formatKey(LOOPRING_EXPORTED_ACCOUNT.testNotOx)).toBe( LOOPRING_EXPORTED_ACCOUNT.testNotOx ); }); // Missing test input data for Uint8Array it("test formatAddress", async () => { expect( formatAddress(LOOPRING_EXPORTED_ACCOUNT.testNotOx).toLowerCase() ).toBe(LOOPRING_EXPORTED_ACCOUNT.address); expect(formatAddress(LOOPRING_EXPORTED_ACCOUNT.address).toLowerCase()).toBe( LOOPRING_EXPORTED_ACCOUNT.address ); expect(formatAddress("0x" + BUFFER)).toBe("0x" + BUFFER); }); it("test addHexPrefix", async () => { expect(addHexPrefix(LOOPRING_EXPORTED_ACCOUNT.address)).toBe( LOOPRING_EXPORTED_ACCOUNT.address ); expect(addHexPrefix(LOOPRING_EXPORTED_ACCOUNT.testNotOx)).toBe( LOOPRING_EXPORTED_ACCOUNT.address ); expect(() => addHexPrefix(420)).toThrowError("Unsupported type"); }); it("test clearHexPrefix", async () => { expect(clearHexPrefix(LOOPRING_EXPORTED_ACCOUNT.address)).toBe( LOOPRING_EXPORTED_ACCOUNT.testNotOx ); expect(() => clearHexPrefix(420)).toThrowError("Unsupported type"); }); it("test toFixed", async () => { expect(toFixed(420, 0, 0)).toBe("420"); expect(toFixed(420, 1, 0)).toBe("420.0"); expect(toFixed(420, 2, 0)).toBe("420.00"); expect(toFixed(421.421, 2, 1)).toBe("421.43"); expect(toFixed(421.421, 1, 1)).toBe("421.5"); expect(toFixed(421.421, 1, 0)).toBe("421.4"); expect(toFixed(BIG_NUMBER, 1, 0)).toBe("123.4"); expect(toFixed(BIG_NUMBER, 1, 1)).toBe("123.5"); expect(toFixed(BIG_NUMBER, 2, 1)).toBe("123.46"); expect(() => toFixed("wow", 2, 1)).toThrowError("Unsupported type"); }); it("test formatEddsaKey", async () => { expect( formatEddsaKey( "027a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33" ) ).toBe( "0x027a5b716c7309a30703ede3f1a218cdec857e424a31543f8a658e7d2208db33" ); expect(formatEddsaKey("1231dsab2")).toBe( "0x00000000000000000000000000000000000000000000000000000001231dsab2" ); }); it("test numberWithCommas", async () => { expect(numberWithCommas(420)).toBe("420"); expect(numberWithCommas(1000)).toBe("1,000"); expect(numberWithCommas("10000")).toBe("10,000"); expect(numberWithCommas("1,000,000")).toBe("1,000,000"); expect(numberWithCommas("10.000.000")).toBe("-"); expect(numberWithCommas(null)).toBe(null); }); it("sleep_test", async () => { console.log(new Date().getTime()); await sleep(5000); console.log(new Date().getTime()); }); it("stringify_test", async () => { const demo: any = { a: 5, c: { d: 5, e: "k" }, b: "ss", }; console.log(JSON.stringify(demo)); const keys = Object.keys(demo).sort(); console.log(keys); console.log(JSON.stringify(demo, keys)); const demo2: any = {}; keys.forEach((item, index) => { demo2[item] = demo[item]; }); console.log(demo2); console.log(JSON.stringify(demo2)); const ordered = Object.keys(demo) .sort() .reduce((obj: any, key) => { obj[key] = demo[key]; return obj; }, {}); console.log(JSON.stringify(ordered)); }); it("isMobile", () => { console.log("isMobile", IsMobile.any()); }); it("cid", () => { // // eslint-disable-next-line @typescript-eslint/no-var-requires // const _CID = require("cids"); // const _1cid = new _CID( // "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" // ); // const _1hashHex = Buffer.from(_1cid.multihash.slice(2)).toString("hex"); // const _1hashBN = new BN(_1hashHex.padStart(64, "0"), 16) // .toString("hex") // .padStart(64, "0"); // // const _cid = CID.parse( // "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" // ); // const hashHex = _cid.toString(base16).padStart(64, "0"); // console.log( // "no slice", // new BN(Buffer.from(_1cid.multihash).toString("hex"), 16) // .toString("hex") // .padStart(64, "0"), // "currentBuffer", // _1hashBN, // "hashHex", // hashHex // ); // // const CID = require("cids"); // const hashBN = new BN( // "0x01701220c3c4733ec8affd06cf9e9ff50ffc6bcd2ec85a6170004bb709669c31de94391a" // // "0xca4f3647b8a11f64ec3dbb64ab756f623044e9e066bb2a79aa4264a3eb265240" // .replace("0x", ""), // 16 // ); // const hex = hashBN.toString("hex").padStart(64, "0"); // // const buf = Buffer.from("1220" + hex, "hex"); // console.log( // "hashHex", // hex // // _hashBN // ); // const buf = Buffer.from("1220" + hex, "hex"); // const cid = CID.parse("f" + hashBN, base16).toString(); // myLog(cid); }); }); export default {}; ================================================ FILE: src/tests/unitTest/account/account.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, signatureKeyPairMock, TOKEN_INFO, } from "../../MockData"; import * as sdk from "../../../index"; /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ describe("AccountDemo", function () { it( "getAccount", async () => { const response = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log(response); }, DEFAULT_TIMEOUT ); it("getWalletType", async () => { const [ { walletType: CFWalletType }, { walletType: EOAWalletType }, { walletType: ContractWalletType }, ] = await Promise.all([ LoopringAPI.walletAPI.getWalletType({ wallet: LOOPRING_EXPORTED_ACCOUNT.addressCF, }), LoopringAPI.walletAPI.getWalletType({ wallet: LOOPRING_EXPORTED_ACCOUNT.address, }), LoopringAPI.walletAPI.getWalletType({ wallet: LOOPRING_EXPORTED_ACCOUNT.addressContractWallet, }), ]); console.log( "CFWalletType, EOAWalletType, ContractWalletType", CFWalletType, EOAWalletType, ContractWalletType ); }); it( "getUserApiKey", async () => { const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); const eddsaKey = await signatureKeyPairMock(accInfo); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); }, DEFAULT_TIMEOUT * 10 ); it( "getEthNonce", async () => { const response = await LoopringAPI.exchangeAPI.getEthNonce({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("getEthNonce:", response); }, DEFAULT_TIMEOUT ); it( "approveMax_LRC", async () => { const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); const response = await sdk.approveMax( web3, LOOPRING_EXPORTED_ACCOUNT.address, TOKEN_INFO.tokenMap.LRC.address, LOOPRING_EXPORTED_ACCOUNT.depositAddress, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, nonce, true ); console.log(`nonce: ${nonce} approveMax: ${JSON.stringify(response)}`); }, DEFAULT_TIMEOUT * 2 ); it( "approveZero_LRC", async () => { const nonce = await sdk.getNonce(web3, LOOPRING_EXPORTED_ACCOUNT.address); const response = await sdk.approveZero( web3, LOOPRING_EXPORTED_ACCOUNT.address, TOKEN_INFO.tokenMap.LRC.address, LOOPRING_EXPORTED_ACCOUNT.depositAddress, LOOPRING_EXPORTED_ACCOUNT.gasPrice, LOOPRING_EXPORTED_ACCOUNT.gasLimit, sdk.ChainId.GOERLI, // @ts-ignor nonce, true ); console.log(`nonce: ${nonce} approveZero: ${response}`); }, DEFAULT_TIMEOUT * 2 ); it( "Layer1_ETH_Balance", async () => { const { ethBalance } = await LoopringAPI.exchangeAPI.getEthBalances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log(`Layer1 ethBalance: ${ethBalance}`); }, DEFAULT_TIMEOUT ); it( "Layer1_ERC20_Balance", async () => { const { tokenBalances } = await LoopringAPI.exchangeAPI.getTokenBalances({ owner: LOOPRING_EXPORTED_ACCOUNT.address, token: [TOKEN_INFO.tokenMap.LRC.address], }); console.log( `Layer1 ERC20 Balance LRC: ${tokenBalances.get( TOKEN_INFO.tokenMap.LRC.address as unknown as sdk.TokenAddress )}` ); }, DEFAULT_TIMEOUT ); it("Layer1_getNFTBalance", async () => { const response = await LoopringAPI.nftAPI.getNFTBalance({ web3, account: LOOPRING_EXPORTED_ACCOUNT.address, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: LOOPRING_EXPORTED_ACCOUNT.nftId, nftType: sdk.NFTType.ERC1155, }); console.log(response); }); it( "Layer2_getUserBalances", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. getUserBalances const { userBalances } = await LoopringAPI.userAPI.getUserBalances( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, tokens: "" }, apiKey ); console.log(`Layer2 ERC20 Balance: ${userBalances}`); }, DEFAULT_TIMEOUT ); it( "Layer2_getUserNFTBalances", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. getUserNFTBalances const { userNFTBalances } = await LoopringAPI.userAPI.getUserNFTBalances( { accountId: accInfo.accountId, limit: 20 }, apiKey ); console.log(`Layer2 NFT Balance: ${userNFTBalances}`); }, DEFAULT_TIMEOUT ); it( "getCounterFactualInfo", async () => { const response = await LoopringAPI.exchangeAPI.getCounterFactualInfo({ accountId: LOOPRING_EXPORTED_ACCOUNT.accountIdCF, }); console.log("getCounterFactualInfo", response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/unitTest/account/sign_tools.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, signatureKeyPairMock, testTypedData, web3, } from '../../MockData' import * as sdk from '../../../index' import { AccountInfo, getEdDSASig, getNftData, NFTOrderRequestV3, NFTTradeRequestV3, NFTType, SubmitOrderRequestV3, } from '../../../index' import { myLog } from '../../../utils/log_tools' import { performance } from 'perf_hooks' let mockData: | { accInfo: AccountInfo apiKey: string eddsaKey: any } | undefined = undefined describe('signature', function () { beforeEach(async () => { // Step 1. getAccount const accInfo = ( await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) ).accInfo const eddsaKey = await signatureKeyPairMock(accInfo) // Step 3. apiKey const apiKey = ( await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk, ) ).apiKey mockData = { apiKey, accInfo, eddsaKey, } }, DEFAULT_TIMEOUT * 3) it( 'generateKeyPair', async () => { const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }) const result = await signatureKeyPairMock(accInfo) console.log(result.sk) }, DEFAULT_TIMEOUT, ) /** * test case is not allow brock by Mock provider */ // it( // "getEcDSASig:eth_signTypedData_v4", // async () => { // // test case is not allow brock by Mock provider // const result = await sdk.getEcDSASig( // web3, // testTypedData, // LOOPRING_EXPORTED_ACCOUNT.address, // sdk.GetEcDSASigType.HasDataStruct, // sdk.ChainId.GOERLI, // LOOPRING_EXPORTED_ACCOUNT.accountId, // "", // sdk.ConnectorNames.Unknown // ); // console.log( // "getEcDSASig:eth_signTypedData_v4", // result, // "ecdsaSig+sdk.SigSuffix.Suffix02", // result.ecdsaSig + sdk.SigSuffix.Suffix02 // ); // }, // DEFAULT_TIMEOUT // ); it('getEcDSASig:WithoutDataStruct(personalSign)', async () => { const result = await sdk.getEcDSASig( web3, testTypedData, LOOPRING_EXPORTED_ACCOUNT.address, sdk.GetEcDSASigType.WithoutDataStruct, sdk.ChainId.GOERLI, LOOPRING_EXPORTED_ACCOUNT.accountId, '', sdk.ConnectorNames.Unknown, ) console.log('getEcDSASig:WithoutDataStruct(personalSign)', result) }) it('genSigWithPadding', async () => { // const sign = genSigWithPadding( // LOOPRING_EXPORTED_ACCOUNT.eddsaKey.sk, // '4802789675835142786409394599364863978702692874349325354528260268536567293213', // ) // console.log(sign) }) it('getEdDSASig', async () => { if (mockData) { const sign = await sdk.getEdDSASig( 'GET', 'https://uat2.loopring.io', '/api/v3/apiKey/', { accountId: '10010', }, mockData.eddsaKey.sk, ) console.log(sign) } }) it('getEdDSASig_100', async () => { if (mockData) { performance.now() for (let i = 100; i > 0; i--) { const sign = await sdk.getEdDSASig( 'GET', 'https://uat2.loopring.io', '/api/v3/apiKey/', { accountId: '10010', }, mockData.eddsaKey.sk, ) console.log(sign) } performance.now() } }) /** * test case is not allow brock by Mock provider */ // it("personalSign Contract", async () => { // await sdk.getEcDSASig( // web3, // testTypedData, // LOOPRING_EXPORTED_ACCOUNT.address, // sdk.GetEcDSASigType.Contract, // sdk.ChainId.GOERLI, // LOOPRING_EXPORTED_ACCOUNT.accountId, // "", // sdk.ConnectorNames.Unknown // ); // }); it('NFTAction Trade Hash', async () => { try { const makerOrder: NFTOrderRequestV3 = { exchange: '0xD1221BA705B653d9Ea22569c911Bddf68264fAF4', accountId: 10979, storageId: 36, sellToken: { tokenId: 2, nftData: '', amount: '118117838', }, buyToken: { tokenId: 32769, nftData: '0x26b32b508180c293e1a6581fd8a1ebff4734966578a008247c9f0bf9cdb5e492', amount: '2', }, allOrNone: false, fillAmountBOrS: true, validUntil: 1680332114, maxFeeBips: 1000, } makerOrder.eddsaSignature = '0x2553b338ce18a9bc4f51412ccf2c37ba04538ef26e4897fdcf244d0bcf71fe270a0ccebe65cc4a13a3c3e6f84c3de919f7d81434df0a50b8f583e4c7b350b2a203c01b8472e3474c6b2225de41da6ae67a781986d43063ce8008d5978cab3c1f' const takerOrder: NFTOrderRequestV3 = { exchange: '0xD1221BA705B653d9Ea22569c911Bddf68264fAF4', accountId: 10092, storageId: 2, sellToken: { tokenId: 32769, nftData: '0x26b32b508180c293e1a6581fd8a1ebff4734966578a008247c9f0bf9cdb5e492', amount: '1', }, buyToken: { tokenId: 2, nftData: '', amount: '59000000', }, allOrNone: false, fillAmountBOrS: false, validUntil: 1680332114, maxFeeBips: 1000, } takerOrder.eddsaSignature = '0x28e1fef32e0e7d119d5f2a078bf9f0ad677f72eed18ef35d98326a389ebb5e900a92fe81cfd4fc0a2758dba3bbf55f79c073ac539490b5fc6e16a8046df946dd060025f14fc3d92ef72163639e4ec8cef629e335343c44a291aed5a6e8d646bf' const tradeRequest: NFTTradeRequestV3 = { maker: makerOrder, makerFeeBips: 99, taker: takerOrder, takerFeeBips: 99, } const hash = await sdk.getNftTradeHash(tradeRequest) // result should be 0x244b36f43e462167942bb336e180df74dcca5726742cfab9ae1b70b6dfe5f4a console.log('NFT Trade hash is 0x' + hash) } catch (err) { console.log(err) } }) it('ERC20 Order Hash', async () => { try { const orderRequest: SubmitOrderRequestV3 = { exchange: '0x2e76EBd1c7c0C8e7c2B875b6d505a260C525d25e', accountId: 11040, storageId: 10, sellToken: { tokenId: 1, volume: '986000000000000000000', }, buyToken: { tokenId: 0, volume: '74861064000000000', }, maxFeeBips: 60, validUntil: 1653995768, fillAmountBOrS: false, allOrNone: false, eddsaSignature: '', } const hash = await sdk.getOrderHash(orderRequest) // result should be : 0x15b965bbf0e01697939a5cd8be6e631f6cf4a3009ee9fb8631d193e0d5218b92 console.log('ERC20 order hash is 0x' + hash) } catch (err) { console.log(err) } }) it('getNftData', async () => { const cid = 'QmNuqdeWUJ9iEiw5qZfJ2pJ9onqAS45ZffvV8JQSUzp7DQ' const nftID = LoopringAPI.nftAPI.ipfsCid0ToNftID(cid) const mintRequest = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, minterId: LOOPRING_EXPORTED_ACCOUNT.accountId, minterAddress: LOOPRING_EXPORTED_ACCOUNT.address, toAccountId: LOOPRING_EXPORTED_ACCOUNT.accountId, toAddress: LOOPRING_EXPORTED_ACCOUNT.address, nftType: NFTType.ERC1155, tokenAddress: LOOPRING_EXPORTED_ACCOUNT.nftTokenAddress, nftId: nftID, //nftId.toString(16), amount: '1', validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, storageId: 9, maxFee: { tokenId: 1, amount: '9400000000000000000', }, royaltyPercentage: 5, forceToMint: true, // suggest use as false, for here is just for run test } myLog('cid', cid, 'nftID', nftID, 'result is:') getNftData({ ...mintRequest, nftId: nftID }) myLog('nftID', '0x1', 'result is:') getNftData({ ...mintRequest, nftId: '0x1' }) }) }) export default {} ================================================ FILE: src/tests/unitTest/appWallet/wallet.test.ts ================================================ import { TOKEN_INFO, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, DEFAULT_TIMEOUT, web3, } from '../../MockData' import * as sdk from '../../../index' import * as ethUtil from 'ethereumjs-util' import { myLog } from '../../../utils/log_tools' describe('WalletApi', function () { it( 'getUserAssets', async () => { const request: sdk.GetUserAssetsRequest = { wallet: LOOPRING_EXPORTED_ACCOUNT.addressContractWallet, offset: 10, limit: 10, } const response = await LoopringAPI.walletAPI.getUserAssets(request) console.log(response) }, DEFAULT_TIMEOUT, ) it( 'getTokenPrices', async () => { const response = await LoopringAPI.walletAPI.getTokenPrices({ token: TOKEN_INFO.tokenMap.LRC.address, }) console.log(response) }, DEFAULT_TIMEOUT, ) it( 'getLatestTokenPrices', async () => { const response = await LoopringAPI.walletAPI.getLatestTokenPrices() console.log(response) }, DEFAULT_TIMEOUT, ) it( 'getLatestTokenPrices_cny', async () => { const response = await LoopringAPI.walletAPI.getLatestTokenPrices({ currency: sdk.Currency.cny, }) console.log(response) }, DEFAULT_TIMEOUT, ) // it( // 'signHebaoApproveWithDataStructureForContract V1', // async () => { // const request: any = { // approveRecordId: 2325, // txAwareHash: '0x7de6db4d1eb828442404daa7a70ee10a5c7d69c3f9685a8a52dc069fd53575bb', // securityNumber: '403265', // signer: '0xff7d59d9316eba168837e3ef924bcdfd64b237d8', // signature: '', // } // const guardian: any = { // ens: 'testcmx1.loopring.eth', // address: '0xd4bd7c71b6d4a09217ccc713f740d6ed8f4ea0cd', // type: 'recovery', // id: 2325, // messageHash: '0x7de6db4d1eb828442404daa7a70ee10a5c7d69c3f9685a8a52dc069fd53575bb', // businessDataJson: { // value: { // value: { // wallet: '0xd4bd7c71b6d4a09217ccc713f740d6ed8f4ea0cd', // newOwner: '0x696c291239931ac87f5053bcb8e621a181fb3f08', // oldOwnerIsLocked: false, // newGuardians: [], // }, // }, // }, // signedRequest: { // signers: '', // signatures: '', // validUntil: '1663646243', // wallet: '0xd4bd7c71b6d4a09217ccc713f740d6ed8f4ea0cd', // }, // approveId: 2325, // wallet: '0xd4bd7c71b6d4a09217ccc713f740d6ed8f4ea0cd', // guardian: '0xff7d59d9316eba168837e3ef924bcdfd64b237d8', // metaTxType: 16, // txAwareHash: '0x7de6db4d1eb828442404daa7a70ee10a5c7d69c3f9685a8a52dc069fd53575bb', // createAt: 1658462243419, // } // // ("0x46d4e3d3db8fcb401e5d4020b958755a3419b84b"); // // const walletModule = { // // moduleName: "FORWARDER_MODULE", // // moduleAddress: "0x21e3143ef5677c465c2aba68ef7137f063933c7c", // // }; // const forwarderModuleAddress = '0x46d4e3d3db8fcb401e5d4020b958755a3419b84b' //walletModule.moduleAddress; // const newOwner = guardian.businessDataJson?.value.value.newOwner // const response = await LoopringAPI.walletAPI.signHebaoApproveWithDataStructureForContract( // web3, // request.signer, // guardian, // LOOPRING_EXPORTED_ACCOUNT.chainId, // newOwner, // undefined, // undefined, // forwarderModuleAddress, // ) // // const hash // // = ; // // console.log(response) // }, // DEFAULT_TIMEOUT, // ) // it( // 'signHebaoApproveWithDataStructureForContract V2 guardians=[]', // async () => { // const contractType = { // network: 'ETHEREUM', // contractVersion: 'V2_0_0', // masterCopy: '0x4b16684de50ebcc60fd54b0a5fd1ccfdc940bb27', // walletFactory: '0x8c3d4e1728f77abcd220323260da4a9306fb6433', // ens: '', // walletStatus: 5, // queueStatus: 3, // walletType: 0, // isCounterFactual: false, // } // const request: any = { // approveRecordId: 2323, // txAwareHash: '0xaeaf14dc427a0fbfa3e60887fd46b7263907d415ff8e392a4429279322fb97c2', // securityNumber: '514038', // signer: '0xff7d59d9316eba168837e3ef924bcdfd64b237d8', // signature: '', // } // const guardian: any = { // ens: '', // address: '0x98ee79ce59f84b16f394cbf89413a256c94e2c1c', // type: 'recovery', // id: 2323, // messageHash: '0xaeaf14dc427a0fbfa3e60887fd46b7263907d415ff8e392a4429279322fb97c2', // businessDataJson: { // value: { // value: { // wallet: '0x98ee79ce59f84b16f394cbf89413a256c94e2c1c', // newOwner: '0xa37f4dddc2389f26d17dc2a4045e905b657a00d8', // oldOwnerIsLocked: true, // request: { // signers: [ // '0xfcc50ae865cdcbd846f2f2ce5099d31f99bc6db1', // '0xff7d59d9316eba168837e3ef924bcdfd64b237d8', // ], // signatures: [ // '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', // '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', // ], // validUntil: '1663645929', // wallet: '0x98ee79ce59f84b16f394cbf89413a256c94e2c1c', // }, // newGuardians: [], // }, // }, // }, // signedRequest: { // signers: '', // signatures: '', // validUntil: '1663645929', // wallet: '0x98ee79ce59f84b16f394cbf89413a256c94e2c1c', // }, // approveId: 2323, // wallet: '0x98ee79ce59f84b16f394cbf89413a256c94e2c1c', // guardian: '0xff7d59d9316eba168837e3ef924bcdfd64b237d8', // metaTxType: 16, // txAwareHash: '0xaeaf14dc427a0fbfa3e60887fd46b7263907d415ff8e392a4429279322fb97c2', // createAt: 1658461929755, // } // const newOwner = guardian.businessDataJson.value.value?.newOwner // // const guardiansBs = LoopringAPI.walletAPI.encodeAddressesPacked( // // guardian.businessDataJson.value.value.newGuardians // // ); // const newGuardians = ethUtil.keccak( // web3.eth.abi.encodeParameter( // 'address[]', // guardian.businessDataJson.value.value.newGuardians, // ), // ) // // ethUtil.keccak( // // // // ); // myLog('newGuardians', newGuardians) // // const guardiansHash = ethUtil.keccak(guardiansBs); // const response = await LoopringAPI.walletAPI.getA( // LOOPRING_EXPORTED_ACCOUNT.chainId, // contractType.masterCopy, // guardian.signedRequest.wallet, // guardian.signedRequest.validUntil, // newOwner, // newGuardians, // ) // //0x7a5b52a82fc0814bb3400e1075ea5aad34ca210497ce5c7e581eea6ee0f75650 // console.log(response, guardian.messageHash) // }, // DEFAULT_TIMEOUT, // ) }) ================================================ FILE: src/tests/unitTest/erc20Trade/amm.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; describe("AmmpoolAPI test", function () { it( "getAmmPoolConf", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolConf(); console.log(response.ammpools); console.log(response.pairs); }, DEFAULT_TIMEOUT ); it( "getAmmPoolUserRewards", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolUserRewards< any[] >({ owner: LOOPRING_EXPORTED_ACCOUNT.accountId, }); console.log("getAmmPoolUserRewards:", response); console.log( "getAmmPoolUserRewards feeRewards:", response.ammUserRewardMap ); console.log( "getAmmPoolUserRewards feeRewards,extraRewards,currentRewards:", response.raw_data[0].feeRewards, response.raw_data[0].extraRewards, response.raw_data[0].currentRewards ); }, DEFAULT_TIMEOUT ); it( "getAmmPoolUserRewards-LRC-ETH", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolUserRewards< any[] >({ owner: LOOPRING_EXPORTED_ACCOUNT.accountId, ammPoolMarkets: "AMM-LRC-ETH", }); console.log("getAmmPoolUserRewards:", response); console.log( "getAmmPoolUserRewards feeRewards:", response.ammUserRewardMap ); console.log( "getAmmPoolUserRewards feeRewards,extraRewards,currentRewards:", response.raw_data[0].feeRewards, response.raw_data[0].extraRewards, response.raw_data[0].currentRewards ); }, DEFAULT_TIMEOUT ); it( 'getAmmPoolUserRewards-LRC-ETH_ETH-USDT"', async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolUserRewards< any[] >({ owner: LOOPRING_EXPORTED_ACCOUNT.accountId, ammPoolMarkets: "AMM-LRC-ETH,AMM-ETH-USDT", }); console.log("getAmmPoolUserRewards:", response); console.log( "getAmmPoolUserRewards feeRewards:", response.ammUserRewardMap ); console.log( "getAmmPoolUserRewards feeRewards,extraRewards,currentRewards:", response.raw_data[0].feeRewards, response.raw_data[0].extraRewards, response.raw_data[0].currentRewards ); }, DEFAULT_TIMEOUT ); it( "getAmmPoolGameRank", async () => { const response: any = await LoopringAPI.ammpoolAPI.getAmmPoolGameRank({ ammPoolMarket: "UNI-ETH", }); console.log("getAmmPoolGameRank:", response.raw_data); console.log("totalRewards:", response.totalRewards); console.log("userRankList 1:", response.userRankList[0]); }, DEFAULT_TIMEOUT ); it( "getAmmAssetHistory", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmAssetHistory({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log("getAmmAssetHistory dataSeries:", response); // console.log('getAmmAssetHistory:', response.raw_data.data[0].tokens) }, DEFAULT_TIMEOUT ); it( "getAmmPoolGameUserRank", async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. getAmmPoolGameUserRank const response = await LoopringAPI.ammpoolAPI.getAmmPoolGameUserRank( { owner: LOOPRING_EXPORTED_ACCOUNT.address, ammPoolMarket: "LRC-ETH" }, apiKey ); console.log("getAmmPoolGameUserRank:", response.raw_data); console.log("userRank:", response.userRank); }, DEFAULT_TIMEOUT ); it( "getAmmPoolActivityRules", async () => { const { activityInProgressRules, activityDateMap, groupByRuleType, groupByActivityStatus, groupByRuleTypeAndStatus, raw_data, } = await LoopringAPI.ammpoolAPI.getAmmPoolActivityRules(); console.log("activityInProgressRules", activityInProgressRules); console.log("activityDateMap", activityDateMap); console.log("groupByRuleType", groupByRuleType); console.log("groupByActivityStatus", groupByActivityStatus); console.log( "groupByRuleTypeAndStatus", groupByRuleTypeAndStatus, JSON.stringify(groupByRuleTypeAndStatus) ); }, DEFAULT_TIMEOUT ); it( "getAmmPoolStats", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolStats(); console.log("ammPoolStats:", response.ammPoolStats); console.log("rewards:", response.ammPoolStats["AMM-BCDT-ETH"]?.rewards); }, DEFAULT_TIMEOUT ); it( "getAmmPoolSnapshot", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log(response); console.log(response.raw_data.pooled); }, DEFAULT_TIMEOUT ); it( "getLiquidityMining", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const response = await LoopringAPI.ammpoolAPI.getLiquidityMining( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, market: "LRC-ETH", size: 120, }, apiKey ); console.log(response); }, DEFAULT_TIMEOUT ); it( "getLiquidityMiningUserHistory", async () => { const response = await LoopringAPI.ammpoolAPI.getLiquidityMiningUserHistory({ accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, start: 0, end: new Date().getTime(), }); console.log(response); }, DEFAULT_TIMEOUT ); // it( // "getLiquidityMiningUserHistory_No_data", // async () => { // const request: GetLiquidityMiningUserHistoryRequest = { // accountId: 10084, // start: 0, // end: new Date().getTime(), // }; // const response = // await LoopringAPI.ammpoolAPI.getLiquidityMiningUserHistory(request); // console.log(response); // }, // DEFAULT_TIMEOUT // ); it( "getAmmPoolSnapshot", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log(response); console.log(response.raw_data.pooled); }, DEFAULT_TIMEOUT ); it( "getAmmPoolBalances", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolBalances(); console.log(response.ammpoolsbalances); console.log(response.ammpoolsbalances["AMM-LRC-ETH"].poolAddress); console.log(response.ammpoolsbalances["AMM-LRC-ETH"].pooled); console.log(response.ammpoolsbalances["AMM-LRC-ETH"].lp); console.log(response.ammpoolsbalances["AMM-LRC-ETH"].pooledMap); }, DEFAULT_TIMEOUT ); it( "getAmmPoolTrades", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolTrades({ ammPoolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log(response); console.log(response.raw_data.trades[0]); }, DEFAULT_TIMEOUT ); it( "getAmmPoolTxs", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolTxs({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log( "getAmmPoolTxs", response.transactions[0]?.lpToken, response.transactions[0]?.poolTokens ); }, DEFAULT_TIMEOUT ); it( "getUserAmmPoolTxs", async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. getUserAmmPoolTxs const response = await LoopringAPI.ammpoolAPI.getUserAmmPoolTxs( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, }, apiKey ); console.log(response); console.log(response.raw_data.transactions[0]); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/unitTest/erc20Trade/amm_calc.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; import { get_EddsaSig_ExitAmmPool, getAmmExitEcdsaTypedData, } from "../../../index"; describe("amm_calc", function () { it( "amm_calc_test", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log(response.raw_data.pooled); const covertVal = sdk.toBig("1e+20").toFixed(0, 0); const coinA_TV = response.ammPoolSnapshot?.pooled[0]; const coinB_TV = response.ammPoolSnapshot?.pooled[1]; const output = sdk.ammPoolCalc( covertVal, true, coinA_TV as sdk.TokenVolumeV3, coinB_TV as sdk.TokenVolumeV3 ); console.log( "covertVal:", covertVal, "output:", output, "ratio:", output.ratio.toString() ); }, DEFAULT_TIMEOUT ); it( "make_join_request", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log(response.raw_data.pooled); // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); // Step 4. fees const { fees } = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, requestType: sdk.OffchainFeeReqType.AMM_JOIN, tokenSymbol: "ETH", }, apiKey ); console.log("---fees:", fees); const { tokensMap, idIndex } = await LoopringAPI.exchangeAPI.getTokens(); const { request: res } = sdk.makeJoinAmmPoolRequest( "100", true, "0.001", LOOPRING_EXPORTED_ACCOUNT.address, fees, response.ammPoolSnapshot as sdk.AmmPoolSnapshot, tokensMap, idIndex, 0, 0 ); console.log("res:", res); }, DEFAULT_TIMEOUT ); it( "make_exit_request", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log(response.raw_data.pooled); // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); // Step 4. fees const { fees } = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, requestType: sdk.OffchainFeeReqType.AMM_JOIN, tokenSymbol: "ETH", }, apiKey ); console.log("---fees:", fees); const { tokensMap, idIndex } = await LoopringAPI.exchangeAPI.getTokens(); const { request: res } = sdk.makeExitAmmPoolRequest2( "100", "0.001", LOOPRING_EXPORTED_ACCOUNT.address, fees, response.ammPoolSnapshot as sdk.AmmPoolSnapshot, tokensMap, idIndex, 0 ); console.log("res:", res); }, DEFAULT_TIMEOUT ); it( "getEddsaSigExitAmmPool", async () => { const response = await LoopringAPI.ammpoolAPI.getAmmPoolSnapshot({ poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, }); console.log(response.raw_data.pooled); // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); // Step 4. fees const { fees } = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, requestType: sdk.OffchainFeeReqType.AMM_JOIN, tokenSymbol: "ETH", }, apiKey ); console.log("---fees:", fees); const { tokensMap, idIndex } = await LoopringAPI.exchangeAPI.getTokens(); const { request } = sdk.makeExitAmmPoolRequest2( "100", "0.001", LOOPRING_EXPORTED_ACCOUNT.address, fees, response.ammPoolSnapshot as sdk.AmmPoolSnapshot, tokensMap, idIndex, 0 ); const patch: sdk.AmmPoolRequestPatch = { chainId: LOOPRING_EXPORTED_ACCOUNT.chainId, ammName: "LRCETH-Pool", poolAddress: TOKEN_INFO.tokenMap["LP-LRC-ETH"].address, eddsaKey: eddsaKey.sk, }; const res = { ...request, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }; get_EddsaSig_ExitAmmPool(res, patch); get_EddsaSig_ExitAmmPool( { ...res, domainSeparator: "0x68561fa99d9c0532544589587b389976733591df662e3ca157dfdd30db83e966", }, patch ); }, DEFAULT_TIMEOUT ); }); export default {}; ================================================ FILE: src/tests/unitTest/erc20Trade/defi.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; import { calcDefi, DefiAction } from "../../../index"; describe("DefiAPI test", function () { it( "getDefiMarkets", async () => { const response = await LoopringAPI.defiAPI.getDefiMarkets({defiType: undefined}); console.log( "getDefiMarkets", response.marketArr, response.markets, response.pairs ); }, DEFAULT_TIMEOUT ); it( "getDefiToken", async () => { const response = await LoopringAPI.defiAPI.getDefiToken(); console.log( "getDefiToken:", response.tokensMap, response.idIndex, response.addressIndex ); }, DEFAULT_TIMEOUT ); it( "getDefiTransaction", async () => { const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, }, LOOPRING_EXPORTED_ACCOUNT.privateKey ); const response = await LoopringAPI.defiAPI.getDefiTransaction( { limit: 20, offset: 0, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId }, apiKey ); console.log("getDefiTransaction:", response); }, DEFAULT_TIMEOUT ); it( "orderDefi", async () => { // Step 1. get account info const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. get apikey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); const sellSymbol = "ETH", buySymbol = "WSTETH", sellValue = "0.00005", isJoin = true, isInputSell = true; const testMarket = "WSTETH-ETH"; const {markets} = await LoopringAPI.defiAPI.getDefiMarkets({defiType: undefined}); const {tokensMap} = await LoopringAPI.defiAPI.getDefiToken(); const marketInfo = markets[ testMarket ]; const buyTokenBalanceVol = marketInfo.baseVolume; const {fees} = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: isJoin ? sdk.OffchainFeeReqType.DEFI_JOIN : sdk.OffchainFeeReqType.DEFI_EXIT, market: testMarket, }, apiKey ); const calcVol = calcDefi({ isJoin, isInputSell, sellAmount: sellValue, feeVol: fees[buySymbol].fee, marketInfo, tokenSell: TOKEN_INFO.tokenMap[sellSymbol], tokenBuy: tokensMap[buySymbol], buyTokenBalanceVol, }); console.log( "calcVol", calcVol, calcVol.miniSellVol, calcVol.maxSellVol, calcVol.maxFeeBips ); const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap[sellSymbol].tokenId, }, apiKey ); const request = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, storageId: storageId.orderId, accountId: accInfo.accountId, sellToken: { tokenId: TOKEN_INFO.tokenMap[sellSymbol].tokenId, volume: calcVol.sellVol, }, buyToken: { tokenId: tokensMap[buySymbol].tokenId, volume: calcVol.buyVol, }, action: DefiAction.Deposit, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, fee: fees[buySymbol].fee, maxFeeBips: calcVol.maxFeeBips, type: marketInfo.type, fillAmountBOrS: false, }; // @output calcVol UI use to validation data const response = await LoopringAPI.defiAPI.orderDefi( request, eddsaKey.sk, apiKey ); console.log("orderDefi:", response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/unitTest/exchange/exchange.test.ts ================================================ import { DEFAULT_TIMEOUT, LoopringAPI } from "../../MockData"; import * as sdk from "../../../index"; describe("ExchangeAPI test", function () { it( "getExchangeInfo", async () => { const response = await LoopringAPI.exchangeAPI.getExchangeInfo(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getWithdrawalAgents", async () => { const response = await LoopringAPI.exchangeAPI.getWithdrawalAgents({ tokenId: 1, amount: "10000000000", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getCandlestick", async () => { const response = await LoopringAPI.exchangeAPI.getCandlestick({ market: "LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getAccountServices", async () => { const response = await LoopringAPI.exchangeAPI.getAccountServices({}); console.log(response); }, DEFAULT_TIMEOUT ); it( "getExchangeFeeInfo", async () => { const response = await LoopringAPI.exchangeAPI.getExchangeFeeInfo(); console.log(response); console.log( response.raw_data[sdk.VipCatergory.ORDERBOOK_TRADING_FEES_STABLECOIN] ); }, DEFAULT_TIMEOUT ); it( "getProtocolPortrait", async () => { const response = await LoopringAPI.exchangeAPI.getProtocolPortrait(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getRecommendedMarkets", async () => { const response = await LoopringAPI.exchangeAPI.getRecommendedMarkets(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getGasPrice", async () => { const response = await LoopringAPI.exchangeAPI.getGasPrice(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getGasPriceRange", async () => { const response = await LoopringAPI.exchangeAPI.getGasPriceRange(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getMarketTrades", async () => { const response = await LoopringAPI.exchangeAPI.getMarketTrades({ market: "ETH-USDT", }); console.log(response.raw_data.trades); }, DEFAULT_TIMEOUT ); it( "getRelayerCurrentTime", async () => { const response = await LoopringAPI.exchangeAPI.getRelayerCurrentTime(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getFiatPriceUSD", async () => { const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "USD", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getFiatPriceCNY", async () => { const response = await LoopringAPI.exchangeAPI.getFiatPrice({ legal: "CNY", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getMarkets", async () => { const response = await LoopringAPI.exchangeAPI.getMarkets(); console.log(response); console.log(response.pairs.LRC.tokenList); console.log( "hasMarket LRC-ETH:", sdk.hasMarket(response.marketArr, "LRC-ETH") ); console.log( "market 1:", sdk.getExistedMarket(response.marketArr, "LRC", "ETH") ); console.log( "market 2:", sdk.getExistedMarket(response.marketArr, "ETH", "LRC") ); }, DEFAULT_TIMEOUT ); it( "getTokens", async () => { const response = await LoopringAPI.exchangeAPI.getTokens(); console.log(response); console.log(response.raw_data[0]); }, DEFAULT_TIMEOUT ); it( "getDepth", async () => { const response = await LoopringAPI.exchangeAPI.getDepth({ market: "LRC-ETH", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getTicker", async () => { const response = await LoopringAPI.exchangeAPI.getTicker({ market: "LRC-ETH", }); console.log(response); }, DEFAULT_TIMEOUT ); it( "getMixMarkets", async () => { const response = await LoopringAPI.exchangeAPI.getMixMarkets(); console.log(response); console.log(response.pairs.LRC.tokenList); console.log( "hasMarket LRC-ETH:", sdk.hasMarket(response.marketArr, "LRC-ETH") ); console.log( "market 1:", sdk.getExistedMarket(response.marketArr, "LRC", "ETH") ); console.log( "market 2:", sdk.getExistedMarket(response.marketArr, "ETH", "LRC") ); }, DEFAULT_TIMEOUT ); it( "getMixDepth", async () => { const response = await LoopringAPI.exchangeAPI.getMixDepth({ market: "LRC-ETH", }); console.log(response); console.log(response.depth.bids); }, DEFAULT_TIMEOUT ); it( "getMixTicker", async () => { const response = await LoopringAPI.exchangeAPI.getMixTicker({ market: ["LRC-ETH", "ETH-USDC", "DAI-USDT"].join(","), }); console.log(response.tickMap["DAI-USDT"]); }, DEFAULT_TIMEOUT ); it( "getAllMixTickers", async () => { const response: any = await LoopringAPI.exchangeAPI.getAllMixTickers(); console.log(response?.tickMap); }, DEFAULT_TIMEOUT ); it( "getAllTickers", async () => { const response = await LoopringAPI.exchangeAPI.getAllTickers(); console.log(response); }, DEFAULT_TIMEOUT ); it( "getMarketTrades_err", async () => { const req: sdk.GetMarketTradesRequest = { market: "LRC-ETH_Not_Existed", }; const response = await LoopringAPI.exchangeAPI.getMarketTrades(req); console.log(response); console.log(response.raw_data.trades); }, DEFAULT_TIMEOUT ); it( "getAllMixTickers0", async () => { const response: any = await LoopringAPI.exchangeAPI.getAllMixTickers(); console.log(response?.tickMap); }, DEFAULT_TIMEOUT ); it( "getAllMixTickers1", async () => { const response: any = await LoopringAPI.exchangeAPI.getAllMixTickers( "AMM-LRC-ETH" ); console.log(response.tickMap); }, DEFAULT_TIMEOUT ); it( "getMixCandlestickAMM", async () => { const response = await LoopringAPI.exchangeAPI.getMixCandlestick({ market: "AMM-LRC-ETH", interval: sdk.TradingInterval.min15, limit: 96, }); console.log(response); }, DEFAULT_TIMEOUT ); }); ================================================ FILE: src/tests/unitTest/transfer/transferUT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, } from "../../MockData"; describe("Transfer UT", function () { it( "getAccountWhitelisted", async () => { const response = LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.whitelistedAddress, }); console.log(response); }, DEFAULT_TIMEOUT ); // it( // "get_EddsaSig_NFT_Transfer", // async () => { // const request: sdk.OriginNFTTransferRequestV3 = { // exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, // fromAccountId: LOOPRING_EXPORTED_ACCOUNT.accountId, // fromAddress: LOOPRING_EXPORTED_ACCOUNT.address, // toAccountId: LOOPRING_EXPORTED_ACCOUNT.accountId2, // toAddress: LOOPRING_EXPORTED_ACCOUNT.address2, // token: { // tokenId: LOOPRING_EXPORTED_ACCOUNT.nftTokenId, // nftData: LOOPRING_EXPORTED_ACCOUNT.nftData, // amount: "1", // }, // maxFee: { // tokenId: 2, // amount: "311000000000000000000", // }, // storageId: 9, // validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, // // memo: '', // }; // const result = sdk.get_EddsaSig_NFT_Transfer(request, ""); // console.log(`resultHash:`, result); // }, // DEFAULT_TIMEOUT // ); // it( // "getUserApiKeyWhitelisted", // async () => { // // Step 1. get account info // const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ // owner: LOOPRING_EXPORTED_ACCOUNT.whitelistedAddress, // }); // console.log("accInfo:", accInfo); // // // Step 2. eddsaKey // const eddsaKey = await signatureKeyPairMock(accInfo); // console.log("eddsaKey:", eddsaKey.sk); // // // Step 3. get apikey // const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( // { // accountId: accInfo.accountId, // }, // eddsaKey.sk // ); // console.log("apiKey:", apiKey); // }, // DEFAULT_TIMEOUT // ); // it( // "whitelistedAccTransfer", // async () => { // // Step 1. get account info // const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ // owner: LOOPRING_EXPORTED_ACCOUNT.whitelistedAddress, // }); // console.log("accInfo:", accInfo); // // // Step 2. eddsaKey // // const eddsaKey = await signatureKeyPairMock(accInfo); // // console.log("eddsaKey:", eddsaKey.sk); // // // Step 3. get apikey // const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( // { // accountId: accInfo.accountId, // }, // LOOPRING_EXPORTED_ACCOUNT.whitelistedEddkey // ); // console.log("apiKey:", apiKey); // // // Step 4. get storageId // const storageId = await LoopringAPI.userAPI.getNextStorageId( // { // accountId: accInfo.accountId, // sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, // }, // apiKey // ); // // // Step 5. get fee // const fee = await LoopringAPI.userAPI.getOffchainFeeAmt( // { // accountId: accInfo.accountId, // requestType: sdk.OffchainFeeReqType.TRANSFER, // }, // apiKey // ); // console.log("fee:", fee); // // // Step 6. transfer // const response = // await LoopringAPI.whitelistedUserAPI.submitInternalTransfer( // { // exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, //exchangeInfo.exchangeAddress, // payerAddr: LOOPRING_EXPORTED_ACCOUNT.whitelistedAddress, // payerId: accInfo.accountId, // payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, // payeeId: 0, // storageId: storageId.offchainId, // token: { // tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, // volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), // }, // maxFee: { // tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, // volume: fee.fees["LRC"].fee ?? "9400000000000000000", // }, // validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, // }, // LOOPRING_EXPORTED_ACCOUNT.whitelistedEddkey, // apiKey // ); // // console.log(response); // }, // DEFAULT_TIMEOUT // ); }); ================================================ FILE: src/tests/unitTest/withdraw/forceWithdrawls.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, signatureKeyPairMock, TOKEN_INFO, web3, } from "../../MockData"; import * as sdk from "../../../index"; it( "submitForceWithdrawals", async () => { // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. fee const fee = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.FORCE_WITHDRAWAL, amount: "0", }, apiKey ); console.log(fee); // Step 5. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, // same as Step 7. transfer->token->tokenId }, apiKey ); // Step 6. broker const { broker } = await LoopringAPI.exchangeAPI.getAvailableBroker({ type: 1, }); // Step 7. Build transfer & Deploy const transfer = { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, payerAddr: LOOPRING_EXPORTED_ACCOUNT.address, payerId: LOOPRING_EXPORTED_ACCOUNT.accountId, payeeAddr: broker, // payeeAddr: LOOPRING_EXPORTED_ACCOUNT.address2, storageId: storageId.offchainId, token: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, validUntil: LOOPRING_EXPORTED_ACCOUNT.validUntil, }; const response = await LoopringAPI.userAPI.submitForceWithdrawals({ request: { transfer, withdrawAddress: "0x7dF81E6683029011bBfefb5D57A1A364C3819110", //test for withdraw requesterAddress: accInfo.owner, tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.Unknown, eddsaKey: eddsaKey.sk, apiKey: apiKey, }); console.log(response); }, DEFAULT_TIMEOUT ); ================================================ FILE: src/tests/unitTest/withdraw/withdrawUT.test.ts ================================================ import { DEFAULT_TIMEOUT, LOOPRING_EXPORTED_ACCOUNT, LoopringAPI, web3, TOKEN_INFO, signatureKeyPairMock, } from "../../MockData"; import * as sdk from "../../../index"; describe("Withdraw NFTAction test", function () { // it( // "get_EddsaSig_Withdraw", // async () => { // const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ // owner: LOOPRING_EXPORTED_ACCOUNT.address, // }); // if (!accInfo) { // return; // } // /* // * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress // */ // const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); // // const request: sdk.OffChainWithdrawalRequestV3 = { // exchange: exchangeInfo.exchangeAddress, // accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, // counterFactualInfo: undefined, // fastWithdrawalMode: false, // hashApproved: "", // maxFee: { // tokenId: 1, // volume: "100000000000000000000", // }, // minGas: 0, // owner: LOOPRING_EXPORTED_ACCOUNT.address, // to: LOOPRING_EXPORTED_ACCOUNT.address, // storageId: 0, // token: { // tokenId: 1, // volume: "100000000000000000000", // }, // validUntil: 0, // }; // // const result = sdk.get_EddsaSig_OffChainWithdraw(request, ""); // console.log(`resultHash:`, result); // }, // DEFAULT_TIMEOUT // ); it( "forceWithdraw", async () => { /* * @replace LOOPRING_EXPORTED_ACCOUNT.exchangeAddress = exchangeInfo.exchangeAddress * const { exchangeInfo } = await LoopringAPI.exchangeAPI.getExchangeInfo(); */ // Step 1. getAccount const { accInfo } = await LoopringAPI.exchangeAPI.getAccount({ owner: LOOPRING_EXPORTED_ACCOUNT.address, }); console.log("accInfo:", accInfo); // Step 2. eddsaKey const eddsaKey = await signatureKeyPairMock(accInfo); console.log("eddsaKey:", eddsaKey.sk); // Step 3. apiKey const { apiKey } = await LoopringAPI.userAPI.getUserApiKey( { accountId: accInfo.accountId, }, eddsaKey.sk ); console.log("apiKey:", apiKey); // Step 4. storageId const storageId = await LoopringAPI.userAPI.getNextStorageId( { accountId: accInfo.accountId, sellTokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, }, apiKey ); console.log("storageId:", storageId); // Step 5. fee const fee = await LoopringAPI.userAPI.getOffchainFeeAmt( { accountId: accInfo.accountId, requestType: sdk.OffchainFeeReqType.FAST_OFFCHAIN_WITHDRAWAL, tokenSymbol: TOKEN_INFO.tokenMap.LRC.symbol, amount: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, apiKey ); console.log("fee:", fee); // Step 6. withdraw const response = await LoopringAPI.userAPI.submitOffchainWithdraw({ request: { exchange: LOOPRING_EXPORTED_ACCOUNT.exchangeAddress, accountId: LOOPRING_EXPORTED_ACCOUNT.accountId, counterFactualInfo: undefined, fastWithdrawalMode: true, hashApproved: "", maxFee: { tokenId: TOKEN_INFO.tokenMap["LRC"].tokenId, volume: fee.fees["LRC"].fee ?? "9400000000000000000", }, minGas: 0, owner: LOOPRING_EXPORTED_ACCOUNT.address, to: LOOPRING_EXPORTED_ACCOUNT.address, storageId: 0, token: { tokenId: TOKEN_INFO.tokenMap.LRC.tokenId, volume: LOOPRING_EXPORTED_ACCOUNT.tradeLRCValue.toString(), }, validUntil: 0, }, web3, chainId: sdk.ChainId.GOERLI, walletType: sdk.ConnectorNames.MetaMask, eddsaKey: eddsaKey.sk, apiKey, }); console.log("response:", response); }, DEFAULT_TIMEOUT * 3 ); }); ================================================ FILE: src/types/eddsa.d.ts ================================================ declare module 'ffjavascript' declare module 'blake-hash' declare module 'circomlib' declare module "blake2b"; ================================================ FILE: src/types.d.ts ================================================ declare global { type Ethereum = any; interface Window { ethereum?: { [key: string]: boolean; isLoopring?: boolean; isImToken?: boolean; isMetaMask?: boolean; } & Ethereum; navigator: { userAgent: any } & any; // socketEventMap: {[key:string]:any // imageConfig:{[key:string]:any}|undefined } } ================================================ FILE: src/utils/formatter.ts ================================================ import * as ethUtil from 'ethereumjs-util' import BN from 'bn.js' import BigNumber from 'bignumber.js' import { Buffer } from 'buffer' import { AmmPoolInfoV3, LOOPRING_URLs, LoopringMap, MarketInfo, MarketStatus, SEP, SoursURL, TokenAddress, TokenInfo, TOKENMAPLIST, TokenRelatedInfo, } from '../defs' import * as loopring_defs from '../defs/loopring_defs' BigNumber.config({ EXPONENTIAL_AT: 100, RANGE: [-100000, 10000000], ROUNDING_MODE: 1, }) /** * Returns hex string with '0x' prefix * @param input * @returns {string} */ export function addHexPrefix(input: any) { if (typeof input === 'string') { return input.startsWith('0x') ? input : '0x' + input } throw new Error('Unsupported type') } /** * * @param mixed Buffer|number|string (hex string must be with '0x' prefix) * @returns {Buffer} */ export function toBuffer(mixed: any): Buffer { if (mixed instanceof Buffer) { return mixed } else if (typeof mixed === 'string' && !mixed.startsWith('0x')) { return Buffer.from(mixed) } else { return ethUtil.toBuffer(mixed) as Buffer } } /** * * @param num number|string (hex string must be with '0x' prefix) * @param places number of zeros to pad * @returns {Buffer} */ export function zeroPad(num: any, places: any) { return toBuffer(String(num).padStart(places, '0')) } /** * * @param mixed number | BigNumber | BN | Buffer | string | Uint8Array * @returns {string} */ export function toHex(mixed: number | BigNumber | BN | Buffer | string | Uint8Array | BigInt) { if (typeof mixed === 'number') { return addHexPrefix(toBig(mixed).toString(16)) } if (mixed instanceof BigNumber || mixed instanceof BN) { return addHexPrefix(mixed.toString(16)) } if (mixed instanceof Buffer || mixed instanceof Uint8Array) { return addHexPrefix((mixed as Buffer).toString('hex')) } if (typeof mixed === 'string') { const regex = new RegExp(/^0x[0-9a-fA-F]*$/) return regex.test(mixed) ? mixed : addHexPrefix(toBuffer(mixed).toString('hex')) } throw new Error('Unsupported type') } /** * * @param mixed number | BigNumber | BN | Buffer | string | Uint8Array * @returns {number} */ export function toNumber(mixed: number | BigNumber | BN | Buffer | string | Uint8Array) { if (typeof mixed === 'number') { return mixed } if (mixed instanceof BigNumber || mixed instanceof BN) { return mixed.toNumber() } if (typeof mixed === 'string') { return Number(mixed) } if (mixed instanceof Buffer || mixed instanceof Uint8Array) { return Number((mixed as Buffer).toString('hex')) } throw new Error('Unsupported type') } /** * * @param mixed number | BigNumber | BN | Buffer | string | Uint8Array * @returns {BigNumber} */ export function toBig(mixed: number | BigNumber | BN | Buffer | string | Uint8Array) { if (BigNumber.isBigNumber(mixed)) { return mixed } if (typeof mixed === 'number') { return new BigNumber(mixed.toString()) } if (typeof mixed === 'string') { return new BigNumber(mixed) } if (mixed instanceof Buffer || mixed instanceof Uint8Array) { return new BigNumber((mixed as Buffer).toString('hex')) } throw new Error('Unsupported type') } /** * * @param mixed number | BigNumber | BN | Buffer | string * @returns {BN} */ export function toBN(mixed: any) { return mixed instanceof BN ? mixed : new BN(toBig(mixed).toString(10), 10) } /** * * @param value number | BigNumber | Buffer | string * @returns {BN} */ export function fromGWEI(value: any) { return new BigNumber(toBig(value).times(1e9).toFixed(0)) } /** * * @param value number | BigNumber | Buffer | string * @returns {BN} */ export function toGWEI(value: any) { return toBig(value).div(1e9) } /** * Returns formatted hex string of a given private key * @param mixed Buffer | string | Uint8Array * @returns {string} */ export function formatKey(mixed: Buffer | string | Uint8Array) { if (mixed instanceof Buffer || mixed instanceof Uint8Array) { return (mixed as Buffer).toString('hex') } if (typeof mixed === 'string') { return mixed.startsWith('0x') ? mixed.slice(2) : mixed } throw new Error('Unsupported type') } /** * Returns hex string of a given address * @param mixed Buffer | string |Uint8Array * @returns {string} */ export function formatAddress(mixed: Buffer | string | Uint8Array) { if (mixed instanceof Buffer || mixed instanceof Uint8Array) { return ethUtil.toChecksumAddress('0x' + (mixed as Buffer).toString('hex')) } if (typeof mixed === 'string') { return ethUtil.toChecksumAddress(mixed.startsWith('0x') ? mixed : '0x' + mixed) } throw new Error('Unsupported type') } /** * Returns hex string without '0x' prefix * @param input string * @returns {string} */ export function clearHexPrefix(input: any) { if (typeof input === 'string') { return input.startsWith('0x') ? input.slice(2) : input } throw new Error('Unsupported type') } /** * * @param hex * @returns {string} */ export function padLeftEven(hex: any) { return hex.length % 2 !== 0 ? `0${hex}` : hex } /** * Returns symbol of a given kind of currency * @param settingsCurrency * @returns {*} */ export function getDisplaySymbol(settingsCurrency: any) { switch (settingsCurrency) { case 'CNY': return '¥' case 'USD': return '$' default: return '' } } /** * Returns number in string with a given precision * @param number number | BigNumber * @param precision number * @param ceil bool round up * @returns {string} */ export function toFixed(number: any, precision: any, ceil: any) { precision = precision || 0 if (number instanceof BigNumber) { const rm = ceil ? 0 : 1 return number.toFixed(precision, rm) } if (typeof number === 'number') { return ceil ? (Math.ceil(number * Number('1e' + precision)) / Number('1e' + precision)).toFixed(precision) : (Math.floor(number * Number('1e' + precision)) / Number('1e' + precision)).toFixed( precision, ) } throw new Error('Unsupported type') } export function formatEddsaKey(key: any) { const hexKey = clearHexPrefix(key) return addHexPrefix(String(hexKey).padStart(64, '0')) } /** * Returns a number with commas as thousands separators * @param number number * @returns {*} */ export function numberWithCommas(number: any) { if (number) { number = number.toString().replace(/,/g, '') if (isNaN(Number(number))) { return '-' } try { const parts = number.toString().split('.') parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') return parts.join('.') } catch (err) { return '-' } } else { return number } } export function sortObjDictionary(obj: { [key: string]: any }): Map { const dataToSig: Map = new Map() if (obj) { Reflect.ownKeys(obj) .sort((a, b) => a.toString().localeCompare(b.toString())) .forEach((key) => { dataToSig.set(key.toString(), obj[key.toString()]) }) } return dataToSig } export function makeMarket(raw_data: R[]): TOKENMAPLIST { const coinMap: LoopringMap<{ icon?: string name: string simpleName: string description?: string company: string }> = {} const totalCoinMap: LoopringMap<{ icon?: string name: string simpleName: string description?: string company: string }> = {} const addressIndex: LoopringMap = {} const idIndex: LoopringMap = {} const tokensMap: LoopringMap = {} if (raw_data instanceof Array) { raw_data.forEach((item) => { if (item?.symbol.startsWith('LP-')) { item.isLpToken = true } else { item.isLpToken = false } tokensMap[item.symbol] = item const coinInfo = { icon: SoursURL + `ethereum/assets/${item.address}/logo.png`, name: item.name, simpleName: item.symbol, description: item.type, company: item.name, } if (!item.symbol.startsWith('LP-')) { coinMap[item.symbol] = coinInfo } totalCoinMap[item.symbol] = coinInfo addressIndex[item.address.toLowerCase()] = item.symbol // @ts-ignore idIndex[/vault/gi.test(item.type?.toLowerCase()) ? item?.vaultTokenId : item.tokenId] = item.symbol }) } return { tokensMap, coinMap, totalCoinMap, idIndex, addressIndex, } } export function makeAmmPool(raw_data: any): { ammpools: LoopringMap pairs: LoopringMap } { const ammpools: LoopringMap = {} const pairs: LoopringMap = {} if (raw_data?.pools instanceof Array) { raw_data.pools.forEach((item: any) => { const market: string = item.market ammpools[market] = item let base = '', quote = '' const ind = market.indexOf('-') const ind2 = market.lastIndexOf('-') base = market.substring(ind + 1, ind2) quote = market.substring(ind2 + 1, market.length) if (!pairs[base]) { pairs[base] = { tokenId: item.tokens.pooled[0], tokenList: [quote], } } else { pairs[base].tokenList = [...pairs[base].tokenList, quote] } if (!pairs[quote]) { pairs[quote] = { tokenId: item.tokens.pooled[1], tokenList: [base], } } else { pairs[quote].tokenList = [...pairs[quote].tokenList, base] } }) } return { ammpools, pairs, } } export function makeMarkets( raw_data: any, url: string = LOOPRING_URLs.GET_MARKETS, ): { markets: LoopringMap pairs: LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string } { const markets: LoopringMap = {} const pairs: LoopringMap = {} const isMix = url === LOOPRING_URLs.GET_MIX_MARKETS if (raw_data?.markets instanceof Array) { raw_data.markets.forEach((item: any) => { const marketInfo: C = { ...item, baseTokenId: item.baseTokenId, enabled: item.enabled, market: item.market, orderbookAggLevels: item.orderbookAggLevels, precisionForPrice: item.precisionForPrice, quoteTokenId: item.quoteTokenId, } if (isMix) { marketInfo.status = item.status as MarketStatus marketInfo.isSwapEnabled = marketInfo.status === MarketStatus.ALL || marketInfo.status === MarketStatus.AMM marketInfo.createdAt = parseInt(item.createdAt) } markets[item.market] = marketInfo if (item.enabled) { const market: string = item.market const ind = market.indexOf('-') const base = market.substring(0, ind) const quote = market.substring(ind + 1, market.length) if (!pairs[base]) { pairs[base] = { tokenId: item.baseTokenId, tokenList: [quote], } } else { pairs[base].tokenList = [...pairs[base].tokenList, quote] } if (!pairs[quote]) { pairs[quote] = { tokenId: item.quoteTokenId, tokenList: [base], } } else { pairs[quote].tokenList = [...pairs[quote].tokenList, base] } } }) } const marketArr: string[] = Reflect.ownKeys(markets) as string[] const tokenArr: string[] = Reflect.ownKeys(pairs) as string[] return { markets, pairs, tokenArr, tokenArrStr: tokenArr.join(SEP), marketArr, marketArrStr: marketArr.join(SEP), } } export function makeMarketsWithIdIndex( raw_data: any, url: string = LOOPRING_URLs.GET_MARKETS, idIndex: any ): { markets: LoopringMap pairs: LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string } { const markets: LoopringMap = {} const pairs: LoopringMap = {} const isMix = url === LOOPRING_URLs.GET_MIX_MARKETS if (raw_data?.markets instanceof Array) { raw_data.markets.forEach((item: any) => { const marketInfo: C = { ...item, baseTokenId: item.baseTokenId, enabled: item.enabled, market: item.market, orderbookAggLevels: item.orderbookAggLevels, precisionForPrice: item.precisionForPrice, quoteTokenId: item.quoteTokenId, } if (isMix) { marketInfo.status = item.status as MarketStatus marketInfo.isSwapEnabled = marketInfo.status === MarketStatus.ALL || marketInfo.status === MarketStatus.AMM marketInfo.createdAt = parseInt(item.createdAt) } const base = idIndex[item.baseTokenId] const quote = idIndex[item.quoteTokenId] markets[`${base}-${quote}`] = { ...marketInfo, market: `${base}-${quote}`, } if (item.enabled) { if (!pairs[base]) { pairs[base] = { tokenId: item.baseTokenId, tokenList: [quote], } } else { pairs[base].tokenList = [...pairs[base].tokenList, quote] } if (!pairs[quote]) { pairs[quote] = { tokenId: item.quoteTokenId, tokenList: [base], } } else { pairs[quote].tokenList = [...pairs[quote].tokenList, base] } } }) } const marketArr: string[] = Reflect.ownKeys(markets) as string[] const tokenArr: string[] = Reflect.ownKeys(pairs) as string[] return { markets, pairs, tokenArr, tokenArrStr: tokenArr.join(SEP), marketArr, marketArrStr: marketArr.join(SEP), } } export function makeInvestMarkets( raw_data: any, types?: string[], ): { markets: LoopringMap pairs: LoopringMap tokenArr: string[] tokenArrStr: string marketArr: string[] marketArrStr: string } { let markets: loopring_defs.LoopringMap = {} let pairs: loopring_defs.LoopringMap = {} // const isMix = url === LOOPRING_URLs.GET_MIX_MARKETS; if (raw_data?.markets instanceof Array) { let _markets = [] if (types) { _markets = raw_data.markets.filter((item: C) => types.includes(item.type?.toUpperCase())) } else { _markets = raw_data.markets } _markets.forEach((item: any) => { const marketInfo: C = { ...item, } markets[item.market] = marketInfo if (item.enabled) { const [_markets, type, base, quote] = item.market.match(/^(\w+-)?(\w+)-(\w+)$/i) if (type === 'DUAL-' && base && quote) { if (!pairs[base]) { pairs[base] = { tokenId: item.baseTokenId, tokenList: [quote], } } else { pairs[base].tokenList = [...pairs[base].tokenList, quote] } if (!pairs[quote]) { pairs[quote] = { tokenId: item.baseTokenId, tokenList: [base], } } else { pairs[quote].tokenList = [...pairs[quote].tokenList, base] } } else if (base && quote) { const market: string = item.market // const ind = market.indexOf("-"); // const base = market.substring(0, ind); // const quote = market.substring(ind + 1, market.length); if (!pairs[base]) { pairs[base] = { tokenId: item.baseTokenId, tokenList: [quote], } } else { pairs[base].tokenList = [...pairs[base].tokenList, quote] } } } }) } const marketArr: string[] = Reflect.ownKeys(markets) as string[] const tokenArr: string[] = Reflect.ownKeys(pairs) as string[] return { markets, pairs, tokenArr, tokenArrStr: tokenArr.join(SEP), marketArr, marketArrStr: marketArr.join(SEP), } } ================================================ FILE: src/utils/index.ts ================================================ export * from "./network_tools"; export * from "./symbol_tools"; export * from "./formatter"; export * from "./swap_calc_utils"; export type RequiredPart = Required<{ [K in keyof T]: Pick }> & T; ================================================ FILE: src/utils/log_tools.ts ================================================ /* eslint-disable no-console, @typescript-eslint/ban-ts-comment */ let _myLog; if ( process.env.NODE_ENV !== "production" || // @ts-ignore (typeof window !== "undefined" && window?.___OhTrustDebugger___) ) { _myLog = console.log; } else { _myLog = function (message?: any, ...optionalParams: any[]) { return ""; }; } let _myError; if ( process.env.NODE_ENV !== "production" || // @ts-ignore (typeof window !== "undefined" && window?.___OhTrustDebugger___) ) { _myError = console.error; } else { _myError = function (message?: any, ...optionalParams: any[]) { return ""; }; } export const myLog = _myLog; export const myError = _myError; ================================================ FILE: src/utils/network_tools.ts ================================================ /* eslint-disable no-console */ export const dumpError400 = (reason: any, src = '') => { if (src) { console.debug('src:', src) } if (reason && reason.response) { console.error(reason.response.data) } else { console.error(reason.message) } } export function sleep(milliseconds: number) { return new Promise((resolve) => setTimeout(resolve, milliseconds)) } ================================================ FILE: src/utils/obj_tools.ts ================================================ export const sortObject = (o: any) => Object.keys(o) .sort() .reduce((r: any, k) => ((r[k] = o[k]), r), {}); ================================================ FILE: src/utils/swap_calc_utils.ts ================================================ import * as fm from './formatter' import { toBig } from './formatter' import { ABInfo, AmmPoolSnapshot, CalDualResult, BTRADE_MARKET, BtradeResult, ConnectorError, DefiMarketInfo, DepthData, DUAL_TYPE, DualBalance, DualIndex, DualProductAndPrice, DualRulesCoinsInfo, ExitAmmPoolRequest, JoinAmmPoolRequest, LoopringErrorCode, LoopringMap, MarketInfo, OffchainFeeInfo, TokenInfo, TokenVolumeV3, XOR, VaultMarket, } from '../defs' import { getExistedMarket, getTokenInfoBySymbol } from './symbol_tools' import BigNumber from 'bignumber.js' import { myLog } from './log_tools' const BIG0 = fm.toBig(0) const BIG1 = fm.toBig(1) const BIG10 = fm.toBig(10) const BIG10K = fm.toBig(10000) export const getToken = (tokens: any, token: any) => { if (!tokens) { throw Error('no tokens list!') } return tokens[token] } export const getTokenInfoByToken = (ammBalance: any, tokens: any, token: any) => { const tokenInfo = getToken(tokens, token) const tokenVol = ammBalance.pooledMap[tokenInfo.tokenId].volume const reserve = fm.toBig(tokenVol) return { tokenInfo, tokenVol, reserve, } } export function fromWEI(tokens: any, symbol: any, valueInWEI: any, precision?: any, ceil?: any) { try { const tokenInfo = getToken(tokens, symbol) const precisionToFixed = precision ? precision : tokenInfo.precision const value = fm.toBig(valueInWEI).div('1e' + tokenInfo.decimals) return fm.toFixed(value, precisionToFixed, ceil) } catch (err) { return undefined } return '0' } export function toWEI(tokens: any, symbol: any, value: any, rm: any = undefined) { const tokenInfo = getToken(tokens, symbol) if (typeof tokenInfo === 'undefined') { return '0' } const bigN = fm.toBig(value).times('1e' + tokenInfo.decimals) return rm === undefined ? bigN.toString() : bigN.toFixed(0, rm) } export function isEmpty(input: any) { if (!input || input.trim() === '') { return true } return false } function getAmountOutWithFeeBips( amountIn: string, feeBips: string, reserveIn: string, reserveOut: string, ) { const amountInBig = fm.toBig(amountIn) const reserveInBig = fm.toBig(reserveIn) const reserveOutBig = fm.toBig(reserveOut) if (amountInBig.lt(BIG0) || reserveInBig.lt(BIG0) || reserveOutBig.lt(BIG0)) { return BIG0 } const feeBipsBig = fm.toBig(feeBips) const amountInWithFee = amountInBig.times(BIG10K.minus(feeBipsBig)) const numerator = amountInWithFee.times(reserveOutBig) const denominator = reserveInBig.times(BIG10K).plus(amountInWithFee) return numerator.div(denominator) } function getAmountInWithFeeBips( amountOut: string, feeBips: string, reserveIn: string, reserveOut: string, ) { const amountOutBig = fm.toBig(amountOut) const reserveInBig = fm.toBig(reserveIn) const reserveOutBig = fm.toBig(reserveOut) if (amountOutBig.lt(BIG0) || reserveInBig.lt(BIG0) || reserveOutBig.lt(BIG0)) { return BIG0 } const feeBipsBig = fm.toBig(feeBips) const numerator = reserveInBig.times(amountOutBig).times(BIG10K) const denominator = reserveOutBig.minus(amountOutBig).times(BIG10K.minus(feeBipsBig)) return numerator.div(denominator).plus(BIG1) } function getOutputOrderbook( input: string, baseToken: TokenInfo | undefined, quoteToken: TokenInfo | undefined, feeBips: string, isAtoB: boolean, isReverse: boolean, depth: DepthData, ) { let output = '0' let remain: string = input const bids = depth.bids // .reverse() // console.log('bids:', bids[0]) // console.log('bids last:', bids[bids.length - 1]) // console.log('asks:', depth.asks[0]) // console.log('asks last:', depth.asks[depth.asks.length - 1]) // console.log(`isAtoB:${isAtoB} isReverse:${isReverse}`) if (!baseToken || !quoteToken) { return output } // myLog(baseToken, ' ', quoteToken) //amt is size(base ETH). vol is volume(quote USDT) if (isAtoB) { if (!isReverse) { //ETH -> USDT remain = fm .toBig(remain) .times('1e' + baseToken.decimals) .toString() for (let i = bids.length - 1; i >= 0; i--) { const abInfo: ABInfo = bids[i] if (fm.toBig(abInfo.amt).lte(BIG0)) { continue } // console.log(`i:${i} abInfo:`, abInfo, `decimals:${baseToken.decimals} ${quoteToken.decimals}`) const consume: string = fm.toBig(remain).gte(fm.toBig(abInfo.amt)) ? abInfo.amt : remain if (fm.toBig(consume).lte(BIG0)) { break } const volValue = fm.toBig(abInfo.vol).div('1e' + quoteToken.decimals) if (fm.toBig(consume).eq(fm.toBig(abInfo.amt))) { output = fm.toBig(output).plus(volValue).toString() } else { const ratio = fm.toBig(consume).div(fm.toBig(abInfo.amt)) // myLog('got ratio:', ratio.toString(), consume, abInfo.amt) output = fm.toBig(output).plus(ratio.times(volValue)).toString() } // myLog('1__ ', i, ' output:', output, ' remain:', remain, ' abInfo.amt:', abInfo.amt, ' abInfo.vol:', abInfo.vol, ' volValue:', volValue.toString()) remain = fm.toBig(remain).minus(fm.toBig(consume)).toString() } } else { // USDT -> ETH // isAtoB = true, isReverse = false remain = fm.toBig(remain).times(BIG10.pow(baseToken.decimals)).toString() for (let i = 0; i < depth.asks.length; i++) { const abInfo: ABInfo = depth.asks[i] if (fm.toBig(abInfo.amt).lte(BIG0)) { continue } // const placed: string = fm.toBig(abInfo.vol).div(BIG10.pow(quoteToken.decimals)).toString() const consume: string = fm.toBig(remain).gte(fm.toBig(abInfo.vol)) ? abInfo.vol : remain if (fm.toBig(consume).lte(BIG0)) { // console.log('return 22222') break } // console.log(`i:${i} abInfo:`, abInfo, `decimals:${baseToken.decimals} ${quoteToken.decimals}`) // console.log('remain:', remain, ' abInfo.vol:', abInfo.vol, ' consume:', consume) const amtValue = fm.toBig(abInfo.amt).div('1e' + quoteToken.decimals) if (fm.toBig(consume).eq(fm.toBig(abInfo.vol))) { output = fm.toBig(output).plus(amtValue).toString() } else { const ratio = fm.toBig(consume).div(fm.toBig(abInfo.vol)) output = fm.toBig(output).plus(ratio.times(amtValue)).toString() } remain = fm.toBig(remain).minus(fm.toBig(consume)).toString() // myLog('2__ ', i, ' output:', output, ' abInfo.vol:', abInfo.vol, ' remain:', remain) } } } else { if (!isReverse) { // ETH <- USDT remain = fm.toBig(remain).times(BIG10.pow(quoteToken.decimals)).toString() for (let i = bids.length - 1; i >= 0; i--) { const abInfo: ABInfo = bids[i] // const placed: string = fm.toBig(abInfo.vol).div(BIG10.pow(quoteToken.decimals)).toString() const consume: string = fm.toBig(remain).gte(fm.toBig(abInfo.vol)) ? abInfo.vol : remain if (fm.toBig(consume).lte(BIG0)) { break } // myLog(`i:${i} abInfo:`, abInfo, `decimals:${baseToken.decimals} ${quoteToken.decimals}`) // myLog('remain:', remain, 'abInfo.vol:', abInfo.vol, ' consume:', consume) const amtValue = fm.toBig(abInfo.amt).div(BIG10.pow(baseToken.decimals)) if (fm.toBig(consume).eq(abInfo.vol)) { output = fm.toBig(output).plus(fm.toBig(amtValue)).toString() } else { const ratio = fm.toBig(consume).div(fm.toBig(abInfo.vol)) output = fm.toBig(output).plus(ratio.times(amtValue)).toString() } remain = fm.toBig(remain).minus(fm.toBig(consume)).toString() // myLog('3__', i, ' output:', output, ' abInfo.vol:', abInfo.vol, ' remain:', remain) } } else { // USDT <- ETH remain = fm.toBig(remain).times(BIG10.pow(quoteToken.decimals)).toString() for (let i = 0; i < depth.asks.length; i++) { const abInfo: ABInfo = depth.asks[i] // myLog(`i:${i} abInfo:`, abInfo, `decimals:${baseToken.decimals} ${quoteToken.decimals}`) const consume: string = fm.toBig(remain).gte(fm.toBig(abInfo.amt)) ? abInfo.amt : remain if (fm.toBig(consume).lte(BIG0)) { break } const volValue = fm.toBig(abInfo.vol).div(BIG10.pow(baseToken.decimals)) if (fm.toBig(consume).eq(fm.toBig(abInfo.amt))) { output = fm.toBig(output).plus(volValue).toString() } else { const ratio = fm.toBig(consume).div(fm.toBig(abInfo.amt)) output = fm.toBig(output).plus(ratio.times(volValue)).toString() } // myLog('4__', i, ' output:', output, ' abInfo.vol:', abInfo.vol, ' volValue:', volValue.toString()) remain = fm.toBig(remain).minus(fm.toBig(consume)).toString() } } } return output } export function getReserveInfo( sell: string, buy: string, marketArr: string[], tokenMap: LoopringMap, marketMap: LoopringMap, ammPoolSnapshot: AmmPoolSnapshot | undefined = undefined, ) { const { market, amm, baseShow, quoteShow } = getExistedMarket(marketArr, sell, buy) if (isEmpty(market) || isEmpty(amm) || Object.keys(marketMap).indexOf(market) < 0) { return undefined } const marketInfo: MarketInfo = marketMap[market] const sellToken = getTokenInfoBySymbol(tokenMap, sell) const buyToken = getTokenInfoBySymbol(tokenMap, buy) let isReverse = false const coinA = ammPoolSnapshot?.pooled[0] const coinB = ammPoolSnapshot?.pooled[1] let reserveIn = '0' let reserveOut = '0' if ( sellToken?.tokenId !== undefined && buyToken?.tokenId !== undefined && coinA?.tokenId !== undefined && coinB?.tokenId !== undefined ) { if (sellToken?.tokenId === coinA?.tokenId) { reserveIn = coinA.volume reserveOut = coinB.volume } else { reserveIn = coinB.volume reserveOut = coinA.volume isReverse = true } } else { if (market === `${buy}-${sell}`) { isReverse = true } } return { reserveIn, reserveOut, sellToken, buyToken, coinA, coinB, isReverse, marketInfo, } } function getPriceImpactStr(curPrice: string, toPrice: string) { if (!curPrice || !toPrice) { return '0' } const toPriceBig = fm.toBig(toPrice) if (toPriceBig.eq(BIG0)) { return '0' } const percent = fm.toBig(toPriceBig).div(curPrice) return BIG1.minus(percent).abs().toString() } export function getCurPrice(reserveIn: string, reserveOut: string) { if (!reserveIn || !reserveOut) { return '0' } reserveIn = reserveIn.trim() reserveOut = reserveOut.trim() const reserveInBig = fm.toBig(reserveIn) const reserveOutBig = fm.toBig(reserveOut) if (reserveInBig.eq(BIG0)) { return '0' } return reserveOutBig.div(reserveInBig).toString() } export function getToPrice(amountS: string, amountB: string) { if (!amountS || !amountB) { return '0' } amountS = amountS.trim() amountB = amountB.trim() const amountSBig = fm.toBig(amountS) const amountBBig = fm.toBig(amountB) if (amountSBig.eq(BIG0)) { return '0' } return amountBBig.div(amountSBig).toString() } export function getPriceImpact( reserveIn: string, reserveOut: string, amountS: string, feeBips: string, takerFee: string, ) { let amountB: BigNumber = getAmountOutWithFeeBips(amountS, feeBips, reserveIn, reserveOut) amountB = amountB.times(BIG10K.minus(fm.toBig(takerFee))).div(BIG10K) const curPrice = getCurPrice(reserveIn, reserveOut) const toPrice = getToPrice(amountS, amountB.toString()) return getPriceImpactStr(curPrice, toPrice) } export function updatePriceImpact_new( reverseIn: string, reverseOut: string, amountS: string, sellDecimal: number, amountBOut: string, buyDecimal: number, feeBips: string, takerFee: string, isAtoB: boolean, isReversed: boolean, exceedDepth: boolean, depth: DepthData, ) { let priceImpact = '0' if (isEmpty(reverseIn) || isEmpty(reverseOut) || isEmpty(feeBips)) { return '0' } if (exceedDepth) { priceImpact = getPriceImpact(reverseIn, reverseOut, amountS, feeBips, '0') } else { if (!depth.mid_price) { return '0' } // LRC / ETH !isReversed isAtoB const coinADecimal = !isReversed ? sellDecimal : buyDecimal const coinBDecimal = !isReversed ? buyDecimal : sellDecimal const curPrice = fm .toBig(depth.mid_price) .times('1e' + coinBDecimal) .div('1e' + coinADecimal) .toString() const toPrice = !isReversed ? getToPrice(amountS, amountBOut) : getToPrice(amountBOut, amountS) // console.log('updatePriceImpact_new isReversed:', isReversed, ' amountS:', amountS, ' amountBOut:', amountBOut) // console.log('updatePriceImpact_new toPrice:', toPrice, ' curPrice:', curPrice) priceImpact = getPriceImpactStr(curPrice, toPrice) } return priceImpact } export function getMinReceived(amountBOut: string, minimumDecimal: number, slipBips: string) { const minReceived = fm .toBig(amountBOut) .times(BIG10K.minus(fm.toBig(slipBips))) .div(BIG10K) return { minReceived: minReceived.toFixed(0, 0), minReceivedVal: minReceived.div('1e' + minimumDecimal).toString(), minimumDecimal, } } export function getOutputAmount({ input, sell, buy, isAtoB, marketArr, tokenMap, marketMap, depth, ammPoolSnapshot, feeBips, takerRate, slipBips, }: { input: string sell: string buy: string isAtoB: boolean marketArr: string[] tokenMap: LoopringMap marketMap: LoopringMap depth: DepthData ammPoolSnapshot: AmmPoolSnapshot | undefined feeBips: string takerRate: string slipBips: string }): | { exceedDepth: boolean isReverse: boolean isAtoB: boolean slipBips: string takerRate: string feeBips: string output: any sellAmt: string buyAmt: string amountS: string amountBOut: string amountBOutWithoutFee: string amountBOutSlip: { minReceived: string minReceivedVal: string minimumDecimal: number } priceImpact: string } | undefined { // console.log('enter getOutputAmount:', input, base, quote, isAtoB, marketArr, tokenMap, marketMap, depth, ammPoolSnapshot, feeBips, takerRate, slipBips) // console.log(`getOutputAmount market: ${base} / ${quote}`) // console.log('ammPoolSnapshot:', ammPoolSnapshot) const reserveInfo = getReserveInfo(sell, buy, marketArr, tokenMap, marketMap, ammPoolSnapshot) if (!reserveInfo) { return undefined } const { reserveIn, reserveOut, sellToken, buyToken, isReverse, marketInfo } = reserveInfo if (!sellToken || !buyToken) { return undefined } input = input.trim() let exceedDepth = false let output: any = '0' let amountS = '0' let amountBOutWithoutFee = '0' let amountBOut = '0' let sellAmt = '0' let buyAmt = '0' let minimumDecimal = 0 if (isAtoB) { // bids_amtTotal -> bidsSizeShown // asks_volTotal -> asksQuoteSizeShown const amountInWei = toWEI(tokenMap, sell, input, 0) // console.log('isAtoB amountInWei:', amountInWei) if (isEmpty(depth.bids_amtTotal) || isEmpty(depth.asks_volTotal)) { exceedDepth = true } else { if (!isReverse) { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.bids_amtTotal)) // console.log('3 amountInWei:', amountInWei, ' bids_amtTotal:', depth.bids_amtTotal) } else { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.asks_volTotal)) // console.log('4 amountInWei:', amountInWei, ' asks_volTotal:', depth.asks_volTotal) } } // console.log(`a2b(input:${input}) exceedDepth:`, exceedDepth, ' isSwapEnabled:', marketInfo.isSwapEnabled) if (exceedDepth) { if (marketInfo.isSwapEnabled) { const amountB = getAmountOutWithFeeBips(amountInWei, feeBips, reserveIn, reserveOut) output = fromWEI(tokenMap, buy, amountB.toFixed(0, 0)) } } else { output = getOutputOrderbook(input, sellToken, buyToken, feeBips, isAtoB, isReverse, depth) } amountBOutWithoutFee = toWEI(tokenMap, buy, output, 0) const leftRatio = BIG10K.minus(fm.toBig(takerRate)).div(BIG10K) // console.log('amountBOutWithoutFee:', amountBOutWithoutFee, ' leftRatio:', leftRatio.toString()) amountBOut = toWEI(tokenMap, buy, fm.toBig(output).times(leftRatio).toString(), 0) amountS = toWEI(tokenMap, sell, input, 0) sellAmt = input buyAmt = output } else { // asks_amtTotal -> asksSizeShown // bids_volTotal -> bidsQuoteSizeShown if (isEmpty(depth.bids_volTotal) || isEmpty(depth.asks_amtTotal)) { exceedDepth = true } else { const amountInWei = toWEI(tokenMap, buy, input, 0) if (!isReverse) { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.bids_volTotal)) } else { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.asks_amtTotal)) } } let amountSBint = BIG0 const amountB: string = toWEI(tokenMap, buy, input, 0) // console.log(`b2a(input:${input}) exceedDepth:${exceedDepth} amountB:${amountB}`) if (exceedDepth) { if (marketInfo.isSwapEnabled) { amountSBint = getAmountInWithFeeBips(amountB, feeBips, reserveIn, reserveOut) } } else { const outputOrderbook = getOutputOrderbook( input, sellToken, buyToken, feeBips, isAtoB, isReverse, depth, ) amountSBint = fm.toBig(toWEI(tokenMap, sell, outputOrderbook)) } if (amountSBint.gt(BIG0)) { output = fromWEI(tokenMap, sell, amountSBint.toString()) amountBOutWithoutFee = fm.toBig(amountB).toFixed(0, 0) // amountBOutWithoutFee = amountB const leftRatio = BIG10K.minus(fm.toBig(takerRate)).div(BIG10K) amountBOut = fm.toBig(amountB).times(leftRatio).toFixed(0, 0) } amountS = amountSBint.toFixed(0, 0) // console.log('got amountSBint:', amountSBint.toString(), amountSBint.gt(BIG0), ' amountBOut:', amountBOut.toString()) sellAmt = output buyAmt = input } minimumDecimal = buyToken.decimals const amountBOutSlip = getMinReceived(amountBOut, minimumDecimal, slipBips) const priceImpact = updatePriceImpact_new( reserveIn, reserveOut, amountS, sellToken.decimals, amountBOut, buyToken.decimals, feeBips, takerRate, isAtoB, isReverse, exceedDepth, depth, ) return { exceedDepth, isReverse, isAtoB, slipBips, takerRate, feeBips, output, sellAmt, buyAmt, amountS, amountBOut, amountBOutWithoutFee, amountBOutSlip, priceImpact, } } export function ammPoolCalc( rawVal: string, isAtoB: boolean, coinA: TokenVolumeV3, coinB: TokenVolumeV3, ) { const coinA_Vol_BIG = fm.toBig(coinA.volume) const coinB_Vol_BIG = fm.toBig(coinB.volume) let output = BIG0 let ratio = BIG0 if (isAtoB) { if (!coinA_Vol_BIG.eq(BIG0)) { ratio = fm.toBig(rawVal).div(coinA_Vol_BIG) output = ratio.times(coinB_Vol_BIG) } } else { if (!coinB_Vol_BIG.eq(BIG0)) { ratio = fm.toBig(rawVal).div(coinB_Vol_BIG) output = ratio.times(coinA_Vol_BIG) } } return { output: output.toFixed(0, 0), ratio, } } /** * * @param rawVal * @param isAtoB * @param slippageTolerance * @param owner * @param fees * @param ammPoolSnapshot * @param tokenMap * @param idIdx * @param coinAOffchainId * @param coinBOffchainId * @param rawValMatchForRawVal first time add to pool */ export function makeJoinAmmPoolRequest( rawVal: string, isAtoB: boolean, slippageTolerance: string, owner: string, fees: LoopringMap, ammPoolSnapshot: AmmPoolSnapshot, tokenMap: LoopringMap, idIdx: LoopringMap, coinAOffchainId = 0, coinBOffchainId = 0, rawValMatchForRawVal?: string, ) { const coinA: TokenVolumeV3 = ammPoolSnapshot.pooled[0] const coinB: TokenVolumeV3 = ammPoolSnapshot.pooled[1] const baseToken: TokenInfo = tokenMap[idIdx[coinA.tokenId]] const quoteToken: TokenInfo = tokenMap[idIdx[coinB.tokenId]] const fee = fees && fees[quoteToken.symbol] && fees[quoteToken.symbol].fee ? fees[quoteToken.symbol].fee : '0' rawVal = fm .toBig(rawVal) .times(BIG10.pow(isAtoB ? baseToken.decimals : quoteToken.decimals)) .toFixed(0, 0) // eslint-disable-next-line prefer-const let { output, ratio } = ammPoolCalc(rawVal, isAtoB, coinA, coinB) let volLp if (output === '0' && rawValMatchForRawVal) { output = fm .toBig(rawValMatchForRawVal) .times(BIG10.pow(isAtoB ? quoteToken.decimals : baseToken.decimals)) .toFixed(0, 0) // ratio = fm.toBig("1"); volLp = '1' } else { const rest = BIG1.minus(fm.toBig(slippageTolerance)) volLp = fm.toBig(ammPoolSnapshot.lp.volume).times(ratio).times(rest).toFixed(0, 0) } const volA = isAtoB ? rawVal : output const volB = isAtoB ? output : rawVal const request: JoinAmmPoolRequest = { owner, poolAddress: ammPoolSnapshot.poolAddress, joinTokens: { pooled: [ { tokenId: coinA.tokenId, volume: volA }, { tokenId: coinB.tokenId, volume: volB }, ], minimumLp: { tokenId: ammPoolSnapshot.lp.tokenId, volume: volLp }, }, storageIds: [coinAOffchainId, coinBOffchainId], fee, } return { request, } } export function makeExitAmmPoolMini( rawVal: string, ammPoolSnapshot: AmmPoolSnapshot, tokenMap: LoopringMap, idIdx: LoopringMap, RatioDecimal = 10, ) { const lpTokenVol: TokenVolumeV3 = ammPoolSnapshot.lp const lpToken: TokenInfo = tokenMap[idIdx[lpTokenVol.tokenId]] const miniLpVol = fm //minLP Volume big number .toBig(lpTokenVol.volume) .times(2) .div('1e' + RatioDecimal) return { miniLpVol: miniLpVol.toString(), miniLpVal: fm .toBig(miniLpVol) .div('1e' + lpToken.decimals) .toString(), } } export function makeExitAmmCoverFeeLP( fees: LoopringMap, ammPoolSnapshot: AmmPoolSnapshot, tokenMap: LoopringMap, idIdx: LoopringMap, slippageTolerance = '0.001', ) { const lpTokenVol: TokenVolumeV3 = ammPoolSnapshot.lp const lpToken: TokenInfo = tokenMap[idIdx[lpTokenVol.tokenId]] const quote: TokenVolumeV3 = ammPoolSnapshot.pooled[1] const quoteToken: TokenInfo = tokenMap[idIdx[quote.tokenId]] const quoteVolume = quote.volume const maxFee = fees && fees[quoteToken.symbol] ? fees[quoteToken.symbol].fee : '0' // feeLp = fee /snap.quote*snap.lp const feeLp = fm.toBig(maxFee).times(lpTokenVol.volume).div(quoteVolume).plus(1) // feeLp = feeLp / (1-slippageTolerance) slippageTolerance default is 0.001 const feeLpWithSlippage = feeLp.div(BIG1.minus(fm.toBig(slippageTolerance))) return { feeLp: feeLp.toString(), feeLpWithSlippage: feeLpWithSlippage.toString(), miniFeeLpWithSlippageVal: fm .toBig(feeLpWithSlippage) .div('1e' + lpToken.decimals) .toString(), feeLpVal: fm .toBig(feeLp) .div('1e' + lpToken.decimals) .toString(), } } export function makeExitAmmPoolRequest2( rawVal: string, slippageTolerance: string, owner: string, fees: LoopringMap, ammPoolSnapshot: AmmPoolSnapshot, tokenMap: LoopringMap, idIdx: LoopringMap, offchainId = 0, minDecimal = 10, ) { const lpTokenVol: TokenVolumeV3 = ammPoolSnapshot.lp const lpToken: TokenInfo = tokenMap[idIdx[lpTokenVol.tokenId]] const burnedVol = fm .toBig(rawVal) .times('1e' + lpToken.decimals) .toFixed(0, 0) const ratio = fm .toBig(burnedVol) .times('1e' + minDecimal) .div(lpTokenVol.volume) .toFixed(0, 1) const coinA: TokenVolumeV3 = ammPoolSnapshot.pooled[0] const coinB: TokenVolumeV3 = ammPoolSnapshot.pooled[1] const rest = BIG1.minus(fm.toBig(slippageTolerance)) const volA = toBig(ratio) .times(coinA.volume) .div('1e' + minDecimal) .times(rest) .toFixed(0, 0) const volB = toBig(ratio) .times(coinB.volume) .div('1e' + minDecimal) .times(rest) .toFixed(0, 0) const baseToken: TokenInfo = tokenMap[idIdx[coinA.tokenId]] const quoteToken: TokenInfo = tokenMap[idIdx[coinB.tokenId]] const maxFee = fees && fees[quoteToken.symbol] ? fees[quoteToken.symbol].fee : '0' const request: ExitAmmPoolRequest = { owner, poolAddress: ammPoolSnapshot.poolAddress, exitTokens: { unPooled: [ { tokenId: coinA.tokenId, volume: volA }, { tokenId: coinB.tokenId, volume: volB }, ], burned: { tokenId: ammPoolSnapshot.lp.tokenId, volume: burnedVol }, }, storageId: offchainId, maxFee, } return { ratio, volA, volB, volA_show: fm .toBig(volA) .div('1e' + baseToken.decimals) .toString(), volB_show: fm .toBig(volB) .div('1e' + quoteToken.decimals) .toString(), request, } } /** * calcDefi * @param isJoin {boolean} true is join, false is exit * @param isInputSell {boolean} user input sell of buy * @param XOR user input sell amount number (without decimals) * @param feeVol fee Volume from server-side (decimals) * @param marketInfo {DefiMarketInfo} DefiMarketInfo from sever-side * @param tokenSell {TokenInfo} token Config information * @param tokenBuy {TokenInfo} token Config information * @param buyTokenBalanceVol buy Token Balance server-side (decimals) * @return {sellVol} sell Volume (decimals); * @return {buyVol} buy Volume (decimals); * @return {maxSellVol} max Sell Volume (decimals); please use ceil for view * @return {miniSellVol} min Sell Volume (decimals); please use round for view * @return {maxFeeBips} number maxFeeBips; * @return {isJoin} boolean; * @return {isInputSell} boolean; */ export function calcDefi({ isJoin, isInputSell, sellAmount, buyAmount, defaultFee, maxFeeBips, marketInfo, tokenSell, tokenBuy, buyTokenBalanceVol, withdrawFeeBips, }: { isJoin: boolean isInputSell: boolean maxFeeBips: number defaultFee: string marketInfo: DefiMarketInfo tokenSell: TokenInfo tokenBuy: TokenInfo buyTokenBalanceVol: string withdrawFeeBips?: string | undefined // feeVol } & XOR<{ sellAmount: string }, { buyAmount: string }>): { sellVol: string buyVol: string maxSellVol: string feeVol: string maxFeeBips: number miniSellVol: string isJoin: boolean isInputSell: boolean } { /** isDeposit calc sellPrice & buyPrice */ const [sellPrice] = isJoin ? [marketInfo.depositPrice] : [marketInfo.withdrawPrice] /** calc MiniSellVol & MaxSellVol**/ const dustToken = tokenBuy const maxSellVol = fm.toBig(buyTokenBalanceVol).div(sellPrice) /** calc MiniSellVol & MaxSellVol END**/ // debugger; /** View input calc sellVol & buyVol */ let sellVol, buyVol if (isInputSell) { sellVol = fm.toBig(sellAmount ? sellAmount : 0).times('1e' + tokenSell.decimals) buyVol = sellVol.times(sellPrice) } else { buyVol = fm.toBig(buyAmount ? buyAmount : 0).times('1e' + tokenBuy.decimals) sellVol = buyVol.div(sellPrice) } /** View input calc sellVol & buyVol END */ let feeVol /** calc current maxFeeBips **/ let cosFeeBips: any = buyVol.gt(0) ? fm.toBig(defaultFee).times(10000).div(buyVol).toFixed(0, BigNumber.ROUND_CEIL) : 0 let _maxFeeBips: any = BigNumber.max(cosFeeBips, 5, maxFeeBips) if (!isJoin && withdrawFeeBips) { _maxFeeBips = fm .toBig(_maxFeeBips) .plus(withdrawFeeBips ?? 0) .toString() } feeVol = fm.toBig(_maxFeeBips).div(10000).times(buyVol).toString() // defaultFee const minVolBuy = BigNumber.max(fm.toBig(feeVol).times(2), dustToken.orderAmounts.dust) const miniSellVol = BigNumber.max(minVolBuy.div(sellPrice), tokenSell.orderAmounts.dust) return { sellVol: sellVol.toString(), buyVol: buyVol.toString(), maxSellVol: maxSellVol.toString(), feeVol, isJoin, isInputSell, maxFeeBips: Number(_maxFeeBips), miniSellVol: miniSellVol.toString(), } } /** * * @param info * @param index * @param rule * @param balance * @param feeVol * @param sellToken * @param buyToken * @param sellAmount * @param currentPrice */ export function calcDual({ info, index, rule, balance, feeVol, sellToken, // dualViewInfo, buyToken, sellAmount, dualMarket, }: { sellAmount: string | undefined info: DualProductAndPrice index: DualIndex rule: DualRulesCoinsInfo balance: { [key: string]: DualBalance } sellToken: TokenInfo buyToken: TokenInfo // dualViewInfo:R feeVol?: string | undefined dualMarket: DefiMarketInfo }): CalDualResult { const sellVol = fm.toBig(sellAmount ? sellAmount : 0).times('1e' + sellToken.decimals) let lessEarnVol, lessEarnTokenSymbol, greaterEarnVol, greaterEarnTokenSymbol, maxSellAmount, miniSellVol, feeTokenSymbol, quota, maxFeeBips let { base } = info let { extra: { quoteAssetSymbol }, } = dualMarket const settleRatio = fm.toBig(info.profit).times(info.ratio).toFixed(6, BigNumber.ROUND_DOWN) if (info.dualType === DUAL_TYPE.DUAL_BASE) { lessEarnVol = toBig(sellVol) lessEarnTokenSymbol = sellToken.symbol greaterEarnVol = toBig( toBig(sellAmount ? sellAmount : 0) .times(info.strike) .toFixed(buyToken.precision, BigNumber.ROUND_CEIL), ).times('1e' + buyToken.decimals) // myLog('greaterEarnVol',toBig(settleRatio).plus(1).times(sellAmount ?sellAmount: 0) // .times(info.strike).toFixed(buyToken.precision,BigNumber.ROUND_CEIL).toString(), greaterEarnVol.toString()) greaterEarnTokenSymbol = buyToken.symbol miniSellVol = BigNumber.max( dualMarket.baseLimitAmount, toBig(rule.baseMin).times('1e' + sellToken.decimals), ) // rule.baseMin; quota = BigNumber.min(info.baseSize, balance ? balance[base]?.free : 0) // quota = BigNumber.min(100, balance ? balance[ base ]?.free : 0) maxSellAmount = BigNumber.min(rule.baseMax ? rule.baseMax : 0, quota) feeTokenSymbol = buyToken.symbol maxFeeBips = 5 //info.dualPrice.dualBid[ 0 ].baseQty; } else { lessEarnVol = toBig( toBig(sellAmount ? sellAmount : 0) // .times(1 + info.ratio) .div(info.strike) .toFixed(buyToken.precision, BigNumber.ROUND_CEIL), ).times('1e' + buyToken.decimals) // sellVol.times(1 + info.ratio).div(dualViewInfo.strike); //.times(1 + dualViewInfo.settleRatio); lessEarnTokenSymbol = buyToken.symbol greaterEarnVol = toBig(sellVol) //.div(dualViewInfo.strike); greaterEarnTokenSymbol = sellToken.symbol miniSellVol = BigNumber.max( dualMarket.quoteLimitAmount, toBig(rule.currencyMin).times('1e' + sellToken.decimals), ) // rule.baseMin; quota = BigNumber.min( toBig(info.baseSize).times(info.strike).toString(), balance[quoteAssetSymbol].free, ) maxSellAmount = BigNumber.min(rule.currencyMax, quota) /** calc current maxFeeBips **/ feeTokenSymbol = buyToken.symbol maxFeeBips = 5 } myLog( 'settleRatio', settleRatio, lessEarnVol.toString(), greaterEarnVol.toString(), 'strike', info.strike, ) return { quota: quota.toString(), sellVol: sellVol.toString(), lessEarnVol: lessEarnVol.toString(), lessEarnTokenSymbol, greaterEarnVol: greaterEarnVol.toString(), greaterEarnTokenSymbol, miniSellVol: miniSellVol.toString(), sellToken, maxSellAmount: maxSellAmount ? maxSellAmount?.toString() : '', maxFeeBips: maxFeeBips, // dualViewInfo: dualViewInfo as unknown as R, feeVol, feeTokenSymbol, } } /** * * @param info BTRADE_MARKET * @param index * @param rule * @param balance * @param feeVol * @param sellToken * @param buyToken * @param sellAmount * @param currentPrice */ export function calcDex({ info, input, sell, buy, isAtoB, marketArr, tokenMap, marketMap, depth, feeBips, slipBips, }: { info: R input: string sell: string buy: string isAtoB: boolean marketArr: string[] tokenMap: LoopringMap marketMap: LoopringMap depth: DepthData feeBips: string slipBips: string }): BtradeResult | undefined { const sellToken = tokenMap[sell] const buyToken = tokenMap[buy] const reserveInfo = getReserveInfo(sell, buy, marketArr, tokenMap, marketMap) if (!reserveInfo) { throw { message: ConnectorError.BTRADE_NO_PRODUCT, msg: ConnectorError.BTRADE_NO_PRODUCT, code: LoopringErrorCode.BTRADE_NO_PRODUCT, } } const { isReverse } = reserveInfo let sellVol, buyVol, amountB, amountS, exceedDepth, amountBSlipped if (isAtoB) { amountS = input sellVol = fm.toBig(input ? input : 0).times('1e' + sellToken.decimals) const amountInWei = sellVol if (isEmpty(depth.bids_amtTotal) || isEmpty(depth.asks_volTotal)) { throw { message: ConnectorError.BTRADE_NO_DEPTH_ERROR, msg: ConnectorError.BTRADE_NO_DEPTH_ERROR, code: LoopringErrorCode.BTRADE_NO_DEPTH_ERROR, } } else { if (!isReverse) { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.bids_amtTotal)) } else { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.asks_volTotal)) } let outputOrderbook if (exceedDepth) { outputOrderbook = fm .toBig(amountS) .times(!isReverse ? depth.mid_price : 1 / depth.mid_price) amountB = outputOrderbook.toString() buyVol = fm.toBig(amountB).times('1e' + buyToken.decimals) } else { outputOrderbook = getOutputOrderbook( input, sellToken, buyToken, feeBips, isAtoB, isReverse, depth, ).toString() amountB = outputOrderbook buyVol = toWEI(tokenMap, buy, outputOrderbook) } } } else { amountB = input buyVol = fm.toBig(input ? input : 0).times('1e' + buyToken.decimals) const amountInWei = buyVol if (isEmpty(depth.bids_volTotal) || isEmpty(depth.asks_amtTotal)) { throw { message: ConnectorError.BTRADE_NO_DEPTH_ERROR, msg: ConnectorError.BTRADE_NO_DEPTH_ERROR, code: LoopringErrorCode.BTRADE_NO_DEPTH_ERROR, } } else { if (!isReverse) { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.bids_volTotal)) } else { exceedDepth = fm.toBig(amountInWei).gt(fm.toBig(depth.asks_amtTotal)) } let outputOrderbook if (exceedDepth) { outputOrderbook = fm.toBig(buyVol).times(!isReverse ? 1 / depth.mid_price : depth.mid_price) amountS = outputOrderbook.toString() sellVol = fm .toBig(amountS) .times('1e' + sellToken.decimals) .toString() } else { outputOrderbook = getOutputOrderbook( input, sellToken, buyToken, feeBips, isAtoB, isReverse, depth, ) myLog(outputOrderbook.toString()) // sellVol = outputOrderbook; sellVol = toWEI(tokenMap, sell, outputOrderbook) amountS = fm .toBig(sellVol ?? 0) .div('1e' + sellToken.decimals) .toString() } } // console.log(`b2a(input:${input}) exceedDepth:${exceedDepth} amountB:${amountB}`) } if (buyVol) { amountBSlipped = getMinReceived(buyVol.toString(), buyToken.decimals, slipBips) // amountBMiniReceiveCutFee = amountBSlipped.minReceived - feeBips } return { feeBips, info, isAtoB, isReverse, sellVol: sellVol.toString(), buyVol: buyVol.toString(), amountB: amountB?.toString(), amountS: amountS?.toString(), amountBSlipped, exceedDepth, } } ================================================ FILE: src/utils/symbol_tools.ts ================================================ import { TokenInfo } from "defs"; const specialSymbols = ["ETH2x-FIL"]; export function getBaseQuote(symbol: string) { if (!symbol) { return { base: undefined, quote: undefined, }; } if (symbol.startsWith("AMM-")) { symbol = symbol.substr(4); } if (specialSymbols.length > 0) { for (let i = 0; i < specialSymbols.length; i++) { const ind = symbol.indexOf(specialSymbols[i]); if (ind >= 0) { if (ind === 0) { return { base: specialSymbols[i], quote: symbol.substr(symbol.lastIndexOf("-") + 1), }; } else { return { base: symbol.substr(0, symbol.indexOf("-")), quote: specialSymbols[i], }; } } } } const base = symbol.substr(0, symbol.indexOf("-")); const quote = symbol.substr(symbol.indexOf("-") + 1); return { base, quote, }; } export const getTokenInfoBySymbol = ( tokenSymbolMap: { [key: string]: TokenInfo }, symbol: string ) => { if (!tokenSymbolMap) { return undefined; } try { return tokenSymbolMap[symbol]; } catch (err) { return undefined; } return undefined; }; export const getTokenInfoById = ( tokenIdMap: { [key: number]: TokenInfo }, id: number ) => { if (!tokenIdMap) { return undefined; } try { return tokenIdMap[id]; } catch (err) { return undefined; } return undefined; }; export const hasMarket = (marketArr: any, market: string) => { if (!marketArr) { return false; } if (marketArr.includes(market)) { return true; } return false; }; export const getExistedMarket = ( marketArr: any, base: string | undefined, quote: string | undefined ) => { let market: any = undefined; let baseShow: any = undefined; let quoteShow: any = undefined; if (base && quote) { market = `${base}-${quote}`; baseShow = base; quoteShow = quote; if (!hasMarket(marketArr, market)) { market = `${quote}-${base}`; if (hasMarket(marketArr, market)) { baseShow = quote; quoteShow = base; } else { market = undefined; baseShow = undefined; quoteShow = undefined; } } } const amm = market ? `AMM-${market}` : undefined; return { market, amm, baseShow, quoteShow, }; }; export const getPair = (marketArr: any, market: string) => { const { base, quote } = getBaseQuote(market); return getExistedMarket(marketArr, base, quote); }; ================================================ FILE: src/utils/window_utils.ts ================================================ export const getWindowSafely = () => { if (typeof global.window === 'undefined' || typeof window === 'undefined') { return undefined } else { return window } } export const getNavigatorSafely = () => { if (typeof global.navigator === 'undefined' || typeof navigator === 'undefined') { return undefined } else { return navigator } } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "baseUrl": "./src", "rootDir": "./src", "outDir": "./dist", "module": "esnext", "target": "ES2022", "lib": [ "es2020", "es5" , "dom" ], "composite": true, "sourceMap": true, "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "declaration": true, "declarationMap": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "noImplicitAny": true, "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true }, "include": [ "src", "src/**/*.json", "src/*" ], "exclude": [ "node_modules", "dist", ".idea" ] }