diff --git a/package-lock.json b/package-lock.json index 15988b4..e4b76f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "roles-subgraph", "version": "1.0.0", - "license": "MIT", + "license": "CC0-1.0", "dependencies": { "@graphprotocol/graph-cli": "^0.55.0" }, diff --git a/schema.graphql b/schema.graphql index 6c471f8..636fd88 100644 --- a/schema.graphql +++ b/schema.graphql @@ -1,28 +1,37 @@ type Nft @entity { id: ID! # tokenId + tokenAddress - address: String! + tokenAddress: String! tokenId: BigInt! owner: Account! - rolesHistory: [Role!] @derivedFrom(field: "nft") + roles: [Role!] @derivedFrom(field: "nft") } type Account @entity { id: ID! # address nfts: [Nft!] @derivedFrom(field: "owner") - rolesGranted: [Role!] @derivedFrom(field: "grantor") - rolesReceived: [Role!] @derivedFrom(field: "grantee") + rolesGranted: [RoleAssignment!] @derivedFrom(field: "grantor") + rolesReceived: [RoleAssignment!] @derivedFrom(field: "grantee") roleApprovals: [RoleApproval!] @derivedFrom(field: "grantor") } -type Role @entity { +type RoleAssignment @entity { id: ID! # grantorAddress + tokenId + tokenAddress + granteeAddress + roleHash - roleId: Bytes! + role: Role! nft: Nft! grantor: Account! grantee: Account! expirationDate: BigInt! revocable: Boolean! data: Bytes! + createdAt: BigInt! + updatedAt: BigInt! +} + +type Role @entity { + id: ID! # tokenId + tokenAddress + roleHash + roleHash: Bytes! + nft: Nft! + roleAssignments: [RoleAssignment!] @derivedFrom(field: "role") } type RoleApproval @entity { diff --git a/src/erc7432/role/grant-handler.ts b/src/erc7432/role/grant-handler.ts index bb9f498..f71db3d 100644 --- a/src/erc7432/role/grant-handler.ts +++ b/src/erc7432/role/grant-handler.ts @@ -1,7 +1,7 @@ import { log } from '@graphprotocol/graph-ts' import { RoleGranted } from '../../../generated/ERC7432-Immutable-Roles/ERC7432' import { Account, Nft } from '../../../generated/schema' -import { generateNftId, findOrCreateAccount, findOrCreateRole } from '../../utils/helper' +import { generateNftId, findOrCreateAccount, findOrCreateRoleAssignment } from '../../utils/helper' export function handleRoleGranted(event: RoleGranted): void { const tokenId = event.params._tokenId.toString() @@ -26,6 +26,10 @@ export function handleRoleGranted(event: RoleGranted): void { } const granteeAccount = findOrCreateAccount(event.params._grantee.toHex()) - const role = findOrCreateRole(event, grantorAccount, granteeAccount, nft) - log.warning('[handleRoleGranted] Role: {} NFT: {} Tx: {}', [role.id, nftId, event.transaction.hash.toHex()]) + const roleAssignment = findOrCreateRoleAssignment(event, grantorAccount, granteeAccount, nft) + log.warning('[handleRoleGranted] roleAssignment: {} NFT: {} Tx: {}', [ + roleAssignment.id, + nftId, + event.transaction.hash.toHex(), + ]) } diff --git a/src/erc7432/role/revoke-handler.ts b/src/erc7432/role/revoke-handler.ts index 71da0fe..ab6fe02 100644 --- a/src/erc7432/role/revoke-handler.ts +++ b/src/erc7432/role/revoke-handler.ts @@ -1,7 +1,7 @@ import { log } from '@graphprotocol/graph-ts' import { RoleRevoked } from '../../../generated/ERC7432-Immutable-Roles/ERC7432' -import { Account, Nft, Role } from '../../../generated/schema' -import { generateNftId, generateRoleId } from '../../utils/helper' +import { Account, Nft, RoleAssignment } from '../../../generated/schema' +import { generateNftId, generateRoleAssignmentId } from '../../utils/helper' export function handleRoleRevoked(event: RoleRevoked): void { const tokenId = event.params._tokenId.toString() @@ -28,18 +28,22 @@ export function handleRoleRevoked(event: RoleRevoked): void { return } - const roleId = generateRoleId(revoker, grantee, nft, event.params._role) - const role = Role.load(roleId) - if (!role) { - log.warning('[handleRoleRevoked] Role {} does not exist, skipping...', [roleId]) + const roleAssignmentId = generateRoleAssignmentId(revoker, grantee, nft, event.params._role) + const roleAssignment = RoleAssignment.load(roleAssignmentId) + if (!roleAssignment) { + log.warning('[handleRoleRevoked] RoleAssignment {} does not exist, skipping...', [roleAssignmentId]) return } - if (event.block.timestamp > role.expirationDate) { - log.warning('[handleRoleRevoked] Role {} already expired, skipping...', [roleId]) + if (event.block.timestamp > roleAssignment.expirationDate) { + log.warning('[handleRoleRevoked] RoleAssignment {} already expired, skipping...', [roleAssignmentId]) return } - role.expirationDate = event.block.timestamp - role.save() - log.warning('[handleRoleRevoked] Revoked Role: {} NFT: {} Tx: {}', [roleId, nftId, event.transaction.hash.toHex()]) + roleAssignment.expirationDate = event.block.timestamp + roleAssignment.save() + log.warning('[handleRoleRevoked] Revoked RoleAssignment: {} NFT: {} Tx: {}', [ + roleAssignmentId, + nftId, + event.transaction.hash.toHex(), + ]) } diff --git a/src/erc7432/role/role-approval-handler.ts b/src/erc7432/role/role-approval-handler.ts index 54490a5..7729ed3 100644 --- a/src/erc7432/role/role-approval-handler.ts +++ b/src/erc7432/role/role-approval-handler.ts @@ -18,14 +18,14 @@ export function handleRoleApprovalForAll(event: RoleApprovalForAll): void { if (isApproved) { const roleApproval = insertRoleApprovalIfNotExist(grantorAccount, operatorAccount, tokenAddress) - log.warning('[handleRoleApprovalForAll] Updated Role Approval: {} Tx: {}', [ + log.warning('[handleRoleApprovalForAll] Updated RoleAssignment Approval: {} Tx: {}', [ roleApproval.id, event.transaction.hash.toHex(), ]) } else { const roleApprovalId = generateRoleApprovalId(grantorAccount, operatorAccount, tokenAddress) deleteRoleApprovalIfExist(roleApprovalId) - log.warning('[handleRoleApprovalForAll] Removed Role Approval: {} Tx: {}', [ + log.warning('[handleRoleApprovalForAll] Removed RoleAssignment Approval: {} Tx: {}', [ roleApprovalId, event.transaction.hash.toHex(), ]) diff --git a/src/utils/helper.ts b/src/utils/helper.ts index 2950412..2c65c04 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -1,5 +1,5 @@ import { BigInt, Bytes, store } from '@graphprotocol/graph-ts' -import { Account, Nft, Role, RoleApproval } from '../../generated/schema' +import { Account, Nft, Role, RoleApproval, RoleAssignment } from '../../generated/schema' import { RoleGranted } from '../../generated/ERC7432-Immutable-Roles/ERC7432' export function findOrCreateAccount(id: string): Account { @@ -15,7 +15,7 @@ export function findOrCreateAccount(id: string): Account { export function createNft(id: string, contractAddress: string, tokenId: BigInt, owner: string): Nft { const nft = new Nft(id) nft.tokenId = tokenId - nft.address = contractAddress.toLowerCase() + nft.tokenAddress = contractAddress.toLowerCase() nft.owner = owner.toLowerCase() nft.save() return nft @@ -25,27 +25,54 @@ export function generateNftId(tokenAddress: string, tokenId: string): string { return tokenAddress + '-' + tokenId } -export function generateRoleId(grantor: Account, grantee: Account, nft: Nft, role: Bytes): string { - return grantor.id + '-' + grantee.id + '-' + nft.id + '-' + role.toHex() +export function generateRoleAssignmentId(grantor: Account, grantee: Account, nft: Nft, roleAssignment: Bytes): string { + return grantor.id + '-' + grantee.id + '-' + nft.id + '-' + roleAssignment.toHex() } -export function findOrCreateRole(event: RoleGranted, grantor: Account, grantee: Account, nft: Nft): Role { - const roleId = generateRoleId(grantor, grantee, nft, event.params._role) +export function findOrCreateRoleAssignment( + event: RoleGranted, + grantor: Account, + grantee: Account, + nft: Nft, +): RoleAssignment { + const roleAssignmentId = generateRoleAssignmentId(grantor, grantee, nft, event.params._role) + let roleAssignment = RoleAssignment.load(roleAssignmentId) + + if (!roleAssignment) { + roleAssignment = new RoleAssignment(roleAssignmentId) + roleAssignment.role = findOrCreateRole(nft, event.params._role).id + roleAssignment.nft = nft.id + roleAssignment.grantor = grantor.id + roleAssignment.grantee = grantee.id + roleAssignment.createdAt = event.block.timestamp + } + + roleAssignment.expirationDate = event.params._expirationDate + roleAssignment.revocable = event.params._revocable + roleAssignment.data = event.params._data + roleAssignment.updatedAt = event.block.timestamp + roleAssignment.save() + return roleAssignment +} + +export function findOrCreateRole(nft: Nft, roleHash: Bytes): Role { + const roleId = generateRoleId(nft, roleHash) let role = Role.load(roleId) + if (!role) { role = new Role(roleId) - role.roleId = event.params._role + role.roleHash = roleHash role.nft = nft.id - role.grantor = grantor.id - role.grantee = grantee.id + role.save() } - role.expirationDate = event.params._expirationDate - role.revocable = event.params._revocable - role.data = event.params._data - role.save() + return role } +export function generateRoleId(nft: Nft, roleHash: Bytes): string { + return nft.id + '-' + roleHash.toHex() +} + export function generateRoleApprovalId(grantor: Account, operator: Account, tokenAddress: string): string { return grantor.id + '-' + operator.id + '-' + tokenAddress.toLowerCase() } diff --git a/subgraph-goerli.yaml b/subgraph-goerli.yaml index 93e7eed..f5bd44b 100644 --- a/subgraph-goerli.yaml +++ b/subgraph-goerli.yaml @@ -38,7 +38,7 @@ dataSources: entities: - Nft - Account - - Role + - RoleAssignment - RoleApproval abis: - name: ERC7432 diff --git a/subgraph-mumbai.yaml b/subgraph-mumbai.yaml index 9dbb61a..6f506ea 100644 --- a/subgraph-mumbai.yaml +++ b/subgraph-mumbai.yaml @@ -18,7 +18,7 @@ dataSources: entities: - Nft - Account - - Role + - RoleAssignment - RoleApproval abis: - name: ERC7432 diff --git a/subgraph-polygon.yaml b/subgraph-polygon.yaml index 5661f9c..423c619 100644 --- a/subgraph-polygon.yaml +++ b/subgraph-polygon.yaml @@ -38,7 +38,7 @@ dataSources: entities: - Nft - Account - - Role + - RoleAssignment - RoleApproval abis: - name: ERC7432 diff --git a/tests/erc721/transfer-handler.test.ts b/tests/erc721/transfer-handler.test.ts index dcc3f24..c12a5ae 100644 --- a/tests/erc721/transfer-handler.test.ts +++ b/tests/erc721/transfer-handler.test.ts @@ -22,7 +22,7 @@ describe('ERC-721 Transfer Handler', () => { assert.entityCount('Account', 1) const _id = generateNftId(event.address.toHexString(), event.params.tokenId.toString()) - assert.fieldEquals('Nft', _id, 'address', ZERO_ADDRESS) + assert.fieldEquals('Nft', _id, 'tokenAddress', ZERO_ADDRESS) assert.fieldEquals('Nft', _id, 'tokenId', tokenId) assert.fieldEquals('Nft', _id, 'owner', Addresses[1]) }) @@ -38,7 +38,7 @@ describe('ERC-721 Transfer Handler', () => { assert.entityCount('Account', 2) const _id = generateNftId(event.address.toHexString(), event.params.tokenId.toString()) - assert.fieldEquals('Nft', _id, 'address', ZERO_ADDRESS) + assert.fieldEquals('Nft', _id, 'tokenAddress', ZERO_ADDRESS) assert.fieldEquals('Nft', _id, 'tokenId', tokenId) assert.fieldEquals('Nft', _id, 'owner', Addresses[2]) }) @@ -54,7 +54,7 @@ describe('ERC-721 Transfer Handler', () => { assert.entityCount('Account', 2) const _id = generateNftId(event.address.toHexString(), event.params.tokenId.toString()) - assert.fieldEquals('Nft', _id, 'address', ZERO_ADDRESS) + assert.fieldEquals('Nft', _id, 'tokenAddress', ZERO_ADDRESS) assert.fieldEquals('Nft', _id, 'tokenId', tokenId) assert.fieldEquals('Nft', _id, 'owner', Addresses[2]) }) diff --git a/tests/erc7432/grant-handler.test.ts b/tests/erc7432/grant-handler.test.ts index 7775630..f91fc82 100644 --- a/tests/erc7432/grant-handler.test.ts +++ b/tests/erc7432/grant-handler.test.ts @@ -6,7 +6,7 @@ import { BigInt, Bytes } from '@graphprotocol/graph-ts' import { createMockAccount, createMockNft, validateRole } from '../helpers/entities' import { Account } from '../../generated/schema' -const RoleId = Bytes.fromUTF8('0xGrantRole') +const RoleAssignmentId = Bytes.fromUTF8('0xGrantRole') const tokenAddress = Addresses[0] const tokenId = '123' const grantee = Addresses[1] @@ -20,12 +20,12 @@ describe('ERC-7432 RoleGranted Handler', () => { clearStore() }) - test('should not grant role when NFT does not exist', () => { - assert.entityCount('Role', 0) + test('should not grant roleAssignment when NFT does not exist', () => { + assert.entityCount('RoleAssignment', 0) assert.entityCount('Account', 0) const event = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId, tokenAddress, grantee, @@ -37,16 +37,18 @@ describe('ERC-7432 RoleGranted Handler', () => { handleRoleGranted(event) assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) assert.entityCount('Account', 0) }) - test('should not grant role when grantor does not exist', () => { + test('should not grant roleAssignment when grantor does not exist', () => { createMockNft(tokenAddress, tokenId, Addresses[0]) + assert.entityCount('RoleAssignment', 0) assert.entityCount('Role', 0) assert.entityCount('Account', 1) const event = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId, tokenAddress, grantee, @@ -57,18 +59,20 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event) + assert.entityCount('RoleAssignment', 0) assert.entityCount('Role', 0) assert.entityCount('Account', 1) }) - test('should not grant role if grantor is not NFT owner', () => { + test('should not grant roleAssignment if grantor is not NFT owner', () => { createMockNft(tokenAddress, tokenId, Addresses[0]) createMockAccount(grantor) + assert.entityCount('RoleAssignment', 0) assert.entityCount('Role', 0) assert.entityCount('Account', 2) const event = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId, tokenAddress, grantee, @@ -79,17 +83,19 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event) + assert.entityCount('RoleAssignment', 0) assert.entityCount('Role', 0) assert.entityCount('Account', 2) }) test('should grant multiple roles for the same NFT', () => { const nft = createMockNft(tokenAddress, tokenId, grantor) + assert.entityCount('RoleAssignment', 0) assert.entityCount('Role', 0) assert.entityCount('Account', 1) const event1 = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId, tokenAddress, Addresses[0], @@ -100,7 +106,7 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event1) const event2 = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId, tokenAddress, Addresses[1], @@ -111,7 +117,7 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event2) const event3 = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId, tokenAddress, Addresses[2], @@ -122,13 +128,14 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event3) - assert.entityCount('Role', 3) + assert.entityCount('RoleAssignment', 3) + assert.entityCount('Role', 1) assert.entityCount('Account', 3) const grantorAccount = new Account(grantor) - validateRole(grantorAccount, new Account(Addresses[0]), nft, RoleId, expirationDate, data) - validateRole(grantorAccount, new Account(Addresses[1]), nft, RoleId, expirationDate, data) - validateRole(grantorAccount, new Account(Addresses[2]), nft, RoleId, expirationDate, data) + validateRole(grantorAccount, new Account(Addresses[0]), nft, RoleAssignmentId, expirationDate, data) + validateRole(grantorAccount, new Account(Addresses[1]), nft, RoleAssignmentId, expirationDate, data) + validateRole(grantorAccount, new Account(Addresses[2]), nft, RoleAssignmentId, expirationDate, data) }) test('should grant multiple roles for different NFTs', () => { @@ -139,11 +146,12 @@ describe('ERC-7432 RoleGranted Handler', () => { const nft1 = createMockNft(tokenAddress, tokenId1, grantor) const nft2 = createMockNft(tokenAddress, tokenId2, grantor) const nft3 = createMockNft(tokenAddress, tokenId3, grantor) + assert.entityCount('RoleAssignment', 0) assert.entityCount('Role', 0) assert.entityCount('Account', 1) const event1 = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId1, tokenAddress, Addresses[0], @@ -154,7 +162,7 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event1) const event2 = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId2, tokenAddress, Addresses[1], @@ -165,7 +173,7 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event2) const event3 = createNewRoleGrantedEvent( - RoleId, + RoleAssignmentId, tokenId3, tokenAddress, Addresses[2], @@ -176,12 +184,13 @@ describe('ERC-7432 RoleGranted Handler', () => { ) handleRoleGranted(event3) + assert.entityCount('RoleAssignment', 3) assert.entityCount('Role', 3) assert.entityCount('Account', 3) const grantorAccount = new Account(grantor) - validateRole(grantorAccount, new Account(Addresses[0]), nft1, RoleId, expirationDate, data) - validateRole(grantorAccount, new Account(Addresses[1]), nft2, RoleId, expirationDate, data) - validateRole(grantorAccount, new Account(Addresses[2]), nft3, RoleId, expirationDate, data) + validateRole(grantorAccount, new Account(Addresses[0]), nft1, RoleAssignmentId, expirationDate, data) + validateRole(grantorAccount, new Account(Addresses[1]), nft2, RoleAssignmentId, expirationDate, data) + validateRole(grantorAccount, new Account(Addresses[2]), nft3, RoleAssignmentId, expirationDate, data) }) }) diff --git a/tests/erc7432/revoke-handler.test.ts b/tests/erc7432/revoke-handler.test.ts index 2e9d364..ba8d5a6 100644 --- a/tests/erc7432/revoke-handler.test.ts +++ b/tests/erc7432/revoke-handler.test.ts @@ -2,13 +2,13 @@ import { assert, describe, test, clearStore, afterEach } from 'matchstick-as' import { createNewRoleRevokedEvent } from '../helpers/events' import { handleRoleRevoked } from '../../src/erc7432' import { Bytes, BigInt } from '@graphprotocol/graph-ts' -import { createMockAccount, createMockNft, createMockRole, validateRole } from '../helpers/entities' +import { createMockAccount, createMockNft, createMockRoleAssignment, validateRole } from '../helpers/entities' import { Addresses, ONE, TWO } from '../helpers/contants' -import { generateNftId, generateRoleId } from '../../src/utils/helper' +import { generateNftId, generateRoleAssignmentId } from '../../src/utils/helper' import { Account, Nft } from '../../generated/schema' const tokenId = '123' -const RoleId = Bytes.fromUTF8('0xGrantRole') +const RoleAssignmentId = Bytes.fromUTF8('0xGrantRole') const tokenAddress = Addresses[0] const grantee = Addresses[1] const revoker = Addresses[2] @@ -20,64 +20,64 @@ describe('ERC-7432 RoleRevoked Handler', () => { clearStore() }) - test('should not revoke role when NFT does not exist', () => { - assert.entityCount('Role', 0) + test('should not revoke roleAssignment when NFT does not exist', () => { + assert.entityCount('RoleAssignment', 0) const nftId = generateNftId(tokenAddress, tokenId) const nft = new Nft(nftId) - nft.address = tokenAddress + nft.tokenAddress = tokenAddress nft.tokenId = BigInt.fromString(tokenId) nft.owner = revoker - const event = createNewRoleRevokedEvent(RoleId, nft, revoker, grantee) + const event = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, grantee) handleRoleRevoked(event) - assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) }) - test('should not revoke role when revoker does not exist', () => { + test('should not revoke roleAssignment when revoker does not exist', () => { const nft = createMockNft(tokenAddress, tokenId, Addresses[0]) - assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) - const event = createNewRoleRevokedEvent(RoleId, nft, revoker, grantee) + const event = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, grantee) handleRoleRevoked(event) - assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) }) - test('should not revoke role when grantee does not exist', () => { + test('should not revoke roleAssignment when grantee does not exist', () => { const nft = createMockNft(tokenAddress, tokenId, revoker) - assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) - const event = createNewRoleRevokedEvent(RoleId, nft, revoker, grantee) + const event = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, grantee) handleRoleRevoked(event) - assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) }) - test('should not revoke role when role does not exist', () => { + test('should not revoke roleAssignment when roleAssignment does not exist', () => { const nft = createMockNft(tokenAddress, tokenId, revoker) createMockAccount(grantee) - assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) - const event = createNewRoleRevokedEvent(RoleId, nft, revoker, grantee) + const event = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, grantee) handleRoleRevoked(event) - assert.entityCount('Role', 0) + assert.entityCount('RoleAssignment', 0) }) - test('should not revoke role when role already expired', () => { + test('should not revoke roleAssignment when roleAssignment already expired', () => { const nft = createMockNft(tokenAddress, tokenId, revoker) const granteeAccount = createMockAccount(grantee) - createMockRole(RoleId, revoker, grantee, nft, BigInt.fromI32(0)) - assert.entityCount('Role', 1) + createMockRoleAssignment(RoleAssignmentId, revoker, grantee, nft, BigInt.fromI32(0)) + assert.entityCount('RoleAssignment', 1) - const event = createNewRoleRevokedEvent(RoleId, nft, revoker, grantee) + const event = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, grantee) handleRoleRevoked(event) - assert.entityCount('Role', 1) - const _id = generateRoleId(new Account(revoker), granteeAccount, nft, RoleId) - assert.fieldEquals('Role', _id, 'expirationDate', '0') + assert.entityCount('RoleAssignment', 1) + const _id = generateRoleAssignmentId(new Account(revoker), granteeAccount, nft, RoleAssignmentId) + assert.fieldEquals('RoleAssignment', _id, 'expirationDate', '0') }) test('should revoke multiple roles for the same NFT', () => { @@ -85,25 +85,27 @@ describe('ERC-7432 RoleRevoked Handler', () => { const account1 = createMockAccount(Addresses[0]) const account2 = createMockAccount(Addresses[1]) const account3 = createMockAccount(Addresses[2]) - createMockRole(RoleId, revoker, Addresses[0], nft, expirationDate) - createMockRole(RoleId, revoker, Addresses[1], nft, expirationDate.plus(ONE)) - createMockRole(RoleId, revoker, Addresses[2], nft, expirationDate.plus(TWO)) - assert.entityCount('Role', 3) + createMockRoleAssignment(RoleAssignmentId, revoker, Addresses[0], nft, expirationDate) + createMockRoleAssignment(RoleAssignmentId, revoker, Addresses[1], nft, expirationDate.plus(ONE)) + createMockRoleAssignment(RoleAssignmentId, revoker, Addresses[2], nft, expirationDate.plus(TWO)) + assert.entityCount('RoleAssignment', 3) + assert.entityCount('Role', 1) - const event1 = createNewRoleRevokedEvent(RoleId, nft, revoker, Addresses[0]) + const event1 = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, Addresses[0]) handleRoleRevoked(event1) - const event2 = createNewRoleRevokedEvent(RoleId, nft, revoker, Addresses[1]) + const event2 = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, Addresses[1]) handleRoleRevoked(event2) - const event3 = createNewRoleRevokedEvent(RoleId, nft, revoker, Addresses[2]) + const event3 = createNewRoleRevokedEvent(RoleAssignmentId, nft, revoker, Addresses[2]) handleRoleRevoked(event3) - assert.entityCount('Role', 3) + assert.entityCount('RoleAssignment', 3) + assert.entityCount('Role', 1) const revokerAccount = new Account(revoker) - validateRole(revokerAccount, account1, nft, RoleId, ONE, data) - validateRole(revokerAccount, account2, nft, RoleId, ONE, data) - validateRole(revokerAccount, account3, nft, RoleId, ONE, data) + validateRole(revokerAccount, account1, nft, RoleAssignmentId, ONE, data) + validateRole(revokerAccount, account2, nft, RoleAssignmentId, ONE, data) + validateRole(revokerAccount, account3, nft, RoleAssignmentId, ONE, data) }) test('should revoke multiple roles for different NFTs', () => { @@ -111,24 +113,26 @@ describe('ERC-7432 RoleRevoked Handler', () => { const nft1 = createMockNft(tokenAddress, '123', revoker) const nft2 = createMockNft(tokenAddress, '456', revoker) const nft3 = createMockNft(tokenAddress, '789', revoker) - createMockRole(RoleId, revoker, grantee, nft1, expirationDate) - createMockRole(RoleId, revoker, grantee, nft2, expirationDate.plus(ONE)) - createMockRole(RoleId, revoker, grantee, nft3, expirationDate.plus(TWO)) + createMockRoleAssignment(RoleAssignmentId, revoker, grantee, nft1, expirationDate) + createMockRoleAssignment(RoleAssignmentId, revoker, grantee, nft2, expirationDate.plus(ONE)) + createMockRoleAssignment(RoleAssignmentId, revoker, grantee, nft3, expirationDate.plus(TWO)) + assert.entityCount('RoleAssignment', 3) assert.entityCount('Role', 3) - const event1 = createNewRoleRevokedEvent(RoleId, nft1, revoker, grantee) + const event1 = createNewRoleRevokedEvent(RoleAssignmentId, nft1, revoker, grantee) handleRoleRevoked(event1) - const event2 = createNewRoleRevokedEvent(RoleId, nft2, revoker, grantee) + const event2 = createNewRoleRevokedEvent(RoleAssignmentId, nft2, revoker, grantee) handleRoleRevoked(event2) - const event3 = createNewRoleRevokedEvent(RoleId, nft3, revoker, grantee) + const event3 = createNewRoleRevokedEvent(RoleAssignmentId, nft3, revoker, grantee) handleRoleRevoked(event3) + assert.entityCount('RoleAssignment', 3) assert.entityCount('Role', 3) const revokerAccount = new Account(revoker) - validateRole(revokerAccount, granteeAccount, nft1, RoleId, ONE, data) - validateRole(revokerAccount, granteeAccount, nft2, RoleId, ONE, data) - validateRole(revokerAccount, granteeAccount, nft3, RoleId, ONE, data) + validateRole(revokerAccount, granteeAccount, nft1, RoleAssignmentId, ONE, data) + validateRole(revokerAccount, granteeAccount, nft2, RoleAssignmentId, ONE, data) + validateRole(revokerAccount, granteeAccount, nft3, RoleAssignmentId, ONE, data) }) }) diff --git a/tests/helpers/entities.ts b/tests/helpers/entities.ts index 5df1ca4..a3214f9 100644 --- a/tests/helpers/entities.ts +++ b/tests/helpers/entities.ts @@ -1,11 +1,11 @@ import { BigInt, Bytes } from '@graphprotocol/graph-ts' -import { Account, Nft, Role, RoleApproval } from '../../generated/schema' -import { generateNftId, generateRoleId, generateRoleApprovalId } from '../../src/utils/helper' +import { Account, Nft, RoleAssignment, RoleApproval, Role } from '../../generated/schema' +import { generateNftId, generateRoleAssignmentId, generateRoleApprovalId, generateRoleId } from '../../src/utils/helper' import { assert } from 'matchstick-as' export function createMockNft(tokenAddress: string, tokenId: string, ownerAddress: string): Nft { const nft = new Nft(generateNftId(tokenAddress, tokenId)) - nft.address = tokenAddress + nft.tokenAddress = tokenAddress nft.tokenId = BigInt.fromString(tokenId) const nftOwner = createMockAccount(ownerAddress) @@ -21,18 +21,32 @@ export function createMockAccount(ethAddress: string): Account { return account } -export function createMockRole(role: Bytes, grantor: string, grantee: string, nft: Nft, expirationDate: BigInt): Role { - const roleId = generateRoleId(new Account(grantor), new Account(grantee), nft, role) - const newRole = new Role(roleId) - newRole.roleId = role - newRole.nft = nft.id - newRole.grantor = grantor - newRole.grantee = grantee - newRole.expirationDate = expirationDate - newRole.revocable = true - newRole.data = Bytes.fromUTF8('data') - newRole.save() - return newRole +export function createMockRoleAssignment( + roleHash: Bytes, + grantor: string, + grantee: string, + nft: Nft, + expirationDate: BigInt, +): RoleAssignment { + const roleId = generateRoleId(nft, roleHash) + const role = new Role(roleId) + role.roleHash = roleHash + role.nft = nft.id + role.save() + + const roleAssignmentId = generateRoleAssignmentId(new Account(grantor), new Account(grantee), nft, roleHash) + const newRoleAssignment = new RoleAssignment(roleAssignmentId) + newRoleAssignment.role = `${nft.id}-${roleHash.toHex()}` + newRoleAssignment.nft = nft.id + newRoleAssignment.grantor = grantor + newRoleAssignment.grantee = grantee + newRoleAssignment.expirationDate = expirationDate + newRoleAssignment.revocable = true + newRoleAssignment.data = Bytes.fromUTF8('data') + newRoleAssignment.createdAt = BigInt.fromI32(123) + newRoleAssignment.updatedAt = BigInt.fromI32(123) + newRoleAssignment.save() + return newRoleAssignment } export function createMockRoleApproval(grantor: string, operator: string, tokenAddress: string): RoleApproval { @@ -49,17 +63,21 @@ export function validateRole( grantor: Account, grantee: Account, nft: Nft, - role: Bytes, + roleAssignment: Bytes, expirationDate: BigInt, data: Bytes, ): void { - const _id = generateRoleId(grantor, grantee, nft, role) - assert.fieldEquals('Role', _id, 'roleId', role.toHex()) - assert.fieldEquals('Role', _id, 'nft', nft.id) - assert.fieldEquals('Role', _id, 'grantor', grantor.id) - assert.fieldEquals('Role', _id, 'grantee', grantee.id) - assert.fieldEquals('Role', _id, 'expirationDate', expirationDate.toString()) - assert.fieldEquals('Role', _id, 'data', data.toHex()) + const roleId = generateRoleId(nft, roleAssignment) + assert.fieldEquals('Role', roleId, 'roleHash', roleAssignment.toHex()) + assert.fieldEquals('Role', roleId, 'nft', nft.id) + + const roleAssignemntId = generateRoleAssignmentId(grantor, grantee, nft, roleAssignment) + assert.fieldEquals('RoleAssignment', roleAssignemntId, 'role', `${nft.id}-${roleAssignment.toHex()}`) + assert.fieldEquals('RoleAssignment', roleAssignemntId, 'nft', nft.id) + assert.fieldEquals('RoleAssignment', roleAssignemntId, 'grantor', grantor.id) + assert.fieldEquals('RoleAssignment', roleAssignemntId, 'grantee', grantee.id) + assert.fieldEquals('RoleAssignment', roleAssignemntId, 'expirationDate', expirationDate.toString()) + assert.fieldEquals('RoleAssignment', roleAssignemntId, 'data', data.toHex()) } export function validateRoleApproval(grantor: string, operator: string, tokenAddress: string): void { diff --git a/tests/helpers/events.ts b/tests/helpers/events.ts index b12cdf1..b51372d 100644 --- a/tests/helpers/events.ts +++ b/tests/helpers/events.ts @@ -14,11 +14,16 @@ export function createTransferEvent(from: string, to: string, tokenId: string, a return event } -export function createNewRoleRevokedEvent(role: Bytes, nft: Nft, revoker: string, grantee: string): RoleRevoked { +export function createNewRoleRevokedEvent( + roleAssignment: Bytes, + nft: Nft, + revoker: string, + grantee: string, +): RoleRevoked { const event = changetype(newMockEvent()) event.parameters = new Array() - event.parameters.push(buildEventParamBytes('_role', role)) - event.parameters.push(buildEventParamAddress('_tokenAddress', nft.address)) + event.parameters.push(buildEventParamBytes('_role', roleAssignment)) + event.parameters.push(buildEventParamAddress('_tokenAddress', nft.tokenAddress)) event.parameters.push(buildEventParamUint('_tokenId', nft.tokenId)) event.parameters.push(buildEventParamAddress('_revoker', revoker)) event.parameters.push(buildEventParamAddress('_grantee', grantee)) @@ -26,7 +31,7 @@ export function createNewRoleRevokedEvent(role: Bytes, nft: Nft, revoker: string } export function createNewRoleGrantedEvent( - role: Bytes, + roleAssignment: Bytes, tokenId: string, tokenAddress: string, grantee: string, @@ -37,7 +42,7 @@ export function createNewRoleGrantedEvent( ): RoleGranted { const event = changetype(newMockEvent()) event.parameters = new Array() - event.parameters.push(buildEventParamBytes('_role', role)) + event.parameters.push(buildEventParamBytes('_role', roleAssignment)) event.parameters.push(buildEventParamAddress('_tokenAddress', tokenAddress)) event.parameters.push(buildEventParamUint('_tokenId', BigInt.fromString(tokenId))) event.parameters.push(buildEventParamAddress('_grantor', grantor))