@explorins/pers-sdk-react-native 1.5.32 → 1.5.33
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/dist/hooks/useWeb3.js +1 -1
- package/dist/index.js +184 -70
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/hooks/useWeb3.ts +1 -1
package/dist/hooks/useWeb3.js
CHANGED
|
@@ -103,7 +103,7 @@ export const useWeb3 = () => {
|
|
|
103
103
|
* chainId: 1
|
|
104
104
|
* });
|
|
105
105
|
* console.log('NFT name:', metadata?.name);
|
|
106
|
-
* console.log('NFT image:', metadata?.
|
|
106
|
+
* console.log('NFT image:', metadata?.imageUrl); // IPFS URLs auto-resolved to HTTPS
|
|
107
107
|
* ```
|
|
108
108
|
*/
|
|
109
109
|
const getTokenMetadata = useCallback(async (request) => {
|
package/dist/index.js
CHANGED
|
@@ -22507,21 +22507,115 @@ const getTokenOfOwnerByIndex = async (tokenContract, ownerAddress, index) => {
|
|
|
22507
22507
|
};
|
|
22508
22508
|
|
|
22509
22509
|
/**
|
|
22510
|
-
* Clean
|
|
22511
|
-
* Removes
|
|
22510
|
+
* Clean input/output parameters for functions, constructors, errors
|
|
22511
|
+
* Removes properties that are invalid for non-event parameters
|
|
22512
|
+
*/
|
|
22513
|
+
function cleanInputsOrOutputs(params) {
|
|
22514
|
+
if (!Array.isArray(params)) {
|
|
22515
|
+
return [];
|
|
22516
|
+
}
|
|
22517
|
+
return params.map((param) => {
|
|
22518
|
+
if (!param || typeof param !== 'object') {
|
|
22519
|
+
return param;
|
|
22520
|
+
}
|
|
22521
|
+
const cleanParam = {
|
|
22522
|
+
name: param.name ?? '',
|
|
22523
|
+
type: param.type ?? 'uint256', // Default to uint256 if type is missing (shouldn't happen)
|
|
22524
|
+
};
|
|
22525
|
+
// Handle tuple types with components
|
|
22526
|
+
if (param.components && Array.isArray(param.components)) {
|
|
22527
|
+
cleanParam.components = cleanInputsOrOutputs(param.components);
|
|
22528
|
+
}
|
|
22529
|
+
// Preserve internalType only if it doesn't cause issues
|
|
22530
|
+
// (some malformed internalTypes can break ethers)
|
|
22531
|
+
if (param.internalType && typeof param.internalType === 'string') {
|
|
22532
|
+
cleanParam.internalType = param.internalType;
|
|
22533
|
+
}
|
|
22534
|
+
// NOTE: 'indexed' is intentionally NOT included here
|
|
22535
|
+
// It's only valid for event parameters
|
|
22536
|
+
return cleanParam;
|
|
22537
|
+
});
|
|
22538
|
+
}
|
|
22539
|
+
/**
|
|
22540
|
+
* Clean an ABI to be compatible with Ethers.js v6
|
|
22541
|
+
* Handles common Web3.js ABI format incompatibilities:
|
|
22542
|
+
* - Removes 'indexed' from function inputs (only valid for event parameters)
|
|
22543
|
+
* - Removes 'indexed' from function outputs
|
|
22544
|
+
* - Removes 'internalType' if it causes parsing issues
|
|
22545
|
+
* - Ensures 'stateMutability' is valid
|
|
22546
|
+
* - Removes unknown/invalid properties that cause ethers to drop fragments
|
|
22512
22547
|
*/
|
|
22513
22548
|
function cleanABI(abi) {
|
|
22514
22549
|
return abi.map(fragment => {
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
inputs: fragment.inputs.map((input) => {
|
|
22519
|
-
const { indexed, ...cleanInput } = input;
|
|
22520
|
-
return cleanInput;
|
|
22521
|
-
})
|
|
22522
|
-
};
|
|
22550
|
+
// Skip non-object fragments
|
|
22551
|
+
if (!fragment || typeof fragment !== 'object') {
|
|
22552
|
+
return fragment;
|
|
22523
22553
|
}
|
|
22524
|
-
|
|
22554
|
+
const cleanFragment = {
|
|
22555
|
+
type: fragment.type,
|
|
22556
|
+
name: fragment.name,
|
|
22557
|
+
};
|
|
22558
|
+
// Handle functions
|
|
22559
|
+
if (fragment.type === 'function') {
|
|
22560
|
+
// Clean inputs - remove 'indexed' (only valid for events)
|
|
22561
|
+
if (fragment.inputs) {
|
|
22562
|
+
cleanFragment.inputs = cleanInputsOrOutputs(fragment.inputs);
|
|
22563
|
+
}
|
|
22564
|
+
// Clean outputs - remove 'indexed'
|
|
22565
|
+
if (fragment.outputs) {
|
|
22566
|
+
cleanFragment.outputs = cleanInputsOrOutputs(fragment.outputs);
|
|
22567
|
+
}
|
|
22568
|
+
// Preserve valid function properties
|
|
22569
|
+
if (fragment.stateMutability) {
|
|
22570
|
+
cleanFragment.stateMutability = fragment.stateMutability;
|
|
22571
|
+
}
|
|
22572
|
+
// Handle legacy 'constant' and 'payable' for older ABIs
|
|
22573
|
+
if (fragment.constant !== undefined && !fragment.stateMutability) {
|
|
22574
|
+
cleanFragment.stateMutability = fragment.constant ? 'view' : 'nonpayable';
|
|
22575
|
+
}
|
|
22576
|
+
if (fragment.payable && !fragment.stateMutability) {
|
|
22577
|
+
cleanFragment.stateMutability = 'payable';
|
|
22578
|
+
}
|
|
22579
|
+
}
|
|
22580
|
+
// Handle events - 'indexed' IS valid here
|
|
22581
|
+
else if (fragment.type === 'event') {
|
|
22582
|
+
cleanFragment.anonymous = fragment.anonymous ?? false;
|
|
22583
|
+
if (fragment.inputs) {
|
|
22584
|
+
cleanFragment.inputs = fragment.inputs.map((input) => ({
|
|
22585
|
+
name: input.name ?? '',
|
|
22586
|
+
type: input.type,
|
|
22587
|
+
indexed: input.indexed ?? false,
|
|
22588
|
+
// Keep internalType for events if present
|
|
22589
|
+
...(input.internalType && { internalType: input.internalType })
|
|
22590
|
+
}));
|
|
22591
|
+
}
|
|
22592
|
+
}
|
|
22593
|
+
// Handle constructor
|
|
22594
|
+
else if (fragment.type === 'constructor') {
|
|
22595
|
+
if (fragment.inputs) {
|
|
22596
|
+
cleanFragment.inputs = cleanInputsOrOutputs(fragment.inputs);
|
|
22597
|
+
}
|
|
22598
|
+
if (fragment.stateMutability) {
|
|
22599
|
+
cleanFragment.stateMutability = fragment.stateMutability;
|
|
22600
|
+
}
|
|
22601
|
+
}
|
|
22602
|
+
// Handle fallback/receive
|
|
22603
|
+
else if (fragment.type === 'fallback' || fragment.type === 'receive') {
|
|
22604
|
+
if (fragment.stateMutability) {
|
|
22605
|
+
cleanFragment.stateMutability = fragment.stateMutability;
|
|
22606
|
+
}
|
|
22607
|
+
}
|
|
22608
|
+
// Handle errors
|
|
22609
|
+
else if (fragment.type === 'error') {
|
|
22610
|
+
if (fragment.inputs) {
|
|
22611
|
+
cleanFragment.inputs = cleanInputsOrOutputs(fragment.inputs);
|
|
22612
|
+
}
|
|
22613
|
+
}
|
|
22614
|
+
// Pass through unknown types unchanged
|
|
22615
|
+
else {
|
|
22616
|
+
return fragment;
|
|
22617
|
+
}
|
|
22618
|
+
return cleanFragment;
|
|
22525
22619
|
});
|
|
22526
22620
|
}
|
|
22527
22621
|
/**
|
|
@@ -22553,24 +22647,45 @@ function convertAbiToInterface(abi) {
|
|
|
22553
22647
|
return abi;
|
|
22554
22648
|
}
|
|
22555
22649
|
// String format (JSON)
|
|
22650
|
+
let abiArray;
|
|
22556
22651
|
if (typeof abi === 'string') {
|
|
22557
|
-
|
|
22558
|
-
|
|
22559
|
-
|
|
22652
|
+
abiArray = JSON.parse(abi);
|
|
22653
|
+
}
|
|
22654
|
+
else if (Array.isArray(abi)) {
|
|
22655
|
+
abiArray = abi;
|
|
22560
22656
|
}
|
|
22561
|
-
|
|
22562
|
-
|
|
22563
|
-
|
|
22564
|
-
return new Interface(cleanedAbi);
|
|
22657
|
+
else if (typeof abi === 'object') {
|
|
22658
|
+
// Single fragment object
|
|
22659
|
+
abiArray = [abi];
|
|
22565
22660
|
}
|
|
22566
|
-
|
|
22661
|
+
else {
|
|
22662
|
+
throw new Error(`Unsupported ABI type: ${typeof abi}`);
|
|
22663
|
+
}
|
|
22664
|
+
// Clean the ABI
|
|
22665
|
+
const cleanedAbi = cleanABI(abiArray);
|
|
22666
|
+
// Try to create Interface and validate all fragments were parsed
|
|
22667
|
+
const iface = new Interface(cleanedAbi);
|
|
22668
|
+
// Validation: Check if expected functions are present
|
|
22669
|
+
const expectedFunctions = cleanedAbi
|
|
22670
|
+
.filter((f) => f.type === 'function')
|
|
22671
|
+
.map((f) => f.name);
|
|
22672
|
+
const actualFunctions = iface.fragments
|
|
22673
|
+
.filter(f => f.type === 'function')
|
|
22674
|
+
.map(f => f.name);
|
|
22675
|
+
const droppedFunctions = expectedFunctions.filter((name) => !actualFunctions.includes(name));
|
|
22676
|
+
if (droppedFunctions.length > 0) {
|
|
22677
|
+
console.warn(`[web3-ts] Warning: ${droppedFunctions.length} function(s) were dropped during ABI conversion:`, droppedFunctions.join(', '));
|
|
22678
|
+
}
|
|
22679
|
+
return iface;
|
|
22567
22680
|
}
|
|
22568
22681
|
catch (error) {
|
|
22569
22682
|
if (error.message.includes('JSON')) {
|
|
22570
22683
|
throw new Error('Invalid JSON format in ABI string');
|
|
22571
22684
|
}
|
|
22572
|
-
if (error.message.includes('invalid fragment') ||
|
|
22573
|
-
|
|
22685
|
+
if (error.message.includes('invalid fragment') ||
|
|
22686
|
+
error.message.includes('parameter cannot be indexed')) {
|
|
22687
|
+
console.error('[web3-ts] ABI fragment error:', error.message);
|
|
22688
|
+
throw new Error(`Invalid ABI fragment structure: ${error.message}`);
|
|
22574
22689
|
}
|
|
22575
22690
|
throw error;
|
|
22576
22691
|
}
|
|
@@ -23974,7 +24089,7 @@ class CampaignApi {
|
|
|
23974
24089
|
* NEW: GET /trigger-sources
|
|
23975
24090
|
*/
|
|
23976
24091
|
async getCampaignTriggers(options) {
|
|
23977
|
-
let url = '/
|
|
24092
|
+
let url = '/campaign-triggers';
|
|
23978
24093
|
const params = [];
|
|
23979
24094
|
if (options) {
|
|
23980
24095
|
if (options.limit)
|
|
@@ -25402,6 +25517,17 @@ class IPFSInfrastructureApi {
|
|
|
25402
25517
|
}
|
|
25403
25518
|
return url;
|
|
25404
25519
|
}
|
|
25520
|
+
/**
|
|
25521
|
+
* Fetch metadata from tokenURI, resolve all IPFS URLs, and normalize to SDK format.
|
|
25522
|
+
*
|
|
25523
|
+
* NORMALIZATION BOUNDARY: This is the single place where ERC metadata standard
|
|
25524
|
+
* (snake_case: image, animation_url) is converted to SDK format (camelCase: imageUrl, animationUrl).
|
|
25525
|
+
* All downstream consumers receive normalized SDK format with resolved HTTPS URLs.
|
|
25526
|
+
*
|
|
25527
|
+
* @param tokenUri - Token URI (can be ipfs:// or https://)
|
|
25528
|
+
* @param chainId - Chain ID for IPFS gateway selection
|
|
25529
|
+
* @returns Normalized TokenMetadata with resolved HTTPS URLs, or null on error
|
|
25530
|
+
*/
|
|
25405
25531
|
async fetchAndProcessMetadata(tokenUri, chainId) {
|
|
25406
25532
|
try {
|
|
25407
25533
|
const resolvedUri = await this.resolveIPFSUrl(tokenUri, chainId);
|
|
@@ -25409,15 +25535,25 @@ class IPFSInfrastructureApi {
|
|
|
25409
25535
|
if (!response.ok) {
|
|
25410
25536
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
25411
25537
|
}
|
|
25412
|
-
const
|
|
25413
|
-
//
|
|
25538
|
+
const rawMetadata = await response.json();
|
|
25539
|
+
// Resolve IPFS URLs for media fields
|
|
25540
|
+
const resolvedImageUrl = rawMetadata.image
|
|
25541
|
+
? await this.resolveIPFSUrl(rawMetadata.image, chainId)
|
|
25542
|
+
: '';
|
|
25543
|
+
const resolvedAnimationUrl = rawMetadata.animation_url
|
|
25544
|
+
? await this.resolveIPFSUrl(rawMetadata.animation_url, chainId)
|
|
25545
|
+
: undefined;
|
|
25546
|
+
// Extract custom properties (anything not in ERC standard)
|
|
25547
|
+
const customProperties = Object.fromEntries(Object.entries(rawMetadata).filter(([key]) => !['name', 'description', 'image', 'animation_url', 'external_url', 'attributes'].includes(key)));
|
|
25548
|
+
// Return normalized SDK format (typed as TokenMetadata)
|
|
25414
25549
|
return {
|
|
25415
|
-
name:
|
|
25416
|
-
description:
|
|
25417
|
-
|
|
25418
|
-
|
|
25419
|
-
|
|
25420
|
-
|
|
25550
|
+
name: rawMetadata.name || '',
|
|
25551
|
+
description: rawMetadata.description || '',
|
|
25552
|
+
imageUrl: resolvedImageUrl,
|
|
25553
|
+
animationUrl: resolvedAnimationUrl,
|
|
25554
|
+
externalUrl: rawMetadata.external_url || undefined,
|
|
25555
|
+
attributes: rawMetadata.attributes || [],
|
|
25556
|
+
...customProperties
|
|
25421
25557
|
};
|
|
25422
25558
|
}
|
|
25423
25559
|
catch (error) {
|
|
@@ -25701,8 +25837,8 @@ class MetadataDomainService {
|
|
|
25701
25837
|
class ContractDomainService {
|
|
25702
25838
|
constructor() { }
|
|
25703
25839
|
analyzeContract(abi) {
|
|
25704
|
-
|
|
25705
|
-
const methods =
|
|
25840
|
+
const cleanAbi = convertAbiToInterface(abi);
|
|
25841
|
+
const methods = cleanAbi.fragments.filter(item => item.type === 'function').map(item => item.name);
|
|
25706
25842
|
// ERC-721 detection
|
|
25707
25843
|
const hasOwnerOf = methods.includes('ownerOf');
|
|
25708
25844
|
const hasTokenURI = methods.includes('tokenURI');
|
|
@@ -25733,28 +25869,6 @@ class ContractDomainService {
|
|
|
25733
25869
|
*/
|
|
25734
25870
|
class Web3ApplicationService {
|
|
25735
25871
|
constructor(web3Api, ipfsApi) {
|
|
25736
|
-
// Type-safe metadata conversion methods for ERC-721/ERC-1155 standards
|
|
25737
|
-
this.metadataMapper = {
|
|
25738
|
-
fromERCStandard: (ercMetadata) => ({
|
|
25739
|
-
name: ercMetadata.name || '',
|
|
25740
|
-
description: ercMetadata.description || '',
|
|
25741
|
-
imageUrl: ercMetadata.image || '',
|
|
25742
|
-
externalUrl: ercMetadata.external_url,
|
|
25743
|
-
animationUrl: ercMetadata.animation_url,
|
|
25744
|
-
animationUrlConverted: undefined, // Will be set by IPFS conversion
|
|
25745
|
-
attributes: ercMetadata.attributes || [],
|
|
25746
|
-
...ercMetadata
|
|
25747
|
-
}),
|
|
25748
|
-
toERCStandard: (metadata) => ({
|
|
25749
|
-
name: metadata.name,
|
|
25750
|
-
description: metadata.description,
|
|
25751
|
-
image: metadata.imageUrl,
|
|
25752
|
-
animation_url: metadata.animationUrl,
|
|
25753
|
-
external_url: metadata.externalUrl,
|
|
25754
|
-
attributes: metadata.attributes,
|
|
25755
|
-
...Object.fromEntries(Object.entries(metadata).filter(([key]) => !['name', 'description', 'imageUrl', 'animationUrl', 'externalUrl', 'attributes', 'animationUrlConverted'].includes(key)))
|
|
25756
|
-
})
|
|
25757
|
-
};
|
|
25758
25872
|
// Create domain services with injected infrastructure dependencies
|
|
25759
25873
|
this.contractDomainService = new ContractDomainService();
|
|
25760
25874
|
this.metadataDomainService = new MetadataDomainService(ipfsApi);
|
|
@@ -25773,7 +25887,15 @@ class Web3ApplicationService {
|
|
|
25773
25887
|
});
|
|
25774
25888
|
}
|
|
25775
25889
|
/**
|
|
25776
|
-
* Get metadata for a specific token from on-chain
|
|
25890
|
+
* Get metadata for a specific token from on-chain.
|
|
25891
|
+
*
|
|
25892
|
+
* Returns metadata with:
|
|
25893
|
+
* - Normalized property names (imageUrl, animationUrl - SDK standard)
|
|
25894
|
+
* - All IPFS URLs already resolved to HTTPS
|
|
25895
|
+
* - Custom properties preserved from original metadata
|
|
25896
|
+
*
|
|
25897
|
+
* Note: Normalization happens at infrastructure layer (IPFS API),
|
|
25898
|
+
* so no additional transformation needed here.
|
|
25777
25899
|
*/
|
|
25778
25900
|
async getTokenMetadata(request) {
|
|
25779
25901
|
const domainResult = await this.tokenDomainService.getTokenMetadata({
|
|
@@ -25782,6 +25904,7 @@ class Web3ApplicationService {
|
|
|
25782
25904
|
tokenId: request.tokenId || '',
|
|
25783
25905
|
chainId: request.chainId
|
|
25784
25906
|
});
|
|
25907
|
+
// Metadata is already normalized at infrastructure layer
|
|
25785
25908
|
return domainResult.metadata;
|
|
25786
25909
|
}
|
|
25787
25910
|
/**
|
|
@@ -25804,22 +25927,13 @@ class Web3ApplicationService {
|
|
|
25804
25927
|
return this.metadataDomainService.resolveIPFSUrl(url, chainId);
|
|
25805
25928
|
}
|
|
25806
25929
|
/**
|
|
25807
|
-
* Fetch and process metadata from URI with IPFS conversion
|
|
25930
|
+
* Fetch and process metadata from any URI with IPFS conversion.
|
|
25931
|
+
*
|
|
25932
|
+
* Use this for ad-hoc metadata fetching when you have a tokenURI.
|
|
25933
|
+
* Normalization happens at infrastructure layer.
|
|
25808
25934
|
*/
|
|
25809
25935
|
async fetchAndProcessMetadata(tokenUri, chainId) {
|
|
25810
|
-
|
|
25811
|
-
if (!domainMetadata)
|
|
25812
|
-
return null;
|
|
25813
|
-
// Convert from ERC token standard to our clean interface
|
|
25814
|
-
const cleanMetadata = this.metadataMapper.fromERCStandard(domainMetadata);
|
|
25815
|
-
// Add IPFS conversion if needed
|
|
25816
|
-
if (cleanMetadata.animationUrl?.startsWith('ipfs://')) {
|
|
25817
|
-
return {
|
|
25818
|
-
...cleanMetadata,
|
|
25819
|
-
animationUrlConverted: await this.resolveIPFSUrl(cleanMetadata.animationUrl, chainId)
|
|
25820
|
-
};
|
|
25821
|
-
}
|
|
25822
|
-
return cleanMetadata;
|
|
25936
|
+
return this.metadataDomainService.fetchAndProcessMetadata(tokenUri, chainId);
|
|
25823
25937
|
}
|
|
25824
25938
|
}
|
|
25825
25939
|
|
|
@@ -34434,7 +34548,7 @@ const useWeb3 = () => {
|
|
|
34434
34548
|
* chainId: 1
|
|
34435
34549
|
* });
|
|
34436
34550
|
* console.log('NFT name:', metadata?.name);
|
|
34437
|
-
* console.log('NFT image:', metadata?.
|
|
34551
|
+
* console.log('NFT image:', metadata?.imageUrl); // IPFS URLs auto-resolved to HTTPS
|
|
34438
34552
|
* ```
|
|
34439
34553
|
*/
|
|
34440
34554
|
const getTokenMetadata = react.useCallback(async (request) => {
|