Skip to content

Commit

Permalink
Add roles registry entity and update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Lima committed Oct 24, 2023
1 parent 00b9c74 commit 1efa572
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 60 deletions.
16 changes: 13 additions & 3 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type Account @entity {
}

type RoleAssignment @entity {
id: ID! # grantorAddress + tokenId + tokenAddress + granteeAddress + roleHash
id: ID! # rolesRegistryAddress + grantorAddress + tokenId + tokenAddress + granteeAddress + roleHash
role: Role!
rolesRegistry: RolesRegistry!
nft: Nft!
grantor: Account!
grantee: Account!
Expand All @@ -28,15 +29,24 @@ type RoleAssignment @entity {
}

type Role @entity {
id: ID! # tokenId + tokenAddress + roleHash
id: ID! # rolesRegistryAddress + tokenId + tokenAddress + roleHash
rolesRegistry: RolesRegistry!
roleHash: Bytes!
nft: Nft!
roleAssignments: [RoleAssignment!] @derivedFrom(field: "role")
}

type RoleApproval @entity {
id: ID! # grantorAddress + operatorAddress + tokenAddress
id: ID! # rolesRegistryAddress + grantorAddress + operatorAddress + tokenAddress
rolesRegistry: RolesRegistry!
grantor: Account!
operator: Account!
tokenAddress: String!
}

type RolesRegistry @entity {
id: ID! # contractAddress
roles: [Role!] @derivedFrom(field: "rolesRegistry")
roleAssignments: [RoleAssignment!] @derivedFrom(field: "rolesRegistry")
roleApprovals: [RoleApproval!] @derivedFrom(field: "rolesRegistry")
}
2 changes: 1 addition & 1 deletion src/erc7432/role/revoke-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function handleRoleRevoked(event: RoleRevoked): void {
return
}

const roleAssignmentId = generateRoleAssignmentId(revoker, grantee, nft, event.params._role)
const roleAssignmentId = generateRoleAssignmentId(event.address.toHex(), revoker, grantee, nft, event.params._role)
const roleAssignment = RoleAssignment.load(roleAssignmentId)
if (!roleAssignment) {
log.warning('[handleRoleRevoked] RoleAssignment {} does not exist, skipping...', [roleAssignmentId])
Expand Down
10 changes: 8 additions & 2 deletions src/erc7432/role/role-approval-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { log } from '@graphprotocol/graph-ts'

export function handleRoleApprovalForAll(event: RoleApprovalForAll): void {
const rolesRegistryAddress = event.address.toHex()
const grantorAddress = event.transaction.from.toHex()
const operatorAddress = event.params._operator.toHex()
const tokenAddress = event.params._tokenAddress.toHex()
Expand All @@ -17,13 +18,18 @@ export function handleRoleApprovalForAll(event: RoleApprovalForAll): void {
const operatorAccount = findOrCreateAccount(operatorAddress)

if (isApproved) {
const roleApproval = insertRoleApprovalIfNotExist(grantorAccount, operatorAccount, tokenAddress)
const roleApproval = insertRoleApprovalIfNotExist(
rolesRegistryAddress,
grantorAccount,
operatorAccount,
tokenAddress,
)
log.warning('[handleRoleApprovalForAll] Updated RoleAssignment Approval: {} Tx: {}', [
roleApproval.id,
event.transaction.hash.toHex(),
])
} else {
const roleApprovalId = generateRoleApprovalId(grantorAccount, operatorAccount, tokenAddress)
const roleApprovalId = generateRoleApprovalId(rolesRegistryAddress, grantorAccount, operatorAccount, tokenAddress)
deleteRoleApprovalIfExist(roleApprovalId)
log.warning('[handleRoleApprovalForAll] Removed RoleAssignment Approval: {} Tx: {}', [
roleApprovalId,
Expand Down
56 changes: 43 additions & 13 deletions src/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BigInt, Bytes, store } from '@graphprotocol/graph-ts'
import { Account, Nft, Role, RoleApproval, RoleAssignment } from '../../generated/schema'
import { Account, Nft, Role, RoleApproval, RoleAssignment, RolesRegistry } from '../../generated/schema'
import { RoleGranted } from '../../generated/ERC7432-Immutable-Roles/ERC7432'

export function findOrCreateAccount(id: string): Account {
Expand All @@ -25,8 +25,14 @@ export function generateNftId(tokenAddress: string, tokenId: string): string {
return tokenAddress + '-' + tokenId
}

export function generateRoleAssignmentId(grantor: Account, grantee: Account, nft: Nft, roleAssignment: Bytes): string {
return grantor.id + '-' + grantee.id + '-' + nft.id + '-' + roleAssignment.toHex()
export function generateRoleAssignmentId(
roleRegistryAddress: string,
grantor: Account,
grantee: Account,
nft: Nft,
roleAssignment: Bytes,
): string {
return roleRegistryAddress + '-' + grantor.id + '-' + grantee.id + '-' + nft.id + '-' + roleAssignment.toHex()
}

export function findOrCreateRoleAssignment(
Expand All @@ -35,12 +41,13 @@ export function findOrCreateRoleAssignment(
grantee: Account,
nft: Nft,
): RoleAssignment {
const roleAssignmentId = generateRoleAssignmentId(grantor, grantee, nft, event.params._role)
const roleAssignmentId = generateRoleAssignmentId(event.address.toHex(), 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.role = findOrCreateRole(event.address.toHex(), nft, event.params._role).id
roleAssignment.rolesRegistry = findOrCreateRolesRegistry(event.address.toHex()).id
roleAssignment.nft = nft.id
roleAssignment.grantor = grantor.id
roleAssignment.grantee = grantee.id
Expand All @@ -55,36 +62,48 @@ export function findOrCreateRoleAssignment(
return roleAssignment
}

export function findOrCreateRole(nft: Nft, roleHash: Bytes): Role {
const roleId = generateRoleId(nft, roleHash)
export function findOrCreateRole(roleRegistryAddress: string, nft: Nft, roleHash: Bytes): Role {
const roleId = generateRoleId(roleRegistryAddress, nft, roleHash)
let role = Role.load(roleId)

if (!role) {
role = new Role(roleId)
role.roleHash = roleHash
role.nft = nft.id
role.rolesRegistry = findOrCreateRolesRegistry(roleRegistryAddress).id
role.save()
}

return role
}

export function generateRoleId(nft: Nft, roleHash: Bytes): string {
return nft.id + '-' + roleHash.toHex()
export function generateRoleId(roleRegistryAddress: string, nft: Nft, roleHash: Bytes): string {
return roleRegistryAddress + '-' + nft.id + '-' + roleHash.toHex()
}

export function generateRoleApprovalId(grantor: Account, operator: Account, tokenAddress: string): string {
return grantor.id + '-' + operator.id + '-' + tokenAddress.toLowerCase()
export function generateRoleApprovalId(
rolesRegistryAddress: string,
grantor: Account,
operator: Account,
tokenAddress: string,
): string {
return rolesRegistryAddress + '-' + grantor.id + '-' + operator.id + '-' + tokenAddress.toLowerCase()
}

export function insertRoleApprovalIfNotExist(grantor: Account, operator: Account, tokenAddress: string): RoleApproval {
const roleApprovalId = generateRoleApprovalId(grantor, operator, tokenAddress)
export function insertRoleApprovalIfNotExist(
rolesRegistryAddress: string,
grantor: Account,
operator: Account,
tokenAddress: string,
): RoleApproval {
const roleApprovalId = generateRoleApprovalId(rolesRegistryAddress, grantor, operator, tokenAddress)
let roleApproval = RoleApproval.load(roleApprovalId)
if (!roleApproval) {
roleApproval = new RoleApproval(roleApprovalId)
roleApproval.grantor = grantor.id
roleApproval.operator = operator.id
roleApproval.tokenAddress = tokenAddress.toLowerCase()
roleApproval.rolesRegistry = findOrCreateRolesRegistry(rolesRegistryAddress).id
roleApproval.save()
}
return roleApproval
Expand All @@ -96,3 +115,14 @@ export function deleteRoleApprovalIfExist(roleApprovalId: string): void {
store.remove('RoleApproval', roleApprovalId)
}
}

export function findOrCreateRolesRegistry(rolesRegistryAddress: string): RolesRegistry {
let rolesRegistry = RolesRegistry.load(rolesRegistryAddress)

if (!rolesRegistry) {
rolesRegistry = new RolesRegistry(rolesRegistryAddress)
rolesRegistry.save()
}

return rolesRegistry
}
11 changes: 6 additions & 5 deletions tests/erc7432/approval-handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { assert, describe, test, clearStore, afterEach } from 'matchstick-as'
import { createNewRoleApprovalForAllEvent } from '../helpers/events'
import { validateRoleApproval, createMockRoleApproval } from '../helpers/entities'
import { Addresses } from '../helpers/contants'
import { Addresses, ZERO_ADDRESS } from '../helpers/contants'
import { handleRoleApprovalForAll } from '../../src/erc7432'

const grantor = Addresses[0]
const operator = Addresses[1]
const tokenAddress = Addresses[2]
const rolesRegistryAddress = ZERO_ADDRESS

describe('ERC-7432 RoleApprovalForAll Handler', () => {
afterEach(() => {
Expand All @@ -15,7 +16,7 @@ describe('ERC-7432 RoleApprovalForAll Handler', () => {

describe('When RoleApproval exists', () => {
test('should remove approval when is set to false', () => {
createMockRoleApproval(grantor, operator, tokenAddress)
createMockRoleApproval(grantor, operator, tokenAddress, rolesRegistryAddress)
assert.entityCount('RoleApproval', 1)

const event = createNewRoleApprovalForAllEvent(grantor, operator, tokenAddress, false)
Expand All @@ -25,14 +26,14 @@ describe('ERC-7432 RoleApprovalForAll Handler', () => {
})

test('should not do anything when is set to true', () => {
createMockRoleApproval(grantor, operator, tokenAddress)
createMockRoleApproval(grantor, operator, tokenAddress, rolesRegistryAddress)
assert.entityCount('RoleApproval', 1)

const event = createNewRoleApprovalForAllEvent(grantor, operator, tokenAddress, true)
handleRoleApprovalForAll(event)

assert.entityCount('RoleApproval', 1)
validateRoleApproval(grantor, operator, tokenAddress)
validateRoleApproval(rolesRegistryAddress, grantor, operator, tokenAddress)
})
})

Expand All @@ -53,7 +54,7 @@ describe('ERC-7432 RoleApprovalForAll Handler', () => {
handleRoleApprovalForAll(event)

assert.entityCount('RoleApproval', 1)
validateRoleApproval(grantor, operator, tokenAddress)
validateRoleApproval(rolesRegistryAddress, grantor, operator, tokenAddress)
})
})
})
63 changes: 56 additions & 7 deletions tests/erc7432/grant-handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assert, describe, test, clearStore, afterEach } from 'matchstick-as'
import { createNewRoleGrantedEvent } from '../helpers/events'
import { handleRoleGranted } from '../../src/erc7432'
import { Addresses } from '../helpers/contants'
import { Addresses, ZERO_ADDRESS } from '../helpers/contants'
import { BigInt, Bytes } from '@graphprotocol/graph-ts'
import { createMockAccount, createMockNft, validateRole } from '../helpers/entities'
import { Account } from '../../generated/schema'
Expand All @@ -14,6 +14,7 @@ const grantor = Addresses[2]
const revocable = true
const data = Bytes.fromUTF8('0x1234567890')
const expirationDate = BigInt.fromI32(99999)
const rolesRegistryAddress = ZERO_ADDRESS

describe('ERC-7432 RoleGranted Handler', () => {
afterEach(() => {
Expand Down Expand Up @@ -133,9 +134,33 @@ describe('ERC-7432 RoleGranted Handler', () => {
assert.entityCount('Account', 3)

const grantorAccount = new Account(grantor)
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)
validateRole(
grantorAccount,
new Account(Addresses[0]),
nft,
RoleAssignmentId,
expirationDate,
data,
event1.address.toHex(),
)
validateRole(
grantorAccount,
new Account(Addresses[1]),
nft,
RoleAssignmentId,
expirationDate,
data,
event2.address.toHex(),
)
validateRole(
grantorAccount,
new Account(Addresses[2]),
nft,
RoleAssignmentId,
expirationDate,
data,
event3.address.toHex(),
)
})

test('should grant multiple roles for different NFTs', () => {
Expand Down Expand Up @@ -189,8 +214,32 @@ describe('ERC-7432 RoleGranted Handler', () => {
assert.entityCount('Account', 3)

const grantorAccount = new Account(grantor)
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)
validateRole(
grantorAccount,
new Account(Addresses[0]),
nft1,
RoleAssignmentId,
expirationDate,
data,
rolesRegistryAddress,
)
validateRole(
grantorAccount,
new Account(Addresses[1]),
nft2,
RoleAssignmentId,
expirationDate,
data,
rolesRegistryAddress,
)
validateRole(
grantorAccount,
new Account(Addresses[2]),
nft3,
RoleAssignmentId,
expirationDate,
data,
rolesRegistryAddress,
)
})
})
Loading

0 comments on commit 1efa572

Please sign in to comment.