Skip to content

Commit

Permalink
Permissions: Track method signatures for permissions (#23)
Browse files Browse the repository at this point in the history
Co-authored-by: Facu Spagnuolo <[email protected]>
  • Loading branch information
PatricioHenderson and facuspagnuolo authored Sep 22, 2023
1 parent e62615f commit 66e3b64
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ build
types
node_modules
subgraph.yaml
/src/permissions/dictionary.ts
*.log
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@
"@graphprotocol/graph-ts": "^0.31.0",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"ethers": "~5.6.0",
"eslint": "^7.9.0",
"eslint-config-mimic": "^0.0.2",
"ts-node": "^10.9.1",
"typescript": "~4.3.4"
},
"eslintConfig": {
Expand Down
1 change: 1 addition & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type Permission @entity {
who: String!
where: String!
what: String!
method: String!
params: [PermissionParam!] @derivedFrom(field: "permission")
}

Expand Down
46 changes: 46 additions & 0 deletions scripts/build-permissions-dictionary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ethers } from 'ethers'
import * as fs from 'fs'

const FUNCTIONS = {}
const DEPENDENCIES_DIR = './node_modules/@mimic-fi'
const DICTIONARY_OUTPUT = './src/permissions/dictionary.ts'

fs.readdirSync(DEPENDENCIES_DIR).forEach((dependency) => {
const interfacesDir = `${DEPENDENCIES_DIR}/${dependency}/artifacts/contracts/interfaces`
if (!fs.existsSync(interfacesDir)) return
fs.readdirSync(interfacesDir).forEach((file) => processInterfaces(`${interfacesDir}/${file}`))
})

writeOutput()

function processInterfaces(path: string): void {
if (fs.statSync(path).isDirectory())
return fs.readdirSync(path).forEach((file) => processInterfaces(`${path}/${file}`))

const data = JSON.parse(fs.readFileSync(path).toString())
if (!data.abi) return

const iface = new ethers.utils.Interface(data.abi)
data.abi
.filter((input) => input.type === 'function')
.filter((input) => input.stateMutability === 'nonpayable')
.forEach((input) => (FUNCTIONS[iface.getSighash(input.name)] = functionDefinition(input.name, input.inputs)))
}

function functionDefinition(name: string, inputs: { type: string; name: string }[]): string {
const args = inputs.map((input) => `${input.type} ${input.name}`).join(', ')
return `${name}(${args})`
}

function writeOutput(): void {
if (fs.existsSync(DICTIONARY_OUTPUT)) fs.unlinkSync(DICTIONARY_OUTPUT)
const content = `// This file was autogenerated at deployment time
const dictionary = new Map<string, string>()
${Object.entries(FUNCTIONS)
.map(([selector, name]) => `dictionary.set('${selector}', '${name}')`)
.join('\n')}
export default dictionary
`
fs.writeFileSync(DICTIONARY_OUTPUT, `${content}`)
}
4 changes: 4 additions & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ sed -i -e "s/{{feeControllerAddress}}/${FEE_CONTROLLER_ADDRESS}/g" subgraph.yaml
sed -i -e "s/{{blockNumber}}/${BLOCK_NUMBER}/g" subgraph.yaml
rm -f subgraph.yaml-e

# Build functions selector dictionary
echo "Building functions selector dictionary"
yarn ts-node ./scripts/build-permissions-dictionary.ts

# Run codegen and build
rm -rf ./types && yarn graph codegen -o types
yarn graph build
5 changes: 4 additions & 1 deletion src/Authorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import { Address, Bytes, crypto, log, store } from '@graphprotocol/graph-ts'

import { Permission, PermissionParam } from '../types/schema'
import { Authorized, Authorizer as AuthorizerContract, Unauthorized } from '../types/templates/Authorizer/Authorizer'
import { getFunctionNameForSelector } from './permissions/index'

export function handleAuthorized(event: Authorized): void {
const permissionId = getPermissionId(event.address, event.params.who, event.params.where, event.params.what)
const permission = new Permission(permissionId)
const what = event.params.what.toHexString()
permission.authorizer = event.address.toHexString()
permission.who = event.params.who.toHexString()
permission.where = event.params.where.toHexString()
permission.what = event.params.what.toHexString()
permission.what = what
permission.method = getFunctionNameForSelector(what)
permission.save()

const authorizerContract = AuthorizerContract.bind(event.address)
Expand Down
6 changes: 6 additions & 0 deletions src/permissions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import dictionary from './dictionary'

export function getFunctionNameForSelector(selector: string): string {
const functionName = dictionary.has(selector) ? dictionary.get(selector) : 'Unknown'
return functionName
}

0 comments on commit 66e3b64

Please sign in to comment.