From 9130b99f85a5c2d88a6b6fd076699d6327a559a8 Mon Sep 17 00:00:00 2001 From: stadolf Date: Fri, 4 Oct 2024 16:40:17 +0200 Subject: [PATCH 1/3] adds metadata templates to the subgraph Signed-off-by: stadolf --- subgraph/package.json | 4 ++-- subgraph/schema.graphql | 10 ++++++++++ subgraph/src/ipnftMapping.ts | 28 +++++++++++++++++++++++----- subgraph/src/metadataMapping.ts | 22 ++++++++++++++++++++++ subgraph/subgraph.yaml | 13 +++++++++++++ 5 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 subgraph/src/metadataMapping.ts diff --git a/subgraph/package.json b/subgraph/package.json index 48a847ca..288739b0 100644 --- a/subgraph/package.json +++ b/subgraph/package.json @@ -8,8 +8,8 @@ "build:sepolia": "graph codegen && graph build --network sepolia", "build:mainnet": "graph codegen && graph build --network mainnet", "deploy:local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 moleculeprotocol/ipnft-subgraph", - "deploy:sepolia": "env-cmd -x -f ../.env graph deploy ip-nft-sepolia --version-label 1.2.0 --node https://subgraphs.alchemy.com/api/subgraphs/deploy --ipfs https://ipfs.satsuma.xyz --deploy-key \\$SATSUMA_DEPLOY_KEY", - "deploy:mainnet": "env-cmd -x -f ../.env graph deploy ip-nft-mainnet --version-label 1.2.0 --node https://subgraphs.alchemy.com/api/subgraphs/deploy --ipfs https://ipfs.satsuma.xyz --deploy-key \\$SATSUMA_DEPLOY_KEY", + "deploy:sepolia": "env-cmd -x -f ../.env graph deploy ip-nft-sepolia --version-label 1.2.1 --node https://subgraphs.alchemy.com/api/subgraphs/deploy --ipfs https://ipfs.satsuma.xyz --deploy-key \\$SATSUMA_DEPLOY_KEY", + "deploy:mainnet": "env-cmd -x -f ../.env graph deploy ip-nft-mainnet --version-label 1.2.1 --node https://subgraphs.alchemy.com/api/subgraphs/deploy --ipfs https://ipfs.satsuma.xyz --deploy-key \\$SATSUMA_DEPLOY_KEY", "create:local": "graph create --node http://localhost:8020/ moleculeprotocol/ipnft-subgraph", "remove:local": "graph remove --node http://localhost:8020/ moleculeprotocol/ipnft-subgraph", "test": "graph test" diff --git a/subgraph/schema.graphql b/subgraph/schema.graphql index 9501460b..07965d53 100644 --- a/subgraph/schema.graphql +++ b/subgraph/schema.graphql @@ -7,6 +7,16 @@ type Ipnft @entity { listings: [Listing!] @derivedFrom(field: "ipnft") readers: [CanRead!] @derivedFrom(field: "ipnft") ipts: [IPT!] @derivedFrom(field: "ipnft") + metadata: IpnftMetadata + updatedAtTimestamp: BigInt +} + +type IpnftMetadata @entity { + id: ID! + name: String! + image: String! + description: String! + externalURL: String! } type IPT @entity { diff --git a/subgraph/src/ipnftMapping.ts b/subgraph/src/ipnftMapping.ts index b61de5ce..0579f535 100644 --- a/subgraph/src/ipnftMapping.ts +++ b/subgraph/src/ipnftMapping.ts @@ -7,14 +7,15 @@ import { store } from '@graphprotocol/graph-ts' import { + IPNFT as IPNFTContract, IPNFTMinted as IPNFTMintedEvent, - Reserved as ReservedEvent, - ReadAccessGranted as ReadAccessGrantedEvent, - Transfer as TransferEvent, MetadataUpdate as MetadataUpdateEvent, - IPNFT as IPNFTContract + ReadAccessGranted as ReadAccessGrantedEvent, + Reserved as ReservedEvent, + Transfer as TransferEvent } from '../generated/IPNFT/IPNFT' -import { Ipnft, Reservation, CanRead } from '../generated/schema' +import { IpnftMetadata as IpnftMetadataTemplate } from '../generated/templates' +import { CanRead, Ipnft, Reservation } from '../generated/schema' export function handleTransfer(event: TransferEvent): void { if (event.params.to == Address.zero()) { @@ -80,8 +81,14 @@ export function handleMint(event: IPNFTMintedEvent): void { ipnft.tokenURI = event.params.tokenURI ipnft.createdAt = event.block.timestamp ipnft.symbol = event.params.symbol + let ipfsLocation = event.params.tokenURI.replace('ipfs://', ''); + ipnft.metadata = ipfsLocation + ipnft.updatedAtTimestamp = event.block.timestamp + IpnftMetadataTemplate.create(ipfsLocation) + store.remove('Reservation', event.params.tokenId.toString()) ipnft.save() + } export function handleMetadataUpdated(event: MetadataUpdateEvent): void { @@ -94,8 +101,19 @@ export function handleMetadataUpdated(event: MetadataUpdateEvent): void { //erc4906 is not emitting the new url, we must query it ourselves let _ipnftContract = IPNFTContract.bind(event.params._event.address); let newUri = _ipnftContract.tokenURI(event.params._tokenId) + if (!newUri) { + log.debug("no new uri found for token, likely just minted {}", [event.params._tokenId.toString()]) + return + } ipnft.tokenURI = newUri + + let ipfsLocation = newUri.replace('ipfs://', ''); + ipnft.updatedAtTimestamp = event.block.timestamp + ipnft.metadata = ipfsLocation + + IpnftMetadataTemplate.create(ipfsLocation) + ipnft.save() } diff --git a/subgraph/src/metadataMapping.ts b/subgraph/src/metadataMapping.ts new file mode 100644 index 00000000..10468e50 --- /dev/null +++ b/subgraph/src/metadataMapping.ts @@ -0,0 +1,22 @@ +import { json, Bytes, dataSource } from '@graphprotocol/graph-ts' +import { IpnftMetadata } from '../generated/schema' + +export function handleMetadata(content: Bytes): void { + let ipnftMetadata = new IpnftMetadata(dataSource.stringParam()) + const value = json.fromBytes(content).toObject() + if (value) { + const image = value.get('image') + const name = value.get('name') + const description = value.get('description') + const externalURL = value.get('external_url') + + if (name && image && description && externalURL) { + ipnftMetadata.name = name.toString() + ipnftMetadata.image = image.toString() + ipnftMetadata.externalURL = externalURL.toString() + ipnftMetadata.description = description.toString() + } + + ipnftMetadata.save() + } +} \ No newline at end of file diff --git a/subgraph/subgraph.yaml b/subgraph/subgraph.yaml index 89d2eb5e..14713803 100644 --- a/subgraph/subgraph.yaml +++ b/subgraph/subgraph.yaml @@ -228,3 +228,16 @@ templates: handler: handleScheduled - event: ScheduleReleased(indexed bytes32,indexed address,uint256) handler: handleReleased + - name: IpnftMetadata + kind: file/ipfs + mapping: + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: ./src/metadataMapping.ts + handler: handleMetadata + entities: + - IpnftMetadata + abis: + - name: IPNFT + file: ./abis/IPNFT.json + network: foundry From bd86bfe30da7ddd0ea883c5db9f7906e87b31be3 Mon Sep 17 00:00:00 2001 From: stadolf Date: Tue, 8 Oct 2024 21:51:01 +0200 Subject: [PATCH 2/3] deduplicate metadata construction code only adds metadata relation if metadata is good Signed-off-by: stadolf --- subgraph/src/ipnftMapping.ts | 35 ++++++++++++++++++--------------- subgraph/src/metadataMapping.ts | 6 +++--- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/subgraph/src/ipnftMapping.ts b/subgraph/src/ipnftMapping.ts index 0579f535..f7a6a2a3 100644 --- a/subgraph/src/ipnftMapping.ts +++ b/subgraph/src/ipnftMapping.ts @@ -1,5 +1,6 @@ import { Address, + BigInt, ByteArray, crypto, ethereum, @@ -74,18 +75,28 @@ export function handleReservation(event: ReservedEvent): void { reservation.save() } +function updateIpnftMetadata(ipnft: Ipnft, uri: string, timestamp: BigInt): Ipnft { + let ipfsLocation = uri.replace('ipfs://', ''); + if (!ipfsLocation || ipfsLocation == uri) { + log.error("Invalid URI format for tokenId {}: {}", [ipnft.id, uri]); + return ipnft; + } + + ipnft.tokenURI = uri; + ipnft.metadata = ipfsLocation; + ipnft.updatedAtTimestamp = timestamp; + IpnftMetadataTemplate.create(ipfsLocation); + + return ipnft; +} + //the underlying parameter arrays are misaligned, hence we cannot cast or unify both events export function handleMint(event: IPNFTMintedEvent): void { let ipnft = new Ipnft(event.params.tokenId.toString()) ipnft.owner = event.params.owner - ipnft.tokenURI = event.params.tokenURI ipnft.createdAt = event.block.timestamp ipnft.symbol = event.params.symbol - let ipfsLocation = event.params.tokenURI.replace('ipfs://', ''); - ipnft.metadata = ipfsLocation - ipnft.updatedAtTimestamp = event.block.timestamp - IpnftMetadataTemplate.create(ipfsLocation) - + ipnft = updateIpnftMetadata(ipnft, event.params.tokenURI, event.block.timestamp) store.remove('Reservation', event.params.tokenId.toString()) ipnft.save() @@ -101,19 +112,11 @@ export function handleMetadataUpdated(event: MetadataUpdateEvent): void { //erc4906 is not emitting the new url, we must query it ourselves let _ipnftContract = IPNFTContract.bind(event.params._event.address); let newUri = _ipnftContract.tokenURI(event.params._tokenId) - if (!newUri) { + if (!newUri || newUri == "") { log.debug("no new uri found for token, likely just minted {}", [event.params._tokenId.toString()]) return } - - ipnft.tokenURI = newUri - - let ipfsLocation = newUri.replace('ipfs://', ''); - ipnft.updatedAtTimestamp = event.block.timestamp - ipnft.metadata = ipfsLocation - - IpnftMetadataTemplate.create(ipfsLocation) - + ipnft = updateIpnftMetadata(ipnft, newUri, event.block.timestamp) ipnft.save() } diff --git a/subgraph/src/metadataMapping.ts b/subgraph/src/metadataMapping.ts index 10468e50..86298cdc 100644 --- a/subgraph/src/metadataMapping.ts +++ b/subgraph/src/metadataMapping.ts @@ -2,21 +2,21 @@ import { json, Bytes, dataSource } from '@graphprotocol/graph-ts' import { IpnftMetadata } from '../generated/schema' export function handleMetadata(content: Bytes): void { - let ipnftMetadata = new IpnftMetadata(dataSource.stringParam()) const value = json.fromBytes(content).toObject() if (value) { const image = value.get('image') const name = value.get('name') const description = value.get('description') const externalURL = value.get('external_url') - + if (name && image && description && externalURL) { + let ipnftMetadata = new IpnftMetadata(dataSource.stringParam()) ipnftMetadata.name = name.toString() ipnftMetadata.image = image.toString() ipnftMetadata.externalURL = externalURL.toString() ipnftMetadata.description = description.toString() + ipnftMetadata.save() } - ipnftMetadata.save() } } \ No newline at end of file From 2b386c984bc50acc278ac3ae93301963ec4538e2 Mon Sep 17 00:00:00 2001 From: stadolf Date: Tue, 8 Oct 2024 22:00:11 +0200 Subject: [PATCH 3/3] if rabbit claims AS uses references it could be right :D Signed-off-by: stadolf --- subgraph/src/ipnftMapping.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/subgraph/src/ipnftMapping.ts b/subgraph/src/ipnftMapping.ts index f7a6a2a3..d97ad381 100644 --- a/subgraph/src/ipnftMapping.ts +++ b/subgraph/src/ipnftMapping.ts @@ -75,19 +75,17 @@ export function handleReservation(event: ReservedEvent): void { reservation.save() } -function updateIpnftMetadata(ipnft: Ipnft, uri: string, timestamp: BigInt): Ipnft { +function updateIpnftMetadata(ipnft: Ipnft, uri: string, timestamp: BigInt): void { let ipfsLocation = uri.replace('ipfs://', ''); if (!ipfsLocation || ipfsLocation == uri) { - log.error("Invalid URI format for tokenId {}: {}", [ipnft.id, uri]); - return ipnft; + log.error("Invalid URI format for tokenId {}: {}", [ipnft.id, uri]) + return } - ipnft.tokenURI = uri; - ipnft.metadata = ipfsLocation; - ipnft.updatedAtTimestamp = timestamp; - IpnftMetadataTemplate.create(ipfsLocation); - - return ipnft; + ipnft.tokenURI = uri + ipnft.metadata = ipfsLocation + ipnft.updatedAtTimestamp = timestamp + IpnftMetadataTemplate.create(ipfsLocation) } //the underlying parameter arrays are misaligned, hence we cannot cast or unify both events @@ -96,7 +94,7 @@ export function handleMint(event: IPNFTMintedEvent): void { ipnft.owner = event.params.owner ipnft.createdAt = event.block.timestamp ipnft.symbol = event.params.symbol - ipnft = updateIpnftMetadata(ipnft, event.params.tokenURI, event.block.timestamp) + updateIpnftMetadata(ipnft, event.params.tokenURI, event.block.timestamp) store.remove('Reservation', event.params.tokenId.toString()) ipnft.save() @@ -116,7 +114,7 @@ export function handleMetadataUpdated(event: MetadataUpdateEvent): void { log.debug("no new uri found for token, likely just minted {}", [event.params._tokenId.toString()]) return } - ipnft = updateIpnftMetadata(ipnft, newUri, event.block.timestamp) + updateIpnftMetadata(ipnft, newUri, event.block.timestamp) ipnft.save() }