Skip to content

Commit

Permalink
ON-553: Add SftRolesResgistry role approval handler and role granted
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Lima committed Dec 12, 2023
1 parent 3c2b883 commit 6f9bb76
Show file tree
Hide file tree
Showing 14 changed files with 812 additions and 21 deletions.
586 changes: 586 additions & 0 deletions abis/SftRolesRegistry.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ type Account @entity {
}

type RoleAssignment @entity {
id: ID! # rolesRegistryAddress + grantorAddress + tokenAddress + tokenId + granteeAddress + roleHash
# id for ERC721: rolesRegistryId + grantorId + nftId + granteeId + roleHash
# id for ERC1155: rolesRegistryId + grantorId + nftId + granteeId + roleHash + nonce
id: ID!
role: Role!
nft: Nft!
grantor: Account!
Expand All @@ -32,18 +34,20 @@ type RoleAssignment @entity {
data: Bytes!
createdAt: BigInt!
updatedAt: BigInt!
nonce: BigInt # only for ERC1155
tokenAmount: BigInt # only for ERC1155
}

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

type RoleApproval @entity {
id: ID! # rolesRegistryAddress + grantorAddress + operatorAddress + tokenAddress
id: ID! # rolesRegistryId + grantorId + operatorId + tokenAddress
rolesRegistry: RolesRegistry!
grantor: Account!
operator: Account!
Expand Down
4 changes: 2 additions & 2 deletions src/erc-7432/role-granted-handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { log } from '@graphprotocol/graph-ts'
import { RoleGranted } from '../../generated/ERC7432/ERC7432'
import { Account, Nft } from '../../generated/schema'
import { generateERC721NftId, findOrCreateAccount, upsertRoleAssignment } from '../../utils'
import { generateERC721NftId, findOrCreateAccount, upsertERC721RoleAssignment } from '../../utils'

/**
@dev This handler is called when a role is granted.
Expand Down Expand Up @@ -52,7 +52,7 @@ export function handleRoleGranted(event: RoleGranted): void {
}

const granteeAccount = findOrCreateAccount(event.params._grantee.toHex())
const roleAssignment = upsertRoleAssignment(event, grantorAccount, granteeAccount, nft)
const roleAssignment = upsertERC721RoleAssignment(event, grantorAccount, granteeAccount, nft)
log.warning('[erc-7432][handleRoleGranted] roleAssignment: {} NFT: {} Tx: {}', [
roleAssignment.id,
nftId,
Expand Down
6 changes: 3 additions & 3 deletions src/erc-7432/role-revoked-handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BigInt, log } from '@graphprotocol/graph-ts'
import { log } from '@graphprotocol/graph-ts'
import { RoleRevoked } from '../../generated/ERC7432/ERC7432'
import { Account, Nft, RoleAssignment } from '../../generated/schema'
import { findOrCreateRole, findOrCreateRolesRegistry, generateERC721NftId, generateRoleAssignmentId } from '../../utils'
import { findOrCreateRolesRegistry, generateERC721NftId, generateERC721RoleAssignmentId } from '../../utils'

/**
@dev This handler is called when a role is revoked.
Expand Down Expand Up @@ -51,7 +51,7 @@ export function handleRoleRevoked(event: RoleRevoked): void {
}

const rolesRegistry = findOrCreateRolesRegistry(event.address.toHex())
const roleAssignmentId = generateRoleAssignmentId(rolesRegistry, revoker, grantee, nft, event.params._role)
const roleAssignmentId = generateERC721RoleAssignmentId(rolesRegistry, revoker, grantee, nft, event.params._role)
const roleAssignment = RoleAssignment.load(roleAssignmentId)
if (!roleAssignment) {
log.error('[erc-7432][handleRoleRevoked] RoleAssignment {} does not exist, tx {} skipping...', [
Expand Down
2 changes: 2 additions & 0 deletions src/sft-roles-registry/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { handleRoleGranted } from './role-granted-handler'
export { handleRoleApprovalForAll } from './role-approval-for-all-handler'
30 changes: 30 additions & 0 deletions src/sft-roles-registry/role-approval-for-all-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { RoleApprovalForAll } from '../../generated/SftRolesRegistry/SftRolesRegistry'
import { findOrCreateRoleApproval, findOrCreateAccount, findOrCreateRolesRegistry } from '../../utils'
import { log } from '@graphprotocol/graph-ts'

/**
@dev This handler is called when a role approval for all is set.
@param event RoleApprovalForAll The event emitted by the contract.
Example:
event RoleApprovalForAll(address indexed _tokenAddress, address indexed _operator, bool _isApproved);
*/
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()
const isApproved = event.params._isApproved

const grantor = findOrCreateAccount(grantorAddress)
const operator = findOrCreateAccount(operatorAddress)
const rolesRegistry = findOrCreateRolesRegistry(rolesRegistryAddress)
const roleApproval = findOrCreateRoleApproval(rolesRegistry, grantor, operator, tokenAddress)
roleApproval.isApproved = isApproved
roleApproval.save()

log.warning('[sft-roles-registry][handleRoleApprovalForAll] Updated RoleAssignment Approval: {} Tx: {}', [
roleApproval.id,
event.transaction.hash.toHex(),
])
}
47 changes: 47 additions & 0 deletions src/sft-roles-registry/role-granted-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { log } from '@graphprotocol/graph-ts'
import { RoleGranted } from '../../generated/SftRolesRegistry/SftRolesRegistry'
import { Nft } from '../../generated/schema'
import { findOrCreateAccount, generateERC1155NftId, upsertERC1155RoleAssignment } from '../../utils'

/**
@dev This handler is called when a role is granted.
@param event RoleGranted The event emitted by the contract.
Example:
event RoleGranted(
bytes32 indexed _role,
address indexed _tokenAddress,
uint256 indexed _tokenId,
address _grantor,
address _grantee,
uint64 _expirationDate,
bool _revocable,
bytes _data
);
*/
export function handleRoleGranted(event: RoleGranted): void {
const tokenId = event.params._tokenId
const tokenAddress = event.params._tokenAddress.toHex()
const grantorAddress = event.params._grantor.toHex()
const grantor = findOrCreateAccount(grantorAddress)
const nonce = event.params._nonce

const nftId = generateERC1155NftId(tokenAddress, tokenId, grantor.id)
const nft = Nft.load(nftId)

if (!nft) {
log.error('[sft-roles-registry][handleRoleGranted] NFT {} does not exist, tx {} skipping...', [
nftId,
event.transaction.hash.toHex(),
])
return
}

const granteeAccount = findOrCreateAccount(event.params._grantee.toHex())
const roleAssignment = upsertERC1155RoleAssignment(event, grantor, granteeAccount, nft, nonce)
log.warning('[sft-roles-registry][handleRoleGranted] roleAssignment: {} NFT: {} Tx: {}', [
roleAssignment.id,
nftId,
event.transaction.hash.toHex(),
])
}
27 changes: 27 additions & 0 deletions subgraph.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,31 @@ dataSources:
handler: handleRoleRevoked
- event: RoleApprovalForAll(indexed address,indexed address,bool)
handler: handleRoleApprovalForAll
file: ./src/erc-7432/index.ts

- name: SftRolesRegistry
kind: ethereum
network: {{network}}
source:
abi: SftRolesRegistry
{{#isSelfHosted}}
startBlock: 1
{{/isSelfHosted}}
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
entities:
- Nft
- Account
- RoleAssignment
- RoleApproval
abis:
- name: SftRolesRegistry
file: ./abis/SftRolesRegistry.json
eventHandlers:
- event: RoleGranted(indexed uint256,indexed bytes32,address,uint256,uint256,address,address,uint64,bool,bytes)
handler: handleRoleGranted
- event: RoleApprovalForAll(indexed address,indexed address,bool)
handler: handleRoleApprovalForAll
file: ./src/erc-7432/index.ts
4 changes: 2 additions & 2 deletions tests/erc-7432/revoke-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { handleRoleRevoked } from '../../src/erc-7432'
import { Bytes, BigInt } from '@graphprotocol/graph-ts'
import { createMockAccount, createMockNft, createMockRoleAssignment } from '../mocks/entities'
import { Addresses, ONE, TWO, ZERO_ADDRESS } from '../helpers/contants'
import { findOrCreateRolesRegistry, generateERC721NftId, generateRoleAssignmentId } from '../../utils'
import { findOrCreateRolesRegistry, generateERC721NftId, generateERC721RoleAssignmentId } from '../../utils'
import { Account, Nft } from '../../generated/schema'
import { validateRole } from '../helpers/assertion'

Expand Down Expand Up @@ -79,7 +79,7 @@ describe('ERC-7432 RoleRevoked Handler', () => {

assert.entityCount('RoleAssignment', 1)
const registry = findOrCreateRolesRegistry(rolesRegistry)
const _id = generateRoleAssignmentId(registry, new Account(revoker), granteeAccount, nft, RoleAssignmentId)
const _id = generateERC721RoleAssignmentId(registry, new Account(revoker), granteeAccount, nft, RoleAssignmentId)
assert.fieldEquals('RoleAssignment', _id, 'expirationDate', '0')
})

Expand Down
4 changes: 2 additions & 2 deletions tests/helpers/assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Account, Nft } from '../../generated/schema'
import {
findOrCreateRolesRegistry,
generateRoleApprovalId,
generateRoleAssignmentId,
generateERC721RoleAssignmentId,
generateRoleId,
} from '../../utils'
import { BigInt, Bytes } from '@graphprotocol/graph-ts'
Expand All @@ -22,7 +22,7 @@ export function validateRole(
assert.fieldEquals('Role', roleId, 'roleHash', roleAssignment.toHex())
assert.fieldEquals('Role', roleId, 'nft', nft.id)

const roleAssignmentId = generateRoleAssignmentId(rolesRegistry, grantor, grantee, nft, roleAssignment)
const roleAssignmentId = generateERC721RoleAssignmentId(rolesRegistry, grantor, grantee, nft, roleAssignment)
assert.fieldEquals(
'RoleAssignment',
roleAssignmentId,
Expand Down
4 changes: 2 additions & 2 deletions tests/mocks/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BigInt, Bytes } from '@graphprotocol/graph-ts'
import { Account, Nft, RoleAssignment, RoleApproval, Role } from '../../generated/schema'
import {
generateERC721NftId,
generateRoleAssignmentId,
generateERC721RoleAssignmentId,
generateRoleApprovalId,
generateRoleId,
findOrCreateRolesRegistry,
Expand Down Expand Up @@ -44,7 +44,7 @@ export function createMockRoleAssignment(
role.rolesRegistry = rolesRegistryAddress
role.save()

const roleAssignmentId = generateRoleAssignmentId(
const roleAssignmentId = generateERC721RoleAssignmentId(
rolesRegistry,
new Account(grantor),
new Account(grantee),
Expand Down
88 changes: 88 additions & 0 deletions utils/entities/role-assignment/erc-1155-role-assignment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { BigInt, Bytes } from '@graphprotocol/graph-ts'
import { Account, Nft, RoleAssignment, RolesRegistry } from '../../../generated/schema'
import { RoleGranted } from '../../../generated/SftRolesRegistry/SftRolesRegistry'
import { findOrCreateRolesRegistry } from '../roles-registry'
import { findOrCreateRole } from '../role'

/**
* @notice Generate a role assignment id.
* @dev roleRegistry, grantor, grantee, nft should be created/exist before calling this function.
* @param roleRegistry The roles registry used for the role assignment.
* @param grantor The grantor of the role assignment.
* @param grantee The grantee of the role assignment.
* @param nft The nft of the role assignment.
* @param roleHash The role hash of the role assignment.
* @param nonce The nonce of the role assignment.
* @returns The role assignment id.
*/
export function generateERC1155RoleAssignmentId(
roleRegistry: RolesRegistry,
grantor: Account,
grantee: Account,
nft: Nft,
roleHash: Bytes,
nonce: BigInt,
): string {
return (
roleRegistry.id +
'-' +
grantor.id +
'-' +
grantee.id +
'-' +
nft.id +
'-' +
roleHash.toHex() +
'-' +
nonce.toString()
)
}

/**
* @notice Upsert a role assignment.
* @dev roleRegistry, grantor, grantee, nft should be created/exist before calling this function.
* @param event The role granted event.
* @param grantor The grantor of the role assignment.
* @param grantee The grantee of the role assignment.
* @param nft The nft of the role assignment.
* @param nonce The nonce of the role assignment.
* @returns The role assignment entity created (or found).
*/
export function upsertERC1155RoleAssignment(
event: RoleGranted,
grantor: Account,
grantee: Account,
nft: Nft,
nonce: BigInt,
): RoleAssignment {
const rolesRegistry = findOrCreateRolesRegistry(event.address.toHex())
const roleAssignmentId = generateERC1155RoleAssignmentId(
rolesRegistry,
grantor,
grantee,
nft,
event.params._role,
nonce,
)
let roleAssignment = RoleAssignment.load(roleAssignmentId)
const role = findOrCreateRole(rolesRegistry, nft, event.params._role)

if (!roleAssignment) {
roleAssignment = new RoleAssignment(roleAssignmentId)
roleAssignment.role = role.id
roleAssignment.nft = nft.id
roleAssignment.grantor = grantor.id
roleAssignment.grantee = grantee.id
roleAssignment.createdAt = event.block.timestamp
roleAssignment.nonce = nonce
roleAssignment.tokenAmount = event.params._tokenAmount
}

roleAssignment.expirationDate = event.params._expirationDate
roleAssignment.revocable = event.params._revocable
roleAssignment.data = event.params._data
roleAssignment.updatedAt = event.block.timestamp

roleAssignment.save()
return roleAssignment
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Bytes } from '@graphprotocol/graph-ts'
import { Account, Nft, RoleAssignment, RolesRegistry } from '../../generated/schema'
import { RoleGranted } from '../../generated/ERC7432/ERC7432'
import { findOrCreateRolesRegistry } from './roles-registry'
import { findOrCreateRole } from './role'
import { Account, Nft, RoleAssignment, RolesRegistry } from '../../../generated/schema'
import { RoleGranted } from '../../../generated/ERC7432/ERC7432'
import { findOrCreateRolesRegistry } from '../roles-registry'
import { findOrCreateRole } from '../role'

/**
* @notice Generate a role assignment id.
Expand All @@ -14,7 +14,7 @@ import { findOrCreateRole } from './role'
* @param roleHash The role hash of the role assignment.
* @returns The role assignment id.
*/
export function generateRoleAssignmentId(
export function generateERC721RoleAssignmentId(
roleRegistry: RolesRegistry,
grantor: Account,
grantee: Account,
Expand All @@ -33,9 +33,14 @@ export function generateRoleAssignmentId(
* @param nft The nft of the role assignment.
* @returns The role assignment entity created (or found).
*/
export function upsertRoleAssignment(event: RoleGranted, grantor: Account, grantee: Account, nft: Nft): RoleAssignment {
export function upsertERC721RoleAssignment(
event: RoleGranted,
grantor: Account,
grantee: Account,
nft: Nft,
): RoleAssignment {
const rolesRegistry = findOrCreateRolesRegistry(event.address.toHex())
const roleAssignmentId = generateRoleAssignmentId(rolesRegistry, grantor, grantee, nft, event.params._role)
const roleAssignmentId = generateERC721RoleAssignmentId(rolesRegistry, grantor, grantee, nft, event.params._role)
let roleAssignment = RoleAssignment.load(roleAssignmentId)
const role = findOrCreateRole(rolesRegistry, nft, event.params._role)

Expand Down
2 changes: 2 additions & 0 deletions utils/entities/role-assignment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './erc-721-role-assignment'
export * from './erc-1155-role-assignment'

0 comments on commit 6f9bb76

Please sign in to comment.