diff --git a/package.json b/package.json index 9dd2642..651239d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "test": "npm run codegen && npm run lint && npm run type-check", "test-integration": "TS_NODE_PROJECT='test/integration/tsconfig.json' mocha --config=test/integration/.mocharc.json --node-env=test --exit 'test/integration/**/*.test.ts'", "test-dispenser": "TS_NODE_PROJECT='test/integration/tsconfig.json' mocha --config=test/integration/.mocharc.json --node-env=test --exit 'test/integration/Dispenser.test.ts'", - "test-simple": "TS_NODE_PROJECT='test/integration/tsconfig.json' mocha --config=test/integration/.mocharc.json --node-env=test --exit 'test/integration/SimplePublishConsume.test.ts'", + "test-simple": "TS_NODE_PROJECT='test/integration/tsconfig.json' mocha --config=test/integration/.mocharc.json --node-env=test --exit 'test/integration/SimpleSubgraph.test.ts'", "test-fixed": "TS_NODE_PROJECT='test/integration/tsconfig.json' mocha --config=test/integration/.mocharc.json --node-env=test --exit 'test/integration/FixedRateExchange.test.ts'", "test-users": "TS_NODE_PROJECT='test/integration/tsconfig.json' mocha --config=test/integration/.mocharc.json --node-env=test --exit 'test/integration/users.test.ts'", "test-ve": "TS_NODE_PROJECT='test/integration/tsconfig.json' mocha --config=test/integration/.mocharc.json --node-env=test --exit 'test/integration/VeOcean.test.ts'", diff --git a/schema.graphql b/schema.graphql index de60748..c0ba684 100644 --- a/schema.graphql +++ b/schema.graphql @@ -76,9 +76,9 @@ type Nft @entity{ tokenUri: String "address of the owner of the nft" - owner: String! + owner: User! "address of the creator of the nft" - creator: String! + creator: User! "same as id, it's just for easy discoverability" address: String! @@ -115,6 +115,7 @@ type Nft @entity{ hasMetadata: Boolean! nftData: [NftData!] @derivedFrom(field: "nft") + transferHistory: [NftTransferHistory!] @derivedFrom(field: "nft") } type NftData @entity{ @@ -148,6 +149,7 @@ type Order @entity { payer: User! amount: BigDecimal! serviceIndex: Int! + nftOwner: User! # the fees will be updated from an event that will be created after (todo) @@ -571,4 +573,16 @@ type DFReward @entity { receiver: User! availableClaims: [DFAvailableClaim!] @derivedFrom(field: "receiver") history: [DFHistory!] @derivedFrom(field: "receiver") -} \ No newline at end of file +} + +type NftTransferHistory @entity { + # ID = hash(nftAddress+txId+eventNumber) + id: ID! + nft: Nft! + oldOwner: User! + newOwner: User! + txId: String + timestamp: Int! + block: Int! +} + diff --git a/src/mappings/erc20Templates.ts b/src/mappings/erc20Templates.ts index a38d48c..31a5239 100644 --- a/src/mappings/erc20Templates.ts +++ b/src/mappings/erc20Templates.ts @@ -38,6 +38,12 @@ export function handleOrderStarted(event: OrderStarted): void { const consumer = getUser(event.params.consumer.toHex()) order.consumer = consumer.id + if (token.nft) { + const nft = Nft.load(token.nft as string) as Nft + const nftOwner = getUser(nft.owner) + order.nftOwner = nftOwner.id + } + const payer = getUser(event.params.payer.toHex()) payer.totalOrders = payer.totalOrders.plus(integer.ONE) payer.save() diff --git a/src/mappings/nftUpdate.ts b/src/mappings/nftUpdate.ts index 70e20cc..60c1ac4 100644 --- a/src/mappings/nftUpdate.ts +++ b/src/mappings/nftUpdate.ts @@ -1,4 +1,4 @@ -import { Nft, NftUpdate, NftData } from '../@types/schema' +import { Nft, NftUpdate, NftData, NftTransferHistory } from '../@types/schema' import { MetadataCreated, MetadataState, @@ -256,9 +256,22 @@ export function handleCleanedPermissions(event: CleanedPermissions): void { export function handleNftTransferred(event: Transfer): void { const id = event.address.toHex() const nft = getNftTokenWithID(id) + const oldOwner = nft.owner const newOwner = getUser(event.params.to.toHexString()) nft.owner = newOwner.id nft.save() + + const transferId = `${nft.address}-${event.transaction.hash.toHex()}-${ + event.logIndex + }` + const newTransfer = new NftTransferHistory(transferId) + newTransfer.oldOwner = oldOwner + newTransfer.nft = nft.id + newTransfer.newOwner = newOwner.id + newTransfer.txId = event.transaction.hash.toHex() + newTransfer.timestamp = event.block.timestamp.toI32() + newTransfer.block = event.block.number.toI32() + newTransfer.save() } export function handleNftData(event: DataChanged): void { diff --git a/test/integration/Datatoken.test.ts b/test/integration/Datatoken.test.ts index a47c158..0b26efc 100644 --- a/test/integration/Datatoken.test.ts +++ b/test/integration/Datatoken.test.ts @@ -358,7 +358,7 @@ describe('Datatoken tests', async () => { assert(Number(user2balance) === 0, 'Invalid user2 balance') const query = { - query: `query {token(id: "${newDtAddress.toLowerCase()}"){id,orderCount,orders {id, lastPriceToken{id}}}}` + query: `query {token(id: "${newDtAddress.toLowerCase()}"){id,orderCount,orders {id, nftOwner{id}, lastPriceToken{id}}}}` } await sleep(2000) @@ -419,5 +419,6 @@ describe('Datatoken tests', async () => { assert(token.orderCount === '1', 'Invalid orderCount after order') assert(token.orders[0].id === orderId) assert(token.orders[0].lastPriceToken.id === ZERO_ADDRESS) + assert(token.orders[0].nftOwner.id === publisher, 'invalid nftOwner') }) }) diff --git a/test/integration/Dispenser.test.ts b/test/integration/Dispenser.test.ts index 17d0748..763ffe8 100644 --- a/test/integration/Dispenser.test.ts +++ b/test/integration/Dispenser.test.ts @@ -125,8 +125,8 @@ describe('Dispenser tests', async () => { symbol, name, tokenUri, - owner, - creator, + owner{id}, + creator{id}, address, providerUrl, assetState, @@ -153,8 +153,8 @@ describe('Dispenser tests', async () => { assert(nft.symbol === nftSymbol, 'incorrect value for: symbol') assert(nft.name === nftName, 'incorrect value for: name') assert(nft.tokenUri === tokenURI, 'incorrect value for: tokenUri') - assert(nft.owner === publisher, 'incorrect value for: owner') - assert(nft.creator === publisher, 'incorrect value for: creator') + assert(nft.owner.id === publisher, 'incorrect value for: owner') + assert(nft.creator.id === publisher, 'incorrect value for: creator') assert(nft.managerRole[0] === publisher, 'incorrect value for: managerRole') assert( nft.erc20DeployerRole[0] === factoryAddress, diff --git a/test/integration/FixedRateExchange.test.ts b/test/integration/FixedRateExchange.test.ts index 7d99469..7b8e8e7 100644 --- a/test/integration/FixedRateExchange.test.ts +++ b/test/integration/FixedRateExchange.test.ts @@ -138,8 +138,8 @@ describe('Fixed Rate Exchange tests', async () => { symbol, name, tokenUri, - owner, - creator, + owner{id}, + creator{id}, address, providerUrl, assetState, @@ -166,8 +166,8 @@ describe('Fixed Rate Exchange tests', async () => { assert(nft.symbol === nftSymbol, 'incorrect value for: symbol') assert(nft.name === nftName, 'incorrect value for: name') assert(nft.tokenUri === tokenURI, 'incorrect value for: tokenUri') - assert(nft.owner === publisher, 'incorrect value for: owner') - assert(nft.creator === publisher, 'incorrect value for: creator') + assert(nft.owner.id === publisher, 'incorrect value for: owner') + assert(nft.creator.id === publisher, 'incorrect value for: creator') assert(nft.managerRole[0] === publisher, 'incorrect value for: managerRole') assert( nft.erc20DeployerRole[0] === factoryAddress, diff --git a/test/integration/Nft.test.ts b/test/integration/Nft.test.ts index 5d09988..2c96dfe 100644 --- a/test/integration/Nft.test.ts +++ b/test/integration/Nft.test.ts @@ -130,8 +130,8 @@ describe('NFT tests', async () => { symbol, name, tokenUri, - owner, - creator, + owner{id}, + creator{id}, address, providerUrl, assetState, @@ -157,8 +157,8 @@ describe('NFT tests', async () => { assert(nft.symbol === nftSymbol, 'incorrect value for: symbol') assert(nft.name === nftName, 'incorrect value for: name') assert(nft.tokenUri === tokenURI, 'incorrect value for: tokenUri') - assert(nft.owner === publisher, 'incorrect value for: owner') - assert(nft.creator === publisher, 'incorrect value for: creator') + assert(nft.owner.id === publisher, 'incorrect value for: owner') + assert(nft.creator.id === publisher, 'incorrect value for: creator') assert(nft.managerRole[0] === publisher, 'incorrect value for: managerRole') assert( nft.erc20DeployerRole[0] === factoryAddress, @@ -219,8 +219,8 @@ describe('NFT tests', async () => { symbol, name, tokenUri, - owner, - creator, + owner{id}, + creator{id}, address, providerUrl, assetState, @@ -247,8 +247,8 @@ describe('NFT tests', async () => { assert(updatedNft.symbol === nftSymbol, 'incorrect value for: symbol') assert(updatedNft.name === nftName, 'incorrect value for: name') assert(updatedNft.tokenUri === tokenURI, 'incorrect value for: tokenUri') - assert(updatedNft.owner === publisher, 'incorrect value for: owner') - assert(updatedNft.creator === publisher, 'incorrect value for: creator') + assert(updatedNft.owner.id === publisher, 'incorrect value for: owner') + assert(updatedNft.creator.id === publisher, 'incorrect value for: creator') assert( updatedNft.managerRole[0] === publisher, 'incorrect value for: managerRole' diff --git a/test/integration/SimplePublishConsume.test.ts b/test/integration/SimplePublishConsume.test.ts index 0de6d92..79071a4 100644 --- a/test/integration/SimplePublishConsume.test.ts +++ b/test/integration/SimplePublishConsume.test.ts @@ -191,7 +191,7 @@ describe('Simple Publish & consume test', async () => { const queryOriginalOwner = { query: `query { - nft(id:"${graphNftToken}"){symbol,id,owner}}` + nft(id:"${graphNftToken}"){symbol,id,owner{id}}}` } const initialResponse = await fetch(subgraphUrl, { method: 'POST', @@ -200,7 +200,7 @@ describe('Simple Publish & consume test', async () => { const initialResult = await initialResponse.json() // Checking original owner account has been set correctly assert( - initialResult.data.nft.owner.toLowerCase() === + initialResult.data.nft.owner.id.toLowerCase() === publisherAccount.toLowerCase() ) @@ -235,14 +235,14 @@ describe('Simple Publish & consume test', async () => { await sleep(2000) const query2 = { query: `query { - nft(id:"${graphNftToken}"){symbol,id,owner, transferable}}` + nft(id:"${graphNftToken}"){symbol,id,owner{id}, transferable}}` } const response = await fetch(subgraphUrl, { method: 'POST', body: JSON.stringify(query2) }) const queryResult = await response.json() - assert(queryResult.data.nft.owner === newOwnerAccount) + assert(queryResult.data.nft.owner.id === newOwnerAccount) }) it('should save provider fees after startOrder is called', async () => { diff --git a/test/integration/SimpleSubgraph.test.ts b/test/integration/SimpleSubgraph.test.ts index 38a5c00..83a1a28 100644 --- a/test/integration/SimpleSubgraph.test.ts +++ b/test/integration/SimpleSubgraph.test.ts @@ -31,6 +31,7 @@ describe('Tests coverage without provider/aquarius', async () => { let accounts: string[] let publisherAccount: string let newOwnerAccount: string + let time: number before(async () => { nft = new Nft(web3) @@ -38,6 +39,8 @@ describe('Tests coverage without provider/aquarius', async () => { accounts = await web3.eth.getAccounts() publisherAccount = accounts[0] newOwnerAccount = accounts[1].toLowerCase() + const date = new Date() + time = Math.floor(date.getTime() / 1000) }) it('should publish a dataset (create NFT + ERC20)', async () => { @@ -105,20 +108,44 @@ describe('Tests coverage without provider/aquarius', async () => { ) await sleep(2000) const erc721Address = result.events.NFTCreated.returnValues[0] - const graphNftToken = erc721Address.toLowerCase() + const nftAddress = erc721Address.toLowerCase() // Transfer the NFT - await nft.transferNft(graphNftToken, publisherAccount, newOwnerAccount) + const tx = await nft.transferNft( + nftAddress, + publisherAccount, + newOwnerAccount + ) + await sleep(2000) const query2 = { query: `query { - nft(id:"${graphNftToken}"){symbol,id,owner, transferable}}` + nft(id:"${nftAddress}"){ + symbol, + id, + owner{id}, + transferable, + transferHistory(orderBy: timestamp, orderDirection: desc){id,nft,oldOwner,newOwner,txId,timestamp,block} + }}` } const response = await fetch(subgraphUrl, { method: 'POST', body: JSON.stringify(query2) }) const queryResult = await response.json() - assert(queryResult.data.nft.owner === newOwnerAccount) + const transferHistory = queryResult.data.nft.transferHistory[0] + + assert(queryResult.data.nft.owner.id === newOwnerAccount) + assert( + transferHistory.id === + `${nftAddress}-${tx.transactionHash}-${tx.events.Transfer.logIndex}`, + 'Invalid transferHistory Id' + ) + assert(transferHistory.txId === tx.transactionHash, 'invalid txId') + assert(transferHistory.timestamp) + + assert(transferHistory.timestamp >= time - 500, 'incorrect value timestamp') + assert(transferHistory.timestamp < time + 500, 'incorrect value timestamp') + assert(transferHistory.block === tx.blockNumber, 'blockNumber') }) })