diff --git a/.123trigger b/.123trigger index 73c14c6eae..98ca5207a7 100644 --- a/.123trigger +++ b/.123trigger @@ -172,4 +172,8 @@ 1.3.4 1.3.5 1.3.6 -1.3.7 \ No newline at end of file +1.3.7 +1.3.8 +1.3.9 +1.3.10 +1.3.11 \ No newline at end of file diff --git a/.env.example b/.env.example index 0d84f2a1e6..cdb6bb3958 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,4 @@ BITTENSOR_API_KEY_3=abafdbad3 BITTENSOR_API_KEY_4=abafdbad4 BITTENSOR_API_KEY_5=abafdbad5 BITTENSOR_API_KEY_6=abafdbad6 +SIMPLE_SWAP_API_KEY=abacasdf diff --git a/.github/workflows/push-koni-dev.yml b/.github/workflows/push-koni-dev.yml index 1b32aefbf3..b0bf8f2f20 100644 --- a/.github/workflows/push-koni-dev.yml +++ b/.github/workflows/push-koni-dev.yml @@ -53,6 +53,7 @@ jobs: BITTENSOR_API_KEY_4: ${{ secrets.BITTENSOR_API_KEY_4 }} BITTENSOR_API_KEY_5: ${{ secrets.BITTENSOR_API_KEY_5 }} BITTENSOR_API_KEY_6: ${{ secrets.BITTENSOR_API_KEY_6 }} + SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }} GH_RELEASE_FILES: master-build.zip,master-src.zip COMMIT_MESSAGE: ${{ github.event.head_commit.message }} REF_NAME: ${{ github.ref_name }} diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index ae535ffbeb..c057a635ce 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -34,6 +34,7 @@ jobs: BITTENSOR_API_KEY_4: ${{ secrets.BITTENSOR_API_KEY_4 }} BITTENSOR_API_KEY_5: ${{ secrets.BITTENSOR_API_KEY_5 }} BITTENSOR_API_KEY_6: ${{ secrets.BITTENSOR_API_KEY_6 }} + SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }} BRANCH_NAME: ${{ github.ref_name }} run: | yarn install --immutable | grep -v 'YN0013' diff --git a/.github/workflows/push-web-runner.yml b/.github/workflows/push-web-runner.yml index 63f9426744..cbb151ab59 100644 --- a/.github/workflows/push-web-runner.yml +++ b/.github/workflows/push-web-runner.yml @@ -38,6 +38,7 @@ jobs: BITTENSOR_API_KEY_4: ${{ secrets.BITTENSOR_API_KEY_4 }} BITTENSOR_API_KEY_5: ${{ secrets.BITTENSOR_API_KEY_5 }} BITTENSOR_API_KEY_6: ${{ secrets.BITTENSOR_API_KEY_6 }} + SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }} BRANCH_NAME: master run: | yarn install --immutable | grep -v 'YN0013' diff --git a/.github/workflows/push-webapp.yml b/.github/workflows/push-webapp.yml index 01dd6f4460..9827f97c0e 100644 --- a/.github/workflows/push-webapp.yml +++ b/.github/workflows/push-webapp.yml @@ -34,6 +34,7 @@ jobs: CHAINFLIP_BROKER_API: ${{ secrets.CHAINFLIP_BROKER_API }} BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }} BITTENSOR_API_KEY_2: ${{ secrets.BITTENSOR_API_KEY_2 }} + SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }} BRANCH_NAME: ${{ github.ref_name }} run: | yarn install --immutable | grep -v 'YN0013' diff --git a/CHANGELOG.md b/CHANGELOG.md index ab3abf0873..b003316d23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,53 @@ # CHANGELOG +## 1.3.11 +Build date: Dec 23, 2024. + +Features & Update: +- Support swap TAO on SimpleSwap (#3855) +- Update content on unstake screen for some earning options (#3778) + +Bug fixed: +- Fix errors when making transactions on Tangle mainnet (#3861) + +## 1.3.10 +Build date: Dec 12, 2024. + +Features & Update: +- Update version polkadot api (#3888) +- Unable to transfer local token on Bifrost (#3896) + + +## 1.3.9 +Build date: Dec 09, 2024. + +Features & Update: +- Allow Polkadot namespace use EVM address (#3870) +- Support Generic ledger app for Vara network (#3835) + +Bug fixed: +- Fixed bug validating recipient balance when sending Substrate token (#3713) +- Fixed bug send NFT on Ethereum network (#3762) + + +## 1.3.8 +Build date: Dec 03, 2024. + +Features & Update: +- Improve chain-list online patch (#3132) +- Support Unified bridge on Polygon (#3826) + - ETH: Ethereum -> Polygon zkEVM + - ETH: Polygon zkEVM -> Ethereum +- Update chain-list + - Add support for G6 network testnet + - Add support for LOVA token (PAH, Hydration) + - Add support for PIP token (Storty Odyssey Testnet) + - Add support for Fraxtal Mainnet + - Add support for Cyber + - Update Aventus Block Explorer + - Delete Story Public Tesnet (Iliad testnet) + + ## 1.3.7 Build date: Nov 23, 2024. diff --git a/CONTRIBUTORS b/CONTRIBUTORS index edd66c486a..cbfed0fc89 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,10 +1,10 @@ - 2891 AnhMTV Release version 1.3.7 - 2546 Nam Phạm [Issue 3747] chore: fix eslint - 2369 S2kael [Issue-3852] Update metadata for case KSM and DOT + 2914 AnhMTV Release version 1.3.11 + 2552 Nam Phạm [Issue 3855] test: add log + 2380 S2kael [Issue-3855] Remove log 869 lw-cdm [MasterAccount] Update logic to reorder account on select account modal 568 nguyentiendung [Issue-3553] [Issue-3553] Refactor code and add recipient to swap history. + 490 bluezdot [Issue-3888] build: update version @galacticcouncil/sdk to 5.1.0 465 Jaco 0.42.5 (#969) - 463 bluezdot [Issue-3814] fix: handle exceptions 354 Thiendekaco [Issue-3707] [fix] Unified account - fix dApp ui bug #4 186 dominhquang [Issue-3507] Fix UI bug for Notification 185 quangdo @@ -13,19 +13,20 @@ 160 Automation Bot [CI Skip] Auto increase webapp build number 104 khainh predefine metadata for Polkadot & Kusama chains 96 lw + 91 PDTnhah [Issue-3861] Fix: Update link navigate to explorer 83 Hieu Dao Update README.md 83 leewong 82 Thibaut Sardan Add account filtering feature (#832) - 73 bluedot [Issue-3579] fix: update check fully migrate - 73 PDTnhah [Issue-3846] Update chainlist + 77 tunghp2002 [update] Reformat sender address + 74 bluedot [Issue-3888] build: upgrade version polkadot api 63 Roman fix ui issues 53 Dominhquangdev update Bridge code - 43 tunghp2002 [Update] Fix UI 26 S2kael 10 Trang Trịnh api get karura and acala nfts 9 Minh Le 9 minhld1029 [#115] [ForgetAccount] catch exception refList undefined 7 Ivan Rukhavets Mention derivation in FAQ (#334) + 6 tunghp2002 6 unknown refactor code 5 anhnhu [issue-2231]: add condition branch_name webapp-dev auto increase run scripts change build number 4 Amaury Martiny PostMessageProvider with on('connected' | 'disconnected') (#279) @@ -33,7 +34,6 @@ 4 Quangdm-cdm 3 Antoine Estienne Add eth test for extension signature (#909) 3 quangdm Update style for Auth Transaction Screen, Export Screen, Derive Screen - 3 tunghp2002 2 Axel Chalon Add typings to messages (#130) 2 Bartłomiej Rutkowski Allow account creation by derivation from existing one (#296) 2 carumusan Support ipfs and ipns urls (#667) diff --git a/package.json b/package.json index 15c05aae80..a692048ff6 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/Koniverse/Subwallet-V2.git" }, "sideEffects": false, - "version": "1.3.7", + "version": "1.3.11", "workspaces": [ "packages/*" ], @@ -105,12 +105,12 @@ "@polkadot/types-support": "^15.0.1", "@polkadot/util": "^13.2.3", "@polkadot/util-crypto": "^13.2.3", - "@subwallet/chain-list": "0.2.95", + "@subwallet/chain-list": "0.2.96-beta.8", "@subwallet/keyring": "^0.1.8-beta.0", "@subwallet/react-ui": "5.1.2-b79", "@subwallet/ui-keyring": "0.1.8-beta.0", "@types/bn.js": "^5.1.6", - "@zondax/ledger-substrate": "0.44.2", + "@zondax/ledger-substrate": "1.0.1", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^29.3.1", "browserify-sign": "^4.2.2", diff --git a/packages/extension-base/package.json b/packages/extension-base/package.json index 52067acf83..5306a7bc49 100644 --- a/packages/extension-base/package.json +++ b/packages/extension-base/package.json @@ -17,7 +17,7 @@ "./detectPackage.cjs" ], "type": "module", - "version": "1.3.7-0", + "version": "1.3.11-0", "main": "index.js", "dependencies": { "@acala-network/api": "^5.0.2", @@ -55,11 +55,11 @@ "@reduxjs/toolkit": "^1.9.1", "@sora-substrate/type-definitions": "^1.17.7", "@substrate/connect": "^0.8.9", - "@subwallet/chain-list": "0.2.95-beta.1", - "@subwallet/extension-base": "^1.3.7-0", - "@subwallet/extension-chains": "^1.3.7-0", - "@subwallet/extension-dapp": "^1.3.7-0", - "@subwallet/extension-inject": "^1.3.7-0", + "@subwallet/chain-list": "0.2.96-beta.8", + "@subwallet/extension-base": "^1.3.11-0", + "@subwallet/extension-chains": "^1.3.11-0", + "@subwallet/extension-dapp": "^1.3.11-0", + "@subwallet/extension-inject": "^1.3.11-0", "@subwallet/keyring": "^0.1.8-beta.0", "@subwallet/ui-keyring": "^0.1.8-beta.0", "@ton/core": "^0.56.3", @@ -105,7 +105,7 @@ "web3-utils": "^1.10.0" }, "devDependencies": { - "@subwallet/extension-mocks": "^1.3.7-0", + "@subwallet/extension-mocks": "^1.3.11-0", "@types/uuid": "^9.0.1" } } diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index ff96b76aeb..063e4e0cd0 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -792,6 +792,7 @@ export interface CreateHardwareAccountItem { name: string; isEthereum: boolean; isGeneric: boolean; + isLedgerRecovery?: boolean; } export interface RequestAccountCreateHardwareV2 extends CreateHardwareAccountItem { @@ -1264,6 +1265,8 @@ export interface LedgerNetwork { isEthereum: boolean; /** Hide networks that are supported by the dot migration app */ isHide?: boolean; + /** Recovery app */ + isRecovery?: boolean; /** Slip44 in the derivation path */ slip44: number; } diff --git a/packages/extension-base/src/background/errors/SwapError.ts b/packages/extension-base/src/background/errors/SwapError.ts index 2815b331a6..3595aca4ef 100644 --- a/packages/extension-base/src/background/errors/SwapError.ts +++ b/packages/extension-base/src/background/errors/SwapError.ts @@ -49,6 +49,11 @@ const defaultErrorMap: Record MAKE_POOL_NOT_ENOUGH_EXISTENTIAL_DEPOSIT: { message: detectTranslate('Insufficient liquidity to complete the swap. Lower your amount and try again'), code: undefined + }, + NOT_MEET_MIN_EXPECTED: { + // TODO: update message + message: detectTranslate('Unable to process this swap at the moment. Try again later'), + code: undefined } }; diff --git a/packages/extension-base/src/constants/staking.ts b/packages/extension-base/src/constants/staking.ts index 4882802bde..6c9c3c0a89 100644 --- a/packages/extension-base/src/constants/staking.ts +++ b/packages/extension-base/src/constants/staking.ts @@ -14,3 +14,8 @@ export const PREDEFINED_STAKING_POOL: Record = { export const MAX_NOMINATIONS = '16'; export const PREDEFINED_EARNING_POOL_PROMISE = fetchStaticData>('nomination-pool-recommendation'); + +export type ChainRecommendValidator = { + maxCount: number; + preSelectValidators: string; +}; diff --git a/packages/extension-base/src/core/logic-validation/swap.ts b/packages/extension-base/src/core/logic-validation/swap.ts index a9af3f6359..a749f5710f 100644 --- a/packages/extension-base/src/core/logic-validation/swap.ts +++ b/packages/extension-base/src/core/logic-validation/swap.ts @@ -6,7 +6,7 @@ import { SwapError } from '@subwallet/extension-base/background/errors/SwapError import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError'; import { _getAssetDecimals, _getTokenMinAmount, _isChainEvmCompatible, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils'; import { BasicTxErrorType } from '@subwallet/extension-base/types'; -import { AssetHubPreValidationMetadata, ChainflipPreValidationMetadata, HydradxPreValidationMetadata, SwapErrorType } from '@subwallet/extension-base/types/swap'; +import { AssetHubPreValidationMetadata, ChainflipPreValidationMetadata, HydradxPreValidationMetadata, SimpleSwapValidationMetadata, SwapErrorType } from '@subwallet/extension-base/types/swap'; import { formatNumber } from '@subwallet/extension-base/utils'; import BigN from 'bignumber.js'; @@ -163,3 +163,30 @@ export function _getEarlyAssetHubValidationError (error: SwapErrorType, metadata return new SwapError(error); } } + +export function _getSimpleSwapEarlyValidationError (error: SwapErrorType, metadata: SimpleSwapValidationMetadata): SwapError { // todo: support more providers + switch (error) { + case SwapErrorType.NOT_MEET_MIN_SWAP: { + const message = `Amount too low. Increase your amount above ${metadata.minSwap.value} ${metadata.minSwap.symbol} and try again`; + + return new SwapError(error, message); + } + + case SwapErrorType.SWAP_EXCEED_ALLOWANCE: { + if (metadata.maxSwap) { + return new SwapError(error, `Amount too high. Lower your amount below ${metadata.maxSwap.value} ${metadata.maxSwap.symbol} and try again`); + } else { + return new SwapError(error, 'Amount too high. Lower your amount and try again'); + } + } + + case SwapErrorType.ASSET_NOT_SUPPORTED: + return new SwapError(error, 'This swap pair is not supported'); + case SwapErrorType.UNKNOWN: + return new SwapError(error, `Undefined error. Check your Internet and ${metadata.chain.slug} connection or contact support`); + case SwapErrorType.ERROR_FETCHING_QUOTE: + return new SwapError(error, 'No swap quote found. Adjust your amount or try again later.'); + default: + return new SwapError(error); + } +} diff --git a/packages/extension-base/src/core/substrate/xcm-parser.ts b/packages/extension-base/src/core/substrate/xcm-parser.ts index e3454f20d0..636d38a9f9 100644 --- a/packages/extension-base/src/core/substrate/xcm-parser.ts +++ b/packages/extension-base/src/core/substrate/xcm-parser.ts @@ -6,6 +6,7 @@ import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types'; import { _Address } from '@subwallet/extension-base/background/KoniTypes'; import { isAvailChainBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/availBridge'; import { _isPolygonChainBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge'; +import { _isPosChainBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/posBridge'; import { _getChainSubstrateAddressPrefix, _getEvmChainId, _getSubstrateParaId, _getSubstrateRelayParent, _getXcmAssetMultilocation, _isChainEvmCompatible, _isPureEvmChain, _isSubstrateParaChain } from '@subwallet/extension-base/services/chain-service/utils'; import { decodeAddress, evmToAddress } from '@polkadot/util-crypto'; @@ -63,7 +64,7 @@ export function _getXcmMultiLocation (originChainInfo: _ChainInfo, destChainInfo } export function _isXcmTransferUnstable (originChainInfo: _ChainInfo, destChainInfo: _ChainInfo, assetSlug: string): boolean { - return !_isXcmWithinSameConsensus(originChainInfo, destChainInfo) || _isMythosFromHydrationToMythos(originChainInfo, destChainInfo, assetSlug) || _isPolygonBridgeXcm(originChainInfo, destChainInfo); + return !_isXcmWithinSameConsensus(originChainInfo, destChainInfo) || _isMythosFromHydrationToMythos(originChainInfo, destChainInfo, assetSlug) || _isPolygonBridgeXcm(originChainInfo, destChainInfo) || _isPosBridgeXcm(originChainInfo, destChainInfo); } function getAssetHubBridgeUnstableWarning (originChainInfo: _ChainInfo): string { @@ -104,8 +105,18 @@ function getPolygonBridgeWarning (originChainInfo: _ChainInfo): string { } } +function getPosBridgeWarning (originChainInfo: _ChainInfo): string { + if (originChainInfo.slug === COMMON_CHAIN_SLUGS.ETHEREUM || originChainInfo.slug === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA) { + return 'Cross-chain transfer of this token may take up to 22 minutes. Do you still want to continue?'; + } else { + return 'Cross-chain transfer of this token may take up to 90 minutes, and you’ll need to manually claim the funds on the destination network to complete the transfer. Do you still want to continue?'; + } +} + export function _getXcmUnstableWarning (originChainInfo: _ChainInfo, destChainInfo: _ChainInfo, assetSlug: string): string { - if (_isPolygonBridgeXcm(originChainInfo, destChainInfo)) { + if (_isPosBridgeXcm(originChainInfo, destChainInfo)) { + return getPosBridgeWarning(originChainInfo); + } else if (_isPolygonBridgeXcm(originChainInfo, destChainInfo)) { return getPolygonBridgeWarning(originChainInfo); } else if (_isAvailBridgeXcm(originChainInfo, destChainInfo)) { return getAvailBridgeWarning(); @@ -141,6 +152,10 @@ export function _isPolygonBridgeXcm (originChainInfo: _ChainInfo, destChainInfo: return _isPolygonChainBridge(originChainInfo.slug, destChainInfo.slug); } +export function _isPosBridgeXcm (originChainInfo: _ChainInfo, destChainInfo: _ChainInfo): boolean { + return _isPosChainBridge(originChainInfo.slug, destChainInfo.slug); +} + // --------------------------------------------------------------------------------------------------------------------- function _getMultiLocationParent (originChainInfo: _ChainInfo, isWithinSameConsensus: boolean): number { diff --git a/packages/extension-base/src/koni/api/contract-handler/utils/index.ts b/packages/extension-base/src/koni/api/contract-handler/utils/index.ts index b4c24fbac1..85fdcbaf13 100644 --- a/packages/extension-base/src/koni/api/contract-handler/utils/index.ts +++ b/packages/extension-base/src/koni/api/contract-handler/utils/index.ts @@ -29,6 +29,10 @@ export const _AVAIL_BRIDGE_GATEWAY_ABI: Record = require('./avail_b export const _AVAIL_TEST_BRIDGE_GATEWAY_ABI: Record = require('./avail_test_bridge_abi.json'); // eslint-disable-next-line @typescript-eslint/no-var-requires,@typescript-eslint/no-unsafe-assignment export const _POLYGON_BRIDGE_ABI: Record = require('./polygon_bridge_abi.json'); +// eslint-disable-next-line @typescript-eslint/no-var-requires,@typescript-eslint/no-unsafe-assignment +export const _POS_BRIDGE_ABI: Record = require('./pos_bridge_abi.json'); +// eslint-disable-next-line @typescript-eslint/no-var-requires,@typescript-eslint/no-unsafe-assignment +export const _POS_BRIDGE_L2_ABI: Record = require('./pos_bridge_l2_abi.json'); const SNOWBRIDGE_GATEWAY_ETHEREUM_CONTRACT_ADDRESS = '0x27ca963C279c93801941e1eB8799c23f407d68e7'; const SNOWBRIDGE_GATEWAY_SEPOLIA_CONTRACT_ADDRESS = '0x5B4909cE6Ca82d2CE23BD46738953c7959E710Cd'; @@ -72,3 +76,29 @@ export function getPolygonBridgeContract (chain: string): string { throw new Error('Invalid chain'); } + +const POSBRIDGE_GATEWAY_AMOY_CONTRACT_ADDRESS = '0x52eF3d68BaB452a294342DC3e5f464d7f610f72E'; +const POSBRIDGE_GATEWAY_SEPOLIA_CONTRACT_ADDRESS = '0x34F5A25B627f50Bb3f5cAb72807c4D4F405a9232'; + +const POSBRIDGE_GATEWAY_CONTRACT_ADDRESS = '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619'; +const POSBRIDGE_GATEWAY_ETHEREUM_CONTRACT_ADDRESS = '0xA0c68C638235ee32657e8f720a23ceC1bFc77C77'; + +export function getPosL2BridgeContract (chain: string): string { + if (chain === 'polygon_amoy' || chain === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA) { + return POSBRIDGE_GATEWAY_AMOY_CONTRACT_ADDRESS; + } else if (chain === 'polygon' || chain === COMMON_CHAIN_SLUGS.ETHEREUM) { + return POSBRIDGE_GATEWAY_CONTRACT_ADDRESS; + } + + throw new Error('Invalid chain'); +} + +export function getPosL1BridgeContract (chain: string): string { + if (chain === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA) { + return POSBRIDGE_GATEWAY_SEPOLIA_CONTRACT_ADDRESS; + } else if (chain === COMMON_CHAIN_SLUGS.ETHEREUM) { + return POSBRIDGE_GATEWAY_ETHEREUM_CONTRACT_ADDRESS; + } + + throw new Error('Invalid chain'); +} diff --git a/packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_abi.json b/packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_abi.json new file mode 100644 index 0000000000..6c65771703 --- /dev/null +++ b/packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_abi.json @@ -0,0 +1,783 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "userAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address payable", + "name": "relayerAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "functionSignature", + "type": "bytes" + } + ], + "name": "MetaTransactionExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "tokenType", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "predicateAddress", + "type": "address" + } + ], + "name": "PredicateRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "rootToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "childToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "tokenType", + "type": "bytes32" + } + ], + "name": "TokenMapped", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEPOSIT", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ERC712_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ETHER_ADDRESS", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAPPER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAP_TOKEN", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkpointManagerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "childChainManagerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "childToRootToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "rootToken", + "type": "address" + }, + { + "internalType": "address", + "name": "childToken", + "type": "address" + } + ], + "name": "cleanMapToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "depositEtherFor", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "rootToken", + "type": "address" + }, + { + "internalType": "bytes", + "name": "depositData", + "type": "bytes" + } + ], + "name": "depositFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "userAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "functionSignature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "sigR", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sigS", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "sigV", + "type": "uint8" + } + ], + "name": "executeMetaTransaction", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "inputData", + "type": "bytes" + } + ], + "name": "exit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getDomainSeperator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initializeEIP712", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "rootToken", + "type": "address" + }, + { + "internalType": "address", + "name": "childToken", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "tokenType", + "type": "bytes32" + } + ], + "name": "mapToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "processedExits", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "tokenType", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "predicateAddress", + "type": "address" + } + ], + "name": "registerPredicate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "rootToken", + "type": "address" + }, + { + "internalType": "address", + "name": "childToken", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "tokenType", + "type": "bytes32" + } + ], + "name": "remapToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "rootToChildToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newCheckpointManager", + "type": "address" + } + ], + "name": "setCheckpointManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newChildChainManager", + "type": "address" + } + ], + "name": "setChildChainManagerAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newStateSender", + "type": "address" + } + ], + "name": "setStateSender", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "setupContractId", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stateSenderAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenToType", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "typeToPredicate", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_l2_abi.json b/packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_l2_abi.json new file mode 100644 index 0000000000..40e62b445c --- /dev/null +++ b/packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_l2_abi.json @@ -0,0 +1,793 @@ +[ + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "address", + "name": "childChainManager", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "constant": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "payable": false, + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "userAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "address payable", + "name": "relayerAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "functionSignature", + "type": "bytes" + } + ], + "name": "MetaTransactionExecuted", + "payable": false, + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "payable": false, + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "payable": false, + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "payable": false, + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "payable": false, + "type": "event" + }, + { + "constant": false, + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "DEPOSITOR_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "ERC712_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "userAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "functionSignature", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "sigR", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "sigS", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "sigV", + "type": "uint8" + } + ], + "name": "executeMetaTransaction", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "getChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "getDomainSeperator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "depositData", + "type": "bytes" + } + ], + "name": "deposit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ] \ No newline at end of file diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 0a6ffa73a8..f27e77bda9 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -35,10 +35,11 @@ import { createTonTransaction } from '@subwallet/extension-base/services/balance import { createAvailBridgeExtrinsicFromAvail, createAvailBridgeTxFromEth, createPolygonBridgeExtrinsic, createSnowBridgeExtrinsic, createXcmExtrinsic, CreateXcmExtrinsicProps, FunctionCreateXcmExtrinsic, getXcmMockTxFee } from '@subwallet/extension-base/services/balance-service/transfer/xcm'; import { getClaimTxOnAvail, getClaimTxOnEthereum, isAvailChainBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/availBridge'; import { _isPolygonChainBridge, getClaimPolygonBridge, isClaimedPolygonBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge'; +import { _isPosChainBridge, getClaimPosBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/posBridge'; import { _API_OPTIONS_CHAIN_GROUP, _DEFAULT_MANTA_ZK_CHAIN, _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX, SUFFICIENT_CHAIN } from '@subwallet/extension-base/services/chain-service/constants'; import { _ChainApiStatus, _ChainConnectionStatus, _ChainState, _NetworkUpsertParams, _SubstrateAdapterQueryArgs, _SubstrateApi, _ValidateCustomAssetRequest, _ValidateCustomAssetResponse, EnableChainParams, EnableMultiChainParams } from '@subwallet/extension-base/services/chain-service/types'; import { _getAssetDecimals, _getAssetSymbol, _getChainNativeTokenBasicInfo, _getContractAddressOfToken, _getEvmChainId, _getTokenMinAmount, _getTokenOnChainAssetId, _getXcmAssetMultilocation, _isAssetSmartContractNft, _isBridgedToken, _isChainEvmCompatible, _isChainSubstrateCompatible, _isChainTonCompatible, _isCustomAsset, _isLocalToken, _isMantaZkAsset, _isNativeToken, _isPureEvmChain, _isTokenEvmSmartContract, _isTokenTransferredByEvm, _isTokenTransferredByTon } from '@subwallet/extension-base/services/chain-service/utils'; -import { NotificationSetup } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; +import { ClaimPolygonBridgeNotificationMetadata, NotificationSetup } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; import { AppBannerData, AppConfirmationData, AppPopupData } from '@subwallet/extension-base/services/mkt-campaign-service/types'; import { EXTENSION_REQUEST_URL } from '@subwallet/extension-base/services/request-service/constants'; import { AuthUrls } from '@subwallet/extension-base/services/request-service/types'; @@ -1463,6 +1464,7 @@ export default class KoniExtension { const isAvailBridgeFromAvail = isAvailChainBridge(originNetworkKey) && _isPureEvmChain(chainInfoMap[destinationNetworkKey]); const isSnowBridgeEvmTransfer = _isPureEvmChain(chainInfoMap[originNetworkKey]) && _isSnowBridgeXcm(chainInfoMap[originNetworkKey], chainInfoMap[destinationNetworkKey]) && !isAvailBridgeFromEvm; const isPolygonBridgeTransfer = _isPolygonChainBridge(originNetworkKey, destinationNetworkKey); + const isPosBridgeTransfer = _isPosChainBridge(originNetworkKey, destinationNetworkKey); let additionalValidator: undefined | ((inputTransaction: SWTransactionResponse) => Promise); let eventsHandler: undefined | ((eventEmitter: TransactionEmitter) => void); @@ -1483,7 +1485,7 @@ export default class KoniExtension { let funcCreateExtrinsic: FunctionCreateXcmExtrinsic; - if (isPolygonBridgeTransfer) { + if (isPosBridgeTransfer || isPolygonBridgeTransfer) { funcCreateExtrinsic = createPolygonBridgeExtrinsic; } else if (isSnowBridgeEvmTransfer) { funcCreateExtrinsic = createSnowBridgeExtrinsic; @@ -1549,7 +1551,7 @@ export default class KoniExtension { transaction: extrinsic, data: inputData, extrinsicType: ExtrinsicType.TRANSFER_XCM, - chainType: !isSnowBridgeEvmTransfer && !isAvailBridgeFromEvm && !isPolygonBridgeTransfer ? ChainType.SUBSTRATE : ChainType.EVM, + chainType: !isSnowBridgeEvmTransfer && !isAvailBridgeFromEvm && !isPolygonBridgeTransfer && !isPosBridgeTransfer ? ChainType.SUBSTRATE : ChainType.EVM, transferNativeAmount: _isNativeToken(originTokenInfo) ? value : '0', ignoreWarnings, isTransferAll: transferAll, @@ -3904,8 +3906,14 @@ export default class KoniExtension { let transaction: SubmittableExtrinsic<'promise'> | TransactionConfig | null = null; const evmApi = this.#koniState.getEvmApi(chain); + const metadata = notification.metadata as ClaimPolygonBridgeNotificationMetadata; + + if (metadata.bridgeType === 'POS') { + transaction = await getClaimPosBridge(chain, notification, evmApi); + } else { + transaction = await getClaimPolygonBridge(chain, notification, evmApi); + } - transaction = await getClaimPolygonBridge(chain, notification, evmApi); const chainType: ChainType = ChainType.EVM; return await this.#koniState.transactionService.handleTransaction({ diff --git a/packages/extension-base/src/packageInfo.ts b/packages/extension-base/src/packageInfo.ts index 26743555cf..c1541585d7 100644 --- a/packages/extension-base/src/packageInfo.ts +++ b/packages/extension-base/src/packageInfo.ts @@ -3,4 +3,4 @@ // Do not edit, auto-generated by @polkadot/dev -export const packageInfo = { name: '@subwallet/extension-base', path: 'auto', type: 'auto', version: '1.3.7-0' }; +export const packageInfo = { name: '@subwallet/extension-base', path: 'auto', type: 'auto', version: '1.3.11-0' }; diff --git a/packages/extension-base/src/services/balance-service/transfer/xcm/index.ts b/packages/extension-base/src/services/balance-service/transfer/xcm/index.ts index 3f8af260c5..6f6407d265 100644 --- a/packages/extension-base/src/services/balance-service/transfer/xcm/index.ts +++ b/packages/extension-base/src/services/balance-service/transfer/xcm/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types'; -import { _isPolygonBridgeXcm, _isSnowBridgeXcm } from '@subwallet/extension-base/core/substrate/xcm-parser'; +import { _isPolygonBridgeXcm, _isPosBridgeXcm, _isSnowBridgeXcm } from '@subwallet/extension-base/core/substrate/xcm-parser'; import { getAvailBridgeExtrinsicFromAvail, getAvailBridgeTxFromEth } from '@subwallet/extension-base/services/balance-service/transfer/xcm/availBridge'; import { getExtrinsicByPolkadotXcmPallet } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polkadotXcm'; import { _createPolygonBridgeL1toL2Extrinsic, _createPolygonBridgeL2toL1Extrinsic } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge'; @@ -19,6 +19,8 @@ import { SubmittableExtrinsic } from '@polkadot/api/types'; import { u8aToHex } from '@polkadot/util'; import { addressToEvm } from '@polkadot/util-crypto'; +import { _createPosBridgeL1toL2Extrinsic, _createPosBridgeL2toL1Extrinsic, _isPosChainBridge } from './posBridge'; + export type CreateXcmExtrinsicProps = { originTokenInfo: _ChainAsset; destinationTokenInfo: _ChainAsset; @@ -122,8 +124,11 @@ export const createPolygonBridgeExtrinsic = async ({ chainInfoMap, sendingValue }: CreateXcmExtrinsicProps): Promise => { const originChainInfo = chainInfoMap[originTokenInfo.originChain]; const destinationChainInfo = chainInfoMap[destinationTokenInfo.originChain]; + const isPolygonBridgeXcm = _isPolygonBridgeXcm(originChainInfo, destinationChainInfo); + + const isValidBridge = isPolygonBridgeXcm || _isPosBridgeXcm(originChainInfo, destinationChainInfo); - if (!_isPolygonBridgeXcm(originChainInfo, destinationChainInfo)) { + if (!isValidBridge) { throw new Error('This is not a valid PolygonBridge transfer'); } @@ -137,11 +142,15 @@ export const createPolygonBridgeExtrinsic = async ({ chainInfoMap, const sourceChain = originChainInfo.slug; - if (sourceChain === 'polygonzkEvm_cardona' || sourceChain === 'polygonZkEvm') { - return _createPolygonBridgeL2toL1Extrinsic(originTokenInfo, originChainInfo, sender, recipient, sendingValue, evmApi); - } else { - return _createPolygonBridgeL1toL2Extrinsic(originTokenInfo, originChainInfo, sender, recipient, sendingValue, evmApi); - } + const createExtrinsic = isPolygonBridgeXcm + ? (sourceChain === 'polygonzkEvm_cardona' || sourceChain === 'polygonZkEvm') + ? _createPolygonBridgeL2toL1Extrinsic + : _createPolygonBridgeL1toL2Extrinsic + : (sourceChain === 'polygon_amoy' || sourceChain === 'polygon') + ? _createPosBridgeL2toL1Extrinsic + : _createPosBridgeL1toL2Extrinsic; + + return createExtrinsic(originTokenInfo, originChainInfo, sender, recipient, sendingValue, evmApi); }; export const getXcmMockTxFee = async (substrateApi: _SubstrateApi, chainInfoMap: Record, originTokenInfo: _ChainAsset, destinationTokenInfo: _ChainAsset): Promise => { diff --git a/packages/extension-base/src/services/balance-service/transfer/xcm/polygonBridge.ts b/packages/extension-base/src/services/balance-service/transfer/xcm/polygonBridge.ts index 7cd5b30c5a..e8d2fc5f92 100644 --- a/packages/extension-base/src/services/balance-service/transfer/xcm/polygonBridge.ts +++ b/packages/extension-base/src/services/balance-service/transfer/xcm/polygonBridge.ts @@ -93,7 +93,7 @@ export async function getClaimPolygonBridge (chainSlug: string, notification: _N const isTestnet = chainSlug === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA; const proofDomain = isTestnet ? POLYGON_PROOF_INDEXER.TESTNET : POLYGON_PROOF_INDEXER.MAINNET; - const proofResponse = await fetch(`${proofDomain}?networkId=${metadata.sourceNetwork}&depositCount=${metadata.counter}`) + const proofResponse = await fetch(`${proofDomain}?networkId=${metadata.sourceNetwork ?? ''}&depositCount=${metadata.counter ?? ''}`) .then((res) => res.json()) as ClaimNotification; const proof = proofResponse.proof; diff --git a/packages/extension-base/src/services/balance-service/transfer/xcm/posBridge.ts b/packages/extension-base/src/services/balance-service/transfer/xcm/posBridge.ts new file mode 100644 index 0000000000..4b64c45437 --- /dev/null +++ b/packages/extension-base/src/services/balance-service/transfer/xcm/posBridge.ts @@ -0,0 +1,180 @@ +// Copyright 2019-2022 @subwallet/extension-base +// SPDX-License-Identifier: Apache-2.0 + +import { COMMON_CHAIN_SLUGS } from '@subwallet/chain-list'; +import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types'; +import { getWeb3Contract } from '@subwallet/extension-base/koni/api/contract-handler/evm/web3'; +import { _POS_BRIDGE_ABI, _POS_BRIDGE_L2_ABI, getPosL1BridgeContract, getPosL2BridgeContract } from '@subwallet/extension-base/koni/api/contract-handler/utils'; +import { _EvmApi } from '@subwallet/extension-base/services/chain-service/types'; +import { calculateGasFeeParams } from '@subwallet/extension-base/services/fee-service/utils'; +import { _NotificationInfo, ClaimPolygonBridgeNotificationMetadata } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; +import { fetchPolygonBridgeTransactions } from '@subwallet/extension-base/services/inapp-notification-service/utils'; +import { BasicTxErrorType } from '@subwallet/extension-base/types'; +import { TransactionConfig } from 'web3-core'; +import { ContractSendMethod } from 'web3-eth-contract'; + +interface inputData { + error?: string + message: string; + result?: string; +} + +interface EventArgument { + topics: string[]; +} + +interface Event { + arguments: EventArgument[]; +} + +export const POS_EXIT_PAYLOAD_INDEXER = { + MAINNET: 'https://proof-generator.polygon.technology/api/v1/matic/exit-payload', + TESTNET: 'https://proof-generator.polygon.technology/api/v1/amoy/exit-payload' +}; + +export async function _createPosBridgeL1toL2Extrinsic (tokenInfo: _ChainAsset, originChainInfo: _ChainInfo, sender: string, recipientAddress: string, value: string, evmApi: _EvmApi): Promise { + const posBridgeContractAddress = getPosL1BridgeContract(originChainInfo.slug); + const posBridgeContract = getWeb3Contract(posBridgeContractAddress, evmApi, _POS_BRIDGE_ABI); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment + const transferCall: ContractSendMethod = posBridgeContract.methods.depositEtherFor(recipientAddress); + const transferEncodedCall = transferCall.encodeABI(); + const priority = await calculateGasFeeParams(evmApi, evmApi.chainSlug); + + const transactionConfig: TransactionConfig = { + from: sender, + to: posBridgeContractAddress, + value: value, + data: transferEncodedCall, + gasPrice: priority.gasPrice, + maxFeePerGas: priority?.maxFeePerGas?.toString(), + maxPriorityFeePerGas: priority?.maxPriorityFeePerGas?.toString() + }; + + const gasLimit = await evmApi.api.eth.estimateGas(transactionConfig).catch(() => 200000); + + transactionConfig.gas = gasLimit.toString(); + + return transactionConfig; +} + +export async function _createPosBridgeL2toL1Extrinsic (tokenInfo: _ChainAsset, originChainInfo: _ChainInfo, sender: string, recipientAddress: string, value: string, evmApi: _EvmApi): Promise { + const posBridgeContractAddress = getPosL2BridgeContract(originChainInfo.slug); + const posBridgeContract = getWeb3Contract(posBridgeContractAddress, evmApi, _POS_BRIDGE_L2_ABI); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment + const transferCall: ContractSendMethod = posBridgeContract.methods.withdraw(value); + const transferEncodedCall = transferCall.encodeABI(); + const priority = await calculateGasFeeParams(evmApi, evmApi.chainSlug); + + const transactionConfig: TransactionConfig = { + from: sender, + to: posBridgeContractAddress, + value: undefined, + data: transferEncodedCall, + gasPrice: priority.gasPrice, + maxFeePerGas: priority?.maxFeePerGas?.toString(), + maxPriorityFeePerGas: priority?.maxPriorityFeePerGas?.toString() + }; + + const gasLimit = await evmApi.api.eth.estimateGas(transactionConfig).catch(() => 200000); + + transactionConfig.gas = gasLimit.toString(); + + return transactionConfig; +} + +export async function getClaimPosBridge (chainSlug: string, notification: _NotificationInfo, evmApi: _EvmApi) { + const posBridgeContractAddress = getPosL2BridgeContract(chainSlug); + const posBridgeContract = getWeb3Contract(posBridgeContractAddress, evmApi, _POS_BRIDGE_L2_ABI); + + const metadata = notification.metadata as ClaimPolygonBridgeNotificationMetadata; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment + const event = posBridgeContract.events.Transfer(metadata.userAddress, metadata.userAddress, metadata.amounts[0]) as Event; + + const isTestnet = chainSlug === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA; + const domain = isTestnet ? POS_EXIT_PAYLOAD_INDEXER.TESTNET : POS_EXIT_PAYLOAD_INDEXER.MAINNET; + + const eventSignature: string = event?.arguments?.[0]?.topics?.[0]; + + let inputData: inputData; + + try { + const res = await fetch(`${domain}/${metadata.transactionHash}?eventSignature=${eventSignature}`); + + inputData = await res.json() as inputData; + + if (inputData.error && inputData.message.includes('not been checkpointed yet')) { + throw new Error(`${inputData.message}. Please try again later.`); + } + } catch (err) { + console.error('Error:', err); + throw new Error(BasicTxErrorType.INTERNAL_ERROR); + } + + const posClaimContractAddress = getPosL1BridgeContract(chainSlug); + const posClaimContract = getWeb3Contract(posClaimContractAddress, evmApi, _POS_BRIDGE_ABI); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment + const transferCall: ContractSendMethod = posClaimContract.methods.exit(inputData.result); + const transferEncodedCall = transferCall.encodeABI(); + + const priority = await calculateGasFeeParams(evmApi, evmApi.chainSlug); + + const transactionConfig = { + from: metadata.userAddress, + to: posClaimContractAddress, + value: '0', + data: transferEncodedCall, + gasPrice: priority.gasPrice, + maxFeePerGas: priority.maxFeePerGas?.toString(), + maxPriorityFeePerGas: priority.maxPriorityFeePerGas?.toString() + } as TransactionConfig; + + const gasLimit = await evmApi.api.eth.estimateGas(transactionConfig).catch(() => 200000); + + transactionConfig.gas = gasLimit.toString(); + + return transactionConfig; +} + +export async function isClaimedPosBridge (id: string, address: string, isTestnet: boolean): Promise { + try { + const isClaimableBridge = await fetchPolygonBridgeTransactions(address, isTestnet); + + if (isClaimableBridge && isClaimableBridge.success) { + const isIdClaimable = isClaimableBridge.result.some((transaction) => transaction._id === id); + + return !isIdClaimable; + } + } catch (err) { + console.error('Error:', err); + } + + return false; +} + +export function _isPosChainBridge (srcChain: string, destChain: string): boolean { + if (srcChain === 'polygon_amoy' && destChain === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA) { + return true; + } else if (srcChain === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA && destChain === 'polygon_amoy') { + return true; + } else if (srcChain === 'polygon' && destChain === COMMON_CHAIN_SLUGS.ETHEREUM) { + return true; + } else if (srcChain === COMMON_CHAIN_SLUGS.ETHEREUM && destChain === 'polygon') { + return true; + } + + return false; +} + +export function _isPosChainL2Bridge (srcChain: string, destChain: string): boolean { + if (srcChain === 'polygon_amoy' && destChain === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA) { + return true; + } else if (srcChain === 'polygon' && destChain === COMMON_CHAIN_SLUGS.ETHEREUM) { + return true; + } + + return false; +} diff --git a/packages/extension-base/src/services/chain-service/constants.ts b/packages/extension-base/src/services/chain-service/constants.ts index a8967d518d..a30bd2d199 100644 --- a/packages/extension-base/src/services/chain-service/constants.ts +++ b/packages/extension-base/src/services/chain-service/constants.ts @@ -231,12 +231,12 @@ export const _TRANSFER_CHAIN_GROUP = { kintsugi: ['kintsugi', 'kintsugi_test', 'interlay', 'mangatax_para'], genshiro: ['genshiro_testnet', 'genshiro', 'equilibrium_parachain'], // crab: ['crab', 'pangolin'], - bitcountry: ['pioneer', 'bitcountry', 'bifrost', 'bifrost_dot'], + bitcountry: ['pioneer', 'bitcountry'], statemine: ['statemint', 'statemine', 'darwinia2', 'astar', 'shiden', 'shibuya', 'parallel', 'liberland', 'liberlandTest', 'dentnet', 'dbcchain'], riochain: ['riochain'], sora_substrate: ['sora_substrate'], avail: ['kate', 'goldberg_testnet'], - pendulum: ['pendulum', 'amplitude', 'amplitude_test', 'hydradx_main'], + pendulum: ['pendulum', 'amplitude', 'amplitude_test', 'hydradx_main', 'bifrost', 'bifrost_dot'], centrifuge: ['centrifuge'], disable_transfer: ['invarch', 'crab', 'pangolin'] }; diff --git a/packages/extension-base/src/services/chain-service/health-check/utils/new-utils/asset-asset-validate.ts b/packages/extension-base/src/services/chain-service/health-check/utils/new-utils/asset-asset-validate.ts index 1f32dd3f23..2483d81992 100644 --- a/packages/extension-base/src/services/chain-service/health-check/utils/new-utils/asset-asset-validate.ts +++ b/packages/extension-base/src/services/chain-service/health-check/utils/new-utils/asset-asset-validate.ts @@ -71,7 +71,7 @@ export function validateNativeLocalTransferMetadata (chainAsset: _ChainAsset) { } const moonbeamGroup = ['moonbeam, moonbase, moonriver']; - const onChainInfoLocalGroup = [_TRANSFER_CHAIN_GROUP.centrifuge, ..._TRANSFER_CHAIN_GROUP.bitcountry, ..._TRANSFER_CHAIN_GROUP.acala, ..._TRANSFER_CHAIN_GROUP.kintsugi, 'pendulum', 'amplitude']; + const onChainInfoLocalGroup = [_TRANSFER_CHAIN_GROUP.centrifuge, ..._TRANSFER_CHAIN_GROUP.bitcountry, ..._TRANSFER_CHAIN_GROUP.acala, ..._TRANSFER_CHAIN_GROUP.kintsugi, 'pendulum', 'amplitude', 'bifrost', 'bifrost_dot']; const onChainInfoNativeGroup = [_TRANSFER_CHAIN_GROUP.centrifuge, ..._TRANSFER_CHAIN_GROUP.acala, ..._TRANSFER_CHAIN_GROUP.kintsugi]; const assetIdLocalGroup = [..._TRANSFER_CHAIN_GROUP.statemine, ..._TRANSFER_CHAIN_GROUP.genshiro, ...moonbeamGroup, 'hydradx']; const assetIdNativeGroup = [..._TRANSFER_CHAIN_GROUP.sora_substrate, 'hydradx']; diff --git a/packages/extension-base/src/services/chain-service/utils/patch.ts b/packages/extension-base/src/services/chain-service/utils/patch.ts index 0a3d84bccb..2dc53844a5 100644 --- a/packages/extension-base/src/services/chain-service/utils/patch.ts +++ b/packages/extension-base/src/services/chain-service/utils/patch.ts @@ -8,7 +8,7 @@ const branchName = process.env.BRANCH_NAME || 'subwallet-dev'; const fetchDomain = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'https://chain-list-assets.subwallet.app' : 'https://dev.sw-chain-list-assets.pages.dev'; const fetchFile = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'list.json' : 'preview.json'; -const ChainListVersion = '0.2.95'; // update this when build chainlist +const ChainListVersion = '0.2.96'; // update this when build chainlist // todo: move this interface to chainlist export interface PatchInfo { diff --git a/packages/extension-base/src/services/earning-service/constants/chains.ts b/packages/extension-base/src/services/earning-service/constants/chains.ts index 9c3d3666f7..8f5c77fee7 100644 --- a/packages/extension-base/src/services/earning-service/constants/chains.ts +++ b/packages/extension-base/src/services/earning-service/constants/chains.ts @@ -27,3 +27,5 @@ export const ST_LIQUID_TOKEN_ABI: Record = require('./abis/st_liqui export const MANTA_VALIDATOR_POINTS_PER_BLOCK = 20; export const MANTA_MIN_DELEGATION = 500; + +export const CHANNEL_ID = 7; diff --git a/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost-manta.ts b/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost-manta.ts index 8d49cb9a55..7a01ef831b 100644 --- a/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost-manta.ts +++ b/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost-manta.ts @@ -4,6 +4,7 @@ import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; import KoniState from '@subwallet/extension-base/koni/background/handlers/State'; import { _getAssetDecimals, _getTokenOnChainInfo } from '@subwallet/extension-base/services/chain-service/utils'; +import { CHANNEL_ID } from '@subwallet/extension-base/services/earning-service/constants'; import { BaseYieldStepDetail, HandleYieldStepData, LiquidYieldPoolInfo, OptimalYieldPath, SubmitYieldJoinData, TransactionData, YieldStepType } from '@subwallet/extension-base/types'; import BifrostLiquidStakingPoolHandler from './bifrost'; @@ -143,7 +144,7 @@ export default class BifrostMantaLiquidStakingPoolHandler extends BifrostLiquidS const substrateApi = await this.substrateApi.isReady; const inputTokenSlug = this.inputAsset; const inputTokenInfo = this.state.getAssetBySlug(inputTokenSlug); - const extrinsic = substrateApi.api.tx.vtokenMinting.mint(_getTokenOnChainInfo(inputTokenInfo), data.amount, undefined, undefined); + const extrinsic = substrateApi.api.tx.vtokenMinting.mint(_getTokenOnChainInfo(inputTokenInfo), data.amount, undefined, CHANNEL_ID); return { txChain: this.chain, diff --git a/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost.ts b/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost.ts index 02387d8fdd..94ea547781 100644 --- a/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost.ts +++ b/packages/extension-base/src/services/earning-service/handlers/liquid-staking/bifrost.ts @@ -5,7 +5,7 @@ import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/K import KoniState from '@subwallet/extension-base/koni/background/handlers/State'; import { _STAKING_ERA_LENGTH_MAP } from '@subwallet/extension-base/services/chain-service/constants'; import { _getAssetDecimals, _getTokenOnChainInfo } from '@subwallet/extension-base/services/chain-service/utils'; -import { fakeAddress } from '@subwallet/extension-base/services/earning-service/constants'; +import { CHANNEL_ID, fakeAddress } from '@subwallet/extension-base/services/earning-service/constants'; import { BaseYieldStepDetail, EarningStatus, HandleYieldStepData, LiquidYieldPoolInfo, LiquidYieldPositionInfo, OptimalYieldPath, OptimalYieldPathParams, RuntimeDispatchInfo, SubmitYieldJoinData, TokenBalanceRaw, TransactionData, UnstakingInfo, UnstakingStatus, YieldPoolMethodInfo, YieldPositionInfo, YieldStepType, YieldTokenBaseInfo } from '@subwallet/extension-base/types'; import { reformatAddress } from '@subwallet/extension-base/utils'; import BigNumber from 'bignumber.js'; @@ -337,7 +337,7 @@ export default class BifrostLiquidStakingPoolHandler extends BaseLiquidStakingPo const defaultFeeTokenSlug = this.feeAssets[0]; if (new BN(params.amount).gt(BN_ZERO)) { - const _mintFeeInfo = await poolOriginSubstrateApi.api.tx.vtokenMinting.mint(_getTokenOnChainInfo(inputTokenInfo), params.amount, undefined, undefined).paymentInfo(fakeAddress); + const _mintFeeInfo = await poolOriginSubstrateApi.api.tx.vtokenMinting.mint(_getTokenOnChainInfo(inputTokenInfo), params.amount, undefined, CHANNEL_ID).paymentInfo(fakeAddress); const mintFeeInfo = _mintFeeInfo.toPrimitive() as unknown as RuntimeDispatchInfo; return { @@ -356,7 +356,7 @@ export default class BifrostLiquidStakingPoolHandler extends BaseLiquidStakingPo const substrateApi = await this.substrateApi.isReady; const inputTokenSlug = this.inputAsset; const inputTokenInfo = this.state.getAssetBySlug(inputTokenSlug); - const extrinsic = substrateApi.api.tx.vtokenMinting.mint(_getTokenOnChainInfo(inputTokenInfo), data.amount, undefined, undefined); + const extrinsic = substrateApi.api.tx.vtokenMinting.mint(_getTokenOnChainInfo(inputTokenInfo), data.amount, undefined, CHANNEL_ID); return { txChain: this.chain, diff --git a/packages/extension-base/src/services/inapp-notification-service/index.ts b/packages/extension-base/src/services/inapp-notification-service/index.ts index cbe795c37e..dc8095d503 100644 --- a/packages/extension-base/src/services/inapp-notification-service/index.ts +++ b/packages/extension-base/src/services/inapp-notification-service/index.ts @@ -329,12 +329,13 @@ export class InappNotificationService implements CronServiceInterface { const symbol = token.symbol; const decimals = token.decimals ?? 0; const notifications: _BaseNotificationInfo[] = transactions.map((transaction) => { - const { _id, amounts, counter, destinationNetwork, originTokenAddress, originTokenNetwork, receiver, sourceNetwork, status, transactionHash, transactionInitiator, userAddress } = transaction; + const { _id, amounts, bridgeType, counter, destinationNetwork, originTokenAddress, originTokenNetwork, receiver, sourceNetwork, status, transactionHash, transactionInitiator, userAddress } = transaction; const metadata: ClaimPolygonBridgeNotificationMetadata = { chainSlug: token.originChain, tokenSlug: token.slug, _id, amounts, + bridgeType, counter, destinationNetwork, originTokenAddress, diff --git a/packages/extension-base/src/services/inapp-notification-service/interfaces.ts b/packages/extension-base/src/services/inapp-notification-service/interfaces.ts index e6fcc52389..c022cc91fd 100644 --- a/packages/extension-base/src/services/inapp-notification-service/interfaces.ts +++ b/packages/extension-base/src/services/inapp-notification-service/interfaces.ts @@ -70,15 +70,16 @@ export interface ClaimPolygonBridgeNotificationMetadata { tokenSlug: string; _id: string; amounts: string[]; - counter: number; + bridgeType: string; + counter?: number; destinationNetwork: number; - originTokenAddress: string; - originTokenNetwork: number; - receiver: string; - sourceNetwork: number; + originTokenAddress?: string; + originTokenNetwork?: number; + receiver?: string; + sourceNetwork?: number; status: BridgeTransactionStatus; transactionHash: string; - transactionInitiator: string; + transactionInitiator?: string; userAddress: string; } diff --git a/packages/extension-base/src/services/inapp-notification-service/utils/polygon.ts b/packages/extension-base/src/services/inapp-notification-service/utils/polygon.ts index 5bdc1476db..d904789d65 100644 --- a/packages/extension-base/src/services/inapp-notification-service/utils/polygon.ts +++ b/packages/extension-base/src/services/inapp-notification-service/utils/polygon.ts @@ -11,7 +11,7 @@ export const POLYGON_BRIDGE_INDEXER = { export interface PolygonTransaction { _id: string; transactionIndex?: number; - sourceNetwork: number; + sourceNetwork?: number; destinationNetwork: number; blockNumber: number; amounts: string[]; @@ -25,16 +25,16 @@ export interface PolygonTransaction { userAddress: string; wrappedTokenAddress?: string; wrappedTokenNetwork?: number; - counter: number; + counter?: number; bridgeContractAddress?: string; eventInitiatorAddress?: string; globalExitRootManager?: string; leaf?: string; mainnetExitRoot?: string; metadata?: string; - originTokenAddress: string; - originTokenNetwork: number; - receiver: string; // empty when not claimed + originTokenAddress?: string; + originTokenNetwork?: number; + receiver?: string; // empty when not claimed refuel: boolean; rollUpExitRoot?: string; nonce?: any; @@ -43,7 +43,7 @@ export interface PolygonTransaction { claimTransactionBlockNumber?: number; claimTransactionHash?: string; claimTransactionTimestamp?: string; - transactionInitiator: string; + transactionInitiator?: string; } interface PaginationData { @@ -87,7 +87,7 @@ export async function fetchPolygonBridgeTransactions (userAddress: string, isTes page: page.toString() }); - const networkIds = [0, 1]; + const networkIds = [0, 1, -1]; networkIds.forEach((networkId) => { params.append('destinationNetworkIds', networkId.toString()); diff --git a/packages/extension-base/src/services/keyring-service/context/handlers/Ledger.ts b/packages/extension-base/src/services/keyring-service/context/handlers/Ledger.ts index 439a0efd8a..f06654aca1 100644 --- a/packages/extension-base/src/services/keyring-service/context/handlers/Ledger.ts +++ b/packages/extension-base/src/services/keyring-service/context/handlers/Ledger.ts @@ -86,7 +86,7 @@ export class AccountLedgerHandler extends AccountBaseHandler { const pairs: KeyringPair[] = []; for (const account of accounts) { - const { accountIndex, address, addressOffset, genesisHash, hardwareType, isEthereum, isGeneric, name, originGenesisHash } = account; + const { accountIndex, address, addressOffset, genesisHash, hardwareType, isEthereum, isGeneric, isLedgerRecovery, name, originGenesisHash } = account; const baseMeta: KeyringPair$Meta = { name, @@ -95,7 +95,8 @@ export class AccountLedgerHandler extends AccountBaseHandler { addressOffset, genesisHash, originGenesisHash, - isGeneric + isGeneric, + isLedgerRecovery }; const type = isEthereum ? 'ethereum' : 'sr25519'; diff --git a/packages/extension-base/src/services/swap-service/handler/chainflip-handler.ts b/packages/extension-base/src/services/swap-service/handler/chainflip-handler.ts index c486d7259d..fc9d21f1a0 100644 --- a/packages/extension-base/src/services/swap-service/handler/chainflip-handler.ts +++ b/packages/extension-base/src/services/swap-service/handler/chainflip-handler.ts @@ -412,8 +412,6 @@ export class ChainflipSwapHandler implements SwapBaseInterface { } }); - console.log('depositAddressResp', depositAddressResponse); - const txData: ChainflipSwapTxData = { address, provider: this.providerInfo, diff --git a/packages/extension-base/src/services/swap-service/handler/simpleswap-handler.ts b/packages/extension-base/src/services/swap-service/handler/simpleswap-handler.ts new file mode 100644 index 0000000000..58c8ba2180 --- /dev/null +++ b/packages/extension-base/src/services/swap-service/handler/simpleswap-handler.ts @@ -0,0 +1,514 @@ +// Copyright 2019-2022 @subwallet/extension-base +// SPDX-License-Identifier: Apache-2.0 + +import { _ChainAsset } from '@subwallet/chain-list/types'; +import { SwapError } from '@subwallet/extension-base/background/errors/SwapError'; +import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError'; +import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; +import { _getSimpleSwapEarlyValidationError } from '@subwallet/extension-base/core/logic-validation/swap'; +import { _getAssetDecimals, _getChainNativeTokenSlug, _getContractAddressOfToken, _isChainSubstrateCompatible, _isNativeToken, _isSmartContractToken } from '@subwallet/extension-base/services/chain-service/utils'; +import { BaseStepDetail, BasicTxErrorType, CommonFeeComponent, CommonOptimalPath, CommonStepFeeInfo, CommonStepType, OptimalSwapPathParams, SimpleSwapTxData, SimpleSwapValidationMetadata, SwapEarlyValidation, SwapErrorType, SwapFeeType, SwapProviderId, SwapQuote, SwapRequest, SwapStepType, SwapSubmitParams, SwapSubmitStepData, TransactionData, ValidateSwapProcessParams } from '@subwallet/extension-base/types'; +import { _reformatAddressWithChain, formatNumber } from '@subwallet/extension-base/utils'; +import BigN, { BigNumber } from 'bignumber.js'; + +import { SubmittableExtrinsic } from '@polkadot/api/types'; + +import { BalanceService } from '../../balance-service'; +import { getERC20TransactionObject, getEVMTransactionObject } from '../../balance-service/transfer/smart-contract'; +import { createTransferExtrinsic, getTransferMockTxFee } from '../../balance-service/transfer/token'; +import { ChainService } from '../../chain-service'; +import { EvmApi } from '../../chain-service/handler/EvmApi'; +import { _SubstrateApi } from '../../chain-service/types'; +import { calculateSwapRate, SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING, SWAP_QUOTE_TIMEOUT_MAP } from '../utils'; +import { SwapBaseHandler, SwapBaseInterface } from './base-handler'; + +interface SwapRange { + min: string; + max: string; +} + +interface ExchangeSimpleSwapData{ + id: string; + trace_id: string; + address_from: string; + amount_to: string; +} + +const apiUrl = 'https://api.simpleswap.io'; + +export const simpleSwapApiKey = process.env.SIMPLE_SWAP_API_KEY || ''; + +const toBNString = (input: string | number | BigNumber, decimal: number): string => { + const raw = new BigNumber(input); + + return raw.shiftedBy(decimal).integerValue(BigNumber.ROUND_CEIL).toFixed(); +}; + +const fetchSwapList = async (params: { fromSymbol: string }): Promise => { + const swapListParams = new URLSearchParams({ + api_key: `${simpleSwapApiKey}`, + fixed: 'false', + symbol: params.fromSymbol + }); + + const response = await fetch(`${apiUrl}/get_pairs?${swapListParams.toString()}`, { + headers: { accept: 'application/json' } + }); + + return await response.json() as string[]; +}; + +const fetchRanges = async (params: { fromSymbol: string; toSymbol: string }): Promise => { + const rangesParams = new URLSearchParams({ + api_key: `${simpleSwapApiKey}`, + fixed: 'false', + currency_from: params.fromSymbol, + currency_to: params.toSymbol + }); + + const response = await fetch(`${apiUrl}/get_ranges?${rangesParams.toString()}`, { + headers: { accept: 'application/json' } + }); + + return await response.json() as SwapRange; +}; + +async function getEstimate (request: SwapRequest, fromAsset: _ChainAsset, toAsset: _ChainAsset): Promise<{ toAmount: string; walletFeeAmount: string }> { + const fromSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[fromAsset.slug]; + const toSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[toAsset.slug]; + const assetDecimals = _getAssetDecimals(fromAsset); + + if (!fromSymbol || !toSymbol) { + throw new SwapError(SwapErrorType.ASSET_NOT_SUPPORTED); + } + + const formatedAmount = formatNumber(request.fromAmount, assetDecimals, (s) => s); + + const params = new URLSearchParams({ + api_key: `${simpleSwapApiKey}`, + fixed: 'false', + currency_from: fromSymbol, + currency_to: toSymbol, + amount: formatedAmount + }); + + try { + const response = await fetch(`${apiUrl}/get_estimated?${params.toString()}`, { + headers: { accept: 'application/json' } + }); + + if (!response.ok) { + throw new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE); + } + + const resToAmount = await response.json() as string; + const toAmount = toBNString(resToAmount, _getAssetDecimals(toAsset)); + const bnToAmount = new BigN(toAmount); + + const walletFeeRate = 4 / 1000; + const toAmountBeforeFee = bnToAmount.dividedBy(new BigN(1 - walletFeeRate)); + const walletFeeAmount = toAmountBeforeFee.multipliedBy(4).dividedBy(1000).toString(); + + return { + toAmount, + walletFeeAmount + }; + } catch (err) { + console.error('Error:', err); + throw new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE); + } +} + +const createSwapRequest = async (params: {fromSymbol: string; toSymbol: string; fromAmount: string; fromAsset: _ChainAsset; receiver: string; sender: string; toAsset: _ChainAsset;}) => { + const fromDecimals = _getAssetDecimals(params.fromAsset); + const toDecimals = _getAssetDecimals(params.toAsset); + const formatedAmount = formatNumber(params.fromAmount, fromDecimals, (s) => s); + const requestBody = { + fixed: false, + currency_from: params.fromSymbol, + currency_to: params.toSymbol, + amount: formatedAmount, // Convert to small number due to require of api + address_to: params.receiver, + extra_id_to: '', + user_refund_address: params.sender, + user_refund_extra_id: '' + }; + + const response = await fetch( + `${apiUrl}/create_exchange?api_key=${simpleSwapApiKey}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json' + }, + body: JSON.stringify(requestBody) + } + ); + + const depositAddressResponse = await response.json() as ExchangeSimpleSwapData; + + return { + id: depositAddressResponse.id, + addressFrom: depositAddressResponse.address_from, + amountTo: toBNString(depositAddressResponse.amount_to, toDecimals) + }; +}; + +export class SimpleSwapHandler implements SwapBaseInterface { + private swapBaseHandler: SwapBaseHandler; + providerSlug: SwapProviderId; + + constructor (chainService: ChainService, balanceService: BalanceService) { + this.swapBaseHandler = new SwapBaseHandler({ + chainService, + balanceService, + providerName: 'SimpleSwap', + providerSlug: SwapProviderId.SIMPLE_SWAP + }); + this.providerSlug = SwapProviderId.SIMPLE_SWAP; + } + + public async validateSwapProcess (params: ValidateSwapProcessParams): Promise { + const amount = params.selectedQuote.fromAmount; + const bnAmount = BigInt(amount); + + if (bnAmount <= BigInt(0)) { + return Promise.resolve([new TransactionError(BasicTxErrorType.INVALID_PARAMS, 'Amount must be greater than 0')]); + } + + let isXcmOk = false; + + for (const [index, step] of params.process.steps.entries()) { + const getErrors = async (): Promise => { + switch (step.type) { + case CommonStepType.DEFAULT: + return Promise.resolve([]); + case CommonStepType.TOKEN_APPROVAL: + return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED)); + default: + return this.swapBaseHandler.validateSwapStep(params, isXcmOk, index); + } + }; + + const errors = await getErrors(); + + if (errors.length) { + return errors; + } else if (step.type === CommonStepType.XCM) { + isXcmOk = true; + } + } + + return []; + } + + get chainService () { + return this.swapBaseHandler.chainService; + } + + get balanceService () { + return this.swapBaseHandler.balanceService; + } + + get providerInfo () { + return this.swapBaseHandler.providerInfo; + } + + get name () { + return this.swapBaseHandler.name; + } + + get slug () { + return this.swapBaseHandler.slug; + } + + public async getSwapQuote (request: SwapRequest): Promise { + try { + const fromAsset = this.chainService.getAssetBySlug(request.pair.from); + const toAsset = this.chainService.getAssetBySlug(request.pair.to); + + if (!fromAsset || !toAsset) { + return new SwapError(SwapErrorType.UNKNOWN); + } + + const earlyValidation = await this.validateSwapRequest(request); + + const metadata = earlyValidation.metadata as SimpleSwapValidationMetadata; + + if (earlyValidation.error) { + return _getSimpleSwapEarlyValidationError(earlyValidation.error, metadata); + } + + const { toAmount, walletFeeAmount } = await getEstimate(request, fromAsset, toAsset); + const fromAmount = request.fromAmount; + + const rate = calculateSwapRate(request.fromAmount, toAmount, fromAsset, toAsset); + + const fromChain = this.chainService.getChainInfoByKey(fromAsset.originChain); + const fromChainNativeTokenSlug = _getChainNativeTokenSlug(fromChain); + const defaultFeeToken = _isNativeToken(fromAsset) ? fromAsset.slug : fromChainNativeTokenSlug; + + const chainType = _isChainSubstrateCompatible(fromChain) ? ChainType.SUBSTRATE : ChainType.EVM; + + let api: _SubstrateApi | EvmApi; + + if (chainType === ChainType.SUBSTRATE) { + api = this.chainService.getSubstrateApi(fromChain.slug); + } else { + api = this.chainService.getEvmApi(fromChain.slug); + } + + const networkFeeAmount = await getTransferMockTxFee(request.address, fromChain, fromAsset, api); + + const networkFee: CommonFeeComponent = { + tokenSlug: fromChainNativeTokenSlug, + amount: networkFeeAmount.toString(), + feeType: SwapFeeType.NETWORK_FEE + }; + + const walletFee: CommonFeeComponent = { + tokenSlug: toAsset.slug, + amount: walletFeeAmount, + feeType: SwapFeeType.WALLET_FEE + }; + + return { + pair: request.pair, + fromAmount, + toAmount, + rate, + provider: this.providerInfo, + aliveUntil: +Date.now() + (SWAP_QUOTE_TIMEOUT_MAP[this.slug] || SWAP_QUOTE_TIMEOUT_MAP.default), + minSwap: toBNString(metadata.minSwap.value, _getAssetDecimals(fromAsset)), + maxSwap: toBNString(metadata.maxSwap?.value, _getAssetDecimals(fromAsset)), + estimatedArrivalTime: 3600, + isLowLiquidity: false, + feeInfo: { + feeComponent: [networkFee, walletFee], + defaultFeeToken, + feeOptions: [defaultFeeToken] + }, + route: { + path: [fromAsset.slug, toAsset.slug] + } + } as SwapQuote; + } catch (e) { + return new SwapError(SwapErrorType.UNKNOWN); + } + } + + generateOptimalProcess (params: OptimalSwapPathParams): Promise { + return this.swapBaseHandler.generateOptimalProcess(params, [ + this.getSubmitStep + ]); + } + + async getSubmitStep (params: OptimalSwapPathParams): Promise<[BaseStepDetail, CommonStepFeeInfo] | undefined> { + if (params.selectedQuote) { + const submitStep = { + name: 'Swap', + type: SwapStepType.SWAP + }; + + return Promise.resolve([submitStep, params.selectedQuote.feeInfo]); + } + + return Promise.resolve(undefined); + } + + public async validateSwapRequest (request: SwapRequest): Promise { + try { + const fromAsset = this.chainService.getAssetBySlug(request.pair.from); + const toAsset = this.chainService.getAssetBySlug(request.pair.to); + + if (!fromAsset || !toAsset) { + return { error: SwapErrorType.ERROR_FETCHING_QUOTE }; + } + + const fromSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[fromAsset.slug]; + const toSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[toAsset.slug]; + + if (!fromSymbol || !toSymbol) { + return { error: SwapErrorType.ASSET_NOT_SUPPORTED }; + } + + try { + const swapList = await fetchSwapList({ fromSymbol }); + + if (!swapList.includes(toSymbol)) { + return { error: SwapErrorType.ASSET_NOT_SUPPORTED }; + } + } catch (err) { + console.error('Error:', err); + } + + const ranges = await fetchRanges({ fromSymbol, toSymbol }) as unknown as SwapRange; + const { max, min } = ranges; + const bnMin = toBNString(min, _getAssetDecimals(fromAsset)); + const bnAmount = BigInt(request.fromAmount); + + if (bnAmount < BigInt(bnMin)) { + return { + error: SwapErrorType.NOT_MEET_MIN_SWAP, + metadata: { + minSwap: { + value: min, + symbol: fromAsset.symbol + }, + maxSwap: max + ? { + value: max, + symbol: fromAsset.symbol + } + : undefined, + chain: this.chainService.getChainInfoByKey(fromAsset.originChain) + } as SimpleSwapValidationMetadata + }; + } + + if (max && bnAmount > BigInt(toBNString(max, _getAssetDecimals(fromAsset)))) { + return { + error: SwapErrorType.SWAP_EXCEED_ALLOWANCE, + metadata: { + minSwap: { + value: min, + symbol: fromAsset.symbol + }, + maxSwap: { + value: max, + symbol: fromAsset.symbol + }, + chain: this.chainService.getChainInfoByKey(fromAsset.originChain) + } as SimpleSwapValidationMetadata + }; + } + + return { + metadata: { + minSwap: { + value: min, + symbol: fromAsset.symbol + }, + maxSwap: max + ? { + value: max, + symbol: fromAsset.symbol + } + : undefined, + chain: this.chainService.getChainInfoByKey(fromAsset.originChain) + } as SimpleSwapValidationMetadata + }; + } catch (e) { + console.error(e); + + return { error: SwapErrorType.UNKNOWN }; + } + } + + public async handleSwapProcess (params: SwapSubmitParams): Promise { + const { currentStep, process } = params; + const type = process.steps[currentStep].type; + + switch (type) { + case CommonStepType.DEFAULT: + return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED)); + case SwapStepType.SWAP: + return this.handleSubmitStep(params); + default: + return this.handleSubmitStep(params); + } + } + + public async handleSubmitStep (params: SwapSubmitParams): Promise { + const { address, quote, recipient } = params; + + const pair = quote.pair; + + const fromAsset = this.chainService.getAssetBySlug(pair.from); + const toAsset = this.chainService.getAssetBySlug(pair.to); + const chainInfo = this.chainService.getChainInfoByKey(fromAsset.originChain); + const toChainInfo = this.chainService.getChainInfoByKey(toAsset.originChain); + const chainType = _isChainSubstrateCompatible(chainInfo) ? ChainType.SUBSTRATE : ChainType.EVM; + const sender = _reformatAddressWithChain(address, chainInfo); + const receiver = _reformatAddressWithChain(recipient ?? sender, toChainInfo); + + const fromSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[fromAsset.slug]; + const toSymbol = SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING[toAsset.slug]; + + const { fromAmount } = quote; + const { addressFrom, amountTo, id } = await createSwapRequest({ fromSymbol, toSymbol, fromAmount, fromAsset, receiver, sender, toAsset }); + + // Validate the amount to be swapped + const rate = BigN(amountTo).div(BigN(quote.toAmount)).multipliedBy(100); + + if (rate.lt(95)) { + throw new SwapError(SwapErrorType.NOT_MEET_MIN_EXPECTED); + } + + // Can modify quote.toAmount to amountTo after confirm real amount received + + const txData: SimpleSwapTxData = { + id: id, + address, + provider: this.providerInfo, + quote: params.quote, + slippage: params.slippage, + recipient: receiver, + process: params.process + }; + + let extrinsic: TransactionData; + + if (chainType === ChainType.SUBSTRATE) { + const chainApi = this.chainService.getSubstrateApi(chainInfo.slug); + const substrateApi = await chainApi.isReady; + + const [submittableExtrinsic] = await createTransferExtrinsic({ + from: address, + networkKey: chainInfo.slug, + substrateApi, + to: addressFrom, + tokenInfo: fromAsset, + transferAll: false, + value: quote.fromAmount + }); + + extrinsic = submittableExtrinsic as SubmittableExtrinsic<'promise'>; + } else { + if (_isNativeToken(fromAsset)) { + const [transactionConfig] = await getEVMTransactionObject( + chainInfo, + address, + addressFrom, + quote.fromAmount, + false, + this.chainService.getEvmApi(chainInfo.slug) + ); + + extrinsic = transactionConfig; + } else { + const [transactionConfig] = await getERC20TransactionObject( + _getContractAddressOfToken(fromAsset), + chainInfo, + address, + addressFrom, + quote.fromAmount, + false, + this.chainService.getEvmApi(chainInfo.slug) + ); + + extrinsic = transactionConfig; + } + } + + return { + txChain: fromAsset.originChain, + txData, + extrinsic, + transferNativeAmount: _isNativeToken(fromAsset) ? quote.fromAmount : '0', + extrinsicType: ExtrinsicType.SWAP, + chainType + } as SwapSubmitStepData; + } +} diff --git a/packages/extension-base/src/services/swap-service/index.ts b/packages/extension-base/src/services/swap-service/index.ts index b7da4e3e6f..9a2231d94a 100644 --- a/packages/extension-base/src/services/swap-service/index.ts +++ b/packages/extension-base/src/services/swap-service/index.ts @@ -18,6 +18,14 @@ import { _SUPPORTED_SWAP_PROVIDERS, OptimalSwapPathParams, QuoteAskResponse, Swa import { createPromiseHandler, PromiseHandler } from '@subwallet/extension-base/utils'; import { BehaviorSubject } from 'rxjs'; +import { SimpleSwapHandler } from './handler/simpleswap-handler'; + +export const _isChainSupportedByProvider = (providerSlug: SwapProviderId, chain: string) => { + const supportedChains = _PROVIDER_TO_SUPPORTED_PAIR_MAP[providerSlug]; + + return supportedChains ? supportedChains.includes(chain) : false; +}; + export class SwapService implements ServiceWithProcessInterface, StoppableServiceInterface { protected readonly state: KoniState; private eventService: EventService; @@ -41,7 +49,7 @@ export class SwapService implements ServiceWithProcessInterface, StoppableServic await Promise.all(Object.values(this.handlers).map(async (handler) => { // temporary solution to reduce number of requests to providers, will work as long as there's only 1 provider for 1 chain - if (!_PROVIDER_TO_SUPPORTED_PAIR_MAP[handler.providerSlug].includes(swappingSrcChain)) { + if (!_isChainSupportedByProvider(handler.providerSlug, swappingSrcChain)) { return; } @@ -141,7 +149,7 @@ export class SwapService implements ServiceWithProcessInterface, StoppableServic quoteError = preferredErrorResp?.error || defaultErrorResp?.error; } else { - selectedQuote = availableQuotes[0]; + selectedQuote = availableQuotes.find((quote) => quote.provider.id === request.currentQuote?.id) || availableQuotes[0]; aliveUntil = selectedQuote?.aliveUntil || (+Date.now() + SWAP_QUOTE_TIMEOUT_MAP.default); } @@ -182,6 +190,9 @@ export class SwapService implements ServiceWithProcessInterface, StoppableServic case SwapProviderId.ROCOCO_ASSET_HUB: this.handlers[providerId] = new AssetHubSwapHandler(this.chainService, this.state.balanceService, 'rococo_assethub'); break; + case SwapProviderId.SIMPLE_SWAP: + this.handlers[providerId] = new SimpleSwapHandler(this.chainService, this.state.balanceService); + break; default: throw new Error('Unsupported provider'); diff --git a/packages/extension-base/src/services/swap-service/utils.ts b/packages/extension-base/src/services/swap-service/utils.ts index 6dd416ab0f..7df6ef92c3 100644 --- a/packages/extension-base/src/services/swap-service/utils.ts +++ b/packages/extension-base/src/services/swap-service/utils.ts @@ -12,6 +12,8 @@ import BigN from 'bignumber.js'; export const CHAIN_FLIP_TESTNET_EXPLORER = 'https://blocks-perseverance.chainflip.io'; export const CHAIN_FLIP_MAINNET_EXPLORER = 'https://scan.chainflip.io'; +export const SIMPLE_SWAP_EXPLORER = 'https://simpleswap.io'; + export const CHAIN_FLIP_SUPPORTED_MAINNET_MAPPING: Record = { [COMMON_CHAIN_SLUGS.POLKADOT]: Chains.Polkadot, [COMMON_CHAIN_SLUGS.ETHEREUM]: Chains.Ethereum, @@ -36,6 +38,14 @@ export const CHAIN_FLIP_SUPPORTED_TESTNET_ASSET_MAPPING: Record = [COMMON_ASSETS.USDC_SEPOLIA]: Assets.USDC }; +export const SIMPLE_SWAP_SUPPORTED_TESTNET_ASSET_MAPPING: Record = { + 'bittensor-NATIVE-TAO': 'tao', + [COMMON_ASSETS.ETH]: 'eth', + [COMMON_ASSETS.DOT]: 'dot', + [COMMON_ASSETS.USDC_ETHEREUM]: 'usdc', + [COMMON_ASSETS.USDT_ETHEREUM]: 'usdterc20' +}; + export const SWAP_QUOTE_TIMEOUT_MAP: Record = { // in milliseconds default: 30000, [SwapProviderId.CHAIN_FLIP_TESTNET]: 30000, @@ -49,7 +59,8 @@ export const _PROVIDER_TO_SUPPORTED_PAIR_MAP: Record = { [SwapProviderId.CHAIN_FLIP_TESTNET]: [COMMON_CHAIN_SLUGS.CHAINFLIP_POLKADOT, COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA], [SwapProviderId.POLKADOT_ASSET_HUB]: [COMMON_CHAIN_SLUGS.POLKADOT_ASSET_HUB], [SwapProviderId.KUSAMA_ASSET_HUB]: [COMMON_CHAIN_SLUGS.KUSAMA_ASSET_HUB], - [SwapProviderId.ROCOCO_ASSET_HUB]: [COMMON_CHAIN_SLUGS.ROCOCO_ASSET_HUB] + [SwapProviderId.ROCOCO_ASSET_HUB]: [COMMON_CHAIN_SLUGS.ROCOCO_ASSET_HUB], + [SwapProviderId.SIMPLE_SWAP]: ['bittensor', COMMON_CHAIN_SLUGS.ETHEREUM, COMMON_CHAIN_SLUGS.POLKADOT] }; export function getSwapAlternativeAsset (swapPair: SwapPair): string | undefined { diff --git a/packages/extension-base/src/services/transaction-service/index.ts b/packages/extension-base/src/services/transaction-service/index.ts index ab0935133a..2249f18118 100644 --- a/packages/extension-base/src/services/transaction-service/index.ts +++ b/packages/extension-base/src/services/transaction-service/index.ts @@ -1098,7 +1098,7 @@ export default class TransactionService { return emitter; } - private async signAndSendSubstrateTransaction ({ address, chain, id, transaction, url }: SWTransaction): Promise { + private signAndSendSubstrateTransaction ({ address, chain, id, transaction, url }: SWTransaction): TransactionEmitter { const emitter = new EventEmitter(); const eventData: TransactionEventResponse = { id, @@ -1108,8 +1108,8 @@ export default class TransactionService { }; const extrinsic = transaction as SubmittableExtrinsic; - const registry = extrinsic.registry; - const signedExtensions = registry.signedExtensions; + // const registry = extrinsic.registry; + // const signedExtensions = registry.signedExtensions; const signerOption: Partial = { signer: { @@ -1126,14 +1126,14 @@ export default class TransactionService { withSignedTransaction: true }; - if (_isRuntimeUpdated(signedExtensions)) { - const metadataHash = await this.state.chainService.calculateMetadataHash(chain); - - if (metadataHash) { - signerOption.mode = 1; - signerOption.metadataHash = metadataHash; - } - } + // if (_isRuntimeUpdated(signedExtensions)) { + // const metadataHash = await this.state.chainService.calculateMetadataHash(chain); + // + // if (metadataHash) { + // signerOption.mode = 1; + // signerOption.metadataHash = metadataHash; + // } + // } extrinsic.signAsync(address, signerOption).then(async (rs) => { // Emit signed event diff --git a/packages/extension-base/src/services/transaction-service/utils.ts b/packages/extension-base/src/services/transaction-service/utils.ts index 72a5dcfa16..87247d9c02 100644 --- a/packages/extension-base/src/services/transaction-service/utils.ts +++ b/packages/extension-base/src/services/transaction-service/utils.ts @@ -4,8 +4,8 @@ import { _ChainInfo } from '@subwallet/chain-list/types'; import { ExtrinsicDataTypeMap, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; import { _getBlockExplorerFromChain, _isChainTestNet, _isPureEvmChain } from '@subwallet/extension-base/services/chain-service/utils'; -import { CHAIN_FLIP_MAINNET_EXPLORER, CHAIN_FLIP_TESTNET_EXPLORER } from '@subwallet/extension-base/services/swap-service/utils'; -import { ChainflipSwapTxData } from '@subwallet/extension-base/types/swap'; +import { CHAIN_FLIP_MAINNET_EXPLORER, CHAIN_FLIP_TESTNET_EXPLORER, SIMPLE_SWAP_EXPLORER } from '@subwallet/extension-base/services/swap-service/utils'; +import { ChainflipSwapTxData, SimpleSwapTxData } from '@subwallet/extension-base/types/swap'; // @ts-ignore export function parseTransactionData (data: unknown): ExtrinsicDataTypeMap[T] { @@ -63,7 +63,7 @@ function getBlockExplorerTxRoute (chainInfo: _ChainInfo) { return 'transaction'; } - if (['invarch'].includes(chainInfo.slug)) { + if (['invarch', 'tangle'].includes(chainInfo.slug)) { return '#/extrinsics'; } @@ -86,6 +86,10 @@ export function getExplorerLink (chainInfo: _ChainInfo, value: string, type: 'ac const route = getBlockExplorerTxRoute(chainInfo); + if (chainInfo.slug === 'tangle') { + return (`${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}extrinsic/${value}${route}/${value}`); + } + return (`${explorerLink}${explorerLink.endsWith('/') ? '' : '/'}${route}/${value}`); } @@ -97,3 +101,9 @@ export function getChainflipExplorerLink (data: ChainflipSwapTxData, chainInfo: return `${chainflipDomain}/channels/${data.depositChannelId}`; } + +export function getSimpleSwapExplorerLink (data: SimpleSwapTxData) { + const simpleswapDomain = SIMPLE_SWAP_EXPLORER; + + return `${simpleswapDomain}/exchange?id=${data.id}`; +} diff --git a/packages/extension-base/src/types/account/info/keyring.ts b/packages/extension-base/src/types/account/info/keyring.ts index ca66298e62..5891a41ca9 100644 --- a/packages/extension-base/src/types/account/info/keyring.ts +++ b/packages/extension-base/src/types/account/info/keyring.ts @@ -59,6 +59,8 @@ export interface AccountLedgerData { originGenesisHash?: string | null; /** Ledger's availableGenesisHashes */ availableGenesisHashes?: string[]; + /** Is Ledger recovery chain */ + isLedgerRecovery?: boolean; } /** diff --git a/packages/extension-base/src/types/swap/index.ts b/packages/extension-base/src/types/swap/index.ts index 129dc7c6f5..aeb654e7eb 100644 --- a/packages/extension-base/src/types/swap/index.ts +++ b/packages/extension-base/src/types/swap/index.ts @@ -53,6 +53,7 @@ export enum SwapErrorType { NOT_ENOUGH_LIQUIDITY = 'NOT_ENOUGH_LIQUIDITY', MAKE_POOL_NOT_ENOUGH_EXISTENTIAL_DEPOSIT = 'MAKE_POOL_NOT_ENOUGH_EXISTENTIAL_DEPOSIT', AMOUNT_CANNOT_BE_ZERO = 'AMOUNT_CANNOT_BE_ZERO', + NOT_MEET_MIN_EXPECTED = 'NOT_MEET_MIN_EXPECTED', } export enum SwapStepType { @@ -67,6 +68,7 @@ export enum SwapProviderId { POLKADOT_ASSET_HUB = 'POLKADOT_ASSET_HUB', KUSAMA_ASSET_HUB = 'KUSAMA_ASSET_HUB', ROCOCO_ASSET_HUB = 'ROCOCO_ASSET_HUB', + SIMPLE_SWAP = 'SIMPLE_SWAP' } export const _SUPPORTED_SWAP_PROVIDERS: SwapProviderId[] = [ @@ -76,7 +78,8 @@ export const _SUPPORTED_SWAP_PROVIDERS: SwapProviderId[] = [ SwapProviderId.HYDRADX_TESTNET, SwapProviderId.POLKADOT_ASSET_HUB, SwapProviderId.KUSAMA_ASSET_HUB, - SwapProviderId.ROCOCO_ASSET_HUB + SwapProviderId.ROCOCO_ASSET_HUB, + SwapProviderId.SIMPLE_SWAP ]; export interface SwapProvider { @@ -93,7 +96,7 @@ export enum SwapFeeType { WALLET_FEE = 'WALLET_FEE' } -export type SwapTxData = ChainflipSwapTxData | HydradxSwapTxData; // todo: will be more +export type SwapTxData = ChainflipSwapTxData | HydradxSwapTxData | SimpleSwapTxData; // todo: will be more export interface SwapBaseTxData { provider: SwapProvider; @@ -110,6 +113,10 @@ export interface ChainflipSwapTxData extends SwapBaseTxData { estimatedDepositChannelExpiryTime?: number; } +export interface SimpleSwapTxData extends SwapBaseTxData { + id: string; +} + export interface HydradxSwapTxData extends SwapBaseTxData { txHex: string; } @@ -135,6 +142,12 @@ export interface AssetHubPreValidationMetadata { priceImpactPct?: string; } +export interface SimpleSwapValidationMetadata{ + minSwap: AmountData; + maxSwap: AmountData; + chain: _ChainInfo; +} + export interface QuoteAskResponse { quote?: SwapQuote; error?: SwapError; @@ -147,6 +160,7 @@ export interface SwapRequest { slippage: number; // Example: 0.01 for 1% recipient?: string; feeToken?: string; + currentQuote?: SwapProvider } export interface SwapRequestResult { @@ -206,3 +220,4 @@ export interface SlippageType { } export const CHAINFLIP_SLIPPAGE = 0.02; // Example: 0.01 for 1% +export const SIMPLE_SWAP_SLIPPAGE = 0.05; diff --git a/packages/extension-base/src/utils/number.ts b/packages/extension-base/src/utils/number.ts index 132c3a8461..9d95cb670e 100644 --- a/packages/extension-base/src/utils/number.ts +++ b/packages/extension-base/src/utils/number.ts @@ -240,6 +240,15 @@ export const toBNString = (input: string | number | BigNumber, decimal: number): return raw.multipliedBy(BN_TEN.pow(decimal)).toFixed(); }; +/** @function formatNumber + * Convert number to a formatted string by dividing by 10^decimal + * @param {string | number | BigNumber} input - Input number + * @param {number} decimal - Decimal number + * @param {NumberFormatter} [formatter] - Formatter function + * - Default: balanceFormatter: With number > 1, show decimal with 2 numbers, + * with number < 1, show decimal with 6 (default) number + * @param {Record} [metadata] - Metadata for formatter + */ export const formatNumber = ( input: string | number | BigNumber, decimal: number, diff --git a/packages/extension-chains/package.json b/packages/extension-chains/package.json index a2684016cc..cc6cec588f 100644 --- a/packages/extension-chains/package.json +++ b/packages/extension-chains/package.json @@ -17,14 +17,14 @@ "./detectPackage.cjs" ], "type": "module", - "version": "1.3.7-0", + "version": "1.3.11-0", "main": "index.js", "dependencies": { "@babel/runtime": "^7.20.6", "@polkadot/networks": "^12.6.2", "@polkadot/util": "^12.6.2", "@polkadot/util-crypto": "^12.6.2", - "@subwallet/extension-inject": "^1.3.7-0" + "@subwallet/extension-inject": "^1.3.11-0" }, "peerDependencies": { "@polkadot/api": "*", diff --git a/packages/extension-chains/src/packageInfo.ts b/packages/extension-chains/src/packageInfo.ts index 12feb6eb36..993a03b29b 100644 --- a/packages/extension-chains/src/packageInfo.ts +++ b/packages/extension-chains/src/packageInfo.ts @@ -3,4 +3,4 @@ // Do not edit, auto-generated by @polkadot/dev -export const packageInfo = { name: '@subwallet/extension-chains', path: 'auto', type: 'auto', version: '1.3.7-0' }; +export const packageInfo = { name: '@subwallet/extension-chains', path: 'auto', type: 'auto', version: '1.3.11-0' }; diff --git a/packages/extension-compat-metamask/package.json b/packages/extension-compat-metamask/package.json index 332dbb50ca..6325b6ef36 100644 --- a/packages/extension-compat-metamask/package.json +++ b/packages/extension-compat-metamask/package.json @@ -17,14 +17,14 @@ "./detectPackage.cjs" ], "type": "module", - "version": "1.3.7-0", + "version": "1.3.11-0", "main": "index.js", "dependencies": { "@babel/runtime": "^7.20.6", "@metamask/detect-provider": "^1.2.0", "@polkadot/types": "^11.0.3", "@polkadot/util": "^12.6.2", - "@subwallet/extension-inject": "^1.3.7-0", + "@subwallet/extension-inject": "^1.3.11-0", "web3": "^1.10.0" }, "peerDependencies": { diff --git a/packages/extension-compat-metamask/src/packageInfo.ts b/packages/extension-compat-metamask/src/packageInfo.ts index 8706e6a9e0..391d715c4e 100644 --- a/packages/extension-compat-metamask/src/packageInfo.ts +++ b/packages/extension-compat-metamask/src/packageInfo.ts @@ -3,4 +3,4 @@ // Do not edit, auto-generated by @polkadot/dev -export const packageInfo = { name: '@subwallet/extension-compat-metamask', path: 'auto', type: 'auto', version: '1.3.7-0' }; +export const packageInfo = { name: '@subwallet/extension-compat-metamask', path: 'auto', type: 'auto', version: '1.3.11-0' }; diff --git a/packages/extension-dapp/package.json b/packages/extension-dapp/package.json index 2f1866ec58..6c3d74eb27 100644 --- a/packages/extension-dapp/package.json +++ b/packages/extension-dapp/package.json @@ -17,13 +17,13 @@ "./detectPackage.cjs" ], "type": "module", - "version": "1.3.7-0", + "version": "1.3.11-0", "main": "index.js", "dependencies": { "@babel/runtime": "^7.20.6", "@polkadot/util": "^12.6.2", "@polkadot/util-crypto": "^12.6.2", - "@subwallet/extension-inject": "^1.3.7-0" + "@subwallet/extension-inject": "^1.3.11-0" }, "peerDependencies": { "@polkadot/api": "*", diff --git a/packages/extension-dapp/src/packageInfo.ts b/packages/extension-dapp/src/packageInfo.ts index f035d0e1d8..ee400c85f0 100644 --- a/packages/extension-dapp/src/packageInfo.ts +++ b/packages/extension-dapp/src/packageInfo.ts @@ -3,4 +3,4 @@ // Do not edit, auto-generated by @polkadot/dev -export const packageInfo = { name: '@subwallet/extension-dapp', path: 'auto', type: 'auto', version: '1.3.7-0' }; +export const packageInfo = { name: '@subwallet/extension-dapp', path: 'auto', type: 'auto', version: '1.3.11-0' }; diff --git a/packages/extension-inject/package.json b/packages/extension-inject/package.json index af574f79d1..71f2cc1a78 100644 --- a/packages/extension-inject/package.json +++ b/packages/extension-inject/package.json @@ -14,7 +14,7 @@ }, "sideEffects": true, "type": "module", - "version": "1.3.7-0", + "version": "1.3.11-0", "main": "index.js", "dependencies": { "@babel/runtime": "^7.20.6", diff --git a/packages/extension-inject/src/packageInfo.ts b/packages/extension-inject/src/packageInfo.ts index 8583f27ac4..7a95154859 100644 --- a/packages/extension-inject/src/packageInfo.ts +++ b/packages/extension-inject/src/packageInfo.ts @@ -3,4 +3,4 @@ // Do not edit, auto-generated by @polkadot/dev -export const packageInfo = { name: '@subwallet/extension-inject', path: 'auto', type: 'auto', version: '1.3.7-0' }; +export const packageInfo = { name: '@subwallet/extension-inject', path: 'auto', type: 'auto', version: '1.3.11-0' }; diff --git a/packages/extension-koni-ui/package.json b/packages/extension-koni-ui/package.json index 8c95376024..d16e001b62 100644 --- a/packages/extension-koni-ui/package.json +++ b/packages/extension-koni-ui/package.json @@ -14,7 +14,7 @@ }, "sideEffects": true, "type": "module", - "version": "1.3.7-0", + "version": "1.3.11-0", "dependencies": { "@babel/runtime": "^7.20.6", "@coinbase/cbpay-js": "^1.7.0", @@ -34,11 +34,11 @@ "@polkadot/util-crypto": "^12.6.2", "@ramonak/react-progress-bar": "^5.0.3", "@reduxjs/toolkit": "^1.9.1", - "@subwallet/chain-list": "0.2.95-beta.1", - "@subwallet/extension-base": "^1.3.7-0", - "@subwallet/extension-chains": "^1.3.7-0", - "@subwallet/extension-dapp": "^1.3.7-0", - "@subwallet/extension-inject": "^1.3.7-0", + "@subwallet/chain-list": "0.2.96-beta.8", + "@subwallet/extension-base": "^1.3.11-0", + "@subwallet/extension-chains": "^1.3.11-0", + "@subwallet/extension-dapp": "^1.3.11-0", + "@subwallet/extension-inject": "^1.3.11-0", "@subwallet/keyring": "^0.1.8-beta.0", "@subwallet/react-ui": "5.1.2-b77", "@subwallet/ui-keyring": "^0.1.8-beta.0", @@ -87,7 +87,7 @@ "usehooks-ts": "^2.9.1" }, "devDependencies": { - "@subwallet/extension-mocks": "^1.3.7-0", + "@subwallet/extension-mocks": "^1.3.11-0", "@types/bn.js": "^5.1.6", "@types/enzyme": "^3.10.12", "@types/enzyme-adapter-react-16": "^1.0.6", diff --git a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx index c303b72158..f2e0588060 100644 --- a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx +++ b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx @@ -4,11 +4,13 @@ import { LedgerNetwork, MigrationLedgerNetwork } from '@subwallet/extension-base/background/KoniTypes'; import { reformatAddress } from '@subwallet/extension-base/utils'; import { AccountItemWithName, AccountWithNameSkeleton, BasicOnChangeFunction, ChainSelector, DualLogo, InfoIcon, Layout, PageWrapper } from '@subwallet/extension-koni-ui/components'; +import { LedgerChainSelector, LedgerItemType } from '@subwallet/extension-koni-ui/components/Field/LedgerChainSelector'; import { ATTACH_ACCOUNT_MODAL, SUBSTRATE_MIGRATION_KEY, USER_GUIDE_URL } from '@subwallet/extension-koni-ui/constants'; import { useAutoNavigateToCreatePassword, useCompleteCreateAccount, useGetSupportedLedger, useGoBackFromCreateAccount, useLedger } from '@subwallet/extension-koni-ui/hooks'; import { createAccountHardwareMultiple } from '@subwallet/extension-koni-ui/messaging'; import { RootState } from '@subwallet/extension-koni-ui/stores'; import { ChainItemType, ThemeProps } from '@subwallet/extension-koni-ui/types'; +import { convertNetworkSlug } from '@subwallet/extension-koni-ui/utils'; import { BackgroundIcon, Button, Icon, Image, SwList } from '@subwallet/react-ui'; import CN from 'classnames'; import { CheckCircle, CircleNotch, Swatches } from 'phosphor-react'; @@ -54,11 +56,14 @@ const Component: React.FC = (props: Props) => { const { accounts } = useSelector((state: RootState) => state.accountState); - const networks = useMemo((): ChainItemType[] => supportedLedger + const networks = useMemo((): LedgerItemType[] => supportedLedger .filter(({ isHide }) => !isHide) .map((network) => ({ - name: !network.isGeneric ? network.networkName.replace(' network', '') : network.networkName, - slug: network.slug + name: !network.isGeneric + ? network.networkName.replace(' network', '').concat(network.isRecovery ? ' Recovery' : '') + : network.networkName, + chain: network.slug, + slug: convertNetworkSlug(network) })), [supportedLedger]); const networkMigrates = useMemo((): ChainItemType[] => migrateSupportLedger @@ -78,7 +83,7 @@ const Component: React.FC = (props: Props) => { const [isSubmitting, setIsSubmitting] = useState(false); const selectedChain = useMemo((): LedgerNetwork | undefined => { - return supportedLedger.find((n) => n.slug === chain); + return supportedLedger.find((n) => convertNetworkSlug(n) === chain); }, [chain, supportedLedger]); const selectedChainMigrateMode = useMemo((): MigrationLedgerNetwork | undefined => { @@ -93,7 +98,7 @@ const Component: React.FC = (props: Props) => { return chainMigrateMode && selectedChain ? `${selectedChain.accountName}` : ''; }, [chainMigrateMode, migrateSupportLedger]); - const { error, getAllAddress, isLoading, isLocked, ledger, refresh, warning } = useLedger(chain, true, false, false, selectedChainMigrateMode?.genesisHash); + const { error, getAllAddress, isLoading, isLocked, ledger, refresh, warning } = useLedger(selectedChain?.slug, true, false, false, selectedChainMigrateMode?.genesisHash, selectedChain?.isRecovery); const onPreviousStep = useCallback(() => { setFirstStep(true); @@ -248,7 +253,8 @@ const Component: React.FC = (props: Props) => { hardwareType: 'ledger', name: item.name, isEthereum: selectedChain.isEthereum, - isGeneric: selectedChain.isGeneric + isGeneric: selectedChain.isGeneric, + isLedgerRecovery: selectedChain?.isRecovery })) }) .then(() => { @@ -326,7 +332,7 @@ const Component: React.FC = (props: Props) => { sizeSquircleBorder={108} /> - = (props: Props) => { refresh: refreshLedger, signMessage: ledgerSignMessage, signTransaction: ledgerSignTransaction, - warning: ledgerWarning } = useLedger(chainSlug, activeLedger, true, forceUseMigrationApp, account?.originGenesisHash); + warning: ledgerWarning } = useLedger(chainSlug, activeLedger, true, forceUseMigrationApp, account?.originGenesisHash, account?.isLedgerRecovery); const isLedgerConnected = useMemo(() => !isLocked && !isLedgerLoading && !!ledger, [isLedgerLoading, isLocked, ledger]); diff --git a/packages/extension-koni-ui/src/Popup/Home/History/Detail/index.tsx b/packages/extension-koni-ui/src/Popup/Home/History/Detail/index.tsx index b47ff9b18f..a9ffe30994 100644 --- a/packages/extension-koni-ui/src/Popup/Home/History/Detail/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Home/History/Detail/index.tsx @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { ExtrinsicType, TransactionAdditionalInfo } from '@subwallet/extension-base/background/KoniTypes'; -import { getExplorerLink } from '@subwallet/extension-base/services/transaction-service/utils'; +import { getExplorerLink, getSimpleSwapExplorerLink } from '@subwallet/extension-base/services/transaction-service/utils'; +import { SimpleSwapTxData, SwapProviderId, SwapTxData } from '@subwallet/extension-base/types'; import { InfoItemBase } from '@subwallet/extension-koni-ui/components'; import { HISTORY_DETAIL_MODAL } from '@subwallet/extension-koni-ui/constants'; import { RootState } from '@subwallet/extension-koni-ui/stores'; @@ -55,7 +56,15 @@ function Component ({ className = '', data, onCancel }: Props): React.ReactEleme originChainInfo = chainInfoMap[additionalInfo.originalChain] || chainInfo; } - const link = (data.extrinsicHash && data.extrinsicHash !== '') && getExplorerLink(originChainInfo, data.extrinsicHash, 'tx'); + let link = (data.extrinsicHash && data.extrinsicHash !== '') && getExplorerLink(originChainInfo, data.extrinsicHash, 'tx'); + + if (extrinsicType === ExtrinsicType.SWAP) { + const additionalInfo = data.additionalInfo as SwapTxData; + + if ([SwapProviderId.SIMPLE_SWAP].includes(additionalInfo.provider.id)) { + link = getSimpleSwapExplorerLink(additionalInfo as SimpleSwapTxData); + } + } return (