Skip to content

Commit

Permalink
implemented RoleApproval entity
Browse files Browse the repository at this point in the history
  • Loading branch information
ernanirst committed Oct 18, 2023
1 parent f484633 commit 0357dc8
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 5 deletions.
8 changes: 8 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Nft @entity {
type Account @entity {
id: ID! # address
nfts: [Nft!] @derivedFrom(field: "owner")
roleApprovals: [RoleApproval!] @derivedFrom(field: "grantor")
}

type Role @entity {
Expand All @@ -21,3 +22,10 @@ type Role @entity {
revocable: Boolean!
data: Bytes!
}

type RoleApproval @entity {
id: ID! # grantorAddress + operatorAddress + tokenAddress
grantor: Account!
operator: Account!
tokenAddress: String!
}
1 change: 1 addition & 0 deletions src/erc7432/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { handleRoleGranted } from './role/grant-handler'
export { handleRoleRevoked } from './role/revoke-handler'
export { handleRoleApprovalForAll } from './role/role-approval-handler'
20 changes: 20 additions & 0 deletions src/erc7432/role/role-approval-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { RoleApprovalForAll } from '../../../generated/ERC7432-Immutable-Roles/ERC7432'
import { findOrCreateAccount, insertRoleApprovalIfNotExist, deleteRoleApprovalIfExist } from '../../utils/helper'
import { log } from '@graphprotocol/graph-ts'

export function handleRoleApprovalForAll(event: RoleApprovalForAll): void {
const grantorAddress = event.transaction.from.toHex()
const operatorAddress = event.params._operator.toHex()
const tokenAddress = event.params._tokenAddress.toHex()
const isApproved = event.params._isApproved

const grantorAccount = findOrCreateAccount(grantorAddress)
const operatorAccount = findOrCreateAccount(operatorAddress)

if (isApproved) {
const roleApproval = insertRoleApprovalIfNotExist(grantorAccount, operatorAccount, tokenAddress)
log.info('[handleRoleGranted] Updated Role Approval: {}', [roleApproval.id])
} else {
deleteRoleApprovalIfExist(grantorAccount, operatorAccount, tokenAddress)
}
}
29 changes: 27 additions & 2 deletions src/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BigInt, Bytes } from '@graphprotocol/graph-ts'
import { Account, Nft, Role } from '../../generated/schema'
import { BigInt, Bytes, store } from '@graphprotocol/graph-ts'
import { Account, Nft, Role, RoleApproval } from '../../generated/schema'
import { RoleGranted } from '../../generated/ERC7432-Immutable-Roles/ERC7432'

export function findOrCreateAccount(id: string): Account {
Expand Down Expand Up @@ -45,3 +45,28 @@ export function findOrCreateRole(event: RoleGranted, grantor: Account, grantee:
role.save()
return role
}

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

export function insertRoleApprovalIfNotExist(grantor: Account, operator: Account, tokenAddress: string): RoleApproval {
const roleApprovalId = generateRoleApprovalId(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.save()
}
return roleApproval
}

export function deleteRoleApprovalIfExist(grantor: Account, operator: Account, tokenAddress: string): void {
const roleApprovalId = generateRoleApprovalId(grantor, operator, tokenAddress)
const roleApproval = RoleApproval.load(roleApprovalId)
if (roleApproval) {
store.remove('RoleApproval', roleApprovalId)
}
}
3 changes: 3 additions & 0 deletions subgraph-goerli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dataSources:
- Nft
- Account
- Role
- RoleApproval
abis:
- name: ERC7432
file: ./abis/ERC7432.json
Expand All @@ -47,4 +48,6 @@ dataSources:
handler: handleRoleGranted
- event: RoleRevoked(indexed bytes32,indexed address,indexed uint256,address,address)
handler: handleRoleRevoked
- event: RoleApprovalForAll(indexed address,indexed address,bool)
handler: handleRoleApprovalForAll
file: ./src/erc7432/index.ts
3 changes: 3 additions & 0 deletions subgraph-mumbai.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dataSources:
- Nft
- Account
- Role
- RoleApproval
abis:
- name: ERC7432
file: ./abis/ERC7432.json
Expand All @@ -27,4 +28,6 @@ dataSources:
handler: handleRoleGranted
- event: RoleRevoked(indexed bytes32,indexed address,indexed uint256,address,address)
handler: handleRoleRevoked
- event: RoleApprovalForAll(indexed address,indexed address,bool)
handler: handleRoleApprovalForAll
file: ./src/erc7432/index.ts
3 changes: 3 additions & 0 deletions subgraph-polygon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dataSources:
- Nft
- Account
- Role
- RoleApproval
abis:
- name: ERC7432
file: ./abis/ERC7432.json
Expand All @@ -47,4 +48,6 @@ dataSources:
handler: handleRoleGranted
- event: RoleRevoked(indexed bytes32,indexed address,indexed uint256,address,address)
handler: handleRoleRevoked
- event: RoleApprovalForAll(indexed address,indexed address,bool)
handler: handleRoleApprovalForAll
file: ./src/erc7432/index.ts
55 changes: 55 additions & 0 deletions tests/erc7432/approval-handler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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 { handleRoleApprovalForAll } from '../../src/erc7432'

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

describe('ERC-7432 RoleApprovalForAll Handler', () => {
afterEach(() => {
clearStore()
})

test('should not do anything when approval does not exist and is set to false', () => {
assert.entityCount('RoleApproval', 0)

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

assert.entityCount('RoleApproval', 0)
})

test('should create approval when approval does not exist and is set to true', () => {
assert.entityCount('RoleApproval', 0)

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

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

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

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

assert.entityCount('RoleApproval', 0)
})

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

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

assert.entityCount('RoleApproval', 1)
validateRoleApproval(grantor, operator, tokenAddress)
})
})
25 changes: 23 additions & 2 deletions tests/helpers/entities.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigInt, Bytes } from '@graphprotocol/graph-ts'
import { Account, Nft, Role } from '../../generated/schema'
import { generateNftId, generateRoleId } from '../../src/utils/helper'
import { Account, Nft, Role, RoleApproval } from '../../generated/schema'
import { generateNftId, generateRoleId, generateRoleApprovalId } from '../../src/utils/helper'
import { assert } from 'matchstick-as'

export function createMockNft(tokenAddress: string, tokenId: string, ownerAddress: string): Nft {
Expand Down Expand Up @@ -35,6 +35,16 @@ export function createMockRole(role: Bytes, grantor: string, grantee: string, nf
return newRole
}

export function createMockRoleApproval(grantor: string, operator: string, tokenAddress: string): RoleApproval {
const roleApprovalId = generateRoleApprovalId(new Account(grantor), new Account(operator), tokenAddress)
const roleApproval = new RoleApproval(roleApprovalId)
roleApproval.grantor = grantor
roleApproval.operator = operator
roleApproval.tokenAddress = tokenAddress
roleApproval.save()
return roleApproval
}

export function validateRole(
grantor: Account,
grantee: Account,
Expand All @@ -51,3 +61,14 @@ export function validateRole(
assert.fieldEquals('Role', _id, 'expirationDate', expirationDate.toString())
assert.fieldEquals('Role', _id, 'data', data.toHex())
}

export function validateRoleApproval(grantor: string, operator: string, tokenAddress: string): void {
const roleApprovalId = generateRoleApprovalId(
new Account(grantor.toLowerCase()),
new Account(operator.toLowerCase()),
tokenAddress,
)
assert.fieldEquals('RoleApproval', roleApprovalId, 'grantor', grantor)
assert.fieldEquals('RoleApproval', roleApprovalId, 'operator', operator)
assert.fieldEquals('RoleApproval', roleApprovalId, 'tokenAddress', tokenAddress)
}
17 changes: 16 additions & 1 deletion tests/helpers/events.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { newMockEvent } from 'matchstick-as'
import { Transfer } from '../../generated/ERC721-Chronos-Traveler/ERC721'
import { Address, BigInt, Bytes, ethereum } from '@graphprotocol/graph-ts'
import { RoleGranted, RoleRevoked } from '../../generated/ERC7432-Immutable-Roles/ERC7432'
import { RoleGranted, RoleRevoked, RoleApprovalForAll } from '../../generated/ERC7432-Immutable-Roles/ERC7432'
import { Nft } from '../../generated/schema'

export function createTransferEvent(from: string, to: string, tokenId: string, address: string): Transfer {
Expand Down Expand Up @@ -48,6 +48,21 @@ export function createNewRoleGrantedEvent(
return event
}

export function createNewRoleApprovalForAllEvent(
grantor: string,
operator: string,
tokenAddress: string,
isApproved: boolean,
): RoleApprovalForAll {
const event = changetype<RoleApprovalForAll>(newMockEvent())
event.parameters = new Array<ethereum.EventParam>()
event.transaction.from = Address.fromString(grantor)
event.parameters.push(buildEventParamAddress('_tokenAddress', tokenAddress))
event.parameters.push(buildEventParamAddress('_operator', operator))
event.parameters.push(buildEventParamBoolean('_isApproved', isApproved))
return event
}

function buildEventParamBoolean(name: string, value: boolean): ethereum.EventParam {
const ethAddress = ethereum.Value.fromBoolean(value)
return new ethereum.EventParam(name, ethAddress)
Expand Down

0 comments on commit 0357dc8

Please sign in to comment.