diff --git a/models/asset.ts b/models/asset.ts index c1724169..aa0fca0d 100644 --- a/models/asset.ts +++ b/models/asset.ts @@ -23,7 +23,7 @@ export class AssetModel extends BaseModel { description: this.string(''), links: this.attr([]), tokenId: this.string(''), - tokenIdType: this.string(''), + tokenIdType: this.number(null), isNativeToken: this.boolean(false), hash: this.string(''), verification: this.attr({}), @@ -46,7 +46,7 @@ export class AssetModel extends BaseModel { declare description?: string declare links?: LinkMetadata[] declare tokenId?: string - declare tokenIdType?: string + declare tokenIdType?: number declare isNativeToken?: boolean declare hash: string declare verification?: ImageMetadata['verification'] diff --git a/package.json b/package.json index 06162c00..697b9c2b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "typechain": "typechain --target web3-v1 --out-dir types/contracts/ './node_modules/@lukso/lsp-smart-contracts/artifacts/*.json'" }, "devDependencies": { - "@erc725/erc725.js": "0.21.2", + "@erc725/erc725.js": "0.21.1", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@formatjs/intl": "^2.9.6", "@lukso/lsp-smart-contracts": "0.12.1", diff --git a/repositories/asset.ts b/repositories/asset.ts index 34d8531f..03e3540c 100644 --- a/repositories/asset.ts +++ b/repositories/asset.ts @@ -30,7 +30,7 @@ export class AssetRepository extends Repository { } if (storageAsset.standard === 'LSP8IdentifiableDigitalAsset') { - assetData = await fetchLsp8Data( + assetData = await getLsp8Data( assetAddress, storageAsset?.tokenIdType, storageAsset?.tokenId diff --git a/shared/schemas/LSP8IdentifiableDigitalAsset.json b/shared/schemas/LSP8IdentifiableDigitalAsset.json deleted file mode 100644 index 7d0af41f..00000000 --- a/shared/schemas/LSP8IdentifiableDigitalAsset.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "name": "LSP8MetadataAddress:
", - "key": "0x73dcc7c3c4096cdc7f8a0000
", - "keyType": "Mapping", - "valueType": "Mixed", - "valueContent": "Mixed" - }, - { - "name": "LSP8MetadataAddress:", - "key": "0x73dcc7c3c4096cdc7f8a0000", - "keyType": "Mapping", - "valueType": "Mixed", - "valueContent": "Mixed" - }, - { - "name": "LSP8MetadataAddress:", - "key": "0x73dcc7c3c4096cdc7f8a0000", - "keyType": "Mapping", - "valueType": "Mixed", - "valueContent": "Mixed" - }, - { - "name": "LSP8MetadataJSON:
", - "key": "0x9a26b4060ae7f7d5e3cd0000
", - "keyType": "Mapping", - "valueType": "bytes", - "valueContent": "JSONURL" - }, - { - "name": "LSP8MetadataJSON:", - "key": "0x9a26b4060ae7f7d5e3cd0000", - "keyType": "Mapping", - "valueType": "bytes", - "valueContent": "JSONURL" - }, - { - "name": "LSP8MetadataJSON:", - "key": "0x9a26b4060ae7f7d5e3cd0000", - "keyType": "Mapping", - "valueType": "bytes", - "valueContent": "JSONURL" - }, - { - "name": "LSP8TokenIdType", - "key": "0x715f248956de7ce65e94d9d836bfead479f7e70d69b718d47bfe7b00e05b4fe4", - "keyType": "Singleton", - "valueType": "uint256", - "valueContent": "Number" - } -] diff --git a/types/assets.ts b/types/assets.ts index 5a733d44..96ca2bfb 100644 --- a/types/assets.ts +++ b/types/assets.ts @@ -1,4 +1,8 @@ -import { INTERFACE_IDS, ImageMetadata } from '@lukso/lsp-smart-contracts' +import { + INTERFACE_IDS, + ImageMetadata, + LSP8_TOKEN_ID_TYPES, +} from '@lukso/lsp-smart-contracts' export type InterfaceId = keyof typeof INTERFACE_IDS @@ -16,11 +20,13 @@ export enum AssetFilter { created = 'created', } -export enum Lsp8TokenIdType { - address = '1', - number = '2', - bytes32 = '3', -} +export const Lsp8TokenIdType = { + NUMBER: 0, + STRING: 1, + UNIQUE_ID: 2, + HASH: 3, + ADDRESS: 4, +} as typeof LSP8_TOKEN_ID_TYPES export type Base64EncodedImage = `data:image/jpeg;base64${string}` diff --git a/utils/fetchAsset.ts b/utils/fetchAsset.ts index 9d7b8a90..18f75688 100644 --- a/utils/fetchAsset.ts +++ b/utils/fetchAsset.ts @@ -1,10 +1,8 @@ -import { Asset } from '@/models/asset' - export const fetchAsset = async ( address: Address, profileAddress?: Address, tokenIds?: string[] -): Promise => { +) => { const standard = await detectStandard(address) switch (standard) { @@ -16,6 +14,7 @@ export const fetchAsset = async ( } default: + console.warn(`Asset ${address} standard is not supported`) return [] } } diff --git a/utils/fetchLSP7Assets.ts b/utils/fetchLSP7Assets.ts index d4b10ba3..3aac082b 100644 --- a/utils/fetchLSP7Assets.ts +++ b/utils/fetchLSP7Assets.ts @@ -1,4 +1,5 @@ import LSP7DigitalAsset from '@lukso/lsp-smart-contracts/artifacts/LSP7DigitalAsset.json' +import { AbiItem } from 'web3-utils' import { ImageMetadataEncoded } from '@/types/assets' import { LSP7DigitalAsset as LSP7DigitalAssetInterface } from '@/types/contracts' @@ -13,7 +14,7 @@ export const fetchLsp7Assets = async ( const { contract } = useWeb3(PROVIDERS.RPC) const lsp7Contract = contract( - LSP7DigitalAsset.abi as any, + LSP7DigitalAsset.abi as AbiItem[], address ) let balance = '' diff --git a/utils/fetchLSP8Assets.ts b/utils/fetchLSP8Assets.ts index 1acf74ed..9a0ab36a 100644 --- a/utils/fetchLSP8Assets.ts +++ b/utils/fetchLSP8Assets.ts @@ -1,4 +1,5 @@ import LSP8IdentifiableDigitalAsset from '@lukso/lsp-smart-contracts/artifacts/LSP8IdentifiableDigitalAsset.json' +import { AbiItem } from 'web3-utils' import { LSP8IdentifiableDigitalAsset as LSP8IdentifiableDigitalAssetInterface } from '@/types/contracts/LSP8IdentifiableDigitalAsset' import { Asset } from '@/models/asset' @@ -11,7 +12,7 @@ export const fetchLsp8Assets = async ( ): Promise => { const { contract } = useWeb3(PROVIDERS.RPC) const lsp8Contract = contract( - LSP8IdentifiableDigitalAsset.abi as any, + LSP8IdentifiableDigitalAsset.abi as AbiItem[], address ) const tokenSupply = await lsp8Contract.methods.totalSupply().call() @@ -36,7 +37,7 @@ export const fetchLsp8Assets = async ( tokenId, address ) - const getData = await fetchLsp8Data(address, tokenIdType, tokenId) + const getData = await getLsp8Data(address, tokenIdType, tokenId) const { description, images: metadataImages, diff --git a/utils/fetchLsp7Balance.ts b/utils/fetchLsp7Balance.ts index 1a52aef7..ceaaa6b6 100644 --- a/utils/fetchLsp7Balance.ts +++ b/utils/fetchLsp7Balance.ts @@ -1,4 +1,5 @@ import LSP7DigitalAsset from '@lukso/lsp-smart-contracts/artifacts/LSP7DigitalAsset.json' +import { AbiItem } from 'web3-utils' import { LSP7DigitalAsset as LSP7DigitalAssetInterface } from '@/types/contracts' @@ -8,7 +9,7 @@ export const fetchLsp7Balance = async ( ) => { const { contract } = useWeb3(PROVIDERS.RPC) const lsp7Contract = contract( - LSP7DigitalAsset.abi as any, + LSP7DigitalAsset.abi as AbiItem[], assetAddress ) diff --git a/utils/fetchLsp8Metadata.ts b/utils/fetchLsp8Metadata.ts index 041cbfa9..53e34603 100644 --- a/utils/fetchLsp8Metadata.ts +++ b/utils/fetchLsp8Metadata.ts @@ -1,24 +1,13 @@ import { LSP4DigitalAssetMetadataJSON } from '@lukso/lsp-smart-contracts' -import { ERC725JSONSchema } from '@erc725/erc725.js' +import ERC725, { ERC725JSONSchema } from '@erc725/erc725.js' +import LSP8IdentifiableDigitalAsset from '@erc725/erc725.js/schemas/LSP8IdentifiableDigitalAsset.json' import { Lsp8TokenIdType } from '@/types/assets' -import LSP8IdentifiableDigitalAsset from '@/shared/schemas/LSP8IdentifiableDigitalAsset.json' export const fetchLsp8Metadata = async ( tokenId: string, assetAddress: Address -): Promise<[LSP4DigitalAssetMetadataJSON, string]> => { - const lsp8MetadataGetter = async ( - tokenIdType: string, - tokenId: string - ): Promise => { - const lsp8Metadata = await erc725.fetchData({ - keyName: `LSP8MetadataJSON:<${tokenIdType}>`, - dynamicKeyParts: tokenId, - }) - return validateLsp4MetaData(lsp8Metadata.value) - } - +): Promise<[LSP4DigitalAssetMetadataJSON, number]> => { const { getInstance } = useErc725() const erc725 = getInstance( assetAddress, @@ -26,43 +15,32 @@ export const fetchLsp8Metadata = async ( ) try { - const lsp8DigitalAsset = await erc725.fetchData(['LSP8TokenIdType']) - const tokenIdType = lsp8DigitalAsset[0].value?.toString() + const lsp8DigitalAsset = await erc725.fetchData('LSP8TokenIdType') + const tokenIdType = Number(lsp8DigitalAsset.value) - // fetch LSP8MetadataJSON depending on tokenIdType + // fetch metadata depending on tokenIdType switch (tokenIdType) { - case Lsp8TokenIdType.address: + case Lsp8TokenIdType.NUMBER: return [ - await lsp8MetadataGetter( - 'address', - // ethers.utils.hexDataSlice(tokenId.toString(), 12) - tokenId.toString() - ), + await getMetadata(tokenIdType, parseInt(tokenId).toString(), erc725), tokenIdType, ] - case Lsp8TokenIdType.number: + case Lsp8TokenIdType.STRING: + case Lsp8TokenIdType.UNIQUE_ID: + case Lsp8TokenIdType.HASH: return [ - await lsp8MetadataGetter('uint256', parseInt(tokenId).toString()), + await getMetadata(tokenIdType, tokenId.toString(), erc725), tokenIdType, ] - case Lsp8TokenIdType.bytes32: + case Lsp8TokenIdType.ADDRESS: return [ - await lsp8MetadataGetter('bytes32', tokenId.toString()), + await getMetadata(tokenIdType, tokenId.slice(0, 42), erc725), tokenIdType, ] default: - return [ - { - LSP4Metadata: { - description: '', - links: [], - images: [[]], - icon: [], - assets: [], - }, - }, - '', - ] + throw new Error( + `Unsupported LSP8 tokenIdType '${tokenIdType}' for '${assetAddress}' asset` + ) } } catch (error) { console.error(error) @@ -76,14 +54,31 @@ export const fetchLsp8Metadata = async ( assets: [], }, }, - '', + -1, ] } } -export const fetchLsp8Data = async ( +const tokenIdTypeString = (tokenIdType: number) => { + switch (tokenIdType) { + case Lsp8TokenIdType.NUMBER: + return 'uint256' + case Lsp8TokenIdType.STRING: + return 'string' + case Lsp8TokenIdType.UNIQUE_ID: + return 'bytes32' + case Lsp8TokenIdType.HASH: + return 'bytes32' + case Lsp8TokenIdType.ADDRESS: + return 'address' + default: + throw new Error(`Unsupported LSP8 tokenIdType '${tokenIdType}'`) + } +} + +export const getLsp8Data = async ( assetAddress: string, - tokenIdType?: string, + tokenIdType?: number, tokenId?: string ) => { if (!tokenIdType || !tokenId) { @@ -96,9 +91,23 @@ export const fetchLsp8Data = async ( LSP8IdentifiableDigitalAsset as ERC725JSONSchema[] ) const metaData = await erc725.getData({ - keyName: `LSP8MetadataJSON:<${tokenIdType}>`, + keyName: `LSP8MetadataTokenURI:<${tokenIdTypeString(tokenIdType)}>`, dynamicKeyParts: tokenId, }) return metaData } + +const getMetadata = async ( + tokenIdType: number, + tokenIdValue: string, + erc725: ERC725 +) => { + const lsp8Metadata = await erc725.fetchData([ + { + keyName: `LSP8MetadataTokenURI:<${tokenIdTypeString(tokenIdType)}>`, + dynamicKeyParts: tokenIdValue, + }, + ]) + return validateLsp4MetaData(lsp8Metadata[0].value) +} diff --git a/utils/supportInterface.ts b/utils/supportInterface.ts index a4b092eb..b188804e 100644 --- a/utils/supportInterface.ts +++ b/utils/supportInterface.ts @@ -1,3 +1,5 @@ +import { AbiItem } from 'web3-utils' + import { LSP0ERC725Account } from '@/types/contracts' /** @@ -16,7 +18,7 @@ export const supportInterface = async ( try { const eip165Contract = contract( - eip165ABI as any, + eip165ABI as AbiItem[], address ) return await eip165Contract.methods.supportsInterface(interfaceId).call() diff --git a/utils/validateLSP4Metadata.ts b/utils/validateLSP4Metadata.ts index 195a2968..5e52a623 100644 --- a/utils/validateLSP4Metadata.ts +++ b/utils/validateLSP4Metadata.ts @@ -1,5 +1,7 @@ -import { Verification } from '@erc725/erc725.js/build/main/src/types' -import { LSP4DigitalAssetMetadataJSON } from '@lukso/lsp-smart-contracts' +import { + LSP4DigitalAssetMetadataJSON, + AssetMetadata, +} from '@lukso/lsp-smart-contracts' export const validateLsp4MetaData = ( LSP4MetadataJSON: any @@ -142,6 +144,6 @@ export const validateVerification = (getDataObject: any) => { 'verification' in getDataObject?.value && 'data' in getDataObject?.value?.verification && 'method' in getDataObject?.value?.verification - ? (getDataObject.value?.verification as Verification) + ? (getDataObject.value?.verification as AssetMetadata['verification']) : undefined } diff --git a/yarn.lock b/yarn.lock index 176b223f..605551b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1020,16 +1020,15 @@ __metadata: languageName: node linkType: hard -"@erc725/erc725.js@npm:0.21.2": - version: 0.21.2 - resolution: "@erc725/erc725.js@npm:0.21.2" +"@erc725/erc725.js@npm:0.21.1": + version: 0.21.1 + resolution: "@erc725/erc725.js@npm:0.21.1" dependencies: - add: "npm:^2.0.6" ethereumjs-util: "npm:^7.1.5" web3-eth-abi: "npm:^1.10.0" web3-providers-http: "npm:^1.10.0" web3-utils: "npm:^1.10.0" - checksum: d2e8cb4743dbd07ea67d41258153aa36f4859f91ebf4fc89830a361270b9db96d69efa3aaeec8475443f39473555a05931e195e474dfdf2de60a6a99509b3dda + checksum: 63bf4a3a7eeca96dc66a4772c4362aa0286d4b50483025b398981aa8902bfbb4e87595b57a95e5d53728c71763c98ff0650c7886e26ed0913dad83bc065640dc languageName: node linkType: hard @@ -3714,13 +3713,6 @@ __metadata: languageName: node linkType: hard -"add@npm:^2.0.6": - version: 2.0.6 - resolution: "add@npm:2.0.6" - checksum: 1d2c0780a660edfc161b7784a846865b28dcd8514cc6042c8e283298f379cd3b150412b67a12603a4ac885912f05759f3e2993c27219f85d1face316ffc0b175 - languageName: node - linkType: hard - "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -16371,7 +16363,7 @@ __metadata: version: 0.0.0-use.local resolution: "wallet.universalprofile.cloud@workspace:." dependencies: - "@erc725/erc725.js": "npm:0.21.2" + "@erc725/erc725.js": "npm:0.21.1" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@formatjs/intl": "npm:^2.9.6" "@lukso/lsp-smart-contracts": "npm:0.12.1"