@subwallet/extension-base 1.3.78-0 → 1.3.80-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 +16 -6
- package/cjs/core/logic-validation/index.js +0 -12
- package/cjs/core/substrate/system-pallet.js +3 -0
- package/cjs/koni/api/nft/index.js +0 -14
- package/cjs/koni/background/cron.js +0 -17
- package/cjs/koni/background/handlers/Extension.js +13 -7
- package/cjs/koni/background/handlers/State.js +24 -6
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/helpers/process.js +49 -21
- package/cjs/services/balance-service/index.js +2 -2
- package/cjs/services/balance-service/transfer/xcm/utils.js +9 -9
- package/cjs/services/chain-service/constants.js +6 -2
- package/cjs/services/earning-service/handlers/native-staking/dtao.js +13 -13
- package/cjs/services/earning-service/handlers/native-staking/tao.js +16 -10
- package/cjs/services/earning-service/handlers/special.js +12 -4
- package/cjs/services/nft-service/index.js +219 -150
- package/cjs/services/nft-service/multi-chain-nft-fetcher.js +145 -0
- package/cjs/services/nft-service/nft-handlers/base-nft-handler.js +28 -0
- package/cjs/services/nft-service/nft-handlers/evm/evm-nft-handler.js +179 -0
- package/cjs/services/nft-service/nft-handlers/registry.js +37 -0
- package/cjs/services/nft-service/nft-handlers/unique/unique-nft-handler.js +187 -0
- package/cjs/services/storage-service/DatabaseService.js +1 -1
- package/cjs/services/swap-service/handler/asset-hub/handler.js +7 -4
- package/cjs/services/swap-service/handler/asset-hub/router.js +2 -66
- package/cjs/services/swap-service/handler/base-handler.js +4 -3
- package/cjs/services/swap-service/handler/hydradx-handler.js +9 -5
- package/cjs/services/swap-service/index.js +5 -4
- package/cjs/types/swap/index.js +4 -9
- package/cjs/utils/account/common.js +44 -8
- package/core/logic-validation/index.d.ts +0 -1
- package/core/logic-validation/index.js +0 -1
- package/core/substrate/system-pallet.js +3 -0
- package/koni/api/nft/index.js +1 -15
- package/koni/background/cron.d.ts +0 -1
- package/koni/background/cron.js +1 -18
- package/koni/background/handlers/Extension.d.ts +4 -0
- package/koni/background/handlers/Extension.js +13 -7
- package/koni/background/handlers/State.d.ts +8 -2
- package/koni/background/handlers/State.js +24 -6
- package/package.json +31 -11
- package/packageInfo.js +1 -1
- package/services/balance-service/helpers/process.d.ts +1 -1
- package/services/balance-service/helpers/process.js +48 -21
- package/services/balance-service/index.js +2 -2
- package/services/balance-service/transfer/xcm/utils.js +9 -9
- package/services/chain-service/constants.d.ts +3 -0
- package/services/chain-service/constants.js +3 -0
- package/services/earning-service/handlers/native-staking/dtao.js +12 -13
- package/services/earning-service/handlers/native-staking/tao.d.ts +2 -1
- package/services/earning-service/handlers/native-staking/tao.js +15 -10
- package/services/earning-service/handlers/special.js +13 -5
- package/services/nft-service/index.d.ts +42 -6
- package/services/nft-service/index.js +219 -151
- package/services/nft-service/multi-chain-nft-fetcher.d.ts +13 -0
- package/services/nft-service/multi-chain-nft-fetcher.js +138 -0
- package/services/nft-service/nft-handlers/base-nft-handler.d.ts +13 -0
- package/services/nft-service/nft-handlers/base-nft-handler.js +21 -0
- package/services/nft-service/nft-handlers/evm/evm-nft-handler.d.ts +9 -0
- package/services/nft-service/nft-handlers/evm/evm-nft-handler.js +171 -0
- package/services/nft-service/nft-handlers/registry.d.ts +11 -0
- package/services/nft-service/nft-handlers/registry.js +29 -0
- package/services/nft-service/nft-handlers/unique/unique-nft-handler.d.ts +12 -0
- package/services/nft-service/nft-handlers/unique/unique-nft-handler.js +177 -0
- package/services/storage-service/DatabaseService.d.ts +1 -1
- package/services/storage-service/DatabaseService.js +1 -1
- package/services/swap-service/handler/asset-hub/handler.js +7 -4
- package/services/swap-service/handler/asset-hub/router.d.ts +0 -4
- package/services/swap-service/handler/asset-hub/router.js +1 -64
- package/services/swap-service/handler/base-handler.js +4 -3
- package/services/swap-service/handler/hydradx-handler.js +9 -5
- package/services/swap-service/index.js +5 -4
- package/types/swap/index.d.ts +7 -35
- package/types/swap/index.js +3 -8
- package/types/yield/actions/join/step.d.ts +1 -0
- package/types/yield/actions/join/submit.d.ts +2 -1
- package/utils/account/common.d.ts +22 -1
- package/utils/account/common.js +44 -8
- package/cjs/core/logic-validation/swap.js +0 -235
- package/core/logic-validation/swap.d.ts +0 -26
- package/core/logic-validation/swap.js +0 -219
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Copyright 2019-2022 @subwallet/extension-koni authors & contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { _AssetType } from '@subwallet/chain-list/types';
|
|
5
|
+
import { ChainType } from '@subwallet/extension-base/background/KoniTypes';
|
|
6
|
+
import { _getEvmChainId } from '@subwallet/extension-base/services/chain-service/utils';
|
|
7
|
+
import { baseParseIPFSUrl, getAddressesByChainType } from '@subwallet/extension-base/utils';
|
|
8
|
+
import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';
|
|
9
|
+
import { BaseNftHandler } from "../base-nft-handler.js";
|
|
10
|
+
export class EvmNftHandler extends BaseNftHandler {
|
|
11
|
+
filterAddresses(addresses) {
|
|
12
|
+
return getAddressesByChainType(addresses, [ChainType.EVM]);
|
|
13
|
+
}
|
|
14
|
+
mapSdkToNftItem(rawInstance, collectionId, owner) {
|
|
15
|
+
var _rawInstance$token_ty, _rawInstance$id;
|
|
16
|
+
const metadata = rawInstance.metadata || {};
|
|
17
|
+
const image = metadata.image || rawInstance.image_url || rawInstance.media_url || '';
|
|
18
|
+
const attributes = Array.isArray(metadata.attributes) ? metadata.attributes : [];
|
|
19
|
+
let rarity;
|
|
20
|
+
const properties = {};
|
|
21
|
+
for (const attr of attributes) {
|
|
22
|
+
try {
|
|
23
|
+
var _attr$trait_type;
|
|
24
|
+
const key = (_attr$trait_type = attr.trait_type) === null || _attr$trait_type === void 0 ? void 0 : _attr$trait_type.trim();
|
|
25
|
+
if (!key) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
let value = attr.value;
|
|
29
|
+
if (typeof value === 'string') {
|
|
30
|
+
const lower = value.toLowerCase();
|
|
31
|
+
if (lower === 'true') {
|
|
32
|
+
value = true;
|
|
33
|
+
}
|
|
34
|
+
if (lower === 'false') {
|
|
35
|
+
value = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
properties[key] = value;
|
|
39
|
+
if (key.toLowerCase() === 'rarity') {
|
|
40
|
+
rarity = String(value);
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// ignore
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const normalizedType = (_rawInstance$token_ty = rawInstance.token_type) === null || _rawInstance$token_ty === void 0 ? void 0 : _rawInstance$token_ty.replace('-', '').toUpperCase();
|
|
47
|
+
if (!['ERC721'].includes(normalizedType)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
id: ((_rawInstance$id = rawInstance.id) === null || _rawInstance$id === void 0 ? void 0 : _rawInstance$id.toString()) || '',
|
|
52
|
+
chain: this.chain,
|
|
53
|
+
collectionId,
|
|
54
|
+
owner: rawInstance.owner || owner,
|
|
55
|
+
name: metadata.name || `#${rawInstance.id}`,
|
|
56
|
+
image: baseParseIPFSUrl(image),
|
|
57
|
+
externalUrl: rawInstance.external_app_url || undefined,
|
|
58
|
+
rarity,
|
|
59
|
+
description: metadata.description || undefined,
|
|
60
|
+
properties: Object.keys(properties).length > 0 ? properties : null,
|
|
61
|
+
type: normalizedType === 'ERC721' ? _AssetType.ERC721 : _AssetType.ERC721,
|
|
62
|
+
originAsset: undefined,
|
|
63
|
+
rmrk_ver: undefined,
|
|
64
|
+
onChainOption: undefined,
|
|
65
|
+
assetHubType: undefined
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
mapSdkToCollection(raw) {
|
|
69
|
+
var _raw$token_instances;
|
|
70
|
+
const token = raw.token || {};
|
|
71
|
+
return {
|
|
72
|
+
collectionId: token.address_hash,
|
|
73
|
+
chain: this.chain,
|
|
74
|
+
collectionName: token.name || token.symbol || 'Unknown Collection',
|
|
75
|
+
image: token.icon_url || undefined,
|
|
76
|
+
itemCount: Number(raw.amount) || ((_raw$token_instances = raw.token_instances) === null || _raw$token_instances === void 0 ? void 0 : _raw$token_instances.length) || 0,
|
|
77
|
+
externalUrl: undefined,
|
|
78
|
+
originAsset: undefined
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ==================== 1. PREVIEW – Used for cron / background ====================
|
|
83
|
+
async fetchPreview(addresses) {
|
|
84
|
+
const items = [];
|
|
85
|
+
const collections = [];
|
|
86
|
+
const api = subwalletApiSdk.nftDetectionApi;
|
|
87
|
+
if (!(api !== null && api !== void 0 && api.getEvmNftCollectionsByAddress)) {
|
|
88
|
+
console.warn('[EvmNftHandler] Preview API not available');
|
|
89
|
+
return {
|
|
90
|
+
items,
|
|
91
|
+
collections
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
for (const address of addresses) {
|
|
95
|
+
try {
|
|
96
|
+
const rawData = await api.getEvmNftCollectionsByAddress(address);
|
|
97
|
+
for (const [chain, sdkCollections] of Object.entries(rawData)) {
|
|
98
|
+
if (chain !== this.chain || !Array.isArray(sdkCollections)) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const col of sdkCollections) {
|
|
102
|
+
const collection = this.mapSdkToCollection(col);
|
|
103
|
+
collections.push(collection);
|
|
104
|
+
if (Array.isArray(col.token_instances)) {
|
|
105
|
+
const mapped = col.token_instances.map(inst => this.mapSdkToNftItem(inst, collection.collectionId, address)).filter(i => i !== null);
|
|
106
|
+
items.push(...mapped);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(`[EvmNftHandler] Preview failed for ${address}`, error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
items: items,
|
|
116
|
+
collections: collections
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ==================== 2. FULL – At collection details screen ====================
|
|
121
|
+
async fetchFullListNftOfACollection(request) {
|
|
122
|
+
const items = [];
|
|
123
|
+
const collections = [];
|
|
124
|
+
const {
|
|
125
|
+
chainInfo,
|
|
126
|
+
collectionId,
|
|
127
|
+
owners
|
|
128
|
+
} = request;
|
|
129
|
+
const chainId = _getEvmChainId(chainInfo);
|
|
130
|
+
if (!collectionId || !owners || !chainId) {
|
|
131
|
+
console.warn('[NftService] missing params for getFullNftInstancesByCollection');
|
|
132
|
+
return {
|
|
133
|
+
items,
|
|
134
|
+
collections
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const nftDetectionApi = subwalletApiSdk.nftDetectionApi;
|
|
139
|
+
if (!(nftDetectionApi !== null && nftDetectionApi !== void 0 && nftDetectionApi.getAllNftInstances)) {
|
|
140
|
+
console.warn('[NftService] getAllNftInstances not available');
|
|
141
|
+
return {
|
|
142
|
+
items,
|
|
143
|
+
collections
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const ownerList = Array.isArray(owners) ? owners : [owners];
|
|
147
|
+
for (const eachOwner of ownerList) {
|
|
148
|
+
try {
|
|
149
|
+
const instances = await nftDetectionApi.getAllNftInstances(collectionId, eachOwner, chainId.toString());
|
|
150
|
+
if (!Array.isArray(instances)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const mappedItems = instances.map(inst => this.mapSdkToNftItem(inst, collectionId, eachOwner)).filter(i => Boolean(i));
|
|
154
|
+
items.push(...mappedItems);
|
|
155
|
+
} catch (innerErr) {
|
|
156
|
+
console.warn(`[NftService] getAllNftInstances failed for ${eachOwner}`, innerErr);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.error(`[NftDetectionService] getFullNftInstancesByCollection error for ${collectionId}`, err);
|
|
161
|
+
return {
|
|
162
|
+
items: [],
|
|
163
|
+
collections: []
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
items: items,
|
|
168
|
+
collections: collections
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { _ChainInfo } from '@subwallet/chain-list/types';
|
|
2
|
+
import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
|
|
3
|
+
import { BaseNftHandler } from '@subwallet/extension-base/services/nft-service/nft-handlers/base-nft-handler';
|
|
4
|
+
export interface NftHandlerDescriptor {
|
|
5
|
+
id: string;
|
|
6
|
+
supports(chainInfo: _ChainInfo): boolean;
|
|
7
|
+
supportsFetchFullNftList: boolean;
|
|
8
|
+
create(chainSlug: string, state: KoniState): BaseNftHandler;
|
|
9
|
+
}
|
|
10
|
+
export declare function isNftV2Chain(chainInfo: _ChainInfo, registry: NftHandlerDescriptor[]): boolean;
|
|
11
|
+
export declare const NFT_HANDLER_REGISTRY: NftHandlerDescriptor[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright 2019-2022 @subwallet/extension-base authors & contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { NFT_CHAIN_GROUPS_MIGRATED } from '@subwallet/extension-base/services/chain-service/constants';
|
|
5
|
+
import { _isChainSupportEvmNft } from '@subwallet/extension-base/services/chain-service/utils';
|
|
6
|
+
import { EvmNftHandler } from '@subwallet/extension-base/services/nft-service/nft-handlers/evm/evm-nft-handler';
|
|
7
|
+
import { UniqueNftHandler } from '@subwallet/extension-base/services/nft-service/nft-handlers/unique/unique-nft-handler';
|
|
8
|
+
export function isNftV2Chain(chainInfo, registry) {
|
|
9
|
+
return registry.some(desc => desc.supports(chainInfo));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// todo list: only update into when migration
|
|
13
|
+
export const NFT_HANDLER_REGISTRY = [{
|
|
14
|
+
id: 'evm',
|
|
15
|
+
supports: chainInfo => _isChainSupportEvmNft(chainInfo),
|
|
16
|
+
supportsFetchFullNftList: true,
|
|
17
|
+
create: chain => new EvmNftHandler(chain)
|
|
18
|
+
}, {
|
|
19
|
+
id: 'unique_network',
|
|
20
|
+
supports: chainInfo => NFT_CHAIN_GROUPS_MIGRATED.unique_network.includes(chainInfo.slug),
|
|
21
|
+
supportsFetchFullNftList: false,
|
|
22
|
+
create: chain => new UniqueNftHandler(chain)
|
|
23
|
+
}
|
|
24
|
+
// {
|
|
25
|
+
// id: 'ordinal',
|
|
26
|
+
// supports: (chainInfo) => _isSupportOrdinal(chainInfo.slug),
|
|
27
|
+
// create: (chain, state) => new OrdinalNftHandler(chain, state)
|
|
28
|
+
// }
|
|
29
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NftDetailRequest, NftFullListRequest, NftItem } from '@subwallet/extension-base/background/KoniTypes';
|
|
2
|
+
import { BaseNftHandler, NftHandlerResult } from '../base-nft-handler';
|
|
3
|
+
export declare class UniqueNftHandler extends BaseNftHandler {
|
|
4
|
+
filterAddresses(addresses: string[]): string[];
|
|
5
|
+
private mapUniqueCollections;
|
|
6
|
+
private mapUniqueRootNftToItem;
|
|
7
|
+
private mapBundleTreeToNftItem;
|
|
8
|
+
fetchNftBundle(collectionId: number | string, tokenId: string, topmostOwner: string): Promise<NftItem | null>;
|
|
9
|
+
fetchPreview(addresses: string[]): Promise<NftHandlerResult>;
|
|
10
|
+
fetchFullListNftOfACollection(request: NftFullListRequest): Promise<NftHandlerResult>;
|
|
11
|
+
fetchNftDetail(request: NftDetailRequest): Promise<NftHandlerResult>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Copyright 2019-2022 @subwallet/extension-koni authors & contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { ChainType } from '@subwallet/extension-base/background/KoniTypes';
|
|
5
|
+
import { getAddressesByChainType } from '@subwallet/extension-base/utils';
|
|
6
|
+
import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';
|
|
7
|
+
import { BaseNftHandler } from "../base-nft-handler.js";
|
|
8
|
+
export class UniqueNftHandler extends BaseNftHandler {
|
|
9
|
+
filterAddresses(addresses) {
|
|
10
|
+
return getAddressesByChainType(addresses, [ChainType.SUBSTRATE]);
|
|
11
|
+
}
|
|
12
|
+
mapUniqueCollections(raws) {
|
|
13
|
+
var _raws$collectionCover;
|
|
14
|
+
return {
|
|
15
|
+
collectionId: raws.collectionId.toString(),
|
|
16
|
+
chain: this.chain,
|
|
17
|
+
collectionName: raws.collectionName || 'Unknown Collection',
|
|
18
|
+
image: ((_raws$collectionCover = raws.collectionCover) === null || _raws$collectionCover === void 0 ? void 0 : _raws$collectionCover.url) || undefined,
|
|
19
|
+
externalUrl: undefined,
|
|
20
|
+
originAsset: undefined
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
mapUniqueRootNftToItem(raw, owner) {
|
|
24
|
+
return {
|
|
25
|
+
id: raw.tokenId.toString(),
|
|
26
|
+
chain: this.chain,
|
|
27
|
+
collectionId: raw.collectionId.toString(),
|
|
28
|
+
name: raw.name || `#${raw.tokenId}`,
|
|
29
|
+
image: raw.image || undefined,
|
|
30
|
+
description: raw.description || undefined,
|
|
31
|
+
owner: owner,
|
|
32
|
+
isBundle: false,
|
|
33
|
+
properties: null
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
mapBundleTreeToNftItem(node, topmostOwner, level = 0, parentId) {
|
|
37
|
+
const item = {
|
|
38
|
+
id: node.tokenId.toString(),
|
|
39
|
+
chain: this.chain,
|
|
40
|
+
collectionId: node.collectionId.toString(),
|
|
41
|
+
name: node.name || `#${node.tokenId}`,
|
|
42
|
+
image: node.image || undefined,
|
|
43
|
+
description: node.description || undefined,
|
|
44
|
+
owner: topmostOwner,
|
|
45
|
+
isBundle: node.nested && node.nested.length > 0 && level === 0,
|
|
46
|
+
properties: null,
|
|
47
|
+
nestingLevel: level,
|
|
48
|
+
parentId: level > 0 ? parentId : undefined
|
|
49
|
+
};
|
|
50
|
+
if (node.nested && node.nested.length > 0) {
|
|
51
|
+
item.nestingTokens = node.nested.map(child => this.mapBundleTreeToNftItem(child, topmostOwner, level + 1, item.id));
|
|
52
|
+
}
|
|
53
|
+
return item;
|
|
54
|
+
}
|
|
55
|
+
async fetchNftBundle(collectionId, tokenId, topmostOwner) {
|
|
56
|
+
const api = subwalletApiSdk.uniqueNftDetectionApi;
|
|
57
|
+
if (!api) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const treeData = await api.getNftBundleTree(collectionId, tokenId);
|
|
62
|
+
if (!treeData) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return this.mapBundleTreeToNftItem(treeData, topmostOwner, 0, tokenId);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error('[UniqueNftHandler] Failed to fetch bundle tree', e);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ==================== MAIN HANDLER ====================
|
|
73
|
+
|
|
74
|
+
async fetchPreview(addresses) {
|
|
75
|
+
const items = [];
|
|
76
|
+
const collectionsMap = new Map();
|
|
77
|
+
const api = subwalletApiSdk.uniqueNftDetectionApi;
|
|
78
|
+
if (!api) {
|
|
79
|
+
return {
|
|
80
|
+
items: [],
|
|
81
|
+
collections: []
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await Promise.all(addresses.map(async address => {
|
|
86
|
+
// 1. Collections
|
|
87
|
+
const sdkCollections = await api.getUniqueCollectionsByTokenOwner(address);
|
|
88
|
+
for (const col of sdkCollections) {
|
|
89
|
+
const collection = this.mapUniqueCollections(col);
|
|
90
|
+
if (!collectionsMap.has(collection.collectionId)) {
|
|
91
|
+
collectionsMap.set(collection.collectionId, collection);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 2. Root NFTs
|
|
96
|
+
const sdkNfts = await api.getAllUniqueRootNfts({
|
|
97
|
+
owner: address
|
|
98
|
+
});
|
|
99
|
+
for (const rootNft of sdkNfts) {
|
|
100
|
+
items.push(this.mapUniqueRootNftToItem(rootNft, address));
|
|
101
|
+
}
|
|
102
|
+
}));
|
|
103
|
+
} catch (e) {
|
|
104
|
+
console.error(`[UniqueNftHandler] Failed to fetch for ${this.chain}`, e);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
items,
|
|
108
|
+
collections: Array.from(collectionsMap.values())
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async fetchFullListNftOfACollection(request) {
|
|
112
|
+
const items = [];
|
|
113
|
+
const collections = [];
|
|
114
|
+
const {
|
|
115
|
+
collectionId,
|
|
116
|
+
owners,
|
|
117
|
+
tokenIds
|
|
118
|
+
} = request;
|
|
119
|
+
if (!collectionId || !owners || !tokenIds) {
|
|
120
|
+
console.warn('[NftService] missing params for getFullNftInstancesByCollection');
|
|
121
|
+
return {
|
|
122
|
+
items,
|
|
123
|
+
collections
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
for (const id of tokenIds) {
|
|
127
|
+
const fullTree = await this.fetchNftBundle(collectionId, id, owners[0]);
|
|
128
|
+
if (fullTree) {
|
|
129
|
+
items.push(fullTree);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
items: items,
|
|
134
|
+
collections: collections
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
async fetchNftDetail(request) {
|
|
138
|
+
const items = [];
|
|
139
|
+
const collections = [];
|
|
140
|
+
const api = subwalletApiSdk.uniqueNftDetectionApi;
|
|
141
|
+
if (!api) {
|
|
142
|
+
return {
|
|
143
|
+
items: [],
|
|
144
|
+
collections: []
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const {
|
|
148
|
+
collectionId,
|
|
149
|
+
tokenId
|
|
150
|
+
} = request;
|
|
151
|
+
if (!collectionId || !tokenId) {
|
|
152
|
+
return {
|
|
153
|
+
items,
|
|
154
|
+
collections
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const rawNft = await api.getNftDetail(collectionId, tokenId);
|
|
159
|
+
if (!rawNft) {
|
|
160
|
+
return {
|
|
161
|
+
items: [],
|
|
162
|
+
collections: []
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const nftItem = this.mapUniqueRootNftToItem(rawNft, rawNft.owner);
|
|
166
|
+
return {
|
|
167
|
+
items: [nftItem],
|
|
168
|
+
collections: []
|
|
169
|
+
};
|
|
170
|
+
} catch (e) {
|
|
171
|
+
return {
|
|
172
|
+
items: [],
|
|
173
|
+
collections: []
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -83,7 +83,7 @@ export default class DatabaseService {
|
|
|
83
83
|
addNft(address: string, nft: NftItem): Promise<unknown>;
|
|
84
84
|
handleNftTransfer(chain: string, addresses: string[], nftItem: NftItem): import("dexie").PromiseExtended<number>;
|
|
85
85
|
removeNfts(chain: string, address: string, collectionId: string, nftIds: string[]): import("dexie").PromiseExtended<number>;
|
|
86
|
-
removeNftsByAddress(address: string): import("dexie").PromiseExtended<number>;
|
|
86
|
+
removeNftsByAddress(address: string[]): import("dexie").PromiseExtended<number>;
|
|
87
87
|
updateChainStore(item: IChain): Promise<unknown>;
|
|
88
88
|
bulkUpdateChainStore(data: IChain[]): Promise<unknown>;
|
|
89
89
|
removeFromChainStore(chains: string[]): Promise<number>;
|
|
@@ -393,7 +393,7 @@ export default class DatabaseService {
|
|
|
393
393
|
return this.stores.nft.removeNfts(chain, address, collectionId, nftIds);
|
|
394
394
|
}
|
|
395
395
|
removeNftsByAddress(address) {
|
|
396
|
-
return this.stores.nft.removeNftsByAddress(
|
|
396
|
+
return this.stores.nft.removeNftsByAddress(address);
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
// Chain
|
|
@@ -64,7 +64,10 @@ export class AssetHubSwapHandler {
|
|
|
64
64
|
const {
|
|
65
65
|
path,
|
|
66
66
|
request: {
|
|
67
|
-
|
|
67
|
+
address,
|
|
68
|
+
alternativeAddress,
|
|
69
|
+
fromAmount,
|
|
70
|
+
recipient
|
|
68
71
|
},
|
|
69
72
|
selectedQuote
|
|
70
73
|
} = params;
|
|
@@ -86,12 +89,12 @@ export class AssetHubSwapHandler {
|
|
|
86
89
|
const needModifyData = swapXcm || xcmSwapXcm;
|
|
87
90
|
let bnSendingValue = BigN(fromAmount);
|
|
88
91
|
let bnExpectedReceive = BigN(selectedQuote.toAmount);
|
|
89
|
-
const sender = _reformatAddressWithChain(
|
|
90
|
-
let receiver = _reformatAddressWithChain(
|
|
92
|
+
const sender = _reformatAddressWithChain(address, originChain, alternativeAddress);
|
|
93
|
+
let receiver = _reformatAddressWithChain(recipient || address, destinationChain);
|
|
91
94
|
if (needModifyData) {
|
|
92
95
|
bnSendingValue = bnSendingValue.multipliedBy(DEFAULT_EXCESS_AMOUNT_WEIGHT);
|
|
93
96
|
bnExpectedReceive = bnExpectedReceive.multipliedBy(DEFAULT_EXCESS_AMOUNT_WEIGHT);
|
|
94
|
-
receiver = _reformatAddressWithChain(
|
|
97
|
+
receiver = _reformatAddressWithChain(address, destinationChain, alternativeAddress);
|
|
95
98
|
}
|
|
96
99
|
const submitStep = {
|
|
97
100
|
name: 'Swap',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { _ChainAsset } from '@subwallet/chain-list/types';
|
|
2
2
|
import { ChainService } from '@subwallet/extension-base/services/chain-service';
|
|
3
3
|
import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
|
|
4
|
-
import { AssetHubSwapEarlyValidation, SwapPair, SwapRequest } from '@subwallet/extension-base/types/swap';
|
|
5
4
|
import { SubmittableExtrinsic } from '@polkadot/api/types';
|
|
6
5
|
export declare class AssetHubRouter {
|
|
7
6
|
private readonly chain;
|
|
@@ -9,8 +8,5 @@ export declare class AssetHubRouter {
|
|
|
9
8
|
constructor(chain: string, chainService: ChainService);
|
|
10
9
|
get substrateApi(): _SubstrateApi;
|
|
11
10
|
get nativeToken(): _ChainAsset;
|
|
12
|
-
buildPath(pair: SwapPair): Array<_ChainAsset>;
|
|
13
|
-
earlyValidateSwapValidation(request: SwapRequest): Promise<AssetHubSwapEarlyValidation>;
|
|
14
|
-
estimateAmountOut(pair: SwapPair, amountIn: string): Promise<string>;
|
|
15
11
|
buildSwapExtrinsic(path: Array<_ChainAsset>, recipient: string, amountIn: string, amountOutMin: string): Promise<SubmittableExtrinsic<'promise'>>;
|
|
16
12
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
// Copyright 2019-2022 @subwallet/extension-base
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { buildSwapExtrinsic, checkLiquidityForPath, checkMinAmountForPath, estimatePriceImpactPct, estimateRateAfterForPath, estimateRateForPath, estimateTokensForPath, getReserveForPath } from '@subwallet/extension-base/services/swap-service/handler/asset-hub/utils';
|
|
6
|
-
import { SwapErrorType } from '@subwallet/extension-base/types/swap';
|
|
7
|
-
import BigN from 'bignumber.js';
|
|
4
|
+
import { buildSwapExtrinsic } from '@subwallet/extension-base/services/swap-service/handler/asset-hub/utils';
|
|
8
5
|
export class AssetHubRouter {
|
|
9
6
|
constructor(chain, chainService) {
|
|
10
7
|
this.chain = chain;
|
|
@@ -16,66 +13,6 @@ export class AssetHubRouter {
|
|
|
16
13
|
get nativeToken() {
|
|
17
14
|
return this.chainService.getNativeTokenInfo(this.chain);
|
|
18
15
|
}
|
|
19
|
-
buildPath(pair) {
|
|
20
|
-
// const nativeToken = this.nativeToken;
|
|
21
|
-
// const nativeTokenSlug = nativeToken.slug;
|
|
22
|
-
|
|
23
|
-
const assetFrom = this.chainService.getAssetBySlug(pair.from);
|
|
24
|
-
const assetTo = this.chainService.getAssetBySlug(pair.to);
|
|
25
|
-
return [assetFrom, assetTo];
|
|
26
|
-
// if (pair.from === nativeTokenSlug || pair.to === nativeTokenSlug) {
|
|
27
|
-
// return [assetFrom, assetTo];
|
|
28
|
-
// } else {
|
|
29
|
-
// return [assetFrom, nativeToken, assetTo];
|
|
30
|
-
// }
|
|
31
|
-
}
|
|
32
|
-
async earlyValidateSwapValidation(request) {
|
|
33
|
-
const substrateApi = await this.substrateApi.isReady;
|
|
34
|
-
const paths = this.buildPath(request.pair);
|
|
35
|
-
const api = await substrateApi.api.isReady;
|
|
36
|
-
const amount = request.fromAmount;
|
|
37
|
-
const reserves = await getReserveForPath(api, paths);
|
|
38
|
-
const amounts = estimateTokensForPath(amount, reserves);
|
|
39
|
-
const marketRate = estimateRateForPath(reserves);
|
|
40
|
-
const marketRateAfter = estimateRateAfterForPath(amount, reserves);
|
|
41
|
-
const priceImpactPct = estimatePriceImpactPct(marketRate, marketRateAfter);
|
|
42
|
-
const errors = [];
|
|
43
|
-
|
|
44
|
-
// Check liquidity
|
|
45
|
-
const liquidityError = checkLiquidityForPath(amounts, reserves);
|
|
46
|
-
if (liquidityError) {
|
|
47
|
-
errors.push(liquidityError);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Check amount token in pool after swap
|
|
51
|
-
const minAmounts = paths.map(asset => _getTokenMinAmount(asset));
|
|
52
|
-
const minAmountAfterSwapError = checkMinAmountForPath(reserves, amounts, minAmounts);
|
|
53
|
-
if (minAmountAfterSwapError) {
|
|
54
|
-
errors.push(minAmountAfterSwapError);
|
|
55
|
-
}
|
|
56
|
-
const bnAmount = new BigN(request.fromAmount);
|
|
57
|
-
if (bnAmount.lte(0)) {
|
|
58
|
-
errors.push(SwapErrorType.AMOUNT_CANNOT_BE_ZERO);
|
|
59
|
-
}
|
|
60
|
-
const metadata = {
|
|
61
|
-
chain: this.chainService.getChainInfoByKey(this.chain),
|
|
62
|
-
toAmount: amounts[amounts.length - 1],
|
|
63
|
-
quoteRate: marketRate,
|
|
64
|
-
priceImpactPct: priceImpactPct
|
|
65
|
-
};
|
|
66
|
-
return {
|
|
67
|
-
error: errors[0],
|
|
68
|
-
metadata
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
async estimateAmountOut(pair, amountIn) {
|
|
72
|
-
const substrateApi = await this.substrateApi.isReady;
|
|
73
|
-
const paths = this.buildPath(pair);
|
|
74
|
-
const api = await substrateApi.api.isReady;
|
|
75
|
-
const reserves = await getReserveForPath(api, paths);
|
|
76
|
-
const amounts = estimateTokensForPath(amountIn, reserves);
|
|
77
|
-
return amounts[amounts.length - 1];
|
|
78
|
-
}
|
|
79
16
|
async buildSwapExtrinsic(path, recipient, amountIn, amountOutMin) {
|
|
80
17
|
const substrateApi = await this.substrateApi.isReady;
|
|
81
18
|
const api = await substrateApi.api.isReady;
|
|
@@ -71,6 +71,7 @@ export class SwapBaseHandler {
|
|
|
71
71
|
path,
|
|
72
72
|
request: {
|
|
73
73
|
address,
|
|
74
|
+
alternativeAddress,
|
|
74
75
|
fromAmount,
|
|
75
76
|
recipient
|
|
76
77
|
},
|
|
@@ -94,12 +95,12 @@ export class SwapBaseHandler {
|
|
|
94
95
|
throw Error('Token or chain not found');
|
|
95
96
|
}
|
|
96
97
|
let recipientAddress;
|
|
97
|
-
const senderAddress = _reformatAddressWithChain(address, fromChainInfo);
|
|
98
|
+
const senderAddress = _reformatAddressWithChain(address, fromChainInfo, alternativeAddress);
|
|
98
99
|
if (stepIndex === 0) {
|
|
99
|
-
recipientAddress = _reformatAddressWithChain(address, toChainInfo);
|
|
100
|
+
recipientAddress = _reformatAddressWithChain(address, toChainInfo, alternativeAddress);
|
|
100
101
|
} else {
|
|
101
102
|
// bridge after swap
|
|
102
|
-
recipientAddress = _reformatAddressWithChain(recipient || address, toChainInfo);
|
|
103
|
+
recipientAddress = _reformatAddressWithChain(recipient || address, toChainInfo, alternativeAddress);
|
|
103
104
|
}
|
|
104
105
|
if (!_isXcmWithinSameConsensus(fromChainInfo, toChainInfo) || _isSnowBridgeXcm(fromChainInfo, toChainInfo) || _isAcrossBridgeXcm(fromChainInfo, toChainInfo)) {
|
|
105
106
|
return undefined;
|
|
@@ -108,7 +108,11 @@ export class HydradxHandler {
|
|
|
108
108
|
const {
|
|
109
109
|
path,
|
|
110
110
|
request: {
|
|
111
|
-
|
|
111
|
+
address,
|
|
112
|
+
alternativeAddress,
|
|
113
|
+
fromAmount,
|
|
114
|
+
recipient,
|
|
115
|
+
slippage
|
|
112
116
|
},
|
|
113
117
|
selectedQuote
|
|
114
118
|
} = params;
|
|
@@ -127,8 +131,8 @@ export class HydradxHandler {
|
|
|
127
131
|
const destinationTokenInfo = this.chainService.getAssetBySlug(swapPairInfo.to);
|
|
128
132
|
const originChain = this.chainService.getChainInfoByKey(originTokenInfo.originChain);
|
|
129
133
|
const destinationChain = this.chainService.getChainInfoByKey(destinationTokenInfo.originChain);
|
|
130
|
-
const sender = _reformatAddressWithChain(
|
|
131
|
-
let receiver = _reformatAddressWithChain(
|
|
134
|
+
const sender = _reformatAddressWithChain(address, originChain, alternativeAddress);
|
|
135
|
+
let receiver = _reformatAddressWithChain(recipient || address, destinationChain);
|
|
132
136
|
const actionList = JSON.stringify(path.map(step => step.action));
|
|
133
137
|
const xcmSwapXcm = actionList === JSON.stringify([DynamicSwapType.BRIDGE, DynamicSwapType.SWAP, DynamicSwapType.BRIDGE]);
|
|
134
138
|
const swapXcm = actionList === JSON.stringify([DynamicSwapType.SWAP, DynamicSwapType.BRIDGE]);
|
|
@@ -152,7 +156,7 @@ export class HydradxHandler {
|
|
|
152
156
|
slug: swapPairInfo.slug
|
|
153
157
|
},
|
|
154
158
|
fromAmount: bnSendingValue.toFixed(0, 1),
|
|
155
|
-
slippage:
|
|
159
|
+
slippage: slippage
|
|
156
160
|
});
|
|
157
161
|
} catch (error) {
|
|
158
162
|
throw new Error(`Failed to fetch swap quote: ${error.message}`);
|
|
@@ -163,7 +167,7 @@ export class HydradxHandler {
|
|
|
163
167
|
}
|
|
164
168
|
const overrideQuote = quoteAskResponse.quote;
|
|
165
169
|
txHex = overrideQuote.metadata;
|
|
166
|
-
receiver = _reformatAddressWithChain(
|
|
170
|
+
receiver = _reformatAddressWithChain(address, destinationChain, alternativeAddress);
|
|
167
171
|
}
|
|
168
172
|
if (!txHex || !isHex(txHex)) {
|
|
169
173
|
return Promise.resolve(undefined);
|
|
@@ -103,8 +103,7 @@ export class SwapService {
|
|
|
103
103
|
if (!params.selectedQuote) {
|
|
104
104
|
return this.getDefaultProcessV2(params);
|
|
105
105
|
} else {
|
|
106
|
-
|
|
107
|
-
const providerId = ((_params$request$curre = params.request.currentQuote) === null || _params$request$curre === void 0 ? void 0 : _params$request$curre.id) || params.selectedQuote.provider.id;
|
|
106
|
+
const providerId = params.selectedQuote.provider.id;
|
|
108
107
|
const handler = this.handlers[providerId];
|
|
109
108
|
if (handler) {
|
|
110
109
|
// todo: handle error response from generateOptimalProcess
|
|
@@ -129,6 +128,8 @@ export class SwapService {
|
|
|
129
128
|
console.group('Swap Logger');
|
|
130
129
|
console.log('path', path);
|
|
131
130
|
console.log('swapQuoteResponse', swapQuoteResponse);
|
|
131
|
+
|
|
132
|
+
// Just to log routing type for Uniswap Quote
|
|
132
133
|
if (swapQuoteResponse.optimalQuote && swapQuoteResponse.optimalQuote.metadata) {
|
|
133
134
|
const routing = swapQuoteResponse.optimalQuote.metadata.routing;
|
|
134
135
|
if (routing) {
|
|
@@ -147,7 +148,7 @@ export class SwapService {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
// override fee for quote because some cases need estimate network fee on Extension (i.e. Optimex)
|
|
150
|
-
if (swapQuoteResponse.optimalQuote) {
|
|
151
|
+
if (swapQuoteResponse.optimalQuote && [SwapProviderId.OPTIMEX, SwapProviderId.OPTIMEX_TESTNET].includes(swapQuoteResponse.optimalQuote.provider.id)) {
|
|
151
152
|
const swapIndex = optimalProcess.steps.findIndex(step => step.type === SwapStepType.SWAP);
|
|
152
153
|
swapQuoteResponse.optimalQuote.feeInfo.feeComponent = optimalProcess.totalFee[swapIndex].feeComponent;
|
|
153
154
|
}
|
|
@@ -190,7 +191,7 @@ export class SwapService {
|
|
|
190
191
|
const swapAction = path.find(step => step.action === DynamicSwapType.SWAP);
|
|
191
192
|
const directSwapRequest = swapAction ? {
|
|
192
193
|
...request,
|
|
193
|
-
address: _reformatAddressWithChain(request.address, this.chainService.getChainInfoByKey(_getAssetOriginChain(this.chainService.getAssetBySlug(swapAction.pair.from)))),
|
|
194
|
+
address: _reformatAddressWithChain(request.address, this.chainService.getChainInfoByKey(_getAssetOriginChain(this.chainService.getAssetBySlug(swapAction.pair.from))), request.alternativeAddress),
|
|
194
195
|
pair: swapAction.pair
|
|
195
196
|
} : undefined;
|
|
196
197
|
if (!directSwapRequest) {
|