@subwallet/extension-base 1.3.67-0 → 1.3.68-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/background/KoniTypes.d.ts +12 -1
- package/cjs/constants/environment.js +1 -3
- package/cjs/constants/index.js +4 -1
- package/cjs/core/substrate/system-pallet.js +4 -0
- package/cjs/koni/api/nft/rari/index.js +1 -1
- package/cjs/koni/background/cron.js +16 -0
- package/cjs/koni/background/handlers/Extension.js +165 -89
- package/cjs/koni/background/handlers/State.js +25 -0
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/helpers/group.js +31 -2
- package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +51 -13
- package/cjs/services/balance-service/helpers/subscribe/substrate/utils.js +69 -0
- package/cjs/services/balance-service/index.js +36 -11
- package/cjs/services/balance-service/transfer/smart-contract.js +2 -0
- package/cjs/services/chain-service/constants.js +8 -46
- package/cjs/services/chain-service/handler/EvmChainHandler.js +6 -3
- package/cjs/services/earning-service/handlers/base.js +7 -1
- package/cjs/services/nft-service/index.js +173 -0
- package/cjs/services/transaction-service/index.js +1 -1
- package/cjs/types/balance/index.js +26 -1
- package/cjs/utils/index.js +25 -2
- package/cjs/utils/setup-api-sdk.js +0 -5
- package/constants/environment.d.ts +0 -1
- package/constants/environment.js +0 -1
- package/constants/index.d.ts +1 -0
- package/constants/index.js +1 -0
- package/core/substrate/system-pallet.d.ts +1 -0
- package/core/substrate/system-pallet.js +3 -0
- package/core/substrate/types.d.ts +14 -0
- package/koni/api/nft/rari/index.js +1 -1
- package/koni/background/cron.d.ts +1 -0
- package/koni/background/cron.js +17 -1
- package/koni/background/handlers/Extension.d.ts +3 -0
- package/koni/background/handlers/Extension.js +87 -13
- package/koni/background/handlers/State.d.ts +4 -0
- package/koni/background/handlers/State.js +25 -0
- package/package.json +16 -6
- package/packageInfo.js +1 -1
- package/services/balance-service/helpers/group.js +31 -2
- package/services/balance-service/helpers/subscribe/substrate/index.js +51 -13
- package/services/balance-service/helpers/subscribe/substrate/utils.d.ts +7 -0
- package/services/balance-service/helpers/subscribe/substrate/utils.js +58 -0
- package/services/balance-service/index.d.ts +4 -2
- package/services/balance-service/index.js +26 -6
- package/services/balance-service/transfer/smart-contract.js +2 -0
- package/services/chain-service/constants.d.ts +5 -24
- package/services/chain-service/constants.js +6 -35
- package/services/chain-service/handler/EvmChainHandler.js +6 -3
- package/services/earning-service/handlers/base.js +7 -1
- package/services/nft-service/index.d.ts +9 -0
- package/services/nft-service/index.js +165 -0
- package/services/transaction-service/index.js +1 -1
- package/services/transaction-service/types.d.ts +2 -1
- package/types/balance/index.d.ts +14 -0
- package/types/balance/index.js +21 -1
- package/utils/index.js +25 -2
- package/utils/setup-api-sdk.js +1 -6
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _types = require("@subwallet/chain-list/types");
|
|
9
|
+
var _utils = require("@subwallet/extension-base/services/chain-service/utils");
|
|
10
|
+
var _utils2 = require("@subwallet/extension-base/utils");
|
|
11
|
+
var _keyring = require("@subwallet/keyring");
|
|
12
|
+
var _types2 = require("@subwallet/keyring/types");
|
|
13
|
+
var _subwalletServicesSdk = _interopRequireDefault(require("@subwallet-monorepos/subwallet-services-sdk"));
|
|
14
|
+
// Copyright 2019-2022 @subwallet/extension-base authors & contributors
|
|
15
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
|
|
17
|
+
function mapSdkToNftItem(rawInstance, chain, collectionId, owner) {
|
|
18
|
+
var _rawInstance$token_ty, _rawInstance$token_ty2, _rawInstance$id;
|
|
19
|
+
const metadata = rawInstance.metadata || {};
|
|
20
|
+
const image = metadata.image || rawInstance.image_url || rawInstance.media_url || '';
|
|
21
|
+
const attributes = Array.isArray(metadata.attributes) ? metadata.attributes : [];
|
|
22
|
+
let rarity;
|
|
23
|
+
const properties = {};
|
|
24
|
+
for (const attr of attributes) {
|
|
25
|
+
try {
|
|
26
|
+
var _attr$trait_type;
|
|
27
|
+
const key = (_attr$trait_type = attr.trait_type) === null || _attr$trait_type === void 0 ? void 0 : _attr$trait_type.trim();
|
|
28
|
+
if (!key) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
let value = attr.value;
|
|
32
|
+
if (typeof value === 'string') {
|
|
33
|
+
const lower = value.toLowerCase();
|
|
34
|
+
if (lower === 'true') {
|
|
35
|
+
value = true;
|
|
36
|
+
} else if (lower === 'false') {
|
|
37
|
+
value = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
properties[key] = value;
|
|
41
|
+
if (key.toLowerCase() === 'rarity') {
|
|
42
|
+
rarity = String(value);
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
}
|
|
46
|
+
const hasProperties = Object.keys(properties).length > 0;
|
|
47
|
+
const normalizedType = (_rawInstance$token_ty = rawInstance.token_type) === null || _rawInstance$token_ty === void 0 ? void 0 : (_rawInstance$token_ty2 = _rawInstance$token_ty.replace('-', '')) === null || _rawInstance$token_ty2 === void 0 ? void 0 : _rawInstance$token_ty2.toUpperCase();
|
|
48
|
+
|
|
49
|
+
// Only support ERC721
|
|
50
|
+
if (normalizedType !== 'ERC721') {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
id: (_rawInstance$id = rawInstance.id) === null || _rawInstance$id === void 0 ? void 0 : _rawInstance$id.toString(),
|
|
55
|
+
chain,
|
|
56
|
+
collectionId,
|
|
57
|
+
owner: rawInstance.owner || owner,
|
|
58
|
+
originAsset: undefined,
|
|
59
|
+
name: metadata.name || `#${rawInstance.id}`,
|
|
60
|
+
image: (0, _utils2.baseParseIPFSUrl)(image),
|
|
61
|
+
externalUrl: rawInstance.external_app_url || undefined,
|
|
62
|
+
rarity,
|
|
63
|
+
description: metadata.description || undefined,
|
|
64
|
+
properties: hasProperties ? properties : null,
|
|
65
|
+
type: normalizedType === 'ERC721' ? _types._AssetType.ERC721 : _types._AssetType.ERC721,
|
|
66
|
+
// currently only support ERC721
|
|
67
|
+
rmrk_ver: undefined,
|
|
68
|
+
onChainOption: undefined,
|
|
69
|
+
assetHubType: undefined
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function mapSdkToCollection(raw, chain) {
|
|
73
|
+
var _raw$token_instances;
|
|
74
|
+
const token = raw.token || {};
|
|
75
|
+
return {
|
|
76
|
+
// must-have
|
|
77
|
+
collectionId: token.address_hash,
|
|
78
|
+
chain,
|
|
79
|
+
originAsset: undefined,
|
|
80
|
+
// optional
|
|
81
|
+
collectionName: token.name || token.symbol || 'Unknown Collection',
|
|
82
|
+
image: token.icon_url || undefined,
|
|
83
|
+
itemCount: Number(raw.amount) || ((_raw$token_instances = raw.token_instances) === null || _raw$token_instances === void 0 ? void 0 : _raw$token_instances.length) || 0,
|
|
84
|
+
externalUrl: undefined
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
class NftService {
|
|
88
|
+
inProgress = new Set();
|
|
89
|
+
constructor(state) {
|
|
90
|
+
this.state = state;
|
|
91
|
+
}
|
|
92
|
+
async fetchEvmCollectionsWithPreview(addresses) {
|
|
93
|
+
for (const address of addresses) {
|
|
94
|
+
const type = (0, _keyring.getKeypairTypeByAddress)(address);
|
|
95
|
+
const typeValid = [..._types2.EthereumKeypairTypes].includes(type);
|
|
96
|
+
if (typeValid) {
|
|
97
|
+
if (this.inProgress.has(address)) {
|
|
98
|
+
console.log(`[NftService] ${address} already running`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
this.inProgress.add(address);
|
|
102
|
+
try {
|
|
103
|
+
const nftDetectionApi = _subwalletServicesSdk.default.nftDetectionApi;
|
|
104
|
+
if (!(nftDetectionApi !== null && nftDetectionApi !== void 0 && nftDetectionApi.getEvmNftCollectionsByAddress)) {
|
|
105
|
+
console.warn('[NftService] NftDetectionApi not available');
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const rawData = await nftDetectionApi.getEvmNftCollectionsByAddress(address);
|
|
109
|
+
const allItems = [];
|
|
110
|
+
const allCollections = [];
|
|
111
|
+
for (const [chain, collections] of Object.entries(rawData)) {
|
|
112
|
+
if (!Array.isArray(collections)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
for (const col of collections) {
|
|
116
|
+
const mappedCollection = mapSdkToCollection(col, chain);
|
|
117
|
+
allCollections.push(mappedCollection);
|
|
118
|
+
if (Array.isArray(col.token_instances)) {
|
|
119
|
+
const items = col.token_instances.map(inst => mapSdkToNftItem(inst, chain, mappedCollection.collectionId, address)).filter(i => Boolean(i));
|
|
120
|
+
allItems.push(...items);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
await this.state.handleDetectedNftCollections(allCollections);
|
|
125
|
+
await this.state.handleDetectedNfts(address, allItems);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.warn(`[NftService] detect error for ${address}`, err);
|
|
128
|
+
} finally {
|
|
129
|
+
this.inProgress.delete(address);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async getFullNftInstancesByCollection(request) {
|
|
135
|
+
const {
|
|
136
|
+
chainInfo,
|
|
137
|
+
contractAddress,
|
|
138
|
+
owners
|
|
139
|
+
} = request;
|
|
140
|
+
const chainId = (0, _utils._getEvmChainId)(chainInfo);
|
|
141
|
+
if (!contractAddress || !owners || !chainId) {
|
|
142
|
+
console.warn('[NftService] missing params for getFullNftInstancesByCollection');
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const nftDetectionApi = _subwalletServicesSdk.default.nftDetectionApi;
|
|
147
|
+
if (!(nftDetectionApi !== null && nftDetectionApi !== void 0 && nftDetectionApi.getAllNftInstances)) {
|
|
148
|
+
console.warn('[NftService] getAllNftInstances not available');
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const ownerList = Array.isArray(owners) ? owners : [owners];
|
|
152
|
+
for (const eachOwner of ownerList) {
|
|
153
|
+
try {
|
|
154
|
+
const instances = await nftDetectionApi.getAllNftInstances(contractAddress, eachOwner, chainId.toString());
|
|
155
|
+
if (!Array.isArray(instances)) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
console.log('FOR TESTER (before)', instances);
|
|
159
|
+
const nftList = instances.map(inst => mapSdkToNftItem(inst, chainInfo.slug, contractAddress, eachOwner)).filter(i => Boolean(i));
|
|
160
|
+
console.log('FOR TESTER (after)', nftList);
|
|
161
|
+
await this.state.handleDetectedNfts(eachOwner, nftList);
|
|
162
|
+
} catch (innerErr) {
|
|
163
|
+
console.warn(`[NftService] getAllNftInstances failed for ${eachOwner}`, innerErr);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error(`[NftDetectionService] getFullNftInstancesByCollection error for ${contractAddress}`, err);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.default = NftService;
|
|
@@ -134,7 +134,7 @@ class TransactionService {
|
|
|
134
134
|
// Check account signing transaction
|
|
135
135
|
|
|
136
136
|
(0, _transfer.checkSigningAccountForTransaction)(validationResponse, chainInfoMap);
|
|
137
|
-
const nativeTokenAvailable = await this.state.balanceService.
|
|
137
|
+
const nativeTokenAvailable = await this.state.balanceService.getBalanceByType(address, chain, nativeTokenInfo.slug, transactionInput.balanceType, extrinsicType);
|
|
138
138
|
|
|
139
139
|
// Check available balance against transaction fee
|
|
140
140
|
(0, _transfer.checkBalanceWithTransactionFee)(validationResponse, transactionInput, nativeTokenInfo, nativeTokenAvailable);
|
|
@@ -1 +1,26 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BalanceType = void 0;
|
|
7
|
+
// Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors
|
|
8
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
9
|
+
let BalanceType;
|
|
10
|
+
/**
|
|
11
|
+
* Balance info of a token on an address
|
|
12
|
+
* @property {string} address - Address
|
|
13
|
+
* @property {string} tokenSlug - Slug of token
|
|
14
|
+
* @property {APIItemState} state - State of information
|
|
15
|
+
* @property {number} [timestamp] - Time to get information
|
|
16
|
+
* @property {string} free - Transferable balance
|
|
17
|
+
* @property {string} locked - Locked balance, cannot be transferred, locked here is only meaningful in the context of token transfer
|
|
18
|
+
* @property {metadata} [metadata] - Could be anything, supposed to be generic to handle various contexts
|
|
19
|
+
*/
|
|
20
|
+
exports.BalanceType = BalanceType;
|
|
21
|
+
(function (BalanceType) {
|
|
22
|
+
BalanceType["TRANSFERABLE"] = "transferable";
|
|
23
|
+
BalanceType["TOTAL"] = "total";
|
|
24
|
+
BalanceType["TOTAL_MINUS_RESERVED"] = "totalMinusReserved";
|
|
25
|
+
BalanceType["KEEP_ALIVE"] = "keepAlive";
|
|
26
|
+
})(BalanceType || (exports.BalanceType = BalanceType = {}));
|
package/cjs/utils/index.js
CHANGED
|
@@ -632,13 +632,36 @@ const stripUrl = url => {
|
|
|
632
632
|
};
|
|
633
633
|
exports.stripUrl = stripUrl;
|
|
634
634
|
const baseParseIPFSUrl = (input, customDomain) => {
|
|
635
|
-
const selectedDomain = customDomain || (0, _config.getRandomIpfsGateway)();
|
|
636
635
|
if (!input || input.length === 0) {
|
|
637
636
|
return undefined;
|
|
638
637
|
}
|
|
639
|
-
|
|
638
|
+
|
|
639
|
+
// Case 1: Return as-is for inline data URLs (e.g. base64-encoded images)
|
|
640
|
+
if (input.startsWith('data:')) {
|
|
640
641
|
return input;
|
|
641
642
|
}
|
|
643
|
+
const selectedDomain = customDomain || (0, _config.getRandomIpfsGateway)();
|
|
644
|
+
|
|
645
|
+
// Case 2: Replace Pinata private gateways with a public IPFS gateway
|
|
646
|
+
// ==== EX: https://ikzttp.mypinata.cloud/ipfs/QmYDvPAXtiJg7s8JdRBSLWdgSphQdac8j1YuQNNxcGE1hg/9586.png
|
|
647
|
+
// Case 2b: Blockscout IPFS debug gateway -> rewrite to public gateway
|
|
648
|
+
// ==== EX: http://ipfs-debug.node.blockscout.com/ipfs/QmX2qHy1o27KgmHJSG2wKj2qLiv1gMCJCTn4nxzEVtTdgF
|
|
649
|
+
// Case 2c: http://ipfs.node.blockscout.com/ipfs/QmeTETrnJcG3iowfT3tXtz2jKmyeYbeag3AeYfDk5pBjGg
|
|
650
|
+
|
|
651
|
+
const privateGatewayPattern = /https?:\/\/([^/]*pinata\.cloud|[^/]*\.node\.blockscout\.com)\/ipfs\//;
|
|
652
|
+
if (privateGatewayPattern.test(input)) {
|
|
653
|
+
return input.replace(privateGatewayPattern, selectedDomain);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Case 3: Handle NFT.storage subdomain links (e.g. https://<cid>.ipfs.nftstorage.link/...)
|
|
657
|
+
// Always redirect to selectedDomain to avoid SSL version/cipher mismatch errors
|
|
658
|
+
// ==== EX: https://bafybeias6as7k66hkghst3w4jwk6x5dkfk56oglqh44x6jmok6n7kcvg7m.ipfs.nftstorage.link/0.gif?ext=gif
|
|
659
|
+
const nftStorageMatch = input.match(/^https?:\/\/([a-zA-Z0-9]+)\.ipfs\.nftstorage\.link\/(.*)$/);
|
|
660
|
+
if (nftStorageMatch) {
|
|
661
|
+
const cid = nftStorageMatch[1];
|
|
662
|
+
const pathAndQuery = nftStorageMatch[2] || '';
|
|
663
|
+
return `${selectedDomain}${cid}/${pathAndQuery}`;
|
|
664
|
+
}
|
|
642
665
|
if (isUrl(input) || input.includes('https://') || input.includes('http')) {
|
|
643
666
|
return input;
|
|
644
667
|
}
|
|
@@ -19,9 +19,4 @@ function setupApiSDK() {
|
|
|
19
19
|
platform: _environment.TARGET_ENV,
|
|
20
20
|
chainListVersion: CHAIN_LIST_VERSION
|
|
21
21
|
});
|
|
22
|
-
|
|
23
|
-
// Custom the price history API with other different base URL
|
|
24
|
-
_subwalletServicesSdk.default.priceHistoryApi.updateConfig({
|
|
25
|
-
baseUrl: _constants.BACKEND_PRICE_HISTORY_URL
|
|
26
|
-
});
|
|
27
22
|
}
|
package/constants/environment.js
CHANGED
|
@@ -6,5 +6,4 @@ const branchName = process.env.BRANCH_NAME || 'subwallet-dev';
|
|
|
6
6
|
export const APP_VERSION = process.env.PKG_VERSION || '';
|
|
7
7
|
export const isProductionMode = PRODUCTION_BRANCHES.indexOf(branchName) > -1;
|
|
8
8
|
export const BACKEND_API_URL = process.env.SUBWALLET_API || (isProductionMode ? 'https://sw-services.subwallet.app/api' : 'https://be-dev.subwallet.app/api');
|
|
9
|
-
export const BACKEND_PRICE_HISTORY_URL = process.env.SUBWALLET_PRICE_HISTORY_API || (isProductionMode ? 'https://price-history.subwallet.app/api' : 'https://price-history-dev.subwallet.app/api');
|
|
10
9
|
export const SW_EXTERNAL_SERVICES_API = process.env.SW_EXTERNAL_SERVICES_API || (isProductionMode ? 'https://external-services.subwallet.app' : 'https://external-services-dev.subwallet.app');
|
package/constants/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL = 90000;
|
|
|
15
15
|
export declare const CRON_REFRESH_HISTORY_INTERVAL = 900000;
|
|
16
16
|
export declare const CRON_GET_API_MAP_STATUS = 10000;
|
|
17
17
|
export declare const CRON_REFRESH_CHAIN_STAKING_METADATA = 90000;
|
|
18
|
+
export declare const CRON_NFT_DETECT_INTERVAL = 7500000;
|
|
18
19
|
export declare const CRON_REFRESH_CHAIN_NOMINATOR_METADATA = 1800000;
|
|
19
20
|
export declare const CRON_RECOVER_HISTORY_INTERVAL = 30000;
|
|
20
21
|
export declare const CRON_SYNC_MANTA_PAY = 300000;
|
package/constants/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export const CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL = 90000;
|
|
|
17
17
|
export const CRON_REFRESH_HISTORY_INTERVAL = 900000;
|
|
18
18
|
export const CRON_GET_API_MAP_STATUS = 10000;
|
|
19
19
|
export const CRON_REFRESH_CHAIN_STAKING_METADATA = 90000;
|
|
20
|
+
export const CRON_NFT_DETECT_INTERVAL = 7500000;
|
|
20
21
|
export const CRON_REFRESH_CHAIN_NOMINATOR_METADATA = 1800000;
|
|
21
22
|
export const CRON_RECOVER_HISTORY_INTERVAL = 30000;
|
|
22
23
|
export const CRON_SYNC_MANTA_PAY = 300000;
|
|
@@ -5,3 +5,4 @@ export declare function _canAccountBeReaped(accountInfo: FrameSystemAccountInfo)
|
|
|
5
5
|
export declare function _isAccountActive(accountInfo: FrameSystemAccountInfo): boolean;
|
|
6
6
|
export declare function _getSystemPalletTotalBalance(accountInfo: FrameSystemAccountInfo): bigint;
|
|
7
7
|
export declare function _getAppliedExistentialDepositWithExtrinsicType(accountInfo: FrameSystemAccountInfo, existentialDeposit: string, extrinsicType?: ExtrinsicType): bigint;
|
|
8
|
+
export declare function _getSystemPalletReservedBalance(accountInfo: FrameSystemAccountInfo): bigint;
|
|
@@ -64,4 +64,7 @@ function _getSystemPalletTransferableV1(accountInfo, existentialDeposit, strictM
|
|
|
64
64
|
}
|
|
65
65
|
function _getSystemPalletTotalBalanceV1(accountInfo) {
|
|
66
66
|
return BigInt(accountInfo.data.free) + BigInt(accountInfo.data.reserved);
|
|
67
|
+
}
|
|
68
|
+
export function _getSystemPalletReservedBalance(accountInfo) {
|
|
69
|
+
return BigInt(accountInfo.data.reserved);
|
|
67
70
|
}
|
|
@@ -18,6 +18,20 @@ export declare type FrameSystemAccountInfoV1 = {
|
|
|
18
18
|
feeFrozen: number;
|
|
19
19
|
};
|
|
20
20
|
};
|
|
21
|
+
export interface FrameBalancesLocksInfo {
|
|
22
|
+
id: string | Record<string, unknown>;
|
|
23
|
+
amount: string | number | bigint;
|
|
24
|
+
reasons?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface FrameBalancesHoldsInfo {
|
|
27
|
+
id: string | Record<string, unknown>;
|
|
28
|
+
amount: string | number | bigint;
|
|
29
|
+
reason?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface FrameBalancesFreezesInfo {
|
|
32
|
+
id: string | Record<string, unknown>;
|
|
33
|
+
amount: string;
|
|
34
|
+
}
|
|
21
35
|
export declare type FrameSystemAccountInfo = FrameSystemAccountInfoV1 | FrameSystemAccountInfoV2;
|
|
22
36
|
export declare type OrmlTokensAccountData = {
|
|
23
37
|
free: number;
|
|
@@ -29,6 +29,7 @@ export declare class KoniCron {
|
|
|
29
29
|
refreshNft: (address: string, apiMap: ApiMap, smartContractNfts: _ChainAsset[], chainInfoMap: Record<string, _ChainInfo>) => () => void;
|
|
30
30
|
resetNft: (newAddress: string) => void;
|
|
31
31
|
checkNetworkAvailable: (serviceInfo: ServiceInfo) => boolean;
|
|
32
|
+
detectEvmCollectionNft: (address: string) => () => void;
|
|
32
33
|
reloadNft(): Promise<boolean>;
|
|
33
34
|
reloadStaking(): Promise<boolean>;
|
|
34
35
|
private needUpdateNft;
|
package/koni/background/cron.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright 2019-2022 @subwallet/extension-koni authors & contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import { CRON_REFRESH_CHAIN_STAKING_METADATA, CRON_REFRESH_MKT_CAMPAIGN_INTERVAL, CRON_REFRESH_NFT_INTERVAL, CRON_SYNC_MANTA_PAY } from '@subwallet/extension-base/constants';
|
|
4
|
+
import { CRON_NFT_DETECT_INTERVAL, CRON_REFRESH_CHAIN_STAKING_METADATA, CRON_REFRESH_MKT_CAMPAIGN_INTERVAL, CRON_REFRESH_NFT_INTERVAL, CRON_SYNC_MANTA_PAY } from '@subwallet/extension-base/constants';
|
|
5
5
|
import { _isChainSupportEvmNft, _isChainSupportNativeNft, _isChainSupportWasmNft } from '@subwallet/extension-base/services/chain-service/utils';
|
|
6
6
|
import { waitTimeout } from '@subwallet/extension-base/utils';
|
|
7
7
|
import { Subject } from 'rxjs';
|
|
@@ -88,6 +88,7 @@ export class KoniCron {
|
|
|
88
88
|
// NFT
|
|
89
89
|
(commonReload || needUpdateNft) && this.resetNft(address);
|
|
90
90
|
(commonReload || needUpdateNft) && this.removeCron('refreshNft');
|
|
91
|
+
(commonReload || needUpdateNft) && this.removeCron('detectNft');
|
|
91
92
|
commonReload && this.removeCron('refreshPoolingStakingReward');
|
|
92
93
|
if (mktCampaignNeedReload) {
|
|
93
94
|
this.removeCron('fetchMktCampaignData');
|
|
@@ -103,6 +104,7 @@ export class KoniCron {
|
|
|
103
104
|
if (this.checkNetworkAvailable(serviceInfo)) {
|
|
104
105
|
// only add cron jobs if there's at least 1 active network
|
|
105
106
|
(commonReload || needUpdateNft) && this.addCron('refreshNft', this.refreshNft(address, serviceInfo.chainApiMap, this.state.getSmartContractNfts(), this.state.getActiveChainInfoMap()), CRON_REFRESH_NFT_INTERVAL);
|
|
107
|
+
(commonReload || needUpdateNft) && this.addCron('detectNft', this.detectEvmCollectionNft(address), CRON_NFT_DETECT_INTERVAL);
|
|
106
108
|
reloadMantaPay && this.addCron('syncMantaPay', this.syncMantaPay, CRON_SYNC_MANTA_PAY);
|
|
107
109
|
}
|
|
108
110
|
};
|
|
@@ -115,6 +117,7 @@ export class KoniCron {
|
|
|
115
117
|
if (Object.keys(this.state.getSubstrateApiMap()).length !== 0 || Object.keys(this.state.getEvmApiMap()).length !== 0) {
|
|
116
118
|
this.resetNft(currentAccountInfo.proxyId);
|
|
117
119
|
this.addCron('refreshNft', this.refreshNft(currentAccountInfo.proxyId, this.state.getApiMap(), this.state.getSmartContractNfts(), this.state.getActiveChainInfoMap()), CRON_REFRESH_NFT_INTERVAL);
|
|
120
|
+
this.addCron('detectNft', this.detectEvmCollectionNft(currentAccountInfo.proxyId), CRON_NFT_DETECT_INTERVAL);
|
|
118
121
|
// this.addCron('refreshStakingReward', this.refreshStakingReward(currentAccountInfo.address), CRON_REFRESH_STAKING_REWARD_INTERVAL);
|
|
119
122
|
this.addCron('syncMantaPay', this.syncMantaPay, CRON_SYNC_MANTA_PAY);
|
|
120
123
|
}
|
|
@@ -164,12 +167,25 @@ export class KoniCron {
|
|
|
164
167
|
checkNetworkAvailable = serviceInfo => {
|
|
165
168
|
return Object.keys(serviceInfo.chainApiMap.substrate).length > 0 || Object.keys(serviceInfo.chainApiMap.evm).length > 0;
|
|
166
169
|
};
|
|
170
|
+
detectEvmCollectionNft = address => {
|
|
171
|
+
return () => {
|
|
172
|
+
let addresses = [];
|
|
173
|
+
addresses = this.state.keyringService.context.getDecodedAddresses();
|
|
174
|
+
if (!addresses.length) {
|
|
175
|
+
console.warn('[Cron] No decoded addresses found for ALL_ACCOUNT_KEY');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
this.state.nftDetectionService.fetchEvmCollectionsWithPreview(addresses).catch(err => console.warn(`[Cron] NFT detection failed for ${address}:`, err));
|
|
179
|
+
};
|
|
180
|
+
};
|
|
167
181
|
async reloadNft() {
|
|
168
182
|
const address = this.state.keyringService.context.currentAccount.proxyId;
|
|
169
183
|
const serviceInfo = this.state.getServiceInfo();
|
|
170
184
|
this.resetNft(address);
|
|
171
185
|
this.removeCron('refreshNft');
|
|
186
|
+
this.removeCron('detectNft');
|
|
172
187
|
this.addCron('refreshNft', this.refreshNft(address, serviceInfo.chainApiMap, this.state.getSmartContractNfts(), this.state.getActiveChainInfoMap()), CRON_REFRESH_NFT_INTERVAL);
|
|
188
|
+
this.addCron('detectNft', this.detectEvmCollectionNft(address), CRON_NFT_DETECT_INTERVAL);
|
|
173
189
|
await waitTimeout(1800);
|
|
174
190
|
return true;
|
|
175
191
|
}
|
|
@@ -116,6 +116,7 @@ export default class KoniExtension {
|
|
|
116
116
|
private subscribeNftCollection;
|
|
117
117
|
private getNft;
|
|
118
118
|
private subscribeNft;
|
|
119
|
+
private handleGetNftFullList;
|
|
119
120
|
private getStakingReward;
|
|
120
121
|
private subscribeStakingReward;
|
|
121
122
|
private getStaking;
|
|
@@ -145,10 +146,12 @@ export default class KoniExtension {
|
|
|
145
146
|
private deleteCustomAsset;
|
|
146
147
|
private validateCustomAsset;
|
|
147
148
|
private getAddressTransferableBalance;
|
|
149
|
+
private getAddressAvailableBalanceByType;
|
|
148
150
|
private getAddressTotalBalance;
|
|
149
151
|
private subscribeMaxTransferable;
|
|
150
152
|
private subscribeTransferableWhenConfirmation;
|
|
151
153
|
private subscribeAddressTransferableBalance;
|
|
154
|
+
private subscribeAddressAvailableBalanceByType;
|
|
152
155
|
private substrateNftSubmitTransaction;
|
|
153
156
|
private enableChains;
|
|
154
157
|
private accountsCreateExternalV2;
|
|
@@ -47,7 +47,7 @@ import { DEFAULT_AUTO_LOCK_TIME } from '@subwallet/extension-base/services/setti
|
|
|
47
47
|
import { isProposalExpired, isSupportWalletConnectChain, isSupportWalletConnectNamespace } from '@subwallet/extension-base/services/wallet-connect-service/helpers';
|
|
48
48
|
import { SWStorage } from '@subwallet/extension-base/storage';
|
|
49
49
|
import { AccountsStore } from '@subwallet/extension-base/stores';
|
|
50
|
-
import { AccountChainType, AccountSignMode, BasicTxErrorType, BasicTxWarningCode, CommonStepType, EarningProcessType, ProcessType, StakingTxErrorType, StepStatus, SwapFeeType, YieldPoolType, YieldStepType } from '@subwallet/extension-base/types';
|
|
50
|
+
import { AccountChainType, AccountSignMode, BalanceType, BasicTxErrorType, BasicTxWarningCode, CommonStepType, EarningProcessType, ProcessType, StakingTxErrorType, StepStatus, SwapFeeType, YieldPoolType, YieldStepType } from '@subwallet/extension-base/types';
|
|
51
51
|
import { _analyzeAddress, calculateMaxTransferable, combineAllAccountProxy, combineBitcoinFee, createPromiseHandler, createTransactionFromRLP, detectTransferTxType, filterUneconomicalUtxos, getAccountSignMode, getSizeInfo, getTransferableBitcoinUtxos, isSameAddress, isSubstrateEcdsaLedgerAssetSupported, MODULE_SUPPORT, reformatAddress, signatureToHex, transformAccounts, transformAddresses, uniqueStringArray } from '@subwallet/extension-base/utils';
|
|
52
52
|
import { parseContractInput, parseEvmRlp } from '@subwallet/extension-base/utils/eth/parseTransaction';
|
|
53
53
|
import { getId } from '@subwallet/extension-base/utils/getId';
|
|
@@ -1144,6 +1144,9 @@ export default class KoniExtension {
|
|
|
1144
1144
|
});
|
|
1145
1145
|
return this.getNft();
|
|
1146
1146
|
}
|
|
1147
|
+
async handleGetNftFullList(request) {
|
|
1148
|
+
return this.#koniState.nftDetectionService.getFullNftInstancesByCollection(request);
|
|
1149
|
+
}
|
|
1147
1150
|
getStakingReward() {
|
|
1148
1151
|
return new Promise((resolve, reject) => {
|
|
1149
1152
|
this.#koniState.getStakingReward(rs => {
|
|
@@ -2033,20 +2036,40 @@ export default class KoniExtension {
|
|
|
2033
2036
|
var _data$metadata;
|
|
2034
2037
|
const evmApi = this.#koniState.getEvmApi(data.originChain);
|
|
2035
2038
|
const contractAddress = (_data$metadata = data.metadata) === null || _data$metadata === void 0 ? void 0 : _data$metadata.contractAddress;
|
|
2036
|
-
|
|
2037
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
2038
2039
|
const tokenContract = new evmApi.api.eth.Contract(_ERC721_ABI, contractAddress);
|
|
2039
2040
|
try {
|
|
2040
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
2041
|
-
await tokenContract.methods.
|
|
2042
|
-
|
|
2043
|
-
} catch (err) {
|
|
2044
|
-
const error = err;
|
|
2045
|
-
if (error.message.includes('index out of bounds')) {
|
|
2046
|
-
return true;
|
|
2047
|
-
} else {
|
|
2041
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
|
2042
|
+
const supports1155 = await tokenContract.methods.supportsInterface('0xd9b67a26').call().catch(() => false);
|
|
2043
|
+
if (supports1155) {
|
|
2048
2044
|
return false;
|
|
2049
2045
|
}
|
|
2046
|
+
|
|
2047
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
|
|
2048
|
+
const supports721 = await tokenContract.methods.supportsInterface('0x80ac58cd').call().catch(() => false);
|
|
2049
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
|
|
2050
|
+
const supportsMetadata = await tokenContract.methods.supportsInterface('0x5b5e139f').call().catch(() => false);
|
|
2051
|
+
if (supports721 || supportsMetadata) {
|
|
2052
|
+
return true;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
2056
|
+
if (tokenContract.methods.ownerOf) {
|
|
2057
|
+
try {
|
|
2058
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
|
2059
|
+
await tokenContract.methods.ownerOf(1).call();
|
|
2060
|
+
return true;
|
|
2061
|
+
} catch (err) {
|
|
2062
|
+
var _err$message, _err$message2;
|
|
2063
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
|
2064
|
+
if ((_err$message = err.message) !== null && _err$message !== void 0 && _err$message.includes('nonexistent token') || (_err$message2 = err.message) !== null && _err$message2 !== void 0 && _err$message2.includes('invalid token ID')) {
|
|
2065
|
+
return true;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
return false;
|
|
2070
|
+
} catch (err) {
|
|
2071
|
+
const error = err;
|
|
2072
|
+
return error.message.includes('index out of bounds');
|
|
2050
2073
|
}
|
|
2051
2074
|
}
|
|
2052
2075
|
async upsertCustomToken(data) {
|
|
@@ -2102,6 +2125,21 @@ export default class KoniExtension {
|
|
|
2102
2125
|
}
|
|
2103
2126
|
return await this.#koniState.balanceService.getTransferableBalance(address, networkKey, token, extrinsicType);
|
|
2104
2127
|
}
|
|
2128
|
+
async getAddressAvailableBalanceByType({
|
|
2129
|
+
address,
|
|
2130
|
+
balanceType,
|
|
2131
|
+
extrinsicType,
|
|
2132
|
+
networkKey,
|
|
2133
|
+
token
|
|
2134
|
+
}) {
|
|
2135
|
+
if (token && _MANTA_ZK_CHAIN_GROUP.includes(networkKey)) {
|
|
2136
|
+
const tokenInfo = this.#koniState.chainService.getAssetBySlug(token);
|
|
2137
|
+
if (tokenInfo.symbol.startsWith(_ZK_ASSET_PREFIX)) {
|
|
2138
|
+
return await this.#koniState.getMantaPayZkBalance(address, tokenInfo);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
return await this.#koniState.balanceService.getBalanceByType(address, networkKey, token, balanceType, extrinsicType);
|
|
2142
|
+
}
|
|
2105
2143
|
async getAddressTotalBalance({
|
|
2106
2144
|
address,
|
|
2107
2145
|
extrinsicType,
|
|
@@ -2173,7 +2211,7 @@ export default class KoniExtension {
|
|
|
2173
2211
|
});
|
|
2174
2212
|
const [unsubBalance, freeBalance] = await (async () => {
|
|
2175
2213
|
try {
|
|
2176
|
-
return await this.#koniState.balanceService.subscribeBalance(address, chain, token,
|
|
2214
|
+
return await this.#koniState.balanceService.subscribeBalance(address, chain, token, BalanceType.TRANSFERABLE, extrinsicType, data => {
|
|
2177
2215
|
freeBalanceSubject.next(data); // Must be called after subscription
|
|
2178
2216
|
});
|
|
2179
2217
|
} catch (e) {
|
|
@@ -2311,7 +2349,7 @@ export default class KoniExtension {
|
|
|
2311
2349
|
convertData(freeBalance, fee, _feeOptions, feeCustom).then(cb).catch(console.error);
|
|
2312
2350
|
}
|
|
2313
2351
|
});
|
|
2314
|
-
const [unsubBalance, freeBalance] = await this.#koniState.balanceService.subscribeBalance(address, chain, token,
|
|
2352
|
+
const [unsubBalance, freeBalance] = await this.#koniState.balanceService.subscribeBalance(address, chain, token, BalanceType.TRANSFERABLE, ExtrinsicType.TRANSFER_BALANCE, data => {
|
|
2315
2353
|
freeBalanceSubject.next(data); // Must be called after subscription
|
|
2316
2354
|
});
|
|
2317
2355
|
|
|
@@ -2354,6 +2392,31 @@ export default class KoniExtension {
|
|
|
2354
2392
|
});
|
|
2355
2393
|
return convertData(currentFreeBalance);
|
|
2356
2394
|
}
|
|
2395
|
+
async subscribeAddressAvailableBalanceByType({
|
|
2396
|
+
address,
|
|
2397
|
+
balanceType,
|
|
2398
|
+
extrinsicType,
|
|
2399
|
+
networkKey,
|
|
2400
|
+
token
|
|
2401
|
+
}, id, port) {
|
|
2402
|
+
const cb = createSubscription(id, port);
|
|
2403
|
+
const convertData = data => {
|
|
2404
|
+
return {
|
|
2405
|
+
...data,
|
|
2406
|
+
id
|
|
2407
|
+
};
|
|
2408
|
+
};
|
|
2409
|
+
const _cb = data => {
|
|
2410
|
+
// eslint-disable-next-line node/no-callback-literal
|
|
2411
|
+
cb(convertData(data));
|
|
2412
|
+
};
|
|
2413
|
+
const [unsub, currentFreeBalance] = await this.#koniState.balanceService.subscribeBalanceByType(address, networkKey, token, balanceType, extrinsicType, _cb);
|
|
2414
|
+
this.createUnsubscriptionHandle(id, unsub);
|
|
2415
|
+
port.onDisconnect.addListener(() => {
|
|
2416
|
+
this.cancelSubscription(id);
|
|
2417
|
+
});
|
|
2418
|
+
return convertData(currentFreeBalance);
|
|
2419
|
+
}
|
|
2357
2420
|
async substrateNftSubmitTransaction(inputData) {
|
|
2358
2421
|
const {
|
|
2359
2422
|
params,
|
|
@@ -3799,6 +3862,10 @@ export default class KoniExtension {
|
|
|
3799
3862
|
});
|
|
3800
3863
|
}
|
|
3801
3864
|
}
|
|
3865
|
+
let balanceTypeForPool = BalanceType.TRANSFERABLE;
|
|
3866
|
+
if (!!poolHandler && [YieldPoolType.NATIVE_STAKING, YieldPoolType.NOMINATION_POOL].includes(poolHandler.type)) {
|
|
3867
|
+
balanceTypeForPool = BalanceType.TOTAL_MINUS_RESERVED;
|
|
3868
|
+
}
|
|
3802
3869
|
return await this.#koniState.transactionService.handleTransaction({
|
|
3803
3870
|
address,
|
|
3804
3871
|
chain: txChain,
|
|
@@ -3810,6 +3877,7 @@ export default class KoniExtension {
|
|
|
3810
3877
|
chainType,
|
|
3811
3878
|
resolveOnDone: !isLastStep,
|
|
3812
3879
|
transferNativeAmount,
|
|
3880
|
+
balanceType: balanceTypeForPool,
|
|
3813
3881
|
skipFeeValidation: isMintingStep && isPoolSupportAlternativeFee,
|
|
3814
3882
|
errorOnTimeOut,
|
|
3815
3883
|
...this.createPassConfirmationParams(isPassConfirmation),
|
|
@@ -4807,6 +4875,8 @@ export default class KoniExtension {
|
|
|
4807
4875
|
return await this.getNftCollection();
|
|
4808
4876
|
case 'pri(nftCollection.getSubscription)':
|
|
4809
4877
|
return await this.subscribeNftCollection(id, port);
|
|
4878
|
+
case 'pri(nft.getFullList)':
|
|
4879
|
+
return await this.handleGetNftFullList(request);
|
|
4810
4880
|
case 'pri(staking.getStaking)':
|
|
4811
4881
|
return this.getStaking();
|
|
4812
4882
|
case 'pri(staking.getSubscription)':
|
|
@@ -5015,6 +5085,10 @@ export default class KoniExtension {
|
|
|
5015
5085
|
return this.getAddressTransferableBalance(request);
|
|
5016
5086
|
case 'pri(freeBalance.subscribe)':
|
|
5017
5087
|
return this.subscribeAddressTransferableBalance(request, id, port);
|
|
5088
|
+
case 'pri(availableBalance.getBalanceByType)':
|
|
5089
|
+
return this.getAddressAvailableBalanceByType(request);
|
|
5090
|
+
case 'pri(availableBalance.subscribeBalanceByType)':
|
|
5091
|
+
return this.subscribeAddressAvailableBalanceByType(request, id, port);
|
|
5018
5092
|
case 'pri(subscription.cancel)':
|
|
5019
5093
|
return this.cancelSubscription(request);
|
|
5020
5094
|
case 'pri(chainService.recoverSubstrateApi)':
|
|
@@ -19,6 +19,7 @@ import { KeyringService } from '@subwallet/extension-base/services/keyring-servi
|
|
|
19
19
|
import MigrationService from '@subwallet/extension-base/services/migration-service';
|
|
20
20
|
import MintCampaignService from '@subwallet/extension-base/services/mint-campaign-service';
|
|
21
21
|
import MktCampaignService from '@subwallet/extension-base/services/mkt-campaign-service';
|
|
22
|
+
import NftService from '@subwallet/extension-base/services/nft-service';
|
|
22
23
|
import NotificationService from '@subwallet/extension-base/services/notification-service/NotificationService';
|
|
23
24
|
import { PriceService } from '@subwallet/extension-base/services/price-service';
|
|
24
25
|
import RequestService from '@subwallet/extension-base/services/request-service';
|
|
@@ -73,6 +74,7 @@ export default class KoniState {
|
|
|
73
74
|
readonly mintCampaignService: MintCampaignService;
|
|
74
75
|
readonly campaignService: CampaignService;
|
|
75
76
|
readonly mktCampaignService: MktCampaignService;
|
|
77
|
+
readonly nftDetectionService: NftService;
|
|
76
78
|
readonly buyService: BuyService;
|
|
77
79
|
readonly earningService: EarningService;
|
|
78
80
|
readonly feeService: FeeService;
|
|
@@ -133,6 +135,8 @@ export default class KoniState {
|
|
|
133
135
|
resetNft(newAddress: string): void;
|
|
134
136
|
updateNftData(network: string, nftData: NftItem, address: string, callback?: (nftData: NftItem) => void): void;
|
|
135
137
|
deleteNftCollection(chain: string, collectionId: string): Promise<void>;
|
|
138
|
+
handleDetectedNfts(address: string, nftItems: NftItem[]): Promise<void>;
|
|
139
|
+
handleDetectedNftCollections(collections: NftCollection[]): Promise<void>;
|
|
136
140
|
cleanUpNfts(chain: string, owner: string, collectionId: string[], nftIds: string[], ownNothing?: boolean): void;
|
|
137
141
|
getNft(): Promise<NftJson | undefined>;
|
|
138
142
|
subscribeNft(): Subject<NftJson>;
|