@metamask-previews/assets-controllers 65.0.0-preview-88a682b → 65.0.0-preview-e5f15167
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/CHANGELOG.md +12 -0
- package/dist/AccountTrackerController.d.cts +2 -2
- package/dist/AccountTrackerController.d.mts +2 -2
- package/dist/CurrencyRateController.d.cts +2 -2
- package/dist/CurrencyRateController.d.mts +2 -2
- package/dist/DeFiPositionsController/DeFiPositionsController.d.cts +2 -2
- package/dist/DeFiPositionsController/DeFiPositionsController.d.mts +2 -2
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.cts +2 -2
- package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.mts +2 -2
- package/dist/NftController.cjs +207 -93
- package/dist/NftController.cjs.map +1 -1
- package/dist/NftController.d.cts +34 -41
- package/dist/NftController.d.cts.map +1 -1
- package/dist/NftController.d.mts +34 -41
- package/dist/NftController.d.mts.map +1 -1
- package/dist/NftController.mjs +208 -94
- package/dist/NftController.mjs.map +1 -1
- package/dist/NftDetectionController.cjs +2 -2
- package/dist/NftDetectionController.cjs.map +1 -1
- package/dist/NftDetectionController.d.cts +2 -1
- package/dist/NftDetectionController.d.cts.map +1 -1
- package/dist/NftDetectionController.d.mts +2 -1
- package/dist/NftDetectionController.d.mts.map +1 -1
- package/dist/NftDetectionController.mjs +2 -2
- package/dist/NftDetectionController.mjs.map +1 -1
- package/dist/TokenBalancesController.d.cts +2 -2
- package/dist/TokenBalancesController.d.mts +2 -2
- package/dist/TokenDetectionController.d.cts +2 -2
- package/dist/TokenDetectionController.d.mts +2 -2
- package/dist/TokenListController.d.cts +2 -2
- package/dist/TokenListController.d.mts +2 -2
- package/dist/TokenRatesController.d.cts +2 -2
- package/dist/TokenRatesController.d.mts +2 -2
- package/package.json +3 -1
package/dist/NftController.cjs
CHANGED
@@ -13,12 +13,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
13
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
15
15
|
};
|
16
|
-
var _NftController_instances, _NftController_mutex, _NftController_selectedAccountId,
|
16
|
+
var _NftController_instances, _NftController_mutex, _NftController_selectedAccountId, _NftController_ipfsGateway, _NftController_openSeaEnabled, _NftController_useIpfsSubdomains, _NftController_isIpfsGatewayEnabled, _NftController_onNftAdded, _NftController_onPreferencesControllerStateChange, _NftController_onSelectedAccountChange, _NftController_updateNestedNftState, _NftController_getNftCollectionApi, _NftController_getNftInformationFromApi, _NftController_getNftInformationFromTokenURI, _NftController_getNftURIAndStandard, _NftController_getNftInformation, _NftController_getNftContractInformationFromContract, _NftController_getNftContractInformation, _NftController_addIndividualNft, _NftController_addNftContract, _NftController_removeAndIgnoreIndividualNft, _NftController_removeIndividualNft, _NftController_removeNftContract, _NftController_validateWatchNft, _NftController_getAddressOrSelectedAddress, _NftController_updateNftUpdateForAccount, _NftController_bulkSanitizeNftMetadata, _NftController_sanitizeNftMetadata;
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
18
18
|
exports.NftController = exports.getDefaultNftControllerState = void 0;
|
19
19
|
const address_1 = require("@ethersproject/address");
|
20
20
|
const base_controller_1 = require("@metamask/base-controller");
|
21
21
|
const controller_utils_1 = require("@metamask/controller-utils");
|
22
|
+
const phishing_controller_1 = require("@metamask/phishing-controller");
|
22
23
|
const rpc_errors_1 = require("@metamask/rpc-errors");
|
23
24
|
const utils_1 = require("@metamask/utils");
|
24
25
|
const async_mutex_1 = require("async-mutex");
|
@@ -52,7 +53,6 @@ class NftController extends base_controller_1.BaseController {
|
|
52
53
|
* Creates an NftController instance.
|
53
54
|
*
|
54
55
|
* @param options - The controller options.
|
55
|
-
* @param options.chainId - The chain ID of the current network.
|
56
56
|
* @param options.ipfsGateway - The configured IPFS gateway.
|
57
57
|
* @param options.openSeaEnabled - Controls whether the OpenSea API is used.
|
58
58
|
* @param options.useIpfsSubdomains - Controls whether IPFS subdomains are used.
|
@@ -62,7 +62,7 @@ class NftController extends base_controller_1.BaseController {
|
|
62
62
|
* @param options.messenger - The messenger.
|
63
63
|
* @param options.state - Initial state to set on this controller.
|
64
64
|
*/
|
65
|
-
constructor({
|
65
|
+
constructor({ ipfsGateway = controller_utils_1.IPFS_DEFAULT_GATEWAY_URL, openSeaEnabled = false, useIpfsSubdomains = true, isIpfsGatewayEnabled = true, onNftAdded, messenger, state = {}, }) {
|
66
66
|
super({
|
67
67
|
name: controllerName,
|
68
68
|
metadata: nftControllerMetadata,
|
@@ -75,14 +75,12 @@ class NftController extends base_controller_1.BaseController {
|
|
75
75
|
_NftController_instances.add(this);
|
76
76
|
_NftController_mutex.set(this, new async_mutex_1.Mutex());
|
77
77
|
_NftController_selectedAccountId.set(this, void 0);
|
78
|
-
_NftController_chainId.set(this, void 0);
|
79
78
|
_NftController_ipfsGateway.set(this, void 0);
|
80
79
|
_NftController_openSeaEnabled.set(this, void 0);
|
81
80
|
_NftController_useIpfsSubdomains.set(this, void 0);
|
82
81
|
_NftController_isIpfsGatewayEnabled.set(this, void 0);
|
83
82
|
_NftController_onNftAdded.set(this, void 0);
|
84
83
|
__classPrivateFieldSet(this, _NftController_selectedAccountId, this.messagingSystem.call('AccountsController:getSelectedAccount').id, "f");
|
85
|
-
__classPrivateFieldSet(this, _NftController_chainId, initialChainId, "f");
|
86
84
|
__classPrivateFieldSet(this, _NftController_ipfsGateway, ipfsGateway, "f");
|
87
85
|
__classPrivateFieldSet(this, _NftController_openSeaEnabled, openSeaEnabled, "f");
|
88
86
|
__classPrivateFieldSet(this, _NftController_useIpfsSubdomains, useIpfsSubdomains, "f");
|
@@ -92,7 +90,6 @@ class NftController extends base_controller_1.BaseController {
|
|
92
90
|
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
93
91
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
94
92
|
__classPrivateFieldGet(this, _NftController_instances, "m", _NftController_onPreferencesControllerStateChange).bind(this));
|
95
|
-
this.messagingSystem.subscribe('NetworkController:networkDidChange', __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_onNetworkControllerNetworkDidChange).bind(this));
|
96
93
|
this.messagingSystem.subscribe('AccountsController:selectedEvmAccountChange',
|
97
94
|
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
98
95
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
@@ -112,23 +109,28 @@ class NftController extends base_controller_1.BaseController {
|
|
112
109
|
* @param asset.tokenId - The ID of the asset.
|
113
110
|
* @param type - The asset type.
|
114
111
|
* @param origin - Domain origin to register the asset from.
|
112
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
115
113
|
* @param options - Options bag.
|
116
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
117
114
|
* @param options.userAddress - The address of the account where the NFT is being added.
|
118
115
|
* @returns Object containing a Promise resolving to the suggestedAsset address if accepted.
|
119
116
|
*/
|
120
|
-
async watchNft(asset, type, origin,
|
117
|
+
async watchNft(asset, type, origin, networkClientId, { userAddress, } = {}) {
|
121
118
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
122
119
|
if (!addressToSearch) {
|
123
120
|
return;
|
124
121
|
}
|
125
|
-
|
122
|
+
if (!networkClientId) {
|
123
|
+
throw rpc_errors_1.rpcErrors.invalidParams('Network client id is required');
|
124
|
+
}
|
125
|
+
await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_validateWatchNft).call(this, asset, type, addressToSearch, networkClientId);
|
126
126
|
const nftMetadata = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformation).call(this, asset.address, asset.tokenId, networkClientId);
|
127
|
-
|
128
|
-
|
127
|
+
// Sanitize metadata
|
128
|
+
const sanitizedMetadata = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_sanitizeNftMetadata).call(this, nftMetadata);
|
129
|
+
if (sanitizedMetadata.standard && sanitizedMetadata.standard !== type) {
|
130
|
+
throw rpc_errors_1.rpcErrors.invalidInput(`Suggested NFT of type ${sanitizedMetadata.standard} does not match received type ${type}`);
|
129
131
|
}
|
130
132
|
const suggestedNftMeta = {
|
131
|
-
asset: { ...asset, ...
|
133
|
+
asset: { ...asset, ...sanitizedMetadata },
|
132
134
|
type,
|
133
135
|
id: (0, uuid_1.v4)(),
|
134
136
|
time: Date.now(),
|
@@ -137,8 +139,8 @@ class NftController extends base_controller_1.BaseController {
|
|
137
139
|
};
|
138
140
|
await this._requestApproval(suggestedNftMeta);
|
139
141
|
const { address, tokenId } = asset;
|
140
|
-
const { name, standard, description, image } =
|
141
|
-
await this.addNft(address, tokenId, {
|
142
|
+
const { name, standard, description, image } = sanitizedMetadata;
|
143
|
+
await this.addNft(address, tokenId, networkClientId, {
|
142
144
|
nftMetadata: {
|
143
145
|
name: name ?? null,
|
144
146
|
description: description ?? null,
|
@@ -147,7 +149,6 @@ class NftController extends base_controller_1.BaseController {
|
|
147
149
|
},
|
148
150
|
userAddress,
|
149
151
|
source: constants_1.Source.Dapp,
|
150
|
-
networkClientId,
|
151
152
|
});
|
152
153
|
}
|
153
154
|
/**
|
@@ -164,11 +165,10 @@ class NftController extends base_controller_1.BaseController {
|
|
164
165
|
* @param ownerAddress - User public address.
|
165
166
|
* @param nftAddress - NFT contract address.
|
166
167
|
* @param tokenId - NFT token ID.
|
167
|
-
* @param
|
168
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
168
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
169
169
|
* @returns Promise resolving the NFT ownership.
|
170
170
|
*/
|
171
|
-
async isNftOwner(ownerAddress, nftAddress, tokenId,
|
171
|
+
async isNftOwner(ownerAddress, nftAddress, tokenId, networkClientId) {
|
172
172
|
// Checks the ownership for ERC-721.
|
173
173
|
try {
|
174
174
|
const owner = await this.messagingSystem.call('AssetsContractController:getERC721OwnerOf', nftAddress, tokenId, networkClientId);
|
@@ -195,20 +195,17 @@ class NftController extends base_controller_1.BaseController {
|
|
195
195
|
*
|
196
196
|
* @param address - Hex address of the NFT contract.
|
197
197
|
* @param tokenId - The NFT identifier.
|
198
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
198
199
|
* @param options - an object of arguments
|
199
200
|
* @param options.userAddress - The address of the current user.
|
200
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
201
201
|
* @param options.source - Whether the NFT was detected, added manually or suggested by a dapp.
|
202
202
|
*/
|
203
|
-
async addNftVerifyOwnership(address, tokenId, { userAddress,
|
203
|
+
async addNftVerifyOwnership(address, tokenId, networkClientId, { userAddress, source, } = {}) {
|
204
204
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
205
|
-
if (!(await this.isNftOwner(addressToSearch, address, tokenId, {
|
206
|
-
networkClientId,
|
207
|
-
}))) {
|
205
|
+
if (!(await this.isNftOwner(addressToSearch, address, tokenId, networkClientId))) {
|
208
206
|
throw new Error('This NFT is not owned by the user');
|
209
207
|
}
|
210
|
-
await this.addNft(address, tokenId, {
|
211
|
-
networkClientId,
|
208
|
+
await this.addNft(address, tokenId, networkClientId, {
|
212
209
|
userAddress: addressToSearch,
|
213
210
|
source,
|
214
211
|
});
|
@@ -218,42 +215,45 @@ class NftController extends base_controller_1.BaseController {
|
|
218
215
|
*
|
219
216
|
* @param tokenAddress - Hex address of the NFT contract.
|
220
217
|
* @param tokenId - The NFT identifier.
|
218
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
221
219
|
* @param options - an object of arguments
|
222
220
|
* @param options.nftMetadata - NFT optional metadata.
|
223
221
|
* @param options.userAddress - The address of the current user.
|
224
222
|
* @param options.source - Whether the NFT was detected, added manually or suggested by a dapp.
|
225
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
226
|
-
* @param options.chainId - The chain ID to add the NFT to.
|
227
223
|
* @returns Promise resolving to the current NFT list.
|
228
224
|
*/
|
229
|
-
async addNft(tokenAddress, tokenId, { nftMetadata, userAddress, source = constants_1.Source.Custom,
|
225
|
+
async addNft(tokenAddress, tokenId, networkClientId, { nftMetadata, userAddress, source = constants_1.Source.Custom, } = {}) {
|
230
226
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
231
227
|
if (!addressToSearch) {
|
232
228
|
return;
|
233
229
|
}
|
234
230
|
const checksumHexAddress = (0, controller_utils_1.toChecksumHexAddress)(tokenAddress);
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
nftMetadata
|
239
|
-
|
240
|
-
|
231
|
+
if (!nftMetadata) {
|
232
|
+
const fetchedMetadata = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformation).call(this, checksumHexAddress, tokenId, networkClientId);
|
233
|
+
// Sanitize metadata
|
234
|
+
nftMetadata = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_sanitizeNftMetadata).call(this, fetchedMetadata);
|
235
|
+
}
|
236
|
+
else {
|
237
|
+
// Sanitize provided metadata
|
238
|
+
nftMetadata = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_sanitizeNftMetadata).call(this, nftMetadata);
|
239
|
+
}
|
240
|
+
const newNftContracts = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_addNftContract).call(this, networkClientId, {
|
241
241
|
tokenAddress: checksumHexAddress,
|
242
242
|
userAddress: addressToSearch,
|
243
|
-
networkClientId,
|
244
243
|
source,
|
245
244
|
nftMetadata,
|
246
|
-
chainIdHex: source === constants_1.Source.Detected ? chainIdToAddTo : undefined,
|
247
245
|
});
|
248
246
|
// If NFT contract was not added, do not add individual NFT
|
249
247
|
const nftContract = newNftContracts.find((contract) => contract.address.toLowerCase() === checksumHexAddress.toLowerCase());
|
248
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
250
249
|
// This is the case when the NFT is added manually and not detected automatically
|
250
|
+
// TODO: An improvement would be to make the chainId a required field and return it when getting the NFT information
|
251
251
|
if (!nftMetadata.chainId) {
|
252
|
-
nftMetadata.chainId = (0, controller_utils_1.convertHexToDecimal)(
|
252
|
+
nftMetadata.chainId = (0, controller_utils_1.convertHexToDecimal)(chainId);
|
253
253
|
}
|
254
254
|
// If NFT contract information, add individual NFT
|
255
255
|
if (nftContract) {
|
256
|
-
await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_addIndividualNft).call(this, checksumHexAddress, tokenId, nftMetadata, nftContract,
|
256
|
+
await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_addIndividualNft).call(this, checksumHexAddress, tokenId, nftMetadata, nftContract, chainId, addressToSearch, source);
|
257
257
|
}
|
258
258
|
}
|
259
259
|
/**
|
@@ -262,42 +262,62 @@ class NftController extends base_controller_1.BaseController {
|
|
262
262
|
* @param options - Options for refetching NFT metadata
|
263
263
|
* @param options.nfts - nfts to update metadata for.
|
264
264
|
* @param options.userAddress - The current user address
|
265
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
266
265
|
*/
|
267
|
-
async updateNftMetadata({ nfts, userAddress,
|
266
|
+
async updateNftMetadata({ nfts, userAddress, }) {
|
268
267
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
269
268
|
const releaseLock = await __classPrivateFieldGet(this, _NftController_mutex, "f").acquire();
|
270
269
|
try {
|
271
|
-
const chainId = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getCorrectChainId).call(this, { networkClientId });
|
272
270
|
const nftsWithChecksumAdr = nfts.map((nft) => {
|
273
271
|
return {
|
274
272
|
...nft,
|
275
273
|
address: (0, controller_utils_1.toChecksumHexAddress)(nft.address),
|
276
274
|
};
|
277
275
|
});
|
278
|
-
|
279
|
-
|
276
|
+
// Get all unsanitized nft metadata
|
277
|
+
const unsanitizedResults = await Promise.all(nftsWithChecksumAdr.map(async (nft) => {
|
278
|
+
// Each NFT should have a chainId; convert nft.chainId to networkClientId
|
279
|
+
const networkClientId = this.messagingSystem.call('NetworkController:findNetworkClientIdByChainId', (0, controller_utils_1.toHex)(nft.chainId));
|
280
|
+
const resMetadata = networkClientId
|
281
|
+
? await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformation).call(this, nft.address, nft.tokenId, networkClientId)
|
282
|
+
: undefined;
|
280
283
|
return {
|
281
284
|
nft,
|
282
285
|
newMetadata: resMetadata,
|
283
286
|
};
|
284
287
|
}));
|
288
|
+
// Extract metadata
|
289
|
+
const unsanitizedMetadata = unsanitizedResults.map((result) => result.newMetadata);
|
290
|
+
// Sanitize all metadata
|
291
|
+
const sanitizedMetadata = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_bulkSanitizeNftMetadata).call(this, unsanitizedMetadata);
|
292
|
+
// Reassemble the results with sanitized metadata
|
293
|
+
const nftMetadataResults = unsanitizedResults.map((result, index) => ({
|
294
|
+
nft: result.nft,
|
295
|
+
newMetadata: sanitizedMetadata[index],
|
296
|
+
}));
|
285
297
|
// We want to avoid updating the state if the state and fetched nft info are the same
|
286
298
|
const nftsWithDifferentMetadata = [];
|
287
299
|
const { allNfts } = this.state;
|
288
|
-
|
300
|
+
// get from state allNfts that match nftsWithChecksumAdr
|
301
|
+
const stateNfts = nftsWithChecksumAdr.map((nft) => {
|
302
|
+
return allNfts[addressToSearch]?.[(0, controller_utils_1.toHex)(nft.chainId)]?.find((nftElement) => nftElement.address.toLowerCase() === nft.address.toLowerCase() &&
|
303
|
+
nftElement.tokenId === nft.tokenId);
|
304
|
+
});
|
289
305
|
nftMetadataResults.forEach((singleNft) => {
|
290
|
-
const existingEntry = stateNfts.find((nft) => nft
|
291
|
-
|
292
|
-
|
306
|
+
const existingEntry = stateNfts.find((nft) => nft?.address.toLowerCase() ===
|
307
|
+
singleNft.nft.address.toLowerCase() &&
|
308
|
+
nft?.tokenId === singleNft.nft.tokenId);
|
309
|
+
if (existingEntry && singleNft.newMetadata) {
|
293
310
|
const differentMetadata = (0, assetsUtil_1.compareNftMetadata)(singleNft.newMetadata, existingEntry);
|
294
311
|
if (differentMetadata) {
|
295
|
-
nftsWithDifferentMetadata.push(
|
312
|
+
nftsWithDifferentMetadata.push({
|
313
|
+
nft: singleNft.nft,
|
314
|
+
newMetadata: singleNft.newMetadata,
|
315
|
+
});
|
296
316
|
}
|
297
317
|
}
|
298
318
|
});
|
299
319
|
if (nftsWithDifferentMetadata.length !== 0) {
|
300
|
-
nftsWithDifferentMetadata.forEach((elm) => this.updateNft(elm.nft, elm.newMetadata, addressToSearch, chainId));
|
320
|
+
nftsWithDifferentMetadata.forEach((elm) => this.updateNft(elm.nft, elm.newMetadata, addressToSearch, (0, controller_utils_1.toHex)(elm.nft.chainId)));
|
301
321
|
}
|
302
322
|
}
|
303
323
|
finally {
|
@@ -309,13 +329,13 @@ class NftController extends base_controller_1.BaseController {
|
|
309
329
|
*
|
310
330
|
* @param address - Hex address of the NFT contract.
|
311
331
|
* @param tokenId - Token identifier of the NFT.
|
332
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
312
333
|
* @param options - an object of arguments
|
313
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
314
334
|
* @param options.userAddress - The address of the account where the NFT is being removed.
|
315
335
|
*/
|
316
|
-
removeNft(address, tokenId,
|
336
|
+
removeNft(address, tokenId, networkClientId, { userAddress } = {}) {
|
317
337
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
318
|
-
const
|
338
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
319
339
|
const checksumHexAddress = (0, controller_utils_1.toChecksumHexAddress)(address);
|
320
340
|
__classPrivateFieldGet(this, _NftController_instances, "m", _NftController_removeIndividualNft).call(this, checksumHexAddress, tokenId, {
|
321
341
|
chainId,
|
@@ -336,13 +356,13 @@ class NftController extends base_controller_1.BaseController {
|
|
336
356
|
*
|
337
357
|
* @param address - Hex address of the NFT contract.
|
338
358
|
* @param tokenId - Token identifier of the NFT.
|
359
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
339
360
|
* @param options - an object of arguments
|
340
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
341
361
|
* @param options.userAddress - The address of the account where the NFT is being removed.
|
342
362
|
*/
|
343
|
-
removeAndIgnoreNft(address, tokenId,
|
363
|
+
removeAndIgnoreNft(address, tokenId, networkClientId, { userAddress } = {}) {
|
344
364
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
345
|
-
const
|
365
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
346
366
|
const checksumHexAddress = (0, controller_utils_1.toChecksumHexAddress)(address);
|
347
367
|
__classPrivateFieldGet(this, _NftController_instances, "m", _NftController_removeAndIgnoreIndividualNft).call(this, checksumHexAddress, tokenId, {
|
348
368
|
chainId,
|
@@ -372,20 +392,18 @@ class NftController extends base_controller_1.BaseController {
|
|
372
392
|
*
|
373
393
|
* @param nft - The NFT object to check and update.
|
374
394
|
* @param batch - A boolean indicating whether this method is being called as part of a batch or single update.
|
395
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
375
396
|
* @param accountParams - The userAddress and chainId to check ownership against
|
376
397
|
* @param accountParams.userAddress - the address passed through the confirmed transaction flow to ensure assets are stored to the correct account
|
377
|
-
* @param accountParams.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
378
398
|
* @returns the NFT with the updated isCurrentlyOwned value
|
379
399
|
*/
|
380
|
-
async checkAndUpdateSingleNftOwnershipStatus(nft, batch, { userAddress
|
400
|
+
async checkAndUpdateSingleNftOwnershipStatus(nft, batch, networkClientId, { userAddress } = {}) {
|
381
401
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
382
|
-
const
|
402
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
383
403
|
const { address, tokenId } = nft;
|
384
404
|
let isOwned = nft.isCurrentlyOwned;
|
385
405
|
try {
|
386
|
-
isOwned = await this.isNftOwner(addressToSearch, address, tokenId,
|
387
|
-
networkClientId,
|
388
|
-
});
|
406
|
+
isOwned = await this.isNftOwner(addressToSearch, address, tokenId, networkClientId);
|
389
407
|
}
|
390
408
|
catch {
|
391
409
|
// ignore error
|
@@ -421,18 +439,18 @@ class NftController extends base_controller_1.BaseController {
|
|
421
439
|
/**
|
422
440
|
* Checks whether NFTs associated with current selectedAddress/chainId combination are still owned by the user
|
423
441
|
* And updates the isCurrentlyOwned value on each accordingly.
|
442
|
+
*
|
443
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
424
444
|
* @param options - an object of arguments
|
425
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
426
445
|
* @param options.userAddress - The address of the account where the NFT ownership status is checked/updated.
|
427
446
|
*/
|
428
|
-
async checkAndUpdateAllNftsOwnershipStatus(
|
447
|
+
async checkAndUpdateAllNftsOwnershipStatus(networkClientId, { userAddress, } = {}) {
|
429
448
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
430
|
-
const
|
449
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
431
450
|
const { allNfts } = this.state;
|
432
451
|
const nfts = allNfts[addressToSearch]?.[chainId] || [];
|
433
452
|
const updatedNfts = await Promise.all(nfts.map(async (nft) => {
|
434
|
-
return ((await this.checkAndUpdateSingleNftOwnershipStatus(nft, true, {
|
435
|
-
networkClientId,
|
453
|
+
return ((await this.checkAndUpdateSingleNftOwnershipStatus(nft, true, networkClientId, {
|
436
454
|
userAddress,
|
437
455
|
})) ?? nft);
|
438
456
|
}));
|
@@ -447,13 +465,13 @@ class NftController extends base_controller_1.BaseController {
|
|
447
465
|
* @param address - Hex address of the NFT contract.
|
448
466
|
* @param tokenId - Hex address of the NFT contract.
|
449
467
|
* @param favorite - NFT new favorite status.
|
468
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
450
469
|
* @param options - an object of arguments
|
451
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
452
470
|
* @param options.userAddress - The address of the account where the NFT is being removed.
|
453
471
|
*/
|
454
|
-
updateNftFavoriteStatus(address, tokenId, favorite,
|
472
|
+
updateNftFavoriteStatus(address, tokenId, favorite, networkClientId, { userAddress, } = {}) {
|
455
473
|
const addressToSearch = __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getAddressOrSelectedAddress).call(this, userAddress);
|
456
|
-
const
|
474
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
457
475
|
const { allNfts } = this.state;
|
458
476
|
const nfts = [...(allNfts[addressToSearch]?.[chainId] || [])];
|
459
477
|
const index = nfts.findIndex((nft) => nft.address === address && nft.tokenId === tokenId);
|
@@ -597,12 +615,10 @@ class NftController extends base_controller_1.BaseController {
|
|
597
615
|
}
|
598
616
|
}
|
599
617
|
exports.NftController = NftController;
|
600
|
-
_NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new WeakMap(),
|
601
|
-
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
|
602
|
-
__classPrivateFieldSet(this, _NftController_chainId, chainId, "f");
|
603
|
-
}, _NftController_onPreferencesControllerStateChange =
|
618
|
+
_NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new WeakMap(), _NftController_ipfsGateway = new WeakMap(), _NftController_openSeaEnabled = new WeakMap(), _NftController_useIpfsSubdomains = new WeakMap(), _NftController_isIpfsGatewayEnabled = new WeakMap(), _NftController_onNftAdded = new WeakMap(), _NftController_instances = new WeakSet(), _NftController_onPreferencesControllerStateChange =
|
604
619
|
/**
|
605
620
|
* Handles the state change of the preference controller.
|
621
|
+
*
|
606
622
|
* @param preferencesState - The new state of the preference controller.
|
607
623
|
* @param preferencesState.ipfsGateway - The configured IPFS gateway.
|
608
624
|
* @param preferencesState.openSeaEnabled - Controls whether the OpenSea API is used.
|
@@ -626,6 +642,7 @@ async function _NftController_onPreferencesControllerStateChange({ ipfsGateway,
|
|
626
642
|
}, _NftController_onSelectedAccountChange =
|
627
643
|
/**
|
628
644
|
* Handles the selected account change on the accounts controller.
|
645
|
+
*
|
629
646
|
* @param internalAccount - The new selected account.
|
630
647
|
*/
|
631
648
|
async function _NftController_onSelectedAccountChange(internalAccount) {
|
@@ -848,16 +865,14 @@ async function _NftController_getNftURIAndStandard(contractAddress, tokenId, net
|
|
848
865
|
* @returns Promise resolving to the current NFT name and image.
|
849
866
|
*/
|
850
867
|
async function _NftController_getNftInformation(contractAddress, tokenId, networkClientId) {
|
851
|
-
const
|
852
|
-
networkClientId,
|
853
|
-
});
|
868
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
854
869
|
const [blockchainMetadata, nftApiMetadata] = await Promise.all([
|
855
870
|
(0, controller_utils_1.safelyExecute)(() => __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformationFromTokenURI).call(this, contractAddress, tokenId, networkClientId)),
|
856
871
|
__classPrivateFieldGet(this, _NftController_openSeaEnabled, "f") && chainId === '0x1'
|
857
872
|
? (0, controller_utils_1.safelyExecute)(() => __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformationFromApi).call(this, contractAddress, tokenId))
|
858
873
|
: undefined,
|
859
874
|
]);
|
860
|
-
|
875
|
+
const metadata = {
|
861
876
|
...nftApiMetadata,
|
862
877
|
name: blockchainMetadata?.name ?? nftApiMetadata?.name ?? null,
|
863
878
|
description: blockchainMetadata?.description ?? nftApiMetadata?.description ?? null,
|
@@ -865,6 +880,8 @@ async function _NftController_getNftInformation(contractAddress, tokenId, networ
|
|
865
880
|
standard: blockchainMetadata?.standard ?? nftApiMetadata?.standard ?? null,
|
866
881
|
tokenURI: blockchainMetadata?.tokenURI ?? null,
|
867
882
|
};
|
883
|
+
// Sanitize the metadata by checking external links against phishing protection
|
884
|
+
return await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_sanitizeNftMetadata).call(this, metadata);
|
868
885
|
}, _NftController_getNftContractInformationFromContract =
|
869
886
|
/**
|
870
887
|
* Request NFT contract information from the contract itself.
|
@@ -873,7 +890,9 @@ async function _NftController_getNftInformation(contractAddress, tokenId, networ
|
|
873
890
|
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
874
891
|
* @returns Promise resolving to the current NFT name and image.
|
875
892
|
*/
|
876
|
-
async function _NftController_getNftContractInformationFromContract(
|
893
|
+
async function _NftController_getNftContractInformationFromContract(
|
894
|
+
// TODO for calls to blockchain we need to explicitly pass the currentNetworkClientId since its relying on the provider
|
895
|
+
contractAddress, networkClientId) {
|
877
896
|
const [name, symbol] = await Promise.all([
|
878
897
|
this.messagingSystem.call('AssetsContractController:getERC721AssetName', contractAddress, networkClientId),
|
879
898
|
this.messagingSystem.call('AssetsContractController:getERC721AssetSymbol', contractAddress, networkClientId),
|
@@ -1008,22 +1027,20 @@ async function _NftController_addIndividualNft(tokenAddress, tokenId, nftMetadat
|
|
1008
1027
|
/**
|
1009
1028
|
* Adds an NFT contract to the stored NFT contracts list.
|
1010
1029
|
*
|
1030
|
+
* @param networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
1011
1031
|
* @param options - options.
|
1012
1032
|
* @param options.tokenAddress - Hex address of the NFT contract.
|
1013
1033
|
* @param options.userAddress - The address of the account where the NFT is being added.
|
1014
1034
|
* @param options.nftMetadata - The retrieved NFTMetadata from API.
|
1015
|
-
* @param options.networkClientId - The networkClientId that can be used to identify the network client to use for this request.
|
1016
1035
|
* @param options.source - Whether the NFT was detected, added manually or suggested by a dapp.
|
1017
|
-
* @param options.chainIdHex - The chainId to add the NFT contract to.
|
1018
1036
|
* @returns Promise resolving to the current NFT contracts list.
|
1019
1037
|
*/
|
1020
|
-
async function _NftController_addNftContract({ tokenAddress, userAddress,
|
1038
|
+
async function _NftController_addNftContract(networkClientId, { tokenAddress, userAddress, source, nftMetadata, }) {
|
1021
1039
|
const releaseLock = await __classPrivateFieldGet(this, _NftController_mutex, "f").acquire();
|
1022
1040
|
try {
|
1023
1041
|
const checksumHexAddress = (0, controller_utils_1.toChecksumHexAddress)(tokenAddress);
|
1024
1042
|
const { allNftContracts } = this.state;
|
1025
|
-
|
1026
|
-
const chainId = chainIdHex || __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getCorrectChainId).call(this, { networkClientId });
|
1043
|
+
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
1027
1044
|
const nftContracts = allNftContracts[userAddress]?.[chainId] || [];
|
1028
1045
|
const existingEntry = nftContracts.find((nftContract) => nftContract.address.toLowerCase() ===
|
1029
1046
|
checksumHexAddress.toLowerCase());
|
@@ -1117,7 +1134,7 @@ async function _NftController_addNftContract({ tokenAddress, userAddress, networ
|
|
1117
1134
|
userAddress,
|
1118
1135
|
});
|
1119
1136
|
return newNftContracts;
|
1120
|
-
}, _NftController_validateWatchNft = async function _NftController_validateWatchNft(asset, type, userAddress,
|
1137
|
+
}, _NftController_validateWatchNft = async function _NftController_validateWatchNft(asset, type, userAddress, networkClientId) {
|
1121
1138
|
const { address: contractAddress, tokenId } = asset;
|
1122
1139
|
// Validate parameters
|
1123
1140
|
if (!type) {
|
@@ -1140,7 +1157,7 @@ async function _NftController_addNftContract({ tokenAddress, userAddress, networ
|
|
1140
1157
|
}
|
1141
1158
|
// Check if the user owns the suggested NFT
|
1142
1159
|
try {
|
1143
|
-
const isOwner = await this.isNftOwner(userAddress, contractAddress, tokenId,
|
1160
|
+
const isOwner = await this.isNftOwner(userAddress, contractAddress, tokenId, networkClientId);
|
1144
1161
|
if (!isOwner) {
|
1145
1162
|
throw rpc_errors_1.rpcErrors.invalidInput('Suggested NFT is not owned by the selected account');
|
1146
1163
|
}
|
@@ -1152,12 +1169,6 @@ async function _NftController_addNftContract({ tokenAddress, userAddress, networ
|
|
1152
1169
|
}
|
1153
1170
|
throw error;
|
1154
1171
|
}
|
1155
|
-
}, _NftController_getCorrectChainId = function _NftController_getCorrectChainId({ networkClientId, }) {
|
1156
|
-
if (networkClientId) {
|
1157
|
-
const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
|
1158
|
-
return chainId;
|
1159
|
-
}
|
1160
|
-
return __classPrivateFieldGet(this, _NftController_chainId, "f");
|
1161
1172
|
}, _NftController_getAddressOrSelectedAddress = function _NftController_getAddressOrSelectedAddress(address) {
|
1162
1173
|
if (address) {
|
1163
1174
|
return address;
|
@@ -1165,8 +1176,16 @@ async function _NftController_addNftContract({ tokenAddress, userAddress, networ
|
|
1165
1176
|
// If the address is not defined (or empty), we fallback to the currently selected account's address
|
1166
1177
|
const selectedAccount = this.messagingSystem.call('AccountsController:getAccount', __classPrivateFieldGet(this, _NftController_selectedAccountId, "f"));
|
1167
1178
|
return selectedAccount?.address || '';
|
1168
|
-
}, _NftController_updateNftUpdateForAccount =
|
1169
|
-
|
1179
|
+
}, _NftController_updateNftUpdateForAccount =
|
1180
|
+
/**
|
1181
|
+
* Updates the all nfts in state for the account.
|
1182
|
+
* Nfts will be updated if they don't have a name, description or image.
|
1183
|
+
*
|
1184
|
+
* @param account - The account to update the NFT metadata for.
|
1185
|
+
*/
|
1186
|
+
async function _NftController_updateNftUpdateForAccount(account) {
|
1187
|
+
// get all nfts for the account for all chains
|
1188
|
+
const nfts = Object.values(this.state.allNfts[account.address] || {}).flat();
|
1170
1189
|
// Filter only nfts
|
1171
1190
|
const nftsToUpdate = nfts.filter((singleNft) => !singleNft.name && !singleNft.description && !singleNft.image);
|
1172
1191
|
if (nftsToUpdate.length !== 0 &&
|
@@ -1176,6 +1195,101 @@ async function _NftController_addNftContract({ tokenAddress, userAddress, networ
|
|
1176
1195
|
userAddress: account.address,
|
1177
1196
|
});
|
1178
1197
|
}
|
1198
|
+
}, _NftController_bulkSanitizeNftMetadata =
|
1199
|
+
/**
|
1200
|
+
* Sanitizes multiple NFT metadata objects by checking external links against PhishingController in a single bulk request
|
1201
|
+
*
|
1202
|
+
* @param metadataList - Array of NFT metadata objects to sanitize
|
1203
|
+
* @returns Array of sanitized NFT metadata objects
|
1204
|
+
*/
|
1205
|
+
async function _NftController_bulkSanitizeNftMetadata(metadataList) {
|
1206
|
+
// Create a copy of the metadata list to avoid mutating the input
|
1207
|
+
const sanitizedMetadataList = metadataList.map((metadata) => ({
|
1208
|
+
...metadata,
|
1209
|
+
}));
|
1210
|
+
// Maps URL to a list of {metadataIndex, fieldName} to track where each URL is used
|
1211
|
+
const urlMap = {};
|
1212
|
+
const fieldsToCheck = [
|
1213
|
+
'externalLink',
|
1214
|
+
'image',
|
1215
|
+
'imagePreview',
|
1216
|
+
'imageThumbnail',
|
1217
|
+
'imageOriginal',
|
1218
|
+
'animation',
|
1219
|
+
'animationOriginal',
|
1220
|
+
];
|
1221
|
+
// Collect all URLs from all metadata objects
|
1222
|
+
sanitizedMetadataList.forEach((metadata, metadataIndex) => {
|
1223
|
+
// Check regular fields
|
1224
|
+
for (const field of fieldsToCheck) {
|
1225
|
+
const url = metadata[field];
|
1226
|
+
if (typeof url === 'string' && url && url.startsWith('http')) {
|
1227
|
+
if (!urlMap[url]) {
|
1228
|
+
urlMap[url] = [];
|
1229
|
+
}
|
1230
|
+
urlMap[url].push({ metadataIndex, fieldName: field });
|
1231
|
+
}
|
1232
|
+
}
|
1233
|
+
// Check collection links if they exist
|
1234
|
+
if (metadata.collection) {
|
1235
|
+
const { collection } = metadata;
|
1236
|
+
if ('externalLink' in collection &&
|
1237
|
+
typeof collection.externalLink === 'string') {
|
1238
|
+
const url = collection.externalLink;
|
1239
|
+
if (!urlMap[url]) {
|
1240
|
+
urlMap[url] = [];
|
1241
|
+
}
|
1242
|
+
urlMap[url].push({
|
1243
|
+
metadataIndex,
|
1244
|
+
fieldName: 'collection.externalLink',
|
1245
|
+
});
|
1246
|
+
}
|
1247
|
+
}
|
1248
|
+
});
|
1249
|
+
const urlsToCheck = Object.keys(urlMap);
|
1250
|
+
if (urlsToCheck.length === 0) {
|
1251
|
+
return sanitizedMetadataList;
|
1252
|
+
}
|
1253
|
+
try {
|
1254
|
+
// Use bulkScanUrls to check all URLs at once
|
1255
|
+
const bulkScanResponse = await this.messagingSystem.call('PhishingController:bulkScanUrls', urlsToCheck);
|
1256
|
+
// Apply scan results to all metadata objects
|
1257
|
+
Object.entries(bulkScanResponse.results).forEach(([url, result]) => {
|
1258
|
+
if (result.recommendedAction === phishing_controller_1.RecommendedAction.Block) {
|
1259
|
+
// Remove this URL from all metadata objects where it appears
|
1260
|
+
urlMap[url].forEach(({ metadataIndex, fieldName }) => {
|
1261
|
+
if (fieldName === 'collection.externalLink' &&
|
1262
|
+
sanitizedMetadataList[metadataIndex].collection // Check if collection exists
|
1263
|
+
) {
|
1264
|
+
const { collection } = sanitizedMetadataList[metadataIndex];
|
1265
|
+
// Ensure collection is not undefined again just to be safe before using 'in'
|
1266
|
+
if (collection && 'externalLink' in collection) {
|
1267
|
+
delete collection.externalLink;
|
1268
|
+
}
|
1269
|
+
}
|
1270
|
+
else {
|
1271
|
+
delete sanitizedMetadataList[metadataIndex][fieldName];
|
1272
|
+
}
|
1273
|
+
});
|
1274
|
+
}
|
1275
|
+
});
|
1276
|
+
}
|
1277
|
+
catch (error) {
|
1278
|
+
console.error('Error during bulk URL scanning:', error);
|
1279
|
+
// If bulk scan fails, we fall back to keeping all URLs
|
1280
|
+
}
|
1281
|
+
return sanitizedMetadataList;
|
1282
|
+
}, _NftController_sanitizeNftMetadata =
|
1283
|
+
/**
|
1284
|
+
* Sanitizes NFT metadata by checking external links against PhishingController
|
1285
|
+
*
|
1286
|
+
* @param metadata - The NFT metadata to sanitize
|
1287
|
+
* @returns Sanitized NFT metadata with potentially dangerous links removed
|
1288
|
+
*/
|
1289
|
+
async function _NftController_sanitizeNftMetadata(metadata) {
|
1290
|
+
// Use the bulk sanitize function with just a single metadata object
|
1291
|
+
const sanitized = await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_bulkSanitizeNftMetadata).call(this, [metadata]);
|
1292
|
+
return sanitized[0];
|
1179
1293
|
};
|
1180
1294
|
exports.default = NftController;
|
1181
1295
|
//# sourceMappingURL=NftController.cjs.map
|