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..d97ad381 100644 --- a/subgraph/src/ipnftMapping.ts +++ b/subgraph/src/ipnftMapping.ts @@ -1,5 +1,6 @@ import { Address, + BigInt, ByteArray, crypto, ethereum, @@ -7,14 +8,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()) { @@ -73,15 +75,29 @@ export function handleReservation(event: ReservedEvent): void { reservation.save() } +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.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 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 + updateIpnftMetadata(ipnft, event.params.tokenURI, event.block.timestamp) store.remove('Reservation', event.params.tokenId.toString()) ipnft.save() + } export function handleMetadataUpdated(event: MetadataUpdateEvent): void { @@ -94,8 +110,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) - - ipnft.tokenURI = newUri + if (!newUri || newUri == "") { + log.debug("no new uri found for token, likely just minted {}", [event.params._tokenId.toString()]) + return + } + updateIpnftMetadata(ipnft, newUri, event.block.timestamp) ipnft.save() } diff --git a/subgraph/src/metadataMapping.ts b/subgraph/src/metadataMapping.ts new file mode 100644 index 00000000..86298cdc --- /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 { + 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() + } + + } +} \ 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