From 9b1987c663890ae056ba663ce72bc6e914b0cc58 Mon Sep 17 00:00:00 2001 From: PDTnhah Date: Wed, 4 Dec 2024 14:24:15 +0700 Subject: [PATCH 01/41] [Bifrost] Add referral code for Bifrost Liquid Staking (vDOT, vMANTA) --- .../src/services/earning-service/constants/chains.ts | 2 ++ .../handlers/liquid-staking/bifrost-manta.ts | 3 ++- .../earning-service/handlers/liquid-staking/bifrost.ts | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) 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, From b88b2b37d1acab5e0fdd02db70353ea5508809a8 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Fri, 6 Dec 2024 18:27:14 +0700 Subject: [PATCH 02/41] [Add] Support bridge ETH <-> POS --- .../src/core/substrate/xcm-parser.ts | 19 +- .../koni/api/contract-handler/utils/index.ts | 30 + .../utils/pos_bridge_abi.json | 783 +++++++++++++++++ .../utils/pos_bridge_l2_abi.json | 793 ++++++++++++++++++ .../src/koni/background/handlers/Extension.ts | 16 +- .../balance-service/transfer/xcm/index.ts | 23 +- .../transfer/xcm/polygonBridge.ts | 2 +- .../balance-service/transfer/xcm/posBridge.ts | 170 ++++ .../inapp-notification-service/index.ts | 3 +- .../inapp-notification-service/interfaces.ts | 13 +- .../utils/polygon.ts | 14 +- .../Settings/Notifications/Notification.tsx | 11 +- .../Popup/Transaction/variants/SendFund.tsx | 3 +- .../Popup/Transaction/variants/SendFund.tsx | 3 +- 14 files changed, 1852 insertions(+), 31 deletions(-) create mode 100644 packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_abi.json create mode 100644 packages/extension-base/src/koni/api/contract-handler/utils/pos_bridge_l2_abi.json create mode 100644 packages/extension-base/src/services/balance-service/transfer/xcm/posBridge.ts 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 5f986c235f..b3f0ecf0f9 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -33,10 +33,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 } from '@subwallet/extension-base/services/chain-service/constants'; import { _ChainApiStatus, _ChainConnectionStatus, _ChainState, _NetworkUpsertParams, _ValidateCustomAssetRequest, _ValidateCustomAssetResponse, EnableChainParams, EnableMultiChainParams } from '@subwallet/extension-base/services/chain-service/types'; import { _getAssetDecimals, _getAssetSymbol, _getChainNativeTokenBasicInfo, _getContractAddressOfToken, _getEvmChainId, _isAssetSmartContractNft, _isChainEvmCompatible, _isChainTonCompatible, _isCustomAsset, _isLocalToken, _isMantaZkAsset, _isNativeToken, _isPureEvmChain, _isTokenEvmSmartContract, _isTokenTransferredByEvm, _isTokenTransferredByTon } from '@subwallet/extension-base/services/chain-service/utils'; -import { _NotificationInfo, NotificationSetup } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; +import { _NotificationInfo, 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'; @@ -1452,6 +1453,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); @@ -1472,7 +1474,7 @@ export default class KoniExtension { let funcCreateExtrinsic: FunctionCreateXcmExtrinsic; - if (isPolygonBridgeTransfer) { + if (isPosBridgeTransfer || isPolygonBridgeTransfer) { funcCreateExtrinsic = createPolygonBridgeExtrinsic; } else if (isSnowBridgeEvmTransfer) { funcCreateExtrinsic = createSnowBridgeExtrinsic; @@ -1538,7 +1540,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, @@ -3860,8 +3862,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/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..186b8f996b --- /dev/null +++ b/packages/extension-base/src/services/balance-service/transfer/xcm/posBridge.ts @@ -0,0 +1,170 @@ +// 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; +} 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-koni-ui/src/Popup/Settings/Notifications/Notification.tsx b/packages/extension-koni-ui/src/Popup/Settings/Notifications/Notification.tsx index ca2b8d198e..d9dfbc3df0 100644 --- a/packages/extension-koni-ui/src/Popup/Settings/Notifications/Notification.tsx +++ b/packages/extension-koni-ui/src/Popup/Settings/Notifications/Notification.tsx @@ -1,9 +1,11 @@ // Copyright 2019-2022 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { COMMON_CHAIN_SLUGS } from '@subwallet/chain-list'; import { NotificationType } from '@subwallet/extension-base/background/KoniTypes'; import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants'; import { _POLYGON_BRIDGE_ABI } from '@subwallet/extension-base/koni/api/contract-handler/utils'; +import { isClaimedPosBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/posBridge'; import { _NotificationInfo, BridgeTransactionStatus, ClaimAvailBridgeNotificationMetadata, ClaimPolygonBridgeNotificationMetadata, NotificationActionType, NotificationSetup, NotificationTab, WithdrawClaimNotificationMetadata } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; import { GetNotificationParams, RequestSwitchStatusParams } from '@subwallet/extension-base/types/notification'; import { detectTranslate } from '@subwallet/extension-base/utils'; @@ -346,7 +348,14 @@ function Component ({ className = '' }: Props): React.ReactElement { const handleClaimPolygonBridge = async () => { try { const metadata = item.metadata as ClaimPolygonBridgeNotificationMetadata; - const isClaimed = await getIsClaimNotificationStatus({ chainslug: metadata.chainSlug, counter: metadata.counter, sourceNetwork: metadata.sourceNetwork }); + let isClaimed = false; + const isTestnet = metadata.chainSlug === COMMON_CHAIN_SLUGS.ETHEREUM_SEPOLIA; + + if (!(metadata.bridgeType === 'POS')) { + isClaimed = await getIsClaimNotificationStatus({ chainslug: metadata.chainSlug, counter: metadata.counter ?? 0, sourceNetwork: metadata.sourceNetwork ?? 0 }); + } else { + isClaimed = await isClaimedPosBridge(metadata._id, metadata.userAddress, isTestnet) || false; + } if (!isClaimed) { setClaimAvailBridgeStorage({ diff --git a/packages/extension-koni-ui/src/Popup/Transaction/variants/SendFund.tsx b/packages/extension-koni-ui/src/Popup/Transaction/variants/SendFund.tsx index b0ec09375b..288416d102 100644 --- a/packages/extension-koni-ui/src/Popup/Transaction/variants/SendFund.tsx +++ b/packages/extension-koni-ui/src/Popup/Transaction/variants/SendFund.tsx @@ -10,6 +10,7 @@ import { ActionType } from '@subwallet/extension-base/core/types'; import { getAvailBridgeGatewayContract, getSnowBridgeGatewayContract } from '@subwallet/extension-base/koni/api/contract-handler/utils'; 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 { _getAssetDecimals, _getAssetName, _getAssetOriginChain, _getAssetSymbol, _getContractAddressOfToken, _getMultiChainAsset, _getOriginChainOfAsset, _getTokenMinAmount, _isChainEvmCompatible, _isNativeToken, _isTokenTransferredByEvm } from '@subwallet/extension-base/services/chain-service/utils'; import { TON_CHAINS } from '@subwallet/extension-base/services/earning-service/constants'; import { SWTransactionResponse } from '@subwallet/extension-base/services/transaction-service/types'; @@ -172,7 +173,7 @@ const Component = ({ className = '', isAllAccount, targetAccountProxy }: Compone const hideMaxButton = useMemo(() => { const chainInfo = chainInfoMap[chainValue]; - if (_isPolygonChainBridge(chainValue, destChainValue)) { + if (_isPolygonChainBridge(chainValue, destChainValue) || _isPosChainBridge(chainValue, destChainValue)) { return true; } diff --git a/packages/extension-web-ui/src/Popup/Transaction/variants/SendFund.tsx b/packages/extension-web-ui/src/Popup/Transaction/variants/SendFund.tsx index acf518ab0e..935e26ebd6 100644 --- a/packages/extension-web-ui/src/Popup/Transaction/variants/SendFund.tsx +++ b/packages/extension-web-ui/src/Popup/Transaction/variants/SendFund.tsx @@ -41,6 +41,7 @@ import { isAddress, isEthereumAddress } from '@polkadot/util-crypto'; import { FreeBalance, TransactionContent, TransactionFooter } from '../parts'; import { _isPolygonChainBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge'; +import { _isPosChainBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/posBridge'; type Props = ThemeProps & { modalContent?: boolean; @@ -259,7 +260,7 @@ const _SendFund = ({ className = '', modalContent }: Props): React.ReactElement< const hideMaxButton = useMemo(() => { const chainInfo = chainInfoMap[chain]; - if (_isPolygonChainBridge(chain, destChain)) { + if (_isPolygonChainBridge(chain, destChain) || _isPosChainBridge(chain, destChain)) { return true; } From b84a2a1bb2dec40d2d0ecb8c130cc0d00c16b2b1 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Tue, 10 Dec 2024 14:47:04 +0700 Subject: [PATCH 03/41] [Add] Update chainlist --- package.json | 2 +- packages/extension-base/package.json | 2 +- packages/extension-koni-ui/package.json | 2 +- packages/extension-web-ui/package.json | 2 +- yarn.lock | 13 +++++++------ 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 4f10cefb0e..4ccdf72db5 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "@polkadot/types-support": "^12.0.2", "@polkadot/util": "^12.6.2", "@polkadot/util-crypto": "^12.6.2", - "@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", diff --git a/packages/extension-base/package.json b/packages/extension-base/package.json index 7997c7d92f..5e0efd7dbf 100644 --- a/packages/extension-base/package.json +++ b/packages/extension-base/package.json @@ -55,7 +55,7 @@ "@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/chain-list": "0.2.96-beta.8", "@subwallet/extension-base": "^1.3.7-0", "@subwallet/extension-chains": "^1.3.7-0", "@subwallet/extension-dapp": "^1.3.7-0", diff --git a/packages/extension-koni-ui/package.json b/packages/extension-koni-ui/package.json index 5c67e86d05..2ce2e10657 100644 --- a/packages/extension-koni-ui/package.json +++ b/packages/extension-koni-ui/package.json @@ -34,7 +34,7 @@ "@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/chain-list": "0.2.96-beta.8", "@subwallet/extension-base": "^1.3.7-0", "@subwallet/extension-chains": "^1.3.7-0", "@subwallet/extension-dapp": "^1.3.7-0", diff --git a/packages/extension-web-ui/package.json b/packages/extension-web-ui/package.json index 4921753d69..96924810be 100644 --- a/packages/extension-web-ui/package.json +++ b/packages/extension-web-ui/package.json @@ -35,7 +35,7 @@ "@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/chain-list": "0.2.96-beta.8", "@subwallet/extension-base": "^1.3.7-0", "@subwallet/extension-chains": "^1.3.7-0", "@subwallet/extension-dapp": "^1.3.7-0", diff --git a/yarn.lock b/yarn.lock index c0e7ba4a4f..fbfa44c47b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6265,14 +6265,15 @@ __metadata: languageName: node linkType: hard -"@subwallet/chain-list@npm:0.2.95": - version: 0.2.95 - resolution: "@subwallet/chain-list@npm:0.2.95" +"@subwallet/chain-list@npm:0.2.96-beta.8": + version: 0.2.96-beta.8 + resolution: "@subwallet/chain-list@npm:0.2.96-beta.8" dependencies: "@polkadot/dev": 0.67.167 "@polkadot/util": ^12.5.1 eventemitter3: ^5.0.1 - checksum: 2bfc7427734e3f15b6695558086ae48fe907cb2613594dc15099f9e254c2b220d8ac783bafd424d7a5afc00ae8fff6bb3433892721a0bdb8bad2793a3232da0d + ts-md5: ^1.3.1 + checksum: 45f78eb53c370e714eb014fb655ec85ccfff36cb79de68440c98ce6d53212aeb380351e4f77d4e7a5bdee8df33beff3164227ea24ffa0e0c54adf06e3f25deee languageName: node linkType: hard @@ -6315,7 +6316,7 @@ __metadata: "@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/chain-list": 0.2.96-beta.8 "@subwallet/extension-base": ^1.3.7-0 "@subwallet/extension-chains": ^1.3.7-0 "@subwallet/extension-dapp": ^1.3.7-0 @@ -6594,7 +6595,7 @@ __metadata: "@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/chain-list": 0.2.96-beta.8 "@subwallet/extension-base": ^1.3.7-0 "@subwallet/extension-chains": ^1.3.7-0 "@subwallet/extension-dapp": ^1.3.7-0 From bd0383c5c5c7408953ab59e46c72c11904f1209c Mon Sep 17 00:00:00 2001 From: bluezdot <72647326+bluezdot@users.noreply.github.com> Date: Wed, 11 Dec 2024 19:02:07 +0700 Subject: [PATCH 04/41] [Issue-3911] feat: change avail bridge max transferable --- .../src/koni/background/handlers/Extension.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 7a33f1698c..0a6ffa73a8 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -3,6 +3,7 @@ import { Common } from '@ethereumjs/common'; import { LegacyTransaction } from '@ethereumjs/tx'; +import { COMMON_CHAIN_SLUGS } from '@subwallet/chain-list'; import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _MultiChainAsset } from '@subwallet/chain-list/types'; import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError'; import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers'; @@ -36,8 +37,8 @@ import { getClaimTxOnAvail, getClaimTxOnEthereum, isAvailChainBridge } from '@su import { _isPolygonChainBridge, getClaimPolygonBridge, isClaimedPolygonBridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/polygonBridge'; 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, _getTokenOnChainAssetId, _getXcmAssetMultilocation, _isAssetSmartContractNft, _isBridgedToken, _isChainEvmCompatible, _isChainSubstrateCompatible, _isChainTonCompatible, _isCustomAsset, _isLocalToken, _isMantaZkAsset, _isNativeToken, _isPureEvmChain, _isTokenEvmSmartContract, _isTokenTransferredByEvm, _isTokenTransferredByTon } from '@subwallet/extension-base/services/chain-service/utils'; -import { _NotificationInfo, NotificationSetup } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; +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 { 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'; @@ -53,7 +54,7 @@ import { RequestClaimBridge } from '@subwallet/extension-base/types/bridge'; import { GetNotificationParams, RequestIsClaimedPolygonBridge, RequestSwitchStatusParams } from '@subwallet/extension-base/types/notification'; import { CommonOptimalPath } from '@subwallet/extension-base/types/service-base'; import { SwapPair, SwapQuoteResponse, SwapRequest, SwapRequestResult, SwapSubmitParams, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap'; -import { _analyzeAddress, BN_ZERO, combineAllAccountProxy, createTransactionFromRLP, isSameAddress, MODULE_SUPPORT, reformatAddress, signatureToHex, Transaction as QrTransaction, transformAccounts, transformAddresses, uniqueStringArray } from '@subwallet/extension-base/utils'; +import { _analyzeAddress, BN_ZERO, combineAllAccountProxy, createTransactionFromRLP, isSameAddress, MODULE_SUPPORT, reformatAddress, signatureToHex, toBNString, Transaction as QrTransaction, transformAccounts, transformAddresses, uniqueStringArray } from '@subwallet/extension-base/utils'; import { parseContractInput, parseEvmRlp } from '@subwallet/extension-base/utils/eth/parseTransaction'; import { metadataExpand } from '@subwallet/extension-chains'; import { MetadataDef } from '@subwallet/extension-inject/types'; @@ -1781,6 +1782,10 @@ export default class KoniExtension { const destinationTokenInfo = this.#koniState.getXcmEqualAssetByChain(destChain, originTokenInfo.slug); const existentialDeposit = originTokenInfo.minAmount || '0'; + // todo: improve this case. Currently set 1 AVAIL for covering fee as default. + const isSpecialBridgeFromAvail = originTokenInfo.slug === 'avail_mainnet-NATIVE-AVAIL' && destChain === COMMON_CHAIN_SLUGS.ETHEREUM; + const specialBridgeFromAvailFee = new BigN(toBNString(1, _getAssetDecimals(originTokenInfo))).minus(new BigN(_getTokenMinAmount(originTokenInfo))); + if (destinationTokenInfo) { const [bnMockExecutionFee, { value }] = await Promise.all([ getXcmMockTxFee(substrateApi, chainInfoMap, originTokenInfo, destinationTokenInfo), @@ -1788,7 +1793,7 @@ export default class KoniExtension { ]); const bnMaxTransferable = new BigN(value); - const estimatedFee = bnMockExecutionFee.multipliedBy(XCM_FEE_RATIO).plus(new BigN(existentialDeposit)); + const estimatedFee = isSpecialBridgeFromAvail ? specialBridgeFromAvailFee : bnMockExecutionFee.multipliedBy(XCM_FEE_RATIO).plus(new BigN(existentialDeposit)); return bnMaxTransferable.minus(estimatedFee); } From 14aa9e9329104195e3038b89349a685afdc0c765 Mon Sep 17 00:00:00 2001 From: S2kael Date: Thu, 12 Dec 2024 12:17:45 +0700 Subject: [PATCH 05/41] [Issue-3915] [Ledger] Hotfix to add `Avail Recovery` app --- .../src/background/KoniTypes.ts | 2 ++ .../src/Popup/Account/ConnectLedger.tsx | 4 +++- .../extension-koni-ui/src/constants/ledger.ts | 22 +++++++++++++++---- .../src/utils/connector/Ledger/index.ts | 2 +- patches/@polkadot+hw-ledger+12.6.2.patch | 7 +++--- patches/@zondax+ledger-substrate+0.44.2.patch | 16 ++++++++++++++ 6 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 patches/@zondax+ledger-substrate+0.44.2.patch diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index ff96b76aeb..ae7f1508a9 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1264,6 +1264,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-koni-ui/src/Popup/Account/ConnectLedger.tsx b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx index c303b72158..6a0e146228 100644 --- a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx +++ b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx @@ -57,7 +57,9 @@ const Component: React.FC = (props: Props) => { const networks = useMemo((): ChainItemType[] => supportedLedger .filter(({ isHide }) => !isHide) .map((network) => ({ - name: !network.isGeneric ? network.networkName.replace(' network', '') : network.networkName, + name: !network.isGeneric + ? network.networkName.replace(' network', '').concat(network.isRecovery ? ' Recovery' : '') + : network.networkName, slug: network.slug })), [supportedLedger]); diff --git a/packages/extension-koni-ui/src/constants/ledger.ts b/packages/extension-koni-ui/src/constants/ledger.ts index 4a1d3635d3..e9768862a9 100644 --- a/packages/extension-koni-ui/src/constants/ledger.ts +++ b/packages/extension-koni-ui/src/constants/ledger.ts @@ -105,18 +105,32 @@ export const PredefinedLedgerNetwork: LedgerNetwork[] = [ slip44: 434, isHide: true }, + // { + // accountName: 'Avail', + // appName: 'Avail', + // networkName: 'Avail network', + // genesisHash: ChainInfoMap.avail_mainnet.substrateInfo?.genesisHash || '0xb91746b45e0346cc2f815a520b9c6cb4d5c0902af848db0a80f85932d2e8276a', + // icon: 'substrate', + // network: 'avail', + // slug: ChainInfoMap.avail_mainnet.slug, + // isDevMode: false, + // isGeneric: false, + // isEthereum: false, + // slip44: 709 + // }, { - accountName: 'Avail', - appName: 'Avail', + accountName: 'Avail Recovery', + appName: 'Avail Recovery', networkName: 'Avail network', genesisHash: ChainInfoMap.avail_mainnet.substrateInfo?.genesisHash || '0xb91746b45e0346cc2f815a520b9c6cb4d5c0902af848db0a80f85932d2e8276a', icon: 'substrate', - network: 'avail', + network: 'availRecovery', slug: ChainInfoMap.avail_mainnet.slug, isDevMode: false, isGeneric: false, isEthereum: false, - slip44: 709 + isRecovery: true, + slip44: 354 }, { accountName: 'Acala', diff --git a/packages/extension-koni-ui/src/utils/connector/Ledger/index.ts b/packages/extension-koni-ui/src/utils/connector/Ledger/index.ts index a88dc0a337..b1b8d7e188 100644 --- a/packages/extension-koni-ui/src/utils/connector/Ledger/index.ts +++ b/packages/extension-koni-ui/src/utils/connector/Ledger/index.ts @@ -47,7 +47,7 @@ export const convertLedgerError = (err: Error, t: TFunction, network: string, is } if (message.includes('Data is invalid')) { - if (isGetAddress) { + if (!isGetAddress) { return { status: 'error', message: t('Unable to sign. Open “{{network}}” on Ledger, refresh and approve again', { replace: { network: network } }) diff --git a/patches/@polkadot+hw-ledger+12.6.2.patch b/patches/@polkadot+hw-ledger+12.6.2.patch index 9dbdf99e5c..6bd660f208 100644 --- a/patches/@polkadot+hw-ledger+12.6.2.patch +++ b/patches/@polkadot+hw-ledger+12.6.2.patch @@ -1,12 +1,13 @@ diff --git a/node_modules/@polkadot/hw-ledger/defaults.js b/node_modules/@polkadot/hw-ledger/defaults.js -index 9f06432..97efba8 100644 +index 9f06432..4daa29a 100644 --- a/node_modules/@polkadot/hw-ledger/defaults.js +++ b/node_modules/@polkadot/hw-ledger/defaults.js -@@ -37,5 +37,6 @@ export const ledgerApps = { +@@ -37,5 +37,7 @@ export const ledgerApps = { unique: 'Unique', vtb: 'VTB', xxnetwork: 'XXNetwork', - zeitgeist: 'Zeitgeist' + zeitgeist: 'Zeitgeist', -+ avail: 'Avail' ++ avail: 'Avail', ++ availRecovery: 'AvailRecovery', }; diff --git a/patches/@zondax+ledger-substrate+0.44.2.patch b/patches/@zondax+ledger-substrate+0.44.2.patch new file mode 100644 index 0000000000..834a01fe07 --- /dev/null +++ b/patches/@zondax+ledger-substrate+0.44.2.patch @@ -0,0 +1,16 @@ +diff --git a/node_modules/@zondax/ledger-substrate/dist/supported_apps.js b/node_modules/@zondax/ledger-substrate/dist/supported_apps.js +index 13b6e54..203d4eb 100644 +--- a/node_modules/@zondax/ledger-substrate/dist/supported_apps.js ++++ b/node_modules/@zondax/ledger-substrate/dist/supported_apps.js +@@ -284,5 +284,11 @@ exports.supportedApps = [ + slip0044: 0x800002c5, + ss58_addr_type: 42, + }, ++ { ++ name: 'AvailRecovery', ++ cla: 0xbc, ++ slip0044: 0x80000162, ++ ss58_addr_type: 42, ++ }, + ]; + //# sourceMappingURL=supported_apps.js.map From 33a383310d10ffe9ab77a81d6bba97644e4e2ef3 Mon Sep 17 00:00:00 2001 From: S2kael Date: Wed, 18 Dec 2024 20:06:42 +0700 Subject: [PATCH 06/41] [Issue-3915] [Ledger] Hotfix to connect with Avail Recovery --- package.json | 2 +- patches/@zondax+ledger-substrate+0.44.2.patch | 16 ---------- yarn.lock | 30 +++++++++---------- 3 files changed, 16 insertions(+), 32 deletions(-) delete mode 100644 patches/@zondax+ledger-substrate+0.44.2.patch diff --git a/package.json b/package.json index ee1306914c..fa582794f9 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "@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/patches/@zondax+ledger-substrate+0.44.2.patch b/patches/@zondax+ledger-substrate+0.44.2.patch deleted file mode 100644 index 834a01fe07..0000000000 --- a/patches/@zondax+ledger-substrate+0.44.2.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/node_modules/@zondax/ledger-substrate/dist/supported_apps.js b/node_modules/@zondax/ledger-substrate/dist/supported_apps.js -index 13b6e54..203d4eb 100644 ---- a/node_modules/@zondax/ledger-substrate/dist/supported_apps.js -+++ b/node_modules/@zondax/ledger-substrate/dist/supported_apps.js -@@ -284,5 +284,11 @@ exports.supportedApps = [ - slip0044: 0x800002c5, - ss58_addr_type: 42, - }, -+ { -+ name: 'AvailRecovery', -+ cla: 0xbc, -+ slip0044: 0x80000162, -+ ss58_addr_type: 42, -+ }, - ]; - //# sourceMappingURL=supported_apps.js.map diff --git a/yarn.lock b/yarn.lock index a8e0f8cd0c..402b38b4f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9243,23 +9243,23 @@ __metadata: languageName: node linkType: hard -"@zondax/ledger-js@npm:^0.8.2": - version: 0.8.2 - resolution: "@zondax/ledger-js@npm:0.8.2" +"@zondax/ledger-js@npm:^0.11.0": + version: 0.11.0 + resolution: "@zondax/ledger-js@npm:0.11.0" dependencies: "@ledgerhq/hw-transport": 6.30.6 - checksum: 7b33cd87d8569732028a57e594a4674f9fa232155c227031a00985bf9b6af62774a5cdbed59a5754c998122862ded64060b042c97850a38e6b734455828b362d + checksum: 38743b887fceaeb13460d70a127cb385f5df034fe014f698d4f164f0d111add71dd01381c182ac71135c7fb43f38e7b21218d5df7d87d69ddaee17dd9c57d392 languageName: node linkType: hard -"@zondax/ledger-substrate@npm:0.44.2": - version: 0.44.2 - resolution: "@zondax/ledger-substrate@npm:0.44.2" +"@zondax/ledger-substrate@npm:1.0.1": + version: 1.0.1 + resolution: "@zondax/ledger-substrate@npm:1.0.1" dependencies: - "@ledgerhq/hw-transport": 6.30.6 - "@zondax/ledger-js": ^0.8.2 - axios: ^1.6.8 - checksum: 1f0035725c88335c8e1c234fc75ebdbc39666d8567bb3d77734ed861f85a49b87d358f520bef76287ff2e75914f790a5454cb27b7814489292100730023a9541 + "@ledgerhq/hw-transport": 6.31.2 + "@zondax/ledger-js": ^0.11.0 + axios: ^1.7.4 + checksum: e5fd0743bb6a5638c7a633131a0f4db84e9e8eec720fa659f6f116a8fe6629125b6d472184683c1a64a88fadf89bb54c6e3ffa4fa063470b4395df87d7be0bb0 languageName: node linkType: hard @@ -10133,14 +10133,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.6.8": - version: 1.6.8 - resolution: "axios@npm:1.6.8" +"axios@npm:^1.7.4": + version: 1.7.9 + resolution: "axios@npm:1.7.9" dependencies: follow-redirects: ^1.15.6 form-data: ^4.0.0 proxy-from-env: ^1.1.0 - checksum: bf007fa4b207d102459300698620b3b0873503c6d47bf5a8f6e43c0c64c90035a4f698b55027ca1958f61ab43723df2781c38a99711848d232cad7accbcdfcdd + checksum: cb8ce291818effda09240cb60f114d5625909b345e10f389a945320e06acf0bc949d0f8422d25720f5dd421362abee302c99f5e97edec4c156c8939814b23d19 languageName: node linkType: hard From 5896c97f3fb7fee6430b3e6153ffb56e1fdde678 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Thu, 19 Dec 2024 09:40:19 +0700 Subject: [PATCH 07/41] [Update] Change priority to get registry to signing --- .../src/koni/background/handlers/Extension.ts | 92 +++++-------------- .../src/koni/background/utils.ts | 69 ++++++++++++++ .../src/services/chain-service/types.ts | 2 + packages/extension-base/src/utils/metadata.ts | 31 +++++-- 4 files changed, 118 insertions(+), 76 deletions(-) create mode 100644 packages/extension-base/src/koni/background/utils.ts diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 7a33f1698c..833e0602e4 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -55,7 +55,6 @@ import { CommonOptimalPath } from '@subwallet/extension-base/types/service-base' import { SwapPair, SwapQuoteResponse, SwapRequest, SwapRequestResult, SwapSubmitParams, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap'; import { _analyzeAddress, BN_ZERO, combineAllAccountProxy, createTransactionFromRLP, isSameAddress, MODULE_SUPPORT, reformatAddress, signatureToHex, Transaction as QrTransaction, transformAccounts, transformAddresses, uniqueStringArray } from '@subwallet/extension-base/utils'; import { parseContractInput, parseEvmRlp } from '@subwallet/extension-base/utils/eth/parseTransaction'; -import { metadataExpand } from '@subwallet/extension-chains'; import { MetadataDef } from '@subwallet/extension-inject/types'; import { getKeypairTypeByAddress, isAddress, isSubstrateAddress, isTonAddress } from '@subwallet/keyring'; import { EthereumKeypairTypes, SubstrateKeypairTypes, TonKeypairTypes } from '@subwallet/keyring/types'; @@ -71,12 +70,13 @@ import { combineLatest, Subject } from 'rxjs'; import { TransactionConfig } from 'web3-core'; import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { Metadata, TypeRegistry } from '@polkadot/types'; -import { ChainProperties } from '@polkadot/types/interfaces'; +import { TypeRegistry } from '@polkadot/types'; import { AnyJson, Registry, SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; import { assert, hexStripPrefix, hexToU8a, isAscii, isHex, u8aToHex } from '@polkadot/util'; import { decodeAddress, isEthereumAddress } from '@polkadot/util-crypto'; +import { getSuitableMetadata, MetadataSource, MetadataWithSource, setupDappRegistry, setupDatabaseRegistry } from '../utils'; + export function isJsonPayload (value: SignerPayloadJSON | SignerPayloadRaw): value is SignerPayloadJSON { return (value as SignerPayloadJSON).genesisHash !== undefined; } @@ -2516,7 +2516,7 @@ export default class KoniExtension { } } - /// Signing substrate request + // Signing substrate request private async signingApprovePasswordV2 ({ id }: RequestSigningApprovePasswordV2): Promise { const queued = this.#koniState.getSignRequest(id); @@ -2527,7 +2527,7 @@ export default class KoniExtension { // unlike queued.account.address the following // address is encoded with the default prefix - // which what is used for password caching mapping + // which is used for password caching mapping const { address } = pair; if (!pair) { @@ -2542,77 +2542,33 @@ export default class KoniExtension { const { payload } = request; - let registry: Registry; + let registry: Registry = new TypeRegistry(); if (isJsonPayload(payload)) { const [, chainInfo] = this.#koniState.findNetworkKeyByGenesisHash(payload.genesisHash); - let metadata: MetadataDef | MetadataItem | undefined; - - /** - * Get the metadata for the genesisHash - * @todo: need to handle case metadata store in db - */ - metadata = this.#koniState.knownMetadata.find((meta: MetadataDef) => - meta.genesisHash === payload.genesisHash); + const [apiMetadata, dbMetadata, dappMetadata] = await Promise.all([ + { metadata: this.#koniState.getSubstrateApi(chainInfo?.slug ?? '').metadata, source: MetadataSource.API }, + { metadata: await this.#koniState.chainService.getMetadataByHash(payload.genesisHash), source: MetadataSource.DB }, + { metadata: this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash), source: MetadataSource.DAPP } + ]); - if (metadata) { - // we have metadata, expand it and extract the info/registry - const expanded = metadataExpand(metadata, false); + const allMetadata: MetadataWithSource[] = [ + { metadata: apiMetadata.metadata, source: apiMetadata.source }, + { metadata: dbMetadata.metadata, source: dbMetadata.source }, + { metadata: dappMetadata.metadata, source: dappMetadata.source } + ].filter((item) => item.metadata); - registry = expanded.registry; - registry.setSignedExtensions(payload.signedExtensions, expanded.definition.userExtensions); + if (allMetadata.length === 0) { + registry.setSignedExtensions(payload.signedExtensions); } else { - metadata = await this.#koniState.chainService.getMetadataByHash(payload.genesisHash); - - if (metadata) { - registry = new TypeRegistry(); - - const _metadata = new Metadata(registry, metadata.hexValue); + const { metadata, source } = getSuitableMetadata(allMetadata, parseInt(payload.specVersion)); - registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, - tokenDecimals: chainInfo?.substrateInfo?.decimals, - tokenSymbol: chainInfo?.substrateInfo?.symbol - }) as unknown as ChainProperties); - registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); - } else { - // we have no metadata, create a new registry - registry = new TypeRegistry(); - registry.setSignedExtensions(payload.signedExtensions); - } + registry = source === MetadataSource.API && chainInfo + ? this.#koniState.getSubstrateApi(chainInfo.slug).api.registry as unknown as TypeRegistry + : source === MetadataSource.DB && chainInfo + ? setupDatabaseRegistry(metadata as MetadataItem, chainInfo, payload) + : setupDappRegistry(metadata as MetadataDef, payload); } - - if (!metadata) { - /* - * Some networks must have metadata to signing, - * so if the chain not active (cannot use metadata from api), it must be rejected - * */ - if ( - chainInfo && - (_API_OPTIONS_CHAIN_GROUP.avail.includes(chainInfo.slug) || _API_OPTIONS_CHAIN_GROUP.goldberg.includes(chainInfo.slug)) // The special case for chains that need metadata to signing - ) { - // For case the chain does not have any provider - if (!Object.keys(chainInfo.providers).length) { - reject(new Error('{{chain}} network does not have any provider to connect, please update metadata from dApp'.replaceAll('{{chain}}', chainInfo.name))); - - return false; - } - - const isChainActive = this.#koniState.getChainStateByKey(chainInfo.slug).active; - - if (!isChainActive) { - reject(new Error('Please activate {{chain}} network before signing'.replaceAll('{{chain}}', chainInfo.name))); - - return false; - } - - registry = this.#koniState.getSubstrateApi(chainInfo.slug).api.registry as unknown as TypeRegistry; - } - } - } else { - // for non-payload, just create a registry to use - registry = new TypeRegistry(); } const result = request.sign(registry as unknown as TypeRegistry, pair); diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts new file mode 100644 index 0000000000..0df2d98858 --- /dev/null +++ b/packages/extension-base/src/koni/background/utils.ts @@ -0,0 +1,69 @@ +// Copyright 2019-2022 @subwallet/extension-koni authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { _ChainInfo } from '@subwallet/chain-list/types'; +import { MetadataItem } from '@subwallet/extension-base/background/KoniTypes'; +import { metadataExpand } from '@subwallet/extension-chains/bundle'; +import { MetadataDef } from '@subwallet/extension-inject/types'; + +import { Metadata, TypeRegistry } from '@polkadot/types'; +import { ChainProperties } from '@polkadot/types/interfaces'; +import { SignerPayloadJSON } from '@polkadot/types/types'; + +export enum MetadataSource { + API = 'api', + DB = 'db', + DAPP = 'dapp' +} + +export interface MetadataWithSource{ + metadata: MetadataDef | MetadataItem | undefined; + source: MetadataSource; +} + +export function getSuitableMetadata (metadatas: MetadataWithSource[], payloadSpecVersion: number) { + const sortedMetadatas = metadatas + .filter((meta): meta is MetadataWithSource => meta.metadata !== undefined) + .map((meta) => ({ + meta: meta.metadata, + source: meta.source, + distance: Math.abs(Number(meta.metadata?.specVersion) - payloadSpecVersion), + isHigher: Number(meta.metadata?.specVersion) >= payloadSpecVersion + })) + .sort((a, b) => { + if (a.distance !== b.distance) { + return a.distance - b.distance; + } + + return Number(b.meta?.specVersion) - Number(a.meta?.specVersion); + }); + + return { + metadata: sortedMetadatas[0].meta, + source: sortedMetadatas[0].source + }; +} + +export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo, payload: SignerPayloadJSON) { + const registry = new TypeRegistry(); + const _metadata = new Metadata(registry, metadata.hexValue); + + registry.register(metadata.types); + registry.setChainProperties(registry.createType('ChainProperties', { + ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, + tokenDecimals: chainInfo?.substrateInfo?.decimals, + tokenSymbol: chainInfo?.substrateInfo?.symbol + }) as unknown as ChainProperties); + registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); + + return registry; +} + +export function setupDappRegistry (metadata: MetadataDef, payload: SignerPayloadJSON) { + const expanded = metadataExpand(metadata, false); + const registry = expanded.registry; + + registry.setSignedExtensions(payload.signedExtensions, expanded.definition.userExtensions); + + return registry; +} diff --git a/packages/extension-base/src/services/chain-service/types.ts b/packages/extension-base/src/services/chain-service/types.ts index 5d0b84b84d..6a975af9c6 100644 --- a/packages/extension-base/src/services/chain-service/types.ts +++ b/packages/extension-base/src/services/chain-service/types.ts @@ -6,6 +6,7 @@ import type { ApiInterfaceRx } from '@polkadot/api/types'; import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _CrowdloanFund } from '@subwallet/chain-list/types'; +import { MetadataItem } from '@subwallet/extension-base/background/KoniTypes'; import { AccountState, TxByMsgResponse } from '@subwallet/extension-base/services/balance-service/helpers/subscribe/ton/types'; import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chain-service/handler/types'; import { TonWalletContract } from '@subwallet/keyring/types'; @@ -94,6 +95,7 @@ export interface _SubstrateApiState { } export interface _SubstrateApi extends _SubstrateApiState, _ChainBaseApi, _SubstrateApiAdapter { + metadata?: MetadataItem; api: ApiPromise; isReady: Promise<_SubstrateApi>; connect: (_callbackUpdateMetadata?: (substrateApi: _SubstrateApi) => void) => void; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 41c6c47c2d..6dfd1eadf9 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -4,6 +4,7 @@ import { ChainService } from '@subwallet/extension-base/services/chain-service'; import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types'; +import { ApiPromise } from '@polkadot/api'; import { getSpecExtensions, getSpecTypes } from '@polkadot/types-known'; import { u8aToHex } from '@polkadot/util'; import { HexString } from '@polkadot/util/types'; @@ -25,6 +26,22 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; +const getMetadataV15 = async (api: ApiPromise): Promise => { + try { + if (api.call.metadata.metadataAtVersion) { + const metadataV15 = await api.call.metadata.metadataAtVersion(15); + + if (!metadataV15.isEmpty) { + return metadataV15.unwrap().toHex(); + } + } + } catch (err) { + console.error('Error fetching metadata V15:', err); + } + + return undefined; +}; + export const cacheMetadata = ( chain: string, substrateApi: _SubstrateApi, @@ -42,23 +59,21 @@ export const cacheMetadata = ( return; } - const systemChain = await api.rpc.system.chain(); + const systemChain = api.runtimeChain; // const _metadata: Option = await api.call.metadata.metadataAtVersion(15); // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object - let hexV15: HexString | undefined; - - const metadataV15 = await api.call.metadata.metadataAtVersion(15); - if (!metadataV15.isEmpty) { - hexV15 = metadataV15.unwrap().toHex(); - } + const [metadataHex, hexV15] = await Promise.all([ + Promise.resolve(api.runtimeMetadata.toHex()), + getMetadataV15(api) + ]); chainService?.upsertMetadata(chain, { chain: chain, genesisHash: genesisHash, specName: specName, specVersion: currentSpecVersion, - hexValue: api.runtimeMetadata.toHex(), + hexValue: metadataHex, types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record, userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), hexV15 From ccb80e45593730f88a44e33af38b2b14866182a6 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Mon, 23 Dec 2024 11:11:32 +0700 Subject: [PATCH 08/41] [Update] Refactor priority logic --- .../src/koni/background/handlers/Extension.ts | 14 +--- .../src/koni/background/utils.ts | 73 ++++++++++++++++--- packages/extension-base/src/utils/metadata.ts | 43 ++++++----- 3 files changed, 88 insertions(+), 42 deletions(-) diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 833e0602e4..39d47ee680 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -75,7 +75,7 @@ import { AnyJson, Registry, SignerPayloadJSON, SignerPayloadRaw } from '@polkado import { assert, hexStripPrefix, hexToU8a, isAscii, isHex, u8aToHex } from '@polkadot/util'; import { decodeAddress, isEthereumAddress } from '@polkadot/util-crypto'; -import { getSuitableMetadata, MetadataSource, MetadataWithSource, setupDappRegistry, setupDatabaseRegistry } from '../utils'; +import { getSuitableRegistry, MetadataSource, MetadataWithSource } from '../utils'; export function isJsonPayload (value: SignerPayloadJSON | SignerPayloadRaw): value is SignerPayloadJSON { return (value as SignerPayloadJSON).genesisHash !== undefined; @@ -2546,14 +2546,12 @@ export default class KoniExtension { if (isJsonPayload(payload)) { const [, chainInfo] = this.#koniState.findNetworkKeyByGenesisHash(payload.genesisHash); - const [apiMetadata, dbMetadata, dappMetadata] = await Promise.all([ - { metadata: this.#koniState.getSubstrateApi(chainInfo?.slug ?? '').metadata, source: MetadataSource.API }, + const [dbMetadata, dappMetadata] = await Promise.all([ { metadata: await this.#koniState.chainService.getMetadataByHash(payload.genesisHash), source: MetadataSource.DB }, { metadata: this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash), source: MetadataSource.DAPP } ]); const allMetadata: MetadataWithSource[] = [ - { metadata: apiMetadata.metadata, source: apiMetadata.source }, { metadata: dbMetadata.metadata, source: dbMetadata.source }, { metadata: dappMetadata.metadata, source: dappMetadata.source } ].filter((item) => item.metadata); @@ -2561,13 +2559,7 @@ export default class KoniExtension { if (allMetadata.length === 0) { registry.setSignedExtensions(payload.signedExtensions); } else { - const { metadata, source } = getSuitableMetadata(allMetadata, parseInt(payload.specVersion)); - - registry = source === MetadataSource.API && chainInfo - ? this.#koniState.getSubstrateApi(chainInfo.slug).api.registry as unknown as TypeRegistry - : source === MetadataSource.DB && chainInfo - ? setupDatabaseRegistry(metadata as MetadataItem, chainInfo, payload) - : setupDappRegistry(metadata as MetadataDef, payload); + registry = getSuitableRegistry(allMetadata, payload, chainInfo, this.#koniState); } } diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index 0df2d98858..bd5f447763 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -8,7 +8,9 @@ import { MetadataDef } from '@subwallet/extension-inject/types'; import { Metadata, TypeRegistry } from '@polkadot/types'; import { ChainProperties } from '@polkadot/types/interfaces'; -import { SignerPayloadJSON } from '@polkadot/types/types'; +import { Registry, SignerPayloadJSON } from '@polkadot/types/types'; + +import KoniState from './handlers/State'; export enum MetadataSource { API = 'api', @@ -21,8 +23,49 @@ export interface MetadataWithSource{ source: MetadataSource; } -export function getSuitableMetadata (metadatas: MetadataWithSource[], payloadSpecVersion: number) { - const sortedMetadatas = metadatas +export interface CachedChainProperties { + ss58Format: number; + tokenDecimals: number | undefined; + tokenSymbol: string | undefined; +} + +const cachedChainProperties: Map> = new Map(); + +function getChainProperties (chainInfo: _ChainInfo, genesisHash: string): Promise { + const cachedPromise = cachedChainProperties.get(genesisHash); + + if (cachedPromise) { + return cachedPromise; + } + + const chainPropertiesPromise = new Promise((resolve) => { + const chainProperties: CachedChainProperties = { + ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, + tokenDecimals: chainInfo?.substrateInfo?.decimals, + tokenSymbol: chainInfo?.substrateInfo?.symbol + }; + + cachedChainProperties.set(genesisHash, Promise.resolve(chainProperties)); + resolve(chainProperties); + }); + + cachedChainProperties.set(genesisHash, chainPropertiesPromise); + + return chainPropertiesPromise; +} + +export function getSuitableRegistry (metadatas: MetadataWithSource[], payload: SignerPayloadJSON, chainInfo: _ChainInfo | undefined, koniState: KoniState) { + const api = chainInfo ? koniState.getSubstrateApi(chainInfo.slug).api : undefined; + const apiSpecVersion = api?.runtimeVersion.specVersion.toString(); + const payloadSpecVersion = parseInt(payload.specVersion); + const extendedMetadatas = [{ + metadata: { specVersion: apiSpecVersion }, + source: MetadataSource.API + }, + ...metadatas + ]; + + const sortedMetadatas = extendedMetadatas .filter((meta): meta is MetadataWithSource => meta.metadata !== undefined) .map((meta) => ({ meta: meta.metadata, @@ -38,22 +81,28 @@ export function getSuitableMetadata (metadatas: MetadataWithSource[], payloadSpe return Number(b.meta?.specVersion) - Number(a.meta?.specVersion); }); - return { - metadata: sortedMetadatas[0].meta, - source: sortedMetadatas[0].source - }; + const closestMetadata = sortedMetadatas[0]; + let registry: Registry; + + if (closestMetadata.source === MetadataSource.API) { + registry = api?.registry as unknown as TypeRegistry; + } else if (closestMetadata.source === MetadataSource.DB && chainInfo) { + registry = setupDatabaseRegistry(closestMetadata.meta as MetadataItem, chainInfo, payload); + } else { + registry = setupDappRegistry(closestMetadata.meta as MetadataDef, payload); + } + + return registry; } export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo, payload: SignerPayloadJSON) { const registry = new TypeRegistry(); const _metadata = new Metadata(registry, metadata.hexValue); + const chainProperties = getChainProperties(chainInfo, payload.genesisHash); + registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, - tokenDecimals: chainInfo?.substrateInfo?.decimals, - tokenSymbol: chainInfo?.substrateInfo?.symbol - }) as unknown as ChainProperties); + registry.setChainProperties(registry.createType('ChainProperties', chainProperties) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); return registry; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 6dfd1eadf9..e0df7b96d8 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -26,20 +26,27 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; -const getMetadataV15 = async (api: ApiPromise): Promise => { - try { - if (api.call.metadata.metadataAtVersion) { - const metadataV15 = await api.call.metadata.metadataAtVersion(15); +const storeMetadataV15: Map = new Map(); +const getMetadataV15 = (api: ApiPromise) => { + const genesisHash = api.genesisHash.toHex(); + + api.call.metadata.metadataAtVersion(15) + .then((metadataV15) => { if (!metadataV15.isEmpty) { - return metadataV15.unwrap().toHex(); + const hexValue = metadataV15.unwrap().toHex(); + + storeMetadataV15.set(genesisHash, hexValue); + } else { + storeMetadataV15.set(genesisHash, undefined); } - } - } catch (err) { - console.error('Error fetching metadata V15:', err); - } + }) + .catch((err) => { + console.error('Error:', err); + storeMetadataV15.set(genesisHash, undefined); + }); - return undefined; + return storeMetadataV15.get(genesisHash); }; export const cacheMetadata = ( @@ -62,21 +69,19 @@ export const cacheMetadata = ( const systemChain = api.runtimeChain; // const _metadata: Option = await api.call.metadata.metadataAtVersion(15); // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object + const metadataHex = api.runtimeMetadata.toHex(); + const metadataV15 = getMetadataV15(api); - const [metadataHex, hexV15] = await Promise.all([ - Promise.resolve(api.runtimeMetadata.toHex()), - getMetadataV15(api) - ]); - - chainService?.upsertMetadata(chain, { + const updateMetadata = { chain: chain, genesisHash: genesisHash, specName: specName, specVersion: currentSpecVersion, hexValue: metadataHex, types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record, - userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), - hexV15 - }).catch(console.error); + userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName) + }; + + chainService?.upsertMetadata(chain, { ...updateMetadata, hexV15: metadataV15 }).catch(console.error); }).catch(console.error); }; From 5cbb5b9d2d69799c74f57fbee154ef571af8cfbe Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Mon, 23 Dec 2024 16:16:22 +0700 Subject: [PATCH 09/41] [Update] Refactor priority logic v2 --- .../src/background/KoniTypes.ts | 3 + .../src/koni/background/handlers/Extension.ts | 28 ++-- .../src/koni/background/utils.ts | 132 ++++++++---------- .../src/services/chain-service/types.ts | 2 - packages/extension-base/src/utils/metadata.ts | 74 +++++----- 5 files changed, 117 insertions(+), 122 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index ff96b76aeb..b297bc6c8a 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -277,6 +277,9 @@ export interface MetadataItem { types: Record | string>; userExtensions?: ExtDef; hexV15?: HexString; + ss58Format?: number; + tokenDecimals?: number; + tokenSymbol?: string; } export interface CrowdloanItem { diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 39d47ee680..00a9267a25 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -75,7 +75,7 @@ import { AnyJson, Registry, SignerPayloadJSON, SignerPayloadRaw } from '@polkado import { assert, hexStripPrefix, hexToU8a, isAscii, isHex, u8aToHex } from '@polkadot/util'; import { decodeAddress, isEthereumAddress } from '@polkadot/util-crypto'; -import { getSuitableRegistry, MetadataSource, MetadataWithSource } from '../utils'; +import { getSuitableRegistry, RegistrySource, setupApiRegistry, setupDappRegistry, setupDatabaseRegistry } from '../utils'; export function isJsonPayload (value: SignerPayloadJSON | SignerPayloadRaw): value is SignerPayloadJSON { return (value as SignerPayloadJSON).genesisHash !== undefined; @@ -2546,20 +2546,24 @@ export default class KoniExtension { if (isJsonPayload(payload)) { const [, chainInfo] = this.#koniState.findNetworkKeyByGenesisHash(payload.genesisHash); - const [dbMetadata, dappMetadata] = await Promise.all([ - { metadata: await this.#koniState.chainService.getMetadataByHash(payload.genesisHash), source: MetadataSource.DB }, - { metadata: this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash), source: MetadataSource.DAPP } - ]); - - const allMetadata: MetadataWithSource[] = [ - { metadata: dbMetadata.metadata, source: dbMetadata.source }, - { metadata: dappMetadata.metadata, source: dappMetadata.source } - ].filter((item) => item.metadata); - if (allMetadata.length === 0) { + const allRegistry: RegistrySource[] = [ + setupApiRegistry(chainInfo, this.#koniState), + setupDatabaseRegistry( + await this.#koniState.chainService.getMetadataByHash(payload.genesisHash) as MetadataItem, + chainInfo, + payload + ), + setupDappRegistry( + this.#koniState.knownMetadata.find((meta: MetadataDef) => meta.genesisHash === payload.genesisHash) as MetadataDef, + payload + ) + ].filter((item): item is RegistrySource => item !== null && item.registry !== undefined); + + if (allRegistry.length === 0) { registry.setSignedExtensions(payload.signedExtensions); } else { - registry = getSuitableRegistry(allMetadata, payload, chainInfo, this.#koniState); + registry = getSuitableRegistry(allRegistry, payload).metadata; } } diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index bd5f447763..e4ca8a42ba 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -12,107 +12,89 @@ import { Registry, SignerPayloadJSON } from '@polkadot/types/types'; import KoniState from './handlers/State'; -export enum MetadataSource { - API = 'api', - DB = 'db', - DAPP = 'dapp' +export interface RegistrySource{ + registry: Registry, + specVersion: string | number, } -export interface MetadataWithSource{ - metadata: MetadataDef | MetadataItem | undefined; - source: MetadataSource; -} - -export interface CachedChainProperties { - ss58Format: number; - tokenDecimals: number | undefined; - tokenSymbol: string | undefined; -} - -const cachedChainProperties: Map> = new Map(); - -function getChainProperties (chainInfo: _ChainInfo, genesisHash: string): Promise { - const cachedPromise = cachedChainProperties.get(genesisHash); - - if (cachedPromise) { - return cachedPromise; - } - - const chainPropertiesPromise = new Promise((resolve) => { - const chainProperties: CachedChainProperties = { - ss58Format: chainInfo?.substrateInfo?.addressPrefix ?? 42, - tokenDecimals: chainInfo?.substrateInfo?.decimals, - tokenSymbol: chainInfo?.substrateInfo?.symbol - }; - - cachedChainProperties.set(genesisHash, Promise.resolve(chainProperties)); - resolve(chainProperties); - }); - - cachedChainProperties.set(genesisHash, chainPropertiesPromise); - - return chainPropertiesPromise; -} - -export function getSuitableRegistry (metadatas: MetadataWithSource[], payload: SignerPayloadJSON, chainInfo: _ChainInfo | undefined, koniState: KoniState) { - const api = chainInfo ? koniState.getSubstrateApi(chainInfo.slug).api : undefined; - const apiSpecVersion = api?.runtimeVersion.specVersion.toString(); +export function getSuitableRegistry (registries: RegistrySource[], payload: SignerPayloadJSON) { const payloadSpecVersion = parseInt(payload.specVersion); - const extendedMetadatas = [{ - metadata: { specVersion: apiSpecVersion }, - source: MetadataSource.API - }, - ...metadatas - ]; - - const sortedMetadatas = extendedMetadatas - .filter((meta): meta is MetadataWithSource => meta.metadata !== undefined) - .map((meta) => ({ - meta: meta.metadata, - source: meta.source, - distance: Math.abs(Number(meta.metadata?.specVersion) - payloadSpecVersion), - isHigher: Number(meta.metadata?.specVersion) >= payloadSpecVersion - })) + const sortedRegistries = registries + .filter((registrySource): registrySource is RegistrySource => registrySource.registry !== undefined) + .map((registrySource) => { + const specVersion = Number(registrySource.specVersion); + const distance = Math.abs(specVersion - payloadSpecVersion); + const isHigher = specVersion >= payloadSpecVersion; + + return { + registry: registrySource.registry, + specVersion, + distance, + isHigher + }; + }) .sort((a, b) => { if (a.distance !== b.distance) { return a.distance - b.distance; } - return Number(b.meta?.specVersion) - Number(a.meta?.specVersion); + return a.specVersion - b.specVersion; }); - const closestMetadata = sortedMetadatas[0]; - let registry: Registry; + return { + metadata: sortedRegistries[0].registry + }; +} - if (closestMetadata.source === MetadataSource.API) { - registry = api?.registry as unknown as TypeRegistry; - } else if (closestMetadata.source === MetadataSource.DB && chainInfo) { - registry = setupDatabaseRegistry(closestMetadata.meta as MetadataItem, chainInfo, payload); - } else { - registry = setupDappRegistry(closestMetadata.meta as MetadataDef, payload); +export function setupApiRegistry (chainInfo: _ChainInfo | undefined, koniState: KoniState): RegistrySource | null { + if (!chainInfo) { + return null; } - return registry; + const api = koniState.getSubstrateApi(chainInfo.slug).api; + const apiSpecVersion = api?.runtimeVersion.specVersion.toString(); + const registry = api?.registry as unknown as TypeRegistry; + + return { + registry, + specVersion: apiSpecVersion + }; } -export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo, payload: SignerPayloadJSON) { +export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _ChainInfo | undefined, payload: SignerPayloadJSON): RegistrySource | null { + if (!metadata || !metadata.genesisHash || !chainInfo) { + return null; + } + const registry = new TypeRegistry(); const _metadata = new Metadata(registry, metadata.hexValue); - const chainProperties = getChainProperties(chainInfo, payload.genesisHash); - registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', chainProperties) as unknown as ChainProperties); + registry.setChainProperties(registry.createType('ChainProperties', { + ss58Format: metadata.ss58Format, + tokenDecimals: metadata.tokenDecimals, + tokenSymbol: metadata.tokenSymbol + }) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); - return registry; + return { + registry, + specVersion: metadata.specVersion + }; } -export function setupDappRegistry (metadata: MetadataDef, payload: SignerPayloadJSON) { +export function setupDappRegistry (metadata: MetadataDef, payload: SignerPayloadJSON): RegistrySource | null { + if (!metadata || !metadata.genesisHash) { + return null; + } + const expanded = metadataExpand(metadata, false); const registry = expanded.registry; registry.setSignedExtensions(payload.signedExtensions, expanded.definition.userExtensions); - return registry; + return { + registry, + specVersion: metadata.specVersion + }; } diff --git a/packages/extension-base/src/services/chain-service/types.ts b/packages/extension-base/src/services/chain-service/types.ts index 6a975af9c6..5d0b84b84d 100644 --- a/packages/extension-base/src/services/chain-service/types.ts +++ b/packages/extension-base/src/services/chain-service/types.ts @@ -6,7 +6,6 @@ import type { ApiInterfaceRx } from '@polkadot/api/types'; import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _CrowdloanFund } from '@subwallet/chain-list/types'; -import { MetadataItem } from '@subwallet/extension-base/background/KoniTypes'; import { AccountState, TxByMsgResponse } from '@subwallet/extension-base/services/balance-service/helpers/subscribe/ton/types'; import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chain-service/handler/types'; import { TonWalletContract } from '@subwallet/keyring/types'; @@ -95,7 +94,6 @@ export interface _SubstrateApiState { } export interface _SubstrateApi extends _SubstrateApiState, _ChainBaseApi, _SubstrateApiAdapter { - metadata?: MetadataItem; api: ApiPromise; isReady: Promise<_SubstrateApi>; connect: (_callbackUpdateMetadata?: (substrateApi: _SubstrateApi) => void) => void; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index e0df7b96d8..5e5db113bd 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -26,27 +26,51 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; -const storeMetadataV15: Map = new Map(); +const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise => { + try { + if (api.call.metadata.metadataAtVersion) { + const metadataV15 = await api.call.metadata.metadataAtVersion(15); -const getMetadataV15 = (api: ApiPromise) => { - const genesisHash = api.genesisHash.toHex(); - - api.call.metadata.metadataAtVersion(15) - .then((metadataV15) => { if (!metadataV15.isEmpty) { const hexValue = metadataV15.unwrap().toHex(); - storeMetadataV15.set(genesisHash, hexValue); - } else { - storeMetadataV15.set(genesisHash, undefined); + if (chainService) { + const metadata = await chainService.getMetadata(chain); + + if (metadata) { + await chainService.upsertMetadata(chain, { ...metadata, hexV15: hexValue }); + } + } } - }) - .catch((err) => { - console.error('Error:', err); - storeMetadataV15.set(genesisHash, undefined); - }); + } + } catch (err) { + console.error('Error:', err); + } +}; - return storeMetadataV15.get(genesisHash); +const getMetadata = (chain: string, api: ApiPromise, currentSpecVersion: string, genesisHash: HexString, chainService?: ChainService) => { + const specName = api.runtimeVersion.specName.toString(); + + const systemChain = api.runtimeChain; + // const _metadata: Option = await api.call.metadata.metadataAtVersion(15); + // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object + const metadataHex = api.runtimeMetadata.toHex(); + const registry = api.registry; + + const updateMetadata = { + chain: chain, + genesisHash: genesisHash, + specName: specName, + specVersion: currentSpecVersion, + hexValue: metadataHex, + types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record, + userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), + ss58Format: registry.chainSS58, + tokenDecimals: registry.chainDecimals[0], + tokenSymbol: registry.chainTokens[0] + }; + + chainService?.upsertMetadata(chain, { ...updateMetadata }).catch(console.error); }; export const cacheMetadata = ( @@ -57,7 +81,6 @@ export const cacheMetadata = ( // Update metadata to database with async methods substrateApi.api.isReady.then(async (api) => { const currentSpecVersion = api.runtimeVersion.specVersion.toString(); - const specName = api.runtimeVersion.specName.toString(); const genesisHash = api.genesisHash.toHex(); const metadata = await chainService?.getMetadata(chain); @@ -66,22 +89,7 @@ export const cacheMetadata = ( return; } - const systemChain = api.runtimeChain; - // const _metadata: Option = await api.call.metadata.metadataAtVersion(15); - // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object - const metadataHex = api.runtimeMetadata.toHex(); - const metadataV15 = getMetadataV15(api); - - const updateMetadata = { - chain: chain, - genesisHash: genesisHash, - specName: specName, - specVersion: currentSpecVersion, - hexValue: metadataHex, - types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record, - userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName) - }; - - chainService?.upsertMetadata(chain, { ...updateMetadata, hexV15: metadataV15 }).catch(console.error); + getMetadata(chain, api, currentSpecVersion, genesisHash, chainService); + await getMetadataV15(chain, api, chainService); }).catch(console.error); }; From 592f8ea716e52a6b438f3942849e39cfbe6cbfe7 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Mon, 23 Dec 2024 17:15:38 +0700 Subject: [PATCH 10/41] [Update] Init metadataV15 store --- .../src/background/KoniTypes.ts | 14 +++- .../src/koni/background/handlers/Extension.ts | 2 +- .../src/koni/background/utils.ts | 10 +-- .../src/services/chain-service/index.ts | 10 ++- .../storage-service/DatabaseService.ts | 4 +- .../storage-service/databases/index.ts | 5 +- .../storage-service/db-stores/MetadataV15.ts | 24 ++++++ .../storage-service/db-stores/index.ts | 2 + packages/extension-base/src/utils/metadata.ts | 80 +++++++++++++------ 9 files changed, 114 insertions(+), 37 deletions(-) create mode 100644 packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index b297bc6c8a..2b892a8f47 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -277,9 +277,17 @@ export interface MetadataItem { types: Record | string>; userExtensions?: ExtDef; hexV15?: HexString; - ss58Format?: number; - tokenDecimals?: number; - tokenSymbol?: string; + tokenInfo?: { + ss58Format: number; + tokenDecimals: number; + tokenSymbol: string; + }; +} + +export interface MetadataV15Item { + genesisHash: string; + specVersion: string; + hexV15?: HexString; } export interface CrowdloanItem { diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 00a9267a25..647f0fcefe 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -2563,7 +2563,7 @@ export default class KoniExtension { if (allRegistry.length === 0) { registry.setSignedExtensions(payload.signedExtensions); } else { - registry = getSuitableRegistry(allRegistry, payload).metadata; + registry = getSuitableRegistry(allRegistry, payload); } } diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index e4ca8a42ba..d435b987bb 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -41,9 +41,7 @@ export function getSuitableRegistry (registries: RegistrySource[], payload: Sign return a.specVersion - b.specVersion; }); - return { - metadata: sortedRegistries[0].registry - }; + return sortedRegistries[0].registry; } export function setupApiRegistry (chainInfo: _ChainInfo | undefined, koniState: KoniState): RegistrySource | null { @@ -71,9 +69,9 @@ export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _Chain registry.register(metadata.types); registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: metadata.ss58Format, - tokenDecimals: metadata.tokenDecimals, - tokenSymbol: metadata.tokenSymbol + ss58Format: metadata.tokenInfo?.ss58Format, + tokenDecimals: metadata.tokenInfo?.tokenDecimals, + tokenSymbol: metadata.tokenInfo?.tokenSymbol }) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); diff --git a/packages/extension-base/src/services/chain-service/index.ts b/packages/extension-base/src/services/chain-service/index.ts index 5d5e3f7562..424dbd84c8 100644 --- a/packages/extension-base/src/services/chain-service/index.ts +++ b/packages/extension-base/src/services/chain-service/index.ts @@ -13,7 +13,7 @@ import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chai import { _ChainApiStatus, _ChainConnectionStatus, _ChainState, _CUSTOM_PREFIX, _DataMap, _EvmApi, _NetworkUpsertParams, _NFT_CONTRACT_STANDARDS, _SMART_CONTRACT_STANDARDS, _SmartContractTokenInfo, _SubstrateApi, _ValidateCustomAssetRequest, _ValidateCustomAssetResponse } from '@subwallet/extension-base/services/chain-service/types'; import { _isAssetAutoEnable, _isAssetCanPayTxFee, _isAssetFungibleToken, _isChainEnabled, _isCustomAsset, _isCustomChain, _isCustomProvider, _isEqualContractAddress, _isEqualSmartContractAsset, _isLocalToken, _isMantaZkAsset, _isPureEvmChain, _isPureSubstrateChain, _parseAssetRefKey, randomizeProvider, updateLatestChainInfo } from '@subwallet/extension-base/services/chain-service/utils'; import { EventService } from '@subwallet/extension-base/services/event-service'; -import { IChain, IMetadataItem } from '@subwallet/extension-base/services/storage-service/databases'; +import { IChain, IMetadataItem, IMetadataV15Item } from '@subwallet/extension-base/services/storage-service/databases'; import DatabaseService from '@subwallet/extension-base/services/storage-service/DatabaseService'; import AssetSettingStore from '@subwallet/extension-base/stores/AssetSetting'; import { addLazy, calculateMetadataHash, fetchStaticData, filterAssetsByChainAndType, getShortMetadata, MODULE_SUPPORT } from '@subwallet/extension-base/utils'; @@ -2070,6 +2070,14 @@ export class ChainService { return this.dbService.stores.metadata.upsertMetadata(chain, metadata); } + getMetadataV15 (chain: string) { + return this.dbService.stores.metadataV15.getMetadata(chain); + } + + upsertMetadataV15 (chain: string, metadata: IMetadataV15Item) { + return this.dbService.stores.metadataV15.upsertMetadata(chain, metadata); + } + getMetadataByHash (hash: string) { return this.dbService.stores.metadata.getMetadataByGenesisHash(hash); } diff --git a/packages/extension-base/src/services/storage-service/DatabaseService.ts b/packages/extension-base/src/services/storage-service/DatabaseService.ts index b0db514b1f..a91949bf05 100644 --- a/packages/extension-base/src/services/storage-service/DatabaseService.ts +++ b/packages/extension-base/src/services/storage-service/DatabaseService.ts @@ -6,7 +6,7 @@ import { APIItemState, ChainStakingMetadata, CrowdloanItem, MantaPayConfig, NftC import { EventService } from '@subwallet/extension-base/services/event-service'; import { _NotificationInfo } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; import KoniDatabase, { IBalance, ICampaign, IChain, ICrowdloanItem, INft } from '@subwallet/extension-base/services/storage-service/databases'; -import { AssetStore, BalanceStore, ChainStore, CrowdloanStore, MetadataStore, MigrationStore, NftCollectionStore, NftStore, PriceStore, StakingStore, TransactionStore } from '@subwallet/extension-base/services/storage-service/db-stores'; +import { AssetStore, BalanceStore, ChainStore, CrowdloanStore, MetadataStore, MetadataV15Store, MigrationStore, NftCollectionStore, NftStore, PriceStore, StakingStore, TransactionStore } from '@subwallet/extension-base/services/storage-service/db-stores'; import BaseStore from '@subwallet/extension-base/services/storage-service/db-stores/BaseStore'; import CampaignStore from '@subwallet/extension-base/services/storage-service/db-stores/Campaign'; import ChainStakingMetadataStore from '@subwallet/extension-base/services/storage-service/db-stores/ChainStakingMetadata'; @@ -55,6 +55,8 @@ export default class DatabaseService { migration: new MigrationStore(this._db.migrations), metadata: new MetadataStore(this._db.metadata), + metadataV15: new MetadataV15Store(this._db.metadataV15), + chain: new ChainStore(this._db.chain), asset: new AssetStore(this._db.asset), diff --git a/packages/extension-base/src/services/storage-service/databases/index.ts b/packages/extension-base/src/services/storage-service/databases/index.ts index 1cda2f9af1..9f4c525870 100644 --- a/packages/extension-base/src/services/storage-service/databases/index.ts +++ b/packages/extension-base/src/services/storage-service/databases/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { _AssetRef, _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types'; -import { CampaignData, ChainStakingMetadata, CrowdloanItem, MetadataItem, NftCollection, NftItem, NominatorMetadata, PriceJson, StakingItem, TransactionHistoryItem } from '@subwallet/extension-base/background/KoniTypes'; +import { CampaignData, ChainStakingMetadata, CrowdloanItem, MetadataItem, MetadataV15Item, NftCollection, NftItem, NominatorMetadata, PriceJson, StakingItem, TransactionHistoryItem } from '@subwallet/extension-base/background/KoniTypes'; import { _NotificationInfo } from '@subwallet/extension-base/services/inapp-notification-service/interfaces'; import { BalanceItem, YieldPoolInfo, YieldPositionInfo } from '@subwallet/extension-base/types'; import Dexie, { Table, Transaction } from 'dexie'; @@ -41,6 +41,7 @@ export interface IMigration { } export interface IMetadataItem extends MetadataItem, DefaultChainDoc {} +export interface IMetadataV15Item extends MetadataV15Item, DefaultChainDoc {} export type IMantaPayLedger = any; @@ -62,6 +63,8 @@ export default class KoniDatabase extends Dexie { public migrations!: Table; public metadata!: Table; + public metadataV15!: Table; + public chain!: Table; public asset!: Table<_ChainAsset, object>; diff --git a/packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts b/packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts new file mode 100644 index 0000000000..a985c2927e --- /dev/null +++ b/packages/extension-base/src/services/storage-service/db-stores/MetadataV15.ts @@ -0,0 +1,24 @@ +// Copyright 2019-2022 @subwallet/extension-base authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import BaseStoreWithChain from '@subwallet/extension-base/services/storage-service/db-stores/BaseStoreWithChain'; + +import { IMetadataV15Item } from '../databases'; + +export default class MetadataV15Store extends BaseStoreWithChain { + getMetadata (chain: string) { + return this.table.where('chain').equals(chain).first(); + } + + upsertMetadata (chain: string, metadata: IMetadataV15Item) { + return this.table.put(metadata, chain); + } + + getMetadataByGenesisHash (genesisHash: string) { + return this.table.get(genesisHash); + } + + updateMetadataByGenesisHash (genesisHash: string, metadata: IMetadataV15Item) { + return this.table.put(metadata, genesisHash); + } +} diff --git a/packages/extension-base/src/services/storage-service/db-stores/index.ts b/packages/extension-base/src/services/storage-service/db-stores/index.ts index f26e20b498..d3e465eaaf 100644 --- a/packages/extension-base/src/services/storage-service/db-stores/index.ts +++ b/packages/extension-base/src/services/storage-service/db-stores/index.ts @@ -11,5 +11,7 @@ export { default as TransactionStore } from './Transaction'; export { default as MigrationStore } from './Migration'; export { default as MetadataStore } from './Metadata'; +export { default as MetadataV15Store } from './MetadataV15'; + export { default as ChainStore } from './Chain'; export { default as AssetStore } from './Asset'; diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 5e5db113bd..01653171a8 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -5,11 +5,26 @@ import { ChainService } from '@subwallet/extension-base/services/chain-service'; import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types'; import { ApiPromise } from '@polkadot/api'; +import { TypeRegistry } from '@polkadot/types'; import { getSpecExtensions, getSpecTypes } from '@polkadot/types-known'; -import { u8aToHex } from '@polkadot/util'; +import { formatBalance, isNumber, u8aToHex } from '@polkadot/util'; import { HexString } from '@polkadot/util/types'; +import { defaults as addressDefaults } from '@polkadot/util-crypto/address/defaults'; import { ExtraInfo, merkleizeMetadata } from '@polkadot-api/merkleize-metadata'; +interface Statics { + api: ApiPromise; + registry: TypeRegistry; +} + +export const statics = { + api: undefined, + registry: new TypeRegistry() +} as unknown as Statics; + +export const DEFAULT_DECIMALS = statics.registry.createType('u32', 12); +export const DEFAULT_SS58 = statics.registry.createType('u32', addressDefaults.prefix); + export const _isRuntimeUpdated = (signedExtensions?: string[]): boolean => { return signedExtensions ? signedExtensions.includes('CheckMetadataHash') : false; }; @@ -28,19 +43,29 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise => { try { + const currentSpecVersion = api.runtimeVersion.specVersion.toString(); + const genesisHash = api.genesisHash.toHex(); + const metadata = await chainService?.getMetadataV15(chain); + + // Avoid date existed metadata + if (metadata && metadata.specVersion === currentSpecVersion && metadata.genesisHash === genesisHash) { + return; + } + if (api.call.metadata.metadataAtVersion) { const metadataV15 = await api.call.metadata.metadataAtVersion(15); if (!metadataV15.isEmpty) { - const hexValue = metadataV15.unwrap().toHex(); + const hexV15 = metadataV15.unwrap().toHex(); + const updateMetadata = { + chain: chain, + genesisHash: genesisHash, + specVersion: currentSpecVersion, + hexV15 - if (chainService) { - const metadata = await chainService.getMetadata(chain); + }; - if (metadata) { - await chainService.upsertMetadata(chain, { ...metadata, hexV15: hexValue }); - } - } + chainService?.upsertMetadataV15(chain, { ...updateMetadata }).catch(console.error); } } } catch (err) { @@ -48,15 +73,33 @@ const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: Cha } }; -const getMetadata = (chain: string, api: ApiPromise, currentSpecVersion: string, genesisHash: HexString, chainService?: ChainService) => { +const getMetadata = async ( + chain: string, + api: ApiPromise, + chainService?: ChainService +) => { + const currentSpecVersion = api.runtimeVersion.specVersion.toString(); + const genesisHash = api.genesisHash.toHex(); const specName = api.runtimeVersion.specName.toString(); + const metadata = await chainService?.getMetadata(chain); + + // Avoid date existed metadata + if (metadata && metadata.specVersion === currentSpecVersion && metadata.genesisHash === genesisHash) { + return; + } const systemChain = api.runtimeChain; - // const _metadata: Option = await api.call.metadata.metadataAtVersion(15); - // const metadataHex = _metadata.isSome ? _metadata.unwrap().toHex().slice(2) : ''; // Need unwrap to create metadata object const metadataHex = api.runtimeMetadata.toHex(); const registry = api.registry; + const tokenInfo = { + ss58Format: isNumber(registry.chainSS58) + ? registry.chainSS58 + : DEFAULT_SS58.toNumber(), + tokenDecimals: (registry.chainDecimals || [DEFAULT_DECIMALS.toNumber()])[0], + tokenSymbol: (registry.chainTokens || formatBalance.getDefaults().unit)[0] + }; + const updateMetadata = { chain: chain, genesisHash: genesisHash, @@ -65,9 +108,7 @@ const getMetadata = (chain: string, api: ApiPromise, currentSpecVersion: string, hexValue: metadataHex, types: getSpecTypes(api.registry, systemChain, api.runtimeVersion.specName, api.runtimeVersion.specVersion) as unknown as Record, userExtensions: getSpecExtensions(api.registry, systemChain, api.runtimeVersion.specName), - ss58Format: registry.chainSS58, - tokenDecimals: registry.chainDecimals[0], - tokenSymbol: registry.chainTokens[0] + tokenInfo }; chainService?.upsertMetadata(chain, { ...updateMetadata }).catch(console.error); @@ -80,16 +121,7 @@ export const cacheMetadata = ( ): void => { // Update metadata to database with async methods substrateApi.api.isReady.then(async (api) => { - const currentSpecVersion = api.runtimeVersion.specVersion.toString(); - const genesisHash = api.genesisHash.toHex(); - const metadata = await chainService?.getMetadata(chain); - - // Avoid date existed metadata - if (metadata && metadata.specVersion === currentSpecVersion && metadata.genesisHash === genesisHash) { - return; - } - - getMetadata(chain, api, currentSpecVersion, genesisHash, chainService); + await getMetadata(chain, api, chainService); await getMetadataV15(chain, api, chainService); }).catch(console.error); }; From 070518e478abc69b2a3dfd1304b82563e2884dcb Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Tue, 24 Dec 2024 09:35:34 +0700 Subject: [PATCH 11/41] [Update] Refactor code --- .../src/koni/background/utils.ts | 8 ++---- .../src/services/chain-service/index.ts | 26 ++++++++++--------- packages/extension-base/src/utils/metadata.ts | 11 ++++---- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/extension-base/src/koni/background/utils.ts b/packages/extension-base/src/koni/background/utils.ts index d435b987bb..3ad32601d2 100644 --- a/packages/extension-base/src/koni/background/utils.ts +++ b/packages/extension-base/src/koni/background/utils.ts @@ -38,7 +38,7 @@ export function getSuitableRegistry (registries: RegistrySource[], payload: Sign return a.distance - b.distance; } - return a.specVersion - b.specVersion; + return b.specVersion - a.specVersion; }); return sortedRegistries[0].registry; @@ -68,11 +68,7 @@ export function setupDatabaseRegistry (metadata: MetadataItem, chainInfo: _Chain const _metadata = new Metadata(registry, metadata.hexValue); registry.register(metadata.types); - registry.setChainProperties(registry.createType('ChainProperties', { - ss58Format: metadata.tokenInfo?.ss58Format, - tokenDecimals: metadata.tokenInfo?.tokenDecimals, - tokenSymbol: metadata.tokenInfo?.tokenSymbol - }) as unknown as ChainProperties); + registry.setChainProperties(registry.createType('ChainProperties', metadata.tokenInfo) as unknown as ChainProperties); registry.setMetadata(_metadata, payload.signedExtensions, metadata.userExtensions); return { diff --git a/packages/extension-base/src/services/chain-service/index.ts b/packages/extension-base/src/services/chain-service/index.ts index 424dbd84c8..accbad33e5 100644 --- a/packages/extension-base/src/services/chain-service/index.ts +++ b/packages/extension-base/src/services/chain-service/index.ts @@ -3,7 +3,7 @@ import { AssetLogoMap, AssetRefMap, ChainAssetMap, ChainInfoMap, ChainLogoMap, MultiChainAssetMap } from '@subwallet/chain-list'; import { _AssetRef, _AssetRefPath, _AssetType, _ChainAsset, _ChainInfo, _ChainStatus, _EvmInfo, _MultiChainAsset, _SubstrateChainType, _SubstrateInfo, _TonInfo } from '@subwallet/chain-list/types'; -import { AssetSetting, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; +import { AssetSetting, MetadataItem, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; import { _DEFAULT_ACTIVE_CHAINS, _ZK_ASSET_PREFIX, LATEST_CHAIN_DATA_FETCHING_INTERVAL } from '@subwallet/extension-base/services/chain-service/constants'; import { EvmChainHandler } from '@subwallet/extension-base/services/chain-service/handler/EvmChainHandler'; import { MantaPrivateHandler } from '@subwallet/extension-base/services/chain-service/handler/manta/MantaPrivateHandler'; @@ -2082,42 +2082,44 @@ export class ChainService { return this.dbService.stores.metadata.getMetadataByGenesisHash(hash); } - getExtraInfo (chain: string): Omit { - const chainInfo = this.getChainInfoByKey(chain); + getExtraInfo (metadata: MetadataItem): Omit { + const tokenInfo = metadata.tokenInfo; return { - decimals: chainInfo.substrateInfo?.decimals ?? 0, - tokenSymbol: chainInfo.substrateInfo?.symbol ?? 'Unit', - base58Prefix: chainInfo.substrateInfo?.addressPrefix ?? 42 + decimals: tokenInfo?.tokenDecimals ?? 0, + tokenSymbol: tokenInfo?.tokenSymbol ?? 'Unit', + base58Prefix: tokenInfo?.ss58Format ?? 42 }; } async calculateMetadataHash (chain: string): Promise { const metadata = await this.getMetadata(chain); + const metadataV15 = await this.getMetadataV15(chain); - if (!metadata || !metadata.hexV15) { + if (!metadata || !metadataV15 || !metadataV15.hexV15) { return undefined; } - const extraInfo = this.getExtraInfo(chain); + const extraInfo = this.getExtraInfo(metadata); const specVersion = parseInt(metadata.specVersion); const specName = metadata.specName; - const hexV15 = metadata.hexV15; + const hexV15 = metadataV15.hexV15; return calculateMetadataHash({ ...extraInfo, specVersion, specName }, hexV15); } async shortenMetadata (chain: string, txBlob: string): Promise { const metadata = await this.getMetadata(chain); + const metadataV15 = await this.getMetadataV15(chain); - if (!metadata || !metadata.hexV15) { + if (!metadata || !metadataV15 || !metadataV15.hexV15) { return undefined; } - const extraInfo = this.getExtraInfo(chain); + const extraInfo = this.getExtraInfo(metadata); const specVersion = parseInt(metadata.specVersion); const specName = metadata.specName; - const hexV15 = metadata.hexV15; + const hexV15 = metadataV15.hexV15; return getShortMetadata(txBlob as HexString, { ...extraInfo, specVersion, specName }, hexV15); } diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 01653171a8..8997baa64e 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -41,7 +41,7 @@ export const getShortMetadata = (blob: HexString, extraInfo: ExtraInfo, metadata return u8aToHex(_merkleizeMetadata.getProofForExtrinsicPayload(blob)); }; -const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise => { +const updateMetadataV15 = async (chain: string, api: ApiPromise, chainService?: ChainService): Promise => { try { const currentSpecVersion = api.runtimeVersion.specVersion.toString(); const genesisHash = api.genesisHash.toHex(); @@ -62,7 +62,6 @@ const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: Cha genesisHash: genesisHash, specVersion: currentSpecVersion, hexV15 - }; chainService?.upsertMetadataV15(chain, { ...updateMetadata }).catch(console.error); @@ -73,7 +72,7 @@ const getMetadataV15 = async (chain: string, api: ApiPromise, chainService?: Cha } }; -const getMetadata = async ( +const updateMetadata = async ( chain: string, api: ApiPromise, chainService?: ChainService @@ -120,8 +119,8 @@ export const cacheMetadata = ( chainService?: ChainService ): void => { // Update metadata to database with async methods - substrateApi.api.isReady.then(async (api) => { - await getMetadata(chain, api, chainService); - await getMetadataV15(chain, api, chainService); + substrateApi.api.isReady.then((api) => { + return updateMetadata(chain, api, chainService) + .then(() => updateMetadataV15(chain, api, chainService)); }).catch(console.error); }; From e112761aa5b3f19ceb8fd048e416ea9452481fa8 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Tue, 24 Dec 2024 11:32:50 +0700 Subject: [PATCH 12/41] [Update] Add metadata conditionalVersion --- .../src/services/storage-service/databases/index.ts | 4 ++++ packages/extension-base/src/utils/metadata.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/extension-base/src/services/storage-service/databases/index.ts b/packages/extension-base/src/services/storage-service/databases/index.ts index 9f4c525870..0b21de49fc 100644 --- a/packages/extension-base/src/services/storage-service/databases/index.ts +++ b/packages/extension-base/src/services/storage-service/databases/index.ts @@ -129,6 +129,10 @@ export default class KoniDatabase extends Dexie { this.conditionalVersion(7, { inappNotification: 'id, address, proxyId, [proxyId+actionType], actionType' }); + + this.conditionalVersion(8, { + metadataV15: 'genesisHash, chain' + }); } private conditionalVersion ( diff --git a/packages/extension-base/src/utils/metadata.ts b/packages/extension-base/src/utils/metadata.ts index 8997baa64e..05e5555936 100644 --- a/packages/extension-base/src/utils/metadata.ts +++ b/packages/extension-base/src/utils/metadata.ts @@ -120,7 +120,7 @@ export const cacheMetadata = ( ): void => { // Update metadata to database with async methods substrateApi.api.isReady.then((api) => { - return updateMetadata(chain, api, chainService) - .then(() => updateMetadataV15(chain, api, chainService)); + updateMetadata(chain, api, chainService).catch(console.error); + updateMetadataV15(chain, api, chainService).catch(console.error); }).catch(console.error); }; From f1943f06b6d563a577c0baec658462c31016f550 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Tue, 24 Dec 2024 11:46:28 +0700 Subject: [PATCH 13/41] [Update] upversion clearmetadataDatabase --- .../src/services/migration-service/scripts/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension-base/src/services/migration-service/scripts/index.ts b/packages/extension-base/src/services/migration-service/scripts/index.ts index 03be614d84..301b328477 100644 --- a/packages/extension-base/src/services/migration-service/scripts/index.ts +++ b/packages/extension-base/src/services/migration-service/scripts/index.ts @@ -62,9 +62,9 @@ export default >{ '1.2.28-02': MigrateTransactionHistoryBySymbol, '1.2.69-01': MigrateRemoveGenesisHash, '1.2.13-01': ReloadMetadata, - '1.2.14-01': ClearMetadataDatabase, '1.2.32-01': MigratePairData, - '1.3.6-01': MigrateTransactionHistoryBridge + '1.3.6-01': MigrateTransactionHistoryBridge, + '1.3.10-01': ClearMetadataDatabase // [`${EVERYTIME}-1.1.42-02`]: MigrateTransactionHistoryBySymbol // [`${EVERYTIME}-1`]: AutoEnableChainsTokens }; From 9c4b8453cd67563e7d7375b91f9e70fe9bee9426 Mon Sep 17 00:00:00 2001 From: PDTnhah Date: Tue, 24 Dec 2024 19:23:56 +0700 Subject: [PATCH 14/41] [Issue-3915] Extension - Ledger - Support Avail Recovery app --- .../src/background/KoniTypes.ts | 1 + .../context/handlers/Ledger.ts | 5 ++-- .../src/types/account/info/keyring.ts | 1 + .../src/Popup/Account/ConnectLedger.tsx | 5 ++-- .../Confirmations/parts/Sign/Substrate.tsx | 2 +- .../extension-koni-ui/src/constants/ledger.ts | 29 ++++++++++--------- .../src/contexts/ThemeContext.tsx | 2 +- .../src/hooks/ledger/useLedger.ts | 20 ++++++++----- 8 files changed, 37 insertions(+), 28 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index ae7f1508a9..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 { 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/types/account/info/keyring.ts b/packages/extension-base/src/types/account/info/keyring.ts index ca66298e62..cde868e8d6 100644 --- a/packages/extension-base/src/types/account/info/keyring.ts +++ b/packages/extension-base/src/types/account/info/keyring.ts @@ -59,6 +59,7 @@ export interface AccountLedgerData { originGenesisHash?: string | null; /** Ledger's availableGenesisHashes */ availableGenesisHashes?: string[]; + isLedgerRecovery?: boolean; } /** diff --git a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx index 6a0e146228..7a05a6dd76 100644 --- a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx +++ b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx @@ -95,7 +95,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(chain, true, false, false, selectedChainMigrateMode?.genesisHash, selectedChain?.isRecovery); const onPreviousStep = useCallback(() => { setFirstStep(true); @@ -250,7 +250,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(() => { diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Substrate.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Substrate.tsx index 3f612e37e0..98142c2207 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Substrate.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Substrate.tsx @@ -257,7 +257,7 @@ const Component: React.FC = (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/constants/ledger.ts b/packages/extension-koni-ui/src/constants/ledger.ts index e9768862a9..af6be77b60 100644 --- a/packages/extension-koni-ui/src/constants/ledger.ts +++ b/packages/extension-koni-ui/src/constants/ledger.ts @@ -105,19 +105,20 @@ export const PredefinedLedgerNetwork: LedgerNetwork[] = [ slip44: 434, isHide: true }, - // { - // accountName: 'Avail', - // appName: 'Avail', - // networkName: 'Avail network', - // genesisHash: ChainInfoMap.avail_mainnet.substrateInfo?.genesisHash || '0xb91746b45e0346cc2f815a520b9c6cb4d5c0902af848db0a80f85932d2e8276a', - // icon: 'substrate', - // network: 'avail', - // slug: ChainInfoMap.avail_mainnet.slug, - // isDevMode: false, - // isGeneric: false, - // isEthereum: false, - // slip44: 709 - // }, + { + accountName: 'Avail', + appName: 'Avail', + networkName: 'Avail network', + genesisHash: ChainInfoMap.avail_mainnet.substrateInfo?.genesisHash || '0xb91746b45e0346cc2f815a520b9c6cb4d5c0902af848db0a80f85932d2e8276a', + icon: 'substrate', + network: 'avail', + slug: ChainInfoMap.avail_mainnet.slug, + isDevMode: false, + isGeneric: false, + isEthereum: false, + isRecovery: false, + slip44: 709 + }, { accountName: 'Avail Recovery', appName: 'Avail Recovery', @@ -125,7 +126,7 @@ export const PredefinedLedgerNetwork: LedgerNetwork[] = [ genesisHash: ChainInfoMap.avail_mainnet.substrateInfo?.genesisHash || '0xb91746b45e0346cc2f815a520b9c6cb4d5c0902af848db0a80f85932d2e8276a', icon: 'substrate', network: 'availRecovery', - slug: ChainInfoMap.avail_mainnet.slug, + slug: 'avail_mainnet_recovery', isDevMode: false, isGeneric: false, isEthereum: false, diff --git a/packages/extension-koni-ui/src/contexts/ThemeContext.tsx b/packages/extension-koni-ui/src/contexts/ThemeContext.tsx index 9d915801a7..0fd5ad817a 100644 --- a/packages/extension-koni-ui/src/contexts/ThemeContext.tsx +++ b/packages/extension-koni-ui/src/contexts/ThemeContext.tsx @@ -361,7 +361,7 @@ export function ThemeProvider ({ children }: ThemeProviderProps): React.ReactEle const themeConfig = useMemo(() => { const config = SW_THEME_CONFIGS[themeName]; - Object.assign(config.logoMap.network, logoMaps.chainLogoMap); + Object.assign(config.logoMap.network, logoMaps.chainLogoMap, { avail_mainnet_recovery: logoMaps.chainLogoMap.avail_mainnet }); Object.assign(config.logoMap.symbol, logoMaps.assetLogoMap); return config; diff --git a/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts b/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts index 1158f9cff4..f4ef25c3b9 100644 --- a/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts +++ b/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts @@ -42,7 +42,11 @@ const baseState: StateBase = { // && uiSettings.ledgerConn !== 'none' }; -const getNetwork = (ledgerChains: LedgerNetwork[], slug: string, isEthereumNetwork: boolean): LedgerNetwork | undefined => { +const getNetwork = (ledgerChains: LedgerNetwork[], slug: string, isEthereumNetwork: boolean, isRecovery?: boolean): LedgerNetwork | undefined => { + if (isRecovery) { + return ledgerChains.find((network) => network.isRecovery); + } + return ledgerChains.find((network) => network.slug === slug || (network.isEthereum && isEthereumNetwork)); }; @@ -50,12 +54,12 @@ const getNetworkByGenesisHash = (ledgerChains: MigrationLedgerNetwork[], genesis return ledgerChains.find((network) => network.genesisHash === genesisHash); }; -const retrieveLedger = (chainSlug: string, ledgerChains: LedgerNetwork[], migrateLedgerChains: MigrationLedgerNetwork[], isEthereumNetwork: boolean, forceMigration: boolean, originGenesisHash?: string | null): Ledger => { +const retrieveLedger = (chainSlug: string, ledgerChains: LedgerNetwork[], migrateLedgerChains: MigrationLedgerNetwork[], isEthereumNetwork: boolean, forceMigration: boolean, originGenesisHash?: string | null, isRecovery?: boolean): Ledger => { const { isLedgerCapable } = baseState; assert(isLedgerCapable, ledgerIncompatible); - const def = getNetwork(ledgerChains, chainSlug, isEthereumNetwork); + const def = getNetwork(ledgerChains, chainSlug, isEthereumNetwork, isRecovery); assert(def, 'There is no known Ledger app available for this chain'); @@ -86,7 +90,7 @@ const retrieveLedger = (chainSlug: string, ledgerChains: LedgerNetwork[], migrat } }; -export function useLedger (chainSlug?: string, active = true, isSigning = false, forceMigration = false, originGenesisHash?: string | null): Result { +export function useLedger (chainSlug?: string, active = true, isSigning = false, forceMigration = false, originGenesisHash?: string | null, isRecovery?: boolean): Result { const { t } = useTranslation(); const [ledgerChains, migrateLedgerChains] = useGetSupportedLedger(); @@ -133,14 +137,14 @@ export function useLedger (chainSlug?: string, active = true, isSigning = false, } try { - return retrieveLedger(chainSlug, ledgerChains, migrateLedgerChains, isEvmNetwork, forceMigration, originGenesisHash); + return retrieveLedger(chainSlug, ledgerChains, migrateLedgerChains, isEvmNetwork, forceMigration, originGenesisHash, isRecovery); } catch (error) { setError((error as Error).message); } } return null; - }, [refreshLock, chainSlug, active, ledgerChains, migrateLedgerChains, isEvmNetwork, forceMigration, originGenesisHash]); + }, [refreshLock, chainSlug, active, ledgerChains, migrateLedgerChains, isEvmNetwork, forceMigration, originGenesisHash, isRecovery]); const appName = useMemo(() => { const unknownNetwork = 'unknown network'; @@ -151,7 +155,7 @@ export function useLedger (chainSlug?: string, active = true, isSigning = false, const chainInfo = chainInfoMap[chainSlug]; const isEthereumNetwork = chainInfo ? _isChainEvmCompatible(chainInfo) : false; - const { appName, isEthereum, isGeneric } = getNetwork(ledgerChains, chainSlug, isEthereumNetwork) || { appName: 'Polkadot Migration', isGeneric: true }; + const { appName, isEthereum, isGeneric } = getNetwork(ledgerChains, chainSlug, isEthereumNetwork, isRecovery) || { appName: 'Polkadot Migration', isGeneric: true }; if (!isGeneric && forceMigration && !isEthereum) { if (NotNeedMigrationGens.includes(chainInfo?.substrateInfo?.genesisHash || '')) { @@ -162,7 +166,7 @@ export function useLedger (chainSlug?: string, active = true, isSigning = false, } return appName; - }, [chainInfoMap, forceMigration, ledgerChains, chainSlug]); + }, [chainInfoMap, forceMigration, ledgerChains, chainSlug, isRecovery]); const refresh = useCallback(() => { setRefreshLock(true); From 8324794bce7c9e66015c6856bd5a4f19b0d5aa42 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Wed, 25 Dec 2024 11:29:44 +0700 Subject: [PATCH 15/41] [Update] Set up Subwallet validator for polkadot and kusama --- .../Earning/EarningValidatorSelector.tsx | 23 +++++++++++++++---- .../Earning/EarningValidatorSelector.tsx | 23 +++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx b/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx index b5b3f78d16..a6f38dc0d4 100644 --- a/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx +++ b/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx @@ -50,8 +50,21 @@ interface SortOption { const SORTING_MODAL_ID = 'nominated-sorting-modal'; const FILTER_MODAL_ID = 'nominated-filter-modal'; -const AVAIL_CHAIN = 'avail_mainnet'; -const AVAIL_VALIDATOR = '5FjdibsxmNFas5HWcT2i1AXbpfgiNfWqezzo88H2tskxWdt2'; + +const CHAIN_VALIDATOR_CONFIG_LIST = { + avail_mainnet: { + maxCount: 16, + preSelectValidators: '5FjdibsxmNFas5HWcT2i1AXbpfgiNfWqezzo88H2tskxWdt2' + }, + polkadot: { + maxCount: 16, + preSelectValidators: '13Nd71b9XLWNptAcSgesGMKJctpm1uLjZBWDfVdeA6oyXdg6,15tEXHHDjEv6VKR2MhpnfeJZnKQnTyMPVvGH51BJvBXNoCNK' + }, + kusama: { + maxCount: 24, + preSelectValidators: 'ERYwYjy1LGY1GWovnxxmXo68SZLLYwjMXNEvhhDhp48zyED,EwwczfxHvFq8zyYFkQv29r9us7M8Gbmw4cUtrvF5ozx6Mf2,HTZ3GN2VpfYoSDxAmaqRSqR5HhNaLcRsoNYJNTuqtiMMptB,CmNYnM3pStTCKgXq67Cp8rURA6GTfdfseJx7posbg2vJVHQ' + } +}; const filterOptions = [ { @@ -313,12 +326,14 @@ const Component = (props: Props, ref: ForwardedRef) => { }, [activeModal, id]); useEffect(() => { - if (chain === AVAIL_CHAIN) { + const chainValidator = CHAIN_VALIDATOR_CONFIG_LIST[chain as keyof typeof CHAIN_VALIDATOR_CONFIG_LIST]; + + if (chainValidator) { setAutoValidator((old) => { if (old) { return old; } else { - const selectedValidator = autoSelectValidatorOptimally(items, 16, true, AVAIL_VALIDATOR); + const selectedValidator = autoSelectValidatorOptimally(items, chainValidator.maxCount, true, chainValidator.preSelectValidators); return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); } diff --git a/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx b/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx index 5c8a68caa6..b1702ac39a 100644 --- a/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx +++ b/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx @@ -50,8 +50,21 @@ interface SortOption { const SORTING_MODAL_ID = 'nominated-sorting-modal'; const FILTER_MODAL_ID = 'nominated-filter-modal'; -const AVAIL_CHAIN = 'avail_mainnet'; -const AVAIL_VALIDATOR = '5FjdibsxmNFas5HWcT2i1AXbpfgiNfWqezzo88H2tskxWdt2'; + +const CHAIN_VALIDATOR_CONFIG_LIST = { + avail_mainnet: { + maxCount: 16, + preSelectValidators: '5FjdibsxmNFas5HWcT2i1AXbpfgiNfWqezzo88H2tskxWdt2' + }, + polkadot: { + maxCount: 16, + preSelectValidators: '13Nd71b9XLWNptAcSgesGMKJctpm1uLjZBWDfVdeA6oyXdg6,15tEXHHDjEv6VKR2MhpnfeJZnKQnTyMPVvGH51BJvBXNoCNK' + }, + kusama: { + maxCount: 24, + preSelectValidators: 'ERYwYjy1LGY1GWovnxxmXo68SZLLYwjMXNEvhhDhp48zyED,EwwczfxHvFq8zyYFkQv29r9us7M8Gbmw4cUtrvF5ozx6Mf2,HTZ3GN2VpfYoSDxAmaqRSqR5HhNaLcRsoNYJNTuqtiMMptB,CmNYnM3pStTCKgXq67Cp8rURA6GTfdfseJx7posbg2vJVHQ' + } +}; const filterOptions = [ { @@ -314,12 +327,14 @@ const Component = (props: Props, ref: ForwardedRef) => { }, [activeModal, id]); useEffect(() => { - if (chain === AVAIL_CHAIN) { + const chainValidator = CHAIN_VALIDATOR_CONFIG_LIST[chain as keyof typeof CHAIN_VALIDATOR_CONFIG_LIST]; + + if (chainValidator) { setAutoValidator((old) => { if (old) { return old; } else { - const selectedValidator = autoSelectValidatorOptimally(items, 16, true, AVAIL_VALIDATOR); + const selectedValidator = autoSelectValidatorOptimally(items, chainValidator.maxCount, true, chainValidator.preSelectValidators); return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); } From a60e2b88462432b2a566e2f72cd29f42bdb97c2c Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Wed, 25 Dec 2024 15:08:50 +0700 Subject: [PATCH 16/41] [Update] Init wallet validator content --- .../extension-base/src/constants/staking.ts | 7 +++ .../Earning/EarningValidatorSelector.tsx | 53 ++++++++----------- .../Earning/EarningValidatorSelector.tsx | 53 ++++++++----------- 3 files changed, 53 insertions(+), 60 deletions(-) diff --git a/packages/extension-base/src/constants/staking.ts b/packages/extension-base/src/constants/staking.ts index 4882802bde..e342f3dc82 100644 --- a/packages/extension-base/src/constants/staking.ts +++ b/packages/extension-base/src/constants/staking.ts @@ -14,3 +14,10 @@ export const PREDEFINED_STAKING_POOL: Record = { export const MAX_NOMINATIONS = '16'; export const PREDEFINED_EARNING_POOL_PROMISE = fetchStaticData>('nomination-pool-recommendation'); + +export type ChainValidatorPreSelect = { + maxCount: number; + preSelectValidators: string; +}; + +export const DIRECT_NOMINATION_VALIDATOR = fetchStaticData>('direct-nomination-validator'); diff --git a/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx b/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx index a6f38dc0d4..0ba8a671a5 100644 --- a/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx +++ b/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx @@ -1,10 +1,11 @@ // Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ChainValidatorPreSelect } from '@subwallet/extension-base/constants'; import { getValidatorLabel } from '@subwallet/extension-base/koni/api/staking/bonding/utils'; import { _STAKING_CHAIN_GROUP } from '@subwallet/extension-base/services/earning-service/constants'; import { YieldPoolType } from '@subwallet/extension-base/types'; -import { detectTranslate } from '@subwallet/extension-base/utils'; +import { detectTranslate, fetchStaticData } from '@subwallet/extension-base/utils'; import { SelectValidatorInput, StakingValidatorItem } from '@subwallet/extension-koni-ui/components'; import EmptyValidator from '@subwallet/extension-koni-ui/components/Account/EmptyValidator'; import { BasicInputWrapper } from '@subwallet/extension-koni-ui/components/Field/Base'; @@ -51,21 +52,6 @@ interface SortOption { const SORTING_MODAL_ID = 'nominated-sorting-modal'; const FILTER_MODAL_ID = 'nominated-filter-modal'; -const CHAIN_VALIDATOR_CONFIG_LIST = { - avail_mainnet: { - maxCount: 16, - preSelectValidators: '5FjdibsxmNFas5HWcT2i1AXbpfgiNfWqezzo88H2tskxWdt2' - }, - polkadot: { - maxCount: 16, - preSelectValidators: '13Nd71b9XLWNptAcSgesGMKJctpm1uLjZBWDfVdeA6oyXdg6,15tEXHHDjEv6VKR2MhpnfeJZnKQnTyMPVvGH51BJvBXNoCNK' - }, - kusama: { - maxCount: 24, - preSelectValidators: 'ERYwYjy1LGY1GWovnxxmXo68SZLLYwjMXNEvhhDhp48zyED,EwwczfxHvFq8zyYFkQv29r9us7M8Gbmw4cUtrvF5ozx6Mf2,HTZ3GN2VpfYoSDxAmaqRSqR5HhNaLcRsoNYJNTuqtiMMptB,CmNYnM3pStTCKgXq67Cp8rURA6GTfdfseJx7posbg2vJVHQ' - } -}; - const filterOptions = [ { label: 'Active validator', @@ -326,21 +312,28 @@ const Component = (props: Props, ref: ForwardedRef) => { }, [activeModal, id]); useEffect(() => { - const chainValidator = CHAIN_VALIDATOR_CONFIG_LIST[chain as keyof typeof CHAIN_VALIDATOR_CONFIG_LIST]; - - if (chainValidator) { - setAutoValidator((old) => { - if (old) { - return old; - } else { - const selectedValidator = autoSelectValidatorOptimally(items, chainValidator.maxCount, true, chainValidator.preSelectValidators); + fetchStaticData>('direct-nomination-validator').then((fetchedPreSelectValidator) => { + const chainValidator = fetchedPreSelectValidator[chain]; - return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); - } - }); - } else { - setAutoValidator(''); - } + if (chainValidator) { + setAutoValidator((old) => { + if (old) { + return old; + } else { + const selectedValidator = autoSelectValidatorOptimally( + items, + chainValidator.maxCount, + true, + chainValidator.preSelectValidators + ); + + return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); + } + }); + } else { + setAutoValidator(''); + } + }).catch(console.error); }, [items, chain]); useEffect(() => { diff --git a/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx b/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx index b1702ac39a..224fd8e4d8 100644 --- a/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx +++ b/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx @@ -1,10 +1,11 @@ // Copyright 2019-2022 @subwallet/extension-web-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ChainValidatorPreSelect } from '@subwallet/extension-base/constants'; import { getValidatorLabel } from '@subwallet/extension-base/koni/api/staking/bonding/utils'; import { _STAKING_CHAIN_GROUP } from '@subwallet/extension-base/services/earning-service/constants'; import { YieldPoolType } from '@subwallet/extension-base/types'; -import { detectTranslate } from '@subwallet/extension-base/utils'; +import { detectTranslate, fetchStaticData } from '@subwallet/extension-base/utils'; import { BaseModal, SelectValidatorInput, StakingValidatorItem } from '@subwallet/extension-web-ui/components'; import EmptyValidator from '@subwallet/extension-web-ui/components/Account/EmptyValidator'; import { BasicInputWrapper } from '@subwallet/extension-web-ui/components/Field/Base'; @@ -51,21 +52,6 @@ interface SortOption { const SORTING_MODAL_ID = 'nominated-sorting-modal'; const FILTER_MODAL_ID = 'nominated-filter-modal'; -const CHAIN_VALIDATOR_CONFIG_LIST = { - avail_mainnet: { - maxCount: 16, - preSelectValidators: '5FjdibsxmNFas5HWcT2i1AXbpfgiNfWqezzo88H2tskxWdt2' - }, - polkadot: { - maxCount: 16, - preSelectValidators: '13Nd71b9XLWNptAcSgesGMKJctpm1uLjZBWDfVdeA6oyXdg6,15tEXHHDjEv6VKR2MhpnfeJZnKQnTyMPVvGH51BJvBXNoCNK' - }, - kusama: { - maxCount: 24, - preSelectValidators: 'ERYwYjy1LGY1GWovnxxmXo68SZLLYwjMXNEvhhDhp48zyED,EwwczfxHvFq8zyYFkQv29r9us7M8Gbmw4cUtrvF5ozx6Mf2,HTZ3GN2VpfYoSDxAmaqRSqR5HhNaLcRsoNYJNTuqtiMMptB,CmNYnM3pStTCKgXq67Cp8rURA6GTfdfseJx7posbg2vJVHQ' - } -}; - const filterOptions = [ { label: 'Active validator', @@ -327,21 +313,28 @@ const Component = (props: Props, ref: ForwardedRef) => { }, [activeModal, id]); useEffect(() => { - const chainValidator = CHAIN_VALIDATOR_CONFIG_LIST[chain as keyof typeof CHAIN_VALIDATOR_CONFIG_LIST]; - - if (chainValidator) { - setAutoValidator((old) => { - if (old) { - return old; - } else { - const selectedValidator = autoSelectValidatorOptimally(items, chainValidator.maxCount, true, chainValidator.preSelectValidators); + fetchStaticData>('direct-nomination-validator').then((fetchedPreSelectValidator) => { + const chainValidator = fetchedPreSelectValidator[chain]; - return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); - } - }); - } else { - setAutoValidator(''); - } + if (chainValidator) { + setAutoValidator((old) => { + if (old) { + return old; + } else { + const selectedValidator = autoSelectValidatorOptimally( + items, + chainValidator.maxCount, + true, + chainValidator.preSelectValidators + ); + + return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); + } + }); + } else { + setAutoValidator(''); + } + }).catch(console.error); }, [items, chain]); useEffect(() => { From 54656a361e4831d880ab9df7a4d179c0c438396c Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Wed, 25 Dec 2024 15:47:40 +0700 Subject: [PATCH 17/41] [Update] Refactor code --- .../extension-base/src/constants/staking.ts | 4 +- .../Earning/EarningValidatorSelector.tsx | 52 +++++++++++-------- .../Earning/EarningValidatorSelector.tsx | 52 +++++++++++-------- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/packages/extension-base/src/constants/staking.ts b/packages/extension-base/src/constants/staking.ts index e342f3dc82..6c9c3c0a89 100644 --- a/packages/extension-base/src/constants/staking.ts +++ b/packages/extension-base/src/constants/staking.ts @@ -15,9 +15,7 @@ export const MAX_NOMINATIONS = '16'; export const PREDEFINED_EARNING_POOL_PROMISE = fetchStaticData>('nomination-pool-recommendation'); -export type ChainValidatorPreSelect = { +export type ChainRecommendValidator = { maxCount: number; preSelectValidators: string; }; - -export const DIRECT_NOMINATION_VALIDATOR = fetchStaticData>('direct-nomination-validator'); diff --git a/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx b/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx index 0ba8a671a5..1691395f10 100644 --- a/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx +++ b/packages/extension-koni-ui/src/components/Field/Earning/EarningValidatorSelector.tsx @@ -1,7 +1,7 @@ // Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { ChainValidatorPreSelect } from '@subwallet/extension-base/constants'; +import { ChainRecommendValidator } from '@subwallet/extension-base/constants'; import { getValidatorLabel } from '@subwallet/extension-base/koni/api/staking/bonding/utils'; import { _STAKING_CHAIN_GROUP } from '@subwallet/extension-base/services/earning-service/constants'; import { YieldPoolType } from '@subwallet/extension-base/types'; @@ -102,6 +102,8 @@ const Component = (props: Props, ref: ForwardedRef) => { const isSingleSelect = useMemo(() => _isSingleSelect || !isRelayChain, [_isSingleSelect, isRelayChain]); const hasReturn = useMemo(() => items[0]?.expectedReturn !== undefined, [items]); + const [defaultPoolMap, setDefaultPoolMap] = useState>({}); + const maxPoolMembersValue = useMemo(() => { if (poolInfo.type === YieldPoolType.NATIVE_STAKING) { // todo: should also check chain group for pool return poolInfo.maxPoolMembers; @@ -312,29 +314,33 @@ const Component = (props: Props, ref: ForwardedRef) => { }, [activeModal, id]); useEffect(() => { - fetchStaticData>('direct-nomination-validator').then((fetchedPreSelectValidator) => { - const chainValidator = fetchedPreSelectValidator[chain]; - - if (chainValidator) { - setAutoValidator((old) => { - if (old) { - return old; - } else { - const selectedValidator = autoSelectValidatorOptimally( - items, - chainValidator.maxCount, - true, - chainValidator.preSelectValidators - ); - - return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); - } - }); - } else { - setAutoValidator(''); - } + fetchStaticData>('direct-nomination-validator').then((earningPoolRecommendation) => { + setDefaultPoolMap(earningPoolRecommendation); }).catch(console.error); - }, [items, chain]); + }, []); + + useEffect(() => { + const recommendValidator = defaultPoolMap[chain]; + + if (recommendValidator) { + setAutoValidator((old) => { + if (old) { + return old; + } else { + const selectedValidator = autoSelectValidatorOptimally( + items, + recommendValidator.maxCount, + true, + recommendValidator.preSelectValidators + ); + + return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); + } + }); + } else { + setAutoValidator(''); + } + }, [items, chain, defaultPoolMap]); useEffect(() => { const _default = nominations?.map((item) => getValidatorKey(item.validatorAddress, item.validatorIdentity)).join(',') || autoValidator || ''; diff --git a/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx b/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx index 224fd8e4d8..83e1fd1151 100644 --- a/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx +++ b/packages/extension-web-ui/src/components/Field/Earning/EarningValidatorSelector.tsx @@ -1,7 +1,7 @@ // Copyright 2019-2022 @subwallet/extension-web-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { ChainValidatorPreSelect } from '@subwallet/extension-base/constants'; +import { ChainRecommendValidator } from '@subwallet/extension-base/constants'; import { getValidatorLabel } from '@subwallet/extension-base/koni/api/staking/bonding/utils'; import { _STAKING_CHAIN_GROUP } from '@subwallet/extension-base/services/earning-service/constants'; import { YieldPoolType } from '@subwallet/extension-base/types'; @@ -103,6 +103,8 @@ const Component = (props: Props, ref: ForwardedRef) => { const isSingleSelect = useMemo(() => _isSingleSelect || !isRelayChain, [_isSingleSelect, isRelayChain]); const hasReturn = useMemo(() => items[0]?.expectedReturn !== undefined, [items]); + const [defaultPoolMap, setDefaultPoolMap] = useState>({}); + const maxPoolMembersValue = useMemo(() => { if (poolInfo.type === YieldPoolType.NATIVE_STAKING) { // todo: should also check chain group for pool return poolInfo.maxPoolMembers; @@ -313,29 +315,33 @@ const Component = (props: Props, ref: ForwardedRef) => { }, [activeModal, id]); useEffect(() => { - fetchStaticData>('direct-nomination-validator').then((fetchedPreSelectValidator) => { - const chainValidator = fetchedPreSelectValidator[chain]; - - if (chainValidator) { - setAutoValidator((old) => { - if (old) { - return old; - } else { - const selectedValidator = autoSelectValidatorOptimally( - items, - chainValidator.maxCount, - true, - chainValidator.preSelectValidators - ); - - return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); - } - }); - } else { - setAutoValidator(''); - } + fetchStaticData>('direct-nomination-validator').then((earningPoolRecommendation) => { + setDefaultPoolMap(earningPoolRecommendation); }).catch(console.error); - }, [items, chain]); + }, []); + + useEffect(() => { + const recommendValidator = defaultPoolMap[chain]; + + if (recommendValidator) { + setAutoValidator((old) => { + if (old) { + return old; + } else { + const selectedValidator = autoSelectValidatorOptimally( + items, + recommendValidator.maxCount, + true, + recommendValidator.preSelectValidators + ); + + return selectedValidator.map((item) => getValidatorKey(item.address, item.identity)).join(','); + } + }); + } else { + setAutoValidator(''); + } + }, [items, chain, defaultPoolMap]); useEffect(() => { const _default = nominations?.map((item) => getValidatorKey(item.validatorAddress, item.validatorIdentity)).join(',') || autoValidator || ''; From a4473fe3b94f8671de52f46264c9a4caa29adf4b Mon Sep 17 00:00:00 2001 From: PDTnhah Date: Wed, 25 Dec 2024 17:50:49 +0700 Subject: [PATCH 18/41] [Issue-3915] Extension - Ledger - Support Avail Recovery app --- .../src/types/account/info/keyring.ts | 1 + .../src/Popup/Account/ConnectLedger.tsx | 13 +- .../components/Field/LedgerChainSelector.tsx | 190 ++++++++++++++++++ .../extension-koni-ui/src/constants/ledger.ts | 3 +- .../src/contexts/ThemeContext.tsx | 2 +- .../src/hooks/ledger/useLedger.ts | 2 +- .../src/utils/account/ledger.ts | 4 +- 7 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 packages/extension-koni-ui/src/components/Field/LedgerChainSelector.tsx diff --git a/packages/extension-base/src/types/account/info/keyring.ts b/packages/extension-base/src/types/account/info/keyring.ts index cde868e8d6..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,7 @@ export interface AccountLedgerData { originGenesisHash?: string | null; /** Ledger's availableGenesisHashes */ availableGenesisHashes?: string[]; + /** Is Ledger recovery chain */ isLedgerRecovery?: boolean; } diff --git a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx index 7a05a6dd76..9d5de9940c 100644 --- a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx +++ b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx @@ -18,6 +18,8 @@ import { useSelector } from 'react-redux'; import styled from 'styled-components'; import DefaultLogosMap from '../../assets/logo'; +import { LedgerChainSelector, LedgerItemType } from '@subwallet/extension-koni-ui/components/Field/LedgerChainSelector'; +import { convertKey } from '@subwallet/extension-koni-ui/utils'; type Props = ThemeProps; @@ -54,13 +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', '').concat(network.isRecovery ? ' Recovery' : '') : network.networkName, - slug: network.slug + chain: network.slug, + slug: convertKey(network) })), [supportedLedger]); const networkMigrates = useMemo((): ChainItemType[] => migrateSupportLedger @@ -80,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) => convertKey(n) === chain); }, [chain, supportedLedger]); const selectedChainMigrateMode = useMemo((): MigrationLedgerNetwork | undefined => { @@ -95,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, selectedChain?.isRecovery); + const { error, getAllAddress, isLoading, isLocked, ledger, refresh, warning } = useLedger(selectedChain?.slug, true, false, false, selectedChainMigrateMode?.genesisHash, selectedChain?.isRecovery); const onPreviousStep = useCallback(() => { setFirstStep(true); @@ -329,7 +332,7 @@ const Component: React.FC = (props: Props) => { sizeSquircleBorder={108} /> - ; + +function Component (props: Props, ref: ForwardedRef): React.ReactElement { + const { className = '', disabled, id = 'address-input', items, label, loading, messageTooltip, placeholder, statusHelp, title, tooltip, value } = props; + const { t } = useTranslation(); + const { token } = useTheme() as Theme; + const { onSelect } = useSelectModalInputHelper(props, ref); + + const renderChainSelected = useCallback((item: LedgerItemType) => { + if (loading) { + return ( +
{t('Loading ...')}
+ ); + } + + return ( +
{item.name}
+ ); + }, [loading, t]); + + const searchFunction = useCallback((item: LedgerItemType, searchText: string) => { + const searchTextLowerCase = searchText.toLowerCase(); + + return ( + item.name.toLowerCase().includes(searchTextLowerCase) + ); + }, []); + + const chainLogo = useMemo(() => { + const chain = value?.replaceAll(RECOVERY_SLUG, '') || ''; + + return ( + + ); + }, [token.controlHeightSM, value]); + + const renderItem = useCallback((item: LedgerItemType, selected: boolean) => { + if (item.disabled && !!messageTooltip) { + return ( + +
+ + +
)} + /> + +
+ ); + } + + return ( + + + )} + /> + ); + }, [messageTooltip, t, token.colorSuccess]); + + return ( + ('Network name')} + selected={value || ''} + statusHelp={statusHelp} + title={title || label || placeholder || t('Select network')} + tooltip={tooltip} + /> + ); +} + +export const LedgerChainSelector = styled(forwardRef(Component))(({ theme: { token } }: Props) => { + return ({ + '&.ant-select-modal-input-container .ant-select-modal-input-wrapper': { + paddingLeft: 12, + paddingRight: 12 + }, + + '&.chain-selector-input': { + '.__selected-item, .__loading-text': { + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis' + }, + + '.__selected-item': { + color: token.colorText, + fontWeight: 500, + lineHeight: token.lineHeightHeading6 + }, + + '.__loading-text': { + color: token.colorTextLight4 + } + }, + + '.chain-logo': { + margin: '-1px 0' + }, + + '.ant-network-item .__check-icon': { + display: 'flex', + width: 40, + justifyContent: 'center' + }, + + '.ant-network-item.disabled': { + opacity: token.opacityDisable, + '.ant-network-item-content': { + cursor: 'not-allowed', + + '&:hover': { + backgroundColor: token.colorBgSecondary + } + } + } + }); +}); diff --git a/packages/extension-koni-ui/src/constants/ledger.ts b/packages/extension-koni-ui/src/constants/ledger.ts index af6be77b60..61dd559cab 100644 --- a/packages/extension-koni-ui/src/constants/ledger.ts +++ b/packages/extension-koni-ui/src/constants/ledger.ts @@ -8,6 +8,7 @@ export const SUBSTRATE_GENERIC_KEY = 'substrate_generic'; export const SUBSTRATE_MIGRATION_KEY = 'substrate_migration'; export const POLKADOT_KEY = 'polkadot'; export const POLKADOT_SLIP_44 = 354; +export const RECOVERY_SLUG = '_recovery'; export const PredefinedLedgerNetwork: LedgerNetwork[] = [ { @@ -126,7 +127,7 @@ export const PredefinedLedgerNetwork: LedgerNetwork[] = [ genesisHash: ChainInfoMap.avail_mainnet.substrateInfo?.genesisHash || '0xb91746b45e0346cc2f815a520b9c6cb4d5c0902af848db0a80f85932d2e8276a', icon: 'substrate', network: 'availRecovery', - slug: 'avail_mainnet_recovery', + slug: ChainInfoMap.avail_mainnet.slug, isDevMode: false, isGeneric: false, isEthereum: false, diff --git a/packages/extension-koni-ui/src/contexts/ThemeContext.tsx b/packages/extension-koni-ui/src/contexts/ThemeContext.tsx index 0fd5ad817a..9d915801a7 100644 --- a/packages/extension-koni-ui/src/contexts/ThemeContext.tsx +++ b/packages/extension-koni-ui/src/contexts/ThemeContext.tsx @@ -361,7 +361,7 @@ export function ThemeProvider ({ children }: ThemeProviderProps): React.ReactEle const themeConfig = useMemo(() => { const config = SW_THEME_CONFIGS[themeName]; - Object.assign(config.logoMap.network, logoMaps.chainLogoMap, { avail_mainnet_recovery: logoMaps.chainLogoMap.avail_mainnet }); + Object.assign(config.logoMap.network, logoMaps.chainLogoMap); Object.assign(config.logoMap.symbol, logoMaps.assetLogoMap); return config; diff --git a/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts b/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts index f4ef25c3b9..7cca4c9ffb 100644 --- a/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts +++ b/packages/extension-koni-ui/src/hooks/ledger/useLedger.ts @@ -44,7 +44,7 @@ const baseState: StateBase = { const getNetwork = (ledgerChains: LedgerNetwork[], slug: string, isEthereumNetwork: boolean, isRecovery?: boolean): LedgerNetwork | undefined => { if (isRecovery) { - return ledgerChains.find((network) => network.isRecovery); + return ledgerChains.find((network) => network.slug === slug && network.isRecovery); } return ledgerChains.find((network) => network.slug === slug || (network.isEthereum && isEthereumNetwork)); diff --git a/packages/extension-koni-ui/src/utils/account/ledger.ts b/packages/extension-koni-ui/src/utils/account/ledger.ts index 9a44b3c6e6..4310237a9d 100644 --- a/packages/extension-koni-ui/src/utils/account/ledger.ts +++ b/packages/extension-koni-ui/src/utils/account/ledger.ts @@ -5,7 +5,7 @@ import { _ChainInfo } from '@subwallet/chain-list/types'; import { LedgerNetwork } from '@subwallet/extension-base/background/KoniTypes'; import { _ChainState } from '@subwallet/extension-base/services/chain-service/types'; import { _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils'; -import { PredefinedLedgerNetwork } from '@subwallet/extension-koni-ui/constants/ledger'; +import { PredefinedLedgerNetwork, RECOVERY_SLUG } from '@subwallet/extension-koni-ui/constants/ledger'; interface ChainItem extends _ChainState { isEthereum: boolean; @@ -25,3 +25,5 @@ export const getSupportedLedger = (networkInfoMap: Record, n return networkInfoItems.find((item) => ledgerNetwork.slug === item.slug || (ledgerNetwork.isEthereum && item.isEthereum)); }); }; + +export const convertKey = (network: LedgerNetwork) => network.slug.concat(network.isRecovery ? RECOVERY_SLUG : ''); From 9f8b532fdc4c1a311487b760a774a1dd216f0f77 Mon Sep 17 00:00:00 2001 From: PDTnhah Date: Wed, 25 Dec 2024 18:08:55 +0700 Subject: [PATCH 19/41] [Issue-3915] Fix lint --- .../extension-koni-ui/src/Popup/Account/ConnectLedger.tsx | 6 +++--- packages/extension-koni-ui/src/utils/account/ledger.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx index 9d5de9940c..c775661bcc 100644 --- a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx +++ b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx @@ -19,7 +19,7 @@ import styled from 'styled-components'; import DefaultLogosMap from '../../assets/logo'; import { LedgerChainSelector, LedgerItemType } from '@subwallet/extension-koni-ui/components/Field/LedgerChainSelector'; -import { convertKey } from '@subwallet/extension-koni-ui/utils'; +import { convertNetworkSlug } from '@subwallet/extension-koni-ui/utils'; type Props = ThemeProps; @@ -63,7 +63,7 @@ const Component: React.FC = (props: Props) => { ? network.networkName.replace(' network', '').concat(network.isRecovery ? ' Recovery' : '') : network.networkName, chain: network.slug, - slug: convertKey(network) + slug: convertNetworkSlug(network) })), [supportedLedger]); const networkMigrates = useMemo((): ChainItemType[] => migrateSupportLedger @@ -83,7 +83,7 @@ const Component: React.FC = (props: Props) => { const [isSubmitting, setIsSubmitting] = useState(false); const selectedChain = useMemo((): LedgerNetwork | undefined => { - return supportedLedger.find((n) => convertKey(n) === chain); + return supportedLedger.find((n) => convertNetworkSlug(n) === chain); }, [chain, supportedLedger]); const selectedChainMigrateMode = useMemo((): MigrationLedgerNetwork | undefined => { diff --git a/packages/extension-koni-ui/src/utils/account/ledger.ts b/packages/extension-koni-ui/src/utils/account/ledger.ts index 4310237a9d..8f7e6c00ed 100644 --- a/packages/extension-koni-ui/src/utils/account/ledger.ts +++ b/packages/extension-koni-ui/src/utils/account/ledger.ts @@ -26,4 +26,4 @@ export const getSupportedLedger = (networkInfoMap: Record, n }); }; -export const convertKey = (network: LedgerNetwork) => network.slug.concat(network.isRecovery ? RECOVERY_SLUG : ''); +export const convertNetworkSlug = (network: LedgerNetwork) => network.slug.concat(network.isRecovery ? RECOVERY_SLUG : ''); From 15c3560d3c9eb38d8af46112bfacfa658e67aeb2 Mon Sep 17 00:00:00 2001 From: PDTnhah Date: Wed, 25 Dec 2024 18:11:07 +0700 Subject: [PATCH 20/41] [Issue-3915] Fix lint --- .../extension-koni-ui/src/Popup/Account/ConnectLedger.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx b/packages/extension-koni-ui/src/Popup/Account/ConnectLedger.tsx index c775661bcc..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'; @@ -18,8 +20,6 @@ import { useSelector } from 'react-redux'; import styled from 'styled-components'; import DefaultLogosMap from '../../assets/logo'; -import { LedgerChainSelector, LedgerItemType } from '@subwallet/extension-koni-ui/components/Field/LedgerChainSelector'; -import { convertNetworkSlug } from '@subwallet/extension-koni-ui/utils'; type Props = ThemeProps; From 3777265833b635bfc5e9563f16fe1433d25ee6c5 Mon Sep 17 00:00:00 2001 From: tunghp2002 Date: Thu, 26 Dec 2024 10:03:38 +0700 Subject: [PATCH 21/41] [Update] Change UI for POS -> ETH bridge --- .../balance-service/transfer/xcm/posBridge.ts | 10 ++++++++ .../Popup/Transaction/variants/SendFund.tsx | 18 +++++++++++++-- .../src/components/Field/AddressInputNew.tsx | 2 ++ .../Popup/Transaction/variants/SendFund.tsx | 23 +++++++++++++++++-- .../src/components/Field/AddressInput.tsx | 1 + 5 files changed, 50 insertions(+), 4 deletions(-) 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 index 186b8f996b..4b64c45437 100644 --- a/packages/extension-base/src/services/balance-service/transfer/xcm/posBridge.ts +++ b/packages/extension-base/src/services/balance-service/transfer/xcm/posBridge.ts @@ -168,3 +168,13 @@ export function _isPosChainBridge (srcChain: string, destChain: string): boolean 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-koni-ui/src/Popup/Transaction/variants/SendFund.tsx b/packages/extension-koni-ui/src/Popup/Transaction/variants/SendFund.tsx index 912e427020..4f1fa1f8d0 100644 --- a/packages/extension-koni-ui/src/Popup/Transaction/variants/SendFund.tsx +++ b/packages/extension-koni-ui/src/Popup/Transaction/variants/SendFund.tsx @@ -10,7 +10,7 @@ import { ActionType } from '@subwallet/extension-base/core/types'; import { getAvailBridgeGatewayContract, getSnowBridgeGatewayContract } from '@subwallet/extension-base/koni/api/contract-handler/utils'; 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 { _isPosChainBridge, _isPosChainL2Bridge } from '@subwallet/extension-base/services/balance-service/transfer/xcm/posBridge'; import { _getAssetDecimals, _getAssetName, _getAssetOriginChain, _getAssetSymbol, _getContractAddressOfToken, _getMultiChainAsset, _getOriginChainOfAsset, _getTokenMinAmount, _isChainEvmCompatible, _isNativeToken, _isTokenTransferredByEvm } from '@subwallet/extension-base/services/chain-service/utils'; import { TON_CHAINS } from '@subwallet/extension-base/services/earning-service/constants'; import { SWTransactionResponse } from '@subwallet/extension-base/services/transaction-service/types'; @@ -180,6 +180,14 @@ const Component = ({ className = '', isAllAccount, targetAccountProxy }: Compone return !!chainInfo && !!assetInfo && _isChainEvmCompatible(chainInfo) && destChainValue === chainValue && _isNativeToken(assetInfo); }, [chainInfoMap, chainValue, destChainValue, assetInfo]); + const disabledToAddressInput = useMemo(() => { + if (_isPosChainL2Bridge(chainValue, destChainValue)) { + return true; + } + + return false; + }, [chainValue, destChainValue]); + const [loading, setLoading] = useState(false); const [isTransferAll, setIsTransferAll] = useState(false); @@ -378,6 +386,11 @@ const Component = ({ className = '', isAllAccount, targetAccountProxy }: Compone } if (part.from || part.destChain) { + if (disabledToAddressInput) { + form.resetFields(['to']); + form.setFieldValue('to', values.from); + } + setForceUpdateMaxValue(isTransferAll ? {} : undefined); } @@ -396,7 +409,7 @@ const Component = ({ className = '', isAllAccount, targetAccountProxy }: Compone persistData(form.getFieldsValue()); }, - [form, assetRegistry, isTransferAll, persistData] + [persistData, form, assetRegistry, disabledToAddressInput, isTransferAll] ); const isShowWarningOnSubmit = useCallback((values: TransferParams): boolean => { @@ -883,6 +896,7 @@ const Component = ({ className = '', isAllAccount, targetAccountProxy }: Compone > ): React.Rea <>
): React.Rea showAddressBook && (